mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Nicholas Velten via B4 Relay <devnull+nicholasfvelten.gmail.com@kernel.org>
To: Dave Kleikamp <shaggy@kernel.org>
Cc: jfs-discussion@lists.sourceforge.net,
	linux-kernel@vger.kernel.org,
	 syzbot+43706fd1eb4c8dccdfe6@syzkaller.appspotmail.com,
	 Nicholas Velten <nicholasfvelten@gmail.com>
Subject: [PATCH] jfs: reject xtree pages whose BT_ROOT flag does not match
Date: Tue, 20 Oct 2026 13:32:21 -0300	[thread overview]
Message-ID: <20261020-jfs-xtroot-check-v1-1-4877a93be93e@gmail.com> (raw)

From: Nicholas Velten <nicholasfvelten@gmail.com>

syzbot reports a crash in release_metapage() when an inode from a
corrupted JFS image is evicted:

  Oops: stack segment: 0000 [#1] SMP KASAN PTI
  RIP: 0010:release_metapage+0x9e/0xab0 fs/jfs/jfs_metapage.c:870
  Call Trace:
   xtTruncate+0xecd/0x2f20 fs/jfs/jfs_xtree.c
   jfs_free_zero_link+0x35b/0x4c0 fs/jfs/namei.c:759
   jfs_evict_inode+0x363/0x440 fs/jfs/inode.c:159
   evict+0x61e/0xb10 fs/inode.c:837
   ...

The xtree root of a regular file is embedded in the inode, and
xt_getpage() returns it for bn == 0 with a pseudo metapage. Callers
such as xtTruncate() and xtTruncate_pmap() then decide whether they
hold the root by testing the on-disk p->header.flag & BT_ROOT. In the
reproducer's image a regular file has an xtroot flag of 0 instead of
DXD_INDEX | BT_ROOT | BT_LEAF, so xtTruncate() takes the non-root path
for the embedded root and releases the pseudo metapage as if it were
a real page, dereferencing an invalid folio.

xt_getpage() already validates nextindex and maxentry for both the
root and regular pages. Also reject a page whose BT_ROOT flag does not
match where it came from: the root (bn == 0) must have it set and any
other page must not. Healthy trees always satisfy this, since
xtInitRoot() sets BT_ROOT and xtSplitRoot()/xtSplitPage() never copy it
to a child page.

Tested in a KASAN guest: the syzbot reproducer crashes the unpatched
kernel. With this patch the bad page is reported as
"xt_getpage: xtree page corrupt" and the reproducer runs to completion.
On a clean mkfs.jfs image, writing 40 separate extents to one file
(forcing a root split), remounting, truncating, renaming over an
existing file and remounting again still works, with the data
unchanged.

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Reported-by: syzbot+43706fd1eb4c8dccdfe6@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=43706fd1eb4c8dccdfe6
Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Nicholas Velten <nicholasfvelten@gmail.com>
---
syzbot sent an AI-generated RFC for this bug in May
(https://lore.kernel.org/all/938f325a-46c6-4ade-8a21-b3271963810c@mail.kernel.org/),
which also bundled unrelated txnmgr/logmgr changes and got no replies.
This patch only addresses the xtree root confusion, by validating the
flag in xt_getpage() next to the existing nextindex/maxentry checks.

The reproducer is syzbot's C repro, run in a virtme-ng KASAN guest built
from next-20261009.

Not tested: architectures other than x86_64, and jfs_fsck on the
rejected image.
---
 fs/jfs/jfs_xtree.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/fs/jfs/jfs_xtree.c b/fs/jfs/jfs_xtree.c
index 28c3cf960..3a5f9d035 100644
--- a/fs/jfs/jfs_xtree.c
+++ b/fs/jfs/jfs_xtree.c
@@ -121,7 +121,8 @@ static inline xtpage_t *xt_getpage(struct inode *ip, s64 bn, struct metapage **m
 		(le16_to_cpu(p->header.nextindex) >
 			le16_to_cpu(p->header.maxentry)) ||
 		(le16_to_cpu(p->header.maxentry) >
-			((bn == 0) ? XTROOTMAXSLOT : PSIZE >> L2XTSLOTSIZE))) {
+			((bn == 0) ? XTROOTMAXSLOT : PSIZE >> L2XTSLOTSIZE)) ||
+		((bn == 0) != !!(p->header.flag & BT_ROOT))) {
 		jfs_error(ip->i_sb, "xt_getpage: xtree page corrupt\n");
 		BT_PUTPAGE(*mp);
 		*mp = NULL;

---
base-commit: becb95e45ef0dda9ffb35156d909d3da7a2789c3
change-id: 20261020-jfs-xtroot-check-e2c2159498b0

Best regards,
--  
Nicholas Velten <nicholasfvelten@gmail.com>



                 reply	other threads:[~2026-10-20 16:32 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

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=20261020-jfs-xtroot-check-v1-1-4877a93be93e@gmail.com \
    --to=devnull+nicholasfvelten.gmail.com@kernel.org \
    --cc=jfs-discussion@lists.sourceforge.net \
    --cc=linux-kernel@vger.kernel.org \
    --cc=nicholasfvelten@gmail.com \
    --cc=shaggy@kernel.org \
    --cc=syzbot+43706fd1eb4c8dccdfe6@syzkaller.appspotmail.com \
    /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

all inboxes | Powered by JetHome®