From 917f69ed71c19b312fdeabfe165905a62384bd22 Mon Sep 17 00:00:00 2001 From: Zongyao Chen Date: Fri, 17 Jul 2026 11:50:22 +0800 Subject: [PATCH 1/5] lua-lsm: lazily allocate task blob state Most tasks never use Lua VM or object property state. Once Lua hooks are active, however, each task carries an embedded kvcache dictionary and a separate teardown flag. This expands the task security blob and adds work to task allocation and teardown. Keep only the VM and dictionary pointers in the task blob and allocate each state on first use. Untouched tasks can now skip Lua teardown entirely. Publish ERR_PTR(-ESRCH) before reclaiming lazy state so reentrant hooks cannot recreate task state while Lua finalizers run. Signed-off-by: Zongyao Chen --- security/lua/lsm.c | 152 +++++++++++++++++++++----------------- security/lua/lsm.h | 19 +++-- security/lua/lsm_defs.c | 4 +- security/lua/lsm_defs.h | 6 +- security/lua/lua_object.h | 22 ++++-- 5 files changed, 119 insertions(+), 84 deletions(-) diff --git a/security/lua/lsm.c b/security/lua/lsm.c index 9f978b2da6b8..2dc83fe3094d 100644 --- a/security/lua/lsm.c +++ b/security/lua/lsm.c @@ -428,35 +428,13 @@ static void lvm_vm_reset(struct lvm_state *lvm) lua_gc(L, LUA_GCCOLLECT, 0); } -static void lvm_mark_dirty(lua_State *L) -{ - void *ud; - - lua_getallocf(L, &ud); - ((struct lvm_state *)ud)->dirty = true; -} - -static bool lvm_task_teardown(const struct lua_lsm_task *llt) -{ - /* Pairs with smp_store_release() in task_blob_free(). */ - return smp_load_acquire(&llt->lvm_teardown); -} - -bool lvm_current_task_teardown(void) -{ - return in_task() && lvm_task_teardown(lua_lsm_task(current)); -} - static struct lvm_state * lvm_get_task_state(const struct task_struct *task, bool create) { struct lua_lsm_task *llt = lua_lsm_task(task); struct lvm_state *lvm, *old; - if (unlikely(lvm_task_teardown(llt))) - return NULL; - - /* Pairs with cmpxchg() publishing a lazy task VM state. */ + /* Pairs with xchg() in lua_lsm_task_blob_free(). */ lvm = smp_load_acquire(&llt->lvm); if (likely(lvm || !create)) return lvm; @@ -465,20 +443,12 @@ lvm_get_task_state(const struct task_struct *task, bool create) if (!lvm) return NULL; - if (unlikely(lvm_task_teardown(llt))) { - kfree(lvm); - return NULL; - } - old = cmpxchg(&llt->lvm, NULL, lvm); if (old) { kfree(lvm); lvm = old; } - if (unlikely(lvm_task_teardown(llt))) - return NULL; - return lvm; } @@ -490,12 +460,16 @@ lvm_get_from_task(const struct task_struct *task, bool require_idle) lua_State *L; int n; + if (IS_ERR(lvm)) + return ERR_CAST(lvm); if (!lvm) - return NULL; + return ERR_PTR(-ENOMEM); n = refcount_acquire(&lvm->refcount); - if (unlikely(lvm_task_teardown(llt))) - goto err_put; + if (IS_ERR(READ_ONCE(llt->lvm))) { + refcount_release(&lvm->refcount); + return ERR_PTR(-ESRCH); + } if (require_idle && n != 1) { refcount_release(&lvm->refcount); return NULL; @@ -521,7 +495,7 @@ lvm_get_from_task(const struct task_struct *task, bool require_idle) memset(&build_owner, 0, sizeof(build_owner)); built = lvm_build_lua_state(&build_owner); if (!built) - goto err_put; + goto err_nomem; owner = &build_owner; } lua_setallocf(built, lvm_alloc, lvm); @@ -541,9 +515,9 @@ lvm_get_from_task(const struct task_struct *task, bool require_idle) } return L; -err_put: +err_nomem: refcount_release(&lvm->refcount); - return NULL; + return ERR_PTR(-ENOMEM); } static void lvm_put_to_task(const struct task_struct *task, lua_State *L) @@ -734,6 +708,14 @@ static int module_load(lua_State *L, struct lua_lsm_module *module) return 0; } +static void lvm_mark_dirty(lua_State *L) +{ + void *ud; + + lua_getallocf(L, &ud); + ((struct lvm_state *)ud)->dirty = true; +} + static int lua_modules_index(lua_State *L) { const char *key = luaL_checkstring(L, 2); @@ -1197,7 +1179,6 @@ static int lvm_remove_module(lua_State *L, struct lua_lsm_module *module) static int task_remove_module(struct task_struct *task, void *arg) { - struct lua_lsm_task *llt = lua_lsm_task(task); struct lua_lsm_module *module = arg; struct lvm_state *lvm; lua_State *L; @@ -1207,13 +1188,16 @@ static int task_remove_module(struct task_struct *task, void *arg) return -EBUSY; /* Unregister must not lazily create a VM for untouched tasks. */ - /* Pairs with cmpxchg() in lvm_get_task_state(). */ - lvm = smp_load_acquire(&llt->lvm); - if (!lvm || !smp_load_acquire(&lvm->L)) + lvm = lvm_get_task_state(task, false); + if (IS_ERR_OR_NULL(lvm)) + return -ENOENT; + + /* Pairs with smp_store_release() publishing a constructed Lua VM. */ + if (!smp_load_acquire(&lvm->L)) return -ENOENT; L = lvm_get_from_task(task, true); - if (!L) + if (IS_ERR_OR_NULL(L)) return -EAGAIN; err = lvm_remove_module(L, module); @@ -1435,48 +1419,88 @@ int modules_show(struct seq_file *m, void *v) return 0; } -/*********************************** main ***********************************/ +/******************************** task blob *********************************/ -int task_blob_init(struct task_struct *task) +struct kvcache_dict *lua_lsm_task_dict(const struct task_struct *task, + bool create) { struct lua_lsm_task *llt = lua_lsm_task(task); + struct kvcache_dict *dict, *old; - WRITE_ONCE(llt->lvm_teardown, false); - kvcache_dict_init(&llt->dict); - return 0; + if (IS_ERR(READ_ONCE(llt->lvm))) + return NULL; + + /* Pairs with cmpxchg() publishing a lazily allocated dictionary. */ + dict = smp_load_acquire(&llt->dict); + if (likely(dict || !create)) + return dict; + + dict = kzalloc(sizeof(*dict), lua_lsm_gfp()); + if (!dict) + return NULL; + + if (IS_ERR(READ_ONCE(llt->lvm))) { + kfree(dict); + return NULL; + } + + old = cmpxchg(&llt->dict, NULL, dict); + if (old) { + kfree(dict); + dict = old; + } + + /* Close a publication race with lua_lsm_task_blob_free(). */ + if (IS_ERR(READ_ONCE(llt->lvm))) { + if (cmpxchg(&llt->dict, dict, NULL) == dict) + kfree(dict); + return NULL; + } + + return dict; } -void task_blob_free(struct task_struct *task) +void lua_lsm_task_blob_free(struct task_struct *task) { struct lua_lsm_task *llt = lua_lsm_task(task); struct lvm_state *lvm; + struct kvcache_dict *dict; + lua_State *L; + + if (likely(!lua_lsm_task_blob_has_state(task))) + return; /* * Lua GC can run finalizers which re-enter LSM hooks. Keep the task VM - * published until reset completes, but make lvm_get() skip this task so - * re-entrant hooks cannot allocate or reuse a VM during teardown. + * private after publishing the teardown state so re-entrant hooks + * cannot allocate or reuse task state during teardown. */ - smp_store_release(&llt->lvm_teardown, true); - /* Pairs with cmpxchg() in lvm_get_task_state(). */ - lvm = smp_load_acquire(&llt->lvm); - if (lvm && READ_ONCE(lvm->L) && lvm->dirty) { - lua_modules_free(task, lvm->L); - lvm_vm_reset(lvm); - lvm->dirty = false; - } + lvm = xchg(&llt->lvm, ERR_PTR(-ESRCH)); + if (!IS_ERR_OR_NULL(lvm)) { + L = READ_ONCE(lvm->L); + if (L && lvm->dirty) { + lua_modules_free(task, L); + lvm_vm_reset(lvm); + lvm->dirty = false; + } - lvm = xchg(&llt->lvm, NULL); - if (lvm) { - if (!READ_ONCE(lvm->L)) { + if (!L) { kfree(lvm); } else { refcount_init(&lvm->refcount, 0); lvm_pool_put(lvm); } } - kvcache_dict_free(&llt->dict); + + dict = xchg(&llt->dict, NULL); + if (dict) { + kvcache_dict_free(dict); + kfree(dict); + } } +/*********************************** main ***********************************/ + /* * TODO: Currently, the key, perf_event, tun_dev, and ib objects do not * have corresponding free LSM callback functions, which prevents object @@ -1532,10 +1556,6 @@ static int __init lua_lsm_init(void) lua_lsm_hook_stats_init_cpu(cpu); #endif - err = task_blob_init(current); - if (err) - return err; - for_each_possible_cpu(cpu) { lvm = kzalloc(sizeof(struct lvm_state), GFP_KERNEL); if (!lvm) diff --git a/security/lua/lsm.h b/security/lua/lsm.h index 597b0ecd2b20..9123a5ef3bc6 100644 --- a/security/lua/lsm.h +++ b/security/lua/lsm.h @@ -8,6 +8,7 @@ #ifndef _SECURITY_LUA_LSM_LSM_H #define _SECURITY_LUA_LSM_LSM_H +#include #include #include #include @@ -114,7 +115,6 @@ extern struct srcu_struct modules_ss; lua_State *lvm_get(void); void lvm_put(lua_State *L); -bool lvm_current_task_teardown(void); int lua_lsm_module_register(const char *code, size_t len); int lua_lsm_module_unregister(const char *name); @@ -143,8 +143,7 @@ struct lvm_state { struct lua_lsm_task { struct lvm_state *lvm; - bool lvm_teardown; - struct kvcache_dict dict; + struct kvcache_dict *dict; }; static inline struct lua_lsm_task *lua_lsm_task(const struct task_struct *task) @@ -152,6 +151,17 @@ static inline struct lua_lsm_task *lua_lsm_task(const struct task_struct *task) return task->security + lua_lsm_blob_sizes.lbs_task; } +static inline bool lua_lsm_task_blob_has_state(const struct task_struct *task) +{ + struct lua_lsm_task *llt = lua_lsm_task(task); + + return READ_ONCE(llt->lvm) || READ_ONCE(llt->dict); +} + +struct kvcache_dict *lua_lsm_task_dict(const struct task_struct *task, + bool create); +void lua_lsm_task_blob_free(struct task_struct *task); + /* common object */ struct lua_lsm_object { @@ -237,9 +247,6 @@ static inline struct lua_lsm_object *lua_lsm_bdev(const struct block_device *bde return bdev->bd_security + lua_lsm_blob_sizes.lbs_bdev; } -int task_blob_init(struct task_struct *task); -void task_blob_free(struct task_struct *task); - /* lua C module */ int luaopen_kernel(lua_State *L); diff --git a/security/lua/lsm_defs.c b/security/lua/lsm_defs.c index 51e2d22ae4f3..41f8e3bbb2f8 100644 --- a/security/lua/lsm_defs.c +++ b/security/lua/lsm_defs.c @@ -1735,7 +1735,7 @@ LUA_LSM_INT_DEFINE1(file_truncate, struct file *, file) */ LUA_LSM_PREPARE_DEFINE2(task_alloc, struct task_struct *, task, u64, clone_flags) { - return task_blob_init(task); + return 0; } /** @@ -1753,7 +1753,7 @@ LUA_LSM_INT_DEFINE2(task_alloc, struct task_struct *, task, u64, clone_flags) */ LUA_LSM_POSTPONE_DEFINE1(task_free, struct task_struct *, task) { - task_blob_free(task); + lua_lsm_task_blob_free(task); } /** diff --git a/security/lua/lsm_defs.h b/security/lua/lsm_defs.h index c6b2149ca122..afd7377c0e75 100644 --- a/security/lua/lsm_defs.h +++ b/security/lua/lsm_defs.h @@ -251,10 +251,10 @@ static inline bool lua_lsm_hook_has_inactive_cleanup(unsigned int nr) if (!lua_lsm_hook_active(__LL_NR_ ## NAME)) \ goto out; \ L = lvm_get(); \ - if (!L) { \ - if (lvm_current_task_teardown()) \ + if (IS_ERR(L)) { \ + if (PTR_ERR(L) == -ESRCH) \ goto out; \ - return -ENOMEM; \ + return PTR_ERR(L); \ } \ lua_pushcfunction(L, lua_traceback); \ lua_pushthread(L); \ diff --git a/security/lua/lua_object.h b/security/lua/lua_object.h index 84b332f8361e..f691b588ebec 100644 --- a/security/lua/lua_object.h +++ b/security/lua/lua_object.h @@ -49,12 +49,20 @@ return (ctype *)checkudata(L, idx, metaname); \ } -#define LUA_OBJECT_KVCACHE_FUNC(name, ctype, blob, method, fname) \ +#define LUA_OBJECT_KVCACHE_FUNC_object(name, ctype, method, fname, ...) \ static int rawmeth_ ## name ## _ ## method(lua_State *L) \ { \ ctype p = toraw ## name(L, 1); \ - struct lua_lsm_ ## blob *ll = lua_lsm_ ## name(p); \ - return lua_object_ ## fname(L, ll ? &ll->dict : NULL); \ + struct lua_lsm_object *ll = lua_lsm_ ## name(p); \ + struct kvcache_dict *dict = ll ? &ll->dict : NULL; \ + return lua_object_ ## fname(L, dict); \ + } + +#define LUA_OBJECT_KVCACHE_FUNC_task(name, ctype, method, fname, create) \ + static int rawmeth_ ## name ## _ ## method(lua_State *L) \ + { \ + ctype p = toraw ## name(L, 1); \ + return lua_object_ ## fname(L, lua_lsm_task_dict(p, create)); \ } #define LUA_OBJECT_FUNCS_DEFINE(name, ctype, has_kvcache) \ @@ -75,10 +83,10 @@ LUA_OBJECT_META_DEFINE(name, ctype, d, METHOD_NAME(name)) \ LUA_OBJECT_META_DEFINE(raw ## name, ctype, d, METHOD_NAME_RAW(name)) \ LUA_OBJECT_META_DEFINE(gc ## name, ctype, d, METHOD_NAME_GC(name)) \ - LUA_OBJECT_KVCACHE_FUNC(name, ctype, blob, kvcache_get, get) \ - LUA_OBJECT_KVCACHE_FUNC(name, ctype, blob, kvcache_incr, incr) \ - LUA_OBJECT_KVCACHE_FUNC(name, ctype, blob, index, index) \ - LUA_OBJECT_KVCACHE_FUNC(name, ctype, blob, newindex, newindex) \ + LUA_OBJECT_KVCACHE_FUNC_ ## blob(name, ctype, kvcache_get, get, false) \ + LUA_OBJECT_KVCACHE_FUNC_ ## blob(name, ctype, kvcache_incr, incr, true) \ + LUA_OBJECT_KVCACHE_FUNC_ ## blob(name, ctype, index, index, false) \ + LUA_OBJECT_KVCACHE_FUNC_ ## blob(name, ctype, newindex, newindex, true) \ LUA_OBJECT_FUNCS_DEFINE(name, ctype, 1) \ static inline void create_ ## name ## _meta(lua_State *L, \ const luaL_Reg *funcs, const luaL_Reg *gc) \ From d4e3edbf937a391f09686d161f1fd4a376e6cbf4 Mon Sep 17 00:00:00 2001 From: Zongyao Chen Date: Mon, 20 Jul 2026 10:35:33 +0800 Subject: [PATCH 2/5] lua-lsm: initialize object dictionaries lazily Most kernel objects never store Lua policy properties, especially when no policy is loaded. Initializing every embedded dictionary during object allocation therefore performs unnecessary lock and tree setup. Leave zero-allocated object and shared dictionaries in the UNINIT state until their first write. Keep reads and teardown safe for uninitialized dictionaries, and report the default shared dictionary capacity before initialization. Signed-off-by: Zongyao Chen --- security/lua/kvcache.c | 16 +++++------- security/lua/kvcache.h | 1 - security/lua/lsm.c | 3 +-- security/lua/lsm_defs.c | 58 ----------------------------------------- 4 files changed, 8 insertions(+), 70 deletions(-) diff --git a/security/lua/kvcache.c b/security/lua/kvcache.c index 37768761b669..d7bf9d25e4c7 100644 --- a/security/lua/kvcache.c +++ b/security/lua/kvcache.c @@ -62,7 +62,7 @@ static int kvcache_node_cmp(struct kvcache_node *n1, struct kvcache_node *n2) RB_GENERATE_STATIC(kvcache, kvcache_node, node, kvcache_node_cmp); -static void __kvcache_dict_init(struct kvcache_dict *dict) +static void kvcache_dict_init(struct kvcache_dict *dict) { rwlock_init(&dict->lock); RB_INIT(&dict->root); @@ -111,7 +111,7 @@ static int kvcache_dict_init_once(struct kvcache_dict *dict) */ if (atomic_try_cmpxchg(&dict->state, &state, KVCACHE_DICT_INITING)) { - __kvcache_dict_init(dict); + kvcache_dict_init(dict); atomic_set_release(&dict->state, KVCACHE_DICT_READY); goto out_bh; } @@ -549,13 +549,6 @@ void kvcache_dict_free(struct kvcache_dict *dict) WARN_ON(atomic_read(&dict->count) != 0); } -void kvcache_dict_init(struct kvcache_dict *dict) -{ - atomic_set(&dict->state, KVCACHE_DICT_INITING); - __kvcache_dict_init(dict); - atomic_set_release(&dict->state, KVCACHE_DICT_READY); -} - /******************************** object cache *******************************/ const int _module_sentinel; @@ -715,6 +708,11 @@ static int shdict_tostring(lua_State *L) struct kvcache_dict *shdict = toshdict(L, 1); unsigned long flags; + if (!kvcache_dict_ready(shdict)) { + lua_pushfstring(L, "shdict (%d / %d)", 0, CACHE_CAPACITY); + return 1; + } + read_lock_irqsave(&shdict->lock, flags); lua_pushfstring(L, "shdict (%d / %d)", atomic_read(&shdict->count), shdict->capacity); diff --git a/security/lua/kvcache.h b/security/lua/kvcache.h index ee9a1d9ce840..43c1eaf3f0bd 100644 --- a/security/lua/kvcache.h +++ b/security/lua/kvcache.h @@ -58,7 +58,6 @@ void kvcache_stats_show(struct seq_file *m); int kvcache_module_nodes_gc(struct lua_lsm_module *module); void kvcache_dict_free(struct kvcache_dict *dict); -void kvcache_dict_init(struct kvcache_dict *dict); /******************************** object cache *******************************/ diff --git a/security/lua/lsm.c b/security/lua/lsm.c index 2dc83fe3094d..91ab4c1e0413 100644 --- a/security/lua/lsm.c +++ b/security/lua/lsm.c @@ -583,13 +583,12 @@ static int lua_shared_index(lua_State *L) unsigned long flags; size_t l = strlen(name); - shdict = kmalloc(struct_size(shdict, name, l + 1), + shdict = kzalloc(struct_size(shdict, name, l + 1), lua_lsm_gfp()); if (!shdict) { __log_err("No memory\n"); return 0; } - kvcache_dict_init(&shdict->dict); memcpy(shdict->name, name, l); shdict->name[l] = '\0'; diff --git a/security/lua/lsm_defs.c b/security/lua/lsm_defs.c index 41f8e3bbb2f8..11a850d89c1b 100644 --- a/security/lua/lsm_defs.c +++ b/security/lua/lsm_defs.c @@ -348,12 +348,6 @@ LUA_LSM_INT_DEFINE2(fs_context_parse_param, struct fs_context *, fc, */ LUA_LSM_PREPARE_DEFINE1(sb_alloc_security, struct super_block *, sb) { - struct lua_lsm_object *llo = lua_lsm_superblock(sb); - - if (!llo) - return -EINVAL; - - kvcache_dict_init(&llo->dict); return 0; } @@ -727,12 +721,6 @@ LUA_LSM_INT_DEFINE3(path_notify, const struct path *, path, */ LUA_LSM_PREPARE_DEFINE1(inode_alloc_security, struct inode *, inode) { - struct lua_lsm_object *llo = lua_lsm_inode(inode); - - if (!llo) - return -EINVAL; - - kvcache_dict_init(&llo->dict); return 0; } @@ -1530,12 +1518,6 @@ LUA_LSM_INT_DEFINE2(file_permission, struct file *, file, int, mask) */ LUA_LSM_PREPARE_DEFINE1(file_alloc_security, struct file *, file) { - struct lua_lsm_object *llo = lua_lsm_file(file); - - if (!llo) - return -EINVAL; - - kvcache_dict_init(&llo->dict); return 0; } @@ -1770,12 +1752,6 @@ LUA_LSM_VOID_DEFINE1(task_free, struct task_struct *, task) */ LUA_LSM_PREPARE_DEFINE2(cred_alloc_blank, struct cred *, cred, gfp_t, gfp) { - struct lua_lsm_object *llo = lua_lsm_cred(cred); - - if (!llo) - return -EINVAL; - - kvcache_dict_init(&llo->dict); return 0; } @@ -1817,12 +1793,6 @@ LUA_LSM_VOID_DEFINE1(cred_free, struct cred *, cred) LUA_LSM_PREPARE_DEFINE3(cred_prepare, struct cred *, new, const struct cred *, old, gfp_t, gfp) { - struct lua_lsm_object *llo = lua_lsm_cred(new); - - if (!llo) - return -EINVAL; - - kvcache_dict_init(&llo->dict); return 0; } @@ -2205,12 +2175,6 @@ LUA_LSM_VOID_DEFINE2(ipc_getlsmprop, struct kern_ipc_perm *, ipcp, */ LUA_LSM_PREPARE_DEFINE1(msg_msg_alloc_security, struct msg_msg *, msg) { - struct lua_lsm_object *llo = lua_lsm_msgmsg(msg); - - if (!llo) - return -EINVAL; - - kvcache_dict_init(&llo->dict); return 0; } @@ -2250,12 +2214,6 @@ LUA_LSM_VOID_DEFINE1(msg_msg_free_security, struct msg_msg *, msg) */ LUA_LSM_PREPARE_DEFINE1(msg_queue_alloc_security, struct kern_ipc_perm *, perm) { - struct lua_lsm_object *llo = lua_lsm_ipc(perm); - - if (!llo) - return -EINVAL; - - kvcache_dict_init(&llo->dict); return 0; } @@ -2343,12 +2301,6 @@ LUA_LSM_INT_DEFINE5(msg_queue_msgrcv, struct kern_ipc_perm *, perm, */ LUA_LSM_PREPARE_DEFINE1(shm_alloc_security, struct kern_ipc_perm *, perm) { - struct lua_lsm_object *llo = lua_lsm_ipc(perm); - - if (!llo) - return -EINVAL; - - kvcache_dict_init(&llo->dict); return 0; } @@ -2420,12 +2372,6 @@ LUA_LSM_INT_DEFINE3(shm_shmat, struct kern_ipc_perm *, perm, */ LUA_LSM_PREPARE_DEFINE1(sem_alloc_security, struct kern_ipc_perm *, perm) { - struct lua_lsm_object *llo = lua_lsm_ipc(perm); - - if (!llo) - return -EINVAL; - - kvcache_dict_init(&llo->dict); return 0; } @@ -2924,8 +2870,6 @@ LUA_LSM_INT_DEFINE3(socket_getpeersec_dgram, struct socket *, sock, LUA_LSM_PREPARE_DEFINE3(sk_alloc_security, struct sock *, sk, int, family, gfp_t, priority) { - struct lua_lsm_object *llo = lua_lsm_sock(sk); - kvcache_dict_init(&llo->dict); return 0; } @@ -3652,8 +3596,6 @@ LUA_LSM_VOID_DEFINE0(initramfs_populated) */ LUA_LSM_PREPARE_DEFINE1(bdev_alloc_security, struct block_device *, bdev) { - struct lua_lsm_object *llo = lua_lsm_bdev(bdev); - kvcache_dict_init(&llo->dict); return 0; } From b67d62fb903159146a21be2820d69004e55c019c Mon Sep 17 00:00:00 2001 From: Zongyao Chen Date: Mon, 20 Jul 2026 14:31:42 +0800 Subject: [PATCH 3/5] lua-lsm: allocate object dictionaries lazily Shrink object security blobs to a pointer and allocate dictionaries on first use. Centralize object teardown and preserve ENOMEM for task and object lazy allocation failures. Signed-off-by: Zongyao Chen --- security/lua/kvcache.c | 6 ++++ security/lua/lsm.c | 43 ++++++++++++++++++++++++++++- security/lua/lsm.h | 6 +++- security/lua/lsm_defs.c | 58 +++++++-------------------------------- security/lua/lua_object.h | 5 ++-- 5 files changed, 65 insertions(+), 53 deletions(-) diff --git a/security/lua/kvcache.c b/security/lua/kvcache.c index d7bf9d25e4c7..88771fd50041 100644 --- a/security/lua/kvcache.c +++ b/security/lua/kvcache.c @@ -598,6 +598,9 @@ int lua_object_incr(lua_State *L, struct kvcache_dict *dict) { struct lua_lsm_module *module; + if (IS_ERR(dict)) + return kvcache_result(L, PTR_ERR(dict)); + if (!dict) return kvcache_result(L, -ESRCH); @@ -653,6 +656,9 @@ int lua_object_newindex(lua_State *L, struct kvcache_dict *dict) { struct lua_lsm_module *module; + if (IS_ERR(dict)) + return kvcache_result(L, PTR_ERR(dict)); + if (!dict) return kvcache_result(L, -ESRCH); diff --git a/security/lua/lsm.c b/security/lua/lsm.c index 91ab4c1e0413..7d271bd9bb96 100644 --- a/security/lua/lsm.c +++ b/security/lua/lsm.c @@ -1436,7 +1436,7 @@ struct kvcache_dict *lua_lsm_task_dict(const struct task_struct *task, dict = kzalloc(sizeof(*dict), lua_lsm_gfp()); if (!dict) - return NULL; + return ERR_PTR(-ENOMEM); if (IS_ERR(READ_ONCE(llt->lvm))) { kfree(dict); @@ -1498,6 +1498,47 @@ void lua_lsm_task_blob_free(struct task_struct *task) } } +/******************************* object blob ********************************/ + +struct kvcache_dict *lua_lsm_object_dict(struct lua_lsm_object *llo, bool create) +{ + struct kvcache_dict *dict, *old; + + if (!llo) + return NULL; + + /* Pairs with cmpxchg() publishing a lazily allocated dictionary. */ + dict = smp_load_acquire(&llo->dict); + if (likely(dict || !create)) + return dict; + + dict = kzalloc(sizeof(*dict), lua_lsm_gfp()); + if (!dict) + return ERR_PTR(-ENOMEM); + + old = cmpxchg(&llo->dict, NULL, dict); + if (old) { + kfree(dict); + dict = old; + } + + return dict; +} + +void lua_lsm_object_dict_free(struct lua_lsm_object *llo) +{ + struct kvcache_dict *dict; + + if (!llo) + return; + + dict = xchg(&llo->dict, NULL); + if (dict) { + kvcache_dict_free(dict); + kfree(dict); + } +} + /*********************************** main ***********************************/ /* diff --git a/security/lua/lsm.h b/security/lua/lsm.h index 9123a5ef3bc6..1ee328c20294 100644 --- a/security/lua/lsm.h +++ b/security/lua/lsm.h @@ -165,9 +165,13 @@ void lua_lsm_task_blob_free(struct task_struct *task); /* common object */ struct lua_lsm_object { - struct kvcache_dict dict; + struct kvcache_dict *dict; }; +struct kvcache_dict *lua_lsm_object_dict(struct lua_lsm_object *llo, + bool create); +void lua_lsm_object_dict_free(struct lua_lsm_object *llo); + static inline struct lua_lsm_object *lua_lsm_cred(const struct cred *cred) { if (unlikely(!cred->security)) diff --git a/security/lua/lsm_defs.c b/security/lua/lsm_defs.c index 11a850d89c1b..8e7cdd2f2234 100644 --- a/security/lua/lsm_defs.c +++ b/security/lua/lsm_defs.c @@ -374,12 +374,7 @@ LUA_LSM_VOID_DEFINE1(sb_delete, struct super_block *, sb) */ LUA_LSM_POSTPONE_DEFINE1(sb_free_security, struct super_block *, sb) { - struct lua_lsm_object *llo = lua_lsm_superblock(sb); - - if (!llo) - return; - - kvcache_dict_free(&llo->dict); + lua_lsm_object_dict_free(lua_lsm_superblock(sb)); } /** @@ -747,8 +742,7 @@ LUA_LSM_VOID_DEFINE1(inode_free_security, struct inode *, inode) */ LUA_LSM_POSTPONE_DEFINE1(inode_free_security_rcu, void *, inode_security) { - struct lua_lsm_object *llo = lua_lsm_inode_rcu(inode_security); - kvcache_dict_free(&llo->dict); + lua_lsm_object_dict_free(lua_lsm_inode_rcu(inode_security)); } /** @@ -1544,12 +1538,7 @@ LUA_LSM_VOID_DEFINE1(file_release, struct file *, file) */ LUA_LSM_POSTPONE_DEFINE1(file_free_security, struct file *, file) { - struct lua_lsm_object *llo = lua_lsm_file(file); - - if (!llo) - return; - - kvcache_dict_free(&llo->dict); + lua_lsm_object_dict_free(lua_lsm_file(file)); } /** @@ -1770,12 +1759,7 @@ LUA_LSM_INT_DEFINE2(cred_alloc_blank, struct cred *, cred, gfp_t, gfp) */ LUA_LSM_POSTPONE_DEFINE1(cred_free, struct cred *, cred) { - struct lua_lsm_object *llo = lua_lsm_cred(cred); - - if (!llo) - return; - - kvcache_dict_free(&llo->dict); + lua_lsm_object_dict_free(lua_lsm_cred(cred)); } /** @@ -2192,12 +2176,7 @@ LUA_LSM_INT_DEFINE1(msg_msg_alloc_security, struct msg_msg *, msg) */ LUA_LSM_POSTPONE_DEFINE1(msg_msg_free_security, struct msg_msg *, msg) { - struct lua_lsm_object *llo = lua_lsm_msgmsg(msg); - - if (!llo) - return; - - kvcache_dict_free(&llo->dict); + lua_lsm_object_dict_free(lua_lsm_msgmsg(msg)); } /** @@ -2231,12 +2210,7 @@ LUA_LSM_INT_DEFINE1(msg_queue_alloc_security, struct kern_ipc_perm *, perm) */ LUA_LSM_POSTPONE_DEFINE1(msg_queue_free_security, struct kern_ipc_perm *, perm) { - struct lua_lsm_object *llo = lua_lsm_ipc(perm); - - if (!llo) - return; - - kvcache_dict_free(&llo->dict); + lua_lsm_object_dict_free(lua_lsm_ipc(perm)); } /** @@ -2318,12 +2292,7 @@ LUA_LSM_INT_DEFINE1(shm_alloc_security, struct kern_ipc_perm *, perm) */ LUA_LSM_POSTPONE_DEFINE1(shm_free_security, struct kern_ipc_perm *, perm) { - struct lua_lsm_object *llo = lua_lsm_ipc(perm); - - if (!llo) - return; - - kvcache_dict_free(&llo->dict); + lua_lsm_object_dict_free(lua_lsm_ipc(perm)); } /** @@ -2389,12 +2358,7 @@ LUA_LSM_INT_DEFINE1(sem_alloc_security, struct kern_ipc_perm *, perm) */ LUA_LSM_POSTPONE_DEFINE1(sem_free_security, struct kern_ipc_perm *, perm) { - struct lua_lsm_object *llo = lua_lsm_ipc(perm); - - if (!llo) - return; - - kvcache_dict_free(&llo->dict); + lua_lsm_object_dict_free(lua_lsm_ipc(perm)); } /** @@ -2890,8 +2854,7 @@ LUA_LSM_INT_DEFINE3(sk_alloc_security, struct sock *, sk, */ LUA_LSM_POSTPONE_DEFINE1(sk_free_security, struct sock *, sk) { - struct lua_lsm_object *llo = lua_lsm_sock(sk); - kvcache_dict_free(&llo->dict); + lua_lsm_object_dict_free(lua_lsm_sock(sk)); } /** @@ -3613,8 +3576,7 @@ LUA_LSM_INT_DEFINE1(bdev_alloc_security, struct block_device *, bdev) */ LUA_LSM_POSTPONE_DEFINE1(bdev_free_security, struct block_device *, bdev) { - struct lua_lsm_object *llo = lua_lsm_bdev(bdev); - kvcache_dict_free(&llo->dict); + lua_lsm_object_dict_free(lua_lsm_bdev(bdev)); } /** diff --git a/security/lua/lua_object.h b/security/lua/lua_object.h index f691b588ebec..24eb9ebbb755 100644 --- a/security/lua/lua_object.h +++ b/security/lua/lua_object.h @@ -49,13 +49,12 @@ return (ctype *)checkudata(L, idx, metaname); \ } -#define LUA_OBJECT_KVCACHE_FUNC_object(name, ctype, method, fname, ...) \ +#define LUA_OBJECT_KVCACHE_FUNC_object(name, ctype, method, fname, create) \ static int rawmeth_ ## name ## _ ## method(lua_State *L) \ { \ ctype p = toraw ## name(L, 1); \ struct lua_lsm_object *ll = lua_lsm_ ## name(p); \ - struct kvcache_dict *dict = ll ? &ll->dict : NULL; \ - return lua_object_ ## fname(L, dict); \ + return lua_object_ ## fname(L, lua_lsm_object_dict(ll, create)); \ } #define LUA_OBJECT_KVCACHE_FUNC_task(name, ctype, method, fname, create) \ From 08e2823415d7b69930a459d26e782a6664eac446 Mon Sep 17 00:00:00 2001 From: Zongyao Chen Date: Mon, 20 Jul 2026 14:33:18 +0800 Subject: [PATCH 4/5] lua-lsm: reorganize lsm header Group declarations by responsibility and order includes consistently. Keep related types, state, and interfaces adjacent. Signed-off-by: Zongyao Chen --- security/lua/lsm.h | 94 +++++++++++++++++++++++++--------------------- 1 file changed, 52 insertions(+), 42 deletions(-) diff --git a/security/lua/lsm.h b/security/lua/lsm.h index 1ee328c20294..0b8a15056501 100644 --- a/security/lua/lsm.h +++ b/security/lua/lsm.h @@ -9,28 +9,31 @@ #define _SECURITY_LUA_LSM_LSM_H #include -#include -#include #include -#include -#include +#include +#include #include -#include +#include +#include #include +#include +#include #include -#include -#include +#include + #include "bitmap.h" #include "kvcache.h" +#define LUA_LSM_VERSION 1 + +/* LSM hook dispatch */ + DECLARE_STATIC_KEY_FALSE(lua_lsm_modules_active); DECLARE_STATIC_KEY_FALSE(lua_lsm_inactive_cleanup_armed); /* Flag indicating whether initialization completed */ extern int lua_lsm_initialized __initdata; -#define LUA_LSM_VERSION 1 - struct lua_lsm_hook_stat { const char *name; atomic_t nhooks; @@ -67,11 +70,7 @@ static inline bool lua_lsm_hook_supported(unsigned int nr) } } -struct lua_lsm_module_shdict { - struct list_head list; - struct kvcache_dict dict; - char name[]; -}; +/* Policy modules */ enum lua_lsm_module_state { LMS_STATE_LIVE, @@ -80,6 +79,12 @@ enum lua_lsm_module_state { LMS_STATE_ZOMBIE, }; +struct lua_lsm_module_shdict { + struct list_head list; + struct kvcache_dict dict; + char name[]; +}; + struct lua_lsm_module { const char *name; const char *author; @@ -109,25 +114,10 @@ struct lua_lsm_module { extern struct list_head lsm_modules; extern struct srcu_struct modules_ss; -#define TABLINE \ - "---------------------------------------------" \ - "---------------------------------------------" - -lua_State *lvm_get(void); -void lvm_put(lua_State *L); - int lua_lsm_module_register(const char *code, size_t len); int lua_lsm_module_unregister(const char *name); -int modules_show(struct seq_file *m, void *v); - -#ifdef CONFIG_SECURITY_LUA_LSM_STATS -void lua_lsm_hook_stats_record(unsigned int nr, u64 delta); -void lvm_stats_show(struct seq_file *m); -int lsm_funcs_show(struct seq_file *m, void *v); -#endif - -extern struct lsm_blob_sizes lua_lsm_blob_sizes; +/* Lua VMs */ struct lvm_state { lua_State *L; @@ -141,11 +131,22 @@ struct lvm_state { #endif }; +lua_State *lvm_get(void); +void lvm_put(lua_State *L); + +/* LSM blobs */ + +extern struct lsm_blob_sizes lua_lsm_blob_sizes; + struct lua_lsm_task { struct lvm_state *lvm; struct kvcache_dict *dict; }; +struct lua_lsm_object { + struct kvcache_dict *dict; +}; + static inline struct lua_lsm_task *lua_lsm_task(const struct task_struct *task) { return task->security + lua_lsm_blob_sizes.lbs_task; @@ -162,16 +163,6 @@ struct kvcache_dict *lua_lsm_task_dict(const struct task_struct *task, bool create); void lua_lsm_task_blob_free(struct task_struct *task); -/* common object */ - -struct lua_lsm_object { - struct kvcache_dict *dict; -}; - -struct kvcache_dict *lua_lsm_object_dict(struct lua_lsm_object *llo, - bool create); -void lua_lsm_object_dict_free(struct lua_lsm_object *llo); - static inline struct lua_lsm_object *lua_lsm_cred(const struct cred *cred) { if (unlikely(!cred->security)) @@ -251,7 +242,25 @@ static inline struct lua_lsm_object *lua_lsm_bdev(const struct block_device *bde return bdev->bd_security + lua_lsm_blob_sizes.lbs_bdev; } -/* lua C module */ +struct kvcache_dict *lua_lsm_object_dict(struct lua_lsm_object *llo, + bool create); +void lua_lsm_object_dict_free(struct lua_lsm_object *llo); + +/* Observability */ + +#define TABLINE \ + "---------------------------------------------" \ + "---------------------------------------------" + +int modules_show(struct seq_file *m, void *v); + +#ifdef CONFIG_SECURITY_LUA_LSM_STATS +void lua_lsm_hook_stats_record(unsigned int nr, u64 delta); +void lvm_stats_show(struct seq_file *m); +int lsm_funcs_show(struct seq_file *m, void *v); +#endif + +/* Lua C modules */ int luaopen_kernel(lua_State *L); int luaopen_fs(lua_State *L); @@ -260,7 +269,8 @@ int luaopen_errno(lua_State *L); int luaopen_capability(lua_State *L); int luaopen_signal(lua_State *L); -/* securityfs interface */ +/* Securityfs interface */ + int lua_lsm_securityfs_init(void); #endif /* ! _SECURITY_LUA_LSM_LSM_H */ From 7e3d9718cca9d183795370e3b45c70492868ae59 Mon Sep 17 00:00:00 2001 From: Zongyao Chen Date: Mon, 20 Jul 2026 15:09:15 +0800 Subject: [PATCH 5/5] lua-lsm: return nil for empty lazy kvcache reads Valid task and object kvcache reads can now see no dictionary after lazy allocation when the key has never been written. lua_object_get() returned no Lua values in that case, while a read from an initialized empty dictionary returns one nil. This changes observable Lua return arity in tail-return and multiret contexts. Push nil for the no-dictionary read path and keep the dictionary unallocated. Signed-off-by: Zongyao Chen --- security/lua/kvcache.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/security/lua/kvcache.c b/security/lua/kvcache.c index 88771fd50041..c0e5756ce45c 100644 --- a/security/lua/kvcache.c +++ b/security/lua/kvcache.c @@ -585,8 +585,10 @@ int lua_object_get(lua_State *L, struct kvcache_dict *dict) { struct lua_lsm_module *module; - if (!dict) - return 0; + if (!dict) { + lua_pushnil(L); + return 1; + } module = module_from_object_fenv(L, 1); if (!module)