[<prev] [next>] [day] [month] [year] [list]
Message-ID: <tencent_E830582A0CCE0A004E3DD413458D711B7009@qq.com>
Date: Tue, 22 Apr 2025 00:23:24 +0800
From: "now4yreal" <now4yreal@...mail.com>
To: "Kleikamp" <shaggy@...nel.org>, "Davis" <eadavis@...com>, "Deeb" <rand.sec96@...il.com>, "Li" <peili.dev@...il.com>, "Chaithanya" <niharchaithanya@...il.com>, "Brasga" <rbrasga@....edu>, "Park" <aha310510@...il.com>, "Agrawal" <ghanshyam1898@...il.com>, "jfs-discussion" <jfs-discussion@...ts.sourceforge.net>, "linux-kernel" <linux-kernel@...r.kernel.org>
Subject: [Bug Report] Arbitrary read-write BUG in JFS filesystem
Dear Maintainers,
I would like to report an arbitrary read-write BUG in JFS filesystem.
1. BUG detail and root cause analysis
The vulnerability occurs in the dbSplit function (fs/jfs/jfs_dmap.c:2629) as shown below. At LOC1, the size of the current node is read from the file system content that is controllable by the attacker (constructed as 0x7e in the poc); at LOC2, the node block size is calculated based on the current node size using the formula 1<<((cursz) - (tp->dmt_budmin)). In the poc, the value of tp->dmt_budmin is 5. Therefore, it is clear that cursz - 5 = 0x79, and 0x79 has already exceeded the normal bounds for left shift, so UBSAN triggered the shift-out-of-bounds error.
```c
static void dbSplit(dmtree_t *tp, int leafno, int splitsz, int newval, bool is_ctl)
{
int budsz;
int cursz;
s8 *leaf = tp->dmt_stree + le32_to_cpu(tp->dmt_leafidx);
/* check if the leaf needs to be split.
*/
if (leaf[leafno] > tp->dmt_budmin) {
/* the split occurs by cutting the buddy system in half
* at the specified leaf until we reach the specified
* size. pick up the starting split size (current size
* - 1 in l2) and the corresponding buddy size.
*/
cursz = leaf[leafno] - 1; <------LOC1
budsz = BUDSIZE(cursz, tp->dmt_budmin); <--------LOC2
/* split until we reach the specified size.
*/
while (cursz >= splitsz) {
/* update the buddy's leaf with its new value.
*/
dbAdjTree(tp, leafno ^ budsz, cursz, is_ctl);<--------LOC3
/* on to the next size and buddy.
*/
cursz -= 1;
budsz >>= 1;
}
}
/* adjust the dmap tree to reflect the specified leaf's new
* value.
*/
dbAdjTree(tp, leafno, newval, is_ctl);
}
```
When UBSAN is not configured (UBSAN is only configured for debugging purposes, and kernels used by normal users do not have this option enabled), the code will continue to call the dbAdjTree function at LOC3. In the poc, the second parameter is 0x2000000, which is the result calculated by 1 << 0x79. The dbAdjTree function is shown below.
It should be noted that at LOC4, there is actually a check on lp (the sum of the second parameter and tp->dmt_leafidx, which calculates to 0x2000055). However, this check is performed using WARN_ON_ONCE. In kernels used by normal users, this check will not trigger an error. It returns True on the first execution and False on subsequent executions, so this check can be considered negligible.
Continuing execution, at LOC5, lp is used as an index to access tp->dmt_stree. The size of tp->dmt_stree is far less than 0x2000055, resulting in an out-of-bounds read.
At LOC6, the code writes newval to tp->dmt_stree[lp]. This newval is controllable by the user and is the same value used earlier to calculate budsz, leading to an out-of-bounds write.
```c
static void dbAdjTree(dmtree_t *tp, int leafno, int newval, bool is_ctl)
{
int lp, pp, k;
int max, size;
size = is_ctl ? CTLTREESIZE : TREESIZE;
/* pick up the index of the leaf for this leafno.
*/
lp = leafno + le32_to_cpu(tp->dmt_leafidx);
if (WARN_ON_ONCE(lp >= size || lp < 0)) <------ LOC4
return;
/* is the current value the same as the old value ? if so,
* there is nothing to do.
*/
if (tp->dmt_stree[lp] == newval) <------ LOC5
return;
/* set the new value.
*/
tp->dmt_stree[lp] = newval; <------ LOC6
```
2. Suggested fix
Change the check from WARN_ON_ONCE to a strict validation. If the boundary is exceeded, return an error directly. Or maybe a better way, check the size of cursz in function dbSplit.
Since I am not a JFS developer and only have a general understanding of the file system’s internal logic, there might be inaccuracies in this analysis. I would appreciate it if you could forward this report to the appropriate maintainers for confirmation and further investigation. Please feel free to reach out if you need any clarification or would like additional information.
I’ve attached the POC (written in C) for your convenience — it can be compiled directly with `gcc`.
Thanks for your attention to this matter.
Best regards,
luka
Download attachment "POC.c" of type "application/octet-stream" (114438 bytes)
Powered by blists - more mailing lists