]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
bpf: Reject BPF_MAP_TYPE_INODE_STORAGE creation if BPF LSM is uninitialized
authorMatt Bobrowski <mattbobrowski@google.com>
Sun, 28 Jun 2026 20:11:03 +0000 (20:11 +0000)
committerDaniel Borkmann <daniel@iogearbox.net>
Tue, 30 Jun 2026 14:31:56 +0000 (16:31 +0200)
When CONFIG_BPF_LSM=y is set, BPF inode storage maps
(BPF_MAP_TYPE_INODE_STORAGE) are compiled into the kernel. However,
if the BPF LSM is not explicitly enabled at boot time (e.g. omitted
from the "lsm=" boot parameter), lsm_prepare() is never executed for
the BPF LSM.

Consequently, the BPF inode security blob offset
(bpf_lsm_blob_sizes.lbs_inode) is never initialized and remains at
its default compiled size of 8 bytes instead of being updated to a
valid offset past the reserved struct rcu_head (typically 16 bytes
or more).

When a privileged user creates and updates a BPF_MAP_TYPE_INODE_STORAGE
map, bpf_inode() evaluates inode->i_security + 8. This erroneously
aliases the struct rcu_head.func callback pointer at the beginning
of the inode->i_security blob. During subsequent map element cleanup
or inode destruction, writing NULL to owner_storage clears the queued
RCU callback pointer. When rcu_do_batch() later executes the queued
callback, it attempts an instruction fetch at address 0x0, triggering
an immediate kernel panic.

Fix this by introducing a global bpf_lsm_initialized boolean flag
marked with __ro_after_init. Set this flag to true inside bpf_lsm_init()
when the LSM framework successfully registers the BPF LSM. Gate map
allocation in inode_storage_map_alloc() on this flag, returning
-EOPNOTSUPP if the BPF LSM is in turn uninitialized.

This fail-fast approach prevents userspace from allocating inode
storage maps when the supporting BPF LSM infrastructure is absent,
avoiding zombie map states.

Fixes: 8ea636848aca ("bpf: Implement bpf_local_storage for inodes")
Reported-by: oxsignal <awo@kakao.com>
Signed-off-by: Matt Bobrowski <mattbobrowski@google.com>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Reviewed-by: Emil Tsalapatis <emil@etsalapatis.com>
Reviewed-by: Amery Hung <ameryhung@gmail.com>
Link: https://lore.kernel.org/bpf/20260628201103.3624525-1-mattbobrowski@google.com
include/linux/bpf_lsm.h
kernel/bpf/bpf_inode_storage.c
security/bpf/hooks.c

index 143775a27a2a0b855e871876f49ad428cc757b32..dda272d78f013f7f13534514971b82d15de39514 100644 (file)
@@ -14,6 +14,8 @@
 
 #ifdef CONFIG_BPF_LSM
 
+extern bool bpf_lsm_initialized __ro_after_init;
+
 #define LSM_HOOK(RET, DEFAULT, NAME, ...) \
        RET bpf_lsm_##NAME(__VA_ARGS__);
 #include <linux/lsm_hook_defs.h>
@@ -56,6 +58,8 @@ bool bpf_lsm_hook_returns_errno(u32 btf_id);
 
 #else /* !CONFIG_BPF_LSM */
 
+#define bpf_lsm_initialized false
+
 static inline bool bpf_lsm_is_sleepable_hook(u32 btf_id)
 {
        return false;
index 0da8d923e39d4b658519ad498b1af076b5bae70e..f9e81060c1f459fd5cbbabe1cc75dcbd47dfe2ff 100644 (file)
@@ -178,6 +178,15 @@ static int notsupp_get_next_key(struct bpf_map *map, void *key,
 
 static struct bpf_map *inode_storage_map_alloc(union bpf_attr *attr)
 {
+       /*
+        * Do not allow allocation of BPF_MAP_TYPE_INODE_STORAGE if the BPF LSM
+        * was not initialized by the LSM framework at boot. Without proper
+        * initialization, the BPF inode security blob offset remains unprepared,
+        * causing bpf_inode() to calculate an invalid memory offset and corrupt
+        * inode->i_security.
+        */
+       if (!bpf_lsm_initialized)
+               return ERR_PTR(-EOPNOTSUPP);
        return bpf_local_storage_map_alloc(attr, &inode_cache);
 }
 
index 40efde233f3ab31c0b81177c2bff9a870eeaa8e3..7b98f5d1e2be83901a6ebde2fb5e165756bac8b1 100644 (file)
@@ -7,6 +7,8 @@
 #include <linux/bpf_lsm.h>
 #include <uapi/linux/lsm.h>
 
+bool bpf_lsm_initialized __ro_after_init;
+
 static struct security_hook_list bpf_lsm_hooks[] __ro_after_init = {
        #define LSM_HOOK(RET, DEFAULT, NAME, ...) \
        LSM_HOOK_INIT(NAME, bpf_lsm_##NAME),
@@ -24,6 +26,7 @@ static int __init bpf_lsm_init(void)
 {
        security_add_hooks(bpf_lsm_hooks, ARRAY_SIZE(bpf_lsm_hooks),
                           &bpf_lsmid);
+       bpf_lsm_initialized = true;
        pr_info("LSM support for eBPF active\n");
        return 0;
 }