Hi, Weiming Shi @ 2026-06-07 05:14 -07: > btrfs_get_name() reads the name length straight from the on-disk > inode_ref (or root_ref) and copies that many bytes into the caller's > buffer with no upper bound. The caller (exportfs_get_name()) supplies a > fixed NAME_MAX + 1 byte stack buffer, but name_len is a __le16 read from > the leaf and the tree-checker only bounds it to the item size, not to > BTRFS_NAME_LEN. A crafted leaf with name_len = 4096 therefore overflows > the 256-byte buffer with attacker-controlled bytes. It is reachable from > a mounted untrusted btrfs image via open_by_handle_at(), and on btrfs > filesystems exported over NFS. > > BUG: KASAN: stack-out-of-bounds in read_extent_buffer (fs/btrfs/extent_io.c:3742) > Write of size 633 at addr ffffc90006c9fc40 by task exploit/5192 > read_extent_buffer (fs/btrfs/extent_io.c:3742) > btrfs_get_name (fs/btrfs/export.c:289) > reconnect_path (fs/exportfs/expfs.c:222) > exportfs_decode_fh_raw (fs/exportfs/expfs.c:473) > do_handle_open (fs/fhandle.c:230) > do_syscall_64 (arch/x86/entry/syscall_64.c:94) > Kernel panic - not syncing: stack-protector: Kernel stack is corrupted > > Reject any name_len greater than BTRFS_NAME_LEN before the copy. Such a > name is never valid on disk, so this only rejects corrupted leaves and > leaves valid names unchanged. > > Fixes: 2ede0daf0154 ("Btrfs: handle NFS lookups properly") > Reported-by: Xiang Mei > Assisted-by: Claude:claude-opus-4-8 > Signed-off-by: Weiming Shi > --- > fs/btrfs/export.c | 10 ++++++++++ > 1 file changed, 10 insertions(+) > > diff --git a/fs/btrfs/export.c b/fs/btrfs/export.c > index c403117ac..a54c6e8b3 100644 > --- a/fs/btrfs/export.c > +++ b/fs/btrfs/export.c > @@ -285,6 +285,16 @@ static int btrfs_get_name(struct dentry *parent, char *name, > name_len = btrfs_inode_ref_name_len(leaf, iref); > } > > + /* The caller's buffer is only NAME_MAX + 1 bytes. */ > + if (name_len > BTRFS_NAME_LEN) { > + btrfs_err(fs_info, > + "corrupt name length %d for inode %llu in root %llu, max %d", > + name_len, btrfs_ino(BTRFS_I(inode)), > + btrfs_root_id(BTRFS_I(inode)->root), BTRFS_NAME_LEN); > + btrfs_free_path(path); Nitpicking: this is not necessary as 'path' is declared with a cleanup attribute. > + return -EUCLEAN; > + } > + > read_extent_buffer(leaf, name, name_ptr, name_len); > btrfs_free_path(path);