Skip to content

Commit 1d51b37

Browse files
committed
Merge tag 'jfs-7.1' of github.com:kleikamp/linux-shaggy
Pull jfs updates from Dave Kleikamp: "More robust data integrity checking and some fixes" * tag 'jfs-7.1' of github.com:kleikamp/linux-shaggy: jfs: avoid -Wtautological-constant-out-of-range-compare warning again JFS: always load filesystem UUID during mount jfs: hold LOG_LOCK on umount to avoid null-ptr-deref jfs: Set the lbmDone flag at the end of lbmIODone jfs: fix corrupted list in dbUpdatePMap jfs: add dmapctl integrity check to prevent invalid operations jfs: add dtpage integrity check to prevent index/pointer overflows jfs: add dtroot integrity check to prevent index out-of-bounds
2 parents 5414f3f + dad98c5 commit 1d51b37

10 files changed

Lines changed: 344 additions & 30 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;

fs/jfs/jfs_dtree.c

Lines changed: 188 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -115,10 +115,7 @@ struct dtsplit {
115115
do { \
116116
BT_GETPAGE(IP, BN, MP, dtpage_t, SIZE, P, RC, i_dtroot); \
117117
if (!(RC)) { \
118-
if (((P)->header.nextindex > \
119-
(((BN) == 0) ? DTROOTMAXSLOT : (P)->header.maxslot)) || \
120-
((BN) && (((P)->header.maxslot > DTPAGEMAXSLOT) || \
121-
((P)->header.stblindex >= DTPAGEMAXSLOT)))) { \
118+
if ((BN) && !check_dtpage(P)) { \
122119
BT_PUTPAGE(MP); \
123120
jfs_error((IP)->i_sb, \
124121
"DT_GETPAGE: dtree page corrupt\n"); \
@@ -4297,3 +4294,190 @@ int dtModify(tid_t tid, struct inode *ip,
42974294

42984295
return 0;
42994296
}
4297+
4298+
bool check_dtroot(dtroot_t *p)
4299+
{
4300+
DECLARE_BITMAP(bitmap, DTROOTMAXSLOT) = {0};
4301+
int i;
4302+
4303+
/* freecnt cannot be negative or exceed DTROOTMAXSLOT-1
4304+
* (since slot[0] is occupied by the header).
4305+
*/
4306+
if (unlikely(p->header.freecnt < 0 ||
4307+
p->header.freecnt > DTROOTMAXSLOT - 1)) {
4308+
jfs_err("Bad freecnt:%d in dtroot\n", p->header.freecnt);
4309+
return false;
4310+
} else if (p->header.freecnt == 0) {
4311+
/* No free slots: freelist must be -1 */
4312+
if (unlikely(p->header.freelist != -1)) {
4313+
jfs_err("freecnt=0, but freelist=%d in dtroot\n",
4314+
p->header.freelist);
4315+
return false;
4316+
}
4317+
} else {
4318+
int fsi, i;
4319+
/* When there are free slots, freelist must be a valid slot index in
4320+
* 1~DTROOTMAXSLOT-1(since slot[0] is occupied by the header).
4321+
*/
4322+
if (unlikely(p->header.freelist < 1 ||
4323+
p->header.freelist >= DTROOTMAXSLOT)) {
4324+
jfs_err("Bad freelist:%d in dtroot\n", p->header.freelist);
4325+
return false;
4326+
}
4327+
4328+
/* Traverse the free list to check validity of all node indices */
4329+
fsi = p->header.freelist;
4330+
for (i = 0; i < p->header.freecnt - 1; i++) {
4331+
/* Check for duplicate indices in the free list */
4332+
if (unlikely(__test_and_set_bit(fsi, bitmap))) {
4333+
jfs_err("duplicate index%d in slot in dtroot\n", fsi);
4334+
return false;
4335+
}
4336+
fsi = p->slot[fsi].next;
4337+
4338+
/* Ensure the next slot index in the free list is valid */
4339+
if (unlikely(fsi < 1 || fsi >= DTROOTMAXSLOT)) {
4340+
jfs_err("Bad index:%d in slot in dtroot\n", fsi);
4341+
return false;
4342+
}
4343+
}
4344+
4345+
/* The last node in the free list must terminate with next = -1 */
4346+
if (unlikely(p->slot[fsi].next != -1)) {
4347+
jfs_err("Bad next:%d of the last slot in dtroot\n",
4348+
p->slot[fsi].next);
4349+
return false;
4350+
}
4351+
}
4352+
4353+
/* Validate nextindex (next free entry index in stbl)
4354+
* stbl array has size 8 (indices 0~7).
4355+
* It may get set to 8 when the last free slot has been filled.
4356+
*/
4357+
if (unlikely(p->header.nextindex > ARRAY_SIZE(p->header.stbl))) {
4358+
jfs_err("Bad nextindex:%d in dtroot\n", p->header.nextindex);
4359+
return false;
4360+
}
4361+
4362+
/* Validate index validity of stbl array (8 elements)
4363+
* Each entry in stbl is a slot index, with valid range: -1 (invalid)
4364+
* or 0~8 (slot[0]~slot[8])
4365+
*/
4366+
for (i = 0; i < p->header.nextindex; i++) {
4367+
int idx = p->header.stbl[i];
4368+
4369+
if (unlikely(idx < 0 || idx >= 9)) {
4370+
jfs_err("Bad index:%d of stbl[%d] in dtroot\n", idx, i);
4371+
return false; /* stbl entry points out of slot array range */
4372+
}
4373+
4374+
/* Check for duplicate valid indices (skip check for idx=0) */
4375+
if (unlikely(idx && __test_and_set_bit(idx, bitmap))) {
4376+
jfs_err("Duplicate index:%d in stbl in dtroot\n", idx);
4377+
return false;
4378+
}
4379+
}
4380+
4381+
return true;
4382+
}
4383+
4384+
bool check_dtpage(dtpage_t *p)
4385+
{
4386+
DECLARE_BITMAP(bitmap, DTPAGEMAXSLOT) = {0};
4387+
const int stblsize = ((PSIZE >> L2DTSLOTSIZE) + 31) >> L2DTSLOTSIZE;
4388+
int i;
4389+
4390+
/* Validate maxslot (maximum number of slots in the page)
4391+
* dtpage_t slot array is defined to hold up to DTPAGEMAXSLOT (128) slots
4392+
*/
4393+
if (unlikely(p->header.maxslot != DTPAGEMAXSLOT)) {
4394+
jfs_err("Bad maxslot:%d in dtpage (expected %d)\n",
4395+
p->header.maxslot, DTPAGEMAXSLOT);
4396+
return false;
4397+
}
4398+
4399+
/* freecnt cannot be negative or exceed DTPAGEMAXSLOT-1
4400+
* (since slot[0] is occupied by the header).
4401+
*/
4402+
if (unlikely(p->header.freecnt < 0 ||
4403+
p->header.freecnt > DTPAGEMAXSLOT - 1)) {
4404+
jfs_err("Bad freecnt:%d in dtpage\n", p->header.freecnt);
4405+
return false;
4406+
} else if (p->header.freecnt == 0) {
4407+
/* No free slots: freelist must be -1 */
4408+
if (unlikely(p->header.freelist != -1)) {
4409+
jfs_err("freecnt=0 but freelist=%d in dtpage\n",
4410+
p->header.freelist);
4411+
return false;
4412+
}
4413+
} else {
4414+
int fsi;
4415+
4416+
if (unlikely(p->header.freelist < 1)) {
4417+
jfs_err("Bad freelist:%d in dtpage\n", p->header.freelist);
4418+
return false;
4419+
}
4420+
4421+
/* Traverse the free list to check validity of all node indices */
4422+
fsi = p->header.freelist;
4423+
for (i = 0; i < p->header.freecnt - 1; i++) {
4424+
/* Check for duplicate indices in the free list */
4425+
if (unlikely(__test_and_set_bit(fsi, bitmap))) {
4426+
jfs_err("duplicate index%d in slot in dtpage\n", fsi);
4427+
return false;
4428+
}
4429+
fsi = p->slot[fsi].next;
4430+
4431+
/* Ensure the next slot index in the free list is valid */
4432+
if (unlikely(fsi < 1 || fsi >= DTPAGEMAXSLOT)) {
4433+
jfs_err("Bad index:%d in slot in dtpage\n", fsi);
4434+
return false;
4435+
}
4436+
}
4437+
4438+
/* The last node in the free list must terminate with next = -1 */
4439+
if (unlikely(p->slot[fsi].next != -1)) {
4440+
jfs_err("Bad next:%d of the last slot in dtpage\n",
4441+
p->slot[fsi].next);
4442+
return false;
4443+
}
4444+
}
4445+
4446+
/* stbl must be little then DTPAGEMAXSLOT */
4447+
if (unlikely(p->header.stblindex >= DTPAGEMAXSLOT - stblsize)) {
4448+
jfs_err("Bad stblindex:%d in dtpage (stbl size %d)\n",
4449+
p->header.stblindex, stblsize);
4450+
return false;
4451+
}
4452+
4453+
/* nextindex must be little then stblsize*32 */
4454+
if (unlikely(p->header.nextindex > (stblsize << L2DTSLOTSIZE))) {
4455+
jfs_err("Bad nextindex:%d in dtpage (stbl size %d)\n",
4456+
p->header.nextindex, stblsize);
4457+
return false;
4458+
}
4459+
4460+
/* Validate stbl entries
4461+
* Each entry is a slot index, valid range: -1 (invalid) or
4462+
* [0, nextindex-1] (valid data slots)
4463+
* (stblindex and higher slots are reserved for stbl itself)
4464+
*/
4465+
for (i = 0; i < p->header.nextindex; i++) {
4466+
int idx = DT_GETSTBL(p)[i];
4467+
4468+
/* Check if index is out of valid data slot range */
4469+
if (unlikely(idx < 1 || idx >= DTPAGEMAXSLOT)) {
4470+
jfs_err("Bad stbl[%d] index:%d (stblindex %d) in dtpage\n",
4471+
i, idx, p->header.stblindex);
4472+
return false;
4473+
}
4474+
4475+
/* Check for duplicate valid indices (skip -1) */
4476+
if (unlikely(__test_and_set_bit(idx, bitmap))) {
4477+
jfs_err("Duplicate index:%d in stbl of dtpage\n", idx);
4478+
return false;
4479+
}
4480+
}
4481+
4482+
return true;
4483+
}

fs/jfs/jfs_dtree.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,4 +253,8 @@ extern int dtModify(tid_t tid, struct inode *ip, struct component_name * key,
253253
ino_t * orig_ino, ino_t new_ino, int flag);
254254

255255
extern int jfs_readdir(struct file *file, struct dir_context *ctx);
256+
257+
extern bool check_dtroot(dtroot_t *p);
258+
259+
extern bool check_dtpage(dtpage_t *p);
256260
#endif /* !_H_JFS_DTREE */

fs/jfs/jfs_imap.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3102,6 +3102,10 @@ static int copy_from_dinode(struct dinode * dip, struct inode *ip)
31023102

31033103
if (S_ISDIR(ip->i_mode)) {
31043104
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+
}
31053109
} else if (S_ISREG(ip->i_mode) || S_ISLNK(ip->i_mode)) {
31063110
memcpy(&jfs_ip->i_xtroot, &dip->di_xtroot, 288);
31073111
} else

0 commit comments

Comments
 (0)