Skip to content

lua-lsm: add skb payload accessors and hand out only full socks - #31

Open
chenzongyao200127 wants to merge 139 commits into
openanolis:lua-lsmfrom
chenzongyao200127:lua-lsm-skb-accessors
Open

lua-lsm: add skb payload accessors and hand out only full socks#31
chenzongyao200127 wants to merge 139 commits into
openanolis:lua-lsmfrom
chenzongyao200127:lua-lsm-skb-accessors

Conversation

@chenzongyao200127

@chenzongyao200127 chenzongyao200127 commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

Summary

Netlink and packet hooks receive an sk_buff, but the skb object only exposed
metadata (sock, protocol, iif, secmark). A policy that has to decide on
message contents — which netlink command is being issued, for instance — could
not be written at all.

This series adds the payload accessors, fixes a fail-open in skb:sock(), and
documents the one thing a content-aware policy cannot guess: where offset 0 is.

  • skb:len() / skb:read(off, len)read() returns the bytes as a string,
    or nil when the range falls outside the window, so the policy keeps the
    verdict instead of taking a Lua error that would fall back to the hook default
    and allow the operation. Reads go through skb_copy_bits(), so a non-linear
    skb is stitched together transparently, and one read is bounded by a fixed
    256-byte on-stack buffer.
  • sock:proto() — the raw sk_protocol. suites() renders that number through
    the IPPROTO_* namespace, which cannot name protocols of other families such
    as netlink's NETLINK_ROUTE.
  • skb:sock() now resolves skb->sk through skb_to_full_sk(). On handshake
    and time-wait paths skb->sk is a request or time-wait sock, which stops short
    of the fields sock:proto() and sock:suites() read, so a policy asking for
    the protocol number got neighbouring slab bytes — and since those pass for a
    valid number, the rule they feed failed open. A half-open connection now
    resolves to its listener and a time-wait sock yields nil. skb:full_sk()
    became a duplicate of skb:sock() and is dropped.
  • docs/API.md defines the data window (skb->len bytes from skb->data) once
    for both accessors and records where it starts in each hook that passes an skb.
    Each layer advances skb->data past its own header, so the base is a property
    of the hook, not of the packet — and getting it wrong is quiet: an out-of-range
    read yields nil, an in-range one yields whatever field happens to sit there.

Multi-byte fields are decoded in Lua, since the in-kernel Lua has no bit
library; the examples below use string.byte and modulo arithmetic.

Example policies

Deny route deletions over rtnetlink. Under netlink_send the window spans
everything one sendmsg() wrote, so a batch arrives as consecutive
length-delimited messages and the policy has to walk them.

local errno = require("errno")

local NETLINK_ROUTE = 0
local RTM_DELROUTE = 25
local NLMSG_HDRLEN = 16

-- struct nlmsghdr: u32 nlmsg_len, u16 nlmsg_type, u16 nlmsg_flags, ...
-- Netlink fields are in native byte order (little-endian here).
local function u16(skb, off)
    local s = skb:read(off, 2)
    if not s then
        return nil
    end
    local lo, hi = s:byte(1, 2)
    return lo + hi * 256
end

local function u32(skb, off)
    local s = skb:read(off, 4)
    if not s then
        return nil
    end
    local b1, b2, b3, b4 = s:byte(1, 4)
    return b1 + b2 * 256 + b3 * 65536 + b4 * 16777216
end

return {
    name = "netlink_route_guard",
    author = "example",
    description = "Deny RTM_DELROUTE over rtnetlink",
    license = "GPL-2.0",
    version = 1,

    netlink_send = function(sk, skb)
        if sk:proto() ~= NETLINK_ROUTE then
            return
        end

        local off, total = 0, skb:len()
        while off + NLMSG_HDRLEN <= total do
            local len = u32(skb, off)
            local nlmsg_type = u16(skb, off + 4)

            -- A truncated or nonsensical header is not something to guess at.
            if not len or not nlmsg_type or len < NLMSG_HDRLEN then
                return false, errno.EINVAL
            end
            if nlmsg_type == RTM_DELROUTE then
                return false, errno.EPERM
            end

            -- NLMSG_ALIGN(len), without a bit library.
            local aligned = len + 3
            off = off + aligned - aligned % 4
        end
    end,
}

Log inbound TCP SYNs to a port. Under socket_sock_rcv_skb offset 0 is the TCP
header on the IPv4/IPv6 TCP path — but the same hook fires for netlink, SCTP,
unix and raw sockets, where the window starts somewhere else, so the family
check is not optional.

local audit = require("audit")

return {
    name = "tcp_syn_watch",
    author = "example",
    description = "Audit inbound SYNs to the SSH port",
    license = "GPL-2.0",
    version = 1,

    socket_sock_rcv_skb = function(sk, skb)
        if not sk:is_tcp() then
            return
        end

        -- struct tcphdr: u16 source, u16 dest, ..., u8 flags at offset 13.
        -- Wire fields are big-endian.
        local hdr = skb:read(0, 14)
        if not hdr then
            return
        end
        local dport = hdr:byte(3) * 256 + hdr:byte(4)
        local flags = hdr:byte(14)
        local syn = flags % 4 >= 2
        local ack = flags % 32 >= 16

        if dport == 22 and syn and not ack then
            audit.log{ op = "tcp_syn", dport = dport, len = skb:len() }
        end
    end,
}

skb:sock() can return nil — no owning socket, or a time-wait sock that is
not a full sock — so a policy that reads through it needs the nil check;
indexing nil raises, and a Lua error inside a hook falls back to that hook's
default, which is "allow" for most hooks. Wrap parsing in pcall() where the
policy must fail closed.

Test plan

  • Load both example policies and confirm the verdicts (ip route del denied,
    SYNs to port 22 audited).
  • skb:read() boundaries: off == skb:len() with len == 0, len == 256,
    len > 256, negative off/len, and a read spanning a non-linear skb.
  • skb:sock() on a SYN (inet_conn_request) returns the listener, and on a
    time-wait skb returns nil rather than a mini-sock.
  • Offsets in docs/API.md re-checked against the hook call sites for each
    row of the table.

uudiin added 30 commits March 24, 2026 14:12
The folling is main instructions:

  export PATH="$(brew --prefix make)/libexec/gnubin:$PATH"
  export PATH="$(brew --prefix llvm)/bin:$PATH"
  export PATH="$(brew --prefix lld)/bin:$PATH"

  # Eliminate compile errors of scripts/mod/file2alias.c
  export HOSTCFLAGS="-D_UUID_T -D__GETHOSTUUID_H"

  # Maybe the symlink is missed on macOS
  #ln -s ../../../scripts/syscall.tbl arch/arm64/tools/syscall_64.tbl
  #ln -s qcom,sm8550-dispcc.h include/dt-bindings/clock/qcom,sm8650-dispcc.h

  #make ARCH=arm64 LLVM=1

  #make LLVM=1 menuconfig
  #make LLVM=1 -j8

