]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
bpf: fix UAF by restoring RCU-delayed inode freeing in bpffs
authorDeepanshu Kartikey <kartikey406@gmail.com>
Tue, 2 Jun 2026 02:52:49 +0000 (08:22 +0530)
committerAlexei Starovoitov <ast@kernel.org>
Tue, 2 Jun 2026 04:07:06 +0000 (21:07 -0700)
commit 4f375ade6aa9 ("bpf: Avoid RCU context warning when unpinning
htab with internal structs") moved inode cleanup from ->free_inode()
into ->destroy_inode() to avoid sleeping in RCU context when calling
bpf_any_put(). However this removed the RCU delay on freeing the
inode itself and the cached symlink body (i_link), both of which
can be accessed by RCU pathwalk (pick_link, may_lookup etc.).

This causes a use-after-free when a concurrent unlinkat() drops the
last inode reference and destroy_inode() frees the inode immediately,
while another task is still walking the path in RCU mode and reads
inode->i_opflags (offset +2) inside current_time() -> is_mgtime().

KASAN reports:
  BUG: KASAN: slab-use-after-free in is_mgtime include/linux/fs.h:2313
  Read of size 2 at addr ffff8880407e4282 (offset +2 = i_opflags)

The rules (per Al Viro):
  ->destroy_inode()  called immediately, can sleep, use for blocking
                     cleanup e.g. bpf_any_put()
  ->free_inode()     called after RCU grace period, use for freeing
                     inode and anything RCU-accessible e.g. i_link

Fix: split the two concerns properly:
  - keep bpf_any_put() in bpf_destroy_inode() since it is blocking
    and needs to run promptly
  - introduce bpf_free_inode() to handle kfree(i_link) and
    free_inode_nonrcu() with proper RCU delay, preventing the UAF

Fixes: 4f375ade6aa9 ("bpf: Avoid RCU context warning when unpinning htab with internal structs")
Reported-by: syzbot+36e50496c8ac4bcde3f9@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=36e50496c8ac4bcde3f9
Suggested-by: Al Viro <viro@zeniv.linux.org.uk>
Link: https://lore.kernel.org/all/20260423043906.GN3518998@ZenIV/
Link: https://lore.kernel.org/all/20260602002607.110866-1-kartikey406@gmail.com/T/
Signed-off-by: Deepanshu Kartikey <kartikey406@gmail.com>
Acked-by: Al Viro <viro@zeniv.linux.org.uk>
Link: https://lore.kernel.org/r/20260602025249.113828-1-kartikey406@gmail.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
kernel/bpf/inode.c

index 25c06a0118258dcafd8af0fff63458ff7272e65d..188c774a469ca56749145f88cca9bf2b084ab95f 100644 (file)
@@ -766,10 +766,18 @@ static void bpf_destroy_inode(struct inode *inode)
 {
        enum bpf_type type;
 
-       if (S_ISLNK(inode->i_mode))
-               kfree(inode->i_link);
        if (!bpf_inode_type(inode, &type))
                bpf_any_put(inode->i_private, type);
+}
+
+/*
+ * Called after RCU grace period - safe to free inode and anything
+ *  that might be accessed by RCU pathwalk (inode fields, i_link).
+ */
+static void bpf_free_inode(struct inode *inode)
+{
+       if (S_ISLNK(inode->i_mode))
+               kfree(inode->i_link);
        free_inode_nonrcu(inode);
 }
 
@@ -778,6 +786,7 @@ const struct super_operations bpf_super_ops = {
        .drop_inode     = inode_just_drop,
        .show_options   = bpf_show_options,
        .destroy_inode  = bpf_destroy_inode,
+       .free_inode     = bpf_free_inode,
 };
 
 enum {