Skip to content

lua-lsm: fix unregister and module lifetime handling - #16

Draft
chenzongyao200127 wants to merge 138 commits into
openanolis:lua-lsmfrom
chenzongyao200127:lua-lsm-unregister-lifetime-fixes
Draft

lua-lsm: fix unregister and module lifetime handling#16
chenzongyao200127 wants to merge 138 commits into
openanolis:lua-lsmfrom
chenzongyao200127:lua-lsm-unregister-lifetime-fixes

Conversation

@chenzongyao200127

Copy link
Copy Markdown
Collaborator

Lua-LSM module unregister has to coordinate several lifetimes at once: the global module list protected by modules_ss SRCU, per-VM loaded module references, shared dictionaries cached in Lua userdata, and the contexts where VM teardown is allowed to run.

This series keeps those pieces ordered so unregister no longer frees state that can still be reached from an existing Lua VM, and so lazy unload can finish modules once the last VM-local reference is gone.

The series contains these commits:

  • lua-lsm: wait for SRCU readers before freeing module
  • lua-lsm: fix shared dict lifetime on unregister
  • lua-lsm: avoid leaking shared dict refs on userdata failure
  • lua-lsm: avoid Lua teardown from task_call_func
  • lua-lsm: finalize drained modules after lazy unload

Main fixes:

  • Wait for modules_ss SRCU readers before freeing an unregistered module.
  • Keep shared dictionaries refcounted while Lua userdata can still access them.
  • Tombstone shared dictionaries at unregister so new lookups fail cleanly.
  • Avoid taking shared-dict references before userdata exists to release them.
  • Stop running Lua VM teardown through task_call_func(), which can execute under task/runqueue locks.
  • Let non-current task VMs purge unloading modules lazily on their next Lua-LSM entry.
  • Queue final teardown for zombie modules once lazy purge drains nloaded to zero.

Please use Rebase and merge, or another merge mode that preserves the individual commits. Please do not squash this PR, since each commit has its own kernel-style subject and message.

Validation:

  • ./scripts/checkpatch.pl --git origin/lua-lsm..lua-lsm-unregister-lifetime-fixes
  • git diff --check origin/lua-lsm..lua-lsm-unregister-lifetime-fixes

Signed-off-by: Zongyao Chen ZongYao.Chen@linux.alibaba.com

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
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>
@chenzongyao200127
chenzongyao200127 force-pushed the lua-lsm-unregister-lifetime-fixes branch from 3b3aa86 to b752d84 Compare July 9, 2026 07:11
@chenzongyao200127
chenzongyao200127 marked this pull request as draft July 9, 2026 09:51
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>
shared[name] caches a Lua userdata that can outlive module unregister.
The userdata used to hold a raw kvcache_dict pointer, while unregister
freed the backing shared dict before every loaded VM was purged.

If unregister left the module busy, later shared-dict access could
dereference freed memory.

Store a refcounted shared-dict wrapper in the userdata, tombstone shared
dicts when unregister starts, and drop the module list reference only
once unregister can finish.

Allocate the Lua userdata slot before taking the per-userdata shared-dict
reference, so allocation failures cannot leak the wrapper ref.

Signed-off-by: Zongyao Chen <ZongYao.Chen@linux.alibaba.com>
task_call_func() may run callbacks under pi_lock and, for runnable
tasks, the runqueue lock. The unregister path used it to remove modules
from other tasks' Lua VMs, which updates Lua tables and can run a full
Lua GC.

Stop doing remote task VM teardown from task_call_func(). Unregister now
purges only the current task and per-cpu Lua VMs directly; other task
VMs purge unloading modules lazily when they next enter Lua-LSM. Modules
that still have loaded VMs remain zombie and unregister returns -EBUSY
until the remaining VMs have purged them.

Signed-off-by: Zongyao Chen <ZongYao.Chen@linux.alibaba.com>
Lazy unload can drop a module's last VM-local reference after
unregister() has already returned -EBUSY. In that case the module
used to stay on the unloading list until another unregister() retried
the final teardown.