Signed-off-by: Tianjia Zhang <tianjia.zhang@linux.alibaba.com>
When developing a dedicated LSM module, we need to operate on the
file object within the LSM function, such as retrieving the path.
However, in `security_file_alloc()`, the passed-in `filp` is
only a valid pointer; the content of `filp` is completely
uninitialized and entirely random, which confuses the LSM function.

Therefore, it is necessary to call `security_file_alloc()` only
after the main fields of the `filp` object have been initialized.
This patch only moves the call to `security_file_alloc()` to the
end of the `init_file()` function.

Signed-off-by: Tianjia Zhang <tianjia.zhang@linux.alibaba.com>
Standard C provides basic library functions for jumping between
functions. They were introduced to support the upcoming Lua
language interpreter, which uses the setjmp/longjmp functions
to implement exception handling.

Signed-off-by: Tianjia Zhang <tianjia.zhang@linux.alibaba.com>
Standard C provides basic library functions for jumping between
functions. They were introduced to support the upcoming Lua
language interpreter, which uses the setjmp/longjmp functions
to implement exception handling.

Signed-off-by: Tianjia Zhang <tianjia.zhang@linux.alibaba.com>
Lua is an extension programming language designed to support general
procedural programming with data description facilities. It also offers
good support for object-oriented programming, functional programming,
and data-driven programming. Lua is intended to be used as a powerful,
light-weight scripting language for any program that needs one. Lua is
implemented as a library, written in clean C (that is, in the common
subset of ANSI C and C++).

Lua is embedded in the kernel. Leveraging Lua's simplicity and powerful
metaprogramming capabilities, it allows the use of scripting languages
to develop complex security access control policies.

The reason for choosing version 5.1 is that Lua has been mature enough
since version 5.1, and secondly, LuaJIT may be introduced in the future
for JIT acceleration. Currently, LuaJIT is compatible with version 5.1.

Signed-off-by: Tianjia Zhang <tianjia.zhang@linux.alibaba.com>
queue.h and tree.h are borrowed from freebsd
d342ae67192f ("uath: add support for GCMP-128 encryption")

bitmap.h is borrowed from bitops of latest netbsd.

Signed-off-by: Tianjia Zhang <tianjia.zhang@linux.alibaba.com>
Signed-off-by: Tianjia Zhang <tianjia.zhang@linux.alibaba.com>
Signed-off-by: Tianjia Zhang <tianjia.zhang@linux.alibaba.com>
Co-developed-by: Zongyao Chen <ZongYao.Chen@linux.alibaba.com>
Signed-off-by: Tianjia Zhang <tianjia.zhang@linux.alibaba.com>
Signed-off-by: Tianjia Zhang <tianjia.zhang@linux.alibaba.com>
Signed-off-by: Tianjia Zhang <tianjia.zhang@linux.alibaba.com>
Signed-off-by: Tianjia Zhang <tianjia.zhang@linux.alibaba.com>
Signed-off-by: Tianjia Zhang <tianjia.zhang@linux.alibaba.com>
This module leverages Lua's metaprogramming capabilities to develop
complex access control logic and policies in Lua. Let us using a
programmatic approach close to natural language, significantly lowers
the barrier to entry for developing LSM access control.

This module has the following notable features:
* Supports developing LSM policies as mini-programs or LSM plugins.
  Multiple mini-programs or LSM plugins can be loaded simultaneously,
  and dynamic, on-demand uninstallation is supported.
* Each mini-program runs in a secure sandbox environment. Script errors
  will not trigger kernel panics, and mini-programs are isolated from
  each other, ensuring no impact.
* Mini-programs interact with the kernel through a specific, limited
  API, ensuring kernel security.
* The LSM module features deep integration with the Lua language,
  enabling the most natural way to share data and set kernel object
  attributes.

Signed-off-by: Tianjia Zhang <tianjia.zhang@linux.alibaba.com>
Co-developed-by: Zongyao Chen <ZongYao.Chen@linux.alibaba.com>
The reason is that the modules_lock is held for a period of time during
module registration. A softirq occurs within this critical section, and
the softirq handler reenters the LSM function, causing a deadlock.

This issue can be resolved by disabling softirq while holding the
modules_lock lock during module registration.

Some logs are as follows:

