Linux Btrfs filesystem development
 help / color / mirror / Atom feed
* [PATCH v2] btrfs: tree-checker: validate inode_ref and root_ref name lengths
@ 2026-06-08  8:35 Weiming Shi
  2026-06-08  9:19 ` Qu Wenruo
  2026-06-10 10:45 ` Weiming Shi
  0 siblings, 2 replies; 4+ messages in thread
From: Weiming Shi @ 2026-06-08  8:35 UTC (permalink / raw)
  To: linux-btrfs; +Cc: dsterba, josef, clm, xmei5, Weiming Shi

The on-disk name length of an INODE_REF and a ROOT_REF/ROOT_BACKREF is a
__le16 and can be up to 65535, but a btrfs name is never longer than
BTRFS_NAME_LEN (255). The tree-checker validated only that a ref's name
stays within the item, not that name_len <= BTRFS_NAME_LEN, and it did
not validate ROOT_REF/ROOT_BACKREF items at all. A reader that copies
name_len bytes into a NAME_MAX-sized buffer therefore overflows it.

For example btrfs_get_name(), used by the NFS/exportfs ->get_name op,
copies the inode_ref/root_ref name into the caller's NAME_MAX + 1 byte
stack buffer with no bound of its own, which a crafted leaf with
name_len = 4096 overflows. It is reachable from a mounted untrusted
btrfs image via open_by_handle_at(), and on btrfs 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 name_len > BTRFS_NAME_LEN for INODE_REF, and add a check_root_ref()
that validates ROOT_REF/ROOT_BACKREF item size and name length. Bounding
the length at read time protects every consumer instead of patching each
copy site, so the corrupted leaf is refused when the tree block is read.

Fixes: 2ede0daf0154 ("Btrfs: handle NFS lookups properly")
Reported-by: Xiang Mei <xmei5@asu.edu>
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Weiming Shi <bestswngs@gmail.com>
---

v2:
- Check name_len in the tree-checker instead of btrfs_get_name().
- Add check_root_ref() for ROOT_REF/ROOT_BACKREF items.

 fs/btrfs/tree-checker.c | 40 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 40 insertions(+)

diff --git a/fs/btrfs/tree-checker.c b/fs/btrfs/tree-checker.c
index 60bba7fbe..fedb0bd55 100644
--- a/fs/btrfs/tree-checker.c
+++ b/fs/btrfs/tree-checker.c
@@ -1760,6 +1760,12 @@ static int check_inode_ref(struct extent_buffer *leaf,
 				ptr, end, namelen);
 			return -EUCLEAN;
 		}
+		if (unlikely(namelen > BTRFS_NAME_LEN)) {
+			inode_ref_err(leaf, slot,
+				"inode ref name too long, namelen %u max %u",
+				namelen, BTRFS_NAME_LEN);
+			return -EUCLEAN;
+		}
 
 		/*
 		 * NOTE: In theory we should record all found index numbers
@@ -1804,6 +1810,36 @@ static int check_inode_extref(struct extent_buffer *leaf,
 	return 0;
 }
 
+static int check_root_ref(struct extent_buffer *leaf, int slot)
+{
+	struct btrfs_root_ref *ref;
+	u32 item_size = btrfs_item_size(leaf, slot);
+	u16 namelen;
+
+	if (unlikely(item_size < sizeof(*ref))) {
+		generic_err(leaf, slot,
+			"invalid root ref item size, have %u expect >= %zu",
+			item_size, sizeof(*ref));
+		return -EUCLEAN;
+	}
+
+	ref = btrfs_item_ptr(leaf, slot, struct btrfs_root_ref);
+	namelen = btrfs_root_ref_name_len(leaf, ref);
+	if (unlikely(sizeof(*ref) + namelen != item_size)) {
+		generic_err(leaf, slot,
+			"invalid root ref name len, have %u expect %zu",
+			namelen, item_size - sizeof(*ref));
+		return -EUCLEAN;
+	}
+	if (unlikely(namelen > BTRFS_NAME_LEN)) {
+		generic_err(leaf, slot,
+			"root ref name too long, namelen %u max %u",
+			namelen, BTRFS_NAME_LEN);
+		return -EUCLEAN;
+	}
+	return 0;
+}
+
 static int check_raid_stripe_extent(const struct extent_buffer *leaf,
 				    const struct btrfs_key *key, int slot)
 {
@@ -1937,6 +1973,10 @@ static enum btrfs_tree_block_status check_leaf_item(struct extent_buffer *leaf,
 	case BTRFS_ROOT_ITEM_KEY:
 		ret = check_root_item(leaf, key, slot);
 		break;
+	case BTRFS_ROOT_REF_KEY:
+	case BTRFS_ROOT_BACKREF_KEY:
+		ret = check_root_ref(leaf, slot);
+		break;
 	case BTRFS_EXTENT_ITEM_KEY:
 	case BTRFS_METADATA_ITEM_KEY:
 		ret = check_extent_item(leaf, key, slot, prev_key);
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2026-06-10 11:26 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-08  8:35 [PATCH v2] btrfs: tree-checker: validate inode_ref and root_ref name lengths Weiming Shi
2026-06-08  9:19 ` Qu Wenruo
2026-06-10 10:45 ` Weiming Shi
2026-06-10 11:26   ` Qu Wenruo

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox