Skip to content

Commit cce219b

Browse files
zhouyun1306kleikamp
authored andcommitted
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>
1 parent 119e448 commit cce219b

1 file changed

Lines changed: 111 additions & 3 deletions

File tree

fs/jfs/jfs_dmap.c

Lines changed: 111 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,93 @@ static const s8 budtab[256] = {
133133
2, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -1
134134
};
135135

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+
136223
/*
137224
* NAME: dbMount()
138225
*
@@ -1372,7 +1459,7 @@ dbAllocAG(struct bmap * bmp, int agno, s64 nblocks, int l2nb, s64 * results)
13721459
dcp = (struct dmapctl *) mp->data;
13731460
budmin = dcp->budmin;
13741461

1375-
if (dcp->leafidx != cpu_to_le32(CTLLEAFIND)) {
1462+
if (unlikely(!check_dmapctl(dcp))) {
13761463
jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmapctl page\n");
13771464
release_metapage(mp);
13781465
return -EIO;
@@ -1702,7 +1789,7 @@ static int dbFindCtl(struct bmap * bmp, int l2nb, int level, s64 * blkno)
17021789
dcp = (struct dmapctl *) mp->data;
17031790
budmin = dcp->budmin;
17041791

1705-
if (dcp->leafidx != cpu_to_le32(CTLLEAFIND)) {
1792+
if (unlikely(!check_dmapctl(dcp))) {
17061793
jfs_error(bmp->db_ipbmap->i_sb,
17071794
"Corrupt dmapctl page\n");
17081795
release_metapage(mp);
@@ -2485,7 +2572,7 @@ dbAdjCtl(struct bmap * bmp, s64 blkno, int newval, int alloc, int level)
24852572
return -EIO;
24862573
dcp = (struct dmapctl *) mp->data;
24872574

2488-
if (dcp->leafidx != cpu_to_le32(CTLLEAFIND)) {
2575+
if (unlikely(!check_dmapctl(dcp))) {
24892576
jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmapctl page\n");
24902577
release_metapage(mp);
24912578
return -EIO;
@@ -3454,6 +3541,11 @@ int dbExtendFS(struct inode *ipbmap, s64 blkno, s64 nblocks)
34543541
return -EIO;
34553542
}
34563543
l2dcp = (struct dmapctl *) l2mp->data;
3544+
if (unlikely(!check_dmapctl(l2dcp))) {
3545+
jfs_error(ipbmap->i_sb, "Corrupt dmapctl page\n");
3546+
release_metapage(l2mp);
3547+
return -EIO;
3548+
}
34573549

34583550
/* compute start L1 */
34593551
k = blkno >> L2MAXL1SIZE;
@@ -3471,6 +3563,10 @@ int dbExtendFS(struct inode *ipbmap, s64 blkno, s64 nblocks)
34713563
if (l1mp == NULL)
34723564
goto errout;
34733565
l1dcp = (struct dmapctl *) l1mp->data;
3566+
if (unlikely(!check_dmapctl(l1dcp))) {
3567+
jfs_error(ipbmap->i_sb, "Corrupt dmapctl page\n");
3568+
goto errout;
3569+
}
34743570

34753571
/* compute start L0 */
34763572
j = (blkno & (MAXL1SIZE - 1)) >> L2MAXL0SIZE;
@@ -3484,6 +3580,10 @@ int dbExtendFS(struct inode *ipbmap, s64 blkno, s64 nblocks)
34843580
goto errout;
34853581

34863582
l1dcp = (struct dmapctl *) l1mp->data;
3583+
if (unlikely(!check_dmapctl(l1dcp))) {
3584+
jfs_error(ipbmap->i_sb, "Corrupt dmapctl page\n");
3585+
goto errout;
3586+
}
34873587

34883588
/* compute start L0 */
34893589
j = 0;
@@ -3503,6 +3603,10 @@ int dbExtendFS(struct inode *ipbmap, s64 blkno, s64 nblocks)
35033603
if (l0mp == NULL)
35043604
goto errout;
35053605
l0dcp = (struct dmapctl *) l0mp->data;
3606+
if (unlikely(!check_dmapctl(l0dcp))) {
3607+
jfs_error(ipbmap->i_sb, "Corrupt dmapctl page\n");
3608+
goto errout;
3609+
}
35063610

35073611
/* compute start dmap */
35083612
i = (blkno & (MAXL0SIZE - 1)) >>
@@ -3518,6 +3622,10 @@ int dbExtendFS(struct inode *ipbmap, s64 blkno, s64 nblocks)
35183622
goto errout;
35193623

35203624
l0dcp = (struct dmapctl *) l0mp->data;
3625+
if (unlikely(!check_dmapctl(l0dcp))) {
3626+
jfs_error(ipbmap->i_sb, "Corrupt dmapctl page\n");
3627+
goto errout;
3628+
}
35213629

35223630
/* compute start dmap */
35233631
i = 0;

0 commit comments

Comments
 (0)