[28031.294632] rcu: INFO: rcu_preempt self-detected stall on CPU
[28031.294801] rcu: 	2-...!: (886811 ticks this GP) idle=ea9c/1/0x4000000000000000 softirq=243287/243287 fqs=0
[28031.295043] rcu: 	(t=887418 jiffies g=263373 q=454 ncpus=4)
[28031.295183] rcu: rcu_preempt kthread starved for 887418 jiffies! g263373 f0x0 RCU_GP_WAIT_FQS(5) ->state=0x0 ->cpu=2
[28031.295524] rcu: 	Unless rcu_preempt kthread gets sufficient CPU time, OOM is now expected behavior.
[28031.295872] rcu: RCU grace-period kthread stack dump:
[28031.296001] task:rcu_preempt     state:R  running task     stack:0     pid:15    tgid:15    ppid:2      task_flags:0x208040 flags:0x00000010
[28031.296319] Call trace:
[28031.296381]  __switch_to+0x194/0x2b4 (T)
[28031.296484]  __schedule+0x5b4/0x980
[28031.296572]  schedule+0x54/0xf8
[28031.296652]  schedule_timeout+0x88/0xf8
[28031.296750]  rcu_gp_fqs_loop+0x1c4/0x6b0
[28031.296851]  rcu_gp_kthread+0x60/0x134
[28031.296946]  kthread+0x140/0x254
[28031.297030]  ret_from_fork+0x10/0x20
[28031.297123] Sending NMI from CPU 2 to CPUs 1:
[28031.297242] NMI backtrace for cpu 1
[28031.297247] CPU: 1 UID: 0 PID: 81146 Comm: sh Not tainted 6.17.0-rc3+ #511 PREEMPT
[28031.297249] Hardware name: QEMU QEMU Virtual Machine, BIOS edk2-stable202408-prebuilt.qemu.org 08/13/2024
[28031.297250] pstate: 01400005 (nzcv daif +PAN -UAO -TCO +DIT -SSBS BTYPE=--)
[28031.297250] pc : queued_read_lock_slowpath+0x80/0x14c
[28031.297253] lr : _raw_read_lock_bh+0x64/0x68
[28031.297254] sp : ffff800088ee39e0
[28031.297254] x29: ffff800088ee39e0 x28: 0000000000002e2e x27: 0000000000000501
[28031.297256] x26: 0000000000000030 x25: 2f2f2f2f2f2f2f2f x24: 61c8864680b583eb
[28031.297257] x23: fefefefefefefeff x22: 0000000000000000 x21: ffff0000c2671138
[28031.297257] x20: 0000000000000081 x19: 00001643fef959e4 x18: 0000000000000000
[28031.297258] x17: 0000000000000000 x16: 0000000000000000 x15: 0000000000000000
[28031.297259] x14: 0000000000000000 x13: 0000000000000000 x12: 8080808080808000
[28031.297259] x11: 000000000000002e x10: 0000000000000018 x9 : 0000000000000000
[28031.297260] x8 : 00000000000008ff x7 : 0000000000000000 x6 : 0000000000000000
[28031.297261] x5 : 0000000000000000 x4 : 0000000000000000 x3 : ffff800088ee3c98
[28031.297261] x2 : 0000000000000000 x1 : 0000000000013cee x0 : ffff8000818ae3c8
[28031.297262] Call trace:
[28031.297262]  queued_read_lock_slowpath+0x80/0x14c (P)
[28031.297264]  _raw_read_lock_bh+0x64/0x68
[28031.297265]  lua_lsm_inode_permission+0x54/0x35c
[28031.297268]  security_inode_permission+0x54/0xb0
[28031.297269]  inode_permission+0x64/0x170
[28031.297270]  link_path_walk+0xb4/0x388
[28031.297272]  path_lookupat+0x68/0x120
[28031.297272]  filename_lookup+0xe0/0x1e0
[28031.297273]  vfs_statx+0x7c/0x1a0
[28031.297276]  vfs_fstatat+0xb4/0xe0
[28031.297277]  __arm64_sys_newfstatat+0x68/0xa8
[28031.297278]  invoke_syscall+0x40/0xf8
[28031.297280]  el0_svc_common+0xa8/0xd8
[28031.297281]  do_el0_svc+0x1c/0x28
[28031.297282]  el0_svc+0x38/0x8c
[28031.297283]  el0t_64_sync_handler+0x84/0x12c
[28031.297284]  el0t_64_sync+0x198/0x19c
[28031.298239] CPU: 2 UID: 0 PID: 81145 Comm: cat Not tainted 6.17.0-rc3+ #511 PREEMPT
[28031.303082] Hardware name: QEMU QEMU Virtual Machine, BIOS edk2-stable202408-prebuilt.qemu.org 08/13/2024
[28031.303326] pstate: 01400005 (nzcv daif +PAN -UAO -TCO +DIT -SSBS BTYPE=--)
[28031.303504] pc : queued_read_lock_slowpath+0x80/0x14c
[28031.303634] lr : _raw_read_lock_bh+0x64/0x68
[28031.303743] sp : ffff8000818ebe00
[28031.303828] x29: ffff8000818ebe00 x28: 0000000000000200 x27: 000000000000000a
[28031.304011] x26: ffff800081788000 x25: ffff8000800fa104 x24: 0000000000000001
[28031.304192] x23: 0000000000000000 x22: 000000000000000a x21: 0000000000000000
[28031.304374] x20: ffff000008774600 x19: 00001643fef8ae04 x18: 0000000000000000
[28031.304556] x17: ffff80007e099000 x16: ffff8000818e8000 x15: 0000000000000000
[28031.304739] x14: 0000000000000004 x13: ffff0000ff6187a8 x12: 0000000000000002
[28031.304922] x11: ffff0000c2288a18 x10: 0000000000000018 x9 : 0000000000000000
[28031.305104] x8 : 00000000000008ff x7 : 7fffffffffffffff x6 : 00003d0900007d00
[28031.305285] x5 : ffff800081874120 x4 : 0000000000000008 x3 : ffff80008a153710
[28031.305470] x2 : 00000000000404d4 x1 : 00000000000138ce x0 : ffff8000818ae3c8
[28031.305651] Call trace:
[28031.305714]  queued_read_lock_slowpath+0x80/0x14c (P)
[28031.305844]  _raw_read_lock_bh+0x64/0x68
[28031.305945]  lua_lsm_cred_free+0x54/0x2bc
[28031.306048]  security_cred_free+0x5c/0x90
[28031.306151]  put_cred_rcu+0x28/0x15c
[28031.306244]  rcu_core+0x2c0/0x5e0
[28031.306330]  rcu_core_si+0x10/0x1c
[28031.306418]  handle_softirqs+0xdc/0x200
[28031.306517]  __do_softirq+0x14/0x20
[28031.306686]  ____do_softirq+0x10/0x1c
[28031.306782]  call_on_irq_stack+0x30/0x48
[28031.306884]  do_softirq_own_stack+0x1c/0x28
[28031.306993]  __irq_exit_rcu+0x54/0xf8
[28031.307087]  irq_exit_rcu+0x10/0x1c
[28031.307176]  el1_interrupt+0x38/0x54
[28031.307269]  el1h_64_irq_handler+0x18/0x24
[28031.307375]  el1h_64_irq+0x6c/0x70
[28031.307463]  lua_module_register+0x550/0x588 (P)
[28031.307582]  register_write+0x78/0xac
[28031.307676]  vfs_write+0x160/0x3bc
[28031.307764]  ksys_write+0x70/0xe4
[28031.307849]  __arm64_sys_write+0x1c/0x28
[28031.307950]  invoke_syscall+0x40/0xf8
[28031.308044]  el0_svc_common+0xa8/0xd8
[28031.308138]  do_el0_svc+0x1c/0x28
[28031.308225]  el0_svc+0x38/0x8c
[28031.308311]  el0t_64_sync_handler+0x84/0x12c
[28031.308422]  el0t_64_sync+0x198/0x19c
When interrupts are disabled, file system operations triggered by memory
allocation may cause a potential deadlock.

The log is as follows:

