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 dmapctl integrity check to prevent invalid operations

Add check_dmapctl() to validate dmapctl structure integrity, focusing on
preventing invalid operations caused by on-disk corruption.

Key checks:
- nleafs bounded by [0, LPERCTL] (maximum leaf nodes per dmapctl).
- l2nleafs bounded by [0, L2LPERCTL] and consistent with nleafs
(nleafs must be 2^l2nleafs).
- leafidx must be exactly CTLLEAFIND (expected leaf index position).
- height bounded by [0, L2LPERCTL >> 1] (valid tree height range).
- budmin validity: NOFREE only if nleafs=0; otherwise >= BUDMIN.
- Leaf nodes fit within stree array (leafidx + nleafs <= CTLTREESIZE).
- Leaf node values are either non-negative or NOFREE.

Invoked in dbAllocAG(), dbFindCtl(), dbAdjCtl() and dbExtendFS() when
accessing dmapctl pages, catching corruption early before dmap operations
trigger invalid memory access or logic errors.

This fixes the following UBSAN warning.

[58245.668090][T14017] ------------[ cut here ]------------
[58245.668103][T14017] UBSAN: shift-out-of-bounds in fs/jfs/jfs_dmap.c:2641:11
[58245.668119][T14017] shift exponent 110 is too large for 32-bit type 'int'
[58245.668137][T14017] CPU: 0 UID: 0 PID: 14017 Comm: 4c1966e88c28fa9 Tainted: G E 6.18.0-rc4-00253-g21ce5d4ba045-dirty #124 PREEMPT_{RT,(full)}
[58245.668174][T14017] Tainted: [E]=UNSIGNED_MODULE
[58245.668176][T14017] Hardware name: QEMU Ubuntu 25.04 PC (i440FX + PIIX, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
[58245.668184][T14017] Call Trace:
[58245.668200][T14017] <TASK>
[58245.668208][T14017] dump_stack_lvl+0x189/0x250
[58245.668288][T14017] ? __pfx_dump_stack_lvl+0x10/0x10
[58245.668301][T14017] ? __pfx__printk+0x10/0x10
[58245.668315][T14017] ? lock_metapage+0x303/0x400 [jfs]
[58245.668406][T14017] ubsan_epilogue+0xa/0x40
[58245.668422][T14017] __ubsan_handle_shift_out_of_bounds+0x386/0x410
[58245.668462][T14017] dbSplit+0x1f8/0x200 [jfs]
[58245.668543][T14017] dbAdjCtl+0x34c/0xa20 [jfs]
[58245.668628][T14017] dbAllocNear+0x2ee/0x3d0 [jfs]
[58245.668710][T14017] dbAlloc+0x933/0xba0 [jfs]
[58245.668797][T14017] ea_write+0x374/0xdd0 [jfs]
[58245.668888][T14017] ? __pfx_ea_write+0x10/0x10 [jfs]
[58245.668966][T14017] ? __jfs_setxattr+0x76e/0x1120 [jfs]
[58245.669046][T14017] __jfs_setxattr+0xa01/0x1120 [jfs]
[58245.669135][T14017] ? __pfx___jfs_setxattr+0x10/0x10 [jfs]
[58245.669216][T14017] ? mutex_lock_nested+0x154/0x1d0
[58245.669252][T14017] ? __jfs_xattr_set+0xb9/0x170 [jfs]
[58245.669333][T14017] __jfs_xattr_set+0xda/0x170 [jfs]
[58245.669430][T14017] ? __pfx___jfs_xattr_set+0x10/0x10 [jfs]
[58245.669509][T14017] ? xattr_full_name+0x6f/0x90
[58245.669546][T14017] ? jfs_xattr_set+0x33/0x60 [jfs]
[58245.669636][T14017] ? __pfx_jfs_xattr_set+0x10/0x10 [jfs]
[58245.669726][T14017] __vfs_setxattr+0x43c/0x480
[58245.669743][T14017] __vfs_setxattr_noperm+0x12d/0x660
[58245.669756][T14017] vfs_setxattr+0x16b/0x2f0
[58245.669768][T14017] ? __pfx_vfs_setxattr+0x10/0x10
[58245.669782][T14017] filename_setxattr+0x274/0x600
[58245.669795][T14017] ? __pfx_filename_setxattr+0x10/0x10
[58245.669806][T14017] ? getname_flags+0x1e5/0x540
[58245.669829][T14017] path_setxattrat+0x364/0x3a0
[58245.669840][T14017] ? __pfx_path_setxattrat+0x10/0x10
[58245.669859][T14017] ? __se_sys_chdir+0x1b9/0x280
[58245.669876][T14017] __x64_sys_lsetxattr+0xbf/0xe0
[58245.669888][T14017] do_syscall_64+0xfa/0xfa0
[58245.669901][T14017] ? lockdep_hardirqs_on+0x9c/0x150
[58245.669913][T14017] ? entry_SYSCALL_64_after_hwframe+0x77/0x7f
[58245.669927][T14017] ? exc_page_fault+0xab/0x100
[58245.669937][T14017] entry_SYSCALL_64_after_hwframe+0x77/0x7f

Reported-by: syzbot+4c1966e88c28fa96e053@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=4c1966e88c28fa96e053
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
cce219b2 119e448b

+111 -3
+111 -3
fs/jfs/jfs_dmap.c
··· 134 134 }; 135 135 136 136 /* 137 + * check_dmapctl - Validate integrity of a dmapctl structure 138 + * @dcp: Pointer to the dmapctl structure to check 139 + * 140 + * Return: true if valid, false if corrupted 141 + */ 142 + static bool check_dmapctl(struct dmapctl *dcp) 143 + { 144 + s8 budmin = dcp->budmin; 145 + u32 nleafs, l2nleafs, leafidx, height; 146 + int i; 147 + 148 + nleafs = le32_to_cpu(dcp->nleafs); 149 + /* Check basic field ranges */ 150 + if (unlikely(nleafs > LPERCTL)) { 151 + jfs_err("dmapctl: invalid nleafs %u (max %u)", 152 + nleafs, LPERCTL); 153 + return false; 154 + } 155 + 156 + l2nleafs = le32_to_cpu(dcp->l2nleafs); 157 + if (unlikely(l2nleafs > L2LPERCTL)) { 158 + jfs_err("dmapctl: invalid l2nleafs %u (max %u)", 159 + l2nleafs, L2LPERCTL); 160 + return false; 161 + } 162 + 163 + /* Verify nleafs matches l2nleafs (must be power of two) */ 164 + if (unlikely((1U << l2nleafs) != nleafs)) { 165 + jfs_err("dmapctl: nleafs %u != 2^%u", 166 + nleafs, l2nleafs); 167 + return false; 168 + } 169 + 170 + leafidx = le32_to_cpu(dcp->leafidx); 171 + /* Check leaf index matches expected position */ 172 + if (unlikely(leafidx != CTLLEAFIND)) { 173 + jfs_err("dmapctl: invalid leafidx %u (expected %u)", 174 + leafidx, CTLLEAFIND); 175 + return false; 176 + } 177 + 178 + height = le32_to_cpu(dcp->height); 179 + /* Check tree height is within valid range */ 180 + if (unlikely(height > (L2LPERCTL >> 1))) { 181 + jfs_err("dmapctl: invalid height %u (max %u)", 182 + height, L2LPERCTL >> 1); 183 + return false; 184 + } 185 + 186 + /* Check budmin is valid (cannot be NOFREE for non-empty tree) */ 187 + if (budmin == NOFREE) { 188 + if (unlikely(nleafs > 0)) { 189 + jfs_err("dmapctl: budmin is NOFREE but nleafs %u", 190 + nleafs); 191 + return false; 192 + } 193 + } else if (unlikely(budmin < BUDMIN)) { 194 + jfs_err("dmapctl: invalid budmin %d (min %d)", 195 + budmin, BUDMIN); 196 + return false; 197 + } 198 + 199 + /* Check leaf nodes fit within stree array */ 200 + if (unlikely(leafidx + nleafs > CTLTREESIZE)) { 201 + jfs_err("dmapctl: leaf range exceeds stree size (end %u > %u)", 202 + leafidx + nleafs, CTLTREESIZE); 203 + return false; 204 + } 205 + 206 + /* Check leaf nodes have valid values */ 207 + for (i = leafidx; i < leafidx + nleafs; i++) { 208 + s8 val = dcp->stree[i]; 209 + 210 + if (unlikely(val < NOFREE)) { 211 + jfs_err("dmapctl: invalid leaf value %d at index %d", 212 + val, i); 213 + return false; 214 + } else if (unlikely(val > 31)) { 215 + jfs_err("dmapctl: leaf value %d too large at index %d", val, i); 216 + return false; 217 + } 218 + } 219 + 220 + return true; 221 + } 222 + 223 + /* 137 224 * NAME: dbMount() 138 225 * 139 226 * FUNCTION: initializate the block allocation map. ··· 1459 1372 dcp = (struct dmapctl *) mp->data; 1460 1373 budmin = dcp->budmin; 1461 1374 1462 - if (dcp->leafidx != cpu_to_le32(CTLLEAFIND)) { 1375 + if (unlikely(!check_dmapctl(dcp))) { 1463 1376 jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmapctl page\n"); 1464 1377 release_metapage(mp); 1465 1378 return -EIO; ··· 1789 1702 dcp = (struct dmapctl *) mp->data; 1790 1703 budmin = dcp->budmin; 1791 1704 1792 - if (dcp->leafidx != cpu_to_le32(CTLLEAFIND)) { 1705 + if (unlikely(!check_dmapctl(dcp))) { 1793 1706 jfs_error(bmp->db_ipbmap->i_sb, 1794 1707 "Corrupt dmapctl page\n"); 1795 1708 release_metapage(mp); ··· 2572 2485 return -EIO; 2573 2486 dcp = (struct dmapctl *) mp->data; 2574 2487 2575 - if (dcp->leafidx != cpu_to_le32(CTLLEAFIND)) { 2488 + if (unlikely(!check_dmapctl(dcp))) { 2576 2489 jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmapctl page\n"); 2577 2490 release_metapage(mp); 2578 2491 return -EIO; ··· 3541 3454 return -EIO; 3542 3455 } 3543 3456 l2dcp = (struct dmapctl *) l2mp->data; 3457 + if (unlikely(!check_dmapctl(l2dcp))) { 3458 + jfs_error(ipbmap->i_sb, "Corrupt dmapctl page\n"); 3459 + release_metapage(l2mp); 3460 + return -EIO; 3461 + } 3544 3462 3545 3463 /* compute start L1 */ 3546 3464 k = blkno >> L2MAXL1SIZE; ··· 3563 3471 if (l1mp == NULL) 3564 3472 goto errout; 3565 3473 l1dcp = (struct dmapctl *) l1mp->data; 3474 + if (unlikely(!check_dmapctl(l1dcp))) { 3475 + jfs_error(ipbmap->i_sb, "Corrupt dmapctl page\n"); 3476 + goto errout; 3477 + } 3566 3478 3567 3479 /* compute start L0 */ 3568 3480 j = (blkno & (MAXL1SIZE - 1)) >> L2MAXL0SIZE; ··· 3580 3484 goto errout; 3581 3485 3582 3486 l1dcp = (struct dmapctl *) l1mp->data; 3487 + if (unlikely(!check_dmapctl(l1dcp))) { 3488 + jfs_error(ipbmap->i_sb, "Corrupt dmapctl page\n"); 3489 + goto errout; 3490 + } 3583 3491 3584 3492 /* compute start L0 */ 3585 3493 j = 0; ··· 3603 3503 if (l0mp == NULL) 3604 3504 goto errout; 3605 3505 l0dcp = (struct dmapctl *) l0mp->data; 3506 + if (unlikely(!check_dmapctl(l0dcp))) { 3507 + jfs_error(ipbmap->i_sb, "Corrupt dmapctl page\n"); 3508 + goto errout; 3509 + } 3606 3510 3607 3511 /* compute start dmap */ 3608 3512 i = (blkno & (MAXL0SIZE - 1)) >> ··· 3622 3518 goto errout; 3623 3519 3624 3520 l0dcp = (struct dmapctl *) l0mp->data; 3521 + if (unlikely(!check_dmapctl(l0dcp))) { 3522 + jfs_error(ipbmap->i_sb, "Corrupt dmapctl page\n"); 3523 + goto errout; 3524 + } 3625 3525 3626 3526 /* compute start dmap */ 3627 3527 i = 0;