Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

jfs: add dtroot integrity check to prevent index out-of-bounds

Add check_dtroot() to validate dtroot_t integrity, focusing on preventing
index/pointer overflows from on-disk corruption.

Key checks:
- freecnt bounded by [0, DTROOTMAXSLOT-1] (slot[0] reserved for header).
- freelist validity: -1 when freecnt=0; 1~DTROOTMAXSLOT-1 when non-zero,
with linked list checks (no duplicates, proper termination via next=-1).
- stbl bounds: nextindex within stbl array size; entries within 0~8, no
duplicates (excluding idx=0).

Invoked in copy_from_dinode() when loading directory inodes, catching
corruption early before directory operations trigger out-of-bounds access.

This fixes the following UBSAN warning.

[ 101.832754][ T5960] ------------[ cut here ]------------
[ 101.832762][ T5960] UBSAN: array-index-out-of-bounds in fs/jfs/jfs_dtree.c:3713:8
[ 101.832792][ T5960] index -1 is out of range for type 'struct dtslot[128]'
[ 101.832807][ T5960] CPU: 2 UID: 0 PID: 5960 Comm: 5f7f0caf9979e9d Tainted: G E 6.18.0-rc4-00250-g2603eb907f03 #119 PREEMPT_{RT,(full
[ 101.832817][ T5960] Tainted: [E]=UNSIGNED_MODULE
[ 101.832819][ T5960] Hardware name: QEMU Ubuntu 25.04 PC (i440FX + PIIX, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
[ 101.832823][ T5960] Call Trace:
[ 101.832833][ T5960] <TASK>
[ 101.832838][ T5960] dump_stack_lvl+0x189/0x250
[ 101.832909][ T5960] ? __pfx_dump_stack_lvl+0x10/0x10
[ 101.832925][ T5960] ? __pfx__printk+0x10/0x10
[ 101.832934][ T5960] ? rt_mutex_slowunlock+0x493/0x8a0
[ 101.832959][ T5960] ubsan_epilogue+0xa/0x40
[ 101.832966][ T5960] __ubsan_handle_out_of_bounds+0xe9/0xf0
[ 101.833007][ T5960] dtInsertEntry+0x936/0x1430 [jfs]
[ 101.833094][ T5960] dtSplitPage+0x2c8b/0x3ed0 [jfs]
[ 101.833177][ T5960] ? __pfx_rt_mutex_slowunlock+0x10/0x10
[ 101.833193][ T5960] dtInsert+0x109b/0x6000 [jfs]
[ 101.833283][ T5960] ? rt_mutex_slowunlock+0x493/0x8a0
[ 101.833296][ T5960] ? __pfx_rt_mutex_slowunlock+0x10/0x10
[ 101.833307][ T5960] ? rt_spin_unlock+0x161/0x200
[ 101.833315][ T5960] ? __pfx_dtInsert+0x10/0x10 [jfs]
[ 101.833391][ T5960] ? txLock+0xaf9/0x1cb0 [jfs]
[ 101.833477][ T5960] ? dtInitRoot+0x22a/0x670 [jfs]
[ 101.833556][ T5960] jfs_mkdir+0x6ec/0xa70 [jfs]
[ 101.833636][ T5960] ? __pfx_jfs_mkdir+0x10/0x10 [jfs]
[ 101.833721][ T5960] ? generic_permission+0x2e5/0x690
[ 101.833760][ T5960] ? bpf_lsm_inode_mkdir+0x9/0x20
[ 101.833776][ T5960] vfs_mkdir+0x306/0x510
[ 101.833786][ T5960] do_mkdirat+0x247/0x590
[ 101.833795][ T5960] ? __pfx_do_mkdirat+0x10/0x10
[ 101.833804][ T5960] ? getname_flags+0x1e5/0x540
[ 101.833815][ T5960] __x64_sys_mkdir+0x6c/0x80
[ 101.833823][ T5960] do_syscall_64+0xfa/0xfa0
[ 101.833832][ T5960] ? lockdep_hardirqs_on+0x9c/0x150
[ 101.833840][ T5960] ? entry_SYSCALL_64_after_hwframe+0x77/0x7f
[ 101.833847][ T5960] ? exc_page_fault+0xab/0x100
[ 101.833856][ T5960] entry_SYSCALL_64_after_hwframe+0x77/0x7f

Signed-off-by: Yun Zhou <yun.zhou@windriver.com>
Signed-off-by: Dave Kleikamp <dave.kleikamp@oracle.com>

authored by

Yun Zhou and committed by
Dave Kleikamp
c83abc76 1f318b96

+92
+86
fs/jfs/jfs_dtree.c
··· 4297 4297 4298 4298 return 0; 4299 4299 } 4300 + 4301 + bool check_dtroot(dtroot_t *p) 4302 + { 4303 + DECLARE_BITMAP(bitmap, DTROOTMAXSLOT) = {0}; 4304 + int i; 4305 + 4306 + /* freecnt cannot be negative or exceed DTROOTMAXSLOT-1 4307 + * (since slot[0] is occupied by the header). 4308 + */ 4309 + if (unlikely(p->header.freecnt < 0 || 4310 + p->header.freecnt > DTROOTMAXSLOT - 1)) { 4311 + jfs_err("Bad freecnt:%d in dtroot\n", p->header.freecnt); 4312 + return false; 4313 + } else if (p->header.freecnt == 0) { 4314 + /* No free slots: freelist must be -1 */ 4315 + if (unlikely(p->header.freelist != -1)) { 4316 + jfs_err("freecnt=0, but freelist=%d in dtroot\n", 4317 + p->header.freelist); 4318 + return false; 4319 + } 4320 + } else { 4321 + int fsi, i; 4322 + /* When there are free slots, freelist must be a valid slot index in 4323 + * 1~DTROOTMAXSLOT-1(since slot[0] is occupied by the header). 4324 + */ 4325 + if (unlikely(p->header.freelist < 1 || 4326 + p->header.freelist >= DTROOTMAXSLOT)) { 4327 + jfs_err("Bad freelist:%d in dtroot\n", p->header.freelist); 4328 + return false; 4329 + } 4330 + 4331 + /* Traverse the free list to check validity of all node indices */ 4332 + fsi = p->header.freelist; 4333 + for (i = 0; i < p->header.freecnt - 1; i++) { 4334 + /* Check for duplicate indices in the free list */ 4335 + if (unlikely(__test_and_set_bit(fsi, bitmap))) { 4336 + jfs_err("duplicate index%d in slot in dtroot\n", fsi); 4337 + return false; 4338 + } 4339 + fsi = p->slot[fsi].next; 4340 + 4341 + /* Ensure the next slot index in the free list is valid */ 4342 + if (unlikely(fsi < 1 || fsi >= DTROOTMAXSLOT)) { 4343 + jfs_err("Bad index:%d in slot in dtroot\n", fsi); 4344 + return false; 4345 + } 4346 + } 4347 + 4348 + /* The last node in the free list must terminate with next = -1 */ 4349 + if (unlikely(p->slot[fsi].next != -1)) { 4350 + jfs_err("Bad next:%d of the last slot in dtroot\n", 4351 + p->slot[fsi].next); 4352 + return false; 4353 + } 4354 + } 4355 + 4356 + /* Validate nextindex (next free entry index in stbl) 4357 + * stbl array has size 8 (indices 0~7). 4358 + * It may get set to 8 when the last free slot has been filled. 4359 + */ 4360 + if (unlikely(p->header.nextindex > ARRAY_SIZE(p->header.stbl))) { 4361 + jfs_err("Bad nextindex:%d in dtroot\n", p->header.nextindex); 4362 + return false; 4363 + } 4364 + 4365 + /* Validate index validity of stbl array (8 elements) 4366 + * Each entry in stbl is a slot index, with valid range: -1 (invalid) 4367 + * or 0~8 (slot[0]~slot[8]) 4368 + */ 4369 + for (i = 0; i < p->header.nextindex; i++) { 4370 + int idx = p->header.stbl[i]; 4371 + 4372 + if (unlikely(idx < 0 || idx >= 9)) { 4373 + jfs_err("Bad index:%d of stbl[%d] in dtroot\n", idx, i); 4374 + return false; /* stbl entry points out of slot array range */ 4375 + } 4376 + 4377 + /* Check for duplicate valid indices (skip check for idx=0) */ 4378 + if (unlikely(idx && __test_and_set_bit(idx, bitmap))) { 4379 + jfs_err("Duplicate index:%d in stbl in dtroot\n", idx); 4380 + return false; 4381 + } 4382 + } 4383 + 4384 + return true; 4385 + }
+2
fs/jfs/jfs_dtree.h
··· 253 253 ino_t * orig_ino, ino_t new_ino, int flag); 254 254 255 255 extern int jfs_readdir(struct file *file, struct dir_context *ctx); 256 + 257 + extern bool check_dtroot(dtroot_t *p); 256 258 #endif /* !_H_JFS_DTREE */
+4
fs/jfs/jfs_imap.c
··· 3102 3102 3103 3103 if (S_ISDIR(ip->i_mode)) { 3104 3104 memcpy(&jfs_ip->u.dir, &dip->u._dir, 384); 3105 + if (!check_dtroot(&jfs_ip->i_dtroot)) { 3106 + jfs_error(ip->i_sb, "Corrupt dtroot\n"); 3107 + return -EIO; 3108 + } 3105 3109 } else if (S_ISREG(ip->i_mode) || S_ISLNK(ip->i_mode)) { 3106 3110 memcpy(&jfs_ip->i_xtroot, &dip->di_xtroot, 288); 3107 3111 } else