[   37.041540] =====================================================
[   37.041790] WARNING: SOFTIRQ-safe -> SOFTIRQ-unsafe lock order detected
[   37.042170] 6.17.0-rc3+ #515 Tainted: G        W
[   37.042492] -----------------------------------------------------
[   37.042953] cat/1610 [HC0[0]:SC0[2]:HE1:SE0] is trying to acquire:
[   37.043316] ffff800081f2b9f8 (fs_reclaim){+.+.}-{0:0}, at: __kmalloc_noprof+0xb0/0x4d4
[   37.043783]
[   37.043783] and this task is already holding:
[   37.044121] ffff0000c177e138 (&dict->lock){+.-.}-{3:3}, at: kvcache_incr+0x68/0x21c
[   37.044569] which would create a new lock dependency:
[   37.044861]  (&dict->lock){+.-.}-{3:3} -> (fs_reclaim){+.+.}-{0:0}
[   37.045222]
[   37.045222] but this new dependency connects a SOFTIRQ-irq-safe lock:
[   37.045685]  (&dict->lock){+.-.}-{3:3}
[   37.045687]
[   37.045687] ... which became SOFTIRQ-irq-safe at:
[   37.046273]   lock_acquire+0x118/0x26c
[   37.046479]   _raw_write_lock+0x4c/0x88
[   37.046736]   kvcache_dict_free+0x24/0x104
[   37.046950]   lua_lsm_inode_free_security_rcu+0x1d4/0x274
[   37.047231]   inode_free_by_rcu+0x54/0x9c
[   37.047442]   rcu_core+0x474/0xa50
[   37.047623]   rcu_core_si+0x10/0x1c
[   37.047805]   handle_softirqs+0x178/0x42c
[   37.048018]   __do_softirq+0x14/0x20
[   37.048205]   ____do_softirq+0x10/0x1c
[   37.048404]   call_on_irq_stack+0x30/0x48
[   37.048614]   do_softirq_own_stack+0x1c/0x28
[   37.048838]   __irq_exit_rcu+0xd4/0x194
[   37.049039]   irq_exit_rcu+0x10/0x34
[   37.049226]   el1_interrupt+0x38/0x54
[   37.049418]   el1h_64_irq_handler+0x18/0x24
[   37.049636]   el1h_64_irq+0x6c/0x70
[   37.049818]   do_idle+0xe8/0x26c
[   37.049994]   cpu_startup_entry+0x34/0x38
[   37.050206]   kernel_init+0x0/0x128
[   37.050389]   start_kernel+0x304/0x3c0
[   37.050659]   __primary_switched+0x88/0x90
[   37.050881]
[   37.050881] to a SOFTIRQ-irq-unsafe lock:
[   37.051169]  (fs_reclaim){+.+.}-{0:0}
[   37.051171]
[   37.051171] ... which became SOFTIRQ-irq-unsafe at:
[   37.051699] ...
[   37.051704]   lock_acquire+0x118/0x26c
[   37.051997]   fs_reclaim_acquire+0x64/0xd0
[   37.052215]   mem_cgroup_css_alloc+0xdc/0x6e4
[   37.052436]   cgroup_init_subsys+0x7c/0x1d0
[   37.052642]   cgroup_init+0x2d8/0x490
[   37.052822]   start_kernel+0x2f4/0x3c0
[   37.053006]   __primary_switched+0x88/0x90
[   37.053210]
[   37.053210] other info that might help us debug this:
[   37.053210]
[   37.053598]  Possible interrupt unsafe locking scenario:
[   37.053598]
[   37.053929]        CPU0                    CPU1
[   37.054152]        ----                    ----
[   37.054372]   lock(fs_reclaim);
[   37.054649]                                local_irq_disable();
[   37.055469]                                lock(&dict->lock);
[   37.055751]                                lock(fs_reclaim);
[   37.056024]   <Interrupt>
[   37.056151]     lock(&dict->lock);
[   37.056316]
[   37.056316]  *** DEADLOCK ***
[   37.056316]
[   37.056602] 2 locks held by cat/1610:
[   37.056779]  #0: ffff800081fdaf18 (modules_lock){++.-}-{3:3}, at: lua_lsm_file_permission+0x58/0x36c
[   37.057221]  openanolis#1: ffff0000c177e138 (&dict->lock){+.-.}-{3:3}, at: kvcache_incr+0x68/0x21c
[   37.057611]
[   37.057611] the dependencies between SOFTIRQ-irq-safe lock and the holding lock:
[   37.058091] -> (&dict->lock){+.-.}-{3:3} {
[   37.058303]    HARDIRQ-ON-W at:
[   37.058495]                     lock_acquire+0x118/0x26c
[   37.058777]                     _raw_write_lock+0x4c/0x88
[   37.059053]                     kvcache_dict_free+0x24/0x104
[   37.059353]                     lua_lsm_file_free_security+0x210/0x2bc
[   37.059691]                     security_file_free+0x5c/0xa4
[   37.059991]                     __fput+0x1a0/0x2f0
[   37.060242]                     delayed_fput+0x44/0x58
[   37.060514]                     process_one_work+0x210/0x548
[   37.060815]                     worker_thread+0x244/0x380
[   37.061096]                     kthread+0x138/0x260
[   37.061348]                     ret_from_fork+0x10/0x20
[   37.061622]    IN-SOFTIRQ-W at:
[   37.061790]                     lock_acquire+0x118/0x26c
[   37.062117]                     _raw_write_lock+0x4c/0x88
[   37.062405]                     kvcache_dict_free+0x24/0x104
[   37.062730]                     lua_lsm_inode_free_security_rcu+0x1d4/0x274
[   37.063097]                     inode_free_by_rcu+0x54/0x9c
[   37.063390]                     rcu_core+0x474/0xa50
[   37.063652]                     rcu_core_si+0x10/0x1c
[   37.063917]                     handle_softirqs+0x178/0x42c
[   37.064213]                     __do_softirq+0x14/0x20
[   37.064475]                     ____do_softirq+0x10/0x1c
[   37.064739]                     call_on_irq_stack+0x30/0x48
[   37.065014]                     do_softirq_own_stack+0x1c/0x28
[   37.065307]                     __irq_exit_rcu+0xd4/0x194
[   37.065583]                     irq_exit_rcu+0x10/0x34
[   37.065901]                     el1_interrupt+0x38/0x54
[   37.066155]                     el1h_64_irq_handler+0x18/0x24
[   37.066399]                     el1h_64_irq+0x6c/0x70
[   37.066660]                     do_idle+0xe8/0x26c
[   37.066891]                     cpu_startup_entry+0x34/0x38
[   37.067168]                     kernel_init+0x0/0x128
[   37.067418]                     start_kernel+0x304/0x3c0
[   37.067679]                     __primary_switched+0x88/0x90
[   37.067959]    INITIAL USE at:
[   37.068115]                    lock_acquire+0x118/0x26c
[   37.068375]                    _raw_write_lock+0x4c/0x88
[   37.068637]                    kvcache_dict_free+0x24/0x104
[   37.068917]                    lua_lsm_file_free_security+0x210/0x2bc
[   37.069222]                    security_file_free+0x5c/0xa4
[   37.069499]                    __fput+0x1a0/0x2f0
[   37.069726]                    delayed_fput+0x44/0x58
[   37.070043]                    process_one_work+0x210/0x548
[   37.070282]                    worker_thread+0x244/0x380
[   37.070499]                    kthread+0x138/0x260
[   37.070713]                    ret_from_fork+0x10/0x20
[   37.070923]  }
[   37.070992]  ... key      at: [<ffff800082ce7398>] kvcache_dict_init.__key+0x0/0x10
[   37.071308]
[   37.071308] the dependencies between the lock to be acquired
[   37.071309]  and SOFTIRQ-irq-unsafe lock:
[   37.071759] -> (fs_reclaim){+.+.}-{0:0} {
[   37.071923]    HARDIRQ-ON-W at:
[   37.072052]                     lock_acquire+0x118/0x26c
[   37.072268]                     fs_reclaim_acquire+0x64/0xd0
[   37.072499]                     mem_cgroup_css_alloc+0xdc/0x6e4
[   37.072740]                     cgroup_init_subsys+0x7c/0x1d0
[   37.072974]                     cgroup_init+0x2d8/0x490
[   37.073187]                     start_kernel+0x2f4/0x3c0
[   37.073403]                     __primary_switched+0x88/0x90
[   37.073633]    SOFTIRQ-ON-W at:
[   37.073761]                     lock_acquire+0x118/0x26c
[   37.074010]                     fs_reclaim_acquire+0x64/0xd0
[   37.074241]                     mem_cgroup_css_alloc+0xdc/0x6e4
[   37.074482]                     cgroup_init_subsys+0x7c/0x1d0
[   37.074735]                     cgroup_init+0x2d8/0x490
[   37.074947]                     start_kernel+0x2f4/0x3c0
[   37.075160]                     __primary_switched+0x88/0x90
[   37.075388]    INITIAL USE at:
[   37.075512]                    lock_acquire+0x118/0x26c
[   37.075722]                    fs_reclaim_acquire+0x64/0xd0
[   37.075944]                    mem_cgroup_css_alloc+0xdc/0x6e4
[   37.076169]                    cgroup_init_subsys+0x7c/0x1d0
[   37.076399]                    cgroup_init+0x2d8/0x490
[   37.076598]                    start_kernel+0x2f4/0x3c0
[   37.076798]                    __primary_switched+0x88/0x90
[   37.077012]  }
[   37.077078]  ... key      at: [<ffff800081f2b9f8>] __fs_reclaim_map+0x0/0x30
[   37.077349]  ... acquired at:
[   37.077464]    fs_reclaim_acquire+0x64/0xd0
[   37.077628]    __kmalloc_noprof+0xb0/0x4d4
[   37.077787]    kvcache_incr+0xe0/0x21c
[   37.077967]    shdict_incr+0x34/0x44
[   37.078108]    luaD_precall+0x330/0x674
[   37.078259]    luaV_execute+0xaac/0x119c
[   37.078413]    luaD_call+0xb0/0x128
[   37.078550]    f_call+0x1c/0x28
[   37.078692]    luaD_rawrunprotected+0x7c/0xb8
[   37.078862]    luaD_pcall+0x40/0x184
[   37.079003]    lua_pcall+0xac/0x190
[   37.079191]    lua_lsm_file_permission+0x1fc/0x36c
[   37.079379]    security_file_permission+0x4c/0xa8
[   37.079562]    rw_verify_area+0x54/0x138
[   37.079715]    vfs_read+0xa4/0x2b0
[   37.079846]    ksys_read+0x70/0xe4
[   37.079974]    __arm64_sys_read+0x1c/0x28
[   37.080123]    invoke_syscall+0x40/0xf8
[   37.080269]    el0_svc_common+0xa8/0xd8
[   37.080413]    do_el0_svc+0x1c/0x28
[   37.080544]    el0_svc+0x50/0xcc
[   37.080665]    el0t_64_sync_handler+0x84/0x12c
[   37.080831]    el0t_64_sync+0x198/0x19c
[   37.080975]
[   37.081031]
[   37.081031] stack backtrace:
[   37.081195] CPU: 1 UID: 0 PID: 1610 Comm: cat Tainted: G        W           6.17.0-rc3+ #515 PREEMPT
[   37.081535] Tainted: [W]=WARN
[   37.081646] Hardware name: QEMU QEMU Virtual Machine, BIOS edk2-stable202408-prebuilt.qemu.org 08/13/2024
[   37.082039] Call trace:
[   37.082130]  show_stack+0x18/0x24 (C)
[   37.082269]  __dump_stack+0x28/0x38
[   37.082400]  dump_stack_lvl+0x64/0x84
[   37.082537]  dump_stack+0x18/0x24
[   37.082688]  __lock_acquire+0x2b2c/0x2b90
[   37.082837]  lock_acquire+0x118/0x26c
[   37.082972]  fs_reclaim_acquire+0x64/0xd0
[   37.083119]  __kmalloc_noprof+0xb0/0x4d4
[   37.083264]  kvcache_incr+0xe0/0x21c
[   37.083395]  shdict_incr+0x34/0x44
[   37.083520]  luaD_precall+0x330/0x674
[   37.083656]  luaV_execute+0xaac/0x119c
[   37.083793]  luaD_call+0xb0/0x128
[   37.083916]  f_call+0x1c/0x28
[   37.084025]  luaD_rawrunprotected+0x7c/0xb8
[   37.084180]  luaD_pcall+0x40/0x184
[   37.084306]  lua_pcall+0xac/0x190
[   37.084428]  lua_lsm_file_permission+0x1fc/0x36c
[   37.084598]  security_file_permission+0x4c/0xa8
[   37.084765]  rw_verify_area+0x54/0x138
[   37.084902]  vfs_read+0xa4/0x2b0
[   37.085021]  ksys_read+0x70/0xe4
[   37.085139]  __arm64_sys_read+0x1c/0x28
[   37.085280]  invoke_syscall+0x40/0xf8
[   37.085416]  el0_svc_common+0xa8/0xd8
[   37.085553]  do_el0_svc+0x1c/0x28
[   37.085676]  el0_svc+0x50/0xcc
[   37.085790]  el0t_64_sync_handler+0x84/0x12c
[   37.086018]  el0t_64_sync+0x198/0x19c
Lua-LSM left the id arguments for kernel_load_data(),
kernel_post_load_data(), kernel_read_file(), and kernel_post_read_file()
as nil placeholders.

Expose those ids as the stable strings returned by
kernel_load_data_id_str() and kernel_read_file_id_str(). Lua policies
can compare descriptive values such as "kernel-module" or
"security-policy" without depending on kernel enum numbers.

Signed-off-by: Zongyao Chen <ZongYao.Chen@linux.alibaba.com>
kernel_post_load_data() and kernel_post_read_file() expose file data to
Lua as binary-safe strings. Lua policy can get the byte length with the
Lua length operator, so passing a separate size argument duplicates
information already carried by the string.

Drop the Lua-visible size argument from those hooks while keeping the
kernel hook signatures unchanged.

Signed-off-by: Zongyao Chen <ZongYao.Chen@linux.alibaba.com>
Signed-off-by: Zongyao Chen <ZongYao.Chen@linux.alibaba.com>
Signed-off-by: Zongyao Chen <ZongYao.Chen@linux.alibaba.com>
Signed-off-by: Zongyao Chen <ZongYao.Chen@linux.alibaba.com>
Signed-off-by: Zongyao Chen <ZongYao.Chen@linux.alibaba.com>
Lazy VM allocation failure is a dispatcher error, not a Lua hook
result. Return it through the generated dispatcher status and map it
at the LSM wrapper boundary.

Keep errno-style hooks failing with -ENOMEM. Add boolean result classes
for predicate hooks and bool-or-errno hooks so Lua true/false are
translated to each hook's native 1/0 semantics instead of
permission-style 0/-EPERM.

Stop Lua module dispatch when a result differs from the LSM default,
matching call_int_hook().

Signed-off-by: Zongyao Chen <ZongYao.Chen@linux.alibaba.com>
security_xfrm_state_pol_flow_match() is not a normal stacking hook. The
hook default return value is 1, and the security core stops after the
first registered implementation because the result is a boolean match
decision.

Lua-LSM currently generates a generic wrapper for the hook. If that
wrapper is registered before SELinux, the XFRM lookup path can use the
Lua-LSM result and never ask SELinux whether the state, policy, and flow
labels match. A dispatch failure also falls back to the default value,
which reports a match.

Keep the hook out of lua_lsm_hook_supported() until Lua-LSM can provide
semantics that do not shadow the provider that owns the XFRM decision.
This hides it from registration, module introspection, and module load
validation.

Signed-off-by: Zongyao Chen <ZongYao.Chen@linux.alibaba.com>
The lazy per-task VM path published lvm->L before moving the VM
allocator userdata from the temporary build owner to the target task
lvm.

Unregister can observe any non-NULL lvm->L through task_call_func().
That window let it treat a half-initialized VM as ready and race with
the first hook dispatch.

Take the task VM refcount before first-use construction, move the
allocator owner and stats before publication, and publish lvm->L only
after the VM is ready.

The unregister path now either sees an untouched VM or fails the
idle-required get while the first dispatch owns the VM.

Signed-off-by: Zongyao Chen <ZongYao.Chen@linux.alibaba.com>
Keep the per-task Lua VM pointer NULL until hook dispatch needs it.
This avoids VM allocation from task allocation and free paths when no
Lua policy is active. Module unregister and task free still handle
already-published states.

Signed-off-by: Zongyao Chen <ZongYao.Chen@linux.alibaba.com>
Leave object kvcache dictionaries uninitialized until Lua writes need
storage. Reads and frees treat the zero state as empty.
This reduces no-policy allocation work without changing object cleanup
semantics.

Signed-off-by: Zongyao Chen <ZongYao.Chen@linux.alibaba.com>
Bypass stats, SRCU, VM preparation, and Lua dispatch when a hook has
no active handlers. Keep the inactive cleanup allowlist for free hooks
so postponed object teardown still runs for state created while policy
was active.

Signed-off-by: Zongyao Chen <ZongYao.Chen@linux.alibaba.com>
Skip inactive cleanup before any Lua policy has been registered.
Once a policy is accepted, keep the key enabled permanently so objects
that outlive module unload still run free-hook cleanup.

Signed-off-by: Zongyao Chen <ZongYao.Chen@linux.alibaba.com>
Lazy kvcache initialization leaves a dictionary in INITING until
__kvcache_dict_init() finishes. A same-CPU softirq hook can reenter the
Lua path in that window, observe INITING for the current task's
dictionary, and spin in kvcache_dict_ready() until the interrupted task
context runs again.

Disable bottom halves around the UNINIT -> INITING -> READY publication
window so same-CPU softirq reentry cannot observe the transient state.

While here, rename the inactive-cleanup static key from *_possible to
*_armed to better match its one-way semantics after the first policy
load.

Signed-off-by: Zongyao Chen <ZongYao.Chen@linux.alibaba.com>
Signed-off-by: Zongyao Chen <ZongYao.Chen@linux.alibaba.com>
lua_lsm_hook_supported() only excluded the procattr and xfrm hooks, so
lua-LSM still registered the key, key-notification, perf_event, tun_dev
and infiniband hooks. Those operate on object classes for which lua-LSM
reserves no security blob (lbs_key, lbs_perf_event, lbs_tun_dev and
lbs_ib are all 0), so a policy can neither attach nor manage state on
them and the hooks were never usefully supported.

Add them to the exclusion list so lua-LSM is not registered on hooks it
cannot back, trimming its footprint on those paths.

Signed-off-by: Zongyao Chen <ZongYao.Chen@linux.alibaba.com>
The *_alloc_security prepare hooks ran kvcache_dict_init() on every LSM
object blob at allocation time. Those blobs are kzalloc'd, and kvcache
promotes the embedded dict from the zero UNINIT state to READY lazily on
first use, so the eager init is redundant work on a hot allocation path
(every inode, file, cred, ipc and sock).

Drop the eager initializers and document that KVCACHE_DICT_UNINIT must
stay the zero value, so a zero-filled blob is valid without them.

Signed-off-by: Zongyao Chen <ZongYao.Chen@linux.alibaba.com>
Each lua_lsm hook was a single function: a fast "no policy loaded" early
return followed by the srcu walk and VM dispatch. The slow part takes
the address of a local (the return slot passed to __prepare_) and uses
callee-saved registers, so the compiler gave the whole function a stack
frame and a -fstack-protector-strong canary. Even with no policy loaded,
every guarded syscall then executed that prologue and two %gs canary
accesses just to reach the early return, whereas an empty hook such as
bpf_lsm's compiles to a bare "xor %eax, %eax; ret".

Split each hook into a thin wrapper and a noinline slow path. When no
policy is loaded both static branches are patched to nops, so the
wrapper takes no local's address and needs no callee-saved registers:
the compiler emits neither a stack frame nor a canary, and the idle path
collapses to two nops and a constant return, matching the empty hook.
The srcu walk, VM dispatch and inactive object cleanup move into the
slow path, entered only once a policy is live.

perf confirms the dormant stub then carries no frame or canary; the
measurable gain is a few ns per guarded syscall on file-permission-heavy
workloads. The residual per-hook cost is the LSM dispatch call itself,
which this change does not touch.

Signed-off-by: Zongyao Chen <ZongYao.Chen@linux.alibaba.com>
Commit "lua-lsm: drop eager kvcache dict init" removed the eager
kvcache_dict_init() from the *_alloc_security prepare hooks but missed
task_blob_init(), which still initialized the task blob's dict on every
task_alloc.

The task blob is kzalloc'd like the other LSM object blobs, so its
embedded dict starts in the zero UNINIT state and is promoted to READY
lazily on first use; the eager init is the same redundant work. Drop it
from task_blob_init() too.

Signed-off-by: Zongyao Chen <ZongYao.Chen@linux.alibaba.com>
A policy can only report an event through kernel.printk(), whose
free-form text auditd can neither parse into fields nor correlate with
the syscall that triggered it, so policy decisions stay invisible to the
audit trail that records every other security event.

Add an audit library that emits one AUDIT_LUA record per call from a
table of fields. A field value commonly carries attacker-influenced data
such as a path name, so values are escaped rather than trusted.

Signed-off-by: Zongyao Chen <ZongYao.Chen@linux.alibaba.com>
A netlink or packet policy has to look at message contents to make a
decision, but the skb object only exposed metadata, so such policies
could not be written at all. Expose the payload, and the raw protocol
number that suites() cannot name, so content-aware policies become
possible.

Signed-off-by: Zongyao Chen <ZongYao.Chen@linux.alibaba.com>
So policy authors can find the accessors and know a bad read yields nil
rather than an error, keeping the decision in the policy's hands.

Signed-off-by: Zongyao Chen <ZongYao.Chen@linux.alibaba.com>
On handshake and time-wait paths skb->sk is a request or time-wait sock,
which stops short of the fields sock:proto() and sock:suites() read. A
policy asking for the protocol number therefore got neighbouring slab
bytes, and since those pass for a valid number the rule they feed fails
open rather than erroring.

Resolve skb->sk through skb_to_full_sk() so a sock handed to a policy is
always a full sock, making the guarantee a property of the object rather
than of each accessor. skb:full_sk() then duplicates skb:sock() and has
no users, so drop it.

Signed-off-by: Zongyao Chen <ZongYao.Chen@linux.alibaba.com>
A policy author cannot tell from the method name that a half-open or
time-wait connection resolves to the listener or to nil, and would read
the nil as "no owning socket" instead of "not a full sock".

Signed-off-by: Zongyao Chen <ZongYao.Chen@linux.alibaba.com>
The helper is local to lua_net.c, but skb_ is netcore's prefix and the
function sits two lines from skb_copy_bits() and skb_to_full_sk(), so
nothing tells a reader it is not a core networking helper. Every other
static function in the file carries the module prefix.

Signed-off-by: Zongyao Chen <ZongYao.Chen@linux.alibaba.com>
skb:len() was documented as "payload length", but it measures the window
starting at skb->data, and each layer advances that pointer past its own
header, so at most hooks the window still covers a header. A policy
author trusting the word "payload" computes every skb:read() offset from
the wrong base, and an out-of-range read yields nil rather than an error,
so the mistake never surfaces.

Define the window once for both accessors and record where it starts in
each hook that passes an skb, since that is the only thing an offset can
be computed from.

Signed-off-by: Zongyao Chen <ZongYao.Chen@linux.alibaba.com>
@chenzongyao200127 chenzongyao200127 changed the title lua-lsm: add skb payload accessors lua-lsm: add skb payload accessors and hand out only full socks Aug 6, 2026
uudiin pushed a commit that referenced this pull request Aug 21, 2026
…g FLR

During Function Level Reset recovery, the MANA driver reads
hardware BAR0 registers that may temporarily contain garbage values.
The SHM (Shared Memory) offset read from GDMA_REG_SHM_OFFSET is used
to compute gc->shm_base, which is later dereferenced via readl() in
mana_smc_poll_register(). If the hardware returns an unaligned or
out-of-range value, the driver must not blindly use it, as this would
propagate the hardware error into a kernel crash.

The following crash was observed on an arm64 Hyper-V guest running
kernel 6.17.0-3013-azure during VF reset recovery triggered by HWC
timeout.

[13291.785274] Unable to handle kernel paging request at virtual address ffff8000a200001b
[13291.785311] Mem abort info:
[13291.785332]   ESR = 0x0000000096000021
[13291.785343]   EC = 0x25: DABT (current EL), IL = 32 bits
[13291.785355]   SET = 0, FnV = 0
[13291.785363]   EA = 0, S1PTW = 0
[13291.785372]   FSC = 0x21: alignment fault
[13291.785382] Data abort info:
[13291.785391]   ISV = 0, ISS = 0x00000021, ISS2 = 0x00000000
[13291.785404]   CM = 0, WnR = 0, TnD = 0, TagAccess = 0
[13291.785412]   GCS = 0, Overlay = 0, DirtyBit = 0, Xs = 0
[13291.785421] swapper pgtable: 4k pages, 48-bit VAs, pgdp=00000014df3a1000
[13291.785432] [ffff8000a200001b] pgd=1000000100438403, p4d=1000000100438403, pud=1000000100439403, pmd=0068000fc2000711
[13291.785703] Internal error: Oops: 0000000096000021 [#1]  SMP
[13291.830975] Modules linked in: tls qrtr mana_ib ib_uverbs ib_core xt_owner xt_tcpudp xt_conntrack nf_conntrack nf_defrag_ipv6 nf_defrag_ipv4 nft_compat nf_tables cfg80211 8021q garp mrp stp llc binfmt_misc joydev serio_raw nls_iso8859_1 hid_generic aes_ce_blk aes_ce_cipher polyval_ce ghash_ce sm4_ce_gcm sm4_ce_ccm sm4_ce sm4_ce_cipher hid_hyperv sm4 sm3_ce sha3_ce hv_netvsc hid vmgenid hyperv_keyboard hyperv_drm sch_fq_codel nvme_fabrics efi_pstore dm_multipath nfnetlink vsock_loopback vmw_vsock_virtio_transport_common hv_sock vmw_vsock_vmci_transport vmw_vmci vsock dmi_sysfs ip_tables x_tables autofs4
[13291.862630] CPU: 122 UID: 0 PID: 61796 Comm: kworker/122:2 Tainted: G        W           6.17.0-3013-azure #13-Ubuntu VOLUNTARY
[13291.869902] Tainted: [W]=WARN
[13291.871901] Hardware name: Microsoft Corporation Virtual Machine/Virtual Machine, BIOS Hyper-V UEFI Release v4.1 01/08/2026
[13291.878086] Workqueue: events mana_serv_func
[13291.880718] pstate: 62400005 (nZCv daif +PAN -UAO +TCO -DIT -SSBS BTYPE=--)
[13291.884835] pc : mana_smc_poll_register+0x48/0xb0
[13291.887902] lr : mana_smc_setup_hwc+0x70/0x1c0
[13291.890493] sp : ffff8000ab79bbb0
[13291.892364] x29: ffff8000ab79bbb0 x28: ffff00410c8b5900 x27: ffff00410d630680
[13291.896252] x26: ffff004171f9fd80 x25: 000000016ed55000 x24: 000000017f37e000
[13291.899990] x23: 0000000000000000 x22: 000000016ed55000 x21: 0000000000000000
[13291.904497] x20: ffff8000a200001b x19: 0000000000004e20 x18: ffff8000a6183050
[13291.908308] x17: 0000000000000000 x16: 0000000000000000 x15: 000000000000000a
[13291.912542] x14: 0000000000000004 x13: 0000000000000000 x12: 0000000000000000
[13291.916298] x11: 0000000000000000 x10: 0000000000000001 x9 : ffffc45006af1bd8
[13291.920945] x8 : ffff000151129000 x7 : 0000000000000000 x6 : 0000000000000000
[13291.925293] x5 : 000000015f214000 x4 : 000000017217a000 x3 : 000000016ed50000
[13291.930436] x2 : 000000016ed55000 x1 : 0000000000000000 x0 : ffff8000a1ffffff
[13291.934342] Call trace:
[13291.935736]  mana_smc_poll_register+0x48/0xb0 (P)
[13291.938611]  mana_smc_setup_hwc+0x70/0x1c0
[13291.941113]  mana_hwc_create_channel+0x1a0/0x3a0
[13291.944283]  mana_gd_setup+0x16c/0x398
[13291.946584]  mana_gd_resume+0x24/0x70
[13291.948917]  mana_do_service+0x13c/0x1d0
[13291.951583]  mana_serv_func+0x34/0x68
[13291.953732]  process_one_work+0x168/0x3d0
[13291.956745]  worker_thread+0x2ac/0x480
[13291.959104]  kthread+0xf8/0x110
[13291.961026]  ret_from_fork+0x10/0x20
[13291.963560] Code: d2807d00 9417c551 71000673 54000220 (b9400281)
[13291.967299] ---[ end trace 0000000000000000 ]---

Disassembly of mana_smc_poll_register() around the crash site:

Disassembly of section .text:

00000000000047c8 <mana_smc_poll_register>:
    47c8: d503201f        nop
    47cc: d503201f        nop
    47d0: d503233f        paciasp
    47d4: f800865e        str     x30, [x18], #8
    47d8: a9bd7bfd        stp     x29, x30, [sp, #-48]!
    47dc: 910003fd        mov     x29, sp
    47e0: a90153f3        stp     x19, x20, [sp, #16]
    47e4: 91007014        add     x20, x0, #0x1c
    47e8: 5289c413        mov     w19, #0x4e20
    47ec: f90013f5        str     x21, [sp, #32]
    47f0: 12001c35        and     w21, w1, #0xff
    47f4: 14000008        b       4814 <mana_smc_poll_register+0x4c>
    47f8: 36f801e1  tbz  w1, #31, 4834 <mana_smc_poll_register+0x6c>
    47fc: 52800042        mov     w2, #0x2
    4800: d280fa01        mov     x1, #0x7d0
    4804: d2807d00        mov     x0, #0x3e8
    4808: 94000000        bl      0 <usleep_range_state>
    480c: 71000673        subs    w19, w19, #0x1
    4810: 54000200        b.eq    4850 <mana_smc_poll_register+0x88>
    4814: b9400281      ldr   w1, [x20] <-- **** CRASHED HERE *****
    4818: d50331bf        dmb     oshld
    481c: 2a0103e2        mov     w2, w1
    ...

From the crash signature x20 = ffff8000a200001b, this address
ends in 0x1b which is not 4-byte aligned, so the 'ldr w1, [x20]'
instruction (readl) triggers the arm64 alignment fault (FSC = 0x21).

The root cause is in mana_gd_init_vf_regs(), which computes:

  gc->shm_base = gc->bar0_va + mana_gd_r64(gc, GDMA_REG_SHM_OFFSET);

The offset is used without any validation.  The same problem exists
in mana_gd_init_pf_regs() for sriov_base_off and sriov_shm_off.

Fix this by validating all offsets before use:

- VF: check shm_off is within BAR0, properly aligned to 4 bytes
  (readl requirement), and leaves room for the full 256-bit
  (32-byte) SMC aperture.

- PF: check sriov_base_off is within BAR0, aligned to 8 bytes
  (readq requirement), and leaves room to safely read the
  sriov_shm_off register at sriov_base_off + GDMA_PF_REG_SHM_OFF.
  Then check sriov_shm_off leaves room for the full SMC aperture.
  All arithmetic uses subtraction rather than addition to avoid
  integer overflow on garbage values.

Define SMC_APERTURE_SIZE (32 bytes, derived from the 256-bit aperture
width)

Return -EPROTO on invalid values.  The existing recovery path in
mana_serv_reset() already handles -EPROTO by falling through to PCI
device rescan, giving the hardware another chance to present valid
register values after reset.

Fixes: 9bf6603 ("net: mana: Handle hardware recovery events when probing the device")
Signed-off-by: Dipayaan Roy <dipayanroy@linux.microsoft.com>
Link: https://patch.msgid.link/afQUMClyjmBVfD+u@linuxonhyperv3.guj3yctzbm1etfxqx2vob5hsef.xx.internal.cloudapp.net
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
uudiin pushed a commit that referenced this pull request Aug 21, 2026
The bitfields are designed in assumption that fields contain unsigned
integer values, thus extracting the values from the field implies
zero-extending.

Some drivers need to sign-extend their fields, and currently do it like:

	dc_re += sign_extend32(FIELD_GET(0xfff000, tmp), 11);
	dc_im += sign_extend32(FIELD_GET(0xfff, tmp), 11);

It's error-prone because it relies on user to provide the correct
index of the most significant bit and proper 32 vs 64 function flavor.

Thus, introduce a FIELD_GET_SIGNED(). With the new API, the above
snippet turns into the more convenient:

	dc_re += FIELD_GET_SIGNED(0xfff000, tmp);
	dc_im += FIELD_GET_SIGNED(0xfff, tmp);

It compiles (on x86_64) into just a couple instructions: shl and sar.
When the mask includes MSB, the '<< __builtin_clzll(mask)' part becomes
a NOP, and the compiler only emits a single sar:

   long long foo(long long reg)
  {
    10:   f3 0f 1e fa             endbr64
          return FIELD_GET_SIGNED(GENMASK_ULL(63, 60), reg);
    14:   48 89 f8                mov    %rdi,%rax
    17:   48 c1 f8 3c             sar    $0x3c,%rax
  }

32-bit code generation is equally well. On arm32:

  long long foo(long long reg)
  {
         return FIELD_GET_SIGNED(0x00f00000ULL, reg);
  }

generates:

  foo(long long):
        lsls    r1, r0, #8
        asrs    r0, r1, #28
        asrs    r1, r1, #31
        bx      lr

Signed-off-by: Yury Norov <ynorov@nvidia.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants