Linux filesystem development
 help / color / mirror / Atom feed
From: Breno Leitao <leitao@debian.org>
To: Farhad Alemi <farhad.alemi@berkeley.edu>
Cc: Andreas Hindborg <a.hindborg@kernel.org>,
	 linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: Re: [BUG] configfs: slab-use-after-free in configfs_drop_dentry() on rmdir
Date: Wed, 27 May 2026 11:27:57 +0100	[thread overview]
Message-ID: <ahbG94EFLD5ntnYr@gmail.com> (raw)
In-Reply-To: <CA+0ovCjv-VYvn-4jUj3wDn+DuP9FSv-GL+yPAHbR=ZGKU0wmGg@mail.gmail.com>

On Tue, May 26, 2026 at 08:42:00PM +0000, Farhad Alemi wrote:
> Hello Andreas and the configfs team,
> 
> I am reporting a configfs use-after-free found by syzkaller.
> 
> Summary:
> A configfs config-item rmdir(2) can land in configfs_drop_dentry() with
> sd->s_dentry pointing at a dentry whose RCU grace period has not yet
> elapsed. The spin_lock(&dentry->d_lock) at fs/configfs/inode.c:209 then
> reads freed slab memory and KASAN reports a slab-use-after-free.

Thanks for the report. I was able to reproduce it on linus' upstream
tree. 

I've came up with the following fix, would you mind trying it, please?


commit d78c04736ef0ede76093c780a188560b6baed3c3
Author: Breno Leitao <leitao@debian.org>
Date:   Wed May 27 06:25:49 2026 -0400

    configfs: fix UAF in configfs_drop_dentry() after failed attribute lookup
    
    When configfs_lookup() matches an attribute it publishes the new dentry
    into sd->s_dentry (and dentry->d_fsdata) before calling
    configfs_create() to allocate the inode:
    
            dentry->d_fsdata = configfs_get(sd);
            sd->s_dentry = dentry;
            spin_unlock(&configfs_dirent_lock);
    
            inode = configfs_create(dentry, mode);
            if (IS_ERR(inode)) {
                    configfs_put(sd);
                    return ERR_CAST(inode);
            }
    
    If configfs_create() fails (e.g. new_inode() returns NULL under memory
    pressure or fault injection), the lookup returns an error and the
    caller dputs the now-negative dentry, which goes through
    __dentry_kill() and is freed via call_rcu().
    
    Because the dentry never gained an inode it does not go through the
    .d_iput op (configfs_d_iput), which is the only place that clears
    sd->s_dentry.  sd therefore keeps a stale pointer to the freed dentry.
    A subsequent rmdir of the parent item walks the parent's s_children
    list in detach_attrs() and configfs_drop_dentry() does
    spin_lock(&sd->s_dentry->d_lock) on freed memory:
    
      BUG: KASAN: slab-use-after-free in _raw_spin_lock+0xac/0x110
      Read of size 1 at addr ffff00012bacd028 by task repro/2440
       _raw_spin_lock+0xac/0x110
       configfs_drop_dentry+0x48/0x158 [configfs]
       detach_attrs.isra.0+0x18c/0x494 [configfs]
       configfs_rmdir+0x450/0x71c [configfs]
       vfs_rmdir+0x170/0x620
       ...
      Freed by task 0:
       __d_free+0x28/0x34
       rcu_do_batch+0x37c/0x1bd0
       ...
      Last potentially related work creation:
       call_rcu+0x34/0x68
       dentry_free+0xe8/0x3e0
       __dentry_kill+0x404/0x604
       dput+0x14/0x30
       lookup_open.isra.0+0x6ac/0xc00
       path_openat+0xd18/0x2588
    
    Fix this by tearing down the sd<->dentry linkage in the
    configfs_create() error path, under configfs_dirent_lock, mirroring
    what configfs_d_iput() would have done for a positive dentry.
    
    Reported-by: Farhad Alemi <farhad.alemi@berkeley.edu>
    Signed-off-by: Breno Leitao <leitao@debian.org>

diff --git a/fs/configfs/dir.c b/fs/configfs/dir.c
index 362b6ff9b908..68d857dcb2d9 100644
--- a/fs/configfs/dir.c
+++ b/fs/configfs/dir.c
@@ -486,6 +486,22 @@ static struct dentry * configfs_lookup(struct inode *dir,
 
 			inode = configfs_create(dentry, mode);
 			if (IS_ERR(inode)) {
+				/*
+				 * configfs_create() failed (e.g. -ENOMEM
+				 * from new_inode()).  The dentry will be
+				 * dput()ed by the caller and freed via RCU;
+				 * because it never gained an inode,
+				 * configfs_d_iput() will not run to clear
+				 * sd->s_dentry.  Drop the linkage here so a
+				 * later detach_attrs() walking the parent's
+				 * s_children list does not dereference a
+				 * freed dentry in configfs_drop_dentry().
+				 */
+				spin_lock(&configfs_dirent_lock);
+				if (sd->s_dentry == dentry)
+					sd->s_dentry = NULL;
+				dentry->d_fsdata = NULL;
+				spin_unlock(&configfs_dirent_lock);
 				configfs_put(sd);
 				return ERR_CAST(inode);
 			}

  reply	other threads:[~2026-05-27 10:28 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-05-27  3:42 [BUG] configfs: slab-use-after-free in configfs_drop_dentry() on rmdir Farhad Alemi
2026-05-27 10:27 ` Breno Leitao [this message]
2026-05-30 17:18   ` Farhad Alemi
2026-06-02  7:17   ` Al Viro

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=ahbG94EFLD5ntnYr@gmail.com \
    --to=leitao@debian.org \
    --cc=a.hindborg@kernel.org \
    --cc=farhad.alemi@berkeley.edu \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox