All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] xfs: validate btree block level on read
@ 2026-07-31 12:03 David Lee
  0 siblings, 0 replies; only message in thread
From: David Lee @ 2026-07-31 12:03 UTC (permalink / raw)
  To: cem
  Cc: David Lee, Kyle Zeng, Dominik 'Disconnect3d' Czarnota,
	linux-xfs, linux-kernel

Btree buffer verifiers validate record counts against the level stored in
the block itself.  Callers of xfs_btree_read_buf_block(), however, use
the returned block according to the level they are traversing.  A
corrupted sibling pointer can therefore return a verifier-valid block
from another level and cause out-of-bounds accesses when layout-specific
entries are moved.

Pass the expected level into xfs_btree_read_buf_block() and reject
blocks whose on-disk level differs before returning them.  This
centralizes the check for traversal and sibling reads.

Fixes: 3d3e6f64e22c ("xfs: verify btree blocks as they are read from disk")
Bug found and triaged by OpenAI Security Research and 
validated by Trail of Bits.

Assisted-by: Codex:gpt-5.6-sol gpt-5.5-cyber
Signed-off-by: Kyle Zeng <kylebot@openai.com>
---
Trail of Bits has a reproducer for this bug that triggers ㅁ
KASAN use-after-free and can share if needed.

 fs/xfs/libxfs/xfs_btree.c         | 48 +++++++++++++++++++------------
 fs/xfs/libxfs/xfs_btree.h         |  2 +-
 fs/xfs/libxfs/xfs_btree_staging.c |  9 +++---
 3 files changed, 36 insertions(+), 23 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_btree.c b/fs/xfs/libxfs/xfs_btree.c
index 60ef7f08b..b2db143bb 100644
--- a/fs/xfs/libxfs/xfs_btree.c
+++ b/fs/xfs/libxfs/xfs_btree.c
@@ -1378,13 +1378,14 @@ xfs_btree_get_buf_block(
 }
 
 /*
- * Read in the buffer at the given ptr and return the buffer and
- * the block pointer within the buffer.
+ * Read in the buffer at the given ptr and return the buffer and the block
+ * pointer within the buffer.  The block must be at the expected level.
  */
 int
 xfs_btree_read_buf_block(
 	struct xfs_btree_cur		*cur,
 	const union xfs_btree_ptr	*ptr,
+	int				level,
 	int				flags,
 	struct xfs_btree_block		**block,
 	struct xfs_buf			**bpp)
@@ -1409,6 +1410,14 @@ xfs_btree_read_buf_block(
 
 	xfs_btree_set_refs(cur, *bpp);
 	*block = XFS_BUF_TO_BLOCK(*bpp);
+	if (xfs_btree_get_level(*block) != level) {
+		xfs_buf_mark_corrupt(*bpp);
+		xfs_trans_brelse(cur->bc_tp, *bpp);
+		xfs_btree_mark_sick(cur);
+		*block = NULL;
+		*bpp = NULL;
+		return -EFSCORRUPTED;
+	}
 	return 0;
 }
 
