[BUG] bpf, lsm: new first argument in 7.4 inode hooks silently breaks BPF LSMs
From: roblabla
Date: Fri Nov 06 2026 - 08:39:27 EST
Resubmitting as plain text. Sorry for the noise.
Hello,
First of all, I'm sorry if I'm sending this email to the wrong mailing lists, it's my first time reporting a linux issue ^^'.
In commit 16959c469f232f5c76cfb628297cafbf6dbdba79 (lsm: expose mount idmaps to inode hooks), merged in 7.4-rc1, a new argument was added in position 0 of various inode-related `security_` functions. Specifically, it added this argument to inode_create, inode_link, inode_symlink, inode_mkdir, inode_mknod and inode_permission.
This has the immediate effect of breaking eBPF LSM programs relying on these hooks that were compiled with BPF CO-RE and using BPF_CORE_READ. Those programs don't simply stop loading (which would be fine enough) - instead, they will misinterpret the argument from one type to another, which can cause them to behave erratically. This has already bitten me in the past, when inode_setattr got a similar argument change in Linux 6.9. This caused our eBPF LSM to misbehave and deny actions that should have been ordinarily accepted, leading to huge production outages on our customers' machines.
Here's a simple reproducer that shows the problem (full repro with the loader, makefile and run.sh is at https://github.com/roblabla/kernel-7.4-bpf-bug-reproducer):
```
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include <bpf/bpf_core_read.h>
char LICENSE[] SEC("license") = "GPL";
#define EPERM 1
#define NAME_LEN 32
static const char forbidden[] = "forbidden";
SEC("lsm/inode_create")
int BPF_PROG(deny_forbidden, struct inode *dir, struct dentry *dentry)
{
char name[NAME_LEN] = {};
const unsigned char *name_ptr;
unsigned long dir_ino;
dir_ino = BPF_CORE_READ(dir, i_ino);
name_ptr = BPF_CORE_READ(dentry, d_name.name);
bpf_probe_read_kernel_str(name, sizeof(name), name_ptr);
bpf_printk("inode_create: dir ino=%lu name='%s'", dir_ino, name);
for (int i = 0; i < sizeof(forbidden); i++)
if (name[i] != forbidden[i])
return 0;
bpf_printk("inode_create: denied '%s'", name);
return -EPERM;
}
```
Compile it under a kernel before 7.4 release, then run it on mainline. Here's the output:
- run on 7.1-rc7: touch forbidden: DENIED (dir ino=59 name='forbidden')
- run on 7.4-rc1: touch forbidden: created (dir ino=0 name='')
Before Linux 7.4, this program will properly deny the creation of any file named "forbidden". But in 7.4, the name_ptr BPF_CORE_READ will read garbage, and the check will fail. This is a bit of a contrived example, of course, but should show that there's a real problem in the way we handle eBPF loading in the face of changing signatures, that is prone to causing big compatibility issues.
Now, I'm not sure how the Linux Kernel treats eBPF LSM hooks - if they're considered part of the userspace that shouldn't be broken. My understanding is that kprobes are not part of the Linux Kernel stability guarantees, for rather obvious reasons (they allow watching the internals of the kernel) - but I'm not sure if this extends to the LSM Hooks. I assume not. Still, this kind of breakage is really painful for us, and I hope we can find a solution.
Could we fix this type of breakage by making sure new arguments are only added at the _end_ of the LSM hooks signatures, such that eBPF written for an older version of the kernel do not start misinterpreting arguments? eBPFs don't care about having "too few" arguments in the hook, so this would solve the situation.
Separately, I think the current lack of error handling in case of argument mismatch should really be improved. Could we perhaps have a mechanism such that the kernel (or maybe libbpf?) could check the arguments of the eBPF program, and refuse loading if it mismatches what the kernel expects?
Thanks,
roblabla