Re: [BUG] bpf, lsm: new first argument in 7.4 inode hooks silently breaks BPF LSMs

From: Andrii Nakryiko

Date: Fri Nov 06 2026 - 14:34:54 EST


On Fri, Nov 6, 2026 at 7:41 AM Paul Moore <paul@xxxxxxxxxxxxxx> wrote:
>
> On Fri, Nov 6, 2026 at 8:39 AM roblabla <linux@xxxxxxxxx> wrote:
> >
> > 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 ^^'.
>
> Hi,
>
> It looks like you sent this email to the right people/lists, well done
> on the first hurdle :)
>
> > 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.
>
> ...
>
> > 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.
>
> Like kprobes, the LSM hooks are intentionally not part of any stable
> API promise. While we try not to needlessly churn the LSM hooks, we
> reserve the right to modify parameters, the LSM callbacks associated
> with a hook, and even the basic hook semantics from one kernel release
> to another. The reasoning is very similar to that of kprobes: the LSM
> hooks are linked to kernel internals and thus need the ability to
> change as the kernel changes.
>
> LSMs that need to provide a stable API must provide an abstraction
> layer between the LSM hook callbacks and their stable API. The LSM
> framework is not responsible for the stability of individual LSM APIs.
>
> > Could we fix this type of breakage by making sure new arguments are only added at the _end_ of the LSM hooks signatures,
>
> I'm sorry, but I can't make that promise at the LSM framework layer.
> It is impossible to predict how the kernel internals will continue to
> evolve and thus I can't promise how the LSM hooks and callbacks will
> need to change as a result.

Yeah, I think that's a reasonable stance and we went through similar
discussion about tracepoints not being stable APIs, no questions about
that. But I'm wondering if it's not too late (as far as I understand,
this hasn't gotten into final release just yet), would it be ok to
just move newly added argument to be the last ones? We had similar
problem in not too distant past with one of scheduler tracepoints,
where new argument was added in the middle of argument list, and that
causes just unnecessary friction when using BPF with old and new
kernels. We did move the argument to the end and resolved this
(without really mandating any stability guarantees).

So, in short, would it be possible to send a follow up to Daan's
commit and move `struct mnt_idmap *idmap` to the end of LSM argument
list?

>
> Regarding the specifics of how the BPF LSM has dealt with this in the
> past, and any plans for the future, I'll let others speak to that;
> hopefully they will have some tricks you can use to make these
> transitions easier.
>

(this is answer to roblabla, I don't think you, Paul, should care)

The answer here is nuanced. On one hand, libbpf and kernel (BPF
verifier) don't really know exactly what BPF program assumes argument
types are. libbpf-provided BPF_PROG() macro that everyone is using
with BPF LSM just assumes user specifies correct sequence and types of
arguments, and libbpf just provides a bit of syntactic sugar with
casting untyped input arguments (they are just "u64" types) into
expected types. So it's not easy to detect this automatically neither
in libbpf nor in BPF verifier.

Having said that, there is a way to handle this, although ugly and
*everyone* using specific LSMs would need to do this on their own.
Instead of using convenience BPF_PROG() macro to define BPF program,
you just use raw `u64 ctx[]` access, and then cast ctx[0] to either
`struct mnt_idmap *` on new kernel, or `struct dentry *` on old
kernel. And you do that for all arguments. E.g., something like this

SEC("...")
int my_prog(u64 ctx[])
{
bool new_kernel = ...;
struct mnt_idmap *idmap = new_kernel ? bpf_core_cast(ctx[0],
struct mnt_idmap) : NULL;
struct dentry *old_dentry = bpf_core_cast(new_kernel ? ctx[1] : ctx[0]);
... and so on ...
}

How you determine "new_kernel" can be done in various ways as well, I
don't want to overburden this email with details, but it's doable as
well.


so, tl;dr, it's possible to handle this LSM signature change, but it's
painful and requires knowing about breakage and doing a bunch of extra
plumbing.

ideally, if it's just a new argument addition, it should go to the end
of arg list and then all these issues go away

> --
> paul-moore.com