Queue a worker when lazy purge or task teardown drains nloaded to zero.
The worker detaches the drained zombie under modules_mutex, waits for
the modules_ss SRCU grace period, and then frees the module outside
the mutex. Also skip purge attempts once nloaded is already zero and
make lua_modules_free() explicitly rely on the caller's existing SRCU
read-side protection.

Signed-off-by: Zongyao Chen <ZongYao.Chen@linux.alibaba.com>
Rely on workqueue serialization for async module finalization.

Use SRCU callbacks for delayed module free after list removal.

Rename VM-side unload helpers around dropping modules from Lua VMs.

Name the loaded VM count explicitly.

Signed-off-by: Zongyao Chen <ZongYao.Chen@linux.alibaba.com>
Unregister used to return -EBUSY whenever a deactivated module still had
loaded Lua VMs, even though the module's hooks no longer run and the
finalize worker reclaims it asynchronously once loaded_vm_count drains to
zero. Reporting an error for an operation that has, from the caller's
point of view, already succeeded (the policy is stopped) is misleading
and hard to script against.

Return 0 in that case. The module stays on the list as ZOMBIE until the
background worker frees it. Expose the module state (live/coming/going/
zombie) as a new column in /sys/kernel/security/lua/modules so the
pending cleanup is observable.

Signed-off-by: Zongyao Chen <ZongYao.Chen@linux.alibaba.com>
When lua-lsm debug logging is disabled, __log_info() expands to an empty
statement, so the loaded_vms locals in lvm_drop_module() and
lvm_put_loaded_modules() are unused and trip -Werror=unused-variable.
The lua_lsm_module_drop_from_vm() call has side effects (it decrements
loaded_vm_count and may queue the finalize worker) and must stay, so
annotate the locals with __maybe_unused.

Signed-off-by: Zongyao Chen <ZongYao.Chen@linux.alibaba.com>
@chenzongyao200127
chenzongyao200127 force-pushed the lua-lsm-unregister-lifetime-fixes branch from 097f152 to f90a7a1 Compare July 31, 2026 07:42
uudiin pushed a commit that referenced this pull request Aug 21, 2026
The xfstests' test-case generic/533 fails to execute
correctly:

FSTYP -- hfsplus
PLATFORM -- Linux/x86_64 hfsplus-testing-0001 6.15.0-rc4+ #8 SMP PREEMPT_DYNAMIC Thu May 1 16:43:22 PDT 2025
MKFS_OPTIONS -- /dev/loop51
MOUNT_OPTIONS -- /dev/loop51 /mnt/scratch

generic/533 _check_generic_filesystem: filesystem on /dev/loop50 is inconsistent
(see xfstests-dev/results//generic/533.full for details)

The key reason of the issue is returning -ENOENT error
code from hfsplus_find_attr(), __hfsplus_delete_attr(),
hfsplus_delete_attr_nolock(), hfsplus_delete_all_attrs().
The file exists but we don't have any xattr for this file.
Finally, -ENODATA error code is expected by application logic.

This patch reworks xattr logic of HFS+ by means exchanging
the -ENOENT error code on -ENODATA error code if xattr
has not been found for existing file or folder.

sudo ./check generic/533
FSTYP         -- hfsplus
PLATFORM      -- Linux/x86_64 hfsplus-testing-0001 7.0.0-rc1+ #16 SMP PREEMPT_DYNAMIC Wed Mar 11 15:04:58 PDT 2026
MKFS_OPTIONS  -- /dev/loop51
MOUNT_OPTIONS -- /dev/loop51 /mnt/scratch

generic/533 33s ...  32s
Ran: generic/533
Passed all 1 tests

Closes: hfs-linux-kernel/hfs-linux-kernel#184
cc: John Paul Adrian Glaubitz <glaubitz@physik.fu-berlin.de>
cc: Yangtao Li <frank.li@vivo.com>
cc: linux-fsdevel@vger.kernel.org
Signed-off-by: Viacheslav Dubeyko <slava@dubeyko.com>
Link: https://lore.kernel.org/r/20260312221920.1422683-2-slava@dubeyko.com
Signed-off-by: Viacheslav Dubeyko <slava@dubeyko.com>
uudiin pushed a commit that referenced this pull request Aug 21, 2026
The LLVM disassembler needs ISA extension features enabled to correctly
decode instructions from those extensions. On aarch64, without these
features, instructions like LSE atomics (e.g. ldaddal) are silently
decoded as incorrect instructions and disassembly is truncated.

Use LLVMCreateDisasmCPUFeatures() with "+all" features for aarch64
targets so that the disassembler can handle any instruction the kernel
JIT might emit.

Before:

int bench_trigger_uprobe(void * ctx):
bpf_prog_538c6a43d1c6b84c_bench_trigger_uprobe:
; int cpu = bpf_get_smp_processor_id();
   0:   mov     x9, x30
   4:   nop
   8:   stp     x29, x30, [sp, #-16]!
   c:   mov     x29, sp
  10:   stp     xzr, x26, [sp, #-16]!
  14:   mov     x26, sp
  18:   mrs     x10, SP_EL0
  1c:   ldr     w7, [x10, #16]
; __sync_add_and_fetch(&hits[cpu & CPU_MASK].value, 1);
  20:   and     w7, w7, #0xff
; __sync_add_and_fetch(&hits[cpu & CPU_MASK].value, 1);
  24:   lsl     x7, x7, #7
  28:   mov     x0, #-281474976710656
  2c:   movk    x0, #32768, lsl #32
  30:   movk    x0, #35407, lsl #16
  34:   add     x0, x0, x7
  38:   mov     x1, #1
; __sync_add_and_fetch(&hits[cpu & CPU_MASK].value, 1);
  3c:   mov     x1, #1

After:

int bench_trigger_uprobe(void * ctx):
bpf_prog_538c6a43d1c6b84c_bench_trigger_uprobe:
; int cpu = bpf_get_smp_processor_id();
   0:   mov     x9, x30
   4:   nop
   8:   stp     x29, x30, [sp, #-16]!
   c:   mov     x29, sp
  10:   stp     xzr, x26, [sp, #-16]!
  14:   mov     x26, sp
  18:   mrs     x10, SP_EL0
  1c:   ldr     w7, [x10, #16]
; __sync_add_and_fetch(&hits[cpu & CPU_MASK].value, 1);
  20:   and     w7, w7, #0xff
; __sync_add_and_fetch(&hits[cpu & CPU_MASK].value, 1);
  24:   lsl     x7, x7, #7
  28:   mov     x0, #-281474976710656
  2c:   movk    x0, #32768, lsl #32
  30:   movk    x0, #35407, lsl #16
  34:   add     x0, x0, x7
  38:   mov     x1, #1
; __sync_add_and_fetch(&hits[cpu & CPU_MASK].value, 1);
  3c:   ldaddal x1, x1, [x0]
; return 0;
  40:   mov     w7, #0
  44:   ldp     xzr, x26, [sp], #16
  48:   ldp     x29, x30, [sp], #16
  4c:   mov     x0, x7
  50:   ret
  54:   nop
  58:   ldr     x10, #8
  5c:   br      x10

Signed-off-by: Puranjay Mohan <puranjay@kernel.org>
Acked-by: Yonghong Song <yonghong.song@linux.dev>
Acked-by: Leon Hwang <leon.hwang@linux.dev>
Acked-by: Quentin Monnet <qmo@kernel.org>
Reviewed-by: Emil Tsalapatis <emil@etsalapatis.com>
Link: https://lore.kernel.org/r/20260318172259.2882792-1-puranjay@kernel.org
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
uudiin pushed a commit that referenced this pull request Aug 21, 2026
Fix the IDR allocation flags by using atomic GFP
flags in non‑sleepable contexts to avoid the __might_sleep()
complaint.

  268.290239] [drm] Initialized amdgpu 3.64.0 for 0000:03:00.0 on minor 0
[  268.294900] BUG: sleeping function called from invalid context at ./include/linux/sched/mm.h:323
[  268.295355] in_atomic(): 1, irqs_disabled(): 0, non_block: 0, pid: 1744, name: modprobe
[  268.295705] preempt_count: 1, expected: 0
[  268.295886] RCU nest depth: 0, expected: 0
[  268.296072] 2 locks held by modprobe/1744:
[  268.296077]  #0: ffff8c3a44abd1b8 (&dev->mutex){....}-{4:4}, at: __driver_attach+0xe4/0x210
[  268.296100]  #1: ffffffffc1a6ea78 (amdgpu_pasid_idr_lock){+.+.}-{3:3}, at: amdgpu_pasid_alloc+0x26/0xe0 [amdgpu]
[  268.296494] CPU: 12 UID: 0 PID: 1744 Comm: modprobe Tainted: G     U     OE       6.19.0-custom #16 PREEMPT(voluntary)
[  268.296498] Tainted: [U]=USER, [O]=OOT_MODULE, [E]=UNSIGNED_MODULE
[  268.296499] Hardware name: AMD Majolica-RN/Majolica-RN, BIOS RMJ1009A 06/13/2021
[  268.296501] Call Trace:

Fixes: 8f1de51 ("drm/amdgpu: prevent immediate PASID reuse case")
Signed-off-by: Prike Liang <Prike.Liang@amd.com>
Reviewed-by: Christian König <christian.koenig@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
uudiin pushed a commit that referenced this pull request Aug 21, 2026
Patch series "mm: cleanups around unmapping / zapping".

A bunch of cleanups around unmapping and zapping.  Mostly simplifications,
code movements, documentation and renaming of zapping functions.

With this series, we'll have the following high-level zap/unmap functions
(excluding high-level folio zapping):
* unmap_vmas() for actual unmapping (vmas will go away)
* zap_vma(): zap all page table entries in a vma
* zap_vma_for_reaping(): zap_vma() that must not block
* zap_vma_range(): zap a range of page table entries
* zap_vma_range_batched(): zap_vma_range() with more options and batching
* zap_special_vma_range(): limited zap_vma_range() for modules
* __zap_vma_range(): internal helper

Patch #1 is not about unmapping/zapping, but I stumbled over it while
verifying MADV_DONTNEED range handling.

Patch #16 is related to [1], but makes sense even independent of that.


This patch (of 16):

madvise_vma_behavior()->
madvise_dontneed_free()->madvise_free_single_vma() is only called from
madvise_walk_vmas()

(a) After try_vma_read_lock() confirmed that the whole range falls into
    a single VMA (see is_vma_lock_sufficient()).

(b) After adjusting the range to the VMA in the loop afterwards.

madvise_dontneed_free() might drop the MM lock when handling userfaultfd,
but it properly looks up the VMA again to adjust the range.

So in madvise_free_single_vma(), the given range should always fall into a
single VMA and should also span at least one page.

Let's drop the error checks.

The code now matches what we do in madvise_dontneed_single_vma(), where we
call zap_vma_range_batched() that documents: "The range must fit into one
VMA.".  Although that function still adjusts that range, we'll change that
soon.

Link: https://lkml.kernel.org/r/20260227200848.114019-1-david@kernel.org
Link: https://lkml.kernel.org/r/20260227200848.114019-2-david@kernel.org
Link: https://lore.kernel.org/r/aYSKyr7StGpGKNqW@google.com [1]
Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
Reviewed-by: Lorenzo Stoakes (Oracle) <ljs@kernel.org>
Cc: Alexander Gordeev <agordeev@linux.ibm.com>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Alice Ryhl <aliceryhl@google.com>
Cc: Andrii Nakryiko <andrii@kernel.org>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Arve <arve@android.com>
Cc: "Borislav Petkov (AMD)" <bp@alien8.de>
Cc: Carlos Llamas <cmllamas@google.com>
Cc: Christian Borntraeger <borntraeger@linux.ibm.com>
Cc: Christian Brauner <brauner@kernel.org>
Cc: Claudio Imbrenda <imbrenda@linux.ibm.com>
Cc: Daniel Borkman <daniel@iogearbox.net>
Cc: Dave Airlie <airlied@gmail.com>
Cc: David Ahern <dsahern@kernel.org>
Cc: David Rientjes <rientjes@google.com>
Cc: David S. Miller <davem@davemloft.net>
Cc: Dimitri Sivanich <dimitri.sivanich@hpe.com>
Cc: Eric Dumazet <edumazet@google.com>
Cc: Gerald Schaefer <gerald.schaefer@linux.ibm.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Hartley Sweeten <hsweeten@visionengravers.com>
Cc: Heiko Carstens <hca@linux.ibm.com>
Cc: Ian Abbott <abbotti@mev.co.uk>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Jakub Kacinski <kuba@kernel.org>
Cc: Jani Nikula <jani.nikula@linux.intel.com>
Cc: Jann Horn <jannh@google.com>
Cc: Janosch Frank <frankja@linux.ibm.com>
Cc: Jarkko Sakkinen <jarkko@kernel.org>
Cc: Jason Gunthorpe <jgg@ziepe.ca>
Cc: Jonas Lahtinen <joonas.lahtinen@linux.intel.com>
Cc: Leon Romanovsky <leon@kernel.org>
Cc: Liam Howlett <liam.howlett@oracle.com>
Cc: Madhavan Srinivasan <maddy@linux.ibm.com>
Cc: Matthew Wilcox (Oracle) <willy@infradead.org>
Cc: Michael Ellerman <mpe@ellerman.id.au>
Cc: Michal Hocko <mhocko@suse.com>
Cc: Miguel Ojeda <ojeda@kernel.org>
Cc: Mike Rapoport <rppt@kernel.org>
Cc: Namhyung kim <namhyung@kernel.org>
Cc: Neal Cardwell <ncardwell@google.com>
Cc: Paolo Abeni <pabeni@redhat.com>
Cc: Pedro Falcato <pfalcato@suse.de>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Rodrigo Vivi <rodrigo.vivi@intel.com>
Cc: Shakeel Butt <shakeel.butt@linux.dev>
Cc: Suren Baghdasaryan <surenb@google.com>
Cc: Todd Kjos <tkjos@android.com>
Cc: Tvrtko Ursulin <tursulin@ursulin.net>
Cc: Vasily Gorbik <gor@linux.ibm.com>
Cc: Vincenzo Frascino <vincenzo.frascino@arm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
uudiin pushed a commit that referenced this pull request Aug 21, 2026
Currently, the initialization of loongarch_jump_ops does not contain an
assignment to its .free field. This causes disasm_line__free() to fall
through to ins_ops__delete() for LoongArch jump instructions.

ins_ops__delete() will free ins_operands.source.raw and
ins_operands.source.name, and these fields overlaps with
ins_operands.jump.raw_comment and ins_operands.jump.raw_func_start.
Since in loongarch_jump__parse(), these two fields are populated by
strchr()-ing the same buffer, trying to free them will lead to undefined
behavior.

This invalid free usually leads to crashes:

        Process 1712902 (perf) of user 1000 dumped core.
        Stack trace of thread 1712902:
        #0  0x00007fffef155c58 n/a (libc.so.6 + 0x95c58)
        #1  0x00007fffef0f7a94 raise (libc.so.6 + 0x37a94)
        #2  0x00007fffef0dd6a8 abort (libc.so.6 + 0x1d6a8)
        #3  0x00007fffef145490 n/a (libc.so.6 + 0x85490)
        #4  0x00007fffef1646f4 n/a (libc.so.6 + 0xa46f4)
        #5  0x00007fffef164718 n/a (libc.so.6 + 0xa4718)
        #6  0x00005555583a6764 __zfree (/home/csmantle/dist/linux-arch/tools/perf/perf + 0x106764)
        #7  0x000055555854fb70 disasm_line__free (/home/csmantle/dist/linux-arch/tools/perf/perf + 0x2afb70)
        #8  0x000055555853d618 annotated_source__purge (/home/csmantle/dist/linux-arch/tools/perf/perf + 0x29d618)
        #9  0x000055555852300c __hist_entry__tui_annotate (/home/csmantle/dist/linux-arch/tools/perf/perf + 0x28300c)
        #10 0x0000555558526718 do_annotate (/home/csmantle/dist/linux-arch/tools/perf/perf + 0x286718)
        #11 0x000055555852ed94 evsel__hists_browse (/home/csmantle/dist/linux-arch/tools/perf/perf + 0x28ed94)
        #12 0x000055555831fdd0 cmd_report (/home/csmantle/dist/linux-arch/tools/perf/perf + 0x7fdd0)
        #13 0x000055555839b644 handle_internal_command (/home/csmantle/dist/linux-arch/tools/perf/perf + 0xfb644)
        #14 0x00005555582fe6ac main (/home/csmantle/dist/linux-arch/tools/perf/perf + 0x5e6ac)
        #15 0x00007fffef0ddd90 n/a (libc.so.6 + 0x1dd90)
        #16 0x00007fffef0ddf0c __libc_start_main (libc.so.6 + 0x1df0c)
        #17 0x00005555582fed10 _start (/home/csmantle/dist/linux-arch/tools/perf/perf + 0x5ed10)
        ELF object binary architecture: LoongArch

... and it can be confirmed with Valgrind:

        ==1721834== Invalid free() / delete / delete[] / realloc()
        ==1721834==    at 0x4EA9014: free (in /usr/lib/valgrind/vgpreload_memcheck-loongarch64-linux.so)
        ==1721834==    by 0x4106287: __zfree (zalloc.c:13)
        ==1721834==    by 0x42ADC8F: disasm_line__free (in /home/csmantle/dist/linux-arch/tools/perf/perf)
        ==1721834==    by 0x429B737: annotated_source__purge (in /home/csmantle/dist/linux-arch/tools/perf/perf)
        ==1721834==    by 0x42811EB: __hist_entry__tui_annotate (in /home/csmantle/dist/linux-arch/tools/perf/perf)
        ==1721834==    by 0x42848D7: do_annotate (in /home/csmantle/dist/linux-arch/tools/perf/perf)
        ==1721834==    by 0x428CF33: evsel__hists_browse (in /home/csmantle/dist/linux-arch/tools/perf/perf)
        ==1721834==  Address 0x7d34303 is 35 bytes inside a block of size 62 alloc'd
        ==1721834==    at 0x4EA59B8: malloc (in /usr/lib/valgrind/vgpreload_memcheck-loongarch64-linux.so)
        ==1721834==    by 0x6B80B6F: strdup (strdup.c:42)
        ==1721834==    by 0x42AD917: disasm_line__new (in /home/csmantle/dist/linux-arch/tools/perf/perf)
        ==1721834==    by 0x42AE5A3: symbol__disassemble_objdump (in /home/csmantle/dist/linux-arch/tools/perf/perf)
        ==1721834==    by 0x42AF0A7: symbol__disassemble (in /home/csmantle/dist/linux-arch/tools/perf/perf)
        ==1721834==    by 0x429B3CF: symbol__annotate (in /home/csmantle/dist/linux-arch/tools/perf/perf)
        ==1721834==    by 0x429C233: symbol__annotate2 (in /home/csmantle/dist/linux-arch/tools/perf/perf)
        ==1721834==    by 0x42804D3: __hist_entry__tui_annotate (in /home/csmantle/dist/linux-arch/tools/perf/perf)
        ==1721834==    by 0x42848D7: do_annotate (in /home/csmantle/dist/linux-arch/tools/perf/perf)
        ==1721834==    by 0x428CF33: evsel__hists_browse (in /home/csmantle/dist/linux-arch/tools/perf/perf)

This patch adds the missing free() specialization in loongarch_jump_ops,
which prevents disasm_line__free() from invoking the default cleanup
function.

Fixes: fb7fd2a ("perf annotate: Move raw_comment and raw_func_start fields out of 'struct ins_operands'")
Cc: stable@vger.kernel.org
Cc: WANG Rui <wangrui@loongson.cn>
Cc: Huacai Chen <chenhuacai@kernel.org>
Cc: WANG Xuerui <kernel@xen0n.name>
Cc: loongarch@lists.linux.dev
Signed-off-by: Rong Bao <rong.bao@csmantle.top>
Tested-by: WANG Rui <wangrui@loongson.cn>
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
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
pin_user_pages_fast() can partially succeed and return the number of
pages that were actually pinned. However, the bio_integrity_map_user()
does not handle this partial pinning. This leads to a general protection
fault since bvec_from_pages() dereferences an unpinned page address,
which is 0.

To fix this, add a check to verify that all requested memory is pinned.
If partial pinning occurs, unpin the memory and return -EFAULT.

Kernel Oops:

Oops: general protection fault, probably for non-canonical address 0xdffffc0000000001: 0000 [#1] SMP KASAN NOPTI
KASAN: null-ptr-deref in range [0x0000000000000008-0x000000000000000f]
CPU: 0 UID: 0 PID: 1061 Comm: nvme-passthroug Not tainted 7.0.0-11783-g90957f9314e8-dirty #16 PREEMPT(lazy)
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.17.0-0-gb52ca86e094d-prebuilt.qemu.org 04/01/2014
RIP: 0010:bio_integrity_map_user.cold+0x1b0/0x9d6

Fixes: 492c5d4 ("block: bio-integrity: directly map user buffers")
Acked-by: Chao Shi <cshi008@fiu.edu>
Acked-by: Weidong Zhu <weizhu@fiu.edu>
Acked-by: Dave Tian <daveti@purdue.edu>
Signed-off-by: Sungwoo Kim <iam@sung-woo.kim>
Tested-by: Shin'ichiro Kawasaki <shinichiro.kawasaki@wdc.com>
Link: linux-blktests/blktests#244
Link: https://patch.msgid.link/20260512050929.541397-2-iam@sung-woo.kim
Signed-off-by: Jens Axboe <axboe@kernel.dk>
uudiin pushed a commit that referenced this pull request Aug 21, 2026
…softlockup

We hit a real softlockup in an internal stress test environment.  The
workload was LTP memory/swap stress on a large arm64 machine, with 320
CPUs, about 1TB memory and an 8.6GB swap device.  The system was under
heavy load and the swap device had a large number of full clusters.  The
softlockup was triggered during a stress test after about 3 days.

So, add periodic cond_resched() calls during large full_clusters
reclaim operations to prevent softlockup issues.

Detailed call trace as follow:

PID: 3817773  TASK: ffff0883bb28b780  CPU: 48   COMMAND: "kworker/48:7"
   #0 [ffff800080183d10] __crash_kexec at ffffa4c1361e5de4
   #1 [ffff800080183d90] panic at ffffa4c1360d5e9c
   #2 [ffff800080183e20] watchdog_timer_fn at ffffa4c136231fa8
   ...
  #16 [ffff8000c4ad3cb0] swap_cache_del_folio at ffffa4c1363e1614
  #17 [ffff8000c4ad3ce0] __try_to_reclaim_swap at ffffa4c1363e4bfc
  #18 [ffff8000c4ad3d40] swap_reclaim_full_clusters at ffffa4c1363e5474
  #19 [ffff8000c4ad3da0] swap_reclaim_work at ffffa4c1363e550c
  #20 [ffff8000c4ad3dc0] process_one_work at ffffa4c136102edc
  #21 [ffff8000c4ad3e10] worker_thread at ffffa4c136103398
  #22 [ffff8000c4ad3e70] kthread at ffffa4c13610d95c

Link: https://lore.kernel.org/20260506130919.2298807-1-kerayhuang@tencent.com
Fixes: 5168a68 ("mm, swap: avoid over reclaim of full clusters")
Signed-off-by: Zijiang Huang <kerayhuang@tencent.com>
Reviewed-by: Kairui Song <kasong@tencent.com>
Reviewed-by: Hao Peng <flyingpeng@tencent.com>
Reviewed-by: albinwyang <albinwyang@tencent.com>
Reviewed-by: Baoquan He <baoquan.he@linux.dev>
Acked-by: Chris Li <chrisl@kernel.org>
Cc: Barry Song <baohua@kernel.org>
Cc: Kairui Song <kasong@tencent.com>
Cc: Kemeng Shi <shikemeng@huaweicloud.com>
Cc: Nhat Pham <nphamcs@gmail.com>
Cc: Youngjun Park <youngjun.park@lge.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
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