@@ -1733,7 +1742,8 @@ xfs_btree_increment(
 
 		ptrp = xfs_btree_ptr_addr(cur, cur->bc_levels[lev].ptr, block);
 		--lev;
-		error = xfs_btree_read_buf_block(cur, ptrp, 0, &block, &bp);
+		error = xfs_btree_read_buf_block(cur, ptrp, lev, 0, &block,
+						 &bp);
 		if (error)
 			goto error0;
 
@@ -1827,7 +1837,8 @@ xfs_btree_decrement(
 
 		ptrp = xfs_btree_ptr_addr(cur, cur->bc_levels[lev].ptr, block);
 		--lev;
-		error = xfs_btree_read_buf_block(cur, ptrp, 0, &block, &bp);
+		error = xfs_btree_read_buf_block(cur, ptrp, lev, 0, &block,
+						 &bp);
 		if (error)
 			goto error0;
 		xfs_btree_setbuf(cur, lev, bp);
@@ -1904,7 +1915,7 @@ xfs_btree_lookup_get_block(
 		return 0;
 	}
 
-	error = xfs_btree_read_buf_block(cur, pp, 0, blkp, &bp);
+	error = xfs_btree_read_buf_block(cur, pp, level, 0, blkp, &bp);
 	if (error)
 		return error;
 
@@ -1912,10 +1923,6 @@ xfs_btree_lookup_get_block(
 	if (xfs_btree_check_block_owner(cur, *blkp) != NULL)
 		goto out_bad;
 
-	/* Did we get the level we were looking for? */
-	if (be16_to_cpu((*blkp)->bb_level) != level)
-		goto out_bad;
-
 	/* Check that internal nodes have at least one record. */
 	if (level != 0 && be16_to_cpu((*blkp)->bb_numrecs) == 0)
 		goto out_bad;
@@ -2457,7 +2464,7 @@ xfs_btree_lshift(
 		goto out0;
 
 	/* Set up the left neighbor as "left". */
-	error = xfs_btree_read_buf_block(cur, &lptr, 0, &left, &lbp);
+	error = xfs_btree_read_buf_block(cur, &lptr, level, 0, &left, &lbp);
 	if (error)
 		goto error0;
 
@@ -2653,7 +2660,7 @@ xfs_btree_rshift(
 		goto out0;
 
 	/* Set up the right neighbor as "right". */
-	error = xfs_btree_read_buf_block(cur, &rptr, 0, &right, &rbp);
+	error = xfs_btree_read_buf_block(cur, &rptr, level, 0, &right, &rbp);
 	if (error)
 		goto error0;
 
@@ -2940,8 +2947,8 @@ __xfs_btree_split(
 	 * point back to right instead of to left.
 	 */
 	if (!xfs_btree_ptr_is_null(cur, &rrptr)) {
-		error = xfs_btree_read_buf_block(cur, &rrptr,
-							0, &rrblock, &rrbp);
+		error = xfs_btree_read_buf_block(cur, &rrptr, level, 0,
+						 &rrblock, &rrbp);
 		if (error)
 			goto error0;
 		xfs_btree_set_sibling(cur, rrblock, &rptr, XFS_BB_LEFTSIB);
@@ -3365,7 +3372,8 @@ xfs_btree_new_root(
 		lbp = bp;
 		xfs_btree_buf_to_ptr(cur, lbp, &lptr);
 		left = block;
-		error = xfs_btree_read_buf_block(cur, &rptr, 0, &right, &rbp);
+		error = xfs_btree_read_buf_block(cur, &rptr,
+						 cur->bc_nlevels - 1, 0, &right, &rbp);
 		if (error)
 			goto error0;
 		bp = rbp;
@@ -3376,7 +3384,8 @@ xfs_btree_new_root(
 		xfs_btree_buf_to_ptr(cur, rbp, &rptr);
 		right = block;
 		xfs_btree_get_sibling(cur, right, &lptr, XFS_BB_LEFTSIB);
-		error = xfs_btree_read_buf_block(cur, &lptr, 0, &left, &lbp);
+		error = xfs_btree_read_buf_block(cur, &lptr,
+						 cur->bc_nlevels - 1, 0, &left, &lbp);
 		if (error)
 			goto error0;
 		bp = lbp;
@@ -4394,7 +4403,8 @@ xfs_btree_delrec(
 		rptr = cptr;
 		right = block;
 		rbp = bp;
-		error = xfs_btree_read_buf_block(cur, &lptr, 0, &left, &lbp);
+		error = xfs_btree_read_buf_block(cur, &lptr, level, 0, &left,
+						 &lbp);
 		if (error)
 			goto error0;
 
@@ -4411,7 +4421,8 @@ xfs_btree_delrec(
 		lptr = cptr;
 		left = block;
 		lbp = bp;
-		error = xfs_btree_read_buf_block(cur, &rptr, 0, &right, &rbp);
+		error = xfs_btree_read_buf_block(cur, &rptr, level, 0, &right,
+						 &rbp);
 		if (error)
 			goto error0;
 
@@ -4483,7 +4494,8 @@ xfs_btree_delrec(
 	/* If there is a right sibling, point it to the remaining block. */
 	xfs_btree_get_sibling(cur, left, &cptr, XFS_BB_RIGHTSIB);
 	if (!xfs_btree_ptr_is_null(cur, &cptr)) {
-		error = xfs_btree_read_buf_block(cur, &cptr, 0, &rrblock, &rrbp);
+		error = xfs_btree_read_buf_block(cur, &cptr, level, 0,
+						 &rrblock, &rrbp);
 		if (error)
 			goto error0;
 		xfs_btree_set_sibling(cur, rrblock, &lptr, XFS_BB_LEFTSIB);
diff --git a/fs/xfs/libxfs/xfs_btree.h b/fs/xfs/libxfs/xfs_btree.h
index 60e78572e..2153ee08d 100644
--- a/fs/xfs/libxfs/xfs_btree.h
+++ b/fs/xfs/libxfs/xfs_btree.h
@@ -650,7 +650,7 @@ int xfs_btree_get_buf_block(struct xfs_btree_cur *cur,
 		const union xfs_btree_ptr *ptr, struct xfs_btree_block **block,
 		struct xfs_buf **bpp);
 int xfs_btree_read_buf_block(struct xfs_btree_cur *cur,
-		const union xfs_btree_ptr *ptr, int flags,
+		const union xfs_btree_ptr *ptr, int level, int flags,
 		struct xfs_btree_block **block, struct xfs_buf **bpp);
 void xfs_btree_set_sibling(struct xfs_btree_cur *cur,
 		struct xfs_btree_block *block, const union xfs_btree_ptr *ptr,
diff --git a/fs/xfs/libxfs/xfs_btree_staging.c b/fs/xfs/libxfs/xfs_btree_staging.c
index c3c7ea548..a8af4f834 100644
--- a/fs/xfs/libxfs/xfs_btree_staging.c
+++ b/fs/xfs/libxfs/xfs_btree_staging.c
@@ -385,6 +385,7 @@ xfs_btree_bload_leaf(
 STATIC int
 xfs_btree_bload_node(
 	struct xfs_btree_cur	*cur,
+	unsigned int		level,
 	unsigned int		recs_this_block,
 	union xfs_btree_ptr	*child_ptr,
 	struct xfs_btree_block	*block)
@@ -407,8 +408,8 @@ xfs_btree_bload_node(
 		 * been reclaimed.  LRU refs will be set on the block, which is
 		 * desirable if the new btree commits.
 		 */
-		ret = xfs_btree_read_buf_block(cur, child_ptr, 0, &child_block,
-				&child_bp);
+		ret = xfs_btree_read_buf_block(cur, child_ptr, level - 1, 0,
+					       &child_block, &child_bp);
 		if (ret)
 			return ret;
 
@@ -767,8 +768,8 @@ xfs_btree_bload(
 			trace_xfs_btree_bload_block(cur, level, i, blocks,
 					&ptr, nr_this_block);
 
-			ret = xfs_btree_bload_node(cur, nr_this_block,
-					&child_ptr, block);
+			ret = xfs_btree_bload_node(cur, level, nr_this_block,
+						   &child_ptr, block);
 			if (ret)
 				goto out;
 

^ permalink raw reply related	[flat|nested] only message in thread

only message in thread, other threads:[~2026-07-31 12:03 UTC | newest]

Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-31 12:03 [PATCH] xfs: validate btree block level on read David Lee

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.