[PATCH] jfs: reject slot 0 in dtroot stbl entries
From: Nicholas Velten via B4 Relay
Date: Tue Oct 20 2026 - 09:49:00 EST
From: Nicholas Velten <nicholasfvelten@xxxxxxxxx>
syzbot reports a general protection fault in bpf_inode_storage_free()
when unmounting a corrupted JFS image:
Oops: general protection fault, probably for non-canonical address 0xdffffc018ef8da81
KASAN: probably user-memory-access in range [0x0000000c77c6d408-0x0000000c77c6d40f]
RIP: 0010:bpf_inode_storage_free+0x1f4/0x5c0 kernel/bpf/bpf_inode_storage.c:67
Call Trace:
security_inode_free+0xdc/0x180 security/security.c:1289
__destroy_inode+0x2a1/0x630 fs/inode.c:363
destroy_inode fs/inode.c:386 [inline]
evict+0x8d4/0xb50 fs/inode.c:847
...
The inode's i_security pointer has been overwritten by JFS itself.
The root directory of the image has a dtroot whose stbl contains slot
index 0. In dtroot_t, slot[0] overlays the header, so entries live in
slot[1] to slot[8]. check_dtroot() accepts 0 as a valid index and also
skips the duplicate check for it.
jfs_readdir() then calls add_missing_indices(), which treats slot[0]
as an ldtentry and writes the new directory index into its index
field. That field overlays header.stbl[4..7], so the write corrupts
the stbl itself. On the next readdir, stbl[4] holds the new index
(11 here), add_missing_indices() computes &p->slot[11] in a 9-slot
array, and the index write lands past the inline dtroot, in the
embedded vfs_inode. A hardware watchpoint on i_security confirms the
write comes from add_missing_indices(). KASAN cannot catch it because
it stays inside the jfs_inode_info object.
Reject stbl entries below 1 in check_dtroot(), as check_dtpage()
already does for regular pages, and apply the duplicate check to every
entry.
Tested in a KASAN guest: the syzbot reproducer crashes the unpatched
kernel within seconds. With this patch the image is rejected at inode
read ("Bad index:0 of stbl[3] in dtroot") and the reproducer runs to
completion. Mounting a clean mkfs.jfs image, creating 80 entries in the
root (forcing a root split), remounting, deleting them and remounting
again still works.
Fixes: c83abc766aeb ("jfs: add dtroot integrity check to prevent index out-of-bounds")
Reported-by: syzbot+b547ffac63b636628299@xxxxxxxxxxxxxxxxxxxxxxxxx
Closes: https://syzkaller.appspot.com/bug?extid=b547ffac63b636628299
Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Nicholas Velten <nicholasfvelten@xxxxxxxxx>
---
Root cause and the hardware-watchpoint trace are in the commit message.
The reproducer is syzbot's C repro, run in a virtme-ng KASAN guest built
from next-20261009 with JFS, BPF LSM and gadgetfs enabled.
Not tested: architectures other than x86_64, and jfs_fsck on the
rejected image.
---
fs/jfs/jfs_dtree.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/fs/jfs/jfs_dtree.c b/fs/jfs/jfs_dtree.c
index 8ce6e4458..b30eb0889 100644
--- a/fs/jfs/jfs_dtree.c
+++ b/fs/jfs/jfs_dtree.c
@@ -4360,19 +4360,19 @@ bool check_dtroot(dtroot_t *p)
}
/* Validate index validity of stbl array (8 elements)
- * Each entry in stbl is a slot index, with valid range: -1 (invalid)
- * or 0~8 (slot[0]~slot[8])
+ * Each entry in stbl is a slot index, with valid range 1~8
+ * (slot[1]~slot[8], since slot[0] is occupied by the header)
*/
for (i = 0; i < p->header.nextindex; i++) {
int idx = p->header.stbl[i];
- if (unlikely(idx < 0 || idx >= 9)) {
+ if (unlikely(idx < 1 || idx >= 9)) {
jfs_err("Bad index:%d of stbl[%d] in dtroot\n", idx, i);
return false; /* stbl entry points out of slot array range */
}
- /* Check for duplicate valid indices (skip check for idx=0) */
- if (unlikely(idx && __test_and_set_bit(idx, bitmap))) {
+ /* Check for duplicate indices */
+ if (unlikely(__test_and_set_bit(idx, bitmap))) {
jfs_err("Duplicate index:%d in stbl in dtroot\n", idx);
return false;
}
---
base-commit: becb95e45ef0dda9ffb35156d909d3da7a2789c3
change-id: 20261020-jfs-dtroot-stbl-slot0-50e98a7fa738
Best regards,
--
Nicholas Velten <nicholasfvelten@xxxxxxxxx>