| 0/0 |
2026/06/30 14:35 |
flow |
patching |
47m
Results: map[AckedBy:[] Fixes:map[Hash:1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 Title:Linux-2.6.12-rc2] KernelBranch:master KernelCommit:dc59e4fea9d83f03bad6bddf3fa2e52491777482 KernelRepo:git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git PatchDescription:jfs: add dmaptree integrity check to prevent array-index-out-of-bounds
An array-index-out-of-bounds issue occurs in dbJoin() when the JFS
filesystem attempts to free blocks using a corrupted dmap structure read
from disk.
The JFS filesystem uses a binary buddy system to manage free space. The
state of the buddy system is stored in dmap (for leaf levels) and dmapctl
(for upper levels) structures on disk. When a dmap is read from disk, its
dmaptree structure is not fully validated. Specifically, the leafidx field
(which indicates the index of the first leaf in the stree array) is read
directly from disk and used to compute pointers to the leaf nodes.
If leafidx is corrupted (e.g., set to 0xffffffff), the computed pointer
will point out of bounds of the stree array. When dbFreeDmap() is called to
free blocks, it calls dbFreeBits(), which in turn calls dbJoin() to update
the buddy system. dbJoin() uses the corrupted leafidx to access the leaf
nodes, resulting in an out-of-bounds memory access.
While a similar integrity check (check_dmapctl()) was recently added for
dmapctl structures, the dmap structures (dmaptree) were left unchecked.
Introduce a check_dmaptree() function, similar to the existing
check_dmapctl() function, to validate the integrity of the dmaptree
structure when it is used. The function verifies that fields like nleafs,
l2nleafs, leafidx, height, and budmin are within their expected bounds and
internally consistent. It also ensures that the leaf nodes fit within the
stree array and have valid values.
Call check_dmaptree() at the entry points of functions that operate on the
dmap structure (dbFreeDmap(), dbAllocNext(), dbAllocNear(),
dbAllocDmapLev(), dbAllocDmapBU(), and dbAllocDmap()). If the validation
fails, log an error and return -EIO to prevent further processing of the
corrupted dmap. This also replaces the existing partial checks for leafidx
in dbAllocNext() and dbAllocNear().
UBSAN: array-index-out-of-bounds in fs/jfs/jfs_dmap.c:2867:24
index 4294967295 is out of range for type 's8[1365]' (aka 'signed
char[1365]')
CPU: 0 UID: 0 PID: 123 Comm: jfsCommit Not tainted
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS
1.16.3-debian-1.16.3-2 04/01/2014
Call Trace:
<TASK>
dump_stack_lvl+0xe8/0x150
ubsan_epilogue+0xa/0x30
__ubsan_handle_out_of_bounds+0xe8/0xf0
dbJoin+0xcc4/0xd60
dbFreeBits+0x4a2/0xd70
dbFreeDmap
dbFree+0x324/0x650
txFreeMap+0x9e6/0xde0
xtTruncate+0xd16/0x2eb0
jfs_free_zero_link+0x35b/0x4c0
jfs_evict_inode+0x356/0x430
evict+0x624/0xb50
txLazyCommit
jfs_lazycommit+0x44c/0xac0
kthread+0x388/0x470
ret_from_fork+0x514/0xb70
ret_from_fork_asm+0x1a/0x30
</TASK> PatchDiff:diff --git a/fs/jfs/jfs_dmap.c b/fs/jfs/jfs_dmap.c
index a841cf21d..4f2e0b78d 100644
--- a/fs/jfs/jfs_dmap.c
+++ b/fs/jfs/jfs_dmap.c
@@ -220,6 +220,85 @@ static bool check_dmapctl(struct dmapctl *dcp)
return true;
}
+/*
+ * check_dmaptree - Validate integrity of a dmaptree structure
+ * @dtp: Pointer to the dmaptree structure to check
+ *
+ * Return: true if valid, false if corrupted
+ */
+static bool check_dmaptree(struct dmaptree *dtp)
+{
+ u32 nleafs, l2nleafs, leafidx, height;
+ int i;
+
+ nleafs = le32_to_cpu(dtp->nleafs);
+ /* Check basic field ranges */
+ if (unlikely(nleafs > LPERDMAP)) {
+ jfs_err("dmaptree: invalid nleafs %u (max %u)", nleafs,
+ LPERDMAP);
+ return false;
+ }
+
+ l2nleafs = le32_to_cpu(dtp->l2nleafs);
+ if (unlikely(l2nleafs > L2LPERDMAP)) {
+ jfs_err("dmaptree: invalid l2nleafs %u (max %u)", l2nleafs,
+ L2LPERDMAP);
+ return false;
+ }
+
+ /* Verify nleafs matches l2nleafs (must be power of two) */
+ if (unlikely((1U << l2nleafs) != nleafs)) {
+ jfs_err("dmaptree: nleafs %u != 2^%u", nleafs, l2nleafs);
+ return false;
+ }
+
+ leafidx = le32_to_cpu(dtp->leafidx);
+ /* Check leaf index matches expected position */
+ if (unlikely(leafidx != LEAFIND)) {
+ jfs_err("dmaptree: invalid leafidx %u (expected %u)", leafidx,
+ LEAFIND);
+ return false;
+ }
+
+ height = le32_to_cpu(dtp->height);
+ /* Check tree height is within valid range */
+ if (unlikely(height > 4)) {
+ jfs_err("dmaptree: invalid height %u (max 4)", height);
+ return false;
+ }
+
+ /* Check budmin is valid */
+ if (unlikely(dtp->budmin < BUDMIN)) {
+ jfs_err("dmaptree: invalid budmin %d (min %d)", dtp->budmin,
+ BUDMIN);
+ return false;
+ }
+
+ /* Check leaf nodes fit within stree array */
+ if (unlikely(leafidx + nleafs > TREESIZE)) {
+ jfs_err("dmaptree: leaf range exceeds stree size (end %u > %u)",
+ leafidx + nleafs, TREESIZE);
+ return false;
+ }
+
+ /* Check leaf nodes have valid values */
+ for (i = leafidx; i < leafidx + nleafs; i++) {
+ s8 val = dtp->stree[i];
+
+ if (unlikely(val < NOFREE)) {
+ jfs_err("dmaptree: invalid leaf value %d at index %d",
+ val, i);
+ return false;
+ } else if (unlikely(val > 31)) {
+ jfs_err("dmaptree: leaf value %d too large at index %d",
+ val, i);
+ return false;
+ }
+ }
+
+ return true;
+}
+
/*
* NAME: dbMount()
*
@@ -1163,7 +1242,7 @@ static int dbAllocNext(struct bmap * bmp, struct dmap * dp, s64 blkno,
s8 *leaf;
u32 mask;
- if (dp->tree.leafidx != cpu_to_le32(LEAFIND)) {
+ if (unlikely(!check_dmaptree(&dp->tree))) {
jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmap page\n");
return -EIO;
}
@@ -1293,7 +1372,7 @@ dbAllocNear(struct bmap * bmp,
int word, lword, rc;
s8 *leaf;
- if (dp->tree.leafidx != cpu_to_le32(LEAFIND)) {
+ if (unlikely(!check_dmaptree(&dp->tree))) {
jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmap page\n");
return -EIO;
}
@@ -2046,6 +2125,11 @@ dbAllocDmapLev(struct bmap * bmp,
s64 blkno;
int leafidx, rc;
+ if (unlikely(!check_dmaptree(&dp->tree))) {
+ jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmap page\n");
+ return -EIO;
+ }
+
/* can't be more than a dmaps worth of blocks */
assert(l2nb <= L2BPERDMAP);
@@ -2112,6 +2196,11 @@ static int dbAllocDmap(struct bmap * bmp, struct dmap * dp, s64 blkno,
s8 oldroot;
int rc;
+ if (unlikely(!check_dmaptree(&dp->tree))) {
+ jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmap page\n");
+ return -EIO;
+ }
+
/* save the current value of the root (i.e. maximum free string)
* of the dmap tree.
*/
@@ -2167,6 +2256,11 @@ static int dbFreeDmap(struct bmap * bmp, struct dmap * dp, s64 blkno,
s8 oldroot;
int rc = 0, word;
+ if (unlikely(!check_dmaptree(&dp->tree))) {
+ jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmap page\n");
+ return -EIO;
+ }
+
/* save the current value of the root (i.e. maximum free string)
* of the dmap tree.
*/
@@ -3338,6 +3432,11 @@ static int dbAllocDmapBU(struct bmap * bmp, struct dmap * dp, s64 blkno,
s8 oldroot;
struct dmaptree *tp = (struct dmaptree *) & dp->tree;
+ if (unlikely(!check_dmaptree(&dp->tree))) {
+ jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmap page\n");
+ return -EIO;
+ }
+
/* save the current value of the root (i.e. maximum free string)
* of the dmap tree.
*/
Recipients:[map[Email:contact@arnaud-lcm.com Name:Arnaud Lecomte To:false] map[Email:jfs-discussion@lists.sourceforge.net Name: To:true] map[Email:kees@kernel.org Name:Kees Cook To:false] map[Email:linux-kernel@vger.kernel.org Name: To:false] map[Email:shaggy@kernel.org Name:Dave Kleikamp To:true] map[Email:yun.zhou@windriver.com Name:Yun Zhou To:false] map[Email:zheng.yu@northwestern.edu Name:Zheng Yu To:false]] ReportedBy:[] ReviewedBy:[] TestedBy:[]]
|
| 1/1 |
2026/06/30 14:35 |
action |
base-commit-picker |
0m
Results: map[KernelBranch:master KernelCommit:dc59e4fea9d83f03bad6bddf3fa2e52491777482 KernelRepo:git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git]
|
| 2/1 |
2026/06/30 14:35 |
action |
syz-repro-to-c-repro |
0m
Results: map[SimplifiedCRepro:// autogenerated by syzkaller (https://github.com/google/syzkaller)
#define _GNU_SOURCE
#include <endian.h>
#include <errno.h>
#include <fcntl.h>
#include <setjmp.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>
#include <linux/loop.h>
#ifndef __NR_memfd_create
#define __NR_memfd_create 319
#endif
static unsigned long long procid;
//% This code is derived from puff.{c,h}, found in the zlib development. The
//% original files come with the following copyright notice:
//% Copyright (C) 2002-2013 Mark Adler, all rights reserved
//% version 2.3, 21 Jan 2013
//% This software is provided 'as-is', without any express or implied
//% warranty. In no event will the author be held liable for any damages
//% arising from the use of this software.
//% Permission is granted to anyone to use this software for any purpose,
//% including commercial applications, and to alter it and redistribute it
//% freely, subject to the following restrictions:
//% 1. The origin of this software must not be misrepresented; you must not
//% claim that you wrote the original software. If you use this software
//% in a product, an acknowledgment in the product documentation would be
//% appreciated but is not required.
//% 2. Altered source versions must be plainly marked as such, and must not be
//% misrepresented as being the original software.
//% 3. This notice may not be removed or altered from any source distribution.
//% Mark Adler madler@alumni.caltech.edu
//% BEGIN CODE DERIVED FROM puff.{c,h}
#define MAXBITS 15
#define MAXLCODES 286
#define MAXDCODES 30
#define MAXCODES (MAXLCODES + MAXDCODES)
#define FIXLCODES 288
struct puff_state {
unsigned char* out;
unsigned long outlen;
unsigned long outcnt;
const unsigned char* in;
unsigned long inlen;
unsigned long incnt;
int bitbuf;
int bitcnt;
jmp_buf env;
};
static int puff_bits(struct puff_state* s, int need)
{
long val = s->bitbuf;
while (s->bitcnt < need) {
if (s->incnt == s->inlen)
longjmp(s->env, 1);
val |= (long)(s->in[s->incnt++]) << s->bitcnt;
s->bitcnt += 8;
}
s->bitbuf = (int)(val >> need);
s->bitcnt -= need;
return (int)(val & ((1L << need) - 1));
}
static int puff_stored(struct puff_state* s)
{
s->bitbuf = 0;
s->bitcnt = 0;
if (s->incnt + 4 > s->inlen)
return 2;
unsigned len = s->in[s->incnt++];
len |= s->in[s->incnt++] << 8;
if (s->in[s->incnt++] != (~len & 0xff) ||
s->in[s->incnt++] != ((~len >> 8) & 0xff))
return -2;
if (s->incnt + len > s->inlen)
return 2;
if (s->outcnt + len > s->outlen)
return 1;
for (; len--; s->outcnt++, s->incnt++) {
if (s->in[s->incnt])
s->out[s->outcnt] = s->in[s->incnt];
}
return 0;
}
struct puff_huffman {
short* count;
short* symbol;
};
static int puff_decode(struct puff_state* s, const struct puff_huffman* h)
{
int first = 0;
int index = 0;
int bitbuf = s->bitbuf;
int left = s->bitcnt;
int code = first = index = 0;
int len = 1;
short* next = h->count + 1;
while (1) {
while (left--) {
code |= bitbuf & 1;
bitbuf >>= 1;
int count = *next++;
if (code - count < first) {
s->bitbuf = bitbuf;
s->bitcnt = (s->bitcnt - len) & 7;
return h->symbol[index + (code - first)];
}
index += count;
first += count;
first <<= 1;
code <<= 1;
len++;
}
left = (MAXBITS + 1) - len;
if (left == 0)
break;
if (s->incnt == s->inlen)
longjmp(s->env, 1);
bitbuf = s->in[s->incnt++];
if (left > 8)
left = 8;
}
return -10;
}
static int puff_construct(struct puff_huffman* h, const short* length, int n)
{
int len;
for (len = 0; len <= MAXBITS; len++)
h->count[len] = 0;
int symbol;
for (symbol = 0; symbol < n; symbol++)
(h->count[length[symbol]])++;
if (h->count[0] == n)
return 0;
int left = 1;
for (len = 1; len <= MAXBITS; len++) {
left <<= 1;
left -= h->count[len];
if (left < 0)
return left;
}
short offs[MAXBITS + 1];
offs[1] = 0;
for (len = 1; len < MAXBITS; len++)
offs[len + 1] = offs[len] + h->count[len];
for (symbol = 0; symbol < n; symbol++)
if (length[symbol] != 0)
h->symbol[offs[length[symbol]]++] = symbol;
return left;
}
static int puff_codes(struct puff_state* s,
const struct puff_huffman* lencode,
const struct puff_huffman* distcode)
{
static const short lens[29] = {
3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258};
static const short lext[29] = {
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0};
static const short dists[30] = {
1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
8193, 12289, 16385, 24577};
static const short dext[30] = {
0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
7, 7, 8, 8, 9, 9, 10, 10, 11, 11,
12, 12, 13, 13};
int symbol;
do {
symbol = puff_decode(s, lencode);
if (symbol < 0)
return symbol;
if (symbol < 256) {
if (s->outcnt == s->outlen)
return 1;
if (symbol)
s->out[s->outcnt] = symbol;
s->outcnt++;
} else if (symbol > 256) {
symbol -= 257;
if (symbol >= 29)
return -10;
int len = lens[symbol] + puff_bits(s, lext[symbol]);
symbol = puff_decode(s, distcode);
if (symbol < 0)
return symbol;
unsigned dist = dists[symbol] + puff_bits(s, dext[symbol]);
if (dist > s->outcnt)
return -11;
if (s->outcnt + len > s->outlen)
return 1;
while (len--) {
if (dist <= s->outcnt && s->out[s->outcnt - dist])
s->out[s->outcnt] = s->out[s->outcnt - dist];
s->outcnt++;
}
}
} while (symbol != 256);
return 0;
}
static int puff_fixed(struct puff_state* s)
{
static int virgin = 1;
static short lencnt[MAXBITS + 1], lensym[FIXLCODES];
static short distcnt[MAXBITS + 1], distsym[MAXDCODES];
static struct puff_huffman lencode, distcode;
if (virgin) {
lencode.count = lencnt;
lencode.symbol = lensym;
distcode.count = distcnt;
distcode.symbol = distsym;
short lengths[FIXLCODES];
int symbol;
for (symbol = 0; symbol < 144; symbol++)
lengths[symbol] = 8;
for (; symbol < 256; symbol++)
lengths[symbol] = 9;
for (; symbol < 280; symbol++)
lengths[symbol] = 7;
for (; symbol < FIXLCODES; symbol++)
lengths[symbol] = 8;
puff_construct(&lencode, lengths, FIXLCODES);
for (symbol = 0; symbol < MAXDCODES; symbol++)
lengths[symbol] = 5;
puff_construct(&distcode, lengths, MAXDCODES);
virgin = 0;
}
return puff_codes(s, &lencode, &distcode);
}
static int puff_dynamic(struct puff_state* s)
{
static const short order[19] =
{16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};
int nlen = puff_bits(s, 5) + 257;
int ndist = puff_bits(s, 5) + 1;
int ncode = puff_bits(s, 4) + 4;
if (nlen > MAXLCODES || ndist > MAXDCODES)
return -3;
short lengths[MAXCODES];
int index;
for (index = 0; index < ncode; index++)
lengths[order[index]] = puff_bits(s, 3);
for (; index < 19; index++)
lengths[order[index]] = 0;
short lencnt[MAXBITS + 1], lensym[MAXLCODES];
struct puff_huffman lencode = {lencnt, lensym};
int err = puff_construct(&lencode, lengths, 19);
if (err != 0)
return -4;
index = 0;
while (index < nlen + ndist) {
int symbol;
int len;
symbol = puff_decode(s, &lencode);
if (symbol < 0)
return symbol;
if (symbol < 16)
lengths[index++] = symbol;
else {
len = 0;
if (symbol == 16) {
if (index == 0)
return -5;
len = lengths[index - 1];
symbol = 3 + puff_bits(s, 2);
} else if (symbol == 17)
symbol = 3 + puff_bits(s, 3);
else
symbol = 11 + puff_bits(s, 7);
if (index + symbol > nlen + ndist)
return -6;
while (symbol--)
lengths[index++] = len;
}
}
if (lengths[256] == 0)
return -9;
err = puff_construct(&lencode, lengths, nlen);
if (err && (err < 0 || nlen != lencode.count[0] + lencode.count[1]))
return -7;
short distcnt[MAXBITS + 1], distsym[MAXDCODES];
struct puff_huffman distcode = {distcnt, distsym};
err = puff_construct(&distcode, lengths + nlen, ndist);
if (err && (err < 0 || ndist != distcode.count[0] + distcode.count[1]))
return -8;
return puff_codes(s, &lencode, &distcode);
}
static int puff(
unsigned char* dest,
unsigned long* destlen,
const unsigned char* source,
unsigned long sourcelen)
{
struct puff_state s = {
.out = dest,
.outlen = *destlen,
.outcnt = 0,
.in = source,
.inlen = sourcelen,
.incnt = 0,
.bitbuf = 0,
.bitcnt = 0,
};
int err;
if (setjmp(s.env) != 0)
err = 2;
else {
int last;
do {
last = puff_bits(&s, 1);
int type = puff_bits(&s, 2);
err = type == 0 ? puff_stored(&s) : (type == 1 ? puff_fixed(&s) : (type == 2 ? puff_dynamic(&s) : -1));
if (err != 0)
break;
} while (!last);
}
*destlen = s.outcnt;
return err;
}
//% END CODE DERIVED FROM puff.{c,h}
#define ZLIB_HEADER_WIDTH 2
static int puff_zlib_to_file(const unsigned char* source, unsigned long sourcelen, int dest_fd)
{
if (sourcelen < ZLIB_HEADER_WIDTH)
return 0;
source += ZLIB_HEADER_WIDTH;
sourcelen -= ZLIB_HEADER_WIDTH;
const unsigned long max_destlen = 132 << 20;
void* ret = mmap(0, max_destlen, PROT_WRITE | PROT_READ, MAP_PRIVATE | MAP_ANON, -1, 0);
if (ret == MAP_FAILED)
return -1;
unsigned char* dest = (unsigned char*)ret;
unsigned long destlen = max_destlen;
int err = puff(dest, &destlen, source, sourcelen);
if (err) {
munmap(dest, max_destlen);
errno = -err;
return -1;
}
if (write(dest_fd, dest, destlen) != (ssize_t)destlen) {
munmap(dest, max_destlen);
return -1;
}
return munmap(dest, max_destlen);
}
static int setup_loop_device(unsigned char* data, unsigned long size, const char* loopname, int* loopfd_p)
{
int err = 0, loopfd = -1;
int memfd = syscall(__NR_memfd_create, "syzkaller", 0);
if (memfd == -1) {
err = errno;
goto error;
}
if (puff_zlib_to_file(data, size, memfd)) {
err = errno;
goto error_close_memfd;
}
loopfd = open(loopname, O_RDWR);
if (loopfd == -1) {
err = errno;
goto error_close_memfd;
}
if (ioctl(loopfd, LOOP_SET_FD, memfd)) {
if (errno != EBUSY) {
err = errno;
goto error_close_loop;
}
ioctl(loopfd, LOOP_CLR_FD, 0);
usleep(1000);
if (ioctl(loopfd, LOOP_SET_FD, memfd)) {
err = errno;
goto error_close_loop;
}
}
close(memfd);
*loopfd_p = loopfd;
return 0;
error_close_loop:
close(loopfd);
error_close_memfd:
close(memfd);
error:
errno = err;
return -1;
}
static void reset_loop_device(const char* loopname)
{
int loopfd = open(loopname, O_RDWR);
if (loopfd == -1) {
return;
}
if (ioctl(loopfd, LOOP_CLR_FD, 0)) {
}
close(loopfd);
}
static long syz_mount_image(
volatile long fsarg,
volatile long dir,
volatile long flags,
volatile long optsarg,
volatile long change_dir,
volatile unsigned long size,
volatile long image)
{
unsigned char* data = (unsigned char*)image;
int res = -1, err = 0, need_loop_device = !!size;
char* mount_opts = (char*)optsarg;
char* target = (char*)dir;
char* fs = (char*)fsarg;
char* source = NULL;
char loopname[64];
if (need_loop_device) {
int loopfd;
memset(loopname, 0, sizeof(loopname));
snprintf(loopname, sizeof(loopname), "/dev/loop%llu", procid);
if (setup_loop_device(data, size, loopname, &loopfd) == -1)
return -1;
close(loopfd);
source = loopname;
}
mkdir(target, 0777);
char opts[256];
memset(opts, 0, sizeof(opts));
if (strlen(mount_opts) > (sizeof(opts) - 32)) {
}
strncpy(opts, mount_opts, sizeof(opts) - 32);
if (strcmp(fs, "iso9660") == 0) {
flags |= MS_RDONLY;
} else if (strncmp(fs, "ext", 3) == 0) {
bool has_remount_ro = false;
char* remount_ro_start = strstr(opts, "errors=remount-ro");
if (remount_ro_start != NULL) {
char after = *(remount_ro_start + strlen("errors=remount-ro"));
char before = remount_ro_start == opts ? '\0' : *(remount_ro_start - 1);
has_remount_ro = ((before == '\0' || before == ',') && (after == '\0' || after == ','));
}
if (strstr(opts, "errors=panic") || !has_remount_ro)
strcat(opts, ",errors=continue");
} else if (strcmp(fs, "xfs") == 0) {
strcat(opts, ",nouuid");
} else if (strncmp(fs, "gfs2", 4) == 0 && (strstr(opts, "errors=panic") || strstr(opts, "debug"))) {
strcat(opts, ",errors=withdraw");
}
res = mount(source, target, fs, flags, opts);
if (res == -1) {
err = errno;
goto error_clear_loop;
}
res = open(target, O_RDONLY | O_DIRECTORY);
if (res == -1) {
err = errno;
goto error_clear_loop;
}
if (change_dir) {
res = chdir(target);
if (res == -1) {
err = errno;
}
}
error_clear_loop:
if (need_loop_device)
reset_loop_device(loopname);
errno = err;
return res;
}
uint64_t r[1] = {0xffffffffffffffff};
int main(void)
{
syscall(__NR_mmap, /*addr=*/0x1ffffffff000ul, /*len=*/0x1000, /*prot=*/0ul, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32ul, /*fd=*/(intptr_t)-1, /*offset=*/0ul);
syscall(__NR_mmap, /*addr=*/0x200000000000ul, /*len=*/0x1000000, /*prot=PROT_WRITE|PROT_READ|PROT_EXEC*/7ul, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32ul, /*fd=*/(intptr_t)-1, /*offset=*/0ul);
syscall(__NR_mmap, /*addr=*/0x200001000000ul, /*len=*/0x1000, /*prot=*/0ul, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32ul, /*fd=*/(intptr_t)-1, /*offset=*/0ul);
const char* reason;
(void)reason;
intptr_t res = 0;
if (write(1, "executing program\n", sizeof("executing program\n") - 1)) {}
// syz_mount_image$jfs arguments: [
// fs: ptr[in, buffer] {
// buffer: {6a 66 73 00} (length 0x4)
// }
// dir: ptr[in, buffer] {
// buffer: {2e 2f 62 75 73 00} (length 0x6)
// }
// flags: mount_flags = 0x402 (8 bytes)
// opts: ptr[inout, array[ANYUNION]] {
// array[ANYUNION] {
// }
// }
// chdir: int8 = 0xfe (1 bytes)
// size: len = 0x61a8 (8 bytes)
// img: ptr[in, buffer] {
// buffer: (compressed buffer with length 0x61a8)
// }
// ]
// returns fd_dir
memcpy((void*)0x200000000000, "jfs\000", 4);
memcpy((void*)0x200000000040, "./bus\000", 6);
memcpy((void*)0x200000009940, "... [truncated large byte array] ...", 25000);
syz_mount_image(/*fs=*/0x200000000000, /*dir=*/0x200000000040, /*flags=MS_NOSUID|MS_NOATIME*/0x402, /*opts=*/0x200000000580, /*chdir=*/0xfe, /*size=*/0x61a8, /*img=*/0x200000009940);
// rename arguments: [
// old: ptr[in, buffer] {
// buffer: {2e 2f 66 69 6c 65 32 00} (length 0x8)
// }
// new: ptr[in, buffer] {
// buffer: {2e 2f 62 75 73 00} (length 0x6)
// }
// ]
memcpy((void*)0x2000000002c0, "./file2\000", 8);
memcpy((void*)0x200000000340, "./bus\000", 6);
syscall(__NR_rename, /*old=*/0x2000000002c0ul, /*new=*/0x200000000340ul);
// mount arguments: [
// src: ptr[in, blockdev_filename] {
// union blockdev_filename {
// loop: loop_filename {
// prefix: buffer: {2f 64 65 76 2f 6c 6f 6f 70} (length 0x9)
// id: proc = 0x0 (1 bytes)
// z: const = 0x0 (1 bytes)
// }
// }
// }
// dst: ptr[in, buffer] {
// buffer: {2e 2f 62 75 73 00} (length 0x6)
// }
// type: nil
// flags: mount_flags = 0x1000 (8 bytes)
// data: nil
// ]
memcpy((void*)0x200000000380, "/dev/loop", 9);
*(uint8_t*)0x200000000389 = 0x30;
*(uint8_t*)0x20000000038a = 0;
memcpy((void*)0x200000000080, "./bus\000", 6);
syscall(__NR_mount, /*src=*/0x200000000380ul, /*dst=*/0x200000000080ul, /*type=*/0ul, /*flags=MS_BIND*/0x1000ul, /*data=*/0ul);
// open arguments: [
// file: ptr[in, buffer] {
// buffer: {2e 2f 62 75 73 00} (length 0x6)
// }
// flags: open_flags = 0x0 (8 bytes)
// mode: open_mode = 0x0 (8 bytes)
// ]
// returns fd
memcpy((void*)0x200000000000, "./bus\000", 6);
res = syscall(__NR_open, /*file=*/0x200000000000ul, /*flags=*/0ul, /*mode=*/0ul);
if (res != -1)
r[0] = res;
// ioctl$LOOP_SET_STATUS64 arguments: [
// fd: fd_loop (resource)
// cmd: const = 0x4c04 (4 bytes)
// arg: ptr[in, loop_info64] {
// loop_info64 {
// lo_device: const = 0x0 (8 bytes)
// lo_inode: const = 0x0 (8 bytes)
// lo_rdevice: const = 0x0 (8 bytes)
// lo_offset: int64 = 0x7f (8 bytes)
// lo_sizelimit: int64 = 0x0 (8 bytes)
// lo_number: const = 0x0 (4 bytes)
// lo_enc_type: lo_encrypt_type = 0x0 (4 bytes)
// lo_enc_key_size: int32 = 0x0 (4 bytes)
// lo_flags: lo_flags = 0x0 (4 bytes)
// lo_file_name: buffer: {ef 35 9f 41 3b b9 01 52 f7 d6 d1 ce 5d 29 c3 ee 5e 5c a9 00 0f 7c 41 49 9d c2 aa c6 3a 01 00 00 00 00 00 00 00 4f aa 2a d9 c0 84 a0 03 ea 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00} (length 0x40)
// lo_crypt_name: buffer: {03 6c 47 c6 78 08 20 04 00 00 00 00 00 00 00 33 52 63 bd bc ef 54 9b a1 97 fc e4 7d df dd 75 3a bd 95 01 00 00 2a 00 ff ff ff ff ff ff ff ff 00 00 00 00 e8 f2 00 00 00 02 00 00 00 00 00 00 00} (length 0x40)
// lo_enc_key: buffer: {b7 32 67 36 18 1c 20 82 20 00 00 00 b9 00 00 00 00 00 00 00 00 00 f0 ff ff ff ff f2 ff 00 00 00} (length 0x20)
// lo_init: array[int64] {
// int64 = 0x0 (8 bytes)
// int64 = 0x0 (8 bytes)
// }
// }
// }
// ]
*(uint64_t*)0x200000000240 = 0;
*(uint64_t*)0x200000000248 = 0;
*(uint64_t*)0x200000000250 = 0;
*(uint64_t*)0x200000000258 = 0x7f;
*(uint64_t*)0x200000000260 = 0;
*(uint32_t*)0x200000000268 = 0;
*(uint32_t*)0x20000000026c = 0;
*(uint32_t*)0x200000000270 = 0;
*(uint32_t*)0x200000000274 = 0;
memcpy((void*)0x200000000278, "... [truncated large byte array] ...", 64);
memcpy((void*)0x2000000002b8, "... [truncated large byte array] ...", 64);
memcpy((void*)0x2000000002f8, "... [truncated large byte array] ...", 32);
*(uint64_t*)0x200000000318 = 0;
*(uint64_t*)0x200000000320 = 0;
syscall(__NR_ioctl, /*fd=*/r[0], /*cmd=*/0x4c04, /*arg=*/0x200000000240ul);
return 0;
}
]
|
| 3/1 |
2026/06/30 14:35 |
action |
kernel-checkouter |
0m
Results: map[KernelSrc:/app/workdir/cache/src/7be5f73ee7b4d1978813accf8bb54cd892ccd800]
|
| 4/1 |
2026/06/30 14:35 |
action |
kernel-builder |
0m
Results: map[KernelObj:/app/workdir/cache/build/5bc2ba8f92aa5e21412e076f42b19309bd220aa4]
|
| 5/1 |
2026/06/30 14:35 |
action |
crash-reproducer |
0m
Results: map[OtherCrashReports:<nil> ReproducedBugTitle:UBSAN: array-index-out-of-bounds in dbJoin ReproducedCrashReport:------------[ cut here ]------------
UBSAN: array-index-out-of-bounds in /app/workdir/cache/src/7be5f73ee7b4d1978813accf8bb54cd892ccd800/fs/jfs/jfs_dmap.c:2867:24
index 4294967295 is out of range for type 's8[1365]' (aka 'signed char[1365]')
CPU: 0 UID: 0 PID: 123 Comm: jfsCommit Not tainted syzkaller #1 PREEMPT(full)
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
Call Trace:
<TASK>
dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120
ubsan_epilogue+0xa/0x30 lib/ubsan.c:233
__ubsan_handle_out_of_bounds+0xe8/0xf0 lib/ubsan.c:455
dbJoin+0xcc4/0xd60 fs/jfs/jfs_dmap.c:2867
dbFreeBits+0x4a2/0xd70 fs/jfs/jfs_dmap.c:2427
dbFreeDmap fs/jfs/jfs_dmap.c:2176 [inline]
dbFree+0x324/0x650 fs/jfs/jfs_dmap.c:485
txFreeMap+0x9e6/0xde0 fs/jfs/jfs_txnmgr.c:2517
xtTruncate+0xd16/0x2eb0 fs/jfs/jfs_xtree.c:2481
jfs_free_zero_link+0x35b/0x4c0 fs/jfs/namei.c:760
jfs_evict_inode+0x356/0x430 fs/jfs/inode.c:159
evict+0x624/0xb50 fs/inode.c:825
txLazyCommit fs/jfs/jfs_txnmgr.c:2666 [inline]
jfs_lazycommit+0x44c/0xac0 fs/jfs/jfs_txnmgr.c:2735
kthread+0x388/0x470 kernel/kthread.c:436
ret_from_fork+0x514/0xb70 arch/x86/kernel/process.c:158
ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245
</TASK>
---[ end trace ]---
ReproducedFaultInjection:]
|
| 6/1 |
2026/06/30 14:35 |
action |
codesearch-prepare |
0m
Results: map[Index:codesearch-index]
|
| 7/1 |
2026/06/30 14:36 |
agent |
debugger |
10m
Model:
Instruction:
You are an experienced Linux kernel developer tasked with debugging a kernel crash root cause.
You need to provide a detailed explanation of the root cause for another developer to be
able to write a fix for the bug based on your explanation. Include all relevant details
into the response: function/struct/field/etc names, code snippets, line numbers,
macro/enum values, etc.
Don't make assumptions about the kernel source code (it may be different from what you assume it is).
Extensively use the provided code access tools (codesearch-*, git-*, grepper, etc)
to examine the actual source code, and confirm any assumptions.
Prefer calling several tools at the same time to save round-trips.
Prompt:
The crash is:
------------[ cut here ]------------
UBSAN: array-index-out-of-bounds in /app/workdir/cache/src/7be5f73ee7b4d1978813accf8bb54cd892ccd800/fs/jfs/jfs_dmap.c:2867:24
index 4294967295 is out of range for type 's8[1365]' (aka 'signed char[1365]')
CPU: 0 UID: 0 PID: 123 Comm: jfsCommit Not tainted syzkaller #1 PREEMPT(full)
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
Call Trace:
<TASK>
dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120
ubsan_epilogue+0xa/0x30 lib/ubsan.c:233
__ubsan_handle_out_of_bounds+0xe8/0xf0 lib/ubsan.c:455
dbJoin+0xcc4/0xd60 fs/jfs/jfs_dmap.c:2867
dbFreeBits+0x4a2/0xd70 fs/jfs/jfs_dmap.c:2427
dbFreeDmap fs/jfs/jfs_dmap.c:2176 [inline]
dbFree+0x324/0x650 fs/jfs/jfs_dmap.c:485
txFreeMap+0x9e6/0xde0 fs/jfs/jfs_txnmgr.c:2517
xtTruncate+0xd16/0x2eb0 fs/jfs/jfs_xtree.c:2481
jfs_free_zero_link+0x35b/0x4c0 fs/jfs/namei.c:760
jfs_evict_inode+0x356/0x430 fs/jfs/inode.c:159
evict+0x624/0xb50 fs/inode.c:825
txLazyCommit fs/jfs/jfs_txnmgr.c:2666 [inline]
jfs_lazycommit+0x44c/0xac0 fs/jfs/jfs_txnmgr.c:2735
kthread+0x388/0x470 kernel/kthread.c:436
ret_from_fork+0x514/0xb70 arch/x86/kernel/process.c:158
ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245
</TASK>
---[ end trace ]---
The following C code is a draft of the vulnerable syscall sequence. Keep in mind that
it may lack the precise threading, sandboxing, and some arguments of a working reproducer:
// autogenerated by syzkaller (https://github.com/google/syzkaller)
#define _GNU_SOURCE
#include <endian.h>
#include <errno.h>
#include <fcntl.h>
#include <setjmp.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>
#include <linux/loop.h>
#ifndef __NR_memfd_create
#define __NR_memfd_create 319
#endif
static unsigned long long procid;
//% This code is derived from puff.{c,h}, found in the zlib development. The
//% original files come with the following copyright notice:
//% Copyright (C) 2002-2013 Mark Adler, all rights reserved
//% version 2.3, 21 Jan 2013
//% This software is provided 'as-is', without any express or implied
//% warranty. In no event will the author be held liable for any damages
//% arising from the use of this software.
//% Permission is granted to anyone to use this software for any purpose,
//% including commercial applications, and to alter it and redistribute it
//% freely, subject to the following restrictions:
//% 1. The origin of this software must not be misrepresented; you must not
//% claim that you wrote the original software. If you use this software
//% in a product, an acknowledgment in the product documentation would be
//% appreciated but is not required.
//% 2. Altered source versions must be plainly marked as such, and must not be
//% misrepresented as being the original software.
//% 3. This notice may not be removed or altered from any source distribution.
//% Mark Adler madler@alumni.caltech.edu
//% BEGIN CODE DERIVED FROM puff.{c,h}
#define MAXBITS 15
#define MAXLCODES 286
#define MAXDCODES 30
#define MAXCODES (MAXLCODES + MAXDCODES)
#define FIXLCODES 288
struct puff_state {
unsigned char* out;
unsigned long outlen;
unsigned long outcnt;
const unsigned char* in;
unsigned long inlen;
unsigned long incnt;
int bitbuf;
int bitcnt;
jmp_buf env;
};
static int puff_bits(struct puff_state* s, int need)
{
long val = s->bitbuf;
while (s->bitcnt < need) {
if (s->incnt == s->inlen)
longjmp(s->env, 1);
val |= (long)(s->in[s->incnt++]) << s->bitcnt;
s->bitcnt += 8;
}
s->bitbuf = (int)(val >> need);
s->bitcnt -= need;
return (int)(val & ((1L << need) - 1));
}
static int puff_stored(struct puff_state* s)
{
s->bitbuf = 0;
s->bitcnt = 0;
if (s->incnt + 4 > s->inlen)
return 2;
unsigned len = s->in[s->incnt++];
len |= s->in[s->incnt++] << 8;
if (s->in[s->incnt++] != (~len & 0xff) ||
s->in[s->incnt++] != ((~len >> 8) & 0xff))
return -2;
if (s->incnt + len > s->inlen)
return 2;
if (s->outcnt + len > s->outlen)
return 1;
for (; len--; s->outcnt++, s->incnt++) {
if (s->in[s->incnt])
s->out[s->outcnt] = s->in[s->incnt];
}
return 0;
}
struct puff_huffman {
short* count;
short* symbol;
};
static int puff_decode(struct puff_state* s, const struct puff_huffman* h)
{
int first = 0;
int index = 0;
int bitbuf = s->bitbuf;
int left = s->bitcnt;
int code = first = index = 0;
int len = 1;
short* next = h->count + 1;
while (1) {
while (left--) {
code |= bitbuf & 1;
bitbuf >>= 1;
int count = *next++;
if (code - count < first) {
s->bitbuf = bitbuf;
s->bitcnt = (s->bitcnt - len) & 7;
return h->symbol[index + (code - first)];
}
index += count;
first += count;
first <<= 1;
code <<= 1;
len++;
}
left = (MAXBITS + 1) - len;
if (left == 0)
break;
if (s->incnt == s->inlen)
longjmp(s->env, 1);
bitbuf = s->in[s->incnt++];
if (left > 8)
left = 8;
}
return -10;
}
static int puff_construct(struct puff_huffman* h, const short* length, int n)
{
int len;
for (len = 0; len <= MAXBITS; len++)
h->count[len] = 0;
int symbol;
for (symbol = 0; symbol < n; symbol++)
(h->count[length[symbol]])++;
if (h->count[0] == n)
return 0;
int left = 1;
for (len = 1; len <= MAXBITS; len++) {
left <<= 1;
left -= h->count[len];
if (left < 0)
return left;
}
short offs[MAXBITS + 1];
offs[1] = 0;
for (len = 1; len < MAXBITS; len++)
offs[len + 1] = offs[len] + h->count[len];
for (symbol = 0; symbol < n; symbol++)
if (length[symbol] != 0)
h->symbol[offs[length[symbol]]++] = symbol;
return left;
}
static int puff_codes(struct puff_state* s,
const struct puff_huffman* lencode,
const struct puff_huffman* distcode)
{
static const short lens[29] = {
3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258};
static const short lext[29] = {
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0};
static const short dists[30] = {
1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
8193, 12289, 16385, 24577};
static const short dext[30] = {
0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
7, 7, 8, 8, 9, 9, 10, 10, 11, 11,
12, 12, 13, 13};
int symbol;
do {
symbol = puff_decode(s, lencode);
if (symbol < 0)
return symbol;
if (symbol < 256) {
if (s->outcnt == s->outlen)
return 1;
if (symbol)
s->out[s->outcnt] = symbol;
s->outcnt++;
} else if (symbol > 256) {
symbol -= 257;
if (symbol >= 29)
return -10;
int len = lens[symbol] + puff_bits(s, lext[symbol]);
symbol = puff_decode(s, distcode);
if (symbol < 0)
return symbol;
unsigned dist = dists[symbol] + puff_bits(s, dext[symbol]);
if (dist > s->outcnt)
return -11;
if (s->outcnt + len > s->outlen)
return 1;
while (len--) {
if (dist <= s->outcnt && s->out[s->outcnt - dist])
s->out[s->outcnt] = s->out[s->outcnt - dist];
s->outcnt++;
}
}
} while (symbol != 256);
return 0;
}
static int puff_fixed(struct puff_state* s)
{
static int virgin = 1;
static short lencnt[MAXBITS + 1], lensym[FIXLCODES];
static short distcnt[MAXBITS + 1], distsym[MAXDCODES];
static struct puff_huffman lencode, distcode;
if (virgin) {
lencode.count = lencnt;
lencode.symbol = lensym;
distcode.count = distcnt;
distcode.symbol = distsym;
short lengths[FIXLCODES];
int symbol;
for (symbol = 0; symbol < 144; symbol++)
lengths[symbol] = 8;
for (; symbol < 256; symbol++)
lengths[symbol] = 9;
for (; symbol < 280; symbol++)
lengths[symbol] = 7;
for (; symbol < FIXLCODES; symbol++)
lengths[symbol] = 8;
puff_construct(&lencode, lengths, FIXLCODES);
for (symbol = 0; symbol < MAXDCODES; symbol++)
lengths[symbol] = 5;
puff_construct(&distcode, lengths, MAXDCODES);
virgin = 0;
}
return puff_codes(s, &lencode, &distcode);
}
static int puff_dynamic(struct puff_state* s)
{
static const short order[19] =
{16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};
int nlen = puff_bits(s, 5) + 257;
int ndist = puff_bits(s, 5) + 1;
int ncode = puff_bits(s, 4) + 4;
if (nlen > MAXLCODES || ndist > MAXDCODES)
return -3;
short lengths[MAXCODES];
int index;
for (index = 0; index < ncode; index++)
lengths[order[index]] = puff_bits(s, 3);
for (; index < 19; index++)
lengths[order[index]] = 0;
short lencnt[MAXBITS + 1], lensym[MAXLCODES];
struct puff_huffman lencode = {lencnt, lensym};
int err = puff_construct(&lencode, lengths, 19);
if (err != 0)
return -4;
index = 0;
while (index < nlen + ndist) {
int symbol;
int len;
symbol = puff_decode(s, &lencode);
if (symbol < 0)
return symbol;
if (symbol < 16)
lengths[index++] = symbol;
else {
len = 0;
if (symbol == 16) {
if (index == 0)
return -5;
len = lengths[index - 1];
symbol = 3 + puff_bits(s, 2);
} else if (symbol == 17)
symbol = 3 + puff_bits(s, 3);
else
symbol = 11 + puff_bits(s, 7);
if (index + symbol > nlen + ndist)
return -6;
while (symbol--)
lengths[index++] = len;
}
}
if (lengths[256] == 0)
return -9;
err = puff_construct(&lencode, lengths, nlen);
if (err && (err < 0 || nlen != lencode.count[0] + lencode.count[1]))
return -7;
short distcnt[MAXBITS + 1], distsym[MAXDCODES];
struct puff_huffman distcode = {distcnt, distsym};
err = puff_construct(&distcode, lengths + nlen, ndist);
if (err && (err < 0 || ndist != distcode.count[0] + distcode.count[1]))
return -8;
return puff_codes(s, &lencode, &distcode);
}
static int puff(
unsigned char* dest,
unsigned long* destlen,
const unsigned char* source,
unsigned long sourcelen)
{
struct puff_state s = {
.out = dest,
.outlen = *destlen,
.outcnt = 0,
.in = source,
.inlen = sourcelen,
.incnt = 0,
.bitbuf = 0,
.bitcnt = 0,
};
int err;
if (setjmp(s.env) != 0)
err = 2;
else {
int last;
do {
last = puff_bits(&s, 1);
int type = puff_bits(&s, 2);
err = type == 0 ? puff_stored(&s) : (type == 1 ? puff_fixed(&s) : (type == 2 ? puff_dynamic(&s) : -1));
if (err != 0)
break;
} while (!last);
}
*destlen = s.outcnt;
return err;
}
//% END CODE DERIVED FROM puff.{c,h}
#define ZLIB_HEADER_WIDTH 2
static int puff_zlib_to_file(const unsigned char* source, unsigned long sourcelen, int dest_fd)
{
if (sourcelen < ZLIB_HEADER_WIDTH)
return 0;
source += ZLIB_HEADER_WIDTH;
sourcelen -= ZLIB_HEADER_WIDTH;
const unsigned long max_destlen = 132 << 20;
void* ret = mmap(0, max_destlen, PROT_WRITE | PROT_READ, MAP_PRIVATE | MAP_ANON, -1, 0);
if (ret == MAP_FAILED)
return -1;
unsigned char* dest = (unsigned char*)ret;
unsigned long destlen = max_destlen;
int err = puff(dest, &destlen, source, sourcelen);
if (err) {
munmap(dest, max_destlen);
errno = -err;
return -1;
}
if (write(dest_fd, dest, destlen) != (ssize_t)destlen) {
munmap(dest, max_destlen);
return -1;
}
return munmap(dest, max_destlen);
}
static int setup_loop_device(unsigned char* data, unsigned long size, const char* loopname, int* loopfd_p)
{
int err = 0, loopfd = -1;
int memfd = syscall(__NR_memfd_create, "syzkaller", 0);
if (memfd == -1) {
err = errno;
goto error;
}
if (puff_zlib_to_file(data, size, memfd)) {
err = errno;
goto error_close_memfd;
}
loopfd = open(loopname, O_RDWR);
if (loopfd == -1) {
err = errno;
goto error_close_memfd;
}
if (ioctl(loopfd, LOOP_SET_FD, memfd)) {
if (errno != EBUSY) {
err = errno;
goto error_close_loop;
}
ioctl(loopfd, LOOP_CLR_FD, 0);
usleep(1000);
if (ioctl(loopfd, LOOP_SET_FD, memfd)) {
err = errno;
goto error_close_loop;
}
}
close(memfd);
*loopfd_p = loopfd;
return 0;
error_close_loop:
close(loopfd);
error_close_memfd:
close(memfd);
error:
errno = err;
return -1;
}
static void reset_loop_device(const char* loopname)
{
int loopfd = open(loopname, O_RDWR);
if (loopfd == -1) {
return;
}
if (ioctl(loopfd, LOOP_CLR_FD, 0)) {
}
close(loopfd);
}
static long syz_mount_image(
volatile long fsarg,
volatile long dir,
volatile long flags,
volatile long optsarg,
volatile long change_dir,
volatile unsigned long size,
volatile long image)
{
unsigned char* data = (unsigned char*)image;
int res = -1, err = 0, need_loop_device = !!size;
char* mount_opts = (char*)optsarg;
char* target = (char*)dir;
char* fs = (char*)fsarg;
char* source = NULL;
char loopname[64];
if (need_loop_device) {
int loopfd;
memset(loopname, 0, sizeof(loopname));
snprintf(loopname, sizeof(loopname), "/dev/loop%llu", procid);
if (setup_loop_device(data, size, loopname, &loopfd) == -1)
return -1;
close(loopfd);
source = loopname;
}
mkdir(target, 0777);
char opts[256];
memset(opts, 0, sizeof(opts));
if (strlen(mount_opts) > (sizeof(opts) - 32)) {
}
strncpy(opts, mount_opts, sizeof(opts) - 32);
if (strcmp(fs, "iso9660") == 0) {
flags |= MS_RDONLY;
} else if (strncmp(fs, "ext", 3) == 0) {
bool has_remount_ro = false;
char* remount_ro_start = strstr(opts, "errors=remount-ro");
if (remount_ro_start != NULL) {
char after = *(remount_ro_start + strlen("errors=remount-ro"));
char before = remount_ro_start == opts ? '\0' : *(remount_ro_start - 1);
has_remount_ro = ((before == '\0' || before == ',') && (after == '\0' || after == ','));
}
if (strstr(opts, "errors=panic") || !has_remount_ro)
strcat(opts, ",errors=continue");
} else if (strcmp(fs, "xfs") == 0) {
strcat(opts, ",nouuid");
} else if (strncmp(fs, "gfs2", 4) == 0 && (strstr(opts, "errors=panic") || strstr(opts, "debug"))) {
strcat(opts, ",errors=withdraw");
}
res = mount(source, target, fs, flags, opts);
if (res == -1) {
err = errno;
goto error_clear_loop;
}
res = open(target, O_RDONLY | O_DIRECTORY);
if (res == -1) {
err = errno;
goto error_clear_loop;
}
if (change_dir) {
res = chdir(target);
if (res == -1) {
err = errno;
}
}
error_clear_loop:
if (need_loop_device)
reset_loop_device(loopname);
errno = err;
return res;
}
uint64_t r[1] = {0xffffffffffffffff};
int main(void)
{
syscall(__NR_mmap, /*addr=*/0x1ffffffff000ul, /*len=*/0x1000, /*prot=*/0ul, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32ul, /*fd=*/(intptr_t)-1, /*offset=*/0ul);
syscall(__NR_mmap, /*addr=*/0x200000000000ul, /*len=*/0x1000000, /*prot=PROT_WRITE|PROT_READ|PROT_EXEC*/7ul, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32ul, /*fd=*/(intptr_t)-1, /*offset=*/0ul);
syscall(__NR_mmap, /*addr=*/0x200001000000ul, /*len=*/0x1000, /*prot=*/0ul, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32ul, /*fd=*/(intptr_t)-1, /*offset=*/0ul);
const char* reason;
(void)reason;
intptr_t res = 0;
if (write(1, "executing program\n", sizeof("executing program\n") - 1)) {}
// syz_mount_image$jfs arguments: [
// fs: ptr[in, buffer] {
// buffer: {6a 66 73 00} (length 0x4)
// }
// dir: ptr[in, buffer] {
// buffer: {2e 2f 62 75 73 00} (length 0x6)
// }
// flags: mount_flags = 0x402 (8 bytes)
// opts: ptr[inout, array[ANYUNION]] {
// array[ANYUNION] {
// }
// }
// chdir: int8 = 0xfe (1 bytes)
// size: len = 0x61a8 (8 bytes)
// img: ptr[in, buffer] {
// buffer: (compressed buffer with length 0x61a8)
// }
// ]
// returns fd_dir
memcpy((void*)0x200000000000, "jfs\000", 4);
memcpy((void*)0x200000000040, "./bus\000", 6);
memcpy((void*)0x200000009940, "... [truncated large byte array] ...", 25000);
syz_mount_image(/*fs=*/0x200000000000, /*dir=*/0x200000000040, /*flags=MS_NOSUID|MS_NOATIME*/0x402, /*opts=*/0x200000000580, /*chdir=*/0xfe, /*size=*/0x61a8, /*img=*/0x200000009940);
// rename arguments: [
// old: ptr[in, buffer] {
// buffer: {2e 2f 66 69 6c 65 32 00} (length 0x8)
// }
// new: ptr[in, buffer] {
// buffer: {2e 2f 62 75 73 00} (length 0x6)
// }
// ]
memcpy((void*)0x2000000002c0, "./file2\000", 8);
memcpy((void*)0x200000000340, "./bus\000", 6);
syscall(__NR_rename, /*old=*/0x2000000002c0ul, /*new=*/0x200000000340ul);
// mount arguments: [
// src: ptr[in, blockdev_filename] {
// union blockdev_filename {
// loop: loop_filename {
// prefix: buffer: {2f 64 65 76 2f 6c 6f 6f 70} (length 0x9)
// id: proc = 0x0 (1 bytes)
// z: const = 0x0 (1 bytes)
// }
// }
// }
// dst: ptr[in, buffer] {
// buffer: {2e 2f 62 75 73 00} (length 0x6)
// }
// type: nil
// flags: mount_flags = 0x1000 (8 bytes)
// data: nil
// ]
memcpy((void*)0x200000000380, "/dev/loop", 9);
*(uint8_t*)0x200000000389 = 0x30;
*(uint8_t*)0x20000000038a = 0;
memcpy((void*)0x200000000080, "./bus\000", 6);
syscall(__NR_mount, /*src=*/0x200000000380ul, /*dst=*/0x200000000080ul, /*type=*/0ul, /*flags=MS_BIND*/0x1000ul, /*data=*/0ul);
// open arguments: [
// file: ptr[in, buffer] {
// buffer: {2e 2f 62 75 73 00} (length 0x6)
// }
// flags: open_flags = 0x0 (8 bytes)
// mode: open_mode = 0x0 (8 bytes)
// ]
// returns fd
memcpy((void*)0x200000000000, "./bus\000", 6);
res = syscall(__NR_open, /*file=*/0x200000000000ul, /*flags=*/0ul, /*mode=*/0ul);
if (res != -1)
r[0] = res;
// ioctl$LOOP_SET_STATUS64 arguments: [
// fd: fd_loop (resource)
// cmd: const = 0x4c04 (4 bytes)
// arg: ptr[in, loop_info64] {
// loop_info64 {
// lo_device: const = 0x0 (8 bytes)
// lo_inode: const = 0x0 (8 bytes)
// lo_rdevice: const = 0x0 (8 bytes)
// lo_offset: int64 = 0x7f (8 bytes)
// lo_sizelimit: int64 = 0x0 (8 bytes)
// lo_number: const = 0x0 (4 bytes)
// lo_enc_type: lo_encrypt_type = 0x0 (4 bytes)
// lo_enc_key_size: int32 = 0x0 (4 bytes)
// lo_flags: lo_flags = 0x0 (4 bytes)
// lo_file_name: buffer: {ef 35 9f 41 3b b9 01 52 f7 d6 d1 ce 5d 29 c3 ee 5e 5c a9 00 0f 7c 41 49 9d c2 aa c6 3a 01 00 00 00 00 00 00 00 4f aa 2a d9 c0 84 a0 03 ea 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00} (length 0x40)
// lo_crypt_name: buffer: {03 6c 47 c6 78 08 20 04 00 00 00 00 00 00 00 33 52 63 bd bc ef 54 9b a1 97 fc e4 7d df dd 75 3a bd 95 01 00 00 2a 00 ff ff ff ff ff ff ff ff 00 00 00 00 e8 f2 00 00 00 02 00 00 00 00 00 00 00} (length 0x40)
// lo_enc_key: buffer: {b7 32 67 36 18 1c 20 82 20 00 00 00 b9 00 00 00 00 00 00 00 00 00 f0 ff ff ff ff f2 ff 00 00 00} (length 0x20)
// lo_init: array[int64] {
// int64 = 0x0 (8 bytes)
// int64 = 0x0 (8 bytes)
// }
// }
// }
// ]
*(uint64_t*)0x200000000240 = 0;
*(uint64_t*)0x200000000248 = 0;
*(uint64_t*)0x200000000250 = 0;
*(uint64_t*)0x200000000258 = 0x7f;
*(uint64_t*)0x200000000260 = 0;
*(uint32_t*)0x200000000268 = 0;
*(uint32_t*)0x20000000026c = 0;
*(uint32_t*)0x200000000270 = 0;
*(uint32_t*)0x200000000274 = 0;
memcpy((void*)0x200000000278, "... [truncated large byte array] ...", 64);
memcpy((void*)0x2000000002b8, "... [truncated large byte array] ...", 64);
memcpy((void*)0x2000000002f8, "... [truncated large byte array] ...", 32);
*(uint64_t*)0x200000000318 = 0;
*(uint64_t*)0x200000000320 = 0;
syscall(__NR_ioctl, /*fd=*/r[0], /*cmd=*/0x4c04, /*arg=*/0x200000000240ul);
return 0;
}
Reply: An array-index-out-of-bounds crash occurs in `dbJoin` when the JFS filesystem attempts to free blocks using a corrupted `dmap` structure read from disk.
### Root Cause
The JFS filesystem uses a binary buddy system to manage free space. The state of the buddy system is stored in `dmap` (for leaf levels) and `dmapctl` (for upper levels) structures on disk.
When a `dmap` is read from disk, its `dmaptree` structure is not fully validated. Specifically, the `leafidx` field (which indicates the index of the first leaf in the `stree` array) is read directly from disk and used to compute pointers to the leaf nodes.
If `leafidx` is corrupted (e.g., set to `0xffffffff` / `-1`), the computed pointer will point out of bounds of the `stree` array. When `dbFreeDmap` is called to free blocks, it calls `dbFreeBits`, which in turn calls `dbJoin` to update the buddy system. `dbJoin` uses the corrupted `leafidx` to access the leaf nodes:
```c
// fs/jfs/jfs_dmap.c:2867
leaf = tp->dmt_stree + le32_to_cpu(tp->dmt_leafidx);
```
This results in an out-of-bounds memory access when `leaf[buddy]` is subsequently accessed.
While a similar integrity check (`check_dmapctl`) was recently added for `dmapctl` structures in commit `cce219b203c4` ("jfs: add dmapctl integrity check to prevent invalid operations"), the `dmap` structures (`dmaptree`) were left unchecked.
### Fix
Introduce a `check_dmaptree` function, similar to the existing `check_dmapctl` function, to validate the integrity of the `dmaptree` structure when it is used.
The `check_dmaptree` function should verify that:
- `nleafs` is within bounds (`<= LPERDMAP`).
- `l2nleafs` is within bounds (`<= L2LPERDMAP`).
- `nleafs` matches `2^l2nleafs`.
- `leafidx` is exactly `LEAFIND`.
- `height` is within bounds (`<= 4`).
- `budmin` is valid (`>= BUDMIN`).
- The leaf nodes fit within the `stree` array (`leafidx + nleafs <= TREESIZE`).
- The values of the leaf nodes are valid (`>= NOFREE` and `<= 31`).
Call `check_dmaptree` in the functions that operate on the `dmap` structure, such as `dbFreeDmap`, `dbAllocNext`, `dbAllocNear`, `dbAllocDmapLev`, `dbAllocDmapBU`, and `dbAllocDmap`. If `check_dmaptree` fails, return `-EIO` to prevent further processing of the corrupted `dmap`. This will also replace the existing partial checks for `leafidx` in `dbAllocNext` and `dbAllocNear`.
```c
/*
* check_dmaptree - Validate integrity of a dmaptree structure
* @dtp: Pointer to the dmaptree structure to check
*
* Return: true if valid, false if corrupted
*/
static bool check_dmaptree(struct dmaptree *dtp)
{
u32 nleafs, l2nleafs, leafidx, height;
int i;
nleafs = le32_to_cpu(dtp->nleafs);
/* Check basic field ranges */
if (unlikely(nleafs > LPERDMAP)) {
jfs_err("dmaptree: invalid nleafs %u (max %u)",
nleafs, LPERDMAP);
return false;
}
l2nleafs = le32_to_cpu(dtp->l2nleafs);
if (unlikely(l2nleafs > L2LPERDMAP)) {
jfs_err("dmaptree: invalid l2nleafs %u (max %u)",
l2nleafs, L2LPERDMAP);
return false;
}
/* Verify nleafs matches l2nleafs (must be power of two) */
if (unlikely((1U << l2nleafs) != nleafs)) {
jfs_err("dmaptree: nleafs %u != 2^%u",
nleafs, l2nleafs);
return false;
}
leafidx = le32_to_cpu(dtp->leafidx);
/* Check leaf index matches expected position */
if (unlikely(leafidx != LEAFIND)) {
jfs_err("dmaptree: invalid leafidx %u (expected %u)",
leafidx, LEAFIND);
return false;
}
height = le32_to_cpu(dtp->height);
/* Check tree height is within valid range */
if (unlikely(height > 4)) {
jfs_err("dmaptree: invalid height %u (max 4)",
height);
return false;
}
/* Check budmin is valid */
if (unlikely(dtp->budmin < BUDMIN)) {
jfs_err("dmaptree: invalid budmin %d (min %d)",
dtp->budmin, BUDMIN);
return false;
}
/* Check leaf nodes fit within stree array */
if (unlikely(leafidx + nleafs > TREESIZE)) {
jfs_err("dmaptree: leaf range exceeds stree size (end %u > %u)",
leafidx + nleafs, TREESIZE);
return false;
}
/* Check leaf nodes have valid values */
for (i = leafidx; i < leafidx + nleafs; i++) {
s8 val = dtp->stree[i];
if (unlikely(val < NOFREE)) {
jfs_err("dmaptree: invalid leaf value %d at index %d",
val, i);
return false;
} else if (unlikely(val > 31)) {
jfs_err("dmaptree: leaf value %d too large at index %d", val, i);
return false;
}
}
return true;
}
```
Then, in functions like `dbFreeDmap`, `dbAllocNext`, `dbAllocNear`, `dbAllocDmapLev`, `dbAllocDmapBU`, and `dbAllocDmap`, add the check at the beginning:
```c
if (unlikely(!check_dmaptree(&dp->tree))) {
jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmap page\n");
return -EIO;
}
```
|
| 8/2 |
2026/06/30 14:36 |
llm |
debugger |
0m
Model:
Tokens:
input: 12766
output: 41
thoughts: 125
Thoughts: **Analyzing a UBSAN Crash in the JFS Filesystem**
Okay, so I've got a UBSAN array-index-out-of-bounds crash on my hands, specifically in the JFS filesystem code. The culprit is `fs/jfs/jfs_dmap.c`, line 2867, position 24. The reported index is 4294967295. Now, that's interesting because, in the world of 32-bit unsigned integers, that's equivalent to -1. Effectively, we're trying to access an array using a negative index, although it is encoded as a maximum unsigned integer. The array type in question is an `s8[1365]`, so a signed 8-bit character array with 1365 elements. The failing function is `dbJoin`. I need to dive into that section of the code and see what's going on. Line 2867 and surrounding lines will be my starting point. I need to understand how the index is being calculated, what data is flowing into that `dbJoin` function, and why it is generating such an unexpected index. Let's see if we can find some clues.
|
| 9/2 |
2026/06/30 14:36 |
tool |
read-file |
0m
Args: map[File:fs/jfs/jfs_dmap.c FirstLine:2850 LineCount:50]
Results: map[Contents:2850: * PARAMETERS:
2851: * tp - pointer to the tree containing the leaf.
2852: * leafno - the number of the leaf to be updated.
2853: * newval - the new value for the leaf.
2854: *
2855: * RETURN VALUES: none
2856: */
2857: static int dbJoin(dmtree_t *tp, int leafno, int newval, bool is_ctl)
2858: {
2859: int budsz, buddy;
2860: s8 *leaf;
2861:
2862: /* can the new leaf value require a join with other leaves ?
2863: */
2864: if (newval >= tp->dmt_budmin) {
2865: /* pickup a pointer to the leaves of the tree.
2866: */
2867: leaf = tp->dmt_stree + le32_to_cpu(tp->dmt_leafidx);
2868:
2869: /* try to join the specified leaf into a large binary
2870: * buddy system. the join proceeds by attempting to join
2871: * the specified leafno with its buddy (leaf) at new value.
2872: * if the join occurs, we attempt to join the left leaf
2873: * of the joined buddies with its buddy at new value + 1.
2874: * we continue to join until we find a buddy that cannot be
2875: * joined (does not have a value equal to the size of the
2876: * last join) or until all leaves have been joined into a
2877: * single system.
2878: *
2879: * get the buddy size (number of words covered) of
2880: * the new value.
2881: */
2882: budsz = BUDSIZE(newval, tp->dmt_budmin);
2883:
2884: /* try to join.
2885: */
2886: while (budsz < le32_to_cpu(tp->dmt_nleafs)) {
2887: /* get the buddy leaf.
2888: */
2889: buddy = leafno ^ budsz;
2890:
2891: /* if the leaf's new value is greater than its
2892: * buddy's value, we join no more.
2893: */
2894: if (newval > leaf[buddy])
2895: break;
2896:
2897: /* It shouldn't be less */
2898: if (newval < leaf[buddy])
2899: return -EIO;
]
|
| 10/2 |
2026/06/30 14:36 |
llm |
debugger |
0m
Model:
Tokens:
input: 13964
output: 29
thoughts: 261
Thoughts: **Debugging a JFS Crash**
Okay, so I've got a crash at line 2867 in `jfs_dmap.c`. The trace is pretty clear: it's a `UBSAN` array-index-out-of-bounds error. The specific index is 4294967295, which is -1 when interpreted as a signed integer, and it's out of range for the array `s8[1365]` named `tp->dmt_stree`. Right, let's look at that line, specifically.
Line 2867 is: `leaf = tp->dmt_stree + le32_to_cpu(tp->dmt_leafidx);`. Alright, so `tp->dmt_leafidx` is the culprit. `le32_to_cpu()` is returning 4294967295, which becomes -1 when we use it as an index, which is definitely out of bounds. The code is interpreting that as a negative index. I need to understand where `tp->dmt_leafidx` is being set and what its valid range is. Let's see how `dmtree_t` is defined; I need to know how these fields are being populated.
|
| 11/2 |
2026/06/30 14:36 |
tool |
codesearch-find-references |
0m
Args: map[IncludeSnippetLines:0 Name:dmtree_t]
Results: map[References:[map[ReferenceKind:uses ReferencingEntityKind:function ReferencingEntityName:dbAdjCtl SourceFile:fs/jfs/jfs_dmap.c SourceLine:2613] map[ReferenceKind:uses ReferencingEntityKind:function ReferencingEntityName:dbAdjCtl SourceFile:fs/jfs/jfs_dmap.c SourceLine:2620] map[ReferenceKind:uses ReferencingEntityKind:function ReferencingEntityName:dbAdjCtl SourceFile:fs/jfs/jfs_dmap.c SourceLine:2622] map[ReferenceKind:uses ReferencingEntityKind:function ReferencingEntityName:dbAdjCtl SourceFile:fs/jfs/jfs_dmap.c SourceLine:2652] map[ReferenceKind:uses ReferencingEntityKind:function ReferencingEntityName:dbAdjCtl SourceFile:fs/jfs/jfs_dmap.c SourceLine:2662] map[ReferenceKind:uses ReferencingEntityKind:function ReferencingEntityName:dbAdjCtl SourceFile:fs/jfs/jfs_dmap.c SourceLine:2664] map[ReferenceKind:uses ReferencingEntityKind:function ReferencingEntityName:dbAdjTree SourceFile:fs/jfs/jfs_dmap.c SourceLine:2951] map[ReferenceKind:uses ReferencingEntityKind:function ReferencingEntityName:dbAllocBits SourceFile:fs/jfs/jfs_dmap.c SourceLine:2230] map[ReferenceKind:uses ReferencingEntityKind:function ReferencingEntityName:dbAllocBits SourceFile:fs/jfs/jfs_dmap.c SourceLine:2230] map[ReferenceKind:uses ReferencingEntityKind:function ReferencingEntityName:dbAllocDmapLev SourceFile:fs/jfs/jfs_dmap.c SourceLine:2056] map[ReferenceKind:uses ReferencingEntityKind:function ReferencingEntityName:dbBackSplit SourceFile:fs/jfs/jfs_dmap.c SourceLine:2778] map[ReferenceKind:uses ReferencingEntityKind:function ReferencingEntityName:dbFindCtl SourceFile:fs/jfs/jfs_dmap.c SourceLine:1804] map[ReferenceKind:uses ReferencingEntityKind:function ReferencingEntityName:dbFindLeaf SourceFile:fs/jfs/jfs_dmap.c SourceLine:3034] map[ReferenceKind:uses ReferencingEntityKind:function ReferencingEntityName:dbFreeBits SourceFile:fs/jfs/jfs_dmap.c SourceLine:2377] map[ReferenceKind:uses ReferencingEntityKind:function ReferencingEntityName:dbFreeBits SourceFile:fs/jfs/jfs_dmap.c SourceLine:2377] map[ReferenceKind:uses ReferencingEntityKind:function ReferencingEntityName:dbFreeDmap SourceFile:fs/jfs/jfs_dmap.c SourceLine:2195] map[ReferenceKind:uses ReferencingEntityKind:function ReferencingEntityName:dbJoin SourceFile:fs/jfs/jfs_dmap.c SourceLine:2857] map[ReferenceKind:uses ReferencingEntityKind:function ReferencingEntityName:dbSplit SourceFile:fs/jfs/jfs_dmap.c SourceLine:2713]] TruncatedOutput:false]
|
| 12/2 |
2026/06/30 14:36 |
llm |
debugger |
0m
Model:
Tokens:
input: 15519
output: 41
thoughts: 14
Thoughts: Here's my thought process, summarized as you requested:
**Delving into `dmtree_t`**
Alright, let's get down to brass tacks. I need to understand what this `dmtree_t` thing is all about. Time to consult the documentation, the source code, maybe even some example usage if it's available. My gut tells me this is probably a type definition, perhaps a struct or typedef, or class. Likely dealing with a data structure. Given the "dm" prefix, I'm already anticipating that this might be related to device management or data management in some capacity. My experience tells me these types are often related to a binary tree implementation, but let's not jump to any conclusions. We need a solid definition. From there I can determine its role within the system. Let's start with a focused search and go from there. This is a critical first step.
|
| 13/2 |
2026/06/30 14:36 |
tool |
codesearch-find-references |
0m
Args: map[IncludeSnippetLines:0 Name:dmtree_t SourceTreePrefix:fs/jfs/]
Results: map[References:[map[ReferenceKind:uses ReferencingEntityKind:function ReferencingEntityName:dbAdjCtl SourceFile:fs/jfs/jfs_dmap.c SourceLine:2613] map[ReferenceKind:uses ReferencingEntityKind:function ReferencingEntityName:dbAdjCtl SourceFile:fs/jfs/jfs_dmap.c SourceLine:2620] map[ReferenceKind:uses ReferencingEntityKind:function ReferencingEntityName:dbAdjCtl SourceFile:fs/jfs/jfs_dmap.c SourceLine:2622] map[ReferenceKind:uses ReferencingEntityKind:function ReferencingEntityName:dbAdjCtl SourceFile:fs/jfs/jfs_dmap.c SourceLine:2652] map[ReferenceKind:uses ReferencingEntityKind:function ReferencingEntityName:dbAdjCtl SourceFile:fs/jfs/jfs_dmap.c SourceLine:2662] map[ReferenceKind:uses ReferencingEntityKind:function ReferencingEntityName:dbAdjCtl SourceFile:fs/jfs/jfs_dmap.c SourceLine:2664] map[ReferenceKind:uses ReferencingEntityKind:function ReferencingEntityName:dbAdjTree SourceFile:fs/jfs/jfs_dmap.c SourceLine:2951] map[ReferenceKind:uses ReferencingEntityKind:function ReferencingEntityName:dbAllocBits SourceFile:fs/jfs/jfs_dmap.c SourceLine:2230] map[ReferenceKind:uses ReferencingEntityKind:function ReferencingEntityName:dbAllocBits SourceFile:fs/jfs/jfs_dmap.c SourceLine:2230] map[ReferenceKind:uses ReferencingEntityKind:function ReferencingEntityName:dbAllocDmapLev SourceFile:fs/jfs/jfs_dmap.c SourceLine:2056] map[ReferenceKind:uses ReferencingEntityKind:function ReferencingEntityName:dbBackSplit SourceFile:fs/jfs/jfs_dmap.c SourceLine:2778] map[ReferenceKind:uses ReferencingEntityKind:function ReferencingEntityName:dbFindCtl SourceFile:fs/jfs/jfs_dmap.c SourceLine:1804] map[ReferenceKind:uses ReferencingEntityKind:function ReferencingEntityName:dbFindLeaf SourceFile:fs/jfs/jfs_dmap.c SourceLine:3034] map[ReferenceKind:uses ReferencingEntityKind:function ReferencingEntityName:dbFreeBits SourceFile:fs/jfs/jfs_dmap.c SourceLine:2377] map[ReferenceKind:uses ReferencingEntityKind:function ReferencingEntityName:dbFreeBits SourceFile:fs/jfs/jfs_dmap.c SourceLine:2377] map[ReferenceKind:uses ReferencingEntityKind:function ReferencingEntityName:dbFreeDmap SourceFile:fs/jfs/jfs_dmap.c SourceLine:2195] map[ReferenceKind:uses ReferencingEntityKind:function ReferencingEntityName:dbJoin SourceFile:fs/jfs/jfs_dmap.c SourceLine:2857] map[ReferenceKind:uses ReferencingEntityKind:function ReferencingEntityName:dbSplit SourceFile:fs/jfs/jfs_dmap.c SourceLine:2713]] TruncatedOutput:false]
|
| 14/2 |
2026/06/30 14:36 |
llm |
debugger |
0m
Model:
Tokens:
input: 16750
output: 21
thoughts: 20
Thoughts: Here's my thought process, as if I were thinking it:
**Digging into `dmtree_t`**
Okay, so `dmtree_t`... I see it's a `typedef`. Right, that means it's an alias for some underlying type. I need to understand what this thing actually *is*. The declaration alone won't tell me everything. Let's see if I can find where it's defined and used. Time to fire up a `grep` and see where this `dmtree_t` is hiding in the codebase. That's the best way to get a quick overview of its structure and purpose. I'm assuming that it is used throughout the code so this will be a good first step.
|
| 15/2 |
2026/06/30 14:36 |
tool |
grepper |
0m
Error:
Args: map[Expression:typedef.*dmtree_t]
Results:
|
| 16/2 |
2026/06/30 14:36 |
llm |
debugger |
0m
Model:
Tokens:
input: 16967
output: 21
thoughts: 14
Thoughts: Okay, here's my summarized thought process, assuming I'm an expert and this is just the beginning of a larger investigation:
**Initiating Code Exploration: Targeting `dmtree_t`**
Alright, time to dive in. My first instinct is to get a handle on what `dmtree_t` actually *is*. This strongly suggests some sort of data structure, potentially tree-like given the naming convention. So, I'm going to start with the most basic approach: a good old `grep`. I'll run a grep search for the definition of `dmtree_t` in the codebase. That should give me the type definition and allow me to determine how it's structured, what fields are available, and the overall purpose of this data structure. Knowing the definition is paramount before I can understand its use and how it relates to the larger system. Let's see what we can find.
|
| 17/2 |
2026/06/30 14:36 |
tool |
grepper |
0m
Error:
Args: map[Expression:struct.*dmtree_t]
Results:
|
| 18/2 |
2026/06/30 14:36 |
llm |
debugger |
0m
Model:
Tokens:
input: 17210
output: 30
thoughts: 20
Thoughts: Okay, here's my interpretation of that thought process, assuming I'm an expert in file systems and kernel internals:
**Diving into JFS with `dmtree_t`**
Alright, time to get my hands dirty. I'm thinking I need to find the definition or usage of `dmtree_t`. Given that the context seems to be about file systems, specifically JFS, the natural starting point is to dive into the JFS source code. I'm going to use `grep` – a classic, reliable tool – to pinpoint the location of `dmtree_t`. This should be quick, and hopefully, I'll be able to trace its purpose and relationship to other JFS data structures without having to do a full code dive. `fs/jfs/` is the directory I'll target first. Let's see what we can dig up!
|
| 19/2 |
2026/06/30 14:36 |
tool |
grepper |
0m
Args: map[Expression:dmtree_t PathPrefix:fs/jfs/]
Results: map[Output:fs/jfs/jfs_dmap.c=64=static void dbAllocBits(struct bmap * bmp, struct dmap * dp, s64 blkno,
fs/jfs/jfs_dmap.c-65- int nblocks);
fs/jfs/jfs_dmap.c:66:static void dbSplit(dmtree_t *tp, int leafno, int splitsz, int newval, bool is_ctl);
fs/jfs/jfs_dmap.c:67:static int dbBackSplit(dmtree_t *tp, int leafno, bool is_ctl);
fs/jfs/jfs_dmap.c:68:static int dbJoin(dmtree_t *tp, int leafno, int newval, bool is_ctl);
fs/jfs/jfs_dmap.c:69:static void dbAdjTree(dmtree_t *tp, int leafno, int newval, bool is_ctl);
fs/jfs/jfs_dmap.c-70-static int dbAdjCtl(struct bmap * bmp, s64 blkno, int newval, int alloc,
--
fs/jfs/jfs_dmap.c=89=static int dbFindCtl(struct bmap * bmp, int l2nb, int level, s64 * blkno);
fs/jfs/jfs_dmap.c:90:static int dbFindLeaf(dmtree_t *tp, int l2nb, int *leafidx, bool is_ctl);
fs/jfs/jfs_dmap.c-91-static int dbFreeBits(struct bmap * bmp, struct dmap * dp, s64 blkno,
--
fs/jfs/jfs_dmap.c=1768=static int dbFindCtl(struct bmap * bmp, int l2nb, int level, s64 * blkno)
--
fs/jfs/jfs_dmap.c-1803- */
fs/jfs/jfs_dmap.c:1804: rc = dbFindLeaf((dmtree_t *) dcp, l2nb, &leafidx, true);
fs/jfs/jfs_dmap.c-1805-
--
fs/jfs/jfs_dmap.c=2043=dbAllocDmapLev(struct bmap * bmp,
--
fs/jfs/jfs_dmap.c-2055- */
fs/jfs/jfs_dmap.c:2056: if (dbFindLeaf((dmtree_t *) &dp->tree, l2nb, &leafidx, false))
fs/jfs/jfs_dmap.c-2057- return -ENOSPC;
--
fs/jfs/jfs_dmap.c=2164=static int dbFreeDmap(struct bmap * bmp, struct dmap * dp, s64 blkno,
--
fs/jfs/jfs_dmap.c-2194- if (dp->tree.stree[word] == NOFREE)
fs/jfs/jfs_dmap.c:2195: dbBackSplit((dmtree_t *)&dp->tree, word, false);
fs/jfs/jfs_dmap.c-2196-
--
fs/jfs/jfs_dmap.c=2226=static void dbAllocBits(struct bmap * bmp, struct dmap * dp, s64 blkno,
--
fs/jfs/jfs_dmap.c-2229- int dbitno, word, rembits, nb, nwords, wbitno, nw, agno;
fs/jfs/jfs_dmap.c:2230: dmtree_t *tp = (dmtree_t *) & dp->tree;
fs/jfs/jfs_dmap.c-2231- int size;
--
fs/jfs/jfs_dmap.c=2373=static int dbFreeBits(struct bmap * bmp, struct dmap * dp, s64 blkno,
--
fs/jfs/jfs_dmap.c-2376- int dbitno, word, rembits, nb, nwords, wbitno, nw, agno;
fs/jfs/jfs_dmap.c:2377: dmtree_t *tp = (dmtree_t *) & dp->tree;
fs/jfs/jfs_dmap.c-2378- int rc = 0;
--
fs/jfs/jfs_dmap.c=2557=dbAdjCtl(struct bmap * bmp, s64 blkno, int newval, int alloc, int level)
--
fs/jfs/jfs_dmap.c-2612- if (oldval == NOFREE) {
fs/jfs/jfs_dmap.c:2613: rc = dbBackSplit((dmtree_t *)dcp, leafno, true);
fs/jfs/jfs_dmap.c-2614- if (rc) {
--
fs/jfs/jfs_dmap.c-2619- }
fs/jfs/jfs_dmap.c:2620: dbSplit((dmtree_t *) dcp, leafno, dcp->budmin, newval, true);
fs/jfs/jfs_dmap.c-2621- } else {
fs/jfs/jfs_dmap.c:2622: rc = dbJoin((dmtree_t *) dcp, leafno, newval, true);
fs/jfs/jfs_dmap.c-2623- if (rc) {
--
fs/jfs/jfs_dmap.c-2651- if (alloc) {
fs/jfs/jfs_dmap.c:2652: dbJoin((dmtree_t *) dcp, leafno,
fs/jfs/jfs_dmap.c-2653- oldval, true);
--
fs/jfs/jfs_dmap.c-2661- if (dcp->stree[ti] == NOFREE)
fs/jfs/jfs_dmap.c:2662: dbBackSplit((dmtree_t *)
fs/jfs/jfs_dmap.c-2663- dcp, leafno, true);
fs/jfs/jfs_dmap.c:2664: dbSplit((dmtree_t *) dcp, leafno,
fs/jfs/jfs_dmap.c-2665- dcp->budmin, oldval, true);
--
fs/jfs/jfs_dmap.c-2712- */
fs/jfs/jfs_dmap.c:2713:static void dbSplit(dmtree_t *tp, int leafno, int splitsz, int newval, bool is_ctl)
fs/jfs/jfs_dmap.c-2714-{
--
fs/jfs/jfs_dmap.c-2777- */
fs/jfs/jfs_dmap.c:2778:static int dbBackSplit(dmtree_t *tp, int leafno, bool is_ctl)
fs/jfs/jfs_dmap.c-2779-{
--
fs/jfs/jfs_dmap.c-2856- */
fs/jfs/jfs_dmap.c:2857:static int dbJoin(dmtree_t *tp, int leafno, int newval, bool is_ctl)
fs/jfs/jfs_dmap.c-2858-{
--
fs/jfs/jfs_dmap.c-2950- */
fs/jfs/jfs_dmap.c:2951:static void dbAdjTree(dmtree_t *tp, int leafno, int newval, bool is_ctl)
fs/jfs/jfs_dmap.c-2952-{
--
fs/jfs/jfs_dmap.c-3013- *
fs/jfs/jfs_dmap.c:3014: * FUNCTION: search a dmtree_t for sufficient free blocks, returning
fs/jfs/jfs_dmap.c-3015- * the index of a leaf describing the free blocks if
--
fs/jfs/jfs_dmap.c-3017- *
fs/jfs/jfs_dmap.c:3018: * the search starts at the top of the dmtree_t tree and
fs/jfs/jfs_dmap.c-3019- * proceeds down the tree to the leftmost leaf with sufficient
--
fs/jfs/jfs_dmap.c-3033- */
fs/jfs/jfs_dmap.c:3034:static int dbFindLeaf(dmtree_t *tp, int l2nb, int *leafidx, bool is_ctl)
fs/jfs/jfs_dmap.c-3035-{
--
fs/jfs/jfs_dmap.h=175=typedef union dmtree {
--
fs/jfs/jfs_dmap.h-177- struct dmapctl t2;
fs/jfs/jfs_dmap.h:178:} dmtree_t;
fs/jfs/jfs_dmap.h-179-
]
|
| 20/2 |
2026/06/30 14:36 |
llm |
debugger |
0m
Model:
Tokens:
input: 19863
output: 40
thoughts: 32
Thoughts: Okay, here's my attempt at summarizing that thought process, tailored for an expert audience:
**Diving into the JFS Data Map Definition**
Alright, so I need to understand how the JFS filesystem handles its data maps, specifically the `dmtree_t` structure. My instinct is always to go straight to the source. The header file I need, according to my mental map of the Linux kernel, is in `fs/jfs/jfs_dmap.h`. Time to pull that up and see what we're dealing with. Let's get a look at the actual definition.
|
| 21/2 |
2026/06/30 14:36 |
tool |
read-file |
0m
Args: map[File:fs/jfs/jfs_dmap.h FirstLine:150 LineCount:50]
Results: map[Contents: 150: __le64 start; /* 8: starting blkno for this dmap */
151: struct dmaptree tree; /* 360: dmap tree */
152: u8 pad[1672]; /* 1672: pad to 2048 bytes */
153: __le32 wmap[LPERDMAP]; /* 1024: bits of the working map */
154: __le32 pmap[LPERDMAP]; /* 1024: bits of the persistent map */
155: }; /* - 4096 - */
156:
157: /*
158: * disk map control page per level.
159: *
160: * dmapctl must be consistent with dmaptree.
161: */
162: struct dmapctl {
163: __le32 nleafs; /* 4: number of tree leafs */
164: __le32 l2nleafs; /* 4: l2 number of tree leafs */
165: __le32 leafidx; /* 4: index of the first tree leaf */
166: __le32 height; /* 4: height of tree */
167: s8 budmin; /* 1: minimum l2 tree leaf value */
168: s8 stree[CTLTREESIZE]; /* CTLTREESIZE: dmapctl tree */
169: u8 pad[2714]; /* 2714: pad to 4096 */
170: }; /* - 4096 - */
171:
172: /*
173: * common definition for dmaptree within dmap and dmapctl
174: */
175: typedef union dmtree {
176: struct dmaptree t1;
177: struct dmapctl t2;
178: } dmtree_t;
179:
180: /* macros for accessing fields within dmtree */
181: #define dmt_nleafs t1.nleafs
182: #define dmt_l2nleafs t1.l2nleafs
183: #define dmt_leafidx t1.leafidx
184: #define dmt_height t1.height
185: #define dmt_budmin t1.budmin
186: #define dmt_stree t2.stree
187:
188: /*
189: * on-disk aggregate disk allocation map descriptor.
190: */
191: struct dbmap_disk {
192: __le64 dn_mapsize; /* 8: number of blocks in aggregate */
193: __le64 dn_nfree; /* 8: num free blks in aggregate map */
194: __le32 dn_l2nbperpage; /* 4: number of blks per page */
195: __le32 dn_numag; /* 4: total number of ags */
196: __le32 dn_maxlevel; /* 4: number of active ags */
197: __le32 dn_maxag; /* 4: max active alloc group number */
198: __le32 dn_agpref; /* 4: preferred alloc group (hint) */
199: __le32 dn_aglevel; /* 4: dmapctl level holding the AG */
]
|
| 22/2 |
2026/06/30 14:36 |
llm |
debugger |
0m
Model:
Tokens:
input: 21086
output: 40
thoughts: 54
Thoughts: Here's my thought process, as if I were analyzing this code:
**Deconstructing Data Structures: A Deep Dive**
Okay, so I'm looking at this code and immediately see a variable, `dmt_leafidx`, and it's being assigned the value of `t1.leafidx`. Right away, I'm thinking, "Okay, we're talking about a leaf index, but where is this coming from?" Then, I remember I need to clarify what these variables represent within the bigger picture, so I remind myself that `t1` is a `struct dmaptree` and `t2` is a `struct dmapctl`. This makes my job easier because I have my bearings now. To fully understand what's happening, I need to look up the definition of `struct dmaptree`. I need to fully understand what each variable actually *is*. Let's get that definition and move forward.
|
| 23/2 |
2026/06/30 14:36 |
tool |
read-file |
0m
Args: map[File:fs/jfs/jfs_dmap.h FirstLine:120 LineCount:30]
Results: map[Contents: 120: */
121: #define BLKTOAG(b,sbi) ((b) >> ((sbi)->bmap->db_agl2size))
122:
123: /* convert allocation group number to starting disk block
124: * number.
125: */
126: #define AGTOBLK(a,ip) \
127: ((s64)(a) << (JFS_SBI((ip)->i_sb)->bmap->db_agl2size))
128:
129: /*
130: * dmap summary tree
131: *
132: * dmaptree must be consistent with dmapctl.
133: */
134: struct dmaptree {
135: __le32 nleafs; /* 4: number of tree leafs */
136: __le32 l2nleafs; /* 4: l2 number of tree leafs */
137: __le32 leafidx; /* 4: index of first tree leaf */
138: __le32 height; /* 4: height of the tree */
139: s8 budmin; /* 1: min l2 tree leaf value to combine */
140: s8 stree[TREESIZE]; /* TREESIZE: tree */
141: u8 pad[2]; /* 2: pad to word boundary */
142: }; /* - 360 - */
143:
144: /*
145: * dmap page per 8K blocks bitmap
146: */
147: struct dmap {
148: __le32 nblocks; /* 4: num blks covered by this dmap */
149: __le32 nfree; /* 4: num of free blks in this dmap */
]
|
| 24/2 |
2026/06/30 14:36 |
llm |
debugger |
0m
Model:
Tokens:
input: 21916
output: 31
thoughts: 14
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience:
**Investigating TREESIZE**
Okay, so `TREESIZE`... what exactly are we talking about here? It's not immediately obvious from the term itself. I need to unpack this. Is it referring to the *physical* size of a tree data structure in memory? Or is it a measure of the number of nodes, height, or perhaps the overall data volume contained within the tree? The specific context is missing, and that will dramatically shape how to interpret and act on it. My initial reflex is to quickly consider the different ways this could be defined. Depending on the type of tree (binary, n-ary, balanced, etc.) and what data it's holding, the precise meaning of `TREESIZE` will need to be pinned down before any meaningful analysis can be done. I'll need to dig deeper for that clarity.
|
| 25/2 |
2026/06/30 14:36 |
tool |
grepper |
0m
Args: map[Expression:#define.*TREESIZE PathPrefix:fs/jfs/]
Results: map[Output:fs/jfs/jfs_dmap.h-10-#define BMAPVERSION 1 /* version number */
fs/jfs/jfs_dmap.h:11:#define TREESIZE (256+64+16+4+1) /* size of a dmap tree */
fs/jfs/jfs_dmap.h-12-#define LEAFIND (64+16+4+1) /* index of 1st leaf of a dmap tree */
--
fs/jfs/jfs_dmap.h-19-#define L2BPERDMAP 13 /* l2 num of blks per dmap */
fs/jfs/jfs_dmap.h:20:#define CTLTREESIZE (1024+256+64+16+4+1) /* size of a dmapctl tree */
fs/jfs/jfs_dmap.h-21-#define CTLLEAFIND (256+64+16+4+1) /* idx of 1st leaf of a dmapctl tree */
]
|
| 26/2 |
2026/06/30 14:36 |
llm |
debugger |
0m
Model:
Tokens:
input: 22438
output: 41
thoughts: 217
Thoughts: **Analyzing a JFS Crash**
Okay, so I'm looking at this crash report, and the core of it seems to be an out-of-bounds access. The error message is screaming: "index 4294967295 is out of range for type 's8[1365]'". Right away, I recognize that `4294967295` is the same as `-1` when interpreted as a signed 32-bit integer, which is what `le32_to_cpu()` is likely doing. That’s my first red flag: a negative index accessing an array!
The code context gives me some critical information. `TREESIZE` is `341`, and `CTLTREESIZE` is `1365`. `dmt_stree` is my array `s8[CTLTREESIZE]`, named `s8 stree[1365]`, and it's being used by `t2.stree`. Then `dmt_leafidx`, which holds the problematic index, is effectively `t1.leafidx`, which is offset in memory in a similar way to `t2.leafidx`. I need to figure out how this `dmt_leafidx` is getting set to -1.
Aha, the syzkaller reproducer... it mounts a corrupted JFS image. That's my critical clue. This value is read from disk. The corruption must be the root cause. This confirms my hypothesis. I need to trace this variable's initialization and any validation that might happen. Where exactly is `dmt_leafidx` being validated? That's where I need to focus my attention to determine if this can be a more widespread issue.
|
| 27/2 |
2026/06/30 14:36 |
tool |
codesearch-find-references |
0m
Error:
Args: map[IncludeSnippetLines:0 Name:dmt_leafidx SourceTreePrefix:fs/jfs/]
Results: map[References:<nil> TruncatedOutput:false]
|
| 28/2 |
2026/06/30 14:36 |
llm |
debugger |
0m
Model:
Tokens:
input: 23104
output: 30
thoughts: 20
Thoughts: Here's my take on those thoughts, assuming I'm an expert:
**Investigating a Macro: `dmt_leafidx`**
Okay, so `dmt_leafidx`... looks like it's a macro. Right away, my brain jumps to "grep." I need to see where this is defined and, more importantly, *used* within the codebase. I need context. Grepping will give me a quick and dirty way to trace its definition and all the calls to it. From there, I can get a much better feel for what it's doing. Probably some index calculation, but let's see where it fits into the broader picture. Let's get to work!
|
| 29/2 |
2026/06/30 14:36 |
tool |
grepper |
0m
Args: map[Expression:dmt_leafidx PathPrefix:fs/jfs/]
Results: map[Output:fs/jfs/jfs_dmap.c=2713=static void dbSplit(dmtree_t *tp, int leafno, int splitsz, int newval, bool is_ctl)
--
fs/jfs/jfs_dmap.c-2716- int cursz;
fs/jfs/jfs_dmap.c:2717: s8 *leaf = tp->dmt_stree + le32_to_cpu(tp->dmt_leafidx);
fs/jfs/jfs_dmap.c-2718-
--
fs/jfs/jfs_dmap.c=2778=static int dbBackSplit(dmtree_t *tp, int leafno, bool is_ctl)
--
fs/jfs/jfs_dmap.c-2781- int cursz;
fs/jfs/jfs_dmap.c:2782: s8 *leaf = tp->dmt_stree + le32_to_cpu(tp->dmt_leafidx);
fs/jfs/jfs_dmap.c-2783-
--
fs/jfs/jfs_dmap.c=2857=static int dbJoin(dmtree_t *tp, int leafno, int newval, bool is_ctl)
--
fs/jfs/jfs_dmap.c-2866- */
fs/jfs/jfs_dmap.c:2867: leaf = tp->dmt_stree + le32_to_cpu(tp->dmt_leafidx);
fs/jfs/jfs_dmap.c-2868-
--
fs/jfs/jfs_dmap.c=2951=static void dbAdjTree(dmtree_t *tp, int leafno, int newval, bool is_ctl)
--
fs/jfs/jfs_dmap.c-2959- */
fs/jfs/jfs_dmap.c:2960: lp = leafno + le32_to_cpu(tp->dmt_leafidx);
fs/jfs/jfs_dmap.c-2961-
--
fs/jfs/jfs_dmap.c=3034=static int dbFindLeaf(dmtree_t *tp, int l2nb, int *leafidx, bool is_ctl)
--
fs/jfs/jfs_dmap.c-3071- }
fs/jfs/jfs_dmap.c:3072: if (le32_to_cpu(tp->dmt_leafidx) >= max_idx)
fs/jfs/jfs_dmap.c-3073- return -ENOSPC;
--
fs/jfs/jfs_dmap.c-3077- */
fs/jfs/jfs_dmap.c:3078: *leafidx = x + n - le32_to_cpu(tp->dmt_leafidx);
fs/jfs/jfs_dmap.c-3079-
--
fs/jfs/jfs_dmap.h=175=typedef union dmtree {
--
fs/jfs/jfs_dmap.h-182-#define dmt_l2nleafs t1.l2nleafs
fs/jfs/jfs_dmap.h:183:#define dmt_leafidx t1.leafidx
fs/jfs/jfs_dmap.h-184-#define dmt_height t1.height
]
|
| 30/2 |
2026/06/30 14:36 |
llm |
debugger |
0m
Model:
Tokens:
input: 24215
output: 30
thoughts: 68
Thoughts: **Analyzing `dbFindLeaf` and Leaf Index Validation**
Okay, let's dive into this. My initial focus is on `dbFindLeaf` and, specifically, how the `dmt_leafidx` is validated within its execution. A crucial point is that this `dmt_leafidx` value is, as I recall, read directly from the disk. This raises an immediate red flag: Is there any validation happening *at the point* the `dmap` or `dmapctl` structures are read from the disk? This is a critical security consideration, as unvalidated data from disk could easily lead to exploits. Therefore, my immediate next step is to trace the read operation for `dmap` itself. Understanding the point of read is crucial. The goal is to pinpoint the exact moment any potential validation might or might not be happening.
|
| 31/2 |
2026/06/30 14:36 |
tool |
grepper |
0m
Args: map[Expression:read_metapage PathPrefix:fs/jfs/]
Results: map[Output:fs/jfs/jfs_btree.h=17=struct btpage {
--
fs/jfs/jfs_btree.h-63- {\
fs/jfs/jfs_btree.h:64: MP = read_metapage((IP), BN, SIZE, 1);\
fs/jfs/jfs_btree.h-65- if (MP) {\
--
fs/jfs/jfs_dmap.c=240=int dbMount(struct inode *ipbmap)
--
fs/jfs/jfs_dmap.c-255- /* read the on-disk bmap descriptor. */
fs/jfs/jfs_dmap.c:256: mp = read_metapage(ipbmap,
fs/jfs/jfs_dmap.c-257- BMAPBLKNO << JFS_SBI(ipbmap->i_sb)->l2nbperpage,
--
fs/jfs/jfs_dmap.c=363=int dbSync(struct inode *ipbmap)
--
fs/jfs/jfs_dmap.c-373- /* get the buffer for the on-disk bmap descriptor. */
fs/jfs/jfs_dmap.c:374: mp = read_metapage(ipbmap,
fs/jfs/jfs_dmap.c-375- BMAPBLKNO << JFS_SBI(ipbmap->i_sb)->l2nbperpage,
--
fs/jfs/jfs_dmap.c-377- if (mp == NULL) {
fs/jfs/jfs_dmap.c:378: jfs_err("dbSync: read_metapage failed!");
fs/jfs/jfs_dmap.c-379- return -EIO;
--
fs/jfs/jfs_dmap.c=431=int dbFree(struct inode *ip, s64 blkno, s64 nblocks)
--
fs/jfs/jfs_dmap.c-471- lblkno = BLKTODMAP(blkno, bmp->db_l2nbperpage);
fs/jfs/jfs_dmap.c:472: mp = read_metapage(ipbmap, lblkno, PSIZE, 0);
fs/jfs/jfs_dmap.c-473- if (mp == NULL) {
--
fs/jfs/jfs_dmap.c=525=dbUpdatePMap(struct inode *ipbmap,
--
fs/jfs/jfs_dmap.c-565-
fs/jfs/jfs_dmap.c:566: mp = read_metapage(bmp->db_ipbmap, lblkno, PSIZE,
fs/jfs/jfs_dmap.c-567- 0);
--
fs/jfs/jfs_dmap.c=801=int dbAlloc(struct inode *ip, s64 hint, s64 nblocks, s64 * results)
--
fs/jfs/jfs_dmap.c-882- lblkno = BLKTODMAP(blkno, bmp->db_l2nbperpage);
fs/jfs/jfs_dmap.c:883: mp = read_metapage(ipbmap, lblkno, PSIZE, 0);
fs/jfs/jfs_dmap.c-884- if (mp == NULL)
--
fs/jfs/jfs_dmap.c=1059=static int dbExtend(struct inode *ip, s64 blkno, s64 nblocks, s64 addnblocks)
--
fs/jfs/jfs_dmap.c-1112- lblkno = BLKTODMAP(extblkno, bmp->db_l2nbperpage);
fs/jfs/jfs_dmap.c:1113: mp = read_metapage(ipbmap, lblkno, PSIZE, 0);
fs/jfs/jfs_dmap.c-1114- if (mp == NULL) {
--
fs/jfs/jfs_dmap.c=1399=dbAllocAG(struct bmap * bmp, int agno, s64 nblocks, int l2nb, s64 * results)
--
fs/jfs/jfs_dmap.c-1455- lblkno = BLKTOCTL(blkno, bmp->db_l2nbperpage, bmp->db_aglevel);
fs/jfs/jfs_dmap.c:1456: mp = read_metapage(bmp->db_ipbmap, lblkno, PSIZE, 0);
fs/jfs/jfs_dmap.c-1457- if (mp == NULL)
--
fs/jfs/jfs_dmap.c=1768=static int dbFindCtl(struct bmap * bmp, int l2nb, int level, s64 * blkno)
--
fs/jfs/jfs_dmap.c-1785- lblkno = BLKTOCTL(b, bmp->db_l2nbperpage, lev);
fs/jfs/jfs_dmap.c:1786: mp = read_metapage(bmp->db_ipbmap, lblkno, PSIZE, 0);
fs/jfs/jfs_dmap.c-1787- if (mp == NULL)
--
fs/jfs/jfs_dmap.c=1887=dbAllocCtl(struct bmap * bmp, s64 nblocks, int l2nb, s64 blkno, s64 * results)
--
fs/jfs/jfs_dmap.c-1899- lblkno = BLKTODMAP(blkno, bmp->db_l2nbperpage);
fs/jfs/jfs_dmap.c:1900: mp = read_metapage(bmp->db_ipbmap, lblkno, PSIZE, 0);
fs/jfs/jfs_dmap.c-1901- if (mp == NULL)
--
fs/jfs/jfs_dmap.c-1931- lblkno = BLKTODMAP(b, bmp->db_l2nbperpage);
fs/jfs/jfs_dmap.c:1932: mp = read_metapage(bmp->db_ipbmap, lblkno, PSIZE, 0);
fs/jfs/jfs_dmap.c-1933- if (mp == NULL) {
--
fs/jfs/jfs_dmap.c-1984- lblkno = BLKTODMAP(b, bmp->db_l2nbperpage);
fs/jfs/jfs_dmap.c:1985: mp = read_metapage(bmp->db_ipbmap, lblkno, PSIZE, 0);
fs/jfs/jfs_dmap.c-1986- if (mp == NULL) {
--
fs/jfs/jfs_dmap.c=2557=dbAdjCtl(struct bmap * bmp, s64 blkno, int newval, int alloc, int level)
--
fs/jfs/jfs_dmap.c-2569- lblkno = BLKTOCTL(blkno, bmp->db_l2nbperpage, level);
fs/jfs/jfs_dmap.c:2570: mp = read_metapage(bmp->db_ipbmap, lblkno, PSIZE, 0);
fs/jfs/jfs_dmap.c-2571- if (mp == NULL)
--
fs/jfs/jfs_dmap.c=3278=int dbAllocBottomUp(struct inode *ip, s64 blkno, s64 nblocks)
--
fs/jfs/jfs_dmap.c-3303- lblkno = BLKTODMAP(blkno, bmp->db_l2nbperpage);
fs/jfs/jfs_dmap.c:3304: mp = read_metapage(ipbmap, lblkno, PSIZE, 0);
fs/jfs/jfs_dmap.c-3305- if (mp == NULL) {
--
fs/jfs/jfs_dmap.c=3454=int dbExtendFS(struct inode *ipbmap, s64 blkno, s64 nblocks)
--
fs/jfs/jfs_dmap.c-3537- p = BMAPBLKNO + nbperpage; /* L2 page */
fs/jfs/jfs_dmap.c:3538: l2mp = read_metapage(ipbmap, p, PSIZE, 0);
fs/jfs/jfs_dmap.c-3539- if (!l2mp) {
--
fs/jfs/jfs_dmap.c-3561- /* read in L1 page: (blkno & (MAXL1SIZE - 1)) */
fs/jfs/jfs_dmap.c:3562: l1mp = read_metapage(ipbmap, p, PSIZE, 0);
fs/jfs/jfs_dmap.c-3563- if (l1mp == NULL)
--
fs/jfs/jfs_dmap.c-3601-
fs/jfs/jfs_dmap.c:3602: l0mp = read_metapage(ipbmap, p, PSIZE, 0);
fs/jfs/jfs_dmap.c-3603- if (l0mp == NULL)
--
fs/jfs/jfs_dmap.c-3645- /* read in dmap page: */
fs/jfs/jfs_dmap.c:3646: mp = read_metapage(ipbmap, p,
fs/jfs/jfs_dmap.c-3647- PSIZE, 0);
--
fs/jfs/jfs_dmap.c-3652- /* assign/init dmap page */
fs/jfs/jfs_dmap.c:3653: mp = read_metapage(ipbmap, p,
fs/jfs/jfs_dmap.c-3654- PSIZE, 0);
--
fs/jfs/jfs_dtree.c=193=static struct metapage *read_index_page(struct inode *inode, s64 blkno)
--
fs/jfs/jfs_dtree.c-203-
fs/jfs/jfs_dtree.c:204: return read_metapage(inode, xaddr, PSIZE, 1);
fs/jfs/jfs_dtree.c-205-}
--
fs/jfs/jfs_dtree.c=319=static u32 add_index(tid_t tid, struct inode *ip, s64 bn, int slot)
--
fs/jfs/jfs_dtree.c-451- if (!mp) {
fs/jfs/jfs_dtree.c:452: jfs_err("add_index: get/read_metapage failed!");
fs/jfs/jfs_dtree.c-453- goto clean_up;
--
fs/jfs/jfs_imap.c=94=int diMount(struct inode *ipimap)
--
fs/jfs/jfs_imap.c-110-
fs/jfs/jfs_imap.c:111: mp = read_metapage(ipimap,
fs/jfs/jfs_imap.c-112- IMAPBLKNO << JFS_SBI(ipimap->i_sb)->l2nbperpage,
--
fs/jfs/jfs_imap.c=290=int diRead(struct inode *ip)
--
fs/jfs/jfs_imap.c-366- /* read the page of disk inode */
fs/jfs/jfs_imap.c:367: mp = read_metapage(ipimap, pageno << sbi->l2nbperpage, PSIZE, 1);
fs/jfs/jfs_imap.c-368- if (!mp) {
fs/jfs/jfs_imap.c:369: jfs_err("diRead: read_metapage failed");
fs/jfs/jfs_imap.c-370- return -EIO;
--
fs/jfs/jfs_imap.c=418=struct inode *diReadSpecial(struct super_block *sb, ino_t inum, int secondary)
--
fs/jfs/jfs_imap.c-446- /* read the page of fixed disk inode (AIT) in raw mode */
fs/jfs/jfs_imap.c:447: mp = read_metapage(ip, address << sbi->l2nbperpage, PSIZE, 1);
fs/jfs/jfs_imap.c-448- if (mp == NULL) {
--
fs/jfs/jfs_imap.c=500=void diWriteSpecial(struct inode *ip, int secondary)
--
fs/jfs/jfs_imap.c-517- /* read the page of fixed disk inode (AIT) in raw mode */
fs/jfs/jfs_imap.c:518: mp = read_metapage(ip, address << sbi->l2nbperpage, PSIZE, 1);
fs/jfs/jfs_imap.c-519- if (mp == NULL) {
--
fs/jfs/jfs_imap.c=581=int diWrite(tid_t tid, struct inode *ip)
--
fs/jfs/jfs_imap.c-638- retry:
fs/jfs/jfs_imap.c:639: mp = read_metapage(ipimap, pageno << sbi->l2nbperpage, PSIZE, 1);
fs/jfs/jfs_imap.c-640- if (!mp)
--
fs/jfs/jfs_imap.c=2663=static int diIAGRead(struct inomap * imap, int iagno, struct metapage ** mpp)
--
fs/jfs/jfs_imap.c-2671- /* read the iag. */
fs/jfs/jfs_imap.c:2672: *mpp = read_metapage(ipimap, blkno, PSIZE, 0);
fs/jfs/jfs_imap.c-2673- if (*mpp == NULL) {
--
fs/jfs/jfs_metapage.h=50=extern struct metapage *__get_metapage(struct inode *inode,
--
fs/jfs/jfs_metapage.h-53-
fs/jfs/jfs_metapage.h:54:#define read_metapage(inode, lblock, size, absolute)\
fs/jfs/jfs_metapage.h-55- __get_metapage(inode, lblock, size, absolute, false)
--
fs/jfs/xattr.c=351=static int ea_read(struct inode *ip, struct jfs_ea_list *ealist)
--
fs/jfs/xattr.c-395-
fs/jfs/xattr.c:396: if (!(mp = read_metapage(ip, blkno + i, bytes_to_read, 1)))
fs/jfs/xattr.c-397- return -EIO;
--
fs/jfs/xattr.c=426=static int ea_get(struct inode *inode, struct ea_buffer *ea_buf, int min_size)
--
fs/jfs/xattr.c-548- ea_buf->flag = EA_EXTENT;
fs/jfs/xattr.c:549: ea_buf->mp = read_metapage(inode, addressDXD(&ji->ea),
fs/jfs/xattr.c-550- lengthDXD(&ji->ea) << sb->s_blocksize_bits,
]
|
| 32/2 |
2026/06/30 14:36 |
llm |
debugger |
0m
Model:
Tokens:
input: 28660
output: 40
thoughts: 20
Thoughts: Here's my thought process, summarized as if I were considering this issue:
**Checking `dbMount`'s Validation of `dmt_leafidx`**
Okay, so I'm looking at this code and I need to make sure that the `dbMount` function is properly validating the `dmt_leafidx` value. This is critical. A flaw here could lead to some pretty nasty data corruption or, worse, security vulnerabilities. I need to understand if `dbMount` is doing the right checks to ensure that `dmt_leafidx` is within acceptable bounds, and that it's handling potential edge cases or malformed inputs correctly. I need to consider exactly what types of validation are *necessary* in this specific context. Is a simple range check enough? Or does it need something more sophisticated like a check against a lookup table or some other form of authorization? I need to think about potential attack vectors and if this validation step is robust enough to prevent them. Let's dig into the code and see exactly what's happening.
|
| 33/2 |
2026/06/30 14:36 |
tool |
read-file |
0m
Args: map[File:fs/jfs/jfs_dmap.c FirstLine:240 LineCount:50]
Results: map[Contents: 240: int dbMount(struct inode *ipbmap)
241: {
242: struct bmap *bmp;
243: struct dbmap_disk *dbmp_le;
244: struct metapage *mp;
245: int i, err;
246:
247: /*
248: * allocate/initialize the in-memory bmap descriptor
249: */
250: /* allocate memory for the in-memory bmap descriptor */
251: bmp = kmalloc_obj(struct bmap);
252: if (bmp == NULL)
253: return -ENOMEM;
254:
255: /* read the on-disk bmap descriptor. */
256: mp = read_metapage(ipbmap,
257: BMAPBLKNO << JFS_SBI(ipbmap->i_sb)->l2nbperpage,
258: PSIZE, 0);
259: if (mp == NULL) {
260: err = -EIO;
261: goto err_kfree_bmp;
262: }
263:
264: /* copy the on-disk bmap descriptor to its in-memory version. */
265: dbmp_le = (struct dbmap_disk *) mp->data;
266: bmp->db_mapsize = le64_to_cpu(dbmp_le->dn_mapsize);
267: bmp->db_nfree = le64_to_cpu(dbmp_le->dn_nfree);
268: bmp->db_l2nbperpage = le32_to_cpu(dbmp_le->dn_l2nbperpage);
269: bmp->db_numag = le32_to_cpu(dbmp_le->dn_numag);
270: bmp->db_maxlevel = le32_to_cpu(dbmp_le->dn_maxlevel);
271: bmp->db_maxag = le32_to_cpu(dbmp_le->dn_maxag);
272: bmp->db_agpref = le32_to_cpu(dbmp_le->dn_agpref);
273: bmp->db_aglevel = le32_to_cpu(dbmp_le->dn_aglevel);
274: bmp->db_agheight = le32_to_cpu(dbmp_le->dn_agheight);
275: bmp->db_agwidth = le32_to_cpu(dbmp_le->dn_agwidth);
276: bmp->db_agstart = le32_to_cpu(dbmp_le->dn_agstart);
277: bmp->db_agl2size = le32_to_cpu(dbmp_le->dn_agl2size);
278:
279: if ((bmp->db_l2nbperpage > L2PSIZE - L2MINBLOCKSIZE) ||
280: (bmp->db_l2nbperpage < 0) ||
281: !bmp->db_numag || (bmp->db_numag > MAXAG) ||
282: (bmp->db_maxag >= MAXAG) || (bmp->db_maxag < 0) ||
283: (bmp->db_agpref >= MAXAG) || (bmp->db_agpref < 0) ||
284: (bmp->db_agheight < 0) || (bmp->db_agheight > (L2LPERCTL >> 1)) ||
285: (bmp->db_agwidth < 1) || (bmp->db_agwidth > (LPERCTL / MAXAG)) ||
286: (bmp->db_agwidth > (1 << (L2LPERCTL - (bmp->db_agheight << 1)))) ||
287: (bmp->db_agstart < 0) ||
288: (bmp->db_agstart > (CTLTREESIZE - 1 - bmp->db_agwidth * (MAXAG - 1))) ||
289: (bmp->db_agl2size > L2MAXL2SIZE - L2MAXAG) ||
]
|
| 34/2 |
2026/06/30 14:36 |
llm |
debugger |
0m
Model:
Tokens:
input: 30083
output: 41
thoughts: 73
Thoughts: **Analyzing Database Memory Allocation**
Okay, so `dbMount` is only concerned with reading `dbmap_disk`. That's important to keep in mind. It's skipping over `dmap` and `dmapctl` – a detail that definitely merits further investigation. My question now is, where *does* `dmap` get read? I know it's crucial for the allocation process, so it has to be somewhere within the relevant code.
A quick mental scan suggests I'll find it in routines such as `dbFreeBits`, `dbFreeDmap`, or `dbAllocBits`. Let's target `dbFreeDmap` for a closer look since it's showing up in the current stack trace. That seems like the most logical first step to understanding the read operations performed on `dmap`.
|
| 35/2 |
2026/06/30 14:36 |
tool |
read-file |
0m
Args: map[File:fs/jfs/jfs_dmap.c FirstLine:2164 LineCount:50]
Results: map[Contents:2164: static int dbFreeDmap(struct bmap * bmp, struct dmap * dp, s64 blkno,
2165: int nblocks)
2166: {
2167: s8 oldroot;
2168: int rc = 0, word;
2169:
2170: /* save the current value of the root (i.e. maximum free string)
2171: * of the dmap tree.
2172: */
2173: oldroot = dp->tree.stree[ROOT];
2174:
2175: /* free the specified (blocks) bits */
2176: rc = dbFreeBits(bmp, dp, blkno, nblocks);
2177:
2178: /* if error or the root has not changed, done. */
2179: if (rc || (dp->tree.stree[ROOT] == oldroot))
2180: return (rc);
2181:
2182: /* root changed. bubble the change up to the dmap control pages.
2183: * if the adjustment of the upper level control pages fails,
2184: * backout the deallocation.
2185: */
2186: if ((rc = dbAdjCtl(bmp, blkno, dp->tree.stree[ROOT], 0, 0))) {
2187: word = (blkno & (BPERDMAP - 1)) >> L2DBWORD;
2188:
2189: /* as part of backing out the deallocation, we will have
2190: * to back split the dmap tree if the deallocation caused
2191: * the freed blocks to become part of a larger binary buddy
2192: * system.
2193: */
2194: if (dp->tree.stree[word] == NOFREE)
2195: dbBackSplit((dmtree_t *)&dp->tree, word, false);
2196:
2197: dbAllocBits(bmp, dp, blkno, nblocks);
2198: }
2199:
2200: return (rc);
2201: }
2202:
2203:
2204: /*
2205: * NAME: dbAllocBits()
2206: *
2207: * FUNCTION: allocate a specified block range from a dmap.
2208: *
2209: * this routine updates the dmap to reflect the working
2210: * state allocation of the specified block range. it directly
2211: * updates the bits of the working map and causes the adjustment
2212: * of the binary buddy system described by the dmap's dmtree
2213: * leaves to reflect the bits allocated. it also causes the
]
|
| 36/2 |
2026/06/30 14:36 |
llm |
debugger |
0m
Model:
Tokens:
input: 31153
output: 41
thoughts: 21
Thoughts: Here's my take on those thoughts, tailored for an expert audience:
**Investigating the Memory Deallocation Pathway**
Okay, so `dbFreeDmap` is directly invoked by `dbFree`. That's a good starting point. My immediate thought is, let's take a closer look at `dbFree` itself. We need to understand the control flow, the data structures it's manipulating, and how it interacts with the underlying memory management system, and of course, where it is called from. Understanding `dbFree` is key to understanding what `dbFreeDmap` does. I need to be sure that `dbFree` behaves as expected, before I consider that `dbFreeDmap` is doing something unexpectedly.
|
| 37/2 |
2026/06/30 14:36 |
tool |
read-file |
0m
Args: map[File:fs/jfs/jfs_dmap.c FirstLine:431 LineCount:100]
Results: map[Contents: 431: int dbFree(struct inode *ip, s64 blkno, s64 nblocks)
432: {
433: struct metapage *mp;
434: struct dmap *dp;
435: int nb, rc;
436: s64 lblkno, rem;
437: struct inode *ipbmap = JFS_SBI(ip->i_sb)->ipbmap;
438: struct bmap *bmp = JFS_SBI(ip->i_sb)->bmap;
439: struct super_block *sb = ipbmap->i_sb;
440:
441: IREAD_LOCK(ipbmap, RDWRLOCK_DMAP);
442:
443: /* block to be freed better be within the mapsize. */
444: if (unlikely((blkno == 0) || (blkno + nblocks > bmp->db_mapsize))) {
445: IREAD_UNLOCK(ipbmap);
446: printk(KERN_ERR "blkno = %Lx, nblocks = %Lx\n",
447: (unsigned long long) blkno,
448: (unsigned long long) nblocks);
449: jfs_error(ip->i_sb, "block to be freed is outside the map\n");
450: return -EIO;
451: }
452:
453: /**
454: * TRIM the blocks, when mounted with discard option
455: */
456: if (JFS_SBI(sb)->flag & JFS_DISCARD)
457: if (JFS_SBI(sb)->minblks_trim <= nblocks)
458: jfs_issue_discard(ipbmap, blkno, nblocks);
459:
460: /*
461: * free the blocks a dmap at a time.
462: */
463: mp = NULL;
464: for (rem = nblocks; rem > 0; rem -= nb, blkno += nb) {
465: /* release previous dmap if any */
466: if (mp) {
467: write_metapage(mp);
468: }
469:
470: /* get the buffer for the current dmap. */
471: lblkno = BLKTODMAP(blkno, bmp->db_l2nbperpage);
472: mp = read_metapage(ipbmap, lblkno, PSIZE, 0);
473: if (mp == NULL) {
474: IREAD_UNLOCK(ipbmap);
475: return -EIO;
476: }
477: dp = (struct dmap *) mp->data;
478:
479: /* determine the number of blocks to be freed from
480: * this dmap.
481: */
482: nb = min(rem, BPERDMAP - (blkno & (BPERDMAP - 1)));
483:
484: /* free the blocks. */
485: if ((rc = dbFreeDmap(bmp, dp, blkno, nb))) {
486: jfs_error(ip->i_sb, "error in block map\n");
487: release_metapage(mp);
488: IREAD_UNLOCK(ipbmap);
489: return (rc);
490: }
491: }
492:
493: /* write the last buffer. */
494: if (mp)
495: write_metapage(mp);
496:
497: IREAD_UNLOCK(ipbmap);
498:
499: return (0);
500: }
501:
502:
503: /*
504: * NAME: dbUpdatePMap()
505: *
506: * FUNCTION: update the allocation state (free or allocate) of the
507: * specified block range in the persistent block allocation map.
508: *
509: * the blocks will be updated in the persistent map one
510: * dmap at a time.
511: *
512: * PARAMETERS:
513: * ipbmap - pointer to in-core inode for the block map.
514: * free - 'true' if block range is to be freed from the persistent
515: * map; 'false' if it is to be allocated.
516: * blkno - starting block number of the range.
517: * nblocks - number of contiguous blocks in the range.
518: * tblk - transaction block;
519: *
520: * RETURN VALUES:
521: * 0 - success
522: * -EIO - i/o error
523: */
524: int
525: dbUpdatePMap(struct inode *ipbmap,
526: int free, s64 blkno, s64 nblocks, struct tblock * tblk)
527: {
528: int nblks, dbitno, wbitno, rbits;
529: int word, nbits, nwords;
530: struct bmap *bmp = JFS_SBI(ipbmap->i_sb)->bmap;
]
|
| 38/2 |
2026/06/30 14:36 |
llm |
debugger |
0m
Model:
Tokens:
input: 32938
output: 42
thoughts: 111
Thoughts: **Analyzing the dbFree Function and Potential Vulnerabilities**
Okay, so I'm looking at this `dbFree` function, and it seems like the first thing it does is read a metapage. That metapage contains a `struct dmap`, and then it calls `dbFreeDmap` on that structure. Inside the `dmap` is a `struct dmaptree tree` which is essentially the `t1` member of a `dmtree_t`. The code then pulls out `t1.leafidx`, which, from what I can see, is assigned the value of `dp->tree.leafidx`.
My immediate concern is: is `dp->tree.leafidx` being validated *anywhere* after it's read from the metapage? This is a critical point. If it's not, we could potentially have a vulnerability. A malicious actor could potentially manipulate the contents of the metapage, and thus, `dp->tree.leafidx`, leading to unintended consequences down the line.
Therefore, my next step is to carefully examine the `read_metapage` and `__get_metapage` functions. I need to understand exactly how the metapage is read, what checks are performed, and, most importantly, if any checks are done to validate this `leafidx` value before it gets used elsewhere. This is where I'll likely find the crux of the matter.
|
| 39/2 |
2026/06/30 14:36 |
tool |
read-file |
0m
Args: map[File:fs/jfs/jfs_metapage.c FirstLine:640 LineCount:100]
Results: map[Contents: 640: }
641:
642: #ifdef CONFIG_MIGRATION
643: /*
644: * metapage_migrate_folio - Migration function for JFS metapages
645: */
646: static int metapage_migrate_folio(struct address_space *mapping,
647: struct folio *dst, struct folio *src,
648: enum migrate_mode mode)
649: {
650: int expected_count;
651:
652: if (!src->private)
653: return filemap_migrate_folio(mapping, dst, src, mode);
654:
655: /* Check whether page does not have extra refs before we do more work */
656: expected_count = folio_expected_ref_count(src) + 1;
657: if (folio_ref_count(src) != expected_count)
658: return -EAGAIN;
659: return __metapage_migrate_folio(mapping, dst, src, mode);
660: }
661: #else
662: #define metapage_migrate_folio NULL
663: #endif /* CONFIG_MIGRATION */
664:
665: static void metapage_invalidate_folio(struct folio *folio, size_t offset,
666: size_t length)
667: {
668: BUG_ON(offset || length < folio_size(folio));
669:
670: BUG_ON(folio_test_writeback(folio));
671:
672: metapage_release_folio(folio, 0);
673: }
674:
675: const struct address_space_operations jfs_metapage_aops = {
676: .read_folio = metapage_read_folio,
677: .writepages = metapage_writepages,
678: .release_folio = metapage_release_folio,
679: .invalidate_folio = metapage_invalidate_folio,
680: .dirty_folio = filemap_dirty_folio,
681: .migrate_folio = metapage_migrate_folio,
682: };
683:
684: struct metapage *__get_metapage(struct inode *inode, unsigned long lblock,
685: unsigned int size, int absolute,
686: unsigned long new)
687: {
688: int l2BlocksPerPage;
689: int l2bsize;
690: struct address_space *mapping;
691: struct metapage *mp = NULL;
692: struct folio *folio;
693: unsigned long page_index;
694: unsigned long page_offset;
695:
696: jfs_info("__get_metapage: ino = %llu, lblock = 0x%lx, abs=%d",
697: inode->i_ino, lblock, absolute);
698:
699: l2bsize = inode->i_blkbits;
700: l2BlocksPerPage = PAGE_SHIFT - l2bsize;
701: page_index = lblock >> l2BlocksPerPage;
702: page_offset = (lblock - (page_index << l2BlocksPerPage)) << l2bsize;
703: if ((page_offset + size) > PAGE_SIZE) {
704: jfs_err("MetaData crosses page boundary!!");
705: jfs_err("lblock = %lx, size = %d", lblock, size);
706: dump_stack();
707: return NULL;
708: }
709: if (absolute)
710: mapping = JFS_SBI(inode->i_sb)->direct_inode->i_mapping;
711: else {
712: /*
713: * If an nfs client tries to read an inode that is larger
714: * than any existing inodes, we may try to read past the
715: * end of the inode map
716: */
717: if ((lblock << inode->i_blkbits) >= inode->i_size)
718: return NULL;
719: mapping = inode->i_mapping;
720: }
721:
722: if (new && (PSIZE == PAGE_SIZE)) {
723: folio = filemap_grab_folio(mapping, page_index);
724: if (IS_ERR(folio)) {
725: jfs_err("filemap_grab_folio failed!");
726: return NULL;
727: }
728: folio_mark_uptodate(folio);
729: } else {
730: folio = read_mapping_folio(mapping, page_index, NULL);
731: if (IS_ERR(folio)) {
732: jfs_err("read_mapping_page failed!");
733: return NULL;
734: }
735: folio_lock(folio);
736: }
737:
738: mp = folio_to_mp(folio, page_offset);
739: if (mp) {
]
|
| 40/2 |
2026/06/30 14:36 |
llm |
debugger |
0m
Model:
Tokens:
input: 34906
output: 31
thoughts: 318
Thoughts: **Vulnerability Deep Dive: Unvalidated Disk Reads**
Okay, so `__get_metapage` is clearly the culprit here. It's just blindly slurping the raw page content off disk, no checks whatsoever on `dmap` or `dmapctl` data. That's a huge red flag. Specifically, I'm zeroing in on `dp->tree.leafidx` – it's being read directly from disk and then *immediately* used without a sanity check. That's asking for trouble.
Looking at `dbJoin`, this is where things get interesting and where the rubber hits the road:
```c
/* can the new leaf value require a join with other leaves ?
*/
if (newval >= tp->dmt_budmin) {
/* pickup a pointer to the leaves of the tree.
*/
leaf = tp->dmt_stree + le32_to_cpu(tp->dmt_leafidx);
```
`tp->dmt_leafidx` is the problem child, because it's sourced directly from `dp->tree.leafidx`. And if *that* is corrupted on disk, then `leaf` is going to point into la-la land, out of bounds of `tp->dmt_stree`. This is a classic buffer overflow scenario.
I need to nail down the expected boundaries, here. `dmt_leafidx` is, as I recall, the index of the *first* leaf within `stree`. For `dmap`, `stree` has a size of `TREESIZE` (341), and the first leaf starts at `LEAFIND` (85). For `dmapctl`, `stree` is `CTLTREESIZE` (1365), with the first leaf at `CTLLEAFIND` (341). Alright, let's just make sure I have those values of `LEAFIND` and `CTLLEAFIND` correct. Then, let's see how we can exploit this to write some nasty values, and cause crashes, or even worse...
|
| 41/2 |
2026/06/30 14:36 |
tool |
grepper |
0m
Args: map[Expression:#define.*LEAFIND PathPrefix:fs/jfs/]
Results: map[Output:fs/jfs/jfs_dmap.h-11-#define TREESIZE (256+64+16+4+1) /* size of a dmap tree */
fs/jfs/jfs_dmap.h:12:#define LEAFIND (64+16+4+1) /* index of 1st leaf of a dmap tree */
fs/jfs/jfs_dmap.h-13-#define LPERDMAP 256 /* num leaves per dmap tree */
--
fs/jfs/jfs_dmap.h-20-#define CTLTREESIZE (1024+256+64+16+4+1) /* size of a dmapctl tree */
fs/jfs/jfs_dmap.h:21:#define CTLLEAFIND (256+64+16+4+1) /* idx of 1st leaf of a dmapctl tree */
fs/jfs/jfs_dmap.h-22-#define LPERCTL 1024 /* num of leaves per dmapctl tree */
]
|
| 42/2 |
2026/06/30 14:36 |
llm |
debugger |
0m
Model:
Tokens:
input: 36001
output: 42
thoughts: 576
Thoughts: **Analyzing the dmtree Indexing Logic**
Okay, so I'm looking at how the `dmtree` structure works, specifically how it manages free space. I see that `LEAFIND` is defined as 85 and `CTLLEAFIND` as 341. The critical part is the value of `dmt_leafidx`, which can be anything, as it's read from disk. The crucial question is, how robust is the system in validating this value when it's read and used?
I'm digging into `dbAllocBottomUp` which seems to load the `dmap` and `dmapctl` structures. I'm focusing on the call to `read_metapage` in this function. And I can't find any validation code here: it just reads the data into the `dp` pointer, assuming the data on the disk is valid. That's a potential vulnerability.
Now, I remember `dmt_leafidx` gets used in `dbFindLeaf`. Let's see: I found the function, and it uses `dmt_leafidx` in a loop condition and a check. Specifically, there's a conditional `if (le32_to_cpu(tp->dmt_leafidx) >= max_idx) return -ENOSPC;`. The use of `max_idx` is tied to `is_ctl`, setting `max_idx` equal to either `CTLLEAFIND` or `LEAFIND`. However, I can see that the check is present, so there is *some* validation, even if it's potentially limited.
But, hang on... `dbFindLeaf` is only called during allocation. What about during *freeing* space? The `dbFreeDmap` function calls `dbFreeBits`, which in turn calls `dbJoin` and `dbAdjTree`. So it's looking like `dmt_leafidx` is not used in the freeing of space, so there's less validation than I initially suspected. I'm moving on to `dbFreeBits` to see what is going on there.
|
| 43/2 |
2026/06/30 14:36 |
tool |
read-file |
0m
Args: map[File:fs/jfs/jfs_dmap.c FirstLine:2373 LineCount:100]
Results: map[Contents:2373: static int dbFreeBits(struct bmap * bmp, struct dmap * dp, s64 blkno,
2374: int nblocks)
2375: {
2376: int dbitno, word, rembits, nb, nwords, wbitno, nw, agno;
2377: dmtree_t *tp = (dmtree_t *) & dp->tree;
2378: int rc = 0;
2379: int size;
2380:
2381: /* determine the bit number and word within the dmap of the
2382: * starting block.
2383: */
2384: dbitno = blkno & (BPERDMAP - 1);
2385: word = dbitno >> L2DBWORD;
2386:
2387: /* block range better be within the dmap.
2388: */
2389: assert(dbitno + nblocks <= BPERDMAP);
2390:
2391: /* free the bits of the dmaps words corresponding to the block range.
2392: * not all bits of the first and last words may be contained within
2393: * the block range. if this is the case, we'll work against those
2394: * words (i.e. partial first and/or last) on an individual basis
2395: * (a single pass), freeing the bits of interest by hand and updating
2396: * the leaf corresponding to the dmap word. a single pass will be used
2397: * for all dmap words fully contained within the specified range.
2398: * within this pass, the bits of all fully contained dmap words will
2399: * be marked as free in a single shot and the leaves will be updated. a
2400: * single leaf may describe the free space of multiple dmap words,
2401: * so we may update only a subset of the actual leaves corresponding
2402: * to the dmap words of the block range.
2403: *
2404: * dbJoin() is used to update leaf values and will join the binary
2405: * buddy system of the leaves if the new leaf values indicate this
2406: * should be done.
2407: */
2408: for (rembits = nblocks; rembits > 0; rembits -= nb, dbitno += nb) {
2409: /* determine the bit number within the word and
2410: * the number of bits within the word.
2411: */
2412: wbitno = dbitno & (DBWORD - 1);
2413: nb = min(rembits, DBWORD - wbitno);
2414:
2415: /* check if only part of a word is to be freed.
2416: */
2417: if (nb < DBWORD) {
2418: /* free (zero) the appropriate bits within this
2419: * dmap word.
2420: */
2421: dp->wmap[word] &=
2422: cpu_to_le32(~(ONES << (DBWORD - nb)
2423: >> wbitno));
2424:
2425: /* update the leaf for this dmap word.
2426: */
2427: rc = dbJoin(tp, word,
2428: dbMaxBud((u8 *)&dp->wmap[word]), false);
2429: if (rc)
2430: return rc;
2431:
2432: word += 1;
2433: } else {
2434: /* one or more dmap words are fully contained
2435: * within the block range. determine how many
2436: * words and free (zero) the bits of these words.
2437: */
2438: nwords = rembits >> L2DBWORD;
2439: memset(&dp->wmap[word], 0, nwords * 4);
2440:
2441: /* determine how many bits.
2442: */
2443: nb = nwords << L2DBWORD;
2444:
2445: /* now update the appropriate leaves to reflect
2446: * the freed words.
2447: */
2448: for (; nwords > 0; nwords -= nw) {
2449: /* determine what the leaf value should be
2450: * updated to as the minimum of the l2 number
2451: * of bits being freed and the l2 (max) number
2452: * of bits that can be described by this leaf.
2453: */
2454: size =
2455: min(LITOL2BSZ
2456: (word, L2LPERDMAP, BUDMIN),
2457: NLSTOL2BSZ(nwords));
2458:
2459: /* update the leaf.
2460: */
2461: rc = dbJoin(tp, word, size, false);
2462: if (rc)
2463: return rc;
2464:
2465: /* get the number of dmap words handled.
2466: */
2467: nw = BUDSIZE(size, BUDMIN);
2468: word += nw;
2469: }
2470: }
2471: }
2472:
]
|
| 44/2 |
2026/06/30 14:36 |
llm |
debugger |
0m
Model:
Tokens:
input: 38593
output: 29
thoughts: 430
Thoughts: **Vulnerability Discovery: Out-of-Bounds Access in `dbJoin`**
Okay, so I've been digging into this `dbFreeBits` function, and it's calling `dbJoin` with `tp`, which is essentially a pointer to `dp->tree`. `dp` is this `struct dmap` that's loaded from disk. The problem is, and this is a glaring omission, there's no check on `dp->tree.leafidx` *before* the `dbJoin` call. This is a potential disaster waiting to happen.
Looking at the `dbJoin` code itself, it seems like the vulnerable spot is this:
```c
/* pickup a pointer to the leaves of the tree.
*/
leaf = tp->dmt_stree + le32_to_cpu(tp->dmt_leafidx);
```
If `tp->dmt_leafidx` is corrupted – say, because the data on disk is bad – then `leaf` is going to point way out of bounds. Further down, `leaf[buddy]` gets accessed, and that's the trigger for the out-of-bounds read. This happens when the new leaf value is less than the existing leaf value. It seems a simple comparison is enough to trigger the issue.
My first thought is, the fix is straightforward: validate `dmt_leafidx` whenever a `dmap` or `dmapctl` struct is read from disk. Or we could put the validation right inside `dbJoin`, `dbSplit`, `dbBackSplit`, `dbAdjTree`, and any other place where the value is used.
But wait a minute... Let me rethink this... `dmt_leafidx` is actually a *constant* for both `dmap` and `dmapctl`. For `dmap`, it's always `LEAFIND` (85), and for `dmapctl`, it's `CTLLEAFIND` (341). Now, this is interesting. I'll need to check how `dmap` and `dmapctl` are initialized to confirm my suspicions and to figure out if it is possible for the value to be corrupted or tampered with. It seems the vulnerability lies in either corruption of the `leafidx` constant, or in another, related structure or variable in the vicinity. I'll need to look at the initialization code.
|
| 45/2 |
2026/06/30 14:36 |
tool |
grepper |
0m
Args: map[Expression:leafidx.*= PathPrefix:fs/jfs/]
Results: map[Output:fs/jfs/jfs_dmap.c=142=static bool check_dmapctl(struct dmapctl *dcp)
--
fs/jfs/jfs_dmap.c-169-
fs/jfs/jfs_dmap.c:170: leafidx = le32_to_cpu(dcp->leafidx);
fs/jfs/jfs_dmap.c-171- /* Check leaf index matches expected position */
fs/jfs/jfs_dmap.c:172: if (unlikely(leafidx != CTLLEAFIND)) {
fs/jfs/jfs_dmap.c-173- jfs_err("dmapctl: invalid leafidx %u (expected %u)",
--
fs/jfs/jfs_dmap.c=1158=static int dbAllocNext(struct bmap * bmp, struct dmap * dp, s64 blkno,
--
fs/jfs/jfs_dmap.c-1165-
fs/jfs/jfs_dmap.c:1166: if (dp->tree.leafidx != cpu_to_le32(LEAFIND)) {
fs/jfs/jfs_dmap.c-1167- jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmap page\n");
--
fs/jfs/jfs_dmap.c=1290=dbAllocNear(struct bmap * bmp,
--
fs/jfs/jfs_dmap.c-1295-
fs/jfs/jfs_dmap.c:1296: if (dp->tree.leafidx != cpu_to_le32(LEAFIND)) {
fs/jfs/jfs_dmap.c-1297- jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmap page\n");
--
fs/jfs/jfs_dmap.c=3034=static int dbFindLeaf(dmtree_t *tp, int l2nb, int *leafidx, bool is_ctl)
--
fs/jfs/jfs_dmap.c-3071- }
fs/jfs/jfs_dmap.c:3072: if (le32_to_cpu(tp->dmt_leafidx) >= max_idx)
fs/jfs/jfs_dmap.c-3073- return -ENOSPC;
--
fs/jfs/jfs_dmap.c-3077- */
fs/jfs/jfs_dmap.c:3078: *leafidx = x + n - le32_to_cpu(tp->dmt_leafidx);
fs/jfs/jfs_dmap.c-3079-
--
fs/jfs/jfs_dmap.c=3940=static int dbInitDmapTree(struct dmap * dp)
--
fs/jfs/jfs_dmap.c-3949- tp->l2nleafs = cpu_to_le32(L2LPERDMAP);
fs/jfs/jfs_dmap.c:3950: tp->leafidx = cpu_to_le32(LEAFIND);
fs/jfs/jfs_dmap.c-3951- tp->height = cpu_to_le32(4);
--
fs/jfs/jfs_dmap.c=4061=static int dbInitDmapCtl(struct dmapctl * dcp, int level, int i)
--
fs/jfs/jfs_dmap.c-4066- dcp->l2nleafs = cpu_to_le32(L2LPERCTL);
fs/jfs/jfs_dmap.c:4067: dcp->leafidx = cpu_to_le32(CTLLEAFIND);
fs/jfs/jfs_dmap.c-4068- dcp->height = cpu_to_le32(5);
]
|
| 46/2 |
2026/06/30 14:36 |
llm |
debugger |
0m
Model:
Tokens:
input: 40604
output: 30
thoughts: 27
Thoughts: Here's my thought process, summarized as you requested:
**A Moment of Realization and Investigation**
Wait a minute, I seem to recall a utility called `check_dmapctl` existing! That's a potentially important piece of the puzzle. Now I need to see where this is being called. My immediate next step is to trace its usage within the system. Understanding the context in which `check_dmapctl` is used will give me the information to understand its importance in the wider system.
|
| 47/2 |
2026/06/30 14:36 |
tool |
grepper |
0m
Args: map[Expression:check_dmapctl PathPrefix:fs/jfs/]
Results: map[Output:fs/jfs/jfs_dmap.c=117=static const s8 budtab[256] = {
--
fs/jfs/jfs_dmap.c-136-/*
fs/jfs/jfs_dmap.c:137: * check_dmapctl - Validate integrity of a dmapctl structure
fs/jfs/jfs_dmap.c-138- * @dcp: Pointer to the dmapctl structure to check
--
fs/jfs/jfs_dmap.c-141- */
fs/jfs/jfs_dmap.c:142:static bool check_dmapctl(struct dmapctl *dcp)
fs/jfs/jfs_dmap.c-143-{
--
fs/jfs/jfs_dmap.c=1399=dbAllocAG(struct bmap * bmp, int agno, s64 nblocks, int l2nb, s64 * results)
--
fs/jfs/jfs_dmap.c-1461-
fs/jfs/jfs_dmap.c:1462: if (unlikely(!check_dmapctl(dcp))) {
fs/jfs/jfs_dmap.c-1463- jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmapctl page\n");
--
fs/jfs/jfs_dmap.c=1768=static int dbFindCtl(struct bmap * bmp, int l2nb, int level, s64 * blkno)
--
fs/jfs/jfs_dmap.c-1791-
fs/jfs/jfs_dmap.c:1792: if (unlikely(!check_dmapctl(dcp))) {
fs/jfs/jfs_dmap.c-1793- jfs_error(bmp->db_ipbmap->i_sb,
--
fs/jfs/jfs_dmap.c=2557=dbAdjCtl(struct bmap * bmp, s64 blkno, int newval, int alloc, int level)
--
fs/jfs/jfs_dmap.c-2574-
fs/jfs/jfs_dmap.c:2575: if (unlikely(!check_dmapctl(dcp))) {
fs/jfs/jfs_dmap.c-2576- jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmapctl page\n");
--
fs/jfs/jfs_dmap.c=3454=int dbExtendFS(struct inode *ipbmap, s64 blkno, s64 nblocks)
--
fs/jfs/jfs_dmap.c-3543- l2dcp = (struct dmapctl *) l2mp->data;
fs/jfs/jfs_dmap.c:3544: if (unlikely(!check_dmapctl(l2dcp))) {
fs/jfs/jfs_dmap.c-3545- jfs_error(ipbmap->i_sb, "Corrupt dmapctl page\n");
--
fs/jfs/jfs_dmap.c-3565- l1dcp = (struct dmapctl *) l1mp->data;
fs/jfs/jfs_dmap.c:3566: if (unlikely(!check_dmapctl(l1dcp))) {
fs/jfs/jfs_dmap.c-3567- jfs_error(ipbmap->i_sb, "Corrupt dmapctl page\n");
--
fs/jfs/jfs_dmap.c-3582- l1dcp = (struct dmapctl *) l1mp->data;
fs/jfs/jfs_dmap.c:3583: if (unlikely(!check_dmapctl(l1dcp))) {
fs/jfs/jfs_dmap.c-3584- jfs_error(ipbmap->i_sb, "Corrupt dmapctl page\n");
--
fs/jfs/jfs_dmap.c-3605- l0dcp = (struct dmapctl *) l0mp->data;
fs/jfs/jfs_dmap.c:3606: if (unlikely(!check_dmapctl(l0dcp))) {
fs/jfs/jfs_dmap.c-3607- jfs_error(ipbmap->i_sb, "Corrupt dmapctl page\n");
--
fs/jfs/jfs_dmap.c-3624- l0dcp = (struct dmapctl *) l0mp->data;
fs/jfs/jfs_dmap.c:3625: if (unlikely(!check_dmapctl(l0dcp))) {
fs/jfs/jfs_dmap.c-3626- jfs_error(ipbmap->i_sb, "Corrupt dmapctl page\n");
]
|
| 48/2 |
2026/06/30 14:36 |
llm |
debugger |
0m
Model:
Tokens:
input: 42062
output: 41
thoughts: 91
Thoughts: Okay, here's my take on those thoughts, framed as an expert's internal monologue:
**Investigating a Discrepancy in the Data Mapper Code**
Hmm, so `check_dmapctl` is implemented, but I'm not seeing a corresponding `check_dmap` function. That's a little odd. Let's dig in. I see that `dbAllocNext` and `dbAllocNear` both perform a check for `dp->tree.leafidx != cpu_to_le32(LEAFIND)`. Okay, that makes sense – they're validating something about the leaf index. However, `dbFree` and `dbFreeBits` don't seem to have that same check. That's a potential point of divergence I need to understand. I should take a closer look at what those allocation routines are actually doing. Let's focus on `dbAllocNext` and `dbAllocNear` and see what's going on. I need to understand why the deallocation path might not require the same validation or if this oversight is a bug.
|
| 49/2 |
2026/06/30 14:36 |
tool |
read-file |
0m
Args: map[File:fs/jfs/jfs_dmap.c FirstLine:1158 LineCount:20]
Results: map[Contents:1158: static int dbAllocNext(struct bmap * bmp, struct dmap * dp, s64 blkno,
1159: int nblocks)
1160: {
1161: int dbitno, word, rembits, nb, nwords, wbitno, nw;
1162: int l2size;
1163: s8 *leaf;
1164: u32 mask;
1165:
1166: if (dp->tree.leafidx != cpu_to_le32(LEAFIND)) {
1167: jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmap page\n");
1168: return -EIO;
1169: }
1170:
1171: /* pick up a pointer to the leaves of the dmap tree.
1172: */
1173: leaf = dp->tree.stree + le32_to_cpu(dp->tree.leafidx);
1174:
1175: /* determine the bit number and word within the dmap of the
1176: * starting block.
1177: */
]
|
| 50/2 |
2026/06/30 14:36 |
llm |
debugger |
0m
Model:
Tokens:
input: 42757
output: 40
thoughts: 96
Thoughts: Okay, let me think this through.
**Analyzing Data Structure Consistency**
Alright, I'm tracing some operations related to database allocation and deallocation. I see that `dbAllocNext` and `dbAllocNear` are properly checking `dp->tree.leafidx`. That's good, ensuring they're operating within the expected boundaries of the leaf index. However, `dbFree` is where things are a little hazy. I see `dbFree` reads `dp` and then calls `dbFreeDmap`. `dbFreeDmap` then cascades further by calling `dbFreeBits`, which eventually calls `dbJoin`. The issue I see is that **none** of these called functions – `dbFreeDmap`, `dbFreeBits`, or `dbJoin` – are explicitly checking `dp->tree.leafidx` again. That seems like a potential inconsistency or vulnerability. Now that I think about it again, `dbFree` reads `dp` and calls `dbFreeDmap` - which is a critical point. I really need to revisit `dbFree` itself to make sure I am fully understanding its role in all of this.
|
| 51/2 |
2026/06/30 14:36 |
tool |
read-file |
0m
Args: map[File:fs/jfs/jfs_dmap.c FirstLine:464 LineCount:30]
Results: map[Contents: 464: for (rem = nblocks; rem > 0; rem -= nb, blkno += nb) {
465: /* release previous dmap if any */
466: if (mp) {
467: write_metapage(mp);
468: }
469:
470: /* get the buffer for the current dmap. */
471: lblkno = BLKTODMAP(blkno, bmp->db_l2nbperpage);
472: mp = read_metapage(ipbmap, lblkno, PSIZE, 0);
473: if (mp == NULL) {
474: IREAD_UNLOCK(ipbmap);
475: return -EIO;
476: }
477: dp = (struct dmap *) mp->data;
478:
479: /* determine the number of blocks to be freed from
480: * this dmap.
481: */
482: nb = min(rem, BPERDMAP - (blkno & (BPERDMAP - 1)));
483:
484: /* free the blocks. */
485: if ((rc = dbFreeDmap(bmp, dp, blkno, nb))) {
486: jfs_error(ip->i_sb, "error in block map\n");
487: release_metapage(mp);
488: IREAD_UNLOCK(ipbmap);
489: return (rc);
490: }
491: }
492:
493: /* write the last buffer. */
]
|
| 52/2 |
2026/06/30 14:36 |
llm |
debugger |
0m
Model:
Tokens:
input: 43605
output: 41
thoughts: 180
Thoughts: **Analyzing Potential Error Points in the Database Operations**
Okay, so I'm thinking about where we could insert a check to catch potential issues, specifically related to the `dmt_leafidx` value. My initial instinct was to look at places where we're freeing resources – `dbFreeDmap`, `dbFreeBits`, `dbFree`, or `dbJoin`. But then I realized that several functions manipulate this `tp->dmt_leafidx` value: `dbJoin`, `dbSplit`, `dbBackSplit`, `dbAdjTree`, and `dbFindLeaf`. That's a lot of places to keep track of.
Hang on...`dbJoin` takes a `dmtree_t *tp` and a `bool is_ctl` as parameters. Right, that's the key. If `is_ctl` is true, then this is operating on a `dmapctl`, and the `tp->dmt_leafidx` should, by design, be `CTLLEAFIND`, which is 341. Conversely, if `is_ctl` is false, it's a regular `dmap`, and we'd expect `tp->dmt_leafidx` to be `LEAFIND`, which is 85.
I need to dive into the implementation of `dbJoin` and see how this value is being used and modified, to pinpoint the best place to add a sanity check. Let me examine `dbJoin` more closely.
|
| 53/2 |
2026/06/30 14:36 |
tool |
read-file |
0m
Args: map[File:fs/jfs/jfs_dmap.c FirstLine:2857 LineCount:20]
Results: map[Contents:2857: static int dbJoin(dmtree_t *tp, int leafno, int newval, bool is_ctl)
2858: {
2859: int budsz, buddy;
2860: s8 *leaf;
2861:
2862: /* can the new leaf value require a join with other leaves ?
2863: */
2864: if (newval >= tp->dmt_budmin) {
2865: /* pickup a pointer to the leaves of the tree.
2866: */
2867: leaf = tp->dmt_stree + le32_to_cpu(tp->dmt_leafidx);
2868:
2869: /* try to join the specified leaf into a large binary
2870: * buddy system. the join proceeds by attempting to join
2871: * the specified leafno with its buddy (leaf) at new value.
2872: * if the join occurs, we attempt to join the left leaf
2873: * of the joined buddies with its buddy at new value + 1.
2874: * we continue to join until we find a buddy that cannot be
2875: * joined (does not have a value equal to the size of the
2876: * last join) or until all leaves have been joined into a
]
|
| 54/2 |
2026/06/30 14:36 |
llm |
debugger |
0m
Model:
Tokens:
input: 44504
output: 36
thoughts: 1182
Thoughts: **Analyzing `dmap` Validation and Location**
Okay, so I'm looking at where we can best validate the `dmap` structure. My initial thought was to add a check in `dbJoin`, right before it accesses `tp->dmt_leafidx`. But, the more I look, the more I realize that `tp->dmt_leafidx` is used in multiple functions: `dbSplit`, `dbBackSplit`, and `dbAdjTree` as well. That means validating there might not be sufficient.
The best place to validate `dmap` is when it is read from disk, or when freeing/allocating bits.
I recall that `check_dmapctl` is called when `dmapctl` is read. Why don't we do something similar, and create a `check_dmap` function and call it when `dmap` is read? Let's check where `dmap` is actually read from disk. Seems like it's read by `read_metapage` in many functions: `dbFree`, `dbUpdatePMap`, `dbAllocBottomUp`, `dbExtendFS`, `dbAlloc`, `dbExtend`, and `dbAllocCtl`.
It seems we can add `check_dmap` and call it in these functions.
Wait a second. I just remembered that `check_dmapctl` is also called from `dbAllocAG`, `dbFindCtl`, `dbAdjCtl`, and `dbExtendFS`. So it's not actually called from within `read_metapage`.
An alternative might be to just check `dp->tree.leafidx == cpu_to_le32(LEAFIND)` in functions like `dbFreeDmap`, `dbAllocBits`, and `dbFreeBits`. Actually, `dbAllocNext` and `dbAllocNear` *already* check `dp->tree.leafidx != cpu_to_le32(LEAFIND)`.
|
| 55/2 |
2026/06/30 14:36 |
tool |
git-log |
10m
Error:
Args: map[CodeRegexp:dp->tree.leafidx != cpu_to_le32\(LEAFIND\)]
Results:
|
| 56/2 |
2026/06/30 14:46 |
llm |
debugger |
0m
Model:
Tokens:
input: 46180
output: 47
thoughts: 12
Thoughts: Okay, here's my attempt at summarizing the thought process, tailored for an expert audience, and written in the first person:
**Focusing on Filesystem Implementations**
Alright, I need to narrow my scope. Instead of a general discussion, I should restrict my focus. The current task requires a deep dive into filesystem implementation. I should concentrate on the specific aspects of `fs/jfs/`, which represents a specific filesystem implementation. This immediately hones my thinking, allowing me to draw on my existing knowledge base and avoid getting bogged down in extraneous details. Focusing on `fs/jfs/` allows for a more concise and direct analysis.
|
| 57/2 |
2026/06/30 14:46 |
tool |
git-log |
0m
Args: map[CodeRegexp:dp->tree.leafidx != cpu_to_le32\(LEAFIND\) PathPrefix:fs/jfs/]
Results: map[Output:1da177e4c3f4 Linux-2.6.12-rc2
]
|
| 58/2 |
2026/06/30 14:46 |
llm |
debugger |
0m
Model:
Tokens:
input: 46410
output: 33
thoughts: 21
Thoughts: Okay, here's my interpretation of those thoughts, tailored for an expert audience, and delivered as a first-person summary:
**The Persistent Challenge**
It's always been lurking in the background, hasn't it? This particular issue – this core problem, it's been a thorn in my side from the start. I need to get back to basics. Let's see if `check_dmapctl` can shed some light on the current state and give me a clue as to the next step. I'll need to run this and parse the output, probably look for inconsistencies.
|
| 59/2 |
2026/06/30 14:46 |
tool |
git-log |
0m
Args: map[CodeRegexp:check_dmapctl PathPrefix:fs/jfs/]
Results: map[Output:cce219b203c4 jfs: add dmapctl integrity check to prevent invalid operations
]
|
| 60/2 |
2026/06/30 14:46 |
llm |
debugger |
0m
Model:
Tokens:
input: 46628
output: 26
thoughts: 32
Thoughts: Okay, here's my interpretation of those thoughts, crafted with the understanding that the user is an expert.
**Initial Investigation into a Recent Addition**
Right, `check_dmapctl`... that's a new one. I seem to recall that popping up in the recent commits. Let's dig into that `cce219b203c4` commit, that should tell the whole story. What specifically did they change or add regarding the dmap control? What problems was the author trying to solve? Or was this just a refactor or addition of functionality? Understanding the context will be important, because while I understand what the function does generally, it's the *why* that's important here. I want to see how this integrates with the existing system. The commit message better be clear and concise!
|
| 61/2 |
2026/06/30 14:46 |
tool |
git-show |
0m
Args:
Results: map[Output:commit cce219b203c4b9cb445e910c7090d1f58af847c5
Author: Yun Zhou <yun.zhou@windriver.com>
Date: Fri Nov 28 23:51:50 2025 +0800
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>
diff --git a/fs/jfs/jfs_dmap.c b/fs/jfs/jfs_dmap.c
index 2abe8cc02ee6..a841cf21da7d 100644
--- a/fs/jfs/jfs_dmap.c
+++ b/fs/jfs/jfs_dmap.c
@@ -133,6 +133,93 @@ static const s8 budtab[256] = {
2, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -1
};
+/*
+ * check_dmapctl - Validate integrity of a dmapctl structure
+ * @dcp: Pointer to the dmapctl structure to check
+ *
+ * Return: true if valid, false if corrupted
+ */
+static bool check_dmapctl(struct dmapctl *dcp)
+{
+ s8 budmin = dcp->budmin;
+ u32 nleafs, l2nleafs, leafidx, height;
+ int i;
+
+ nleafs = le32_to_cpu(dcp->nleafs);
+ /* Check basic field ranges */
+ if (unlikely(nleafs > LPERCTL)) {
+ jfs_err("dmapctl: invalid nleafs %u (max %u)",
+ nleafs, LPERCTL);
+ return false;
+ }
+
+ l2nleafs = le32_to_cpu(dcp->l2nleafs);
+ if (unlikely(l2nleafs > L2LPERCTL)) {
+ jfs_err("dmapctl: invalid l2nleafs %u (max %u)",
+ l2nleafs, L2LPERCTL);
+ return false;
+ }
+
+ /* Verify nleafs matches l2nleafs (must be power of two) */
+ if (unlikely((1U << l2nleafs) != nleafs)) {
+ jfs_err("dmapctl: nleafs %u != 2^%u",
+ nleafs, l2nleafs);
+ return false;
+ }
+
+ leafidx = le32_to_cpu(dcp->leafidx);
+ /* Check leaf index matches expected position */
+ if (unlikely(leafidx != CTLLEAFIND)) {
+ jfs_err("dmapctl: invalid leafidx %u (expected %u)",
+ leafidx, CTLLEAFIND);
+ return false;
+ }
+
+ height = le32_to_cpu(dcp->height);
+ /* Check tree height is within valid range */
+ if (unlikely(height > (L2LPERCTL >> 1))) {
+ jfs_err("dmapctl: invalid height %u (max %u)",
+ height, L2LPERCTL >> 1);
+ return false;
+ }
+
+ /* Check budmin is valid (cannot be NOFREE for non-empty tree) */
+ if (budmin == NOFREE) {
+ if (unlikely(nleafs > 0)) {
+ jfs_err("dmapctl: budmin is NOFREE but nleafs %u",
+ nleafs);
+ return false;
+ }
+ } else if (unlikely(budmin < BUDMIN)) {
+ jfs_err("dmapctl: invalid budmin %d (min %d)",
+ budmin, BUDMIN);
+ return false;
+ }
+
+ /* Check leaf nodes fit within stree array */
+ if (unlikely(leafidx + nleafs > CTLTREESIZE)) {
+ jfs_err("dmapctl: leaf range exceeds stree size (end %u > %u)",
+ leafidx + nleafs, CTLTREESIZE);
+ return false;
+ }
+
+ /* Check leaf nodes have valid values */
+ for (i = leafidx; i < leafidx + nleafs; i++) {
+ s8 val = dcp->stree[i];
+
+ if (unlikely(val < NOFREE)) {
+ jfs_err("dmapctl: invalid leaf value %d at index %d",
+ val, i);
+ return false;
+ } else if (unlikely(val > 31)) {
+ jfs_err("dmapctl: leaf value %d too large at index %d", val, i);
+ return false;
+ }
+ }
+
+ return true;
+}
+
/*
* NAME: dbMount()
*
@@ -1372,7 +1459,7 @@ dbAllocAG(struct bmap * bmp, int agno, s64 nblocks, int l2nb, s64 * results)
dcp = (struct dmapctl *) mp->data;
budmin = dcp->budmin;
- if (dcp->leafidx != cpu_to_le32(CTLLEAFIND)) {
+ if (unlikely(!check_dmapctl(dcp))) {
jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmapctl page\n");
release_metapage(mp);
return -EIO;
@@ -1702,7 +1789,7 @@ static int dbFindCtl(struct bmap * bmp, int l2nb, int level, s64 * blkno)
dcp = (struct dmapctl *) mp->data;
budmin = dcp->budmin;
- if (dcp->leafidx != cpu_to_le32(CTLLEAFIND)) {
+ if (unlikely(!check_dmapctl(dcp))) {
jfs_error(bmp->db_ipbmap->i_sb,
"Corrupt dmapctl page\n");
release_metapage(mp);
@@ -2485,7 +2572,7 @@ dbAdjCtl(struct bmap * bmp, s64 blkno, int newval, int alloc, int level)
return -EIO;
dcp = (struct dmapctl *) mp->data;
- if (dcp->leafidx != cpu_to_le32(CTLLEAFIND)) {
+ if (unlikely(!check_dmapctl(dcp))) {
jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmapctl page\n");
release_metapage(mp);
return -EIO;
@@ -3454,6 +3541,11 @@ int dbExtendFS(struct inode *ipbmap, s64 blkno, s64 nblocks)
return -EIO;
}
l2dcp = (struct dmapctl *) l2mp->data;
+ if (unlikely(!check_dmapctl(l2dcp))) {
+ jfs_error(ipbmap->i_sb, "Corrupt dmapctl page\n");
+ release_metapage(l2mp);
+ return -EIO;
+ }
/* compute start L1 */
k = blkno >> L2MAXL1SIZE;
@@ -3471,6 +3563,10 @@ int dbExtendFS(struct inode *ipbmap, s64 blkno, s64 nblocks)
if (l1mp == NULL)
goto errout;
l1dcp = (struct dmapctl *) l1mp->data;
+ if (unlikely(!check_dmapctl(l1dcp))) {
+ jfs_error(ipbmap->i_sb, "Corrupt dmapctl page\n");
+ goto errout;
+ }
/* compute start L0 */
j = (blkno & (MAXL1SIZE - 1)) >> L2MAXL0SIZE;
@@ -3484,6 +3580,10 @@ int dbExtendFS(struct inode *ipbmap, s64 blkno, s64 nblocks)
goto errout;
l1dcp = (struct dmapctl *) l1mp->data;
+ if (unlikely(!check_dmapctl(l1dcp))) {
+ jfs_error(ipbmap->i_sb, "Corrupt dmapctl page\n");
+ goto errout;
+ }
/* compute start L0 */
j = 0;
@@ -3503,6 +3603,10 @@ int dbExtendFS(struct inode *ipbmap, s64 blkno, s64 nblocks)
if (l0mp == NULL)
goto errout;
l0dcp = (struct dmapctl *) l0mp->data;
+ if (unlikely(!check_dmapctl(l0dcp))) {
+ jfs_error(ipbmap->i_sb, "Corrupt dmapctl page\n");
+ goto errout;
+ }
/* compute start dmap */
i = (blkno & (MAXL0SIZE - 1)) >>
@@ -3518,6 +3622,10 @@ int dbExtendFS(struct inode *ipbmap, s64 blkno, s64 nblocks)
goto errout;
l0dcp = (struct dmapctl *) l0mp->data;
+ if (unlikely(!check_dmapctl(l0dcp))) {
+ jfs_error(ipbmap->i_sb, "Corrupt dmapctl page\n");
+ goto errout;
+ }
/* compute start dmap */
i = 0;
]
|
| 62/2 |
2026/06/30 14:46 |
llm |
debugger |
0m
Model:
Tokens:
input: 51947
output: 40
thoughts: 118
Thoughts: **Analyzing the DMAP/DMAPCTL Implementation**
Okay, so we added `check_dmapctl`, which makes sense for, you know, checking `dmapctl`. But then it struck me – we're not actually checking `dmap` itself. That seemed like a gap. Hmm, let me think. Right! `dmap` has that internal `struct dmaptree tree`, so it’s all connected.
Now, if I recall correctly, `struct dmaptree` holds all that vital metadata: `nleafs`, `l2nleafs`, `leafidx`, `height`, `budmin`, `stree`. The structure is actually quite similar in purpose to what we find in `dmapctl`. It's really the same underlying data!
Therefore, we need to think about adding a `check_dmap` or maybe, even better, `check_dmaptree` function. It would make more sense to check the underlying datastructure, and if that is valid, then `dmap` is valid by extension. Now I'm going to have to dive into that `struct dmaptree` definition again. Let's see...
|
| 63/2 |
2026/06/30 14:46 |
tool |
read-file |
0m
Args: map[File:fs/jfs/jfs_dmap.h FirstLine:134 LineCount:10]
Results: map[Contents: 134: struct dmaptree {
135: __le32 nleafs; /* 4: number of tree leafs */
136: __le32 l2nleafs; /* 4: l2 number of tree leafs */
137: __le32 leafidx; /* 4: index of first tree leaf */
138: __le32 height; /* 4: height of the tree */
139: s8 budmin; /* 1: min l2 tree leaf value to combine */
140: s8 stree[TREESIZE]; /* TREESIZE: tree */
141: u8 pad[2]; /* 2: pad to word boundary */
142: }; /* - 360 - */
143:
]
|
| 64/2 |
2026/06/30 14:46 |
llm |
debugger |
0m
Model:
Tokens:
input: 52587
output: 41
thoughts: 498
Thoughts: **Validating the `dmap` Structure for Robustness**
Okay, so I'm thinking about the `struct dmaptree` and specifically that `leafidx` member. If it's corrupted, we're in trouble, right? The potential problem lies with `dbFree`, because it reads the `dmap` and then calls `dbFreeDmap`. If `dp->tree.leafidx` is bad, `dbFreeBits` will call `dbJoin` which could lead to an out-of-bounds access.
So, the key here is to validate `dp->tree.leafidx` whenever the `dmap` is read from disk. The obvious place to start is within `dbFree` itself. Let's see... looking at the code:
```c
mp = read_metapage(ipbmap, lblkno, PSIZE, 0);
if (mp == NULL) {
IREAD_UNLOCK(ipbmap);
return -EIO;
}
dp = (struct dmap *) mp->data;
```
Right, we can add `check_dmap(dp)` right there after the read.
Now, let's make sure we haven't missed any other reads. Okay, I'm going through the other functions... `dbUpdatePMap`, `dbAllocBottomUp`, `dbExtendFS`, `dbAlloc`, `dbExtend`, `dbAllocCtl`. Wait a minute, `dbAllocNext` and `dbAllocNear` are *already* checking `dp->tree.leafidx != cpu_to_le32(LEAFIND)`. Interesting!
If we create `check_dmap` and make it robust, we could potentially replace those checks. I think that's the right direction. Let's start thinking about what this `check_dmap` or maybe `check_dmaptree` function needs to do.
Ah, of course. `check_dmapctl` checks a lot of stuff: `nleafs`, `l2nleafs`, `leafidx`, `height`, `budmin`, and `stree`. For the basic `dmap`, `nleafs` should be `LPERDMAP` (256), `l2nleafs` should be `L2LPERDMAP` (8), `leafidx` is expected to be `LEAFIND` (85), `height` is 4, `budmin` should be `BUDMIN` (0). Also, the `stree` size is `TREESIZE` (341). Got it. That sounds like a good check to implement. Let me just take a quick look at `dbInitDmapTree` to ensure that it has the correct values.
|
| 65/2 |
2026/06/30 14:46 |
tool |
read-file |
0m
Args: map[File:fs/jfs/jfs_dmap.c FirstLine:3940 LineCount:20]
Results: map[Contents:3940: static int dbInitDmapTree(struct dmap * dp)
3941: {
3942: struct dmaptree *tp;
3943: s8 *cp;
3944: int i;
3945:
3946: /* init fixed info of tree */
3947: tp = &dp->tree;
3948: tp->nleafs = cpu_to_le32(LPERDMAP);
3949: tp->l2nleafs = cpu_to_le32(L2LPERDMAP);
3950: tp->leafidx = cpu_to_le32(LEAFIND);
3951: tp->height = cpu_to_le32(4);
3952: tp->budmin = BUDMIN;
3953:
3954: /* init each leaf from corresponding wmap word:
3955: * note: leaf is set to NOFREE(-1) if all blocks of corresponding
3956: * bitmap word are allocated.
3957: */
3958: cp = tp->stree + le32_to_cpu(tp->leafidx);
3959: for (i = 0; i < LPERDMAP; i++)
]
|
| 66/2 |
2026/06/30 14:46 |
llm |
debugger |
0m
Model:
Tokens:
input: 54077
output: 41
thoughts: 2349
Thoughts: **Analyzing and Securing the JFS dmap Implementation**
Okay, so I'm looking at this `check_dmaptree` function, and it seems like a solid approach to validating the structure of our `dmaptree` data. I've confirmed that the critical fields like `nleafs`, `l2nleafs`, `leafidx`, and `height` are defined correctly with their corresponding constants (`LPERDMAP`, `L2LPERDMAP`, `LEAFIND`, and 4 respectively) and can follow a similar pattern to `check_dmapctl`.
I'm considering incorporating this `check_dmaptree` function into the various read paths. The current code checks for invalid values during initialization, but it doesn't appear to perform this comprehensive validation upon reading from disk. I've noted that `check_dmapctl` was added later, but `check_dmaptree` wasn't, potentially because early fuzzing efforts didn't uncover dmap corruption as readily.
The crash is happening in `dbJoin`, which is used for both `dmap` and `dmapctl`. Although validating `tp->dmt_leafidx` inside `dbJoin`, `dbSplit`, `dbBackSplit`, `dbAdjTree`, and `dbFindLeaf` would provide robustness, it may introduce an unnecessary performance overhead with the addition of more `if` statements.
Therefore, the most critical locations to introduce the `check_dmaptree` call would be where `dmap` is read from disk, like in `dbFree`, `dbUpdatePMap`, `dbAllocBottomUp`, `dbExtendFS`, `dbAlloc`, `dbExtend`, and `dbAllocCtl`. I also noted `dbAllocNext` and `dbAllocNear` check `dp->tree.leafidx` already. But they don't check for all the other conditions, like the number of leaves or height.
Since `dbFreeDmap` isn't calling `dbAllocNext`, it's potentially vulnerable. Therefore, this is the most critical area to add either `check_dmaptree`, or, at a minimum, a `dp->tree.leafidx != cpu_to_le32(LEAFIND)` check. I need to closely examine `dbFreeDmap` to see where the checks should be placed. Adding these validations should help prevent corruption from leading to more serious issues.
|
| 67/2 |
2026/06/30 14:46 |
tool |
read-file |
0m
Args: map[File:fs/jfs/jfs_dmap.c FirstLine:2164 LineCount:20]
Results: map[Contents:2164: static int dbFreeDmap(struct bmap * bmp, struct dmap * dp, s64 blkno,
2165: int nblocks)
2166: {
2167: s8 oldroot;
2168: int rc = 0, word;
2169:
2170: /* save the current value of the root (i.e. maximum free string)
2171: * of the dmap tree.
2172: */
2173: oldroot = dp->tree.stree[ROOT];
2174:
2175: /* free the specified (blocks) bits */
2176: rc = dbFreeBits(bmp, dp, blkno, nblocks);
2177:
2178: /* if error or the root has not changed, done. */
2179: if (rc || (dp->tree.stree[ROOT] == oldroot))
2180: return (rc);
2181:
2182: /* root changed. bubble the change up to the dmap control pages.
2183: * if the adjustment of the upper level control pages fails,
]
|
| 68/2 |
2026/06/30 14:46 |
llm |
debugger |
0m
Model:
Tokens:
input: 57306
output: 41
thoughts: 149
Thoughts: **Analyzing a Potential Bug Fix**
Okay, so I'm looking at this potential crash in `dbFreeDmap`. The immediate thought is that adding an `if (dp->tree.leafidx != cpu_to_le32(LEAFIND))` check in `dbFreeDmap` *should* prevent the issue. Seems straightforward enough. But, I need to take a step back and consider the broader implications.
The red flag is that `dbFreeBits` is also called from `dbUpdatePMap`, and I need to check how it will be affected by this fix. A quick scan reveals that neither `dbFreeBits` nor `dbAllocBits` actually validate `dp->tree.leafidx`, and that’s a problem. A change in the logic of one of these functions may cause an issue further up the call stack.
Right now, I'm leaning toward the `dbAllocBits` function as the place to start digging deeper. It seems like the common function which causes the problem.
|
| 69/2 |
2026/06/30 14:46 |
tool |
read-file |
0m
Args: map[File:fs/jfs/jfs_dmap.c FirstLine:2226 LineCount:20]
Results: map[Contents:2226: static void dbAllocBits(struct bmap * bmp, struct dmap * dp, s64 blkno,
2227: int nblocks)
2228: {
2229: int dbitno, word, rembits, nb, nwords, wbitno, nw, agno;
2230: dmtree_t *tp = (dmtree_t *) & dp->tree;
2231: int size;
2232: s8 *leaf;
2233:
2234: /* pick up a pointer to the leaves of the dmap tree */
2235: leaf = dp->tree.stree + LEAFIND;
2236:
2237: /* determine the bit number and word within the dmap of the
2238: * starting block.
2239: */
2240: dbitno = blkno & (BPERDMAP - 1);
2241: word = dbitno >> L2DBWORD;
2242:
2243: /* block range better be within the dmap */
2244: assert(dbitno + nblocks <= BPERDMAP);
2245:
]
|
| 70/2 |
2026/06/30 14:46 |
llm |
debugger |
0m
Model:
Tokens:
input: 58042
output: 40
thoughts: 225
Thoughts: **Vulnerability Assessment: `dbAllocBits`**
Okay, hold on a second! I've been focusing on `LEAFIND` and the potential for direct manipulation there, but something just clicked. I see that `dbAllocBits` *directly* accesses the dmap tree leaves using `LEAFIND`:
```c
/* pick up a pointer to the leaves of the dmap tree */
leaf = dp->tree.stree + LEAFIND;
```
But, further down in the code, `dbAllocBits` also calls `dbJoin`, and that function is using `tp->dmt_leafidx`!
```c
/* update the leaf for this dmap word.
*/
dbJoin(tp, word,
dbMaxBud((u8 *) & dp->wmap[word]), false);
```
```c
leaf = tp->dmt_stree + le32_to_cpu(tp->dmt_leafidx);
```
Meaning, we've got *two* points of potential vulnerability within `dbAllocBits`. If `tp->dmt_leafidx` gets corrupted, even if `LEAFIND` is intact, we're still going to be reading from the wrong leaf! This doubles the attack surface, potentially.
The solution seems straightforward. We've got `check_dmaptree` in place for dmap trees. It's a matter of adding a check *everywhere* the dmap is read to make it significantly safer. Let's see what `check_dmapctl` is doing and what we can learn from it for this scenario.
|
| 71/2 |
2026/06/30 14:46 |
tool |
read-file |
0m
Args: map[File:fs/jfs/jfs_dmap.c FirstLine:133 LineCount:50]
Results: map[Contents: 133: 2, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -1
134: };
135:
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);
]
|
| 72/2 |
2026/06/30 14:46 |
llm |
debugger |
0m
Model:
Tokens:
input: 59523
output: 39
thoughts: 2146
Thoughts: **Analyzing `dmaptree` Validation in JFS**
Okay, so I'm looking at this `check_dmaptree` function, and it seems pretty straightforward. It's a sanity check for the `dmaptree` structure within the `dmap`. My initial thought is that if this validation is in place, it should prevent the out-of-bounds access I was worried about in those `dbJoin` and `dbSplit` calls.
I'm making a mental note that the `dmap` structure also contains `nblocks` and `nfree`. I'm going to keep an eye on these additional checks within the `dmap` structure. It looks like just checking the `dmaptree` might be enough to prevent the specific out-of-bounds problems in those functions, but I'll make sure to verify that.
Where should I be placing the `check_dmaptree` calls? It looks like it's already implemented in `dbFree`, `dbUpdatePMap`, `dbAllocBottomUp`, `dbExtendFS`, `dbAlloc`, `dbExtend`, and `dbAllocCtl`. Okay, good. That's a good set of checks.
I'm seeing that functions like `dbAllocNext` and `dbAllocNear` also have a similar check for `leafidx`. But I can optimize this a bit. If I call the `check_dmaptree` function after I read the metapage, I could remove this redundant check. Since `dbAllocNext` and `dbAllocNear` are called from `dbAlloc` and `dbAllocCtl`, I can just check the `dmaptree` there. But, I should double-check that `dbAllocNext` is only called from `dbAlloc` and `dbAllocCtl`, and not from something like `dbAllocAG`.
Alright, I will verify the call graph for `dbAllocNext` and `dbAllocNear`, and make sure my assumption is correct before I go ahead and remove the redundant `leafidx` checks. That's a good optimization.
|
| 73/2 |
2026/06/30 14:46 |
tool |
codesearch-find-references |
0m
Args: map[IncludeSnippetLines:0 Name:dbAllocNext SourceTreePrefix:fs/jfs/]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:dbAlloc SourceFile:fs/jfs/jfs_dmap.c SourceLine:892] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:dbExtend SourceFile:fs/jfs/jfs_dmap.c SourceLine:1124]] TruncatedOutput:false]
|
| 74/2 |
2026/06/30 14:46 |
llm |
debugger |
0m
Model:
Tokens:
input: 62275
output: 39
thoughts: 6
Thoughts: Okay, here's my attempt at summarizing the thought process, speaking from the first person, and assuming I'm an expert in the field:
**Internal Monologue on Memory Allocation**
Right, so I'm thinking about memory allocation, specifically `dbAllocNear`. What's the best way to approach this? We're dealing with a system, likely embedded, where memory is a precious resource. The "Near" part of the name tells me we're probably aiming for something performance-oriented, low latency access, maybe in a specific memory region like a scratchpad or tightly coupled memory.
My initial thoughts revolve around the trade-offs. Speed versus fragmentation is always the dance we do. Am I going for a really fast, pre-allocated pool with predictable performance but potentially wasted space? Or are we considering a more dynamic, general-purpose allocator that can handle fragmentation but introduces latency? The context of where this `dbAllocNear` is used is key. Is it in a critical real-time loop, or something more background? This will heavily influence the design.
I'm starting to consider various allocation strategies. Maybe a simple bump allocator would be good if the memory regions are contiguous and the allocations are primarily for short-lived, ordered objects. I'd then have to consider, what is the size of the objects to be allocated? The efficiency of the `dbAllocNear` is paramount. I'd need to consider alignment constraints – can we work with a simpler system using word-aligned blocks, or do we need bit-precise addressing for a particular data layout? I am also considering error handling - what happens when this allocation fails, and what can I do to recover from the failure?
Furthermore, I have to think about concurrency. If there are multiple threads accessing this allocation mechanism, I'll need some form of synchronization, likely a mutex or lock-free structure, to ensure data consistency and prevent memory corruption. I need to be careful with the chosen approach. A mutex can introduce significant overhead, but a poorly implemented lock-free approach can be even worse. We need the right balance.
Then, there's the question of deallocation. Is this a system where objects are explicitly freed, or do we have some kind of garbage collection (unlikely for a "near" allocator in a performance-critical area)? The deallocation strategy will directly impact the complexity of the allocator and the potential for fragmentation. If deallocation happens, is it likely to free smaller areas, or large areas - if the smaller areas, I would think about managing free lists, which will increase the complexity.
Finally, I'm thinking about testing and debugging. How can I verify that `dbAllocNear` is working correctly? I'd need to design test cases that cover various allocation patterns, including boundary conditions, concurrent access, and error scenarios. We need to be able to detect memory leaks and corruption as quickly as possible. This requires carefully-crafted unit tests and possibly memory profiling tools. The documentation is critical - I'll need to clearly document any assumptions, limitations, and usage guidelines for this function to prevent unexpected problems down the line. It's time to dig into the existing codebase and see if there are any hints!
|
| 75/2 |
2026/06/30 14:46 |
tool |
codesearch-find-references |
0m
Args: map[IncludeSnippetLines:0 Name:dbAllocNear SourceTreePrefix:fs/jfs/]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:dbAlloc SourceFile:fs/jfs/jfs_dmap.c SourceLine:919]] TruncatedOutput:false]
|
| 76/2 |
2026/06/30 14:46 |
llm |
debugger |
0m
Model:
Tokens:
input: 63058
output: 41
thoughts: 149
Thoughts: **Refactoring Allocation Checks**
Okay, here's the plan. If I introduce `check_dmaptree` immediately after `read_metapage` within both `dbAlloc` and `dbExtend`, I can safely eliminate the redundant checks currently present in `dbAllocNext` and `dbAllocNear`. That's a decent simplification.
Hold on a second... what about `dbAllocCtl`? It has a direct call to `dbAllocBits`. Let me think... right, `dbAllocCtl` reads from the `dmap` and then uses the result with `dbAllocBits`.
```c
/* allocate the blocks. */
dbAllocBits(bmp, dp, blkno, nb);
```
So, yes, it seems adding `check_dmaptree` within `dbAllocCtl` is absolutely crucial to maintaining data integrity. It's a key piece.
Just to be absolutely sure, I should probably double-check the implementation of `dbAllocCtl` to make sure I haven't missed anything. Gotta be meticulous about this kind of change.
|
| 77/2 |
2026/06/30 14:46 |
tool |
read-file |
0m
Args: map[File:fs/jfs/jfs_dmap.c FirstLine:1887 LineCount:50]
Results: map[Contents:1887: dbAllocCtl(struct bmap * bmp, s64 nblocks, int l2nb, s64 blkno, s64 * results)
1888: {
1889: int rc, nb;
1890: s64 b, lblkno, n;
1891: struct metapage *mp;
1892: struct dmap *dp;
1893:
1894: /* check if the allocation request is confined to a single dmap.
1895: */
1896: if (l2nb <= L2BPERDMAP) {
1897: /* get the buffer for the dmap.
1898: */
1899: lblkno = BLKTODMAP(blkno, bmp->db_l2nbperpage);
1900: mp = read_metapage(bmp->db_ipbmap, lblkno, PSIZE, 0);
1901: if (mp == NULL)
1902: return -EIO;
1903: dp = (struct dmap *) mp->data;
1904:
1905: if (dp->tree.budmin < 0) {
1906: release_metapage(mp);
1907: return -EIO;
1908: }
1909:
1910: /* try to allocate the blocks.
1911: */
1912: rc = dbAllocDmapLev(bmp, dp, (int) nblocks, l2nb, results);
1913: if (rc == 0)
1914: mark_metapage_dirty(mp);
1915:
1916: release_metapage(mp);
1917:
1918: return (rc);
1919: }
1920:
1921: /* allocation request involving multiple dmaps. it must start on
1922: * a dmap boundary.
1923: */
1924: assert((blkno & (BPERDMAP - 1)) == 0);
1925:
1926: /* allocate the blocks dmap by dmap.
1927: */
1928: for (n = nblocks, b = blkno; n > 0; n -= nb, b += nb) {
1929: /* get the buffer for the dmap.
1930: */
1931: lblkno = BLKTODMAP(b, bmp->db_l2nbperpage);
1932: mp = read_metapage(bmp->db_ipbmap, lblkno, PSIZE, 0);
1933: if (mp == NULL) {
1934: rc = -EIO;
1935: goto backout;
1936: }
]
|
| 78/2 |
2026/06/30 14:46 |
llm |
debugger |
0m
Model:
Tokens:
input: 64261
output: 41
thoughts: 595
Thoughts: **Refining the dmap Tree Consistency Check**
Okay, so I see that `dbAllocCtl` reads the `dmap` structure in a couple of places. The initial instinct was to add a `check_dmaptree` call *everywhere* that `dmap` is accessed in that function, which quickly ballooned into a lot of additions. That's not the most elegant solution.
Then I considered adding `check_dmaptree` to all of the *major* allocation/deallocation functions: `dbAllocNext`, `dbAllocNear`, `dbAllocDmapLev`, `dbAllocBits`, `dbFreeBits`, and `dbFreeDmap`. That seems more targeted.
Hold on a second... `dbFreeDmap` calls `dbFreeBits`. So, checking inside `dbFreeBits` would effectively cover the `dbFreeDmap` case.
Thinking more broadly, let me list all the functions that directly *operate* on the `dmap`:
* `dbFreeDmap`
* `dbFreeBits`
* `dbAllocBits`
* `dbAllocNext`
* `dbAllocNear`
* `dbAllocDmapLev`
If I place the `check_dmaptree` at the *beginning* of *each* of these functions, I should have comprehensive coverage, validating the `dmap` tree before any changes are made. The `dbFreeDmap` will be indirectly taken care of by the `dbFreeBits` check. Now to just double check `dbAllocDmapLev`.
|
| 79/2 |
2026/06/30 14:46 |
tool |
read-file |
0m
Args: map[File:fs/jfs/jfs_dmap.c FirstLine:2043 LineCount:20]
Results: map[Contents:2043: dbAllocDmapLev(struct bmap * bmp,
2044: struct dmap * dp, int nblocks, int l2nb, s64 * results)
2045: {
2046: s64 blkno;
2047: int leafidx, rc;
2048:
2049: /* can't be more than a dmaps worth of blocks */
2050: assert(l2nb <= L2BPERDMAP);
2051:
2052: /* search the tree within the dmap page for sufficient
2053: * free space. if sufficient free space is found, dbFindLeaf()
2054: * returns the index of the leaf at which free space was found.
2055: */
2056: if (dbFindLeaf((dmtree_t *) &dp->tree, l2nb, &leafidx, false))
2057: return -ENOSPC;
2058:
2059: if (leafidx < 0)
2060: return -EIO;
2061:
2062: /* determine the block number within the file system corresponding
]
|
| 80/2 |
2026/06/30 14:46 |
llm |
debugger |
0m
Model:
Tokens:
input: 65563
output: 39
thoughts: 233
Thoughts: **Refining the DMAP Tree Integrity Checks**
Okay, so I'm looking at enhancing the error handling within the DMAP tree code. The core idea is to introduce `check_dmaptree` in several key allocation and deallocation functions to improve robustness. My initial plan is to insert this check into `dbFreeBits`, `dbAllocBits`, `dbAllocNext`, `dbAllocNear`, and `dbAllocDmapLev`.
Hold on... I just realized that `dbAllocNext` and `dbAllocNear` are *already* performing a check on the `dp->tree.leafidx`. Currently, it tests for `cpu_to_le32(LEAFIND)`. My thought is to replace this conditional with a call to `check_dmaptree`, and I can apply the same logic to `dbFreeBits`, and `dbAllocDmapLev`.
A potential wrinkle has appeared, though. I'm focusing on `dbAllocBits` now and remember that its return types are important. Specifically, `dbFreeBits` returns an `int` and `dbAllocBits` is `void`. This means `dbAllocBits` cannot directly signal an error using its return value. So, if `check_dmaptree` fails inside `dbAllocBits`, I can't simply return an error code like `-EIO`. This changes things. I need to investigate how `dbAllocBits` is used. I'll have to trace its callers to understand the broader context and see how to propagate a failure condition, or if there's an alternative. This might change my approach.
|
| 81/2 |
2026/06/30 14:46 |
tool |
codesearch-find-references |
0m
Args: map[IncludeSnippetLines:0 Name:dbAllocBits SourceTreePrefix:fs/jfs/]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:dbAllocDmap SourceFile:fs/jfs/jfs_dmap.c SourceLine:2121] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:dbFreeDmap SourceFile:fs/jfs/jfs_dmap.c SourceLine:2197]] TruncatedOutput:false]
|
| 82/2 |
2026/06/30 14:46 |
llm |
debugger |
0m
Model:
Tokens:
input: 66302
output: 28
thoughts: 56
Thoughts: Here's my take on the situation:
**Investigating the Scope of `dbAllocBits` Usage**
Okay, so I see `dbAllocBits` popping up in a bunch of crucial places: `dbAllocDmap`, `dbFreeDmap`, `dbUpdatePMap`, `dbAllocBottomUp`, `dbExtendFS`, and `dbAllocCtl`. That's a pretty diverse set of functions, all of which seem fundamental to the storage layer, and potentially the filesystems themselves. Since the function in question is so broadly used across core features, I need to get a better handle on the implications. Time to fire up the grepper and see exactly how `dbAllocBits` is utilized in each of those contexts. It's time to follow the trail.
|
| 83/2 |
2026/06/30 14:46 |
tool |
grepper |
0m
Args: map[Expression:dbAllocBits PathPrefix:fs/jfs/]
Results: map[Output:fs/jfs/jfs_dmap.c-63- */
fs/jfs/jfs_dmap.c:64:static void dbAllocBits(struct bmap * bmp, struct dmap * dp, s64 blkno,
fs/jfs/jfs_dmap.c-65- int nblocks);
--
fs/jfs/jfs_dmap.c=2043=dbAllocDmapLev(struct bmap * bmp,
--
fs/jfs/jfs_dmap.c-2088- * this routine allocates the specified blocks from the dmap
fs/jfs/jfs_dmap.c:2089: * through a call to dbAllocBits(). if the allocation of the
fs/jfs/jfs_dmap.c-2090- * block range causes the maximum string of free blocks within
--
fs/jfs/jfs_dmap.c=2109=static int dbAllocDmap(struct bmap * bmp, struct dmap * dp, s64 blkno,
--
fs/jfs/jfs_dmap.c-2120- /* allocate the specified (blocks) bits */
fs/jfs/jfs_dmap.c:2121: dbAllocBits(bmp, dp, blkno, nblocks);
fs/jfs/jfs_dmap.c-2122-
--
fs/jfs/jfs_dmap.c=2164=static int dbFreeDmap(struct bmap * bmp, struct dmap * dp, s64 blkno,
--
fs/jfs/jfs_dmap.c-2196-
fs/jfs/jfs_dmap.c:2197: dbAllocBits(bmp, dp, blkno, nblocks);
fs/jfs/jfs_dmap.c-2198- }
--
fs/jfs/jfs_dmap.c-2204-/*
fs/jfs/jfs_dmap.c:2205: * NAME: dbAllocBits()
fs/jfs/jfs_dmap.c-2206- *
--
fs/jfs/jfs_dmap.c-2225- */
fs/jfs/jfs_dmap.c:2226:static void dbAllocBits(struct bmap * bmp, struct dmap * dp, s64 blkno,
fs/jfs/jfs_dmap.c-2227- int nblocks)
]
|
| 84/2 |
2026/06/30 14:46 |
llm |
debugger |
0m
Model:
Tokens:
input: 67190
output: 40
thoughts: 36
Thoughts: Okay, let's see...
**Analyzing Data Block Allocation in the System**
Hmm, it seems like `dbAllocBits` is exclusively called within `dbAllocDmap` and `dbFreeDmap`. That's interesting, and potentially a very narrow scope to consider. I need to understand if this has implications for how allocation and deallocation function together. Let's see if there's any important overlap or interaction with something like `dbUpdatePMap`. I need to ensure that the allocated bits are properly reflected across the whole system.
|
| 85/2 |
2026/06/30 14:46 |
tool |
read-file |
0m
Args: map[File:fs/jfs/jfs_dmap.c FirstLine:525 LineCount:50]
Results: map[Contents: 525: dbUpdatePMap(struct inode *ipbmap,
526: int free, s64 blkno, s64 nblocks, struct tblock * tblk)
527: {
528: int nblks, dbitno, wbitno, rbits;
529: int word, nbits, nwords;
530: struct bmap *bmp = JFS_SBI(ipbmap->i_sb)->bmap;
531: s64 lblkno, rem, lastlblkno;
532: u32 mask;
533: struct dmap *dp;
534: struct metapage *mp;
535: struct jfs_log *log;
536: int lsn, difft, diffp;
537: unsigned long flags;
538:
539: /* the blocks better be within the mapsize. */
540: if (blkno + nblocks > bmp->db_mapsize) {
541: printk(KERN_ERR "blkno = %Lx, nblocks = %Lx\n",
542: (unsigned long long) blkno,
543: (unsigned long long) nblocks);
544: jfs_error(ipbmap->i_sb, "blocks are outside the map\n");
545: return -EIO;
546: }
547:
548: /* compute delta of transaction lsn from log syncpt */
549: lsn = tblk->lsn;
550: log = (struct jfs_log *) JFS_SBI(tblk->sb)->log;
551: logdiff(difft, lsn, log);
552:
553: /*
554: * update the block state a dmap at a time.
555: */
556: mp = NULL;
557: lastlblkno = 0;
558: for (rem = nblocks; rem > 0; rem -= nblks, blkno += nblks) {
559: /* get the buffer for the current dmap. */
560: lblkno = BLKTODMAP(blkno, bmp->db_l2nbperpage);
561: if (lblkno != lastlblkno) {
562: if (mp) {
563: write_metapage(mp);
564: }
565:
566: mp = read_metapage(bmp->db_ipbmap, lblkno, PSIZE,
567: 0);
568: if (mp == NULL)
569: return -EIO;
570: metapage_wait_for_io(mp);
571: }
572: dp = (struct dmap *) mp->data;
573:
574: /* determine the bit number and word within the dmap of
]
|
| 86/2 |
2026/06/30 14:46 |
llm |
debugger |
0m
Model:
Tokens:
input: 68213
output: 40
thoughts: 3
Thoughts: Okay, I'm ready. Here's how I'll approach summarizing the "thoughts," assuming I'm an expert in the relevant field:
**Assessing the Landscape: Initial Impressions & Analysis**
Okay, here's what I'm thinking. The initial presentation is crucial – it's the foundation upon which everything else will be built. I need to quickly identify the core problem or concept being addressed. What are the key elements? What's the potential significance? Is it something novel, or a rehash of existing ideas with a new spin? I'll be looking for nuances, those little details that separate solid work from truly insightful contributions. The quality of the first impression matters enormously. I need to determine if this is a concept that warrants further investment.
|
| 87/2 |
2026/06/30 14:46 |
tool |
read-file |
0m
Args: map[File:fs/jfs/jfs_dmap.c FirstLine:575 LineCount:50]
Results: map[Contents: 575: * the starting block. also determine how many blocks
576: * are to be updated within this dmap.
577: */
578: dbitno = blkno & (BPERDMAP - 1);
579: word = dbitno >> L2DBWORD;
580: nblks = min(rem, (s64)BPERDMAP - dbitno);
581:
582: /* update the bits of the dmap words. the first and last
583: * words may only have a subset of their bits updated. if
584: * this is the case, we'll work against that word (i.e.
585: * partial first and/or last) only in a single pass. a
586: * single pass will also be used to update all words that
587: * are to have all their bits updated.
588: */
589: for (rbits = nblks; rbits > 0;
590: rbits -= nbits, dbitno += nbits) {
591: /* determine the bit number within the word and
592: * the number of bits within the word.
593: */
594: wbitno = dbitno & (DBWORD - 1);
595: nbits = min(rbits, DBWORD - wbitno);
596:
597: /* check if only part of the word is to be updated. */
598: if (nbits < DBWORD) {
599: /* update (free or allocate) the bits
600: * in this word.
601: */
602: mask =
603: (ONES << (DBWORD - nbits) >> wbitno);
604: if (free)
605: dp->pmap[word] &=
606: cpu_to_le32(~mask);
607: else
608: dp->pmap[word] |=
609: cpu_to_le32(mask);
610:
611: word += 1;
612: } else {
613: /* one or more words are to have all
614: * their bits updated. determine how
615: * many words and how many bits.
616: */
617: nwords = rbits >> L2DBWORD;
618: nbits = nwords << L2DBWORD;
619:
620: /* update (free or allocate) the bits
621: * in these words.
622: */
623: if (free)
624: memset(&dp->pmap[word], 0,
]
|
| 88/2 |
2026/06/30 14:46 |
llm |
debugger |
0m
Model:
Tokens:
input: 69197
output: 41
thoughts: 311
Thoughts: **Analyzing dmap Allocation and Freeing Logic**
Okay, so I'm looking at this `dbUpdatePMap` function, and it seems like it's updating `dp->pmap`, not `dp->wmap`. That's important to note. Also, it doesn't call `dbFreeBits` or `dbAllocBits` directly, which means those functions are handled elsewhere. Now, where do *those* get called? Hmm, `dbAllocBits` is called in `dbAllocDmap` and `dbFreeDmap`. Right, so `dbAllocDmap` is called by `dbAllocDmapLev`. And `dbAllocDmapLev` is ultimately called by `dbAllocCtl`. Got it. So, `dbAllocBits` is exclusively called by `dbAllocDmap` and `dbFreeDmap`, which is what I thought. Similarly, `dbFreeBits` is only called by `dbFreeDmap`. And finally, `dbFreeDmap` is called by `dbFree`.
Alright, I think I've got it. I think adding `check_dmaptree` in `dbFreeDmap`, `dbAllocDmap`, `dbAllocNext`, `dbAllocNear`, and `dbAllocDmapLev` should cover all locations where the dmap is used for allocation and freeing. But wait, `dbAllocNext` and `dbAllocNear` check `dp->tree.leafidx != cpu_to_le32(LEAFIND)`. Ah, I see - they have that check already. If I replace those with `check_dmaptree`, in addition to the other places, and then add `check_dmaptree` to `dbFreeDmap` and `dbAllocDmapLev`.... Well, I *think* that's it. Let me just confirm. Ah, `dbAllocDmap` is called by `dbAllocDmapLev`. Let me double-check that part again. Time to look at `dbAllocDmapLev` in detail.
|
| 89/2 |
2026/06/30 14:46 |
tool |
read-file |
0m
Args: map[File:fs/jfs/jfs_dmap.c FirstLine:2043 LineCount:30]
Results: map[Contents:2043: dbAllocDmapLev(struct bmap * bmp,
2044: struct dmap * dp, int nblocks, int l2nb, s64 * results)
2045: {
2046: s64 blkno;
2047: int leafidx, rc;
2048:
2049: /* can't be more than a dmaps worth of blocks */
2050: assert(l2nb <= L2BPERDMAP);
2051:
2052: /* search the tree within the dmap page for sufficient
2053: * free space. if sufficient free space is found, dbFindLeaf()
2054: * returns the index of the leaf at which free space was found.
2055: */
2056: if (dbFindLeaf((dmtree_t *) &dp->tree, l2nb, &leafidx, false))
2057: return -ENOSPC;
2058:
2059: if (leafidx < 0)
2060: return -EIO;
2061:
2062: /* determine the block number within the file system corresponding
2063: * to the leaf at which free space was found.
2064: */
2065: blkno = le64_to_cpu(dp->start) + (leafidx << L2DBWORD);
2066:
2067: /* if not all bits of the dmap word are free, get the starting
2068: * bit number within the dmap word of the required string of free
2069: * bits and adjust the block number with this value.
2070: */
2071: if (dp->tree.stree[leafidx + LEAFIND] < BUDMIN)
2072: blkno += dbFindBits(le32_to_cpu(dp->wmap[leafidx]), l2nb);
]
|
| 90/2 |
2026/06/30 14:46 |
llm |
debugger |
0m
Model:
Tokens:
input: 70507
output: 41
thoughts: 112
Thoughts: **Checking the DMAP Tree**
Okay, so I'm thinking through the `dbAllocDmapLev` function and confirming that it, indeed, utilizes `dp->tree`. Therefore, if I want to ensure the integrity of the DMAP tree, I need to add that `check_dmaptree` function to the following locations in the code: `dbFreeDmap`, `dbAllocNext`, `dbAllocNear`, and, of course, `dbAllocDmapLev` itself. I need to make sure this is done thoroughly.
Hmm, wait a second... looking closer, `dbAllocBottomUp` calls `dbAllocBits` directly, and `dbExtendFS` also calls `dbAllocBits` directly. That's something I need to keep in mind, and so those need to be updated. Got to be thorough here.
|
| 91/2 |
2026/06/30 14:46 |
tool |
read-file |
0m
Args: map[File:fs/jfs/jfs_dmap.c FirstLine:3278 LineCount:40]
Results: map[Contents:3278: int dbAllocBottomUp(struct inode *ip, s64 blkno, s64 nblocks)
3279: {
3280: struct metapage *mp;
3281: struct dmap *dp;
3282: int nb, rc;
3283: s64 lblkno, rem;
3284: struct inode *ipbmap = JFS_SBI(ip->i_sb)->ipbmap;
3285: struct bmap *bmp = JFS_SBI(ip->i_sb)->bmap;
3286:
3287: IREAD_LOCK(ipbmap, RDWRLOCK_DMAP);
3288:
3289: /* block to be allocated better be within the mapsize. */
3290: ASSERT(nblocks <= bmp->db_mapsize - blkno);
3291:
3292: /*
3293: * allocate the blocks a dmap at a time.
3294: */
3295: mp = NULL;
3296: for (rem = nblocks; rem > 0; rem -= nb, blkno += nb) {
3297: /* release previous dmap if any */
3298: if (mp) {
3299: write_metapage(mp);
3300: }
3301:
3302: /* get the buffer for the current dmap. */
3303: lblkno = BLKTODMAP(blkno, bmp->db_l2nbperpage);
3304: mp = read_metapage(ipbmap, lblkno, PSIZE, 0);
3305: if (mp == NULL) {
3306: IREAD_UNLOCK(ipbmap);
3307: return -EIO;
3308: }
3309: dp = (struct dmap *) mp->data;
3310:
3311: /* determine the number of blocks to be allocated from
3312: * this dmap.
3313: */
3314: nb = min(rem, BPERDMAP - (blkno & (BPERDMAP - 1)));
3315:
3316: /* allocate the blocks. */
3317: if ((rc = dbAllocDmapBU(bmp, dp, blkno, nb))) {
]
|
| 92/2 |
2026/06/30 14:46 |
llm |
debugger |
0m
Model:
Tokens:
input: 71488
output: 41
thoughts: 28
Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience:
**Tracing Down Allocation Strategies**
Right, so `dbAllocBottomUp` is the starting point here. Seems like it's delegating to `dbAllocDmapBU` – that's the key function to dissect. I need to understand the specifics of how `dbAllocDmapBU` is handling its allocation strategy. Is it using a dynamic memory allocation scheme, or something more specialized? Is it a bottom-up approach? What data structures are involved in its Dmap process? It's time to dig into the internals of `dbAllocDmapBU`. I need to assess its performance characteristics.
|
| 93/2 |
2026/06/30 14:46 |
tool |
read-file |
0m
Args: map[File:fs/jfs/jfs_dmap.c FirstLine:3335 LineCount:20]
Results: map[Contents:3335: {
3336: int rc;
3337: int dbitno, word, rembits, nb, nwords, wbitno, agno;
3338: s8 oldroot;
3339: struct dmaptree *tp = (struct dmaptree *) & dp->tree;
3340:
3341: /* save the current value of the root (i.e. maximum free string)
3342: * of the dmap tree.
3343: */
3344: oldroot = tp->stree[ROOT];
3345:
3346: /* determine the bit number and word within the dmap of the
3347: * starting block.
3348: */
3349: dbitno = blkno & (BPERDMAP - 1);
3350: word = dbitno >> L2DBWORD;
3351:
3352: /* block range better be within the dmap */
3353: assert(dbitno + nblocks <= BPERDMAP);
3354:
]
|
| 94/2 |
2026/06/30 14:46 |
llm |
debugger |
0m
Model:
Tokens:
input: 72009
output: 41
thoughts: 180
Thoughts: **My Analysis of Dmap Tree Consistency**
Okay, let's break this down. It looks like `dbAllocDmapBU` utilizes `tp->stree[ROOT]` and then calls `dbJoin`. Since `dbJoin` is involved, and we want to ensure the integrity of the dmap tree, it makes sense that `dbAllocDmapBU` should also incorporate the `check_dmaptree` function.
Actually, it makes more sense to consolidate the `check_dmaptree` call across all the functions that interact with the dmap, to reduce code duplication and centralize the check. So, that means adding the `check_dmaptree` call to `dbFreeDmap`, `dbAllocNext`, `dbAllocNear`, `dbAllocDmapLev`, `dbAllocDmapBU`, and `dbExtendFS`, since they all use `dmap`.
Hold on... what about `dbUpdatePMap`? It reads from `dmap` and updates `dp->pmap`. But the critical point is that it *doesn't* rely on `dp->tree`. Therefore, it should be safe from potential corruption related to the `dp->tree` structure, since it's not actually using the tree. That's a good relief.
Wait a second, there's `dbExtendFS`. I recall that one reads from `dmap` and then calls `dbAllocBits` directly. That's a direct interaction with the dmap. Okay, better double-check `dbExtendFS` carefully to be sure.
|
| 95/2 |
2026/06/30 14:46 |
tool |
read-file |
0m
Args: map[File:fs/jfs/jfs_dmap.c FirstLine:3645 LineCount:20]
Results: map[Contents:3645: /* read in dmap page: */
3646: mp = read_metapage(ipbmap, p,
3647: PSIZE, 0);
3648: if (mp == NULL)
3649: goto errout;
3650: n = min(nblocks, (s64)BPERDMAP - n);
3651: } else {
3652: /* assign/init dmap page */
3653: mp = read_metapage(ipbmap, p,
3654: PSIZE, 0);
3655: if (mp == NULL)
3656: goto errout;
3657:
3658: n = min_t(s64, nblocks, BPERDMAP);
3659: }
3660:
3661: dp = (struct dmap *) mp->data;
3662: *l0leaf = dbInitDmap(dp, blkno, n);
3663:
3664: bmp->db_nfree += n;
]
|
| 96/2 |
2026/06/30 14:46 |
llm |
debugger |
0m
Model:
Tokens:
input: 72871
output: 41
thoughts: 59
Thoughts: Here's my summary, as though I'm thinking through this process:
**Diving into `dbExtendFS` and `dbInitDmap`**
Okay, so `dbExtendFS` is calling `dbInitDmap`. That's the key starting point. I need to understand what `dbInitDmap` is actually *doing* to get the full picture. My immediate thought is, "What specifically does `dbInitDmap` initialize?". Ah, right, it initializes the `dmap` itself, which is a critical piece, and it *overwrites* `dp->tree`. So, any previous state, even if corrupted on disk, gets wiped out. That's good, that means we have a clean slate from which to work with! I don't need to be concerned about the integrity of the existing tree before the call to `dbInitDmap`. Now I should take a look at the code for `dbInitDmap` to understand *how* it's doing this to confirm that my mental model is correct.
|
| 97/2 |
2026/06/30 14:46 |
tool |
read-file |
0m
Args: map[File:fs/jfs/jfs_dmap.c FirstLine:3887 LineCount:20]
Results: map[Contents:3887: memset(&dp->pmap[w], 0, nw * 4);
3888:
3889: /* skip the words freed */
3890: nb = nw << L2DBWORD;
3891: w += nw;
3892: }
3893: }
3894:
3895: /*
3896: * mark bits following the range to be freed (non-existing
3897: * blocks) as allocated (ONES)
3898: */
3899:
3900: if (blkno == BPERDMAP)
3901: goto initTree;
3902:
3903: /* the first word beyond the end of existing blocks */
3904: w = blkno >> L2DBWORD;
3905:
3906: /* does nblocks fall on a 32-bit boundary ? */
]
|
| 98/2 |
2026/06/30 14:46 |
llm |
debugger |
0m
Model:
Tokens:
input: 73458
output: 41
thoughts: 454
Thoughts: **Ensuring the Safety of the Dmap Tree**
Okay, so `dbInitDmap` is calling `dbInitDmapTree`, which means `dp->tree` is properly initialized. That’s good, so `dbExtendFS` should be safe in this context. Now, I need to pinpoint everywhere the `dmap` structure and its `tree` component are actually being *used*. Let's see:
* `dbFreeDmap` (called by `dbFree`)
* `dbAllocNext` (called by `dbAlloc`, `dbExtend`)
* `dbAllocNear` (called by `dbAlloc`)
* `dbAllocDmapLev` (called by `dbAllocCtl`)
* `dbAllocDmapBU` (called by `dbAllocBottomUp`)
Hmm, wait a minute, what about `dbAllocBits`? It's called by both `dbAllocDmap` *and* `dbFreeDmap`. And `dbAllocDmap` itself is called by `dbAllocDmapLev`. So, if I ensure I'm using `check_dmaptree` in `dbAllocDmapLev`, then `dbAllocDmap` and, by extension, `dbAllocBits` are all protected. The same logic applies if I check the tree in `dbFreeDmap` - that would protect `dbFreeBits` and `dbAllocBits`. So, it looks like I need to integrate `check_dmaptree` in the following functions:
* `dbFreeDmap`
* `dbAllocNext`
* `dbAllocNear`
* `dbAllocDmapLev`
* `dbAllocDmapBU`
But hold on, `dbAllocNext` and `dbAllocNear` already contain this:
```c
if (dp->tree.leafidx != cpu_to_le32(LEAFIND)) {
jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmap page\n");
return -EIO;
}
```
That can be replaced with the `check_dmaptree` call: `if (unlikely(!check_dmaptree(&dp->tree))) return -EIO;`. And I'll need to add that same check to `dbFreeDmap`, `dbAllocDmapLev`, and `dbAllocDmapBU`.
Alright, let's start with `dbFreeDmap` and begin the implementation.
|
| 99/2 |
2026/06/30 14:46 |
tool |
read-file |
0m
Args: map[File:fs/jfs/jfs_dmap.c FirstLine:2164 LineCount:20]
Results: map[Contents:2164: static int dbFreeDmap(struct bmap * bmp, struct dmap * dp, s64 blkno,
2165: int nblocks)
2166: {
2167: s8 oldroot;
2168: int rc = 0, word;
2169:
2170: /* save the current value of the root (i.e. maximum free string)
2171: * of the dmap tree.
2172: */
2173: oldroot = dp->tree.stree[ROOT];
2174:
2175: /* free the specified (blocks) bits */
2176: rc = dbFreeBits(bmp, dp, blkno, nblocks);
2177:
2178: /* if error or the root has not changed, done. */
2179: if (rc || (dp->tree.stree[ROOT] == oldroot))
2180: return (rc);
2181:
2182: /* root changed. bubble the change up to the dmap control pages.
2183: * if the adjustment of the upper level control pages fails,
]
|
| 100/2 |
2026/06/30 14:46 |
llm |
debugger |
0m
Model:
Tokens:
input: 74826
output: 41
thoughts: 69
Thoughts: Here's my thought process, as requested:
**Assessing a Potential Optimization in `dbFreeDmap`**
Okay, so I'm looking at `dbFreeDmap`, and I'm thinking, "Could we short-circuit an error earlier?" The core idea is to add that check at the beginning: `if (unlikely(!check_dmaptree(&dp->tree))) return -EIO;`. That `unlikely` macro is good because this condition shouldn't happen often, so we don't want to slow down the common path.
And yeah, since `dbFreeDmap`'s return type is `int`, returning `-EIO` is perfectly valid to indicate an I/O error – that's clean and consistent. No problems there from a type-safety perspective. That's good.
Alright, let's just make sure this doesn't break anything. Before I commit to this change, I should probably check how `dbAllocDmapLev` is working and how it interacts with the rest of this code. So my next thought is, let's go see what's happening with that function.
|
| 101/2 |
2026/06/30 14:46 |
tool |
read-file |
0m
Args: map[File:fs/jfs/jfs_dmap.c FirstLine:2043 LineCount:20]
Results: map[Contents:2043: dbAllocDmapLev(struct bmap * bmp,
2044: struct dmap * dp, int nblocks, int l2nb, s64 * results)
2045: {
2046: s64 blkno;
2047: int leafidx, rc;
2048:
2049: /* can't be more than a dmaps worth of blocks */
2050: assert(l2nb <= L2BPERDMAP);
2051:
2052: /* search the tree within the dmap page for sufficient
2053: * free space. if sufficient free space is found, dbFindLeaf()
2054: * returns the index of the leaf at which free space was found.
2055: */
2056: if (dbFindLeaf((dmtree_t *) &dp->tree, l2nb, &leafidx, false))
2057: return -ENOSPC;
2058:
2059: if (leafidx < 0)
2060: return -EIO;
2061:
2062: /* determine the block number within the file system corresponding
]
|
| 102/2 |
2026/06/30 14:46 |
llm |
debugger |
0m
Model:
Tokens:
input: 75507
output: 41
thoughts: 48
Thoughts: Okay, here's my take on those thoughts, framed as my own:
**Analyzing `dbAllocDmapLev` and `dbAllocDmapBU` for Robustness**
Alright, I'm looking at `dbAllocDmapLev` and thinking about a potential safeguard. It occurs to me that adding `if (unlikely(!check_dmaptree(&dp->tree))) return -EIO;` right at the start of that function might be a good idea. This check would, ideally, quickly bail out with an I/O error if the dmap tree is somehow corrupt or inconsistent before any real allocation attempts are made. It's a sort of preemptive sanity check, right? Better to fail fast if something's fundamentally wrong.
Now, let's not just stop there. I need to see how that change impacts the system overall. Let's shift our focus to `dbAllocDmapBU`. I'll need to assess if this same check, or a similar one, should also be placed at the entry point of *that* function, too. My gut says it probably should, but I need to examine the specific logic of `dbAllocDmapBU` to be certain. Perhaps it calls the same underlying tree checking routine, in which case we may need to make sure the same checks can be run from each function's beginning. It's all about making sure we have consistency in our dmap integrity checks, and we should consider if `check_dmaptree` needs further scrutiny.
|
| 103/2 |
2026/06/30 14:46 |
tool |
read-file |
0m
Args: map[File:fs/jfs/jfs_dmap.c FirstLine:3334 LineCount:20]
Results: map[Contents:3334: int nblocks)
3335: {
3336: int rc;
3337: int dbitno, word, rembits, nb, nwords, wbitno, agno;
3338: s8 oldroot;
3339: struct dmaptree *tp = (struct dmaptree *) & dp->tree;
3340:
3341: /* save the current value of the root (i.e. maximum free string)
3342: * of the dmap tree.
3343: */
3344: oldroot = tp->stree[ROOT];
3345:
3346: /* determine the bit number and word within the dmap of the
3347: * starting block.
3348: */
3349: dbitno = blkno & (BPERDMAP - 1);
3350: word = dbitno >> L2DBWORD;
3351:
3352: /* block range better be within the dmap */
3353: assert(dbitno + nblocks <= BPERDMAP);
]
|
| 104/2 |
2026/06/30 14:46 |
llm |
debugger |
0m
Model:
Tokens:
input: 76227
output: 41
thoughts: 2178
Thoughts: **Refining the dmap Tree Corruption Check**
Okay, so I've been mulling over the best place to add `check_dmaptree` for dmap integrity. Initially, I thought about adding it at the start of `dbAllocDmapBU`. But then, I had a thought: why not integrate it with the metapage reads? That way, we'd catch corruption right after reading from disk, which is a very clean approach.
I then did a quick scan and saw that `read_metapage` is used in a few key places: `dbFree`, `dbUpdatePMap`, `dbAllocBottomUp`, `dbExtendFS`, `dbAlloc`, `dbExtend`, and `dbAllocCtl`. I quickly checked `dbExtendFS` and found it's safe because `dbInitDmap` overwrites the tree. The initial thought was to add the check to these six spots, making it explicit and catching issues immediately after disk reads. However, `dbAllocCtl` reads `dmap` in two locations.
Then I realized that I needed to revisit `dbUpdatePMap`. I noticed that `mp` is reused in the loop if `lblkno == lastlblkno`, and if it's reused, it doesn't need to be checked in the loop. The `mp` only needs to be checked after a new read. This would clean up the loop considerably.
However, after stepping back, I remembered that `dbAllocNext`, `dbAllocNear`, `dbAllocDmapLev`, `dbAllocDmapBU`, and `dbFreeDmap` are actually the ones that work with `dp->tree`. So, instead of adding checks everywhere `read_metapage` is called, I could just add `check_dmaptree` to *those* functions directly. This seemed cleaner and less error-prone.
So, I looked through the list and found the following:
* `dbFreeDmap`
* `dbAllocNext`
* `dbAllocNear`
* `dbAllocDmapLev`
* `dbAllocDmapBU`
These five functions are where it really matters. It's much more localized and concise. However, `dbUpdatePMap` is fine; it's using the pmap. I also checked that `dbFreeBits` and `dbAllocBits` are called by `dbFreeDmap` and `dbAllocDmapLev`.
Wait... I just recalled that `dbAllocCtl` calls `dbAllocBits` directly. I need to re-examine that. After revisiting `dbAllocCtl` I'm good to go. I think this approach is the most efficient and robust way to ensure dmap integrity.
|
| 105/2 |
2026/06/30 14:46 |
tool |
read-file |
0m
Args: map[File:fs/jfs/jfs_dmap.c FirstLine:1926 LineCount:30]
Results: map[Contents:1926: /* allocate the blocks dmap by dmap.
1927: */
1928: for (n = nblocks, b = blkno; n > 0; n -= nb, b += nb) {
1929: /* get the buffer for the dmap.
1930: */
1931: lblkno = BLKTODMAP(b, bmp->db_l2nbperpage);
1932: mp = read_metapage(bmp->db_ipbmap, lblkno, PSIZE, 0);
1933: if (mp == NULL) {
1934: rc = -EIO;
1935: goto backout;
1936: }
1937: dp = (struct dmap *) mp->data;
1938:
1939: /* the dmap better be all free.
1940: */
1941: if (dp->tree.stree[ROOT] != L2BPERDMAP) {
1942: release_metapage(mp);
1943: jfs_error(bmp->db_ipbmap->i_sb,
1944: "the dmap is not all free\n");
1945: rc = -EIO;
1946: goto backout;
1947: }
1948:
1949: /* determine how many blocks to allocate from this dmap.
1950: */
1951: nb = min_t(s64, n, BPERDMAP);
1952:
1953: /* allocate the blocks from the dmap.
1954: */
1955: if ((rc = dbAllocDmap(bmp, dp, b, nb))) {
]
|
| 106/2 |
2026/06/30 14:46 |
llm |
debugger |
0m
Model:
Tokens:
input: 79517
output: 41
thoughts: 27
Thoughts: Okay, here's my take on those thoughts, framed for an expert audience, and written as if they were my own:
**Investigating the Allocation Chain**
Right, so `dbAllocCtl` is clearly the entry point for allocation control, and it's calling `dbAllocDmap`. That's a good place to start digging. My initial thought process is to trace the allocation strategy, and the fact that `dbAllocCtl` is interacting with `dbAllocDmap` tells me that the primary method of allocating resources is a direct memory allocation via a Direct Memory Access Plan. I need to understand this part. I need to immediately understand the implications. Let's see what `dbAllocDmap` is all about. This is where the core allocation logic probably resides, and understanding this function is key to how the allocation strategies are implemented, and the interactions between the control and direct mapping plans. The allocation system is critical, so let's get a handle on `dbAllocDmap`.
|
| 107/2 |
2026/06/30 14:46 |
tool |
read-file |
0m
Args: map[File:fs/jfs/jfs_dmap.c FirstLine:2109 LineCount:20]
Results: map[Contents:2109: static int dbAllocDmap(struct bmap * bmp, struct dmap * dp, s64 blkno,
2110: int nblocks)
2111: {
2112: s8 oldroot;
2113: int rc;
2114:
2115: /* save the current value of the root (i.e. maximum free string)
2116: * of the dmap tree.
2117: */
2118: oldroot = dp->tree.stree[ROOT];
2119:
2120: /* allocate the specified (blocks) bits */
2121: dbAllocBits(bmp, dp, blkno, nblocks);
2122:
2123: /* if the root has not changed, done. */
2124: if (dp->tree.stree[ROOT] == oldroot)
2125: return (0);
2126:
2127: /* root changed. bubble the change up to the dmap control pages.
2128: * if the adjustment of the upper level control pages fails,
]
|
| 108/2 |
2026/06/30 14:46 |
llm |
debugger |
0m
Model:
Tokens:
input: 80107
output: 41
thoughts: 81
Thoughts: Okay, let's see, I'm tracking down this memory allocation path. Right, so `**dbAllocCtl and Dmap Allocation Review**`, I see that `dbAllocCtl` calls `dbAllocDmap`. That's good, so any checks I put in `dbAllocDmap` will automatically cover `dbAllocCtl`, excellent. Hold on a second, though... I need to be completely thorough here. I'm pretty sure I remember `dbAllocDmapLev` also utilizing `dbAllocDmap`. Let me just quickly confirm that... I should probably investigate `dbAllocDmapLev` directly to make absolutely certain. It would be a nightmare to miss a call path in a memory management routine.
|
| 109/2 |
2026/06/30 14:46 |
tool |
read-file |
0m
Args: map[File:fs/jfs/jfs_dmap.c FirstLine:2080 LineCount:20]
Results: map[Contents:2080:
2081:
2082: /*
2083: * NAME: dbAllocDmap()
2084: *
2085: * FUNCTION: adjust the disk allocation map to reflect the allocation
2086: * of a specified block range within a dmap.
2087: *
2088: * this routine allocates the specified blocks from the dmap
2089: * through a call to dbAllocBits(). if the allocation of the
2090: * block range causes the maximum string of free blocks within
2091: * the dmap to change (i.e. the value of the root of the dmap's
2092: * dmtree), this routine will cause this change to be reflected
2093: * up through the appropriate levels of the dmap control pages
2094: * by a call to dbAdjCtl() for the L0 dmap control page that
2095: * covers this dmap.
2096: *
2097: * PARAMETERS:
2098: * bmp - pointer to bmap descriptor
2099: * dp - pointer to dmap to allocate the block range from.
]
|
| 110/2 |
2026/06/30 14:46 |
llm |
debugger |
0m
Model:
Tokens:
input: 80724
output: 31
thoughts: 3588
Thoughts: **Dmap Tree Integrity Check Implementation**
Okay, so I've been tracing the allocation paths in the Dmap code, and I need to solidify how we're going to ensure the integrity of the Dmap tree. The initial thought was that `dbAllocDmapLev` calls `dbAllocDmap`, so if we check the tree in `dbAllocDmap` that should cover the bases. But then I saw that `dbAllocDmapLev` uses `dp->tree` *before* it calls `dbAllocDmap`. That's important. It means `dbAllocDmapLev` needs to perform this integrity check itself.
So, the key functions that interact with `dp->tree` before calling the Dmap functions are: `dbFreeDmap`, `dbAllocNext`, `dbAllocNear`, `dbAllocDmapLev`, `dbAllocDmapBU` and `dbAllocDmap`. I also need to verify that these functions are the entry points.
I then saw that `dbFreeBits` and `dbAllocBits` also use the `dp->tree`. `dbFreeBits` is called from `dbFreeDmap` and `dbAllocBits` is called by `dbAllocDmap` and `dbFreeDmap`. That reinforces the list. The `dbExtendFS` overwrites `dp->tree` and is not an entry point. `dbUpdatePMap` doesn't use `dp->tree`.
I've also looked at `dbAllocCtl` and `dbAllocDmap` interaction. We should add a check to `dbAllocDmap` because `dbAllocCtl` calls it.
I need to create a `check_dmaptree` function. Based on the existing `check_dmapctl` function, I can infer that the basic field checks, range checks on `nleafs`, `l2nleafs`, `height`, `budmin`, and the `stree` array, are going to be key. I need to make sure `nleafs` matches `l2nleafs`, that the `leafidx` is correct, that the `height` is within valid range (max 4). Also to check `budmin`, `leafidx + nleafs` within `TREESIZE`, and the values of the leaf nodes within a valid range.
Now, after carefully inspecting the existing code, I'm confident about the approach. We just replace the existing `leafidx != LEAFIND` check in `dbAllocNext` and `dbAllocNear` with the `check_dmaptree` function. I also need to make sure that the functions return `-EIO` if the tree is corrupt.
I'm ready to write the patch now.
|
| 111/2 |
2026/06/30 14:46 |
tool |
grepper |
0m
Args: map[Expression:#define.*BUDMIN PathPrefix:fs/jfs/]
Results: map[Output:fs/jfs/jfs_dmap.h-16-#define L2DBWORD 5 /* l2 # of blks covered by a mword */
fs/jfs/jfs_dmap.h:17:#define BUDMIN L2DBWORD /* max free string in a map word */
fs/jfs/jfs_dmap.h-18-#define BPERDMAP (LPERDMAP * DBWORD) /* num of blks per dmap */
--
fs/jfs/jfs_dmap.h=230=struct bmap {
--
fs/jfs/jfs_dmap.h-264-/* convert number of leafs to log2 leaf value */
fs/jfs/jfs_dmap.h:265:#define NLSTOL2BSZ(n) (31 - cntlz((n)) + BUDMIN)
fs/jfs/jfs_dmap.h-266-
]
|
| 112/2 |
2026/06/30 14:46 |
llm |
debugger |
0m
Model:
Tokens:
input: 85172
output: 1622
thoughts: 2184
Thoughts: **Diving into JFS Dmap Integrity**
Okay, so I've been dissecting this JFS file system code, specifically the dmap and buddy system implementation. My initial concern was about how the `dmaptree` structure is handled, and I'm seeing a potential vulnerability. Specifically, `BUDMIN` is equal to `L2DBWORD`, which is 5, but there's a check `dtp->budmin < BUDMIN` that needs to be valid. And the `dbInitDmapTree` sets `tp->budmin = BUDMIN;`, so this makes sense.
Essentially, the issue boils down to the fact that when a `dmap` is read from disk, the `leafidx` field is directly consumed without validation, and used to compute pointers within the `stree` array which can lead to an out of bounds memory access. We are dealing with signed values for `val`, with `val < -1` being a valid range. This means that we're dealing with values from -1, 0, 1, ... up to 31 as the maximum, which makes sense because it's a log2 value.
So, here's my understanding of the problem and the proposed solution:
**Root Cause:** The JFS uses a binary buddy system for free space management. The `dmap` and `dmapctl` structures on disk store the state of this buddy system. The issue arises because when the `dmap` is read from disk, the `leafidx` field (which indicates the starting index of leaves in the `stree` array) is directly trusted. If this `leafidx` is corrupted, the resulting pointer will point outside the `stree` array's boundaries. This corruption can be triggered in the function `dbFreeDmap`, because `dbJoin` is called to update the buddy system, where it relies on this corrupted `leafidx` to access the leaf nodes, resulting in an out-of-bounds access.
**Fix:** The fix is to introduce a function, which I'm calling `check_dmaptree`, to thoroughly validate the `dmaptree` structure when it's loaded from disk. This validation will be very similar to `check_dmapctl`, but specifically tailored to the `dmaptree` structure. It's essentially a sanity check to make sure that the data read from disk is internally consistent.
Here's the validation logic I'm planning:
* **Bounds Checks:** Make sure `nleafs` is within `LPERDMAP`, and `l2nleafs` is within `L2LPERDMAP`.
* **Power of Two Check:** `nleafs` must match `2` raised to the power of `l2nleafs`.
* **`leafidx` Validation:** `leafidx` should exactly be `LEAFIND`.
* **Height Check:** The `height` must be within a valid range (<= 4).
* **`budmin` Validation:** The `budmin` should be valid (>= `BUDMIN`).
* **`stree` Array Bounds:** Make sure the leaf nodes (indexed by `leafidx` and `nleafs`) fit within the `stree` array.
* **Leaf Node Value Range:** The values of the leaf nodes within the `stree` array must be within the valid range, meaning they're `>= NOFREE` and `<= 31`.
I'll be calling `check_dmaptree` in several crucial functions: `dbFreeDmap`, `dbAllocNext`, `dbAllocNear`, `dbAllocDmapLev`, `dbAllocDmapBU`, and `dbAllocDmap`. If `check_dmaptree` fails, I'll return `-EIO` to signal an I/O error and prevent further operations on the corrupted `dmap`, which will prevent the out of bounds memory access. This will replace existing partial checks for `leafidx` in `dbAllocNext` and `dbAllocNear` with a much more comprehensive validation routine.
I will be adding:
```c
static bool check_dmaptree(struct dmaptree *dtp)
{
u32 nleafs, l2nleafs, leafidx, height;
int i;
nleafs = le32_to_cpu(dtp->nleafs);
/* Check basic field ranges */
if (unlikely(nleafs > LPERDMAP)) {
jfs_err("dmaptree: invalid nleafs %u (max %u)",
nleafs, LPERDMAP);
return false;
}
l2nleafs
|
| 113/1 |
2026/06/30 14:46 |
agent |
history-explorer |
0m
Model:
Instruction:
You are an experienced Linux kernel developer researching prior art for fixing a kernel bug.
You are given a bug explanation. This explanation details the root cause of the bug resulting
from debugging, but does not provide the final fix strategy. Your goal is to explore how
similar bugs were fixed in the past in the same subsystem or files.
CRITICAL: Do NOT attempt to debug the issue further or write a patch for it yourself.
Your ONLY objective is to research and provide the necessary historical context.
Use the git-log tool with the Since parameter set to "3 years" to focus on recent history.
Search for commits that address issues with similar root causes (e.g. similar missing locks,
incorrect refcounting, or similar error path bugs) in the affected files.
Your final reply must summarize your findings: what idioms, locking rules, or common patterns
should be followed when writing a fix for this bug based on how previous similar bugs were addressed.
If you find no relevant past fixes, clearly state that.
Don't make assumptions about the kernel source code (it may be different from what you assume it is).
Extensively use the provided code access tools (codesearch-*, git-*, grepper, etc)
to examine the actual source code, and confirm any assumptions.
Prefer calling several tools at the same time to save round-trips.
Prompt:
The crash is:
------------[ cut here ]------------
UBSAN: array-index-out-of-bounds in /app/workdir/cache/src/7be5f73ee7b4d1978813accf8bb54cd892ccd800/fs/jfs/jfs_dmap.c:2867:24
index 4294967295 is out of range for type 's8[1365]' (aka 'signed char[1365]')
CPU: 0 UID: 0 PID: 123 Comm: jfsCommit Not tainted syzkaller #1 PREEMPT(full)
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
Call Trace:
<TASK>
dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120
ubsan_epilogue+0xa/0x30 lib/ubsan.c:233
__ubsan_handle_out_of_bounds+0xe8/0xf0 lib/ubsan.c:455
dbJoin+0xcc4/0xd60 fs/jfs/jfs_dmap.c:2867
dbFreeBits+0x4a2/0xd70 fs/jfs/jfs_dmap.c:2427
dbFreeDmap fs/jfs/jfs_dmap.c:2176 [inline]
dbFree+0x324/0x650 fs/jfs/jfs_dmap.c:485
txFreeMap+0x9e6/0xde0 fs/jfs/jfs_txnmgr.c:2517
xtTruncate+0xd16/0x2eb0 fs/jfs/jfs_xtree.c:2481
jfs_free_zero_link+0x35b/0x4c0 fs/jfs/namei.c:760
jfs_evict_inode+0x356/0x430 fs/jfs/inode.c:159
evict+0x624/0xb50 fs/inode.c:825
txLazyCommit fs/jfs/jfs_txnmgr.c:2666 [inline]
jfs_lazycommit+0x44c/0xac0 fs/jfs/jfs_txnmgr.c:2735
kthread+0x388/0x470 kernel/kthread.c:436
ret_from_fork+0x514/0xb70 arch/x86/kernel/process.c:158
ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245
</TASK>
---[ end trace ]---
The explanation of the root cause of the bug is:
An array-index-out-of-bounds crash occurs in `dbJoin` when the JFS filesystem attempts to free blocks using a corrupted `dmap` structure read from disk.
### Root Cause
The JFS filesystem uses a binary buddy system to manage free space. The state of the buddy system is stored in `dmap` (for leaf levels) and `dmapctl` (for upper levels) structures on disk.
When a `dmap` is read from disk, its `dmaptree` structure is not fully validated. Specifically, the `leafidx` field (which indicates the index of the first leaf in the `stree` array) is read directly from disk and used to compute pointers to the leaf nodes.
If `leafidx` is corrupted (e.g., set to `0xffffffff` / `-1`), the computed pointer will point out of bounds of the `stree` array. When `dbFreeDmap` is called to free blocks, it calls `dbFreeBits`, which in turn calls `dbJoin` to update the buddy system. `dbJoin` uses the corrupted `leafidx` to access the leaf nodes:
```c
// fs/jfs/jfs_dmap.c:2867
leaf = tp->dmt_stree + le32_to_cpu(tp->dmt_leafidx);
```
This results in an out-of-bounds memory access when `leaf[buddy]` is subsequently accessed.
While a similar integrity check (`check_dmapctl`) was recently added for `dmapctl` structures in commit `cce219b203c4` ("jfs: add dmapctl integrity check to prevent invalid operations"), the `dmap` structures (`dmaptree`) were left unchecked.
### Fix
Introduce a `check_dmaptree` function, similar to the existing `check_dmapctl` function, to validate the integrity of the `dmaptree` structure when it is used.
The `check_dmaptree` function should verify that:
- `nleafs` is within bounds (`<= LPERDMAP`).
- `l2nleafs` is within bounds (`<= L2LPERDMAP`).
- `nleafs` matches `2^l2nleafs`.
- `leafidx` is exactly `LEAFIND`.
- `height` is within bounds (`<= 4`).
- `budmin` is valid (`>= BUDMIN`).
- The leaf nodes fit within the `stree` array (`leafidx + nleafs <= TREESIZE`).
- The values of the leaf nodes are valid (`>= NOFREE` and `<= 31`).
Call `check_dmaptree` in the functions that operate on the `dmap` structure, such as `dbFreeDmap`, `dbAllocNext`, `dbAllocNear`, `dbAllocDmapLev`, `dbAllocDmapBU`, and `dbAllocDmap`. If `check_dmaptree` fails, return `-EIO` to prevent further processing of the corrupted `dmap`. This will also replace the existing partial checks for `leafidx` in `dbAllocNext` and `dbAllocNear`.
```c
/*
* check_dmaptree - Validate integrity of a dmaptree structure
* @dtp: Pointer to the dmaptree structure to check
*
* Return: true if valid, false if corrupted
*/
static bool check_dmaptree(struct dmaptree *dtp)
{
u32 nleafs, l2nleafs, leafidx, height;
int i;
nleafs = le32_to_cpu(dtp->nleafs);
/* Check basic field ranges */
if (unlikely(nleafs > LPERDMAP)) {
jfs_err("dmaptree: invalid nleafs %u (max %u)",
nleafs, LPERDMAP);
return false;
}
l2nleafs = le32_to_cpu(dtp->l2nleafs);
if (unlikely(l2nleafs > L2LPERDMAP)) {
jfs_err("dmaptree: invalid l2nleafs %u (max %u)",
l2nleafs, L2LPERDMAP);
return false;
}
/* Verify nleafs matches l2nleafs (must be power of two) */
if (unlikely((1U << l2nleafs) != nleafs)) {
jfs_err("dmaptree: nleafs %u != 2^%u",
nleafs, l2nleafs);
return false;
}
leafidx = le32_to_cpu(dtp->leafidx);
/* Check leaf index matches expected position */
if (unlikely(leafidx != LEAFIND)) {
jfs_err("dmaptree: invalid leafidx %u (expected %u)",
leafidx, LEAFIND);
return false;
}
height = le32_to_cpu(dtp->height);
/* Check tree height is within valid range */
if (unlikely(height > 4)) {
jfs_err("dmaptree: invalid height %u (max 4)",
height);
return false;
}
/* Check budmin is valid */
if (unlikely(dtp->budmin < BUDMIN)) {
jfs_err("dmaptree: invalid budmin %d (min %d)",
dtp->budmin, BUDMIN);
return false;
}
/* Check leaf nodes fit within stree array */
if (unlikely(leafidx + nleafs > TREESIZE)) {
jfs_err("dmaptree: leaf range exceeds stree size (end %u > %u)",
leafidx + nleafs, TREESIZE);
return false;
}
/* Check leaf nodes have valid values */
for (i = leafidx; i < leafidx + nleafs; i++) {
s8 val = dtp->stree[i];
if (unlikely(val < NOFREE)) {
jfs_err("dmaptree: invalid leaf value %d at index %d",
val, i);
return false;
} else if (unlikely(val > 31)) {
jfs_err("dmaptree: leaf value %d too large at index %d", val, i);
return false;
}
}
return true;
}
```
Then, in functions like `dbFreeDmap`, `dbAllocNext`, `dbAllocNear`, `dbAllocDmapLev`, `dbAllocDmapBU`, and `dbAllocDmap`, add the check at the beginning:
```c
if (unlikely(!check_dmaptree(&dp->tree))) {
jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmap page\n");
return -EIO;
}
```
Reply: Based on the research into how similar bugs were addressed in the JFS subsystem (specifically in `fs/jfs/jfs_dmap.c`), here is a summary of the idioms, locking rules, and common patterns that should be followed when writing a fix for this bug:
### 1. Dedicated Validation Function
The standard idiom for preventing out-of-bounds accesses and invalid operations due to corrupted on-disk metadata is to introduce a dedicated, standalone validation function.
- For example, commit `cce219b203c4` ("jfs: add dmapctl integrity check to prevent invalid operations") introduced `check_dmapctl()`.
- Following this pattern, introducing `check_dmaptree(struct dmaptree *dtp)` is the correct approach. The function should return a boolean (`true` if valid, `false` if corrupted).
### 2. Comprehensive Field Checks and Detailed Logging
The validation function must thoroughly verify the bounds and internal consistency of the structure's fields before they are used in any calculations or array indexing.
- **Consistency:** Ensure fields like `nleafs` match `2^l2nleafs`.
- **Bounds:** Check that `leafidx`, `height`, and `budmin` are within their defined macros/limits (e.g., `LEAFIND`, `BUDMIN`, `TREESIZE`).
- **Detailed Logging:** Inside the validation function, use `jfs_err()` to log the specific field that failed validation, including the corrupted value and the expected limit. This aids in debugging disk corruption issues.
### 3. Caller-Side Handling and Error Propagation
Functions that operate on the `dmap` structure (such as `dbFreeDmap`, `dbAllocNext`, `dbAllocNear`, `dbAllocDmapLev`, `dbAllocDmapBU`, and `dbAllocDmap`) should invoke the validation function as early as possible.
- **Branch Prediction:** Wrap the check in the `unlikely()` macro, as corruption is the exceptional case:
```c
if (unlikely(!check_dmaptree(&dp->tree))) { ... }
```
- **Generic Error Logging:** If the check fails, the caller should log a generic filesystem error using `jfs_error()`. The superblock pointer can typically be accessed via the bmap descriptor: `jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmap page\n");`.
- **Abort Operation:** Return `-EIO` immediately to halt further processing and prevent the out-of-bounds memory access.
- **Resource Cleanup:** While the specific functions mentioned (`dbFreeDmap`, `dbAllocNext`, etc.) do not directly hold a metapage reference that needs releasing at their entry points, the general pattern (seen in `dbAllocAG` and `dbFindCtl`) dictates that any held resources (like `release_metapage(mp)`) must be freed before returning `-EIO`.
### 4. Locking Rules
The integrity checks do not require any new locks. They should be performed synchronously within the existing locking context of the callers.
- Functions like `dbFreeDmap` and `dbAllocNext` are already serialized via `IREAD_LOCK(ipbmap)` or `IWRITE_LOCK(ipbmap)` upon entry.
- The validation simply acts as a safe, read-only guard rail before the code proceeds to modify the buddy system state or traverse the tree.
|
| 114/2 |
2026/06/30 14:46 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 6191
output: 40
thoughts: 217
Thoughts: **Investigating the JFS Kernel Bug**
Okay, so we've got an array-index-out-of-bounds bug in `dbJoin` within the JFS filesystem, and it looks like it's being triggered by a corrupted `dmap` structure retrieved from disk. My first instinct is to dig into the prior art here, to see how similar problems have been tackled.
The description mentions that they added an integrity check (`check_dmapctl`) for `dmapctl` structures fairly recently in commit `cce219b203c4`. That's a great lead. I need to pull up the code for that commit – I'll use `git log` to find it quickly and examine the implementation of `check_dmapctl`. My immediate goal is to understand the methodology behind that check. What kind of corruption was it designed to catch? What was the general approach?
Then I'll shift my search slightly. I'll need a new function called `check_dmaptree` and it looks like it needs to be used in `dbFreeDmap`, `dbAllocNext`, `dbAllocNear`, `dbAllocDmapLev`, `dbAllocDmapBU`, and `dbAllocDmap`. I will look for other similar fixes in `fs/jfs/jfs_dmap.c`, to see if there are any other integrity checks in this file. That will hopefully help me determine the appropriate implementation strategy for `check_dmaptree`. From there, I can analyze the code, understand the root cause of the `dbJoin` issue, and determine the optimal strategy for mitigation.
|
| 115/2 |
2026/06/30 14:46 |
tool |
git-log |
0m
Args: map[Count:10 PathPrefix:fs/jfs/jfs_dmap.c Since:3 years]
Results: map[Output:cce219b203c4 jfs: add dmapctl integrity check to prevent invalid operations
bf4afc53b77a Convert 'alloc_obj' family to use the new default GFP_KERNEL argument
69050f8d6d07 treewide: Replace kmalloc with kmalloc_obj for non-scalar types
856db3759202 jfs: fix metapage reference count leak in dbAllocCtl
c214006856ff jfs: upper bound check of tree index in dbAllocAG
37bfb464ddca jfs: validate AG parameters in dbMount() to prevent crashes
0d250b1c5248 fs/jfs: consolidate sanity checking in dbMount
ddf2846f22e8 jfs: add sanity check for agwidth in dbMount
7fcbf789629c fs/jfs: Prevent integer overflow in AG size calculation
70ca3246ad20 fs/jfs: cast inactags to s64 to prevent potential overflow
]
|
| 116/2 |
2026/06/30 14:46 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 7063
output: 52
thoughts: 44
Thoughts: Okay, here's my interpretation of those code snippets as if they were my own mental processes, keeping in mind my expertise:
**Diving into the Code: Initial Assessment**
Alright, let's get down to brass tacks. I need to take a look at these three specific code snippets: `cce219b203c4`, `c214006856ff`, and `37bfb464ddca`. Just seeing the identifiers, I'm already forming some initial hypotheses. Are these commit hashes? Function names? Specific data points? Context is key here. Given the format, my instincts lean towards thinking these are probably version control identifiers, perhaps Git commit SHAs, or unique identifiers within a larger system. However, until I have more context, I'm hesitant to make definitive judgements. My priority is to analyze the relationship, if any, between these three IDs. Are they related in sequence or purpose? Are there any obvious patterns or dependencies that jump out? The goal is to determine the scope of these particular areas of code and see if there are commonalities or relationships between them. Let's see what these snippets tell me.
|
| 117/2 |
2026/06/30 14:46 |
tool |
git-show |
0m
Args:
Results: map[Output:commit cce219b203c4b9cb445e910c7090d1f58af847c5
Author: Yun Zhou <yun.zhou@windriver.com>
Date: Fri Nov 28 23:51:50 2025 +0800
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>
diff --git a/fs/jfs/jfs_dmap.c b/fs/jfs/jfs_dmap.c
index 2abe8cc02ee6..a841cf21da7d 100644
--- a/fs/jfs/jfs_dmap.c
+++ b/fs/jfs/jfs_dmap.c
@@ -133,6 +133,93 @@ static const s8 budtab[256] = {
2, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -1
};
+/*
+ * check_dmapctl - Validate integrity of a dmapctl structure
+ * @dcp: Pointer to the dmapctl structure to check
+ *
+ * Return: true if valid, false if corrupted
+ */
+static bool check_dmapctl(struct dmapctl *dcp)
+{
+ s8 budmin = dcp->budmin;
+ u32 nleafs, l2nleafs, leafidx, height;
+ int i;
+
+ nleafs = le32_to_cpu(dcp->nleafs);
+ /* Check basic field ranges */
+ if (unlikely(nleafs > LPERCTL)) {
+ jfs_err("dmapctl: invalid nleafs %u (max %u)",
+ nleafs, LPERCTL);
+ return false;
+ }
+
+ l2nleafs = le32_to_cpu(dcp->l2nleafs);
+ if (unlikely(l2nleafs > L2LPERCTL)) {
+ jfs_err("dmapctl: invalid l2nleafs %u (max %u)",
+ l2nleafs, L2LPERCTL);
+ return false;
+ }
+
+ /* Verify nleafs matches l2nleafs (must be power of two) */
+ if (unlikely((1U << l2nleafs) != nleafs)) {
+ jfs_err("dmapctl: nleafs %u != 2^%u",
+ nleafs, l2nleafs);
+ return false;
+ }
+
+ leafidx = le32_to_cpu(dcp->leafidx);
+ /* Check leaf index matches expected position */
+ if (unlikely(leafidx != CTLLEAFIND)) {
+ jfs_err("dmapctl: invalid leafidx %u (expected %u)",
+ leafidx, CTLLEAFIND);
+ return false;
+ }
+
+ height = le32_to_cpu(dcp->height);
+ /* Check tree height is within valid range */
+ if (unlikely(height > (L2LPERCTL >> 1))) {
+ jfs_err("dmapctl: invalid height %u (max %u)",
+ height, L2LPERCTL >> 1);
+ return false;
+ }
+
+ /* Check budmin is valid (cannot be NOFREE for non-empty tree) */
+ if (budmin == NOFREE) {
+ if (unlikely(nleafs > 0)) {
+ jfs_err("dmapctl: budmin is NOFREE but nleafs %u",
+ nleafs);
+ return false;
+ }
+ } else if (unlikely(budmin < BUDMIN)) {
+ jfs_err("dmapctl: invalid budmin %d (min %d)",
+ budmin, BUDMIN);
+ return false;
+ }
+
+ /* Check leaf nodes fit within stree array */
+ if (unlikely(leafidx + nleafs > CTLTREESIZE)) {
+ jfs_err("dmapctl: leaf range exceeds stree size (end %u > %u)",
+ leafidx + nleafs, CTLTREESIZE);
+ return false;
+ }
+
+ /* Check leaf nodes have valid values */
+ for (i = leafidx; i < leafidx + nleafs; i++) {
+ s8 val = dcp->stree[i];
+
+ if (unlikely(val < NOFREE)) {
+ jfs_err("dmapctl: invalid leaf value %d at index %d",
+ val, i);
+ return false;
+ } else if (unlikely(val > 31)) {
+ jfs_err("dmapctl: leaf value %d too large at index %d", val, i);
+ return false;
+ }
+ }
+
+ return true;
+}
+
/*
* NAME: dbMount()
*
@@ -1372,7 +1459,7 @@ dbAllocAG(struct bmap * bmp, int agno, s64 nblocks, int l2nb, s64 * results)
dcp = (struct dmapctl *) mp->data;
budmin = dcp->budmin;
- if (dcp->leafidx != cpu_to_le32(CTLLEAFIND)) {
+ if (unlikely(!check_dmapctl(dcp))) {
jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmapctl page\n");
release_metapage(mp);
return -EIO;
@@ -1702,7 +1789,7 @@ static int dbFindCtl(struct bmap * bmp, int l2nb, int level, s64 * blkno)
dcp = (struct dmapctl *) mp->data;
budmin = dcp->budmin;
- if (dcp->leafidx != cpu_to_le32(CTLLEAFIND)) {
+ if (unlikely(!check_dmapctl(dcp))) {
jfs_error(bmp->db_ipbmap->i_sb,
"Corrupt dmapctl page\n");
release_metapage(mp);
@@ -2485,7 +2572,7 @@ dbAdjCtl(struct bmap * bmp, s64 blkno, int newval, int alloc, int level)
return -EIO;
dcp = (struct dmapctl *) mp->data;
- if (dcp->leafidx != cpu_to_le32(CTLLEAFIND)) {
+ if (unlikely(!check_dmapctl(dcp))) {
jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmapctl page\n");
release_metapage(mp);
return -EIO;
@@ -3454,6 +3541,11 @@ int dbExtendFS(struct inode *ipbmap, s64 blkno, s64 nblocks)
return -EIO;
}
l2dcp = (struct dmapctl *) l2mp->data;
+ if (unlikely(!check_dmapctl(l2dcp))) {
+ jfs_error(ipbmap->i_sb, "Corrupt dmapctl page\n");
+ release_metapage(l2mp);
+ return -EIO;
+ }
/* compute start L1 */
k = blkno >> L2MAXL1SIZE;
@@ -3471,6 +3563,10 @@ int dbExtendFS(struct inode *ipbmap, s64 blkno, s64 nblocks)
if (l1mp == NULL)
goto errout;
l1dcp = (struct dmapctl *) l1mp->data;
+ if (unlikely(!check_dmapctl(l1dcp))) {
+ jfs_error(ipbmap->i_sb, "Corrupt dmapctl page\n");
+ goto errout;
+ }
/* compute start L0 */
j = (blkno & (MAXL1SIZE - 1)) >> L2MAXL0SIZE;
@@ -3484,6 +3580,10 @@ int dbExtendFS(struct inode *ipbmap, s64 blkno, s64 nblocks)
goto errout;
l1dcp = (struct dmapctl *) l1mp->data;
+ if (unlikely(!check_dmapctl(l1dcp))) {
+ jfs_error(ipbmap->i_sb, "Corrupt dmapctl page\n");
+ goto errout;
+ }
/* compute start L0 */
j = 0;
@@ -3503,6 +3603,10 @@ int dbExtendFS(struct inode *ipbmap, s64 blkno, s64 nblocks)
if (l0mp == NULL)
goto errout;
l0dcp = (struct dmapctl *) l0mp->data;
+ if (unlikely(!check_dmapctl(l0dcp))) {
+ jfs_error(ipbmap->i_sb, "Corrupt dmapctl page\n");
+ goto errout;
+ }
/* compute start dmap */
i = (blkno & (MAXL0SIZE - 1)) >>
@@ -3518,6 +3622,10 @@ int dbExtendFS(struct inode *ipbmap, s64 blkno, s64 nblocks)
goto errout;
l0dcp = (struct dmapctl *) l0mp->data;
+ if (unlikely(!check_dmapctl(l0dcp))) {
+ jfs_error(ipbmap->i_sb, "Corrupt dmapctl page\n");
+ goto errout;
+ }
/* compute start dmap */
i = 0;
]
|
| 118/2 |
2026/06/30 14:46 |
tool |
git-show |
0m
Args:
Results: map[Output:commit c214006856ff52a8ff17ed8da52d50601d54f9ce
Author: Arnaud Lecomte <contact@arnaud-lcm.com>
Date: Thu Apr 24 00:13:51 2025 +0200
jfs: upper bound check of tree index in dbAllocAG
When computing the tree index in dbAllocAG, we never check if we are
out of bounds realative to the size of the stree.
This could happen in a scenario where the filesystem metadata are
corrupted.
Reported-by: syzbot+cffd18309153948f3c3e@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=cffd18309153948f3c3e
Tested-by: syzbot+cffd18309153948f3c3e@syzkaller.appspotmail.com
Signed-off-by: Arnaud Lecomte <contact@arnaud-lcm.com>
Signed-off-by: Dave Kleikamp <dave.kleikamp@oracle.com>
diff --git a/fs/jfs/jfs_dmap.c b/fs/jfs/jfs_dmap.c
index 35e063c9f3a4..5080d59089bd 100644
--- a/fs/jfs/jfs_dmap.c
+++ b/fs/jfs/jfs_dmap.c
@@ -1389,6 +1389,12 @@ dbAllocAG(struct bmap * bmp, int agno, s64 nblocks, int l2nb, s64 * results)
(1 << (L2LPERCTL - (bmp->db_agheight << 1))) / bmp->db_agwidth;
ti = bmp->db_agstart + bmp->db_agwidth * (agno & (agperlev - 1));
+ if (ti < 0 || ti >= le32_to_cpu(dcp->nleafs)) {
+ jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmapctl page\n");
+ release_metapage(mp);
+ return -EIO;
+ }
+
/* dmap control page trees fan-out by 4 and a single allocation
* group may be described by 1 or 2 subtrees within the ag level
* dmap control page, depending upon the ag size. examine the ag's
]
|
| 119/2 |
2026/06/30 14:46 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 13176
output: 31
thoughts: 116
Thoughts: Okay, let's break this down.
**Analyzing the JFS Integrity Fixes**
Alright, so commit `cce219b203c4` caught my attention. It introduced `check_dmapctl` to validate `dmapctl` structures, which is a good step towards improving JFS's robustness. Now, the note mentions that the `dmaptree` fix mirrors this approach. My gut tells me this is likely referring to ensuring the integrity of the `dmaptree` structures associated with the dmap, which is critical for preventing corruption and invalid operations, especially during crucial file system operations. The introduction of a `check_dmaptree` function and its invocation within dmap-manipulating functions makes perfect sense. To get a better understanding of its application, I think checking `fs/jfs/jfs_dmap.c` will provide the most information on how and where this new integrity check is being implemented and to what degree. Knowing the functions that are modified will give some idea of where there might have been the chance for invalid structures in the past.
|
| 120/2 |
2026/06/30 14:46 |
tool |
codesearch-file-index |
0m
Args: map[SourceFile:fs/jfs/jfs_dmap.c]
Results: map[Entities:[map[Kind:function Name:blkstol2] map[Kind:function Name:check_dmapctl] map[Kind:function Name:cntlz] map[Kind:function Name:cnttz] map[Kind:function Name:dbAdjCtl] map[Kind:function Name:dbAdjTree] map[Kind:function Name:dbAlloc] map[Kind:function Name:dbAllocAG] map[Kind:function Name:dbAllocAny] map[Kind:function Name:dbAllocBits] map[Kind:function Name:dbAllocBottomUp] map[Kind:function Name:dbAllocCtl] map[Kind:function Name:dbAllocDmap] map[Kind:function Name:dbAllocDmapBU] map[Kind:function Name:dbAllocDmapLev] map[Kind:function Name:dbAllocNear] map[Kind:function Name:dbAllocNext] map[Kind:function Name:dbBackSplit] map[Kind:function Name:dbDiscardAG] map[Kind:function Name:dbExtend] map[Kind:function Name:dbExtendFS] map[Kind:function Name:dbFinalizeBmap] map[Kind:function Name:dbFindBits] map[Kind:function Name:dbFindCtl] map[Kind:function Name:dbFindLeaf] map[Kind:function Name:dbFree] map[Kind:function Name:dbFreeBits] map[Kind:function Name:dbFreeDmap] map[Kind:function Name:dbGetL2AGSize] map[Kind:function Name:dbInitDmap] map[Kind:function Name:dbInitDmapCtl] map[Kind:function Name:dbInitDmapTree] map[Kind:function Name:dbInitTree] map[Kind:function Name:dbJoin] map[Kind:function Name:dbMapFileSizeToMapSize] map[Kind:function Name:dbMaxBud] map[Kind:function Name:dbMount] map[Kind:function Name:dbNextAG] map[Kind:function Name:dbReAlloc] map[Kind:function Name:dbSplit] map[Kind:function Name:dbSync] map[Kind:function Name:dbUnmount] map[Kind:function Name:dbUpdatePMap] map[Kind:struct Name:range2trim] map[Kind:global_variable Name:budtab]]]
|
| 121/2 |
2026/06/30 14:46 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 14218
output: 54
thoughts: 149
Thoughts: **Analyzing Past Fixes to Guide the Current Bug Resolution**
Okay, so the user wants me to dive into the history books, so to speak, and see how similar problems in this `fs/jfs` subsystem, specifically those related to dmap allocation, were handled before. My job is to distill these past experiences into actionable insights: which idioms were favored? Were there specific locking protocols that consistently proved effective? Are there recognizable patterns in how these issues were tackled? This should give us a good set of best practices to guide writing a reliable fix.
The current proposed solution, adding and calling `check_dmaptree` in several `dbAlloc` and `dbFree` functions (`dbFreeDmap`, `dbAllocNext`, `dbAllocNear`, `dbAllocDmapLev`, `dbAllocDmapBU`, and `dbAllocDmap`), sounds like a good starting point. I need to understand the rationale and context behind it. Let's start by scrutinizing the existing `check_dmapctl` function in `fs/jfs/jfs_dmap.c`. How is it used? What is its scope? This will give me a point of reference to build on as I look at these prior fixes.
|
| 122/2 |
2026/06/30 14:46 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:fs/jfs/jfs_dmap.c IncludeSnippetLines:2 Name:check_dmapctl SourceTreePrefix:]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:dbAdjCtl SourceFile:fs/jfs/jfs_dmap.c SourceLine:2575 SourceSnippet:2573: dcp = (struct dmapctl *) mp->data;
2574:
2575: if (unlikely(!check_dmapctl(dcp))) {
2576: jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmapctl page\n");
2577: release_metapage(mp);
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:dbAllocAG SourceFile:fs/jfs/jfs_dmap.c SourceLine:1462 SourceSnippet:1460: budmin = dcp->budmin;
1461:
1462: if (unlikely(!check_dmapctl(dcp))) {
1463: jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmapctl page\n");
1464: release_metapage(mp);
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:dbExtendFS SourceFile:fs/jfs/jfs_dmap.c SourceLine:3544 SourceSnippet:3542: }
3543: 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);
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:dbExtendFS SourceFile:fs/jfs/jfs_dmap.c SourceLine:3566 SourceSnippet:3564: goto errout;
3565: l1dcp = (struct dmapctl *) l1mp->data;
3566: if (unlikely(!check_dmapctl(l1dcp))) {
3567: jfs_error(ipbmap->i_sb, "Corrupt dmapctl page\n");
3568: goto errout;
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:dbExtendFS SourceFile:fs/jfs/jfs_dmap.c SourceLine:3583 SourceSnippet:3581:
3582: l1dcp = (struct dmapctl *) l1mp->data;
3583: if (unlikely(!check_dmapctl(l1dcp))) {
3584: jfs_error(ipbmap->i_sb, "Corrupt dmapctl page\n");
3585: goto errout;
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:dbExtendFS SourceFile:fs/jfs/jfs_dmap.c SourceLine:3606 SourceSnippet:3604: goto errout;
3605: l0dcp = (struct dmapctl *) l0mp->data;
3606: if (unlikely(!check_dmapctl(l0dcp))) {
3607: jfs_error(ipbmap->i_sb, "Corrupt dmapctl page\n");
3608: goto errout;
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:dbExtendFS SourceFile:fs/jfs/jfs_dmap.c SourceLine:3625 SourceSnippet:3623:
3624: l0dcp = (struct dmapctl *) l0mp->data;
3625: if (unlikely(!check_dmapctl(l0dcp))) {
3626: jfs_error(ipbmap->i_sb, "Corrupt dmapctl page\n");
3627: goto errout;
] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:dbFindCtl SourceFile:fs/jfs/jfs_dmap.c SourceLine:1792 SourceSnippet:1790: budmin = dcp->budmin;
1791:
1792: if (unlikely(!check_dmapctl(dcp))) {
1793: jfs_error(bmp->db_ipbmap->i_sb,
1794: "Corrupt dmapctl page\n");
]] TruncatedOutput:false]
|
| 123/2 |
2026/06/30 14:46 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 15947
output: 79
thoughts: 162
Thoughts: **Analyzing the dmapctl Corruption Check**
Okay, so I'm looking at this error handling pattern, and it's pretty straightforward. It boils down to this: `if (unlikely(!check_dmapctl(dcp))) { /* Error handling */ }`. I've seen this a million times – the code checks `check_dmapctl`, and if that's *unlikely* to be successful, it means there's a problem, likely corruption, with the `dmapctl` page. The error handling then involves logging the error, releasing the metapage, and returning `-EIO`. Standard stuff for signaling a critical I/O error.
The interesting bit is where this check *should* be added. The comment suggests we integrate the `check_dmaptree` function. Right, so I need to go through the code, looking at those functions mentioned: `dbFreeDmap`, `dbAllocNext`, `dbAllocNear`, `dbAllocDmapLev`, `dbAllocDmapBU`, and `dbAllocDmap`. I need to ensure the check is included in any of those functions that interact with the DMAP control structure, and I'll need to figure out where they are returning a value - `goto errout` or another similar method to propagate the error. My job now is to determine if this pattern is correctly implemented or if any functions are missing this critical check, and how they propagate the error.
|
| 124/2 |
2026/06/30 14:46 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/jfs/jfs_dmap.c Name:dbFreeDmap]
Results: map[SourceCode:2138: /*
2139: * NAME: dbFreeDmap()
2140: *
2141: * FUNCTION: adjust the disk allocation map to reflect the allocation
2142: * of a specified block range within a dmap.
2143: *
2144: * this routine frees the specified blocks from the dmap through
2145: * a call to dbFreeBits(). if the deallocation of the block range
2146: * causes the maximum string of free blocks within the dmap to
2147: * change (i.e. the value of the root of the dmap's dmtree), this
2148: * routine will cause this change to be reflected up through the
2149: * appropriate levels of the dmap control pages by a call to
2150: * dbAdjCtl() for the L0 dmap control page that covers this dmap.
2151: *
2152: * PARAMETERS:
2153: * bmp - pointer to bmap descriptor
2154: * dp - pointer to dmap to free the block range from.
2155: * blkno - starting block number of the block to be freed.
2156: * nblocks - number of blocks to be freed.
2157: *
2158: * RETURN VALUES:
2159: * 0 - success
2160: * -EIO - i/o error
2161: *
2162: * serialization: IREAD_LOCK(ipbmap) or IWRITE_LOCK(ipbmap) held on entry/exit;
2163: */
2164: static int dbFreeDmap(struct bmap * bmp, struct dmap * dp, s64 blkno,
2165: int nblocks)
2166: {
2167: s8 oldroot;
2168: int rc = 0, word;
2169:
2170: /* save the current value of the root (i.e. maximum free string)
2171: * of the dmap tree.
2172: */
2173: oldroot = dp->tree.stree[ROOT];
2174:
2175: /* free the specified (blocks) bits */
2176: rc = dbFreeBits(bmp, dp, blkno, nblocks);
2177:
2178: /* if error or the root has not changed, done. */
2179: if (rc || (dp->tree.stree[ROOT] == oldroot))
2180: return (rc);
2181:
2182: /* root changed. bubble the change up to the dmap control pages.
2183: * if the adjustment of the upper level control pages fails,
2184: * backout the deallocation.
2185: */
2186: if ((rc = dbAdjCtl(bmp, blkno, dp->tree.stree[ROOT], 0, 0))) {
2187: word = (blkno & (BPERDMAP - 1)) >> L2DBWORD;
2188:
2189: /* as part of backing out the deallocation, we will have
2190: * to back split the dmap tree if the deallocation caused
2191: * the freed blocks to become part of a larger binary buddy
2192: * system.
2193: */
2194: if (dp->tree.stree[word] == NOFREE)
2195: dbBackSplit((dmtree_t *)&dp->tree, word, false);
2196:
2197: dbAllocBits(bmp, dp, blkno, nblocks);
2198: }
2199:
2200: return (rc);
2201: }
SourceFile:fs/jfs/jfs_dmap.c]
|
| 125/2 |
2026/06/30 14:46 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/jfs/jfs_dmap.c Name:dbAllocNext]
Results: map[SourceCode:1139: /*
1140: * NAME: dbAllocNext()
1141: *
1142: * FUNCTION: attempt to allocate the blocks of the specified block
1143: * range within a dmap.
1144: *
1145: * PARAMETERS:
1146: * bmp - pointer to bmap descriptor
1147: * dp - pointer to dmap.
1148: * blkno - starting block number of the range.
1149: * nblocks - number of contiguous free blocks of the range.
1150: *
1151: * RETURN VALUES:
1152: * 0 - success
1153: * -ENOSPC - insufficient disk resources
1154: * -EIO - i/o error
1155: *
1156: * serialization: IREAD_LOCK(ipbmap) held on entry/exit;
1157: */
1158: static int dbAllocNext(struct bmap * bmp, struct dmap * dp, s64 blkno,
1159: int nblocks)
1160: {
1161: int dbitno, word, rembits, nb, nwords, wbitno, nw;
1162: int l2size;
1163: s8 *leaf;
1164: u32 mask;
1165:
1166: if (dp->tree.leafidx != cpu_to_le32(LEAFIND)) {
1167: jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmap page\n");
1168: return -EIO;
1169: }
1170:
1171: /* pick up a pointer to the leaves of the dmap tree.
1172: */
1173: leaf = dp->tree.stree + le32_to_cpu(dp->tree.leafidx);
1174:
1175: /* determine the bit number and word within the dmap of the
1176: * starting block.
1177: */
1178: dbitno = blkno & (BPERDMAP - 1);
1179: word = dbitno >> L2DBWORD;
1180:
1181: /* check if the specified block range is contained within
1182: * this dmap.
1183: */
1184: if (dbitno + nblocks > BPERDMAP)
1185: return -ENOSPC;
1186:
1187: /* check if the starting leaf indicates that anything
1188: * is free.
1189: */
1190: if (leaf[word] == NOFREE)
1191: return -ENOSPC;
1192:
1193: /* check the dmaps words corresponding to block range to see
1194: * if the block range is free. not all bits of the first and
1195: * last words may be contained within the block range. if this
1196: * is the case, we'll work against those words (i.e. partial first
1197: * and/or last) on an individual basis (a single pass) and examine
1198: * the actual bits to determine if they are free. a single pass
1199: * will be used for all dmap words fully contained within the
1200: * specified range. within this pass, the leaves of the dmap
1201: * tree will be examined to determine if the blocks are free. a
1202: * single leaf may describe the free space of multiple dmap
1203: * words, so we may visit only a subset of the actual leaves
1204: * corresponding to the dmap words of the block range.
1205: */
1206: for (rembits = nblocks; rembits > 0; rembits -= nb, dbitno += nb) {
1207: /* determine the bit number within the word and
1208: * the number of bits within the word.
1209: */
1210: wbitno = dbitno & (DBWORD - 1);
1211: nb = min(rembits, DBWORD - wbitno);
1212:
1213: /* check if only part of the word is to be examined.
1214: */
1215: if (nb < DBWORD) {
1216: /* check if the bits are free.
1217: */
1218: mask = (ONES << (DBWORD - nb) >> wbitno);
1219: if ((mask & ~le32_to_cpu(dp->wmap[word])) != mask)
1220: return -ENOSPC;
1221:
1222: word += 1;
1223: } else {
1224: /* one or more dmap words are fully contained
1225: * within the block range. determine how many
1226: * words and how many bits.
1227: */
1228: nwords = rembits >> L2DBWORD;
1229: nb = nwords << L2DBWORD;
1230:
1231: /* now examine the appropriate leaves to determine
1232: * if the blocks are free.
1233: */
1234: while (nwords > 0) {
1235: /* does the leaf describe any free space ?
1236: */
1237: if (leaf[word] < BUDMIN)
1238: return -ENOSPC;
1239:
1240: /* determine the l2 number of bits provided
1241: * by this leaf.
1242: */
1243: l2size =
1244: min_t(int, leaf[word], NLSTOL2BSZ(nwords));
1245:
1246: /* determine how many words were handled.
1247: */
1248: nw = BUDSIZE(l2size, BUDMIN);
1249:
1250: nwords -= nw;
1251: word += nw;
1252: }
1253: }
1254: }
1255:
1256: /* allocate the blocks.
1257: */
1258: return (dbAllocDmap(bmp, dp, blkno, nblocks));
1259: }
SourceFile:fs/jfs/jfs_dmap.c]
|
| 126/2 |
2026/06/30 14:46 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 19394
output: 39
thoughts: 123
Thoughts: **Analyzing the `dbAllocNext` Bug Fix**
Okay, so looking at this code snippet from `dbAllocNext`, it's clear we're dealing with a check for `leafidx`. Specifically, it appears to be ensuring that the `leafidx` field within the data structure (`dp->tree.leafidx`) matches a predefined expected value, `LEAFIND`. The check itself is straightforward: if it *doesn't* match, we log an error ("Corrupt dmap page") and return an I/O error (`-EIO`).
This is pretty much verbatim what the bug description mentioned, that this check is going to replace the partial checks. So, the bugfix explanation is on point. Now, to make sure this is comprehensive, I should also check `dbAllocNear` to confirm the code's consistency. Let's dig into that next.
|
| 127/2 |
2026/06/30 14:46 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/jfs/jfs_dmap.c Name:dbAllocNear]
Results: map[SourceCode:1262: /*
1263: * NAME: dbAllocNear()
1264: *
1265: * FUNCTION: attempt to allocate a number of contiguous free blocks near
1266: * a specified block (hint) within a dmap.
1267: *
1268: * starting with the dmap leaf that covers the hint, we'll
1269: * check the next four contiguous leaves for sufficient free
1270: * space. if sufficient free space is found, we'll allocate
1271: * the desired free space.
1272: *
1273: * PARAMETERS:
1274: * bmp - pointer to bmap descriptor
1275: * dp - pointer to dmap.
1276: * blkno - block number to allocate near.
1277: * nblocks - actual number of contiguous free blocks desired.
1278: * l2nb - log2 number of contiguous free blocks desired.
1279: * results - on successful return, set to the starting block number
1280: * of the newly allocated range.
1281: *
1282: * RETURN VALUES:
1283: * 0 - success
1284: * -ENOSPC - insufficient disk resources
1285: * -EIO - i/o error
1286: *
1287: * serialization: IREAD_LOCK(ipbmap) held on entry/exit;
1288: */
1289: static int
1290: dbAllocNear(struct bmap * bmp,
1291: struct dmap * dp, s64 blkno, int nblocks, int l2nb, s64 * results)
1292: {
1293: int word, lword, rc;
1294: s8 *leaf;
1295:
1296: if (dp->tree.leafidx != cpu_to_le32(LEAFIND)) {
1297: jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmap page\n");
1298: return -EIO;
1299: }
1300:
1301: leaf = dp->tree.stree + le32_to_cpu(dp->tree.leafidx);
1302:
1303: /* determine the word within the dmap that holds the hint
1304: * (i.e. blkno). also, determine the last word in the dmap
1305: * that we'll include in our examination.
1306: */
1307: word = (blkno & (BPERDMAP - 1)) >> L2DBWORD;
1308: lword = min(word + 4, LPERDMAP);
1309:
1310: /* examine the leaves for sufficient free space.
1311: */
1312: for (; word < lword; word++) {
1313: /* does the leaf describe sufficient free space ?
1314: */
1315: if (leaf[word] < l2nb)
1316: continue;
1317:
1318: /* determine the block number within the file system
1319: * of the first block described by this dmap word.
1320: */
1321: blkno = le64_to_cpu(dp->start) + (word << L2DBWORD);
1322:
1323: /* if not all bits of the dmap word are free, get the
1324: * starting bit number within the dmap word of the required
1325: * string of free bits and adjust the block number with the
1326: * value.
1327: */
1328: if (leaf[word] < BUDMIN)
1329: blkno +=
1330: dbFindBits(le32_to_cpu(dp->wmap[word]), l2nb);
1331:
1332: /* allocate the blocks.
1333: */
1334: if ((rc = dbAllocDmap(bmp, dp, blkno, nblocks)) == 0)
1335: *results = blkno;
1336:
1337: return (rc);
1338: }
1339:
1340: return -ENOSPC;
1341: }
SourceFile:fs/jfs/jfs_dmap.c]
|
| 128/2 |
2026/06/30 14:46 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 21005
output: 82
thoughts: 103
Thoughts: **Analyzing Potential Corruption in JFS Disk Allocation**
Okay, so I'm looking at `dbAllocNear` and I see that crucial `if` statement right away. It's checking if the `leafidx` within the `dp->tree` structure matches `LEAFIND`. A mismatch here, and we're immediately throwing a `-EIO` error with a "Corrupt dmap page" message. That's a clear red flag. This points directly to potential corruption in the dmap. I need to keep this in mind as I work to debug this.
Now, let's step back and consider how this `leafidx` could be getting messed up. To get a better grasp of the situation, I need to dig deeper into the allocation functions themselves: `dbAllocDmapLev`, `dbAllocDmapBU`, and `dbAllocDmap`. Understanding their individual roles will help me pinpoint where a discrepancy could originate and determine the root cause of this corruption. I'll focus on how they interact with `dp->tree` and specifically manipulate `leafidx`.
|
| 129/2 |
2026/06/30 14:46 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/jfs/jfs_dmap.c Name:dbAllocDmapLev]
Results: map[SourceCode:2016: /*
2017: * NAME: dbAllocDmapLev()
2018: *
2019: * FUNCTION: attempt to allocate a specified number of contiguous blocks
2020: * from a specified dmap.
2021: *
2022: * this routine checks if the contiguous blocks are available.
2023: * if so, nblocks of blocks are allocated; otherwise, ENOSPC is
2024: * returned.
2025: *
2026: * PARAMETERS:
2027: * mp - pointer to bmap descriptor
2028: * dp - pointer to dmap to attempt to allocate blocks from.
2029: * l2nb - log2 number of contiguous block desired.
2030: * nblocks - actual number of contiguous block desired.
2031: * results - on successful return, set to the starting block number
2032: * of the newly allocated range.
2033: *
2034: * RETURN VALUES:
2035: * 0 - success
2036: * -ENOSPC - insufficient disk resources
2037: * -EIO - i/o error
2038: *
2039: * serialization: IREAD_LOCK(ipbmap), e.g., from dbAlloc(), or
2040: * IWRITE_LOCK(ipbmap), e.g., dbAllocCtl(), held on entry/exit;
2041: */
2042: static int
2043: dbAllocDmapLev(struct bmap * bmp,
2044: struct dmap * dp, int nblocks, int l2nb, s64 * results)
2045: {
2046: s64 blkno;
2047: int leafidx, rc;
2048:
2049: /* can't be more than a dmaps worth of blocks */
2050: assert(l2nb <= L2BPERDMAP);
2051:
2052: /* search the tree within the dmap page for sufficient
2053: * free space. if sufficient free space is found, dbFindLeaf()
2054: * returns the index of the leaf at which free space was found.
2055: */
2056: if (dbFindLeaf((dmtree_t *) &dp->tree, l2nb, &leafidx, false))
2057: return -ENOSPC;
2058:
2059: if (leafidx < 0)
2060: return -EIO;
2061:
2062: /* determine the block number within the file system corresponding
2063: * to the leaf at which free space was found.
2064: */
2065: blkno = le64_to_cpu(dp->start) + (leafidx << L2DBWORD);
2066:
2067: /* if not all bits of the dmap word are free, get the starting
2068: * bit number within the dmap word of the required string of free
2069: * bits and adjust the block number with this value.
2070: */
2071: if (dp->tree.stree[leafidx + LEAFIND] < BUDMIN)
2072: blkno += dbFindBits(le32_to_cpu(dp->wmap[leafidx]), l2nb);
2073:
2074: /* allocate the blocks */
2075: if ((rc = dbAllocDmap(bmp, dp, blkno, nblocks)) == 0)
2076: *results = blkno;
2077:
2078: return (rc);
2079: }
SourceFile:fs/jfs/jfs_dmap.c]
|
| 130/2 |
2026/06/30 14:46 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/jfs/jfs_dmap.c Name:dbAllocDmapBU]
Results: map[SourceCode:3333: static int dbAllocDmapBU(struct bmap * bmp, struct dmap * dp, s64 blkno,
3334: int nblocks)
3335: {
3336: int rc;
3337: int dbitno, word, rembits, nb, nwords, wbitno, agno;
3338: s8 oldroot;
3339: struct dmaptree *tp = (struct dmaptree *) & dp->tree;
3340:
3341: /* save the current value of the root (i.e. maximum free string)
3342: * of the dmap tree.
3343: */
3344: oldroot = tp->stree[ROOT];
3345:
3346: /* determine the bit number and word within the dmap of the
3347: * starting block.
3348: */
3349: dbitno = blkno & (BPERDMAP - 1);
3350: word = dbitno >> L2DBWORD;
3351:
3352: /* block range better be within the dmap */
3353: assert(dbitno + nblocks <= BPERDMAP);
3354:
3355: /* allocate the bits of the dmap's words corresponding to the block
3356: * range. not all bits of the first and last words may be contained
3357: * within the block range. if this is the case, we'll work against
3358: * those words (i.e. partial first and/or last) on an individual basis
3359: * (a single pass), allocating the bits of interest by hand and
3360: * updating the leaf corresponding to the dmap word. a single pass
3361: * will be used for all dmap words fully contained within the
3362: * specified range. within this pass, the bits of all fully contained
3363: * dmap words will be marked as free in a single shot and the leaves
3364: * will be updated. a single leaf may describe the free space of
3365: * multiple dmap words, so we may update only a subset of the actual
3366: * leaves corresponding to the dmap words of the block range.
3367: */
3368: for (rembits = nblocks; rembits > 0; rembits -= nb, dbitno += nb) {
3369: /* determine the bit number within the word and
3370: * the number of bits within the word.
3371: */
3372: wbitno = dbitno & (DBWORD - 1);
3373: nb = min(rembits, DBWORD - wbitno);
3374:
3375: /* check if only part of a word is to be allocated.
3376: */
3377: if (nb < DBWORD) {
3378: /* allocate (set to 1) the appropriate bits within
3379: * this dmap word.
3380: */
3381: dp->wmap[word] |= cpu_to_le32(ONES << (DBWORD - nb)
3382: >> wbitno);
3383:
3384: word++;
3385: } else {
3386: /* one or more dmap words are fully contained
3387: * within the block range. determine how many
3388: * words and allocate (set to 1) the bits of these
3389: * words.
3390: */
3391: nwords = rembits >> L2DBWORD;
3392: memset(&dp->wmap[word], (int) ONES, nwords * 4);
3393:
3394: /* determine how many bits */
3395: nb = nwords << L2DBWORD;
3396: word += nwords;
3397: }
3398: }
3399:
3400: /* update the free count for this dmap */
3401: le32_add_cpu(&dp->nfree, -nblocks);
3402:
3403: /* reconstruct summary tree */
3404: dbInitDmapTree(dp);
3405:
3406: BMAP_LOCK(bmp);
3407:
3408: /* if this allocation group is completely free,
3409: * update the highest active allocation group number
3410: * if this allocation group is the new max.
3411: */
3412: agno = blkno >> bmp->db_agl2size;
3413: if (agno > bmp->db_maxag)
3414: bmp->db_maxag = agno;
3415:
3416: /* update the free count for the allocation group and map */
3417: bmp->db_agfree[agno] -= nblocks;
3418: bmp->db_nfree -= nblocks;
3419:
3420: BMAP_UNLOCK(bmp);
3421:
3422: /* if the root has not changed, done. */
3423: if (tp->stree[ROOT] == oldroot)
3424: return (0);
3425:
3426: /* root changed. bubble the change up to the dmap control pages.
3427: * if the adjustment of the upper level control pages fails,
3428: * backout the bit allocation (thus making everything consistent).
3429: */
3430: if ((rc = dbAdjCtl(bmp, blkno, tp->stree[ROOT], 1, 0)))
3431: dbFreeBits(bmp, dp, blkno, nblocks);
3432:
3433: return (rc);
3434: }
SourceFile:fs/jfs/jfs_dmap.c]
|
| 131/2 |
2026/06/30 14:46 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 24106
output: 40
thoughts: 60
Thoughts: Here's my summary, as the expert:
**Investigating Potential Data Structure Access Issues in DMAP Allocation**
Okay, so I'm looking into these DMAP allocation functions and it looks like `dbAllocDmapLev` is potentially problematic. I see it skips the `leafidx` check directly, but that could be offset if it's hitting `stree` through `dbFindLeaf`. I need to confirm that. And, I also noticed `dbAllocDmapBU` isn't doing the check either.
To get a clearer picture of how this is all working, I'm going to dig into `dbAllocDmap` itself. That seems like the logical next step to understand the flow and see what's really going on with `stree` access in these allocation routines. I need to know how these things are interacting, if at all, and whether there's a vulnerability or just an oversight. Let's see what `dbAllocDmap` reveals.
|
| 132/2 |
2026/06/30 14:46 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/jfs/jfs_dmap.c Name:dbAllocDmap]
Results: map[SourceCode:2082: /*
2083: * NAME: dbAllocDmap()
2084: *
2085: * FUNCTION: adjust the disk allocation map to reflect the allocation
2086: * of a specified block range within a dmap.
2087: *
2088: * this routine allocates the specified blocks from the dmap
2089: * through a call to dbAllocBits(). if the allocation of the
2090: * block range causes the maximum string of free blocks within
2091: * the dmap to change (i.e. the value of the root of the dmap's
2092: * dmtree), this routine will cause this change to be reflected
2093: * up through the appropriate levels of the dmap control pages
2094: * by a call to dbAdjCtl() for the L0 dmap control page that
2095: * covers this dmap.
2096: *
2097: * PARAMETERS:
2098: * bmp - pointer to bmap descriptor
2099: * dp - pointer to dmap to allocate the block range from.
2100: * blkno - starting block number of the block to be allocated.
2101: * nblocks - number of blocks to be allocated.
2102: *
2103: * RETURN VALUES:
2104: * 0 - success
2105: * -EIO - i/o error
2106: *
2107: * serialization: IREAD_LOCK(ipbmap) or IWRITE_LOCK(ipbmap) held on entry/exit;
2108: */
2109: static int dbAllocDmap(struct bmap * bmp, struct dmap * dp, s64 blkno,
2110: int nblocks)
2111: {
2112: s8 oldroot;
2113: int rc;
2114:
2115: /* save the current value of the root (i.e. maximum free string)
2116: * of the dmap tree.
2117: */
2118: oldroot = dp->tree.stree[ROOT];
2119:
2120: /* allocate the specified (blocks) bits */
2121: dbAllocBits(bmp, dp, blkno, nblocks);
2122:
2123: /* if the root has not changed, done. */
2124: if (dp->tree.stree[ROOT] == oldroot)
2125: return (0);
2126:
2127: /* root changed. bubble the change up to the dmap control pages.
2128: * if the adjustment of the upper level control pages fails,
2129: * backout the bit allocation (thus making everything consistent).
2130: */
2131: if ((rc = dbAdjCtl(bmp, blkno, dp->tree.stree[ROOT], 1, 0)))
2132: dbFreeBits(bmp, dp, blkno, nblocks);
2133:
2134: return (rc);
2135: }
SourceFile:fs/jfs/jfs_dmap.c]
|
| 133/2 |
2026/06/30 14:46 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 25319
output: 79
thoughts: 719
Thoughts: **Analyzing and Replicating a Proven Bug-Fix Pattern for dmap Corruption**
Okay, so the proposed solution is to add a `check_dmaptree` function and call it in several key dmap allocation and deallocation functions. Before diving into the implementation details of `check_dmaptree`, I need to ground this in past solutions for related issues within the JFS filesystem. The goal here is to leverage established patterns and idioms to ensure that the fix is robust and aligns with existing coding standards. This avoids introducing new, potentially unforeseen, problems.
I've reviewed `cce219b203c4` ("jfs: add dmapctl integrity check to prevent invalid operations") and `c214006856ff` ("jfs: upper bound check of tree index in dbAllocAG") as they seem most relevant. The `cce219b203c4` commit, in particular, offers a good template. It introduced a `check_dmapctl` function to validate a structure read from disk. The crucial aspect is the return value - a boolean indicating validity – and the handling of failures.
The `check_dmapctl` function performs a series of checks:
* Bounds checks on fields like `nleafs`, `l2nleafs`, `leafidx`, and `height`.
* Validity of `budmin`.
* Ensuring leaf nodes fit within the allocated `stree` array.
* Validation of leaf node values.
Importantly, the failure handling is consistent. When `check_dmapctl` returns `false`, indicating corruption, the code does the following:
1. Logs an error using `jfs_error`, providing context with the superblock (`sb`).
2. Releases the metapage if applicable.
3. Returns `-EIO` (or jumps to an error handling label) to signal a fatal I/O error.
The use of the `unlikely()` macro for the failure path (`if (unlikely(!check_dmapctl(dcp)))`) is also noteworthy, as this helps the compiler optimize code by considering the common case (valid dmap) to be the "likely" one.
Therefore, to address the `dmaptree` corruption, I propose the following plan, based on the `cce219b203c4` pattern:
1. Create `check_dmaptree` function returning a `bool` value.
2. Within `check_dmaptree`, perform similar integrity checks as in `check_dmapctl`: boundary validation of fields like `nleafs`, `l2nleafs`, `leafidx`, `height`, `budmin`, and validate the leaf node values.
3. Use `jfs_err` *inside* `check_dmaptree` to log specific validation failures; this adds granularity to the error reporting.
4. In the calling functions (`dbFreeDmap`, etc.), call `check_dmaptree` and check the return value using `unlikely(!check_dmaptree(&dp->tree))`.
5. If `check_dmaptree` returns `false`, log a general corruption error using `jfs_error`, using the superblock accessible through `bmp->db_ipbmap->i_sb` (or a similar construct) to provide context. It's important to verify that `bmp->db_ipbmap->i_sb` is accessible in all the affected functions.
6. Return `-EIO` to indicate a fatal I/O error and propagate it upwards.
So, this approach replicates the structure of the existing integrity checks, which will ensure that it is safe to use within the codebase.
|
| 134/2 |
2026/06/30 14:46 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/jfs/jfs_dmap.c Name:dbFreeDmap]
Results: map[SourceCode:2138: /*
2139: * NAME: dbFreeDmap()
2140: *
2141: * FUNCTION: adjust the disk allocation map to reflect the allocation
2142: * of a specified block range within a dmap.
2143: *
2144: * this routine frees the specified blocks from the dmap through
2145: * a call to dbFreeBits(). if the deallocation of the block range
2146: * causes the maximum string of free blocks within the dmap to
2147: * change (i.e. the value of the root of the dmap's dmtree), this
2148: * routine will cause this change to be reflected up through the
2149: * appropriate levels of the dmap control pages by a call to
2150: * dbAdjCtl() for the L0 dmap control page that covers this dmap.
2151: *
2152: * PARAMETERS:
2153: * bmp - pointer to bmap descriptor
2154: * dp - pointer to dmap to free the block range from.
2155: * blkno - starting block number of the block to be freed.
2156: * nblocks - number of blocks to be freed.
2157: *
2158: * RETURN VALUES:
2159: * 0 - success
2160: * -EIO - i/o error
2161: *
2162: * serialization: IREAD_LOCK(ipbmap) or IWRITE_LOCK(ipbmap) held on entry/exit;
2163: */
2164: static int dbFreeDmap(struct bmap * bmp, struct dmap * dp, s64 blkno,
2165: int nblocks)
2166: {
2167: s8 oldroot;
2168: int rc = 0, word;
2169:
2170: /* save the current value of the root (i.e. maximum free string)
2171: * of the dmap tree.
2172: */
2173: oldroot = dp->tree.stree[ROOT];
2174:
2175: /* free the specified (blocks) bits */
2176: rc = dbFreeBits(bmp, dp, blkno, nblocks);
2177:
2178: /* if error or the root has not changed, done. */
2179: if (rc || (dp->tree.stree[ROOT] == oldroot))
2180: return (rc);
2181:
2182: /* root changed. bubble the change up to the dmap control pages.
2183: * if the adjustment of the upper level control pages fails,
2184: * backout the deallocation.
2185: */
2186: if ((rc = dbAdjCtl(bmp, blkno, dp->tree.stree[ROOT], 0, 0))) {
2187: word = (blkno & (BPERDMAP - 1)) >> L2DBWORD;
2188:
2189: /* as part of backing out the deallocation, we will have
2190: * to back split the dmap tree if the deallocation caused
2191: * the freed blocks to become part of a larger binary buddy
2192: * system.
2193: */
2194: if (dp->tree.stree[word] == NOFREE)
2195: dbBackSplit((dmtree_t *)&dp->tree, word, false);
2196:
2197: dbAllocBits(bmp, dp, blkno, nblocks);
2198: }
2199:
2200: return (rc);
2201: }
SourceFile:fs/jfs/jfs_dmap.c]
|
| 135/2 |
2026/06/30 14:46 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/jfs/jfs_dmap.c Name:dbAllocNext]
Results: map[SourceCode:1139: /*
1140: * NAME: dbAllocNext()
1141: *
1142: * FUNCTION: attempt to allocate the blocks of the specified block
1143: * range within a dmap.
1144: *
1145: * PARAMETERS:
1146: * bmp - pointer to bmap descriptor
1147: * dp - pointer to dmap.
1148: * blkno - starting block number of the range.
1149: * nblocks - number of contiguous free blocks of the range.
1150: *
1151: * RETURN VALUES:
1152: * 0 - success
1153: * -ENOSPC - insufficient disk resources
1154: * -EIO - i/o error
1155: *
1156: * serialization: IREAD_LOCK(ipbmap) held on entry/exit;
1157: */
1158: static int dbAllocNext(struct bmap * bmp, struct dmap * dp, s64 blkno,
1159: int nblocks)
1160: {
1161: int dbitno, word, rembits, nb, nwords, wbitno, nw;
1162: int l2size;
1163: s8 *leaf;
1164: u32 mask;
1165:
1166: if (dp->tree.leafidx != cpu_to_le32(LEAFIND)) {
1167: jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmap page\n");
1168: return -EIO;
1169: }
1170:
1171: /* pick up a pointer to the leaves of the dmap tree.
1172: */
1173: leaf = dp->tree.stree + le32_to_cpu(dp->tree.leafidx);
1174:
1175: /* determine the bit number and word within the dmap of the
1176: * starting block.
1177: */
1178: dbitno = blkno & (BPERDMAP - 1);
1179: word = dbitno >> L2DBWORD;
1180:
1181: /* check if the specified block range is contained within
1182: * this dmap.
1183: */
1184: if (dbitno + nblocks > BPERDMAP)
1185: return -ENOSPC;
1186:
1187: /* check if the starting leaf indicates that anything
1188: * is free.
1189: */
1190: if (leaf[word] == NOFREE)
1191: return -ENOSPC;
1192:
1193: /* check the dmaps words corresponding to block range to see
1194: * if the block range is free. not all bits of the first and
1195: * last words may be contained within the block range. if this
1196: * is the case, we'll work against those words (i.e. partial first
1197: * and/or last) on an individual basis (a single pass) and examine
1198: * the actual bits to determine if they are free. a single pass
1199: * will be used for all dmap words fully contained within the
1200: * specified range. within this pass, the leaves of the dmap
1201: * tree will be examined to determine if the blocks are free. a
1202: * single leaf may describe the free space of multiple dmap
1203: * words, so we may visit only a subset of the actual leaves
1204: * corresponding to the dmap words of the block range.
1205: */
1206: for (rembits = nblocks; rembits > 0; rembits -= nb, dbitno += nb) {
1207: /* determine the bit number within the word and
1208: * the number of bits within the word.
1209: */
1210: wbitno = dbitno & (DBWORD - 1);
1211: nb = min(rembits, DBWORD - wbitno);
1212:
1213: /* check if only part of the word is to be examined.
1214: */
1215: if (nb < DBWORD) {
1216: /* check if the bits are free.
1217: */
1218: mask = (ONES << (DBWORD - nb) >> wbitno);
1219: if ((mask & ~le32_to_cpu(dp->wmap[word])) != mask)
1220: return -ENOSPC;
1221:
1222: word += 1;
1223: } else {
1224: /* one or more dmap words are fully contained
1225: * within the block range. determine how many
1226: * words and how many bits.
1227: */
1228: nwords = rembits >> L2DBWORD;
1229: nb = nwords << L2DBWORD;
1230:
1231: /* now examine the appropriate leaves to determine
1232: * if the blocks are free.
1233: */
1234: while (nwords > 0) {
1235: /* does the leaf describe any free space ?
1236: */
1237: if (leaf[word] < BUDMIN)
1238: return -ENOSPC;
1239:
1240: /* determine the l2 number of bits provided
1241: * by this leaf.
1242: */
1243: l2size =
1244: min_t(int, leaf[word], NLSTOL2BSZ(nwords));
1245:
1246: /* determine how many words were handled.
1247: */
1248: nw = BUDSIZE(l2size, BUDMIN);
1249:
1250: nwords -= nw;
1251: word += nw;
1252: }
1253: }
1254: }
1255:
1256: /* allocate the blocks.
1257: */
1258: return (dbAllocDmap(bmp, dp, blkno, nblocks));
1259: }
SourceFile:fs/jfs/jfs_dmap.c]
|
| 136/2 |
2026/06/30 14:46 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 29824
output: 24
thoughts: 207
Thoughts: **Analyzing Potential Integrity Checks and Error Handling in JFS**
Okay, so I'm observing that `bmp->db_ipbmap->i_sb` is accessible within all the functions we're looking at, which is a key piece of information. This is good; it's a common element, so it makes comparison easier. Now, I need to see if there are any other similar instances of this type of fix within JFS.
I recall seeing commit `c214006856ff`, specifically "jfs: upper bound check of tree index in dbAllocAG." Let's pull that up and see. Ah, yes, it added a critical integrity check:
```c
if (ti < 0 || ti >= le32_to_cpu(dcp->nleafs)) {
jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmapctl page\n");
release_metapage(mp);
return -EIO;
}
```
Perfect. This mirrors what we're aiming for. It's an integrity check, specifically targeting the upper bound of a tree index. If the index is out of bounds, it logs an error, uses the `bmp->db_ipbmap->i_sb` superblock pointer to help with logging, releases a metapage, and returns `-EIO`, which is the standard I/O error code. This is exactly the kind of approach that we need to investigate.
Then I also remember `37bfb464ddca` ("jfs: validate AG parameters in dbMount() to prevent crashes"). Now I'm interested to see how that was addressed. I need to understand that commit as well to see if there's any related methodology or reasoning that can inform my current analysis.
|
| 137/2 |
2026/06/30 14:46 |
tool |
git-show |
0m
Args:
Results: map[Output:commit 37bfb464ddca87f203071b5bd562cd91ddc0b40a
Author: Vasiliy Kovalev <kovalev@altlinux.org>
Date: Mon Mar 10 11:56:02 2025 +0300
jfs: validate AG parameters in dbMount() to prevent crashes
Validate db_agheight, db_agwidth, and db_agstart in dbMount to catch
corrupted metadata early and avoid undefined behavior in dbAllocAG.
Limits are derived from L2LPERCTL, LPERCTL/MAXAG, and CTLTREESIZE:
- agheight: 0 to L2LPERCTL/2 (0 to 5) ensures shift
(L2LPERCTL - 2*agheight) >= 0.
- agwidth: 1 to min(LPERCTL/MAXAG, 2^(L2LPERCTL - 2*agheight))
ensures agperlev >= 1.
- Ranges: 1-8 (agheight 0-3), 1-4 (agheight 4), 1 (agheight 5).
- LPERCTL/MAXAG = 1024/128 = 8 limits leaves per AG;
2^(10 - 2*agheight) prevents division to 0.
- agstart: 0 to CTLTREESIZE-1 - agwidth*(MAXAG-1) keeps ti within
stree (size 1365).
- Ranges: 0-1237 (agwidth 1), 0-348 (agwidth 8).
UBSAN: shift-out-of-bounds in fs/jfs/jfs_dmap.c:1400:9
shift exponent -335544310 is negative
CPU: 0 UID: 0 PID: 5822 Comm: syz-executor130 Not tainted 6.14.0-rc5-syzkaller #0
Hardware name: Google Compute Engine/Google Compute Engine, BIOS Google 02/12/2025
Call Trace:
<TASK>
__dump_stack lib/dump_stack.c:94 [inline]
dump_stack_lvl+0x241/0x360 lib/dump_stack.c:120
ubsan_epilogue lib/ubsan.c:231 [inline]
__ubsan_handle_shift_out_of_bounds+0x3c8/0x420 lib/ubsan.c:468
dbAllocAG+0x1087/0x10b0 fs/jfs/jfs_dmap.c:1400
dbDiscardAG+0x352/0xa20 fs/jfs/jfs_dmap.c:1613
jfs_ioc_trim+0x45a/0x6b0 fs/jfs/jfs_discard.c:105
jfs_ioctl+0x2cd/0x3e0 fs/jfs/ioctl.c:131
vfs_ioctl fs/ioctl.c:51 [inline]
__do_sys_ioctl fs/ioctl.c:906 [inline]
__se_sys_ioctl+0xf5/0x170 fs/ioctl.c:892
do_syscall_x64 arch/x86/entry/common.c:52 [inline]
do_syscall_64+0xf3/0x230 arch/x86/entry/common.c:83
entry_SYSCALL_64_after_hwframe+0x77/0x7f
Found by Linux Verification Center (linuxtesting.org) with Syzkaller.
Cc: stable@vger.kernel.org
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Reported-by: syzbot+fe8264911355151c487f@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=fe8264911355151c487f
Signed-off-by: Vasiliy Kovalev <kovalev@altlinux.org>
Signed-off-by: Dave Kleikamp <dave.kleikamp@oracle.com>
diff --git a/fs/jfs/jfs_dmap.c b/fs/jfs/jfs_dmap.c
index 26e89d0c69b6..35e063c9f3a4 100644
--- a/fs/jfs/jfs_dmap.c
+++ b/fs/jfs/jfs_dmap.c
@@ -194,7 +194,11 @@ int dbMount(struct inode *ipbmap)
!bmp->db_numag || (bmp->db_numag > MAXAG) ||
(bmp->db_maxag >= MAXAG) || (bmp->db_maxag < 0) ||
(bmp->db_agpref >= MAXAG) || (bmp->db_agpref < 0) ||
- !bmp->db_agwidth ||
+ (bmp->db_agheight < 0) || (bmp->db_agheight > (L2LPERCTL >> 1)) ||
+ (bmp->db_agwidth < 1) || (bmp->db_agwidth > (LPERCTL / MAXAG)) ||
+ (bmp->db_agwidth > (1 << (L2LPERCTL - (bmp->db_agheight << 1)))) ||
+ (bmp->db_agstart < 0) ||
+ (bmp->db_agstart > (CTLTREESIZE - 1 - bmp->db_agwidth * (MAXAG - 1))) ||
(bmp->db_agl2size > L2MAXL2SIZE - L2MAXAG) ||
(bmp->db_agl2size < 0) ||
((bmp->db_mapsize - 1) >> bmp->db_agl2size) > MAXAG) {
]
|
| 138/2 |
2026/06/30 14:46 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 32016
output: 79
thoughts: 427
Thoughts: **My Approach to Validating On-Disk Metadata**
Okay, so I'm looking at this pattern in `fs/jfs/jfs_dmap.c`, and it's quite clear how they're handling potential corruption in the on-disk metadata. It's a solid, pragmatic approach, and one I've used myself in similar situations. Essentially, the strategy is this:
First, introduce a dedicated validation function like `check_dmapctl` or `check_dmaptree`, and now likely `check_dmaptree`, to check the consistency of the structure's fields. This dedicated function is key. It's where the specific validation logic resides.
Second, the validation function needs to give me a clear "yes" or "no" answer, so a `bool` return (`true` for valid, `false` for corrupted) is perfect.
Third, and this is crucial for debugging and future analysis, inside this validation function, I need to log *specific* details about any field that fails validation. `jfs_err()` is a good choice for this. It's essential to pinpoint *why* the data is considered corrupted, not just that it *is* corrupted.
Fourth, call this validation function at critical points - when the structure is read from disk, or immediately before it's used. This keeps the integrity check as close to the source of the data as possible. Functions like `dbFreeDmap`, `dbAllocNext`, etc. are prime candidates.
Fifth, to keep the code clean and perform the check, use `unlikely(!check_dmaptree(...))` for the check.
Sixth, if the validation fails, log a generic error using `jfs_error(sb, "Corrupt dmap page\n");`. This is a fallback and provides context for the broader system. I'd typically also include the specific details from `jfs_err()` in the more verbose logging.
Seventh, and this is important, return `-EIO` to abort the operation and prevent further processing of the bad data. Better to fail early and safely than to potentially cause more damage by continuing with corrupted information.
Finally, if the function has allocated resources, make sure they are released before returning. Memory leaks are the enemy! It looks like there may not be much direct metapage management within the specific functions (`dbAllocNext` and `dbFreeDmap`) but I should double-check and make sure to release any resources. For example `release_metapage(mp);`. However, as I'm looking now, it appears that `dbAllocNext` and `dbFreeDmap` simply return `-EIO`. So the responsibility may fall on the caller, but I'll still keep cleanup in mind.
So, let me check the specific cleanup requirements for `dbFreeDmap`, `dbAllocNext`, `dbAllocNear`, `dbAllocDmapLev`, `dbAllocDmapBU`, and `dbAllocDmap`. It's always worth verifying this. This also needs to be added as a part of the integrity check in `dbMount`.
|
| 139/2 |
2026/06/30 14:46 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/jfs/jfs_dmap.c Name:dbFreeDmap]
Results: map[SourceCode:2138: /*
2139: * NAME: dbFreeDmap()
2140: *
2141: * FUNCTION: adjust the disk allocation map to reflect the allocation
2142: * of a specified block range within a dmap.
2143: *
2144: * this routine frees the specified blocks from the dmap through
2145: * a call to dbFreeBits(). if the deallocation of the block range
2146: * causes the maximum string of free blocks within the dmap to
2147: * change (i.e. the value of the root of the dmap's dmtree), this
2148: * routine will cause this change to be reflected up through the
2149: * appropriate levels of the dmap control pages by a call to
2150: * dbAdjCtl() for the L0 dmap control page that covers this dmap.
2151: *
2152: * PARAMETERS:
2153: * bmp - pointer to bmap descriptor
2154: * dp - pointer to dmap to free the block range from.
2155: * blkno - starting block number of the block to be freed.
2156: * nblocks - number of blocks to be freed.
2157: *
2158: * RETURN VALUES:
2159: * 0 - success
2160: * -EIO - i/o error
2161: *
2162: * serialization: IREAD_LOCK(ipbmap) or IWRITE_LOCK(ipbmap) held on entry/exit;
2163: */
2164: static int dbFreeDmap(struct bmap * bmp, struct dmap * dp, s64 blkno,
2165: int nblocks)
2166: {
2167: s8 oldroot;
2168: int rc = 0, word;
2169:
2170: /* save the current value of the root (i.e. maximum free string)
2171: * of the dmap tree.
2172: */
2173: oldroot = dp->tree.stree[ROOT];
2174:
2175: /* free the specified (blocks) bits */
2176: rc = dbFreeBits(bmp, dp, blkno, nblocks);
2177:
2178: /* if error or the root has not changed, done. */
2179: if (rc || (dp->tree.stree[ROOT] == oldroot))
2180: return (rc);
2181:
2182: /* root changed. bubble the change up to the dmap control pages.
2183: * if the adjustment of the upper level control pages fails,
2184: * backout the deallocation.
2185: */
2186: if ((rc = dbAdjCtl(bmp, blkno, dp->tree.stree[ROOT], 0, 0))) {
2187: word = (blkno & (BPERDMAP - 1)) >> L2DBWORD;
2188:
2189: /* as part of backing out the deallocation, we will have
2190: * to back split the dmap tree if the deallocation caused
2191: * the freed blocks to become part of a larger binary buddy
2192: * system.
2193: */
2194: if (dp->tree.stree[word] == NOFREE)
2195: dbBackSplit((dmtree_t *)&dp->tree, word, false);
2196:
2197: dbAllocBits(bmp, dp, blkno, nblocks);
2198: }
2199:
2200: return (rc);
2201: }
SourceFile:fs/jfs/jfs_dmap.c]
|
| 140/2 |
2026/06/30 14:46 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/jfs/jfs_dmap.c Name:dbAllocNext]
Results: map[SourceCode:1139: /*
1140: * NAME: dbAllocNext()
1141: *
1142: * FUNCTION: attempt to allocate the blocks of the specified block
1143: * range within a dmap.
1144: *
1145: * PARAMETERS:
1146: * bmp - pointer to bmap descriptor
1147: * dp - pointer to dmap.
1148: * blkno - starting block number of the range.
1149: * nblocks - number of contiguous free blocks of the range.
1150: *
1151: * RETURN VALUES:
1152: * 0 - success
1153: * -ENOSPC - insufficient disk resources
1154: * -EIO - i/o error
1155: *
1156: * serialization: IREAD_LOCK(ipbmap) held on entry/exit;
1157: */
1158: static int dbAllocNext(struct bmap * bmp, struct dmap * dp, s64 blkno,
1159: int nblocks)
1160: {
1161: int dbitno, word, rembits, nb, nwords, wbitno, nw;
1162: int l2size;
1163: s8 *leaf;
1164: u32 mask;
1165:
1166: if (dp->tree.leafidx != cpu_to_le32(LEAFIND)) {
1167: jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmap page\n");
1168: return -EIO;
1169: }
1170:
1171: /* pick up a pointer to the leaves of the dmap tree.
1172: */
1173: leaf = dp->tree.stree + le32_to_cpu(dp->tree.leafidx);
1174:
1175: /* determine the bit number and word within the dmap of the
1176: * starting block.
1177: */
1178: dbitno = blkno & (BPERDMAP - 1);
1179: word = dbitno >> L2DBWORD;
1180:
1181: /* check if the specified block range is contained within
1182: * this dmap.
1183: */
1184: if (dbitno + nblocks > BPERDMAP)
1185: return -ENOSPC;
1186:
1187: /* check if the starting leaf indicates that anything
1188: * is free.
1189: */
1190: if (leaf[word] == NOFREE)
1191: return -ENOSPC;
1192:
1193: /* check the dmaps words corresponding to block range to see
1194: * if the block range is free. not all bits of the first and
1195: * last words may be contained within the block range. if this
1196: * is the case, we'll work against those words (i.e. partial first
1197: * and/or last) on an individual basis (a single pass) and examine
1198: * the actual bits to determine if they are free. a single pass
1199: * will be used for all dmap words fully contained within the
1200: * specified range. within this pass, the leaves of the dmap
1201: * tree will be examined to determine if the blocks are free. a
1202: * single leaf may describe the free space of multiple dmap
1203: * words, so we may visit only a subset of the actual leaves
1204: * corresponding to the dmap words of the block range.
1205: */
1206: for (rembits = nblocks; rembits > 0; rembits -= nb, dbitno += nb) {
1207: /* determine the bit number within the word and
1208: * the number of bits within the word.
1209: */
1210: wbitno = dbitno & (DBWORD - 1);
1211: nb = min(rembits, DBWORD - wbitno);
1212:
1213: /* check if only part of the word is to be examined.
1214: */
1215: if (nb < DBWORD) {
1216: /* check if the bits are free.
1217: */
1218: mask = (ONES << (DBWORD - nb) >> wbitno);
1219: if ((mask & ~le32_to_cpu(dp->wmap[word])) != mask)
1220: return -ENOSPC;
1221:
1222: word += 1;
1223: } else {
1224: /* one or more dmap words are fully contained
1225: * within the block range. determine how many
1226: * words and how many bits.
1227: */
1228: nwords = rembits >> L2DBWORD;
1229: nb = nwords << L2DBWORD;
1230:
1231: /* now examine the appropriate leaves to determine
1232: * if the blocks are free.
1233: */
1234: while (nwords > 0) {
1235: /* does the leaf describe any free space ?
1236: */
1237: if (leaf[word] < BUDMIN)
1238: return -ENOSPC;
1239:
1240: /* determine the l2 number of bits provided
1241: * by this leaf.
1242: */
1243: l2size =
1244: min_t(int, leaf[word], NLSTOL2BSZ(nwords));
1245:
1246: /* determine how many words were handled.
1247: */
1248: nw = BUDSIZE(l2size, BUDMIN);
1249:
1250: nwords -= nw;
1251: word += nw;
1252: }
1253: }
1254: }
1255:
1256: /* allocate the blocks.
1257: */
1258: return (dbAllocDmap(bmp, dp, blkno, nblocks));
1259: }
SourceFile:fs/jfs/jfs_dmap.c]
|
| 141/2 |
2026/06/30 14:46 |
llm |
history-explorer |
0m
Model:
Tokens:
input: 36082
output: 754
thoughts: 693
Thoughts: **My Analysis of Idioms and Best Practices for Handling Potential Data Corruption**
Okay, so the prompt wants me to distill the common patterns and idioms we use when addressing bugs stemming from corrupted on-disk structures, specifically within `fs/jfs/jfs_dmap.c`. Let me break down what I've learned from similar past fixes.
Firstly, the core approach revolves around a **dedicated validation function**. We need to introduce a dedicated function, like `check_dmapctl()` or `check_dmaptree()`, tailored to thoroughly scrutinize structures like `dmapctl` or `dmap`. This isn't just a cursory glance; we're talking **comprehensive field checks**. The validation function needs to meticulously verify the bounds and consistency of *every* relevant field, checking things like `nleafs`, `l2nleafs`, `leafidx`, `height`, `budmin`, and critically, array bounds for fields like `stree`.
Crucially, this validation function is the place to provide **detailed error logging**. We need to use `jfs_err()` within the validation function itself to pinpoint *exactly* what failed. We need to log specific details, such as which field failed validation and what the expected/maximum values should be. This is critical for diagnosis down the line.
The validation function should return a simple boolean. `true` means the structure passes muster; `false` indicates corruption. The calling code then handles the failure. Speaking of which, all the callers of functions that work with those structures -- the `dbFreeDmap` and the `dbAlloc...` functions, for example -- should invoke the validation function *early*. Ideally, they call it immediately after they obtain a pointer to the structure they are operating on.
To optimize, it's essential to use `unlikely()` because we are assuming corruption is the exception. So wrap the check in an `unlikely()` clause: `if (unlikely(!check_dmaptree(&dp->tree)))`.
When corruption *is* detected by the caller, the strategy is consistent. **Generic error logging** is done by the caller using `jfs_error(sb, "Corrupt dmap page\n");` (where `sb` can be grabbed via `bmp->db_ipbmap->i_sb`). The error needs to propagate: We need to return `-EIO` to signal that we must abort the operation, so that we don't try to further process bad data.
A very important best practice is **resource cleanup**. If, for instance, we’ve obtained a metapage lock, we *must* release it before returning the `-EIO` error. Note that this isn't necessarily a requirement for the specific functions mentioned (like `dbFreeDmap`, `dbAllocNext`), but it's a general principle we need to keep in mind, as seen in other functions, such as `dbAllocAG` or `dbFindCtl`.
Regarding **locking rules**, the validation process doesn't introduce any *new* locking requirements. It's meant to work within the *existing* locking context. For instance, if `dbFreeDmap` or `dbAllocNext` is called with either `IREAD_LOCK(ipbmap)` or `IWRITE_LOCK(ipbmap)` held, the validation must happen *within* that lock. That ensures that the structure is protected during the check and prevents any race conditions before we can detect and report the error. It's important to remember that the locking protects the structure during the check, and must be held *before* calling the validation function. This ensures that the structure remains consistent and protected from concurrent modification.
|
| 142/1 |
2026/06/30 14:46 |
action |
kernel-scratch-checkouter |
0m
Results: map[KernelScratchSrc:/app/workdir/cache/tmp/tmp486162105]
|
| 143/1 |
2026/06/30 14:47 |
loop |
|
21m
|
| 144/2 |
2026/06/30 14:47 |
iteration |
0 |
21m
|
| 145/3 |
2026/06/30 14:47 |
agent |
patch-generator |
0m
Model:
Instruction:
You are an experienced Linux kernel developer tasked with creating a fix for a kernel bug.
You will be given a crash report, an initial explanation of the root cause done by another
kernel expert, and a summary of how similar bugs were fixed in the past.
Use the codeeditor tool to do code edits.
Note: you will not see your changes when looking at the code using codesearch tools.
Use the patch-diff tool to review the modifications you applied.
Your final reply should contain explanation of what you did in the patch and why
(details not present in the initial explanation of the bug).
Your fix must not just prevent the given crash, but also be the best fix for the underlying
root cause from the software engineering point of view. There can be several ways to fix the
same bug. Consider alternatives, and pick the best one. For example, additional checks may be
added at different locations/functions, it's usually better to place them earlier in the
execution to avoid multiple checks at various locations later.
Frequently the same coding mistake is done in several locations in the source code.
Check if your fix should be extended/applied to similar cases around to fix other similar bugs.
But don't go too wide, don't try to fix problems kernel-wide, fix similar issues
in the same file only.
If you are changing post-conditions of a function, consider all callers of the functions,
and if they need to be updated to handle new post-conditions. For example, if you make
a function that previously never returned a NULL, return NULL, consider if callers
need to be updated to handle NULL return value.
Prefer calling several tools at the same time to save round-trips.
Prompt:
The crash that corresponds to the bug is:
------------[ cut here ]------------
UBSAN: array-index-out-of-bounds in /app/workdir/cache/src/7be5f73ee7b4d1978813accf8bb54cd892ccd800/fs/jfs/jfs_dmap.c:2867:24
index 4294967295 is out of range for type 's8[1365]' (aka 'signed char[1365]')
CPU: 0 UID: 0 PID: 123 Comm: jfsCommit Not tainted syzkaller #1 PREEMPT(full)
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
Call Trace:
<TASK>
dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120
ubsan_epilogue+0xa/0x30 lib/ubsan.c:233
__ubsan_handle_out_of_bounds+0xe8/0xf0 lib/ubsan.c:455
dbJoin+0xcc4/0xd60 fs/jfs/jfs_dmap.c:2867
dbFreeBits+0x4a2/0xd70 fs/jfs/jfs_dmap.c:2427
dbFreeDmap fs/jfs/jfs_dmap.c:2176 [inline]
dbFree+0x324/0x650 fs/jfs/jfs_dmap.c:485
txFreeMap+0x9e6/0xde0 fs/jfs/jfs_txnmgr.c:2517
xtTruncate+0xd16/0x2eb0 fs/jfs/jfs_xtree.c:2481
jfs_free_zero_link+0x35b/0x4c0 fs/jfs/namei.c:760
jfs_evict_inode+0x356/0x430 fs/jfs/inode.c:159
evict+0x624/0xb50 fs/inode.c:825
txLazyCommit fs/jfs/jfs_txnmgr.c:2666 [inline]
jfs_lazycommit+0x44c/0xac0 fs/jfs/jfs_txnmgr.c:2735
kthread+0x388/0x470 kernel/kthread.c:436
ret_from_fork+0x514/0xb70 arch/x86/kernel/process.c:158
ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245
</TASK>
---[ end trace ]---
The explanation of the root cause of the bug is:
An array-index-out-of-bounds crash occurs in `dbJoin` when the JFS filesystem attempts to free blocks using a corrupted `dmap` structure read from disk.
### Root Cause
The JFS filesystem uses a binary buddy system to manage free space. The state of the buddy system is stored in `dmap` (for leaf levels) and `dmapctl` (for upper levels) structures on disk.
When a `dmap` is read from disk, its `dmaptree` structure is not fully validated. Specifically, the `leafidx` field (which indicates the index of the first leaf in the `stree` array) is read directly from disk and used to compute pointers to the leaf nodes.
If `leafidx` is corrupted (e.g., set to `0xffffffff` / `-1`), the computed pointer will point out of bounds of the `stree` array. When `dbFreeDmap` is called to free blocks, it calls `dbFreeBits`, which in turn calls `dbJoin` to update the buddy system. `dbJoin` uses the corrupted `leafidx` to access the leaf nodes:
```c
// fs/jfs/jfs_dmap.c:2867
leaf = tp->dmt_stree + le32_to_cpu(tp->dmt_leafidx);
```
This results in an out-of-bounds memory access when `leaf[buddy]` is subsequently accessed.
While a similar integrity check (`check_dmapctl`) was recently added for `dmapctl` structures in commit `cce219b203c4` ("jfs: add dmapctl integrity check to prevent invalid operations"), the `dmap` structures (`dmaptree`) were left unchecked.
### Fix
Introduce a `check_dmaptree` function, similar to the existing `check_dmapctl` function, to validate the integrity of the `dmaptree` structure when it is used.
The `check_dmaptree` function should verify that:
- `nleafs` is within bounds (`<= LPERDMAP`).
- `l2nleafs` is within bounds (`<= L2LPERDMAP`).
- `nleafs` matches `2^l2nleafs`.
- `leafidx` is exactly `LEAFIND`.
- `height` is within bounds (`<= 4`).
- `budmin` is valid (`>= BUDMIN`).
- The leaf nodes fit within the `stree` array (`leafidx + nleafs <= TREESIZE`).
- The values of the leaf nodes are valid (`>= NOFREE` and `<= 31`).
Call `check_dmaptree` in the functions that operate on the `dmap` structure, such as `dbFreeDmap`, `dbAllocNext`, `dbAllocNear`, `dbAllocDmapLev`, `dbAllocDmapBU`, and `dbAllocDmap`. If `check_dmaptree` fails, return `-EIO` to prevent further processing of the corrupted `dmap`. This will also replace the existing partial checks for `leafidx` in `dbAllocNext` and `dbAllocNear`.
```c
/*
* check_dmaptree - Validate integrity of a dmaptree structure
* @dtp: Pointer to the dmaptree structure to check
*
* Return: true if valid, false if corrupted
*/
static bool check_dmaptree(struct dmaptree *dtp)
{
u32 nleafs, l2nleafs, leafidx, height;
int i;
nleafs = le32_to_cpu(dtp->nleafs);
/* Check basic field ranges */
if (unlikely(nleafs > LPERDMAP)) {
jfs_err("dmaptree: invalid nleafs %u (max %u)",
nleafs, LPERDMAP);
return false;
}
l2nleafs = le32_to_cpu(dtp->l2nleafs);
if (unlikely(l2nleafs > L2LPERDMAP)) {
jfs_err("dmaptree: invalid l2nleafs %u (max %u)",
l2nleafs, L2LPERDMAP);
return false;
}
/* Verify nleafs matches l2nleafs (must be power of two) */
if (unlikely((1U << l2nleafs) != nleafs)) {
jfs_err("dmaptree: nleafs %u != 2^%u",
nleafs, l2nleafs);
return false;
}
leafidx = le32_to_cpu(dtp->leafidx);
/* Check leaf index matches expected position */
if (unlikely(leafidx != LEAFIND)) {
jfs_err("dmaptree: invalid leafidx %u (expected %u)",
leafidx, LEAFIND);
return false;
}
height = le32_to_cpu(dtp->height);
/* Check tree height is within valid range */
if (unlikely(height > 4)) {
jfs_err("dmaptree: invalid height %u (max 4)",
height);
return false;
}
/* Check budmin is valid */
if (unlikely(dtp->budmin < BUDMIN)) {
jfs_err("dmaptree: invalid budmin %d (min %d)",
dtp->budmin, BUDMIN);
return false;
}
/* Check leaf nodes fit within stree array */
if (unlikely(leafidx + nleafs > TREESIZE)) {
jfs_err("dmaptree: leaf range exceeds stree size (end %u > %u)",
leafidx + nleafs, TREESIZE);
return false;
}
/* Check leaf nodes have valid values */
for (i = leafidx; i < leafidx + nleafs; i++) {
s8 val = dtp->stree[i];
if (unlikely(val < NOFREE)) {
jfs_err("dmaptree: invalid leaf value %d at index %d",
val, i);
return false;
} else if (unlikely(val > 31)) {
jfs_err("dmaptree: leaf value %d too large at index %d", val, i);
return false;
}
}
return true;
}
```
Then, in functions like `dbFreeDmap`, `dbAllocNext`, `dbAllocNear`, `dbAllocDmapLev`, `dbAllocDmapBU`, and `dbAllocDmap`, add the check at the beginning:
```c
if (unlikely(!check_dmaptree(&dp->tree))) {
jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmap page\n");
return -EIO;
}
```
Historical context on how similar bugs were fixed in the past:
Based on the research into how similar bugs were addressed in the JFS subsystem (specifically in `fs/jfs/jfs_dmap.c`), here is a summary of the idioms, locking rules, and common patterns that should be followed when writing a fix for this bug:
### 1. Dedicated Validation Function
The standard idiom for preventing out-of-bounds accesses and invalid operations due to corrupted on-disk metadata is to introduce a dedicated, standalone validation function.
- For example, commit `cce219b203c4` ("jfs: add dmapctl integrity check to prevent invalid operations") introduced `check_dmapctl()`.
- Following this pattern, introducing `check_dmaptree(struct dmaptree *dtp)` is the correct approach. The function should return a boolean (`true` if valid, `false` if corrupted).
### 2. Comprehensive Field Checks and Detailed Logging
The validation function must thoroughly verify the bounds and internal consistency of the structure's fields before they are used in any calculations or array indexing.
- **Consistency:** Ensure fields like `nleafs` match `2^l2nleafs`.
- **Bounds:** Check that `leafidx`, `height`, and `budmin` are within their defined macros/limits (e.g., `LEAFIND`, `BUDMIN`, `TREESIZE`).
- **Detailed Logging:** Inside the validation function, use `jfs_err()` to log the specific field that failed validation, including the corrupted value and the expected limit. This aids in debugging disk corruption issues.
### 3. Caller-Side Handling and Error Propagation
Functions that operate on the `dmap` structure (such as `dbFreeDmap`, `dbAllocNext`, `dbAllocNear`, `dbAllocDmapLev`, `dbAllocDmapBU`, and `dbAllocDmap`) should invoke the validation function as early as possible.
- **Branch Prediction:** Wrap the check in the `unlikely()` macro, as corruption is the exceptional case:
```c
if (unlikely(!check_dmaptree(&dp->tree))) { ... }
```
- **Generic Error Logging:** If the check fails, the caller should log a generic filesystem error using `jfs_error()`. The superblock pointer can typically be accessed via the bmap descriptor: `jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmap page\n");`.
- **Abort Operation:** Return `-EIO` immediately to halt further processing and prevent the out-of-bounds memory access.
- **Resource Cleanup:** While the specific functions mentioned (`dbFreeDmap`, `dbAllocNext`, etc.) do not directly hold a metapage reference that needs releasing at their entry points, the general pattern (seen in `dbAllocAG` and `dbFindCtl`) dictates that any held resources (like `release_metapage(mp)`) must be freed before returning `-EIO`.
### 4. Locking Rules
The integrity checks do not require any new locks. They should be performed synchronously within the existing locking context of the callers.
- Functions like `dbFreeDmap` and `dbAllocNext` are already serialized via `IREAD_LOCK(ipbmap)` or `IWRITE_LOCK(ipbmap)` upon entry.
- The validation simply acts as a safe, read-only guard rail before the code proceeds to modify the buddy system state or traverse the tree.
Reply: I have implemented the fix by introducing a dedicated validation function `check_dmaptree()` and calling it at the entry points of all functions that operate on the `dmap` structure.
### What I did in the patch:
1. **Introduced `check_dmaptree()`**: I added a new function in `fs/jfs/jfs_dmap.c` right after `check_dmapctl()`. This function thoroughly validates the integrity of a `dmaptree` structure by checking its fields (`nleafs`, `l2nleafs`, `leafidx`, `height`, `budmin`) against their expected bounds and ensuring internal consistency (e.g., `nleafs == 2^l2nleafs`). It also iterates over the leaf nodes to ensure their values are within the valid range (`NOFREE` to 31) and that the leaf range does not exceed the `stree` array size.
2. **Added Validation Checks**: I added `if (unlikely(!check_dmaptree(&dp->tree)))` checks at the beginning of the following functions:
- `dbFreeDmap()`
- `dbAllocNext()`
- `dbAllocNear()`
- `dbAllocDmapLev()`
- `dbAllocDmapBU()`
- `dbAllocDmap()`
3. **Replaced Partial Checks**: In `dbAllocNext()` and `dbAllocNear()`, I replaced the existing partial checks (`if (dp->tree.leafidx != cpu_to_le32(LEAFIND))`) with the comprehensive `check_dmaptree()` call.
4. **Error Handling**: If the validation fails, the functions log a generic filesystem error using `jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmap page\n");` and immediately return `-EIO`.
### Why this is the best approach:
- **Fail-Fast Mechanism**: By placing the validation checks at the very beginning of the entry-point functions, we ensure that corrupted metadata is caught before any calculations or array indexing occur. This prevents out-of-bounds memory accesses (like the one in `dbJoin()`) and avoids propagating corrupted state into the buddy system.
- **Comprehensive Validation**: The previous partial checks only verified `leafidx`, leaving other fields like `nleafs` and the actual leaf values unchecked. `check_dmaptree()` provides a robust defense against various forms of disk corruption.
- **Consistency**: This approach mirrors the existing pattern established by `check_dmapctl()` for `dmapctl` structures, maintaining consistency in how JFS handles on-disk metadata validation.
- **Locking Safety**: The checks are performed synchronously within the existing locking context (e.g., `IREAD_LOCK` or `IWRITE_LOCK` on `ipbmap`), requiring no new locks and ensuring thread-safe validation.
|
| 146/4 |
2026/06/30 14:47 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 7405
output: 31
thoughts: 331
Thoughts: **Analyzing a JFS Crash Report**
Alright, let's break this down. We've got a crash report, and the UBSAN output clearly points to an array-index-out-of-bounds error deep within the JFS file system, specifically in `jfs_dmap.c`. Line 2867 is the culprit, trying to access an invalid index within the `s8[1365]` array. It's a classic case of bad indexing. The call stack gives us the sequence leading up to the error: `dbJoin`, `dbFreeBits`, `dbFreeDmap` are all implicated.
Looking at the code and the context, I'm pretty sure the root cause is a lack of robust validation of the `dmaptree` structure. It seems we're not properly checking the integrity of this critical data structure before using it, allowing an out-of-bounds access to sneak through. This is concerning, we need to take action.
My immediate thought is to introduce a validation function, perhaps named `check_dmaptree`, in `fs/jfs/jfs_dmap.c`. This function would thoroughly inspect the `dmaptree` structure, making sure indices are within bounds, pointers are valid, and the overall structure is internally consistent.
The real challenge is to pinpoint *where* this validation needs to be injected. Based on the call stack, I should insert calls to `check_dmaptree` within `dbFreeDmap` – the function that's being called, which then in turns calls those other functions we saw in the stack trace. The places where the allocations are made are also suspects. Therefore, I also need to call `check_dmaptree` in `dbAllocNext`, `dbAllocNear`, `dbAllocDmapLev`, `dbAllocDmapBU`, and `dbAllocDmap` . It seems like these are the key points in the code where the `dmaptree` structure is modified or utilized, making them prime candidates for checking.
Time to dive into `fs/jfs/jfs_dmap.c` and start coding that validation function. Let's get this fixed.
|
| 147/4 |
2026/06/30 14:47 |
tool |
codesearch-file-index |
0m
Args: map[SourceFile:fs/jfs/jfs_dmap.c]
Results: map[Entities:[map[Kind:function Name:blkstol2] map[Kind:function Name:check_dmapctl] map[Kind:function Name:cntlz] map[Kind:function Name:cnttz] map[Kind:function Name:dbAdjCtl] map[Kind:function Name:dbAdjTree] map[Kind:function Name:dbAlloc] map[Kind:function Name:dbAllocAG] map[Kind:function Name:dbAllocAny] map[Kind:function Name:dbAllocBits] map[Kind:function Name:dbAllocBottomUp] map[Kind:function Name:dbAllocCtl] map[Kind:function Name:dbAllocDmap] map[Kind:function Name:dbAllocDmapBU] map[Kind:function Name:dbAllocDmapLev] map[Kind:function Name:dbAllocNear] map[Kind:function Name:dbAllocNext] map[Kind:function Name:dbBackSplit] map[Kind:function Name:dbDiscardAG] map[Kind:function Name:dbExtend] map[Kind:function Name:dbExtendFS] map[Kind:function Name:dbFinalizeBmap] map[Kind:function Name:dbFindBits] map[Kind:function Name:dbFindCtl] map[Kind:function Name:dbFindLeaf] map[Kind:function Name:dbFree] map[Kind:function Name:dbFreeBits] map[Kind:function Name:dbFreeDmap] map[Kind:function Name:dbGetL2AGSize] map[Kind:function Name:dbInitDmap] map[Kind:function Name:dbInitDmapCtl] map[Kind:function Name:dbInitDmapTree] map[Kind:function Name:dbInitTree] map[Kind:function Name:dbJoin] map[Kind:function Name:dbMapFileSizeToMapSize] map[Kind:function Name:dbMaxBud] map[Kind:function Name:dbMount] map[Kind:function Name:dbNextAG] map[Kind:function Name:dbReAlloc] map[Kind:function Name:dbSplit] map[Kind:function Name:dbSync] map[Kind:function Name:dbUnmount] map[Kind:function Name:dbUpdatePMap] map[Kind:struct Name:range2trim] map[Kind:global_variable Name:budtab]]]
|
| 148/4 |
2026/06/30 14:47 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 8895
output: 41
thoughts: 44
Thoughts: Okay, let's dive into this. Here's how I'm thinking about it:
**Locating and Contextualizing `check_dmaptree` in JFS**
Alright, I need to figure out where `check_dmapctl` lives within the JFS codebase, specifically the `fs/jfs` directory. My aim is to get a sense of its immediate surrounding code, so I can then decide the best place to insert `check_dmaptree`. The rationale here is to keep related functionalities close together for maintainability and comprehension, which is standard practice when dealing with filesystem internals. Going to examine `fs/jfs/jfs_dmap.c` feels like the most logical first step. Hopefully `check_dmapctl` is defined there and I can establish some context!
|
| 149/4 |
2026/06/30 14:47 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/jfs/jfs_dmap.c Name:check_dmapctl]
Results: map[SourceCode: 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: }
SourceFile:fs/jfs/jfs_dmap.c]
|
| 150/4 |
2026/06/30 14:47 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 10551
output: 875
thoughts: 19
Thoughts: Here's my thought process, summarized as you requested:
**Planning the Integration: `check_dmaptree` After `check_dmapctl`**
Okay, so I'm thinking about the order of operations here. We've got `check_dmapctl` already in place, and the next logical step is integrating `check_dmaptree`. From an architectural standpoint, the flow seems cleanest if `check_dmaptree` follows immediately after `check_dmapctl`. This sequencing likely minimizes dependencies and maximizes clarity in the overall system operation. I'm choosing this order because it allows for a clear progression in the checks, and likely mirrors the inherent logic of how the dmap system is expected to function. No need to overcomplicate the implementation – this keeps it simple and efficient.
|
| 151/4 |
2026/06/30 14:47 |
tool |
codeeditor |
0m
Error:
Args: map[CurrentCode: return true;
}
/*
* dbMount()
NewCode: return true;
}
/*
* check_dmaptree - Validate integrity of a dmaptree structure
* @dtp: Pointer to the dmaptree structure to check
*
* Return: true if valid, false if corrupted
*/
static bool check_dmaptree(struct dmaptree *dtp)
{
u32 nleafs, l2nleafs, leafidx, height;
int i;
nleafs = le32_to_cpu(dtp->nleafs);
/* Check basic field ranges */
if (unlikely(nleafs > LPERDMAP)) {
jfs_err("dmaptree: invalid nleafs %u (max %u)",
nleafs, LPERDMAP);
return false;
}
l2nleafs = le32_to_cpu(dtp->l2nleafs);
if (unlikely(l2nleafs > L2LPERDMAP)) {
jfs_err("dmaptree: invalid l2nleafs %u (max %u)",
l2nleafs, L2LPERDMAP);
return false;
}
/* Verify nleafs matches l2nleafs (must be power of two) */
if (unlikely((1U << l2nleafs) != nleafs)) {
jfs_err("dmaptree: nleafs %u != 2^%u",
nleafs, l2nleafs);
return false;
}
leafidx = le32_to_cpu(dtp->leafidx);
/* Check leaf index matches expected position */
if (unlikely(leafidx != LEAFIND)) {
jfs_err("dmaptree: invalid leafidx %u (expected %u)",
leafidx, LEAFIND);
return false;
}
height = le32_to_cpu(dtp->height);
/* Check tree height is within valid range */
if (unlikely(height > 4)) {
jfs_err("dmaptree: invalid height %u (max 4)",
height);
return false;
}
/* Check budmin is valid */
if (unlikely(dtp->budmin < BUDMIN)) {
jfs_err("dmaptree: invalid budmin %d (min %d)",
dtp->budmin, BUDMIN);
return false;
}
/* Check leaf nodes fit within stree array */
if (unlikely(leafidx + nleafs > TREESIZE)) {
jfs_err("dmaptree: leaf range exceeds stree size (end %u > %u)",
leafidx + nleafs, TREESIZE);
return false;
}
/* Check leaf nodes have valid values */
for (i = leafidx; i < leafidx + nleafs; i++) {
s8 val = dtp->stree[i];
if (unlikely(val < NOFREE)) {
jfs_err("dmaptree: invalid leaf value %d at index %d",
val, i);
return false;
} else if (unlikely(val > 31)) {
jfs_err("dmaptree: leaf value %d too large at index %d", val, i);
return false;
}
}
return true;
}
/*
* dbMount()
SourceFile:fs/jfs/jfs_dmap.c]
Results:
|
| 152/4 |
2026/06/30 14:47 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 11645
output: 44
thoughts: 13
Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience, and presented as if it's my own thought process:
**Focusing In: Targeted File Analysis**
Right, let's zero in on a specific area within this file. Line 220, give or take a few around it, is where I need to start. I'm not going to approach this with a broad sweep; I'm going in with a targeted analysis. I've likely got a specific hypothesis or question I'm trying to answer, and that line, or the surrounding code, seems the most likely point of interest. I'm probably anticipating a certain structure or logic around that line and know the file's general architecture. The context around this area is crucial for understanding what's happening.
|
| 153/4 |
2026/06/30 14:47 |
tool |
read-file |
0m
Error:
Args: map[File:fs/jfs/jfs_dmap.c FirstLine:215 LineCount:15]
|
| 154/4 |
2026/06/30 14:47 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 11900
output: 40
thoughts: 8
Thoughts: Okay, here's the summary, assuming I'm the one having these thoughts and that I'm an expert in the field:
**Initial Data Type Assessment**
Alright, looking at this code or data structure... my first thought is, "FirstLine needs to be an integer." There's just no way it's going to work reliably as anything else. Everything is pointing to a numerical representation, probably a row or line number, and a string or other non-integer type would introduce so many problems further down the line. It would cause conversion errors and unexpected behavior in any logic that depends on it. This is a basic, fundamental assessment I have to make before doing anything else.
|
| 155/4 |
2026/06/30 14:47 |
tool |
read-file |
0m
Args: map[File:fs/jfs/jfs_dmap.c FirstLine:215 LineCount:15]
Results: map[Contents: 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: /*
224: * NAME: dbMount()
225: *
226: * FUNCTION: initializate the block allocation map.
227: *
228: * memory is allocated for the in-core bmap descriptor and
229: * the in-core descriptor is initialized from disk.
]
|
| 156/4 |
2026/06/30 14:47 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 12290
output: 877
thoughts: 8
Thoughts: Okay, I'm ready. Here's a summary, in the first person, assuming I'm an expert in the field and the text is the only available context:
**Analyzing the Snippet**
Alright, I'm looking at this snippet. The core of my analysis is to pinpoint the most effective application of its components within a given system. My expertise allows me to quickly identify potential areas of optimization or conflict. I need to understand its role in a broader context – how it integrates with existing infrastructure, what its dependencies are, and what performance characteristics I should anticipate. I immediately start assessing potential edge cases and error scenarios – my experience tells me where the subtle bugs often reside. Ultimately, I'm evaluating its contribution to the overall goal and determining if it efficiently fulfills its intended purpose.
|
| 157/4 |
2026/06/30 14:47 |
tool |
codeeditor |
0m
Args: map[CurrentCode: return true;
}
/*
* NAME: dbMount() NewCode: return true;
}
/*
* check_dmaptree - Validate integrity of a dmaptree structure
* @dtp: Pointer to the dmaptree structure to check
*
* Return: true if valid, false if corrupted
*/
static bool check_dmaptree(struct dmaptree *dtp)
{
u32 nleafs, l2nleafs, leafidx, height;
int i;
nleafs = le32_to_cpu(dtp->nleafs);
/* Check basic field ranges */
if (unlikely(nleafs > LPERDMAP)) {
jfs_err("dmaptree: invalid nleafs %u (max %u)",
nleafs, LPERDMAP);
return false;
}
l2nleafs = le32_to_cpu(dtp->l2nleafs);
if (unlikely(l2nleafs > L2LPERDMAP)) {
jfs_err("dmaptree: invalid l2nleafs %u (max %u)",
l2nleafs, L2LPERDMAP);
return false;
}
/* Verify nleafs matches l2nleafs (must be power of two) */
if (unlikely((1U << l2nleafs) != nleafs)) {
jfs_err("dmaptree: nleafs %u != 2^%u",
nleafs, l2nleafs);
return false;
}
leafidx = le32_to_cpu(dtp->leafidx);
/* Check leaf index matches expected position */
if (unlikely(leafidx != LEAFIND)) {
jfs_err("dmaptree: invalid leafidx %u (expected %u)",
leafidx, LEAFIND);
return false;
}
height = le32_to_cpu(dtp->height);
/* Check tree height is within valid range */
if (unlikely(height > 4)) {
jfs_err("dmaptree: invalid height %u (max 4)",
height);
return false;
}
/* Check budmin is valid */
if (unlikely(dtp->budmin < BUDMIN)) {
jfs_err("dmaptree: invalid budmin %d (min %d)",
dtp->budmin, BUDMIN);
return false;
}
/* Check leaf nodes fit within stree array */
if (unlikely(leafidx + nleafs > TREESIZE)) {
jfs_err("dmaptree: leaf range exceeds stree size (end %u > %u)",
leafidx + nleafs, TREESIZE);
return false;
}
/* Check leaf nodes have valid values */
for (i = leafidx; i < leafidx + nleafs; i++) {
s8 val = dtp->stree[i];
if (unlikely(val < NOFREE)) {
jfs_err("dmaptree: invalid leaf value %d at index %d",
val, i);
return false;
} else if (unlikely(val > 31)) {
jfs_err("dmaptree: leaf value %d too large at index %d", val, i);
return false;
}
}
return true;
}
/*
* NAME: dbMount() SourceFile:fs/jfs/jfs_dmap.c]
Results:
|
| 158/4 |
2026/06/30 14:47 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 13346
output: 40
thoughts: 62
Thoughts: **Integrating `check_dmaptree` into Key Allocation and Deallocation Routines**
Okay, time to get this done. I need to make sure `check_dmaptree` is integrated into a few critical functions within the database management system. I'll need to add it to `dbFreeDmap`, `dbAllocNext`, `dbAllocNear`, `dbAllocDmapLev`, `dbAllocDmapBU`, and `dbAllocDmap`. These are the core routines where we're allocating and freeing space within the data map structure, so verifying the tree's integrity at these points is crucial. Let's start by digging into `dbFreeDmap` and see where this function is.
|
| 159/4 |
2026/06/30 14:47 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/jfs/jfs_dmap.c Name:dbFreeDmap]
Results: map[SourceCode:2138: /*
2139: * NAME: dbFreeDmap()
2140: *
2141: * FUNCTION: adjust the disk allocation map to reflect the allocation
2142: * of a specified block range within a dmap.
2143: *
2144: * this routine frees the specified blocks from the dmap through
2145: * a call to dbFreeBits(). if the deallocation of the block range
2146: * causes the maximum string of free blocks within the dmap to
2147: * change (i.e. the value of the root of the dmap's dmtree), this
2148: * routine will cause this change to be reflected up through the
2149: * appropriate levels of the dmap control pages by a call to
2150: * dbAdjCtl() for the L0 dmap control page that covers this dmap.
2151: *
2152: * PARAMETERS:
2153: * bmp - pointer to bmap descriptor
2154: * dp - pointer to dmap to free the block range from.
2155: * blkno - starting block number of the block to be freed.
2156: * nblocks - number of blocks to be freed.
2157: *
2158: * RETURN VALUES:
2159: * 0 - success
2160: * -EIO - i/o error
2161: *
2162: * serialization: IREAD_LOCK(ipbmap) or IWRITE_LOCK(ipbmap) held on entry/exit;
2163: */
2164: static int dbFreeDmap(struct bmap * bmp, struct dmap * dp, s64 blkno,
2165: int nblocks)
2166: {
2167: s8 oldroot;
2168: int rc = 0, word;
2169:
2170: /* save the current value of the root (i.e. maximum free string)
2171: * of the dmap tree.
2172: */
2173: oldroot = dp->tree.stree[ROOT];
2174:
2175: /* free the specified (blocks) bits */
2176: rc = dbFreeBits(bmp, dp, blkno, nblocks);
2177:
2178: /* if error or the root has not changed, done. */
2179: if (rc || (dp->tree.stree[ROOT] == oldroot))
2180: return (rc);
2181:
2182: /* root changed. bubble the change up to the dmap control pages.
2183: * if the adjustment of the upper level control pages fails,
2184: * backout the deallocation.
2185: */
2186: if ((rc = dbAdjCtl(bmp, blkno, dp->tree.stree[ROOT], 0, 0))) {
2187: word = (blkno & (BPERDMAP - 1)) >> L2DBWORD;
2188:
2189: /* as part of backing out the deallocation, we will have
2190: * to back split the dmap tree if the deallocation caused
2191: * the freed blocks to become part of a larger binary buddy
2192: * system.
2193: */
2194: if (dp->tree.stree[word] == NOFREE)
2195: dbBackSplit((dmtree_t *)&dp->tree, word, false);
2196:
2197: dbAllocBits(bmp, dp, blkno, nblocks);
2198: }
2199:
2200: return (rc);
2201: }
SourceFile:fs/jfs/jfs_dmap.c]
|
| 160/4 |
2026/06/30 14:47 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 14676
output: 289
thoughts: 15
Thoughts: Okay, here's my summary, in the first person, as if reflecting on this thought:
**Prioritizing Resource Management: The Placement of a Critical Check**
Alright, so the core of this seems straightforward enough. I'm thinking about the function `dbFreeDmap`, and specifically, where to place a check within its logic. The crucial point is that this check isn't just *any* check; it's significant enough to warrant positioning at the very beginning of the function's execution. My expertise tells me that this early placement likely serves a critical purpose related to resource management and preventing potential errors or inconsistencies down the line. It signals that this test is a pre-condition, a gatekeeper, and the function is best not continuing without it. The specific reason for this I'd likely understand from context, either from the code itself or the wider problem that the code is solving, but it is clear that this check is vital to the function.
|
| 161/4 |
2026/06/30 14:47 |
tool |
codeeditor |
0m
Args: map[CurrentCode:static int dbFreeDmap(struct bmap * bmp, struct dmap * dp, s64 blkno,
int nblocks)
{
s8 oldroot;
int rc = 0, word;
/* save the current value of the root (i.e. maximum free string)
* of the dmap tree.
*/
oldroot = dp->tree.stree[ROOT]; NewCode:static int dbFreeDmap(struct bmap * bmp, struct dmap * dp, s64 blkno,
int nblocks)
{
s8 oldroot;
int rc = 0, word;
if (unlikely(!check_dmaptree(&dp->tree))) {
jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmap page\n");
return -EIO;
}
/* save the current value of the root (i.e. maximum free string)
* of the dmap tree.
*/
oldroot = dp->tree.stree[ROOT]; SourceFile:fs/jfs/jfs_dmap.c]
Results:
|
| 162/4 |
2026/06/30 14:47 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 15188
output: 39
thoughts: 6
Thoughts: Okay, here's my attempt at summarizing the thought process, tailored for an expert audience, and written in the first person:
**Allocating the Next Database Resource**
Alright, let's get down to brass tacks: `dbAllocNext`. This is where the rubber meets the road, isn't it? The core of resource management, specifically, how we allocate the *next* piece of the database. I'm thinking about the implications instantly. Is this a new object? A segment of existing space? What's the allocation strategy? Is it contiguous, extent-based, or something more sophisticated like a B-tree based scheme? The choice has cascading effects, especially in a concurrent environment.
Right now, my mind's running through the key considerations. First, the metadata lookup. Where do we even start looking? This function needs to know the storage pool, the allocation bitmap (if it’s that type of scheme), or the free list. Am I dealing with a simple list, or something more complex like a segmented free list to avoid fragmentation? Then comes the critical check. Do we have enough *contiguous* space, if contiguity is a constraint, or are we allowed to use fragmentation? What's the cost of defragmentation if we don’t?
I need to make sure this allocation is atomic, even if we are using an optimistic locking approach. We can't have two threads grabbing the same resource simultaneously. That means careful attention to locking primitives, whether they are mutexes, semaphores, or something more specialized to the underlying storage system. What's the granularity of the lock? This has implications for throughput and contention.
I'm considering error handling now. What if `dbAllocNext` fails to find available space? Do we return an error code, throw an exception, or attempt to grow the storage pool? What are the implications of each approach? We need a clear, consistent response. Should the caller be able to specify a timeout, or a maximum retry count? I'm debating the level of abstraction, too. Do we handle the low-level allocation details here, or delegate that to a lower-level storage manager, offering the database layer with a simpler interface? This is where the design's coupling becomes apparent.
Finally, I'm thinking about logging. If something goes wrong, or even if it goes right, we need to log this. Error logs need enough context to debug a problem. The same is true for successful allocation, but the level of detail can be lower. Is this going to require an update to the database's transaction log?
It needs to be efficient, thread safe, and robust. Now to get into the code...
|
| 163/4 |
2026/06/30 14:47 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/jfs/jfs_dmap.c Name:dbAllocNext]
Results: map[SourceCode:1139: /*
1140: * NAME: dbAllocNext()
1141: *
1142: * FUNCTION: attempt to allocate the blocks of the specified block
1143: * range within a dmap.
1144: *
1145: * PARAMETERS:
1146: * bmp - pointer to bmap descriptor
1147: * dp - pointer to dmap.
1148: * blkno - starting block number of the range.
1149: * nblocks - number of contiguous free blocks of the range.
1150: *
1151: * RETURN VALUES:
1152: * 0 - success
1153: * -ENOSPC - insufficient disk resources
1154: * -EIO - i/o error
1155: *
1156: * serialization: IREAD_LOCK(ipbmap) held on entry/exit;
1157: */
1158: static int dbAllocNext(struct bmap * bmp, struct dmap * dp, s64 blkno,
1159: int nblocks)
1160: {
1161: int dbitno, word, rembits, nb, nwords, wbitno, nw;
1162: int l2size;
1163: s8 *leaf;
1164: u32 mask;
1165:
1166: if (dp->tree.leafidx != cpu_to_le32(LEAFIND)) {
1167: jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmap page\n");
1168: return -EIO;
1169: }
1170:
1171: /* pick up a pointer to the leaves of the dmap tree.
1172: */
1173: leaf = dp->tree.stree + le32_to_cpu(dp->tree.leafidx);
1174:
1175: /* determine the bit number and word within the dmap of the
1176: * starting block.
1177: */
1178: dbitno = blkno & (BPERDMAP - 1);
1179: word = dbitno >> L2DBWORD;
1180:
1181: /* check if the specified block range is contained within
1182: * this dmap.
1183: */
1184: if (dbitno + nblocks > BPERDMAP)
1185: return -ENOSPC;
1186:
1187: /* check if the starting leaf indicates that anything
1188: * is free.
1189: */
1190: if (leaf[word] == NOFREE)
1191: return -ENOSPC;
1192:
1193: /* check the dmaps words corresponding to block range to see
1194: * if the block range is free. not all bits of the first and
1195: * last words may be contained within the block range. if this
1196: * is the case, we'll work against those words (i.e. partial first
1197: * and/or last) on an individual basis (a single pass) and examine
1198: * the actual bits to determine if they are free. a single pass
1199: * will be used for all dmap words fully contained within the
1200: * specified range. within this pass, the leaves of the dmap
1201: * tree will be examined to determine if the blocks are free. a
1202: * single leaf may describe the free space of multiple dmap
1203: * words, so we may visit only a subset of the actual leaves
1204: * corresponding to the dmap words of the block range.
1205: */
1206: for (rembits = nblocks; rembits > 0; rembits -= nb, dbitno += nb) {
1207: /* determine the bit number within the word and
1208: * the number of bits within the word.
1209: */
1210: wbitno = dbitno & (DBWORD - 1);
1211: nb = min(rembits, DBWORD - wbitno);
1212:
1213: /* check if only part of the word is to be examined.
1214: */
1215: if (nb < DBWORD) {
1216: /* check if the bits are free.
1217: */
1218: mask = (ONES << (DBWORD - nb) >> wbitno);
1219: if ((mask & ~le32_to_cpu(dp->wmap[word])) != mask)
1220: return -ENOSPC;
1221:
1222: word += 1;
1223: } else {
1224: /* one or more dmap words are fully contained
1225: * within the block range. determine how many
1226: * words and how many bits.
1227: */
1228: nwords = rembits >> L2DBWORD;
1229: nb = nwords << L2DBWORD;
1230:
1231: /* now examine the appropriate leaves to determine
1232: * if the blocks are free.
1233: */
1234: while (nwords > 0) {
1235: /* does the leaf describe any free space ?
1236: */
1237: if (leaf[word] < BUDMIN)
1238: return -ENOSPC;
1239:
1240: /* determine the l2 number of bits provided
1241: * by this leaf.
1242: */
1243: l2size =
1244: min_t(int, leaf[word], NLSTOL2BSZ(nwords));
1245:
1246: /* determine how many words were handled.
1247: */
1248: nw = BUDSIZE(l2size, BUDMIN);
1249:
1250: nwords -= nw;
1251: word += nw;
1252: }
1253: }
1254: }
1255:
1256: /* allocate the blocks.
1257: */
1258: return (dbAllocDmap(bmp, dp, blkno, nblocks));
1259: }
SourceFile:fs/jfs/jfs_dmap.c]
|
| 164/4 |
2026/06/30 14:47 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 17622
output: 235
thoughts: 12
Thoughts: Okay, here's my summary, in the first person, as if these are my thoughts, keeping in mind I'm dealing with database allocation and am assumed to be an expert:
**Refactoring the Allocation Logic: `dbAllocNext`**
Right, looking at this `dbAllocNext` function... I'm noticing a potential issue. That "partial check" we have in there? It's bugging me. It feels like a potential source of subtle errors, maybe even resource leaks or allocation fragmentation down the line if we're not careful. It's a bit of a code smell, honestly. I've seen similar patterns lead to trouble in the past, especially when dealing with concurrency or complex allocation schemes.
I'm thinking we need to rethink that piece. The partial check, as it stands, might be masking a deeper issue, or at least making the logic harder to follow and maintain. We're probably better off with a more robust and complete check, or even better, refactoring the surrounding code to avoid the need for such a granular, potentially incomplete, validation. I need to dig deeper to see exactly *why* that partial check is there. What's it trying to protect against? And is there a cleaner, more efficient way to achieve the same result? A more elegant solution would involve consolidating the validation, or eliminating the partial validation entirely, ideally simplifying the overall function's responsibility. It's time to replace that partial check with something more definitive. Let's see what we can do to make this more reliable.
|
| 165/4 |
2026/06/30 14:47 |
tool |
codeeditor |
0m
Args: map[CurrentCode: int l2size;
s8 *leaf;
u32 mask;
if (dp->tree.leafidx != cpu_to_le32(LEAFIND)) {
jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmap page\n");
return -EIO;
}
/* pick up a pointer to the leaves of the dmap tree.
*/ NewCode: int l2size;
s8 *leaf;
u32 mask;
if (unlikely(!check_dmaptree(&dp->tree))) {
jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmap page\n");
return -EIO;
}
/* pick up a pointer to the leaves of the dmap tree.
*/ SourceFile:fs/jfs/jfs_dmap.c]
Results:
|
| 166/4 |
2026/06/30 14:47 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 18200
output: 39
thoughts: 6
Thoughts: Okay, here's my attempt at summarizing the thought process, tailored for an expert audience, and written in the first person:
**Allocating Memory Efficiently: Focusing on `dbAllocNear`**
Alright, let's zero in on `dbAllocNear`. We're dealing with memory allocation here, and given the name, I'm immediately thinking "near" implies something specific about the architecture or memory model in play. This isn't just a generic `malloc` replacement. It's likely optimized for allocating blocks that are... well, *near* something else, likely within a specific segment, page, or addressing range.
My mind immediately starts running through the implications. Why `near`? Is it about avoiding expensive page table lookups? Minimizing cache misses by keeping related data clustered? Or perhaps it's for performance gains related to accessing memory within a specific segment or region, something that leverages a particular CPU instruction set or memory addressing mode. The constraints are crucial here. What are the size limitations? What's the context? Is this for a critical section, a kernel module, or some high-frequency data buffer?
I'm also considering the potential downsides. "Near" allocation, by definition, implies a limited scope. This could lead to fragmentation issues if not managed very carefully. There's a tradeoff here between speed and flexibility, and the right choice will depend on the application's specific needs and its memory profile. I need to understand the details to evaluate whether `dbAllocNear` is the right allocation mechanism for this task or not.
|
| 167/4 |
2026/06/30 14:47 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/jfs/jfs_dmap.c Name:dbAllocNear]
Results: map[SourceCode:1262: /*
1263: * NAME: dbAllocNear()
1264: *
1265: * FUNCTION: attempt to allocate a number of contiguous free blocks near
1266: * a specified block (hint) within a dmap.
1267: *
1268: * starting with the dmap leaf that covers the hint, we'll
1269: * check the next four contiguous leaves for sufficient free
1270: * space. if sufficient free space is found, we'll allocate
1271: * the desired free space.
1272: *
1273: * PARAMETERS:
1274: * bmp - pointer to bmap descriptor
1275: * dp - pointer to dmap.
1276: * blkno - block number to allocate near.
1277: * nblocks - actual number of contiguous free blocks desired.
1278: * l2nb - log2 number of contiguous free blocks desired.
1279: * results - on successful return, set to the starting block number
1280: * of the newly allocated range.
1281: *
1282: * RETURN VALUES:
1283: * 0 - success
1284: * -ENOSPC - insufficient disk resources
1285: * -EIO - i/o error
1286: *
1287: * serialization: IREAD_LOCK(ipbmap) held on entry/exit;
1288: */
1289: static int
1290: dbAllocNear(struct bmap * bmp,
1291: struct dmap * dp, s64 blkno, int nblocks, int l2nb, s64 * results)
1292: {
1293: int word, lword, rc;
1294: s8 *leaf;
1295:
1296: if (dp->tree.leafidx != cpu_to_le32(LEAFIND)) {
1297: jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmap page\n");
1298: return -EIO;
1299: }
1300:
1301: leaf = dp->tree.stree + le32_to_cpu(dp->tree.leafidx);
1302:
1303: /* determine the word within the dmap that holds the hint
1304: * (i.e. blkno). also, determine the last word in the dmap
1305: * that we'll include in our examination.
1306: */
1307: word = (blkno & (BPERDMAP - 1)) >> L2DBWORD;
1308: lword = min(word + 4, LPERDMAP);
1309:
1310: /* examine the leaves for sufficient free space.
1311: */
1312: for (; word < lword; word++) {
1313: /* does the leaf describe sufficient free space ?
1314: */
1315: if (leaf[word] < l2nb)
1316: continue;
1317:
1318: /* determine the block number within the file system
1319: * of the first block described by this dmap word.
1320: */
1321: blkno = le64_to_cpu(dp->start) + (word << L2DBWORD);
1322:
1323: /* if not all bits of the dmap word are free, get the
1324: * starting bit number within the dmap word of the required
1325: * string of free bits and adjust the block number with the
1326: * value.
1327: */
1328: if (leaf[word] < BUDMIN)
1329: blkno +=
1330: dbFindBits(le32_to_cpu(dp->wmap[word]), l2nb);
1331:
1332: /* allocate the blocks.
1333: */
1334: if ((rc = dbAllocDmap(bmp, dp, blkno, nblocks)) == 0)
1335: *results = blkno;
1336:
1337: return (rc);
1338: }
1339:
1340: return -ENOSPC;
1341: }
SourceFile:fs/jfs/jfs_dmap.c]
|
| 168/4 |
2026/06/30 14:47 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 19836
output: 241
thoughts: 12
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience:
**Refactoring `dbAllocNear` for Enhanced Precision**
Okay, so I'm looking at the `dbAllocNear` function again. Something's been nagging at me, and I've just realized what it is: that partial check. It's a bit of a kludge, isn't it? A half-measure, potentially leading to inaccuracies and edge-case behaviors I'd rather avoid. We're aiming for optimal allocation strategy here, and this 'partial' approach feels like it might be introducing some undesirable imprecision or subtle bugs down the line. Perhaps it's preventing the algorithm from making the best allocation decisions in certain scenarios. Time to dig in and devise a more robust replacement strategy. Let's see if we can find a cleaner, more definitive solution for that specific condition. Maybe a more precise metric, or a different logical structure, would be an improvement. This is about ensuring optimal resource utilization, and I'm confident we can tighten things up.
|
| 169/4 |
2026/06/30 14:47 |
tool |
codeeditor |
0m
Args: map[CurrentCode: int word, lword, rc;
s8 *leaf;
if (dp->tree.leafidx != cpu_to_le32(LEAFIND)) {
jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmap page\n");
return -EIO;
}
leaf = dp->tree.stree + le32_to_cpu(dp->tree.leafidx); NewCode: int word, lword, rc;
s8 *leaf;
if (unlikely(!check_dmaptree(&dp->tree))) {
jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmap page\n");
return -EIO;
}
leaf = dp->tree.stree + le32_to_cpu(dp->tree.leafidx); SourceFile:fs/jfs/jfs_dmap.c]
Results:
|
| 170/4 |
2026/06/30 14:47 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 20319
output: 41
thoughts: 8
Thoughts: Okay, here's my attempt at summarizing the thought process, tailored for an expert audience, and written in the first person:
**Diving into `dbAllocDmapLev`**
Alright, let's break this down. My mind immediately goes to the potential complexity here. The function's name, `dbAllocDmapLev`, suggests we're dealing with *database* allocation, specifically a *dmap* (data map) structure, and likely at a *level* – so probably hierarchical allocation. The "db" prefix implies this is core database-related and likely storage-centric.
I'm starting to think about the underlying data structures. Am I dealing with a block-based storage system? Is this a tiered storage setup? Is this allocation strategy for metadata, data blocks, or both? The "level" aspect makes me immediately consider concepts like B-trees, or perhaps a more specialized, custom indexing scheme optimized for this particular database engine. Are we talking about a multi-level index to efficiently manage space within a data volume?
The core question now is *how* the allocation happens. Is this a simple bitfield allocation, a more sophisticated buddy system, or something entirely different? What are the implications for fragmentation, performance, and recovery? Are there safeguards against race conditions, particularly in a multi-threaded environment? Does it involve journaling or atomic operations to ensure data consistency? My head is already spinning with the details of memory management strategies. I want to look for potential bottlenecks as well.
I need to quickly assess the context. What's the target system? What's the scope of this allocation? What's the underlying hardware? What are the performance and availability requirements? If this is for high-throughput, mission-critical data, the allocation strategy will be far more critical than if it's for something less performance-sensitive.
Next, I'll need to dig into the function's parameters and return values. What inputs are provided to control the allocation size, the level of the dmap, and any other relevant configurations? What kind of error codes are returned, and what do they signify? Are there opportunities for optimization or potential pitfalls that I can anticipate?
I also need to see if this is tied to any other allocation mechanisms. Is this a bottom-up or top-down allocation? Does it work with specific block sizes or larger logical units of storage? This could reveal a lot about the design philosophy.
Finally, I need to look for any documentation, comments, or related code that provides insight into its overall purpose, constraints, and dependencies.
|
| 171/4 |
2026/06/30 14:47 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/jfs/jfs_dmap.c Name:dbAllocDmapLev]
Results: map[SourceCode:2016: /*
2017: * NAME: dbAllocDmapLev()
2018: *
2019: * FUNCTION: attempt to allocate a specified number of contiguous blocks
2020: * from a specified dmap.
2021: *
2022: * this routine checks if the contiguous blocks are available.
2023: * if so, nblocks of blocks are allocated; otherwise, ENOSPC is
2024: * returned.
2025: *
2026: * PARAMETERS:
2027: * mp - pointer to bmap descriptor
2028: * dp - pointer to dmap to attempt to allocate blocks from.
2029: * l2nb - log2 number of contiguous block desired.
2030: * nblocks - actual number of contiguous block desired.
2031: * results - on successful return, set to the starting block number
2032: * of the newly allocated range.
2033: *
2034: * RETURN VALUES:
2035: * 0 - success
2036: * -ENOSPC - insufficient disk resources
2037: * -EIO - i/o error
2038: *
2039: * serialization: IREAD_LOCK(ipbmap), e.g., from dbAlloc(), or
2040: * IWRITE_LOCK(ipbmap), e.g., dbAllocCtl(), held on entry/exit;
2041: */
2042: static int
2043: dbAllocDmapLev(struct bmap * bmp,
2044: struct dmap * dp, int nblocks, int l2nb, s64 * results)
2045: {
2046: s64 blkno;
2047: int leafidx, rc;
2048:
2049: /* can't be more than a dmaps worth of blocks */
2050: assert(l2nb <= L2BPERDMAP);
2051:
2052: /* search the tree within the dmap page for sufficient
2053: * free space. if sufficient free space is found, dbFindLeaf()
2054: * returns the index of the leaf at which free space was found.
2055: */
2056: if (dbFindLeaf((dmtree_t *) &dp->tree, l2nb, &leafidx, false))
2057: return -ENOSPC;
2058:
2059: if (leafidx < 0)
2060: return -EIO;
2061:
2062: /* determine the block number within the file system corresponding
2063: * to the leaf at which free space was found.
2064: */
2065: blkno = le64_to_cpu(dp->start) + (leafidx << L2DBWORD);
2066:
2067: /* if not all bits of the dmap word are free, get the starting
2068: * bit number within the dmap word of the required string of free
2069: * bits and adjust the block number with this value.
2070: */
2071: if (dp->tree.stree[leafidx + LEAFIND] < BUDMIN)
2072: blkno += dbFindBits(le32_to_cpu(dp->wmap[leafidx]), l2nb);
2073:
2074: /* allocate the blocks */
2075: if ((rc = dbAllocDmap(bmp, dp, blkno, nblocks)) == 0)
2076: *results = blkno;
2077:
2078: return (rc);
2079: }
SourceFile:fs/jfs/jfs_dmap.c]
|
| 172/4 |
2026/06/30 14:47 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 21968
output: 273
thoughts: 16
Thoughts: Here's my thought process, summarized as you requested:
**Optimizing `dbAllocDmapLev` for Efficiency**
Okay, I'm looking at `dbAllocDmapLev` again, and I realize there's a potential optimization here. I really should add that initial check right at the *very* beginning of the function. It'll prevent unnecessary execution of subsequent code if the criteria for allocation is immediately determined to be unmet. This small change could save compute cycles and improve overall performance, especially in scenarios where allocation failures are common. Gotta stay sharp and make these kinds of micro-optimizations where possible. It's the little things, you know?
|
| 173/4 |
2026/06/30 14:47 |
tool |
codeeditor |
0m
Args: map[CurrentCode:static int
dbAllocDmapLev(struct bmap * bmp,
struct dmap * dp, int nblocks, int l2nb, s64 * results)
{
s64 blkno;
int leafidx, rc;
/* can't be more than a dmaps worth of blocks */
assert(l2nb <= L2BPERDMAP); NewCode:static int
dbAllocDmapLev(struct bmap * bmp,
struct dmap * dp, int nblocks, int l2nb, s64 * results)
{
s64 blkno;
int leafidx, rc;
if (unlikely(!check_dmaptree(&dp->tree))) {
jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmap page\n");
return -EIO;
}
/* can't be more than a dmaps worth of blocks */
assert(l2nb <= L2BPERDMAP); SourceFile:fs/jfs/jfs_dmap.c]
Results:
|
| 174/4 |
2026/06/30 14:47 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 22404
output: 41
thoughts: 8
Thoughts: Okay, here's my attempt at summarizing the thought process, tailored for an expert audience, and written as if it's my own internal monologue:
**Focusing on `dbAllocDmapBU`**
Alright, let's zero in on `dbAllocDmapBU`. We're talking about dynamic block allocation, specifically within the context of a Distributed Map (DMap) backing store, and the 'BU' likely hints at a Bottom-Up allocation strategy. So, immediately, I'm thinking about the implications for fragmentation. Is it using a bitmap-based approach to track free blocks? Or perhaps a more sophisticated structure like a free list, potentially organized by size class? I need to understand the underlying data structures involved – what is the granularity of these blocks? What's the size of the DMap and its capacity?
The "Bottom-Up" suggests we're allocating from the end of the DMap storage space or starting at the beginning depending on how this design is setup. That's interesting, and it can have its advantages. Bottom-up can sometimes allow for a more efficient allocation strategy when paired with certain algorithms, potentially reducing initial overhead in some situations. However, it's also worth considering the cost of traversal to find available blocks. Is the implementation optimized for cache locality?
Given the dynamic nature, I need to know how the system deals with allocation failure. What's the error handling? Does it have a retry mechanism? Does it fall back to another allocation strategy if bottom-up fails at the moment? Is it configured to trigger a defragmentation or compaction of the DMap to resolve space limitation?
My next consideration is concurrency. Is this allocation routine thread-safe? Are there locking mechanisms in place? And if so, how granular are they? High concurrency is critical, and poorly designed locks could be a major performance bottleneck. Understanding this lock hierarchy is paramount. The system’s performance will depend significantly on this.
Finally, I'm wondering about the broader implications on the entire DMap's performance characteristics. This allocation function has huge impact on how fast writes happen, how efficiently space is utilized, and how fast reads can happen (if data placement is influenced). Is this function optimized for performance metrics important for the target application? It's essential to understand the typical workload patterns. Overall, I need a deep dive into the implementation details to fully grasp the trade-offs and potential performance characteristics, especially within this particular DMap.
|
| 175/4 |
2026/06/30 14:47 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/jfs/jfs_dmap.c Name:dbAllocDmapBU]
Results: map[SourceCode:3333: static int dbAllocDmapBU(struct bmap * bmp, struct dmap * dp, s64 blkno,
3334: int nblocks)
3335: {
3336: int rc;
3337: int dbitno, word, rembits, nb, nwords, wbitno, agno;
3338: s8 oldroot;
3339: struct dmaptree *tp = (struct dmaptree *) & dp->tree;
3340:
3341: /* save the current value of the root (i.e. maximum free string)
3342: * of the dmap tree.
3343: */
3344: oldroot = tp->stree[ROOT];
3345:
3346: /* determine the bit number and word within the dmap of the
3347: * starting block.
3348: */
3349: dbitno = blkno & (BPERDMAP - 1);
3350: word = dbitno >> L2DBWORD;
3351:
3352: /* block range better be within the dmap */
3353: assert(dbitno + nblocks <= BPERDMAP);
3354:
3355: /* allocate the bits of the dmap's words corresponding to the block
3356: * range. not all bits of the first and last words may be contained
3357: * within the block range. if this is the case, we'll work against
3358: * those words (i.e. partial first and/or last) on an individual basis
3359: * (a single pass), allocating the bits of interest by hand and
3360: * updating the leaf corresponding to the dmap word. a single pass
3361: * will be used for all dmap words fully contained within the
3362: * specified range. within this pass, the bits of all fully contained
3363: * dmap words will be marked as free in a single shot and the leaves
3364: * will be updated. a single leaf may describe the free space of
3365: * multiple dmap words, so we may update only a subset of the actual
3366: * leaves corresponding to the dmap words of the block range.
3367: */
3368: for (rembits = nblocks; rembits > 0; rembits -= nb, dbitno += nb) {
3369: /* determine the bit number within the word and
3370: * the number of bits within the word.
3371: */
3372: wbitno = dbitno & (DBWORD - 1);
3373: nb = min(rembits, DBWORD - wbitno);
3374:
3375: /* check if only part of a word is to be allocated.
3376: */
3377: if (nb < DBWORD) {
3378: /* allocate (set to 1) the appropriate bits within
3379: * this dmap word.
3380: */
3381: dp->wmap[word] |= cpu_to_le32(ONES << (DBWORD - nb)
3382: >> wbitno);
3383:
3384: word++;
3385: } else {
3386: /* one or more dmap words are fully contained
3387: * within the block range. determine how many
3388: * words and allocate (set to 1) the bits of these
3389: * words.
3390: */
3391: nwords = rembits >> L2DBWORD;
3392: memset(&dp->wmap[word], (int) ONES, nwords * 4);
3393:
3394: /* determine how many bits */
3395: nb = nwords << L2DBWORD;
3396: word += nwords;
3397: }
3398: }
3399:
3400: /* update the free count for this dmap */
3401: le32_add_cpu(&dp->nfree, -nblocks);
3402:
3403: /* reconstruct summary tree */
3404: dbInitDmapTree(dp);
3405:
3406: BMAP_LOCK(bmp);
3407:
3408: /* if this allocation group is completely free,
3409: * update the highest active allocation group number
3410: * if this allocation group is the new max.
3411: */
3412: agno = blkno >> bmp->db_agl2size;
3413: if (agno > bmp->db_maxag)
3414: bmp->db_maxag = agno;
3415:
3416: /* update the free count for the allocation group and map */
3417: bmp->db_agfree[agno] -= nblocks;
3418: bmp->db_nfree -= nblocks;
3419:
3420: BMAP_UNLOCK(bmp);
3421:
3422: /* if the root has not changed, done. */
3423: if (tp->stree[ROOT] == oldroot)
3424: return (0);
3425:
3426: /* root changed. bubble the change up to the dmap control pages.
3427: * if the adjustment of the upper level control pages fails,
3428: * backout the bit allocation (thus making everything consistent).
3429: */
3430: if ((rc = dbAdjCtl(bmp, blkno, tp->stree[ROOT], 1, 0)))
3431: dbFreeBits(bmp, dp, blkno, nblocks);
3432:
3433: return (rc);
3434: }
SourceFile:fs/jfs/jfs_dmap.c]
|
| 176/4 |
2026/06/30 14:47 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 24594
output: 339
thoughts: 16
Thoughts: Okay, here's my summarized thought process, presented as if I were considering this myself:
**Addressing the `dbAllocDmapBU` Routine**
Right, so I'm thinking about the `dbAllocDmapBU` function. I need to make a change, and the most logical place to start is right at the beginning. It's crucial to implement that check upfront. This will help avoid more complications down the line. It's a fundamental step to ensure the rest of the routine functions correctly. It really feels like an essential preventative measure, making the code more robust from the get-go.
|
| 177/4 |
2026/06/30 14:47 |
tool |
codeeditor |
0m
Args: map[CurrentCode:static int dbAllocDmapBU(struct bmap * bmp, struct dmap * dp, s64 blkno,
int nblocks)
{
int rc;
int dbitno, word, rembits, nb, nwords, wbitno, agno;
s8 oldroot;
struct dmaptree *tp = (struct dmaptree *) & dp->tree;
/* save the current value of the root (i.e. maximum free string)
* of the dmap tree.
*/ NewCode:static int dbAllocDmapBU(struct bmap * bmp, struct dmap * dp, s64 blkno,
int nblocks)
{
int rc;
int dbitno, word, rembits, nb, nwords, wbitno, agno;
s8 oldroot;
struct dmaptree *tp = (struct dmaptree *) & dp->tree;
if (unlikely(!check_dmaptree(&dp->tree))) {
jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmap page\n");
return -EIO;
}
/* save the current value of the root (i.e. maximum free string)
* of the dmap tree.
*/ SourceFile:fs/jfs/jfs_dmap.c]
Results:
|
| 178/4 |
2026/06/30 14:47 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 25084
output: 40
thoughts: 7
Thoughts: Okay, here's my attempt at summarizing the thought process, tailored for an expert audience, and written in the first person:
**Allocating a DMA Buffer: Let's Dive into `dbAllocDmap`**
Alright, so we're talking about DMA (Direct Memory Access) and the allocation process. Specifically, I'm thinking about `dbAllocDmap`. The core concept here is that we're carving out a contiguous physical memory region that our DMA engine can directly access. No virtual address translation overhead, pure physical access for high-throughput I/O. We need this buffer and the kernel data structure which tracks it, known as the dmap, for a device to have direct access to system memory.
The allocation strategy, that's where things get interesting. Is this a system where we have large contiguous physical memory regions readily available, or are we dealing with a fragmented physical address space? That influences whether we can just grab a large chunk or if we need to implement something more clever, maybe using a buddy allocator or a zone allocator. We are likely to need to consider page sizes – are we aligning to a specific page boundary for optimal DMA performance?
What about error handling? `dbAllocDmap` will need to gracefully handle memory exhaustion scenarios. What error codes are returned? Should we retry with a smaller size request? Are we handling NUMA (Non-Uniform Memory Access) considerations? Ideally, we'd want to allocate the DMA buffer as close to the initiating device as possible to minimize latency.
And then, the bookkeeping. The dmap structure itself – what information does it need to store? Physical address, size, mapping information (if applicable), and any associated metadata for the device driver. This includes the logic to track which DMA buffers are currently in use, and which are available to be allocated. How are we ensuring thread safety when accessing this structure? Mutexes, spinlocks, read-write locks - the right choice depends on the specific concurrency profile.
Finally, consider the deallocation. What happens when the DMA buffer is no longer needed? We have to ensure it's properly freed back into the system, and that all associated mappings are cleaned up to avoid memory leaks. The potential for resource exhaustion is a concern, so this needs to be bulletproof.
|
| 179/4 |
2026/06/30 14:47 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/jfs/jfs_dmap.c Name:dbAllocDmap]
Results: map[SourceCode:2082: /*
2083: * NAME: dbAllocDmap()
2084: *
2085: * FUNCTION: adjust the disk allocation map to reflect the allocation
2086: * of a specified block range within a dmap.
2087: *
2088: * this routine allocates the specified blocks from the dmap
2089: * through a call to dbAllocBits(). if the allocation of the
2090: * block range causes the maximum string of free blocks within
2091: * the dmap to change (i.e. the value of the root of the dmap's
2092: * dmtree), this routine will cause this change to be reflected
2093: * up through the appropriate levels of the dmap control pages
2094: * by a call to dbAdjCtl() for the L0 dmap control page that
2095: * covers this dmap.
2096: *
2097: * PARAMETERS:
2098: * bmp - pointer to bmap descriptor
2099: * dp - pointer to dmap to allocate the block range from.
2100: * blkno - starting block number of the block to be allocated.
2101: * nblocks - number of blocks to be allocated.
2102: *
2103: * RETURN VALUES:
2104: * 0 - success
2105: * -EIO - i/o error
2106: *
2107: * serialization: IREAD_LOCK(ipbmap) or IWRITE_LOCK(ipbmap) held on entry/exit;
2108: */
2109: static int dbAllocDmap(struct bmap * bmp, struct dmap * dp, s64 blkno,
2110: int nblocks)
2111: {
2112: s8 oldroot;
2113: int rc;
2114:
2115: /* save the current value of the root (i.e. maximum free string)
2116: * of the dmap tree.
2117: */
2118: oldroot = dp->tree.stree[ROOT];
2119:
2120: /* allocate the specified (blocks) bits */
2121: dbAllocBits(bmp, dp, blkno, nblocks);
2122:
2123: /* if the root has not changed, done. */
2124: if (dp->tree.stree[ROOT] == oldroot)
2125: return (0);
2126:
2127: /* root changed. bubble the change up to the dmap control pages.
2128: * if the adjustment of the upper level control pages fails,
2129: * backout the bit allocation (thus making everything consistent).
2130: */
2131: if ((rc = dbAdjCtl(bmp, blkno, dp->tree.stree[ROOT], 1, 0)))
2132: dbFreeBits(bmp, dp, blkno, nblocks);
2133:
2134: return (rc);
2135: }
SourceFile:fs/jfs/jfs_dmap.c]
|
| 180/4 |
2026/06/30 14:47 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 26511
output: 251
thoughts: 15
Thoughts: Okay, here's my summarized thought process, presented as if I were considering this myself:
**Addressing the `dbAllocDmap` Function**
Right, so I'm looking at the `dbAllocDmap` function, and a crucial optimization jumps out at me. We need to improve its robustness and potentially its efficiency. My immediate thought is, "We need to put that error check *right at the beginning*." I'm talking about a prominent early check. This will prevent a lot of downstream issues, catch invalid states sooner, and generally make the function more resilient. Doing this upfront protects against bad data right away, which is paramount. It's a foundational change that will pay dividends.
|
| 181/4 |
2026/06/30 14:47 |
tool |
codeeditor |
0m
Args: map[CurrentCode:static int dbAllocDmap(struct bmap * bmp, struct dmap * dp, s64 blkno,
int nblocks)
{
s8 oldroot;
int rc;
/* save the current value of the root (i.e. maximum free string)
* of the dmap tree.
*/ NewCode:static int dbAllocDmap(struct bmap * bmp, struct dmap * dp, s64 blkno,
int nblocks)
{
s8 oldroot;
int rc;
if (unlikely(!check_dmaptree(&dp->tree))) {
jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmap page\n");
return -EIO;
}
/* save the current value of the root (i.e. maximum free string)
* of the dmap tree.
*/ SourceFile:fs/jfs/jfs_dmap.c]
Results:
|
| 182/4 |
2026/06/30 14:47 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 26931
output: 39
thoughts: 109
Thoughts: **Checking for Unvalidated `dmaptree` Usage**
Okay, let's take a methodical approach to this. My primary concern is ensuring that the `dmaptree` structure isn't being used directly in any place that could introduce vulnerabilities, without proper validation in the calls. So, I need to methodically review the code. I started by looking at calls to `dbAllocBits` and `dbFreeBits`. I'm relieved to see that they are called from `dbAllocDmap` and `dbFreeDmap`, and those functions now *do* have the necessary checks in place. That's a good start. I also just validated that `dbAllocDmapBU`, `dbAllocDmapLev` and `dbAllocNear` all appear to have the expected validation checks as well. Now, the natural next step is to examine `dbFindLeaf`. I need to confirm that this critical function also validates inputs before interacting with the `dmaptree`. This feels like it's going well, but I need to be thorough.
|
| 183/4 |
2026/06/30 14:47 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/jfs/jfs_dmap.c Name:dbFindLeaf]
Results: map[SourceCode:3011: /*
3012: * NAME: dbFindLeaf()
3013: *
3014: * FUNCTION: search a dmtree_t for sufficient free blocks, returning
3015: * the index of a leaf describing the free blocks if
3016: * sufficient free blocks are found.
3017: *
3018: * the search starts at the top of the dmtree_t tree and
3019: * proceeds down the tree to the leftmost leaf with sufficient
3020: * free space.
3021: *
3022: * PARAMETERS:
3023: * tp - pointer to the tree to be searched.
3024: * l2nb - log2 number of free blocks to search for.
3025: * leafidx - return pointer to be set to the index of the leaf
3026: * describing at least l2nb free blocks if sufficient
3027: * free blocks are found.
3028: * is_ctl - determines if the tree is of type ctl
3029: *
3030: * RETURN VALUES:
3031: * 0 - success
3032: * -ENOSPC - insufficient free blocks.
3033: */
3034: static int dbFindLeaf(dmtree_t *tp, int l2nb, int *leafidx, bool is_ctl)
3035: {
3036: int ti, n = 0, k, x = 0;
3037: int max_size, max_idx;
3038:
3039: max_size = is_ctl ? CTLTREESIZE : TREESIZE;
3040: max_idx = is_ctl ? LPERCTL : LPERDMAP;
3041:
3042: /* first check the root of the tree to see if there is
3043: * sufficient free space.
3044: */
3045: if (l2nb > tp->dmt_stree[ROOT])
3046: return -ENOSPC;
3047:
3048: /* sufficient free space available. now search down the tree
3049: * starting at the next level for the leftmost leaf that
3050: * describes sufficient free space.
3051: */
3052: for (k = le32_to_cpu(tp->dmt_height), ti = 1;
3053: k > 0; k--, ti = ((ti + n) << 2) + 1) {
3054: /* search the four nodes at this level, starting from
3055: * the left.
3056: */
3057: for (x = ti, n = 0; n < 4; n++) {
3058: /* sufficient free space found. move to the next
3059: * level (or quit if this is the last level).
3060: */
3061: if (x + n > max_size)
3062: return -ENOSPC;
3063: if (l2nb <= tp->dmt_stree[x + n])
3064: break;
3065: }
3066:
3067: /* better have found something since the higher
3068: * levels of the tree said it was here.
3069: */
3070: assert(n < 4);
3071: }
3072: if (le32_to_cpu(tp->dmt_leafidx) >= max_idx)
3073: return -ENOSPC;
3074:
3075: /* set the return to the leftmost leaf describing sufficient
3076: * free space.
3077: */
3078: *leafidx = x + n - le32_to_cpu(tp->dmt_leafidx);
3079:
3080: return (0);
3081: }
SourceFile:fs/jfs/jfs_dmap.c]
|
| 184/4 |
2026/06/30 14:47 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 28430
output: 20
thoughts: 69
Thoughts: Here's a breakdown of my thought process:
**Internal Code Analysis and Dependency Mapping**
Okay, so I'm tracing the calls to `dbFindLeaf` within this storage subsystem code. I see it's being invoked from `dbAllocDmapLev`, specifically within a context that now includes `check_dmaptree`. That's interesting; it suggests this particular instance of `dbFindLeaf` might be subject to additional data consistency checks related to the dmap tree structure. I'll need to keep that in mind when examining the code flow there.
Next, I observe that `dbFindLeaf` is also called from `dbAllocCtl`. Aha, that's for handling `dmapctl`, and it has its own associated check, `check_dmapctl`. This tells me that `dbFindLeaf` is likely a core routine for finding a leaf node within a b-tree-like structure (or similar) used for managing both the dmap itself and potentially its control information. The checks suggest integrity is paramount.
Now, given the significance of this function and its presence in both the allocation and control paths, I want to round out my understanding of how it's used. I should dig deeper and examine the calls to `dbSplit` and `dbBackSplit` to see if there's any context I'm missing. They seem like critical operations in the tree management. They likely split the tree and/or balance the tree as needed. I'll check to see if `dbFindLeaf` is related to splitting or balancing in any way.
|
| 185/4 |
2026/06/30 14:47 |
tool |
codesearch-find-references |
0m
Args:
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:dbAdjCtl SourceFile:fs/jfs/jfs_dmap.c SourceLine:2620] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:dbAdjCtl SourceFile:fs/jfs/jfs_dmap.c SourceLine:2664] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:dbAllocBits SourceFile:fs/jfs/jfs_dmap.c SourceLine:2280] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:dbAllocBits SourceFile:fs/jfs/jfs_dmap.c SourceLine:2321] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:dbBackSplit SourceFile:fs/jfs/jfs_dmap.c SourceLine:2829]] TruncatedOutput:false]
|
| 186/4 |
2026/06/30 14:47 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 29137
output: 20
thoughts: 43
Thoughts: Here's my analysis of the situation:
**Investigating Memory Allocation Dependencies**
Okay, so `dbAllocBits` is a key function, and I see it's called within `dbAllocDmap` for allocation and, importantly, during error handling in `dbFreeDmap`. This immediately flags potential issues with memory management and error propagation. Both of those functions include a call to `check_dmaptree`, which likely points to a data structure consistency verification step. Now, I need to understand the relationship, so I'm going to shift focus and look at `dbJoin`. I need to figure out how `dbJoin` interacts, if at all, with these allocation and deallocation routines. This will allow me to build a fuller picture of the memory lifecycle and pinpoint potential areas of concern for race conditions, leaks or inconsistencies.
|
| 187/4 |
2026/06/30 14:47 |
tool |
codesearch-find-references |
0m
Args:
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:dbAdjCtl SourceFile:fs/jfs/jfs_dmap.c SourceLine:2622] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:dbAdjCtl SourceFile:fs/jfs/jfs_dmap.c SourceLine:2652] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:dbFreeBits SourceFile:fs/jfs/jfs_dmap.c SourceLine:2427] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:dbFreeBits SourceFile:fs/jfs/jfs_dmap.c SourceLine:2461]] TruncatedOutput:false]
|
| 188/4 |
2026/06/30 14:47 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 29610
output: 21
thoughts: 56
Thoughts: Here's my summary, as the expert:
**Investigating `dbFreeBits` Usage**
Okay, so I'm looking at `dbFreeBits` and tracing its calls. It's called from a few key places: `dbFreeDmap`, and then on error paths from `dbAllocDmapBU` and `dbAllocDmap`. Importantly, all of these call sites also include a call to `check_dmaptree`. That's the crucial context. Now, what's interesting here isn't *that* it's called, but *where* and *why*. I need to understand the implications of releasing these bits under these circumstances, especially with that tree check. My next step is to drill into `dbAdjTree` and see how it fits into this picture. Knowing how this tree is being adjusted after releasing memory will be key to understanding the overall integrity of the system and whether there are any potential race conditions, memory leaks or other bugs lurking. Let's see how that function works.
|
| 189/4 |
2026/06/30 14:47 |
tool |
codesearch-find-references |
0m
Args:
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:dbJoin SourceFile:fs/jfs/jfs_dmap.c SourceLine:2912] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:dbJoin SourceFile:fs/jfs/jfs_dmap.c SourceLine:2917] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:dbJoin SourceFile:fs/jfs/jfs_dmap.c SourceLine:2930] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:dbSplit SourceFile:fs/jfs/jfs_dmap.c SourceLine:2735] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:dbSplit SourceFile:fs/jfs/jfs_dmap.c SourceLine:2747]] TruncatedOutput:false]
|
| 190/4 |
2026/06/30 14:47 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 30183
output: 39
thoughts: 27
Thoughts: Okay, here's my take on those thoughts, framed as my own:
**Reviewing `dmaptree` Operation Entry Points**
Alright, after this review, I'm pretty confident we've got all the bases covered regarding the potential entry points for operations within the `dmaptree` module. We've traced through the major functions and identified where the relevant calls are made. It feels pretty thorough, but given the critical nature of this, let's just do a quick double-check on `dbAllocAG`. That specific function seems like a likely candidate for potential issues, so it's worth a second look just to be absolutely certain we haven't overlooked anything, especially considering its role in allocation, which always has interesting implications for memory management and potential race conditions. Just a quick sanity check to ensure our comprehensive approach is truly complete.
|
| 191/4 |
2026/06/30 14:47 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/jfs/jfs_dmap.c Name:dbAllocAG]
Results: map[SourceCode:1344: /*
1345: * NAME: dbAllocAG()
1346: *
1347: * FUNCTION: attempt to allocate the specified number of contiguous
1348: * free blocks within the specified allocation group.
1349: *
1350: * unless the allocation group size is equal to the number
1351: * of blocks per dmap, the dmap control pages will be used to
1352: * find the required free space, if available. we start the
1353: * search at the highest dmap control page level which
1354: * distinctly describes the allocation group's free space
1355: * (i.e. the highest level at which the allocation group's
1356: * free space is not mixed in with that of any other group).
1357: * in addition, we start the search within this level at a
1358: * height of the dmapctl dmtree at which the nodes distinctly
1359: * describe the allocation group's free space. at this height,
1360: * the allocation group's free space may be represented by 1
1361: * or two sub-trees, depending on the allocation group size.
1362: * we search the top nodes of these subtrees left to right for
1363: * sufficient free space. if sufficient free space is found,
1364: * the subtree is searched to find the leftmost leaf that
1365: * has free space. once we have made it to the leaf, we
1366: * move the search to the next lower level dmap control page
1367: * corresponding to this leaf. we continue down the dmap control
1368: * pages until we find the dmap that contains or starts the
1369: * sufficient free space and we allocate at this dmap.
1370: *
1371: * if the allocation group size is equal to the dmap size,
1372: * we'll start at the dmap corresponding to the allocation
1373: * group and attempt the allocation at this level.
1374: *
1375: * the dmap control page search is also not performed if the
1376: * allocation group is completely free and we go to the first
1377: * dmap of the allocation group to do the allocation. this is
1378: * done because the allocation group may be part (not the first
1379: * part) of a larger binary buddy system, causing the dmap
1380: * control pages to indicate no free space (NOFREE) within
1381: * the allocation group.
1382: *
1383: * PARAMETERS:
1384: * bmp - pointer to bmap descriptor
1385: * agno - allocation group number.
1386: * nblocks - actual number of contiguous free blocks desired.
1387: * l2nb - log2 number of contiguous free blocks desired.
1388: * results - on successful return, set to the starting block number
1389: * of the newly allocated range.
1390: *
1391: * RETURN VALUES:
1392: * 0 - success
1393: * -ENOSPC - insufficient disk resources
1394: * -EIO - i/o error
1395: *
1396: * note: IWRITE_LOCK(ipmap) held on entry/exit;
1397: */
1398: static int
1399: dbAllocAG(struct bmap * bmp, int agno, s64 nblocks, int l2nb, s64 * results)
1400: {
1401: struct metapage *mp;
1402: struct dmapctl *dcp;
1403: int rc, ti, i, k, m, n, agperlev;
1404: s64 blkno, lblkno;
1405: int budmin;
1406:
1407: /* allocation request should not be for more than the
1408: * allocation group size.
1409: */
1410: if (l2nb > bmp->db_agl2size) {
1411: jfs_error(bmp->db_ipbmap->i_sb,
1412: "allocation request is larger than the allocation group size\n");
1413: return -EIO;
1414: }
1415:
1416: /* determine the starting block number of the allocation
1417: * group.
1418: */
1419: blkno = (s64) agno << bmp->db_agl2size;
1420:
1421: /* check if the allocation group size is the minimum allocation
1422: * group size or if the allocation group is completely free. if
1423: * the allocation group size is the minimum size of BPERDMAP (i.e.
1424: * 1 dmap), there is no need to search the dmap control page (below)
1425: * that fully describes the allocation group since the allocation
1426: * group is already fully described by a dmap. in this case, we
1427: * just call dbAllocCtl() to search the dmap tree and allocate the
1428: * required space if available.
1429: *
1430: * if the allocation group is completely free, dbAllocCtl() is
1431: * also called to allocate the required space. this is done for
1432: * two reasons. first, it makes no sense searching the dmap control
1433: * pages for free space when we know that free space exists. second,
1434: * the dmap control pages may indicate that the allocation group
1435: * has no free space if the allocation group is part (not the first
1436: * part) of a larger binary buddy system.
1437: */
1438: if (bmp->db_agsize == BPERDMAP
1439: || bmp->db_agfree[agno] == bmp->db_agsize) {
1440: rc = dbAllocCtl(bmp, nblocks, l2nb, blkno, results);
1441: if ((rc == -ENOSPC) &&
1442: (bmp->db_agfree[agno] == bmp->db_agsize)) {
1443: printk(KERN_ERR "blkno = %Lx, blocks = %Lx\n",
1444: (unsigned long long) blkno,
1445: (unsigned long long) nblocks);
1446: jfs_error(bmp->db_ipbmap->i_sb,
1447: "dbAllocCtl failed in free AG\n");
1448: }
1449: return (rc);
1450: }
1451:
1452: /* the buffer for the dmap control page that fully describes the
1453: * allocation group.
1454: */
1455: lblkno = BLKTOCTL(blkno, bmp->db_l2nbperpage, bmp->db_aglevel);
1456: mp = read_metapage(bmp->db_ipbmap, lblkno, PSIZE, 0);
1457: if (mp == NULL)
1458: return -EIO;
1459: dcp = (struct dmapctl *) mp->data;
1460: budmin = dcp->budmin;
1461:
1462: if (unlikely(!check_dmapctl(dcp))) {
1463: jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmapctl page\n");
1464: release_metapage(mp);
1465: return -EIO;
1466: }
1467:
1468: /* search the subtree(s) of the dmap control page that describes
1469: * the allocation group, looking for sufficient free space. to begin,
1470: * determine how many allocation groups are represented in a dmap
1471: * control page at the control page level (i.e. L0, L1, L2) that
1472: * fully describes an allocation group. next, determine the starting
1473: * tree index of this allocation group within the control page.
1474: */
1475: agperlev =
1476: (1 << (L2LPERCTL - (bmp->db_agheight << 1))) / bmp->db_agwidth;
1477: ti = bmp->db_agstart + bmp->db_agwidth * (agno & (agperlev - 1));
1478:
1479: if (ti < 0 || ti >= le32_to_cpu(dcp->nleafs)) {
1480: jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmapctl page\n");
1481: release_metapage(mp);
1482: return -EIO;
1483: }
1484:
1485: /* dmap control page trees fan-out by 4 and a single allocation
1486: * group may be described by 1 or 2 subtrees within the ag level
1487: * dmap control page, depending upon the ag size. examine the ag's
1488: * subtrees for sufficient free space, starting with the leftmost
1489: * subtree.
1490: */
1491: for (i = 0; i < bmp->db_agwidth; i++, ti++) {
1492: /* is there sufficient free space ?
1493: */
1494: if (l2nb > dcp->stree[ti])
1495: continue;
1496:
1497: /* sufficient free space found in a subtree. now search down
1498: * the subtree to find the leftmost leaf that describes this
1499: * free space.
1500: */
1501: for (k = bmp->db_agheight; k > 0; k--) {
1502: for (n = 0, m = (ti << 2) + 1; n < 4; n++) {
1503: if (l2nb <= dcp->stree[m + n]) {
1504: ti = m + n;
1505: break;
1506: }
1507: }
1508: if (n == 4) {
1509: jfs_error(bmp->db_ipbmap->i_sb,
1510: "failed descending stree\n");
1511: release_metapage(mp);
1512: return -EIO;
1513: }
1514: }
1515:
1516: /* determine the block number within the file system
1517: * that corresponds to this leaf.
1518: */
1519: if (bmp->db_aglevel == 2)
1520: blkno = 0;
1521: else if (bmp->db_aglevel == 1)
1522: blkno &= ~(MAXL1SIZE - 1);
1523: else /* bmp->db_aglevel == 0 */
1524: blkno &= ~(MAXL0SIZE - 1);
1525:
1526: blkno +=
1527: ((s64) (ti - le32_to_cpu(dcp->leafidx))) << budmin;
1528:
1529: /* release the buffer in preparation for going down
1530: * the next level of dmap control pages.
1531: */
1532: release_metapage(mp);
1533:
1534: /* check if we need to continue to search down the lower
1535: * level dmap control pages. we need to if the number of
1536: * blocks required is less than maximum number of blocks
1537: * described at the next lower level.
1538: */
1539: if (l2nb < budmin) {
1540:
1541: /* search the lower level dmap control pages to get
1542: * the starting block number of the dmap that
1543: * contains or starts off the free space.
1544: */
1545: if ((rc =
1546: dbFindCtl(bmp, l2nb, bmp->db_aglevel - 1,
1547: &blkno))) {
1548: if (rc == -ENOSPC) {
1549: jfs_error(bmp->db_ipbmap->i_sb,
1550: "control page inconsistent\n");
1551: return -EIO;
1552: }
1553: return (rc);
1554: }
1555: }
1556:
1557: /* allocate the blocks.
1558: */
1559: rc = dbAllocCtl(bmp, nblocks, l2nb, blkno, results);
1560: if (rc == -ENOSPC) {
1561: jfs_error(bmp->db_ipbmap->i_sb,
1562: "unable to allocate blocks\n");
1563: rc = -EIO;
1564: }
1565: return (rc);
1566: }
1567:
1568: /* no space in the allocation group. release the buffer and
1569: * return -ENOSPC.
1570: */
1571: release_metapage(mp);
1572:
1573: return -ENOSPC;
1574: }
SourceFile:fs/jfs/jfs_dmap.c]
|
| 192/4 |
2026/06/30 14:47 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 34283
output: 40
thoughts: 173
Thoughts: **Reviewing the DMAP Allocation Changes**
Okay, so `dbAllocAG` is working with `dmapctl`, and we already have `check_dmapctl` in place. Good. Now, let's step through the recent modifications I've made, specifically focusing on the DMAP tree integrity.
I introduced this `check_dmaptree` function, which is critical. I've now integrated it into a few core allocation/deallocation routines. Specifically, I've added a call to `check_dmaptree` wrapped in an `unlikely` conditional, checking for a potential issue, to the following:
* `dbFreeDmap`: Ensures the deallocation process doesn't corrupt the tree.
* `dbAllocNext`: I've replaced the old `leafidx` check (`if (dp->tree.leafidx != cpu_to_le32(LEAFIND))`) with this `check_dmaptree` call to provide more robust verification of the tree's health.
* `dbAllocNear`: Same rationale as `dbAllocNext` – replacing the `leafidx` check with the `check_dmaptree` call for improved stability.
* `dbAllocDmapLev`, `dbAllocDmapBU`, and `dbAllocDmap`: I've included the `check_dmaptree` within these functions as well, which are involved with level, bottom-up, and general DMAP allocation, respectively. This should improve their reliability.
Before I go further, I should verify the `dbAllocCtl` function, just to ensure that those changes were correctly integrated.
|
| 193/4 |
2026/06/30 14:47 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:fs/jfs/jfs_dmap.c Name:dbAllocCtl]
Results: map[SourceCode:1841: /*
1842: * NAME: dbAllocCtl()
1843: *
1844: * FUNCTION: attempt to allocate a specified number of contiguous
1845: * blocks starting within a specific dmap.
1846: *
1847: * this routine is called by higher level routines that search
1848: * the dmap control pages above the actual dmaps for contiguous
1849: * free space. the result of successful searches by these
1850: * routines are the starting block numbers within dmaps, with
1851: * the dmaps themselves containing the desired contiguous free
1852: * space or starting a contiguous free space of desired size
1853: * that is made up of the blocks of one or more dmaps. these
1854: * calls should not fail due to insufficent resources.
1855: *
1856: * this routine is called in some cases where it is not known
1857: * whether it will fail due to insufficient resources. more
1858: * specifically, this occurs when allocating from an allocation
1859: * group whose size is equal to the number of blocks per dmap.
1860: * in this case, the dmap control pages are not examined prior
1861: * to calling this routine (to save pathlength) and the call
1862: * might fail.
1863: *
1864: * for a request size that fits within a dmap, this routine relies
1865: * upon the dmap's dmtree to find the requested contiguous free
1866: * space. for request sizes that are larger than a dmap, the
1867: * requested free space will start at the first block of the
1868: * first dmap (i.e. blkno).
1869: *
1870: * PARAMETERS:
1871: * bmp - pointer to bmap descriptor
1872: * nblocks - actual number of contiguous free blocks to allocate.
1873: * l2nb - log2 number of contiguous free blocks to allocate.
1874: * blkno - starting block number of the dmap to start the allocation
1875: * from.
1876: * results - on successful return, set to the starting block number
1877: * of the newly allocated range.
1878: *
1879: * RETURN VALUES:
1880: * 0 - success
1881: * -ENOSPC - insufficient disk resources
1882: * -EIO - i/o error
1883: *
1884: * serialization: IWRITE_LOCK(ipbmap) held on entry/exit;
1885: */
1886: static int
1887: dbAllocCtl(struct bmap * bmp, s64 nblocks, int l2nb, s64 blkno, s64 * results)
1888: {
1889: int rc, nb;
1890: s64 b, lblkno, n;
1891: struct metapage *mp;
1892: struct dmap *dp;
1893:
1894: /* check if the allocation request is confined to a single dmap.
1895: */
1896: if (l2nb <= L2BPERDMAP) {
1897: /* get the buffer for the dmap.
1898: */
1899: lblkno = BLKTODMAP(blkno, bmp->db_l2nbperpage);
1900: mp = read_metapage(bmp->db_ipbmap, lblkno, PSIZE, 0);
1901: if (mp == NULL)
1902: return -EIO;
1903: dp = (struct dmap *) mp->data;
1904:
1905: if (dp->tree.budmin < 0) {
1906: release_metapage(mp);
1907: return -EIO;
1908: }
1909:
1910: /* try to allocate the blocks.
1911: */
1912: rc = dbAllocDmapLev(bmp, dp, (int) nblocks, l2nb, results);
1913: if (rc == 0)
1914: mark_metapage_dirty(mp);
1915:
1916: release_metapage(mp);
1917:
1918: return (rc);
1919: }
1920:
1921: /* allocation request involving multiple dmaps. it must start on
1922: * a dmap boundary.
1923: */
1924: assert((blkno & (BPERDMAP - 1)) == 0);
1925:
1926: /* allocate the blocks dmap by dmap.
1927: */
1928: for (n = nblocks, b = blkno; n > 0; n -= nb, b += nb) {
1929: /* get the buffer for the dmap.
1930: */
1931: lblkno = BLKTODMAP(b, bmp->db_l2nbperpage);
1932: mp = read_metapage(bmp->db_ipbmap, lblkno, PSIZE, 0);
1933: if (mp == NULL) {
1934: rc = -EIO;
1935: goto backout;
1936: }
1937: dp = (struct dmap *) mp->data;
1938:
1939: /* the dmap better be all free.
1940: */
1941: if (dp->tree.stree[ROOT] != L2BPERDMAP) {
1942: release_metapage(mp);
1943: jfs_error(bmp->db_ipbmap->i_sb,
1944: "the dmap is not all free\n");
1945: rc = -EIO;
1946: goto backout;
1947: }
1948:
1949: /* determine how many blocks to allocate from this dmap.
1950: */
1951: nb = min_t(s64, n, BPERDMAP);
1952:
1953: /* allocate the blocks from the dmap.
1954: */
1955: if ((rc = dbAllocDmap(bmp, dp, b, nb))) {
1956: release_metapage(mp);
1957: goto backout;
1958: }
1959:
1960: /* write the buffer.
1961: */
1962: write_metapage(mp);
1963: }
1964:
1965: /* set the results (starting block number) and return.
1966: */
1967: *results = blkno;
1968: return (0);
1969:
1970: /* something failed in handling an allocation request involving
1971: * multiple dmaps. we'll try to clean up by backing out any
1972: * allocation that has already happened for this request. if
1973: * we fail in backing out the allocation, we'll mark the file
1974: * system to indicate that blocks have been leaked.
1975: */
1976: backout:
1977:
1978: /* try to backout the allocations dmap by dmap.
1979: */
1980: for (n = nblocks - n, b = blkno; n > 0;
1981: n -= BPERDMAP, b += BPERDMAP) {
1982: /* get the buffer for this dmap.
1983: */
1984: lblkno = BLKTODMAP(b, bmp->db_l2nbperpage);
1985: mp = read_metapage(bmp->db_ipbmap, lblkno, PSIZE, 0);
1986: if (mp == NULL) {
1987: /* could not back out. mark the file system
1988: * to indicate that we have leaked blocks.
1989: */
1990: jfs_error(bmp->db_ipbmap->i_sb,
1991: "I/O Error: Block Leakage\n");
1992: continue;
1993: }
1994: dp = (struct dmap *) mp->data;
1995:
1996: /* free the blocks is this dmap.
1997: */
1998: if (dbFreeDmap(bmp, dp, b, BPERDMAP)) {
1999: /* could not back out. mark the file system
2000: * to indicate that we have leaked blocks.
2001: */
2002: release_metapage(mp);
2003: jfs_error(bmp->db_ipbmap->i_sb, "Block Leakage\n");
2004: continue;
2005: }
2006:
2007: /* write the buffer.
2008: */
2009: write_metapage(mp);
2010: }
2011:
2012: return (rc);
2013: }
SourceFile:fs/jfs/jfs_dmap.c]
|
| 194/4 |
2026/06/30 14:47 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 37517
output: 15
thoughts: 374
Thoughts: **Reviewing the DMAP Allocation/Freeing Code**
Okay, so I'm looking at this `dbAllocCtl` function, and it's calling a few other functions, specifically `dbAllocDmapLev`, `dbAllocDmap`, and `dbFreeDmap`. All of those guys have `check_dmaptree` calls in them, which is good. But `dbAllocCtl` itself is also doing some checks on the `dp->tree` structure *before* it calls those other functions. Specifically, I see it's checking `dp->tree.budmin < 0` and also if `dp->tree.stree[ROOT]` is not equal to `L2BPERDMAP`.
I was initially thinking that since `dbAllocDmapLev` and `dbAllocDmap` also check `dp->tree`, maybe it was redundant. But then I realized that `dbAllocCtl` accesses `dp->tree.stree[ROOT]` *before* it calls any of those other functions. Hmm, so that's a point of potential concern. Let's look at that access:
```c
if (dp->tree.stree[ROOT] != L2BPERDMAP) {
```
Right, okay, so it's checking `dp->tree.stree[ROOT]`. Now, `ROOT` is just a constant of 1, so it's always accessing the second element (`stree[1]`) of the array. The `stree` array itself has a size of `TREESIZE`, which is defined as 341. So, access to `stree[1]` is always in bounds. So, the potential out-of-bounds issue I was initially worried about is not an issue.
However, it's still checking `dp->tree.budmin < 0`.
The question then becomes: Is it worth adding a `check_dmaptree` call *inside* `dbAllocCtl` itself? The documentation I have says to put `check_dmaptree` in functions that operate on the `dmap` structure. So, I added it to all the functions specified. I think I will leave it out for now.
Let's do a quick run over using `patch-diff` and then I'll sign off.
|
| 195/4 |
2026/06/30 14:47 |
tool |
patch-diff |
0m
Args:
Results: map[Output:diff --git a/fs/jfs/jfs_dmap.c b/fs/jfs/jfs_dmap.c
index a841cf21d..d2cd7d2e8 100644
--- a/fs/jfs/jfs_dmap.c
+++ b/fs/jfs/jfs_dmap.c
@@ -136,104 +136,184 @@ static const s8 budtab[256] = {
/*
* check_dmapctl - Validate integrity of a dmapctl structure
* @dcp: Pointer to the dmapctl structure to check
*
* Return: true if valid, false if corrupted
*/
static bool check_dmapctl(struct dmapctl *dcp)
{
s8 budmin = dcp->budmin;
u32 nleafs, l2nleafs, leafidx, height;
int i;
nleafs = le32_to_cpu(dcp->nleafs);
/* Check basic field ranges */
if (unlikely(nleafs > LPERCTL)) {
jfs_err("dmapctl: invalid nleafs %u (max %u)",
nleafs, LPERCTL);
return false;
}
l2nleafs = le32_to_cpu(dcp->l2nleafs);
if (unlikely(l2nleafs > L2LPERCTL)) {
jfs_err("dmapctl: invalid l2nleafs %u (max %u)",
l2nleafs, L2LPERCTL);
return false;
}
/* Verify nleafs matches l2nleafs (must be power of two) */
if (unlikely((1U << l2nleafs) != nleafs)) {
jfs_err("dmapctl: nleafs %u != 2^%u",
nleafs, l2nleafs);
return false;
}
leafidx = le32_to_cpu(dcp->leafidx);
/* Check leaf index matches expected position */
if (unlikely(leafidx != CTLLEAFIND)) {
jfs_err("dmapctl: invalid leafidx %u (expected %u)",
leafidx, CTLLEAFIND);
return false;
}
height = le32_to_cpu(dcp->height);
/* Check tree height is within valid range */
if (unlikely(height > (L2LPERCTL >> 1))) {
jfs_err("dmapctl: invalid height %u (max %u)",
height, L2LPERCTL >> 1);
return false;
}
/* Check budmin is valid (cannot be NOFREE for non-empty tree) */
if (budmin == NOFREE) {
if (unlikely(nleafs > 0)) {
jfs_err("dmapctl: budmin is NOFREE but nleafs %u",
nleafs);
return false;
}
} else if (unlikely(budmin < BUDMIN)) {
jfs_err("dmapctl: invalid budmin %d (min %d)",
budmin, BUDMIN);
return false;
}
/* Check leaf nodes fit within stree array */
if (unlikely(leafidx + nleafs > CTLTREESIZE)) {
jfs_err("dmapctl: leaf range exceeds stree size (end %u > %u)",
leafidx + nleafs, CTLTREESIZE);
return false;
}
/* Check leaf nodes have valid values */
for (i = leafidx; i < leafidx + nleafs; i++) {
s8 val = dcp->stree[i];
if (unlikely(val < NOFREE)) {
jfs_err("dmapctl: invalid leaf value %d at index %d",
val, i);
return false;
} else if (unlikely(val > 31)) {
jfs_err("dmapctl: leaf value %d too large at index %d", val, i);
return false;
}
}
return true;
}
+/*
+ * check_dmaptree - Validate integrity of a dmaptree structure
+ * @dtp: Pointer to the dmaptree structure to check
+ *
+ * Return: true if valid, false if corrupted
+ */
+static bool check_dmaptree(struct dmaptree *dtp)
+{
+ u32 nleafs, l2nleafs, leafidx, height;
+ int i;
+
+ nleafs = le32_to_cpu(dtp->nleafs);
+ /* Check basic field ranges */
+ if (unlikely(nleafs > LPERDMAP)) {
+ jfs_err("dmaptree: invalid nleafs %u (max %u)",
+ nleafs, LPERDMAP);
+ return false;
+ }
+
+ l2nleafs = le32_to_cpu(dtp->l2nleafs);
+ if (unlikely(l2nleafs > L2LPERDMAP)) {
+ jfs_err("dmaptree: invalid l2nleafs %u (max %u)",
+ l2nleafs, L2LPERDMAP);
+ return false;
+ }
+
+ /* Verify nleafs matches l2nleafs (must be power of two) */
+ if (unlikely((1U << l2nleafs) != nleafs)) {
+ jfs_err("dmaptree: nleafs %u != 2^%u",
+ nleafs, l2nleafs);
+ return false;
+ }
+
+ leafidx = le32_to_cpu(dtp->leafidx);
+ /* Check leaf index matches expected position */
+ if (unlikely(leafidx != LEAFIND)) {
+ jfs_err("dmaptree: invalid leafidx %u (expected %u)",
+ leafidx, LEAFIND);
+ return false;
+ }
+
+ height = le32_to_cpu(dtp->height);
+ /* Check tree height is within valid range */
+ if (unlikely(height > 4)) {
+ jfs_err("dmaptree: invalid height %u (max 4)",
+ height);
+ return false;
+ }
+
+ /* Check budmin is valid */
+ if (unlikely(dtp->budmin < BUDMIN)) {
+ jfs_err("dmaptree: invalid budmin %d (min %d)",
+ dtp->budmin, BUDMIN);
+ return false;
+ }
+
+ /* Check leaf nodes fit within stree array */
+ if (unlikely(leafidx + nleafs > TREESIZE)) {
+ jfs_err("dmaptree: leaf range exceeds stree size (end %u > %u)",
+ leafidx + nleafs, TREESIZE);
+ return false;
+ }
+
+ /* Check leaf nodes have valid values */
+ for (i = leafidx; i < leafidx + nleafs; i++) {
+ s8 val = dtp->stree[i];
+
+ if (unlikely(val < NOFREE)) {
+ jfs_err("dmaptree: invalid leaf value %d at index %d",
+ val, i);
+ return false;
+ } else if (unlikely(val > 31)) {
+ jfs_err("dmaptree: leaf value %d too large at index %d", val, i);
+ return false;
+ }
+ }
+
+ return true;
+}
+
/*
* NAME: dbMount()
*
* FUNCTION: initializate the block allocation map.
*
* memory is allocated for the in-core bmap descriptor and
* the in-core descriptor is initialized from disk.
*
* PARAMETERS:
* ipbmap - pointer to in-core inode for the block map.
*
* RETURN VALUES:
* 0 - success
* -ENOMEM - insufficient memory
* -EIO - i/o error
* -EINVAL - wrong bmap data
*/
@@ -1139,259 +1219,259 @@ static int dbExtend(struct inode *ip, s64 blkno, s64 nblocks, s64 addnblocks)
/*
* NAME: dbAllocNext()
*
* FUNCTION: attempt to allocate the blocks of the specified block
* range within a dmap.
*
* PARAMETERS:
* bmp - pointer to bmap descriptor
* dp - pointer to dmap.
* blkno - starting block number of the range.
* nblocks - number of contiguous free blocks of the range.
*
* RETURN VALUES:
* 0 - success
* -ENOSPC - insufficient disk resources
* -EIO - i/o error
*
* serialization: IREAD_LOCK(ipbmap) held on entry/exit;
*/
static int dbAllocNext(struct bmap * bmp, struct dmap * dp, s64 blkno,
int nblocks)
{
int dbitno, word, rembits, nb, nwords, wbitno, nw;
int l2size;
s8 *leaf;
u32 mask;
- if (dp->tree.leafidx != cpu_to_le32(LEAFIND)) {
+ if (unlikely(!check_dmaptree(&dp->tree))) {
jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmap page\n");
return -EIO;
}
/* pick up a pointer to the leaves of the dmap tree.
*/
leaf = dp->tree.stree + le32_to_cpu(dp->tree.leafidx);
/* determine the bit number and word within the dmap of the
* starting block.
*/
dbitno = blkno & (BPERDMAP - 1);
word = dbitno >> L2DBWORD;
/* check if the specified block range is contained within
* this dmap.
*/
if (dbitno + nblocks > BPERDMAP)
return -ENOSPC;
/* check if the starting leaf indicates that anything
* is free.
*/
if (leaf[word] == NOFREE)
return -ENOSPC;
/* check the dmaps words corresponding to block range to see
* if the block range is free. not all bits of the first and
* last words may be contained within the block range. if this
* is the case, we'll work against those words (i.e. partial first
* and/or last) on an individual basis (a single pass) and examine
* the actual bits to determine if they are free. a single pass
* will be used for all dmap words fully contained within the
* specified range. within this pass, the leaves of the dmap
* tree will be examined to determine if the blocks are free. a
* single leaf may describe the free space of multiple dmap
* words, so we may visit only a subset of the actual leaves
* corresponding to the dmap words of the block range.
*/
for (rembits = nblocks; rembits > 0; rembits -= nb, dbitno += nb) {
/* determine the bit number within the word and
* the number of bits within the word.
*/
wbitno = dbitno & (DBWORD - 1);
nb = min(rembits, DBWORD - wbitno);
/* check if only part of the word is to be examined.
*/
if (nb < DBWORD) {
/* check if the bits are free.
*/
mask = (ONES << (DBWORD - nb) >> wbitno);
if ((mask & ~le32_to_cpu(dp->wmap[word])) != mask)
return -ENOSPC;
word += 1;
} else {
/* one or more dmap words are fully contained
* within the block range. determine how many
* words and how many bits.
*/
nwords = rembits >> L2DBWORD;
nb = nwords << L2DBWORD;
/* now examine the appropriate leaves to determine
* if the blocks are free.
*/
while (nwords > 0) {
/* does the leaf describe any free space ?
*/
if (leaf[word] < BUDMIN)
return -ENOSPC;
/* determine the l2 number of bits provided
* by this leaf.
*/
l2size =
min_t(int, leaf[word], NLSTOL2BSZ(nwords));
/* determine how many words were handled.
*/
nw = BUDSIZE(l2size, BUDMIN);
nwords -= nw;
word += nw;
}
}
}
/* allocate the blocks.
*/
return (dbAllocDmap(bmp, dp, blkno, nblocks));
}
/*
* NAME: dbAllocNear()
*
* FUNCTION: attempt to allocate a number of contiguous free blocks near
* a specified block (hint) within a dmap.
*
* starting with the dmap leaf that covers the hint, we'll
* check the next four contiguous leaves for sufficient free
* space. if sufficient free space is found, we'll allocate
* the desired free space.
*
* PARAMETERS:
* bmp - pointer to bmap descriptor
* dp - pointer to dmap.
* blkno - block number to allocate near.
* nblocks - actual number of contiguous free blocks desired.
* l2nb - log2 number of contiguous free blocks desired.
* results - on successful return, set to the starting block number
* of the newly allocated range.
*
* RETURN VALUES:
* 0 - success
* -ENOSPC - insufficient disk resources
* -EIO - i/o error
*
* serialization: IREAD_LOCK(ipbmap) held on entry/exit;
*/
static int
dbAllocNear(struct bmap * bmp,
struct dmap * dp, s64 blkno, int nblocks, int l2nb, s64 * results)
{
int word, lword, rc;
s8 *leaf;
- if (dp->tree.leafidx != cpu_to_le32(LEAFIND)) {
+ if (unlikely(!check_dmaptree(&dp->tree))) {
jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmap page\n");
return -EIO;
}
leaf = dp->tree.stree + le32_to_cpu(dp->tree.leafidx);
/* determine the word within the dmap that holds the hint
* (i.e. blkno). also, determine the last word in the dmap
* that we'll include in our examination.
*/
word = (blkno & (BPERDMAP - 1)) >> L2DBWORD;
lword = min(word + 4, LPERDMAP);
/* examine the leaves for sufficient free space.
*/
for (; word < lword; word++) {
/* does the leaf describe sufficient free space ?
*/
if (leaf[word] < l2nb)
continue;
/* determine the block number within the file system
* of the first block described by this dmap word.
*/
blkno = le64_to_cpu(dp->start) + (word << L2DBWORD);
/* if not all bits of the dmap word are free, get the
* starting bit number within the dmap word of the required
* string of free bits and adjust the block number with the
* value.
*/
if (leaf[word] < BUDMIN)
blkno +=
dbFindBits(le32_to_cpu(dp->wmap[word]), l2nb);
/* allocate the blocks.
*/
if ((rc = dbAllocDmap(bmp, dp, blkno, nblocks)) == 0)
*results = blkno;
return (rc);
}
return -ENOSPC;
}
/*
* NAME: dbAllocAG()
*
* FUNCTION: attempt to allocate the specified number of contiguous
* free blocks within the specified allocation group.
*
* unless the allocation group size is equal to the number
* of blocks per dmap, the dmap control pages will be used to
* find the required free space, if available. we start the
* search at the highest dmap control page level which
* distinctly describes the allocation group's free space
* (i.e. the highest level at which the allocation group's
* free space is not mixed in with that of any other group).
* in addition, we start the search within this level at a
* height of the dmapctl dmtree at which the nodes distinctly
* describe the allocation group's free space. at this height,
* the allocation group's free space may be represented by 1
* or two sub-trees, depending on the allocation group size.
* we search the top nodes of these subtrees left to right for
* sufficient free space. if sufficient free space is found,
* the subtree is searched to find the leftmost leaf that
* has free space. once we have made it to the leaf, we
* move the search to the next lower level dmap control page
* corresponding to this leaf. we continue down the dmap control
* pages until we find the dmap that contains or starts the
* sufficient free space and we allocate at this dmap.
*
* if the allocation group size is equal to the dmap size,
* we'll start at the dmap corresponding to the allocation
* group and attempt the allocation at this level.
*
* the dmap control page search is also not performed if the
* allocation group is completely free and we go to the first
* dmap of the allocation group to do the allocation. this is
* done because the allocation group may be part (not the first
* part) of a larger binary buddy system, causing the dmap
* control pages to indicate no free space (NOFREE) within
* the allocation group.
*
* PARAMETERS:
* bmp - pointer to bmap descriptor
* agno - allocation group number.
* nblocks - actual number of contiguous free blocks desired.
* l2nb - log2 number of contiguous free blocks desired.
* results - on successful return, set to the starting block number
* of the newly allocated range.
*
* RETURN VALUES:
* 0 - success
* -ENOSPC - insufficient disk resources
* -EIO - i/o error
*
* note: IWRITE_LOCK(ipmap) held on entry/exit;
*/
@@ -2039,187 +2119,202 @@ dbAllocCtl(struct bmap * bmp, s64 nblocks, int l2nb, s64 blkno, s64 * results)
* serialization: IREAD_LOCK(ipbmap), e.g., from dbAlloc(), or
* IWRITE_LOCK(ipbmap), e.g., dbAllocCtl(), held on entry/exit;
*/
static int
dbAllocDmapLev(struct bmap * bmp,
struct dmap * dp, int nblocks, int l2nb, s64 * results)
{
s64 blkno;
int leafidx, rc;
+ if (unlikely(!check_dmaptree(&dp->tree))) {
+ jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmap page\n");
+ return -EIO;
+ }
+
/* can't be more than a dmaps worth of blocks */
assert(l2nb <= L2BPERDMAP);
/* search the tree within the dmap page for sufficient
* free space. if sufficient free space is found, dbFindLeaf()
* returns the index of the leaf at which free space was found.
*/
if (dbFindLeaf((dmtree_t *) &dp->tree, l2nb, &leafidx, false))
return -ENOSPC;
if (leafidx < 0)
return -EIO;
/* determine the block number within the file system corresponding
* to the leaf at which free space was found.
*/
blkno = le64_to_cpu(dp->start) + (leafidx << L2DBWORD);
/* if not all bits of the dmap word are free, get the starting
* bit number within the dmap word of the required string of free
* bits and adjust the block number with this value.
*/
if (dp->tree.stree[leafidx + LEAFIND] < BUDMIN)
blkno += dbFindBits(le32_to_cpu(dp->wmap[leafidx]), l2nb);
/* allocate the blocks */
if ((rc = dbAllocDmap(bmp, dp, blkno, nblocks)) == 0)
*results = blkno;
return (rc);
}
/*
* NAME: dbAllocDmap()
*
* FUNCTION: adjust the disk allocation map to reflect the allocation
* of a specified block range within a dmap.
*
* this routine allocates the specified blocks from the dmap
* through a call to dbAllocBits(). if the allocation of the
* block range causes the maximum string of free blocks within
* the dmap to change (i.e. the value of the root of the dmap's
* dmtree), this routine will cause this change to be reflected
* up through the appropriate levels of the dmap control pages
* by a call to dbAdjCtl() for the L0 dmap control page that
* covers this dmap.
*
* PARAMETERS:
* bmp - pointer to bmap descriptor
* dp - pointer to dmap to allocate the block range from.
* blkno - starting block number of the block to be allocated.
* nblocks - number of blocks to be allocated.
*
* RETURN VALUES:
* 0 - success
* -EIO - i/o error
*
* serialization: IREAD_LOCK(ipbmap) or IWRITE_LOCK(ipbmap) held on entry/exit;
*/
static int dbAllocDmap(struct bmap * bmp, struct dmap * dp, s64 blkno,
int nblocks)
{
s8 oldroot;
int rc;
+ if (unlikely(!check_dmaptree(&dp->tree))) {
+ jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmap page\n");
+ return -EIO;
+ }
+
/* save the current value of the root (i.e. maximum free string)
* of the dmap tree.
*/
oldroot = dp->tree.stree[ROOT];
/* allocate the specified (blocks) bits */
dbAllocBits(bmp, dp, blkno, nblocks);
/* if the root has not changed, done. */
if (dp->tree.stree[ROOT] == oldroot)
return (0);
/* root changed. bubble the change up to the dmap control pages.
* if the adjustment of the upper level control pages fails,
* backout the bit allocation (thus making everything consistent).
*/
if ((rc = dbAdjCtl(bmp, blkno, dp->tree.stree[ROOT], 1, 0)))
dbFreeBits(bmp, dp, blkno, nblocks);
return (rc);
}
/*
* NAME: dbFreeDmap()
*
* FUNCTION: adjust the disk allocation map to reflect the allocation
* of a specified block range within a dmap.
*
* this routine frees the specified blocks from the dmap through
* a call to dbFreeBits(). if the deallocation of the block range
* causes the maximum string of free blocks within the dmap to
* change (i.e. the value of the root of the dmap's dmtree), this
* routine will cause this change to be reflected up through the
* appropriate levels of the dmap control pages by a call to
* dbAdjCtl() for the L0 dmap control page that covers this dmap.
*
* PARAMETERS:
* bmp - pointer to bmap descriptor
* dp - pointer to dmap to free the block range from.
* blkno - starting block number of the block to be freed.
* nblocks - number of blocks to be freed.
*
* RETURN VALUES:
* 0 - success
* -EIO - i/o error
*
* serialization: IREAD_LOCK(ipbmap) or IWRITE_LOCK(ipbmap) held on entry/exit;
*/
static int dbFreeDmap(struct bmap * bmp, struct dmap * dp, s64 blkno,
int nblocks)
{
s8 oldroot;
int rc = 0, word;
+ if (unlikely(!check_dmaptree(&dp->tree))) {
+ jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmap page\n");
+ return -EIO;
+ }
+
/* save the current value of the root (i.e. maximum free string)
* of the dmap tree.
*/
oldroot = dp->tree.stree[ROOT];
/* free the specified (blocks) bits */
rc = dbFreeBits(bmp, dp, blkno, nblocks);
/* if error or the root has not changed, done. */
if (rc || (dp->tree.stree[ROOT] == oldroot))
return (rc);
/* root changed. bubble the change up to the dmap control pages.
* if the adjustment of the upper level control pages fails,
* backout the deallocation.
*/
if ((rc = dbAdjCtl(bmp, blkno, dp->tree.stree[ROOT], 0, 0))) {
word = (blkno & (BPERDMAP - 1)) >> L2DBWORD;
/* as part of backing out the deallocation, we will have
* to back split the dmap tree if the deallocation caused
* the freed blocks to become part of a larger binary buddy
* system.
*/
if (dp->tree.stree[word] == NOFREE)
dbBackSplit((dmtree_t *)&dp->tree, word, false);
dbAllocBits(bmp, dp, blkno, nblocks);
}
return (rc);
}
/*
* NAME: dbAllocBits()
*
* FUNCTION: allocate a specified block range from a dmap.
*
* this routine updates the dmap to reflect the working
* state allocation of the specified block range. it directly
* updates the bits of the working map and causes the adjustment
* of the binary buddy system described by the dmap's dmtree
* leaves to reflect the bits allocated. it also causes the
* dmap's dmtree, as a whole, to reflect the allocated range.
*
* PARAMETERS:
* bmp - pointer to bmap descriptor
* dp - pointer to dmap to allocate bits from.
* blkno - starting block number of the bits to be allocated.
* nblocks - number of bits to be allocated.
*
* RETURN VALUES: none
*
* serialization: IREAD_LOCK(ipbmap) or IWRITE_LOCK(ipbmap) held on entry/exit;
*/
@@ -3331,123 +3426,128 @@ int dbAllocBottomUp(struct inode *ip, s64 blkno, s64 nblocks)
static int dbAllocDmapBU(struct bmap * bmp, struct dmap * dp, s64 blkno,
int nblocks)
{
int rc;
int dbitno, word, rembits, nb, nwords, wbitno, agno;
s8 oldroot;
struct dmaptree *tp = (struct dmaptree *) & dp->tree;
+ if (unlikely(!check_dmaptree(&dp->tree))) {
+ jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmap page\n");
+ return -EIO;
+ }
+
/* save the current value of the root (i.e. maximum free string)
* of the dmap tree.
*/
oldroot = tp->stree[ROOT];
/* determine the bit number and word within the dmap of the
* starting block.
*/
dbitno = blkno & (BPERDMAP - 1);
word = dbitno >> L2DBWORD;
/* block range better be within the dmap */
assert(dbitno + nblocks <= BPERDMAP);
/* allocate the bits of the dmap's words corresponding to the block
* range. not all bits of the first and last words may be contained
* within the block range. if this is the case, we'll work against
* those words (i.e. partial first and/or last) on an individual basis
* (a single pass), allocating the bits of interest by hand and
* updating the leaf corresponding to the dmap word. a single pass
* will be used for all dmap words fully contained within the
* specified range. within this pass, the bits of all fully contained
* dmap words will be marked as free in a single shot and the leaves
* will be updated. a single leaf may describe the free space of
* multiple dmap words, so we may update only a subset of the actual
* leaves corresponding to the dmap words of the block range.
*/
for (rembits = nblocks; rembits > 0; rembits -= nb, dbitno += nb) {
/* determine the bit number within the word and
* the number of bits within the word.
*/
wbitno = dbitno & (DBWORD - 1);
nb = min(rembits, DBWORD - wbitno);
/* check if only part of a word is to be allocated.
*/
if (nb < DBWORD) {
/* allocate (set to 1) the appropriate bits within
* this dmap word.
*/
dp->wmap[word] |= cpu_to_le32(ONES << (DBWORD - nb)
>> wbitno);
word++;
} else {
/* one or more dmap words are fully contained
* within the block range. determine how many
* words and allocate (set to 1) the bits of these
* words.
*/
nwords = rembits >> L2DBWORD;
memset(&dp->wmap[word], (int) ONES, nwords * 4);
/* determine how many bits */
nb = nwords << L2DBWORD;
word += nwords;
}
}
/* update the free count for this dmap */
le32_add_cpu(&dp->nfree, -nblocks);
/* reconstruct summary tree */
dbInitDmapTree(dp);
BMAP_LOCK(bmp);
/* if this allocation group is completely free,
* update the highest active allocation group number
* if this allocation group is the new max.
*/
agno = blkno >> bmp->db_agl2size;
if (agno > bmp->db_maxag)
bmp->db_maxag = agno;
/* update the free count for the allocation group and map */
bmp->db_agfree[agno] -= nblocks;
bmp->db_nfree -= nblocks;
BMAP_UNLOCK(bmp);
/* if the root has not changed, done. */
if (tp->stree[ROOT] == oldroot)
return (0);
/* root changed. bubble the change up to the dmap control pages.
* if the adjustment of the upper level control pages fails,
* backout the bit allocation (thus making everything consistent).
*/
if ((rc = dbAdjCtl(bmp, blkno, tp->stree[ROOT], 1, 0)))
dbFreeBits(bmp, dp, blkno, nblocks);
return (rc);
}
/*
* NAME: dbExtendFS()
*
* FUNCTION: extend bmap from blkno for nblocks;
* dbExtendFS() updates bmap ready for dbAllocBottomUp();
*
* L2
* |
* L1---------------------------------L1
* | |
* L0---------L0---------L0 L0---------L0---------L0
* | | | | | |
* d0,...,dn d0,...,dn d0,...,dn d0,...,dn d0,...,dn d0,.,dm;
* L2L1L0d0,...,dnL0d0,...,dnL0d0,...,dnL1L0d0,...,dnL0d0,...,dnL0d0,..dm
*
* <---old---><----------------------------extend----------------------->
*/
]
|
| 196/4 |
2026/06/30 14:47 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 47373
output: 628
thoughts: 512
Thoughts: **Validation of the dmaptree Implementation**
Okay, the changes look solid, exactly what was specified. I've integrated `check_dmaptree` as requested, baking it into the relevant functions: `dbFreeDmap`, `dbAllocNext`, `dbAllocNear`, `dbAllocDmapLev`, `dbAllocDmapBU`, and `dbAllocDmap`. The patch diff confirms the implementation is precisely what I had in mind.
Now, let's just make sure there aren't any sneaky spots where `dmaptree` is used directly without that crucial validation. Let's trace it through: `dbInitDmapTree` and `dbInitDmap`, and `dbInitTree` all initialize it, so those are safe. `dbAdjTree` modifies it, but that's always called from `dbJoin` and `dbSplit`. Those are called from `dbFreeBits` and `dbAllocBits`, and *those* are now called by functions with the validation in place. `dbFindLeaf` gets called from `dbAllocDmapLev`, which has validation, and `dbAllocCtl` (for `dmapctl`), which also has validation. `dbBackSplit` is called from `dbFreeDmap` - which is also covered. `dbJoin` is called from `dbFreeBits`, which in turn is called from `dbFreeDmap`, `dbAllocDmapBU` and `dbAllocDmap`, all covered. Finally, `dbSplit` is called from `dbAllocBits`, which is called from `dbAllocDmap` and `dbFreeDmap` (covered), and `dbAdjCtl` (for `dmapctl`), also covered.
Alright, looks like every entry point that touches the `dmaptree` is now covered by the new validation. Seems comprehensive. I can formulate the final reply. This is a good solution to prevent out-of-bounds access.
|
| 197/3 |
2026/06/30 14:47 |
action |
test-patch |
21m
Results: map[PatchDiff:diff --git a/fs/jfs/jfs_dmap.c b/fs/jfs/jfs_dmap.c
index a841cf21d..4f2e0b78d 100644
--- a/fs/jfs/jfs_dmap.c
+++ b/fs/jfs/jfs_dmap.c
@@ -220,6 +220,85 @@ static bool check_dmapctl(struct dmapctl *dcp)
return true;
}
+/*
+ * check_dmaptree - Validate integrity of a dmaptree structure
+ * @dtp: Pointer to the dmaptree structure to check
+ *
+ * Return: true if valid, false if corrupted
+ */
+static bool check_dmaptree(struct dmaptree *dtp)
+{
+ u32 nleafs, l2nleafs, leafidx, height;
+ int i;
+
+ nleafs = le32_to_cpu(dtp->nleafs);
+ /* Check basic field ranges */
+ if (unlikely(nleafs > LPERDMAP)) {
+ jfs_err("dmaptree: invalid nleafs %u (max %u)", nleafs,
+ LPERDMAP);
+ return false;
+ }
+
+ l2nleafs = le32_to_cpu(dtp->l2nleafs);
+ if (unlikely(l2nleafs > L2LPERDMAP)) {
+ jfs_err("dmaptree: invalid l2nleafs %u (max %u)", l2nleafs,
+ L2LPERDMAP);
+ return false;
+ }
+
+ /* Verify nleafs matches l2nleafs (must be power of two) */
+ if (unlikely((1U << l2nleafs) != nleafs)) {
+ jfs_err("dmaptree: nleafs %u != 2^%u", nleafs, l2nleafs);
+ return false;
+ }
+
+ leafidx = le32_to_cpu(dtp->leafidx);
+ /* Check leaf index matches expected position */
+ if (unlikely(leafidx != LEAFIND)) {
+ jfs_err("dmaptree: invalid leafidx %u (expected %u)", leafidx,
+ LEAFIND);
+ return false;
+ }
+
+ height = le32_to_cpu(dtp->height);
+ /* Check tree height is within valid range */
+ if (unlikely(height > 4)) {
+ jfs_err("dmaptree: invalid height %u (max 4)", height);
+ return false;
+ }
+
+ /* Check budmin is valid */
+ if (unlikely(dtp->budmin < BUDMIN)) {
+ jfs_err("dmaptree: invalid budmin %d (min %d)", dtp->budmin,
+ BUDMIN);
+ return false;
+ }
+
+ /* Check leaf nodes fit within stree array */
+ if (unlikely(leafidx + nleafs > TREESIZE)) {
+ jfs_err("dmaptree: leaf range exceeds stree size (end %u > %u)",
+ leafidx + nleafs, TREESIZE);
+ return false;
+ }
+
+ /* Check leaf nodes have valid values */
+ for (i = leafidx; i < leafidx + nleafs; i++) {
+ s8 val = dtp->stree[i];
+
+ if (unlikely(val < NOFREE)) {
+ jfs_err("dmaptree: invalid leaf value %d at index %d",
+ val, i);
+ return false;
+ } else if (unlikely(val > 31)) {
+ jfs_err("dmaptree: leaf value %d too large at index %d",
+ val, i);
+ return false;
+ }
+ }
+
+ return true;
+}
+
/*
* NAME: dbMount()
*
@@ -1163,7 +1242,7 @@ static int dbAllocNext(struct bmap * bmp, struct dmap * dp, s64 blkno,
s8 *leaf;
u32 mask;
- if (dp->tree.leafidx != cpu_to_le32(LEAFIND)) {
+ if (unlikely(!check_dmaptree(&dp->tree))) {
jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmap page\n");
return -EIO;
}
@@ -1293,7 +1372,7 @@ dbAllocNear(struct bmap * bmp,
int word, lword, rc;
s8 *leaf;
- if (dp->tree.leafidx != cpu_to_le32(LEAFIND)) {
+ if (unlikely(!check_dmaptree(&dp->tree))) {
jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmap page\n");
return -EIO;
}
@@ -2046,6 +2125,11 @@ dbAllocDmapLev(struct bmap * bmp,
s64 blkno;
int leafidx, rc;
+ if (unlikely(!check_dmaptree(&dp->tree))) {
+ jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmap page\n");
+ return -EIO;
+ }
+
/* can't be more than a dmaps worth of blocks */
assert(l2nb <= L2BPERDMAP);
@@ -2112,6 +2196,11 @@ static int dbAllocDmap(struct bmap * bmp, struct dmap * dp, s64 blkno,
s8 oldroot;
int rc;
+ if (unlikely(!check_dmaptree(&dp->tree))) {
+ jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmap page\n");
+ return -EIO;
+ }
+
/* save the current value of the root (i.e. maximum free string)
* of the dmap tree.
*/
@@ -2167,6 +2256,11 @@ static int dbFreeDmap(struct bmap * bmp, struct dmap * dp, s64 blkno,
s8 oldroot;
int rc = 0, word;
+ if (unlikely(!check_dmaptree(&dp->tree))) {
+ jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmap page\n");
+ return -EIO;
+ }
+
/* save the current value of the root (i.e. maximum free string)
* of the dmap tree.
*/
@@ -3338,6 +3432,11 @@ static int dbAllocDmapBU(struct bmap * bmp, struct dmap * dp, s64 blkno,
s8 oldroot;
struct dmaptree *tp = (struct dmaptree *) & dp->tree;
+ if (unlikely(!check_dmaptree(&dp->tree))) {
+ jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmap page\n");
+ return -EIO;
+ }
+
/* save the current value of the root (i.e. maximum free string)
* of the dmap tree.
*/
TestError:]
|
| 198/1 |
2026/06/30 15:08 |
agent |
fixes-finder |
13m
Model:
Results: map[FixesHash:1da177e4c3f41524e886b7f1b8a0c1fc7321cac2]
Instruction:
You are an experienced Linux kernel developer tasked with identifying the commit
that introduced the bug being fixed. Identifying the correct buggy commit is crucial
for proper kernel maintenance (backporting to stable trees, etc.).
Your investigation strategy:
1. Examine the patch that fixes the bug. Use git tools (like git-log or git-blame)
to trace the history of the lines or functions modified by the patch.
2. Analyze the stack trace in the crash report. Identify the key files and functions
involved in the crash and investigate their history to see when the problematic
logic was introduced.
3. Compare the bug explanation with the commit history to find the point where
the described logic error first appeared.
A bug is typically introduced when a piece of code is first written, or when
a refactoring changed its logic in a way that introduced the bug.
Trace the history of relevant symbols or find when specific code patterns were introduced/removed.
You must provide exactly one bug-introducing commit hash.
If you are unable to confidently determine the bug-introducing commit after investigation,
return an empty string rather than guessing.
Prefer calling several tools at the same time to save round-trips.
Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.
Prompt:
The crash is:
------------[ cut here ]------------
UBSAN: array-index-out-of-bounds in /app/workdir/cache/src/7be5f73ee7b4d1978813accf8bb54cd892ccd800/fs/jfs/jfs_dmap.c:2867:24
index 4294967295 is out of range for type 's8[1365]' (aka 'signed char[1365]')
CPU: 0 UID: 0 PID: 123 Comm: jfsCommit Not tainted syzkaller #1 PREEMPT(full)
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
Call Trace:
<TASK>
dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120
ubsan_epilogue+0xa/0x30 lib/ubsan.c:233
__ubsan_handle_out_of_bounds+0xe8/0xf0 lib/ubsan.c:455
dbJoin+0xcc4/0xd60 fs/jfs/jfs_dmap.c:2867
dbFreeBits+0x4a2/0xd70 fs/jfs/jfs_dmap.c:2427
dbFreeDmap fs/jfs/jfs_dmap.c:2176 [inline]
dbFree+0x324/0x650 fs/jfs/jfs_dmap.c:485
txFreeMap+0x9e6/0xde0 fs/jfs/jfs_txnmgr.c:2517
xtTruncate+0xd16/0x2eb0 fs/jfs/jfs_xtree.c:2481
jfs_free_zero_link+0x35b/0x4c0 fs/jfs/namei.c:760
jfs_evict_inode+0x356/0x430 fs/jfs/inode.c:159
evict+0x624/0xb50 fs/inode.c:825
txLazyCommit fs/jfs/jfs_txnmgr.c:2666 [inline]
jfs_lazycommit+0x44c/0xac0 fs/jfs/jfs_txnmgr.c:2735
kthread+0x388/0x470 kernel/kthread.c:436
ret_from_fork+0x514/0xb70 arch/x86/kernel/process.c:158
ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245
</TASK>
---[ end trace ]---
The explanation of the root cause is:
An array-index-out-of-bounds crash occurs in `dbJoin` when the JFS filesystem attempts to free blocks using a corrupted `dmap` structure read from disk.
### Root Cause
The JFS filesystem uses a binary buddy system to manage free space. The state of the buddy system is stored in `dmap` (for leaf levels) and `dmapctl` (for upper levels) structures on disk.
When a `dmap` is read from disk, its `dmaptree` structure is not fully validated. Specifically, the `leafidx` field (which indicates the index of the first leaf in the `stree` array) is read directly from disk and used to compute pointers to the leaf nodes.
If `leafidx` is corrupted (e.g., set to `0xffffffff` / `-1`), the computed pointer will point out of bounds of the `stree` array. When `dbFreeDmap` is called to free blocks, it calls `dbFreeBits`, which in turn calls `dbJoin` to update the buddy system. `dbJoin` uses the corrupted `leafidx` to access the leaf nodes:
```c
// fs/jfs/jfs_dmap.c:2867
leaf = tp->dmt_stree + le32_to_cpu(tp->dmt_leafidx);
```
This results in an out-of-bounds memory access when `leaf[buddy]` is subsequently accessed.
While a similar integrity check (`check_dmapctl`) was recently added for `dmapctl` structures in commit `cce219b203c4` ("jfs: add dmapctl integrity check to prevent invalid operations"), the `dmap` structures (`dmaptree`) were left unchecked.
### Fix
Introduce a `check_dmaptree` function, similar to the existing `check_dmapctl` function, to validate the integrity of the `dmaptree` structure when it is used.
The `check_dmaptree` function should verify that:
- `nleafs` is within bounds (`<= LPERDMAP`).
- `l2nleafs` is within bounds (`<= L2LPERDMAP`).
- `nleafs` matches `2^l2nleafs`.
- `leafidx` is exactly `LEAFIND`.
- `height` is within bounds (`<= 4`).
- `budmin` is valid (`>= BUDMIN`).
- The leaf nodes fit within the `stree` array (`leafidx + nleafs <= TREESIZE`).
- The values of the leaf nodes are valid (`>= NOFREE` and `<= 31`).
Call `check_dmaptree` in the functions that operate on the `dmap` structure, such as `dbFreeDmap`, `dbAllocNext`, `dbAllocNear`, `dbAllocDmapLev`, `dbAllocDmapBU`, and `dbAllocDmap`. If `check_dmaptree` fails, return `-EIO` to prevent further processing of the corrupted `dmap`. This will also replace the existing partial checks for `leafidx` in `dbAllocNext` and `dbAllocNear`.
```c
/*
* check_dmaptree - Validate integrity of a dmaptree structure
* @dtp: Pointer to the dmaptree structure to check
*
* Return: true if valid, false if corrupted
*/
static bool check_dmaptree(struct dmaptree *dtp)
{
u32 nleafs, l2nleafs, leafidx, height;
int i;
nleafs = le32_to_cpu(dtp->nleafs);
/* Check basic field ranges */
if (unlikely(nleafs > LPERDMAP)) {
jfs_err("dmaptree: invalid nleafs %u (max %u)",
nleafs, LPERDMAP);
return false;
}
l2nleafs = le32_to_cpu(dtp->l2nleafs);
if (unlikely(l2nleafs > L2LPERDMAP)) {
jfs_err("dmaptree: invalid l2nleafs %u (max %u)",
l2nleafs, L2LPERDMAP);
return false;
}
/* Verify nleafs matches l2nleafs (must be power of two) */
if (unlikely((1U << l2nleafs) != nleafs)) {
jfs_err("dmaptree: nleafs %u != 2^%u",
nleafs, l2nleafs);
return false;
}
leafidx = le32_to_cpu(dtp->leafidx);
/* Check leaf index matches expected position */
if (unlikely(leafidx != LEAFIND)) {
jfs_err("dmaptree: invalid leafidx %u (expected %u)",
leafidx, LEAFIND);
return false;
}
height = le32_to_cpu(dtp->height);
/* Check tree height is within valid range */
if (unlikely(height > 4)) {
jfs_err("dmaptree: invalid height %u (max 4)",
height);
return false;
}
/* Check budmin is valid */
if (unlikely(dtp->budmin < BUDMIN)) {
jfs_err("dmaptree: invalid budmin %d (min %d)",
dtp->budmin, BUDMIN);
return false;
}
/* Check leaf nodes fit within stree array */
if (unlikely(leafidx + nleafs > TREESIZE)) {
jfs_err("dmaptree: leaf range exceeds stree size (end %u > %u)",
leafidx + nleafs, TREESIZE);
return false;
}
/* Check leaf nodes have valid values */
for (i = leafidx; i < leafidx + nleafs; i++) {
s8 val = dtp->stree[i];
if (unlikely(val < NOFREE)) {
jfs_err("dmaptree: invalid leaf value %d at index %d",
val, i);
return false;
} else if (unlikely(val > 31)) {
jfs_err("dmaptree: leaf value %d too large at index %d", val, i);
return false;
}
}
return true;
}
```
Then, in functions like `dbFreeDmap`, `dbAllocNext`, `dbAllocNear`, `dbAllocDmapLev`, `dbAllocDmapBU`, and `dbAllocDmap`, add the check at the beginning:
```c
if (unlikely(!check_dmaptree(&dp->tree))) {
jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmap page\n");
return -EIO;
}
```
The patch that fixes the bug is:
diff --git a/fs/jfs/jfs_dmap.c b/fs/jfs/jfs_dmap.c
index a841cf21d..4f2e0b78d 100644
--- a/fs/jfs/jfs_dmap.c
+++ b/fs/jfs/jfs_dmap.c
@@ -220,6 +220,85 @@ static bool check_dmapctl(struct dmapctl *dcp)
return true;
}
+/*
+ * check_dmaptree - Validate integrity of a dmaptree structure
+ * @dtp: Pointer to the dmaptree structure to check
+ *
+ * Return: true if valid, false if corrupted
+ */
+static bool check_dmaptree(struct dmaptree *dtp)
+{
+ u32 nleafs, l2nleafs, leafidx, height;
+ int i;
+
+ nleafs = le32_to_cpu(dtp->nleafs);
+ /* Check basic field ranges */
+ if (unlikely(nleafs > LPERDMAP)) {
+ jfs_err("dmaptree: invalid nleafs %u (max %u)", nleafs,
+ LPERDMAP);
+ return false;
+ }
+
+ l2nleafs = le32_to_cpu(dtp->l2nleafs);
+ if (unlikely(l2nleafs > L2LPERDMAP)) {
+ jfs_err("dmaptree: invalid l2nleafs %u (max %u)", l2nleafs,
+ L2LPERDMAP);
+ return false;
+ }
+
+ /* Verify nleafs matches l2nleafs (must be power of two) */
+ if (unlikely((1U << l2nleafs) != nleafs)) {
+ jfs_err("dmaptree: nleafs %u != 2^%u", nleafs, l2nleafs);
+ return false;
+ }
+
+ leafidx = le32_to_cpu(dtp->leafidx);
+ /* Check leaf index matches expected position */
+ if (unlikely(leafidx != LEAFIND)) {
+ jfs_err("dmaptree: invalid leafidx %u (expected %u)", leafidx,
+ LEAFIND);
+ return false;
+ }
+
+ height = le32_to_cpu(dtp->height);
+ /* Check tree height is within valid range */
+ if (unlikely(height > 4)) {
+ jfs_err("dmaptree: invalid height %u (max 4)", height);
+ return false;
+ }
+
+ /* Check budmin is valid */
+ if (unlikely(dtp->budmin < BUDMIN)) {
+ jfs_err("dmaptree: invalid budmin %d (min %d)", dtp->budmin,
+ BUDMIN);
+ return false;
+ }
+
+ /* Check leaf nodes fit within stree array */
+ if (unlikely(leafidx + nleafs > TREESIZE)) {
+ jfs_err("dmaptree: leaf range exceeds stree size (end %u > %u)",
+ leafidx + nleafs, TREESIZE);
+ return false;
+ }
+
+ /* Check leaf nodes have valid values */
+ for (i = leafidx; i < leafidx + nleafs; i++) {
+ s8 val = dtp->stree[i];
+
+ if (unlikely(val < NOFREE)) {
+ jfs_err("dmaptree: invalid leaf value %d at index %d",
+ val, i);
+ return false;
+ } else if (unlikely(val > 31)) {
+ jfs_err("dmaptree: leaf value %d too large at index %d",
+ val, i);
+ return false;
+ }
+ }
+
+ return true;
+}
+
/*
* NAME: dbMount()
*
@@ -1163,7 +1242,7 @@ static int dbAllocNext(struct bmap * bmp, struct dmap * dp, s64 blkno,
s8 *leaf;
u32 mask;
- if (dp->tree.leafidx != cpu_to_le32(LEAFIND)) {
+ if (unlikely(!check_dmaptree(&dp->tree))) {
jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmap page\n");
return -EIO;
}
@@ -1293,7 +1372,7 @@ dbAllocNear(struct bmap * bmp,
int word, lword, rc;
s8 *leaf;
- if (dp->tree.leafidx != cpu_to_le32(LEAFIND)) {
+ if (unlikely(!check_dmaptree(&dp->tree))) {
jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmap page\n");
return -EIO;
}
@@ -2046,6 +2125,11 @@ dbAllocDmapLev(struct bmap * bmp,
s64 blkno;
int leafidx, rc;
+ if (unlikely(!check_dmaptree(&dp->tree))) {
+ jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmap page\n");
+ return -EIO;
+ }
+
/* can't be more than a dmaps worth of blocks */
assert(l2nb <= L2BPERDMAP);
@@ -2112,6 +2196,11 @@ static int dbAllocDmap(struct bmap * bmp, struct dmap * dp, s64 blkno,
s8 oldroot;
int rc;
+ if (unlikely(!check_dmaptree(&dp->tree))) {
+ jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmap page\n");
+ return -EIO;
+ }
+
/* save the current value of the root (i.e. maximum free string)
* of the dmap tree.
*/
@@ -2167,6 +2256,11 @@ static int dbFreeDmap(struct bmap * bmp, struct dmap * dp, s64 blkno,
s8 oldroot;
int rc = 0, word;
+ if (unlikely(!check_dmaptree(&dp->tree))) {
+ jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmap page\n");
+ return -EIO;
+ }
+
/* save the current value of the root (i.e. maximum free string)
* of the dmap tree.
*/
@@ -3338,6 +3432,11 @@ static int dbAllocDmapBU(struct bmap * bmp, struct dmap * dp, s64 blkno,
s8 oldroot;
struct dmaptree *tp = (struct dmaptree *) & dp->tree;
+ if (unlikely(!check_dmaptree(&dp->tree))) {
+ jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmap page\n");
+ return -EIO;
+ }
+
/* save the current value of the root (i.e. maximum free string)
* of the dmap tree.
*/
Search for the commit(s) that introduced this bug.
|
| 199/2 |
2026/06/30 15:08 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 8271
output: 36
thoughts: 212
Thoughts: **Tracking Down the Root Cause of a JFS Bug**
Okay, here's the situation. I've got a patch in front of me that fixes an array-index-out-of-bounds bug, specifically in the `dbJoin` function within `fs/jfs/jfs_dmap.c`. My task is to pinpoint the exact commit that introduced this vulnerability. The description says the problem is that the `dmap` structures read from disk aren't being properly validated, particularly the `leafidx` field.
The fix itself introduces a new function, `check_dmaptree`, to handle this validation and replaces the original partial checks within `dbAllocNext` and `dbAllocNear`. So, essentially, we're dealing with a case where a value, `leafidx`, is being read directly from disk and then used without any bounds checking. That's a classic recipe for trouble.
Time to dive into `fs/jfs/jfs_dmap.c`. I need to figure out when either `dbJoin` or, maybe, `dbFreeDmap` was first introduced. JFS has been around for ages, so this could require some digging. Let's start by examining the commit history for this file. It should be a good starting point to locate the introduction of the relevant functions or their initial behavior.
|
| 200/2 |
2026/06/30 15:09 |
tool |
git-log |
0m
Error:
Args: map[PathPrefix:fs/jfs/jfs_dmap.c SymbolName:dbJoin]
Results:
|
| 201/2 |
2026/06/30 15:09 |
llm |
fixes-finder |
1m
Model:
Tokens:
input: 8826
output: 36
thoughts: 16
Thoughts: Okay, here's the summary, rendered as a first-person thought process, keeping in mind the user is an expert:
**Navigating the Dependency Labyrinth**
Right, so I'm looking at this code, and it's crystal clear what's happening. I'm working with `SymbolName`, and I'm realizing, after a moment's consideration, that I was heading in the wrong direction with the pathing for this. The core of the issue, which is suddenly obvious, is the reliance on `SourcePath`. It's not about the `PathPrefix` as I initially thought; that was a red herring. It's the full, specific location described by the former, which is the crucial component here. I need to rethink the dependency resolution logic around `SourcePath` instead of messing around with `PathPrefix`, and refactor the code to accommodate this. This change is necessary to integrate the two.
|
| 202/2 |
2026/06/30 15:10 |
tool |
git-log |
0m
Args: map[SourcePath:fs/jfs/jfs_dmap.c SymbolName:dbJoin]
Results: map[Output:74ecdda68242 jfs: fix array-index-out-of-bounds in dbAdjTree
b6a47fd8ff08 JFS: Corrupted block map should not cause trap
56d1254917d9 JFS: Remove assert statement in dbJoin & return -EIO instead
1da177e4c3f4 Linux-2.6.12-rc2
]
|
| 203/2 |
2026/06/30 15:10 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 9187
output: 38
thoughts: 51
Thoughts: Okay, here's my interpretation of those thoughts, tailored for an expert audience, presented in the first person:
**Investigating the Data Base Join Function's History**
Right, let's see, `dbJoin`... that function's been around for ages. I remember that commit, `1da177e4c3f4`. Yep, that's Linux-2.6.12-rc2. Okay, so we know its lineage. Now, I need to check when `dbFreeDmap` was introduced. It's crucial to understand the lifecycle of these related functions, and when support was actually put into place for de-allocating resources. It’ll give me a better grasp of the potential architectural changes that could have occurred between the join function and its complementary freeing function. This is vital context.
|
| 204/2 |
2026/06/30 15:10 |
tool |
git-log |
0m
Args: map[SourcePath:fs/jfs/jfs_dmap.c SymbolName:dbFreeDmap]
Results: map[Output:1da177e4c3f4 Linux-2.6.12-rc2
]
|
| 205/2 |
2026/06/30 15:10 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 9490
output: 58
thoughts: 177
Thoughts: **Analyzing the JFS Bug**
Okay, so the issue seems to stem from `leafidx` being read directly from disk and then used without any validation checks. That's a classic vulnerability. Now, the explanation mentions a recent integrity check for `dmapctl` structures, implemented in `cce219b203c4`. It seems they patched similar structures but missed the `dmap` structures, specifically the `dmaptree` used for managing the allocation bitmaps. If this structure has been read from disk without validation since the beginning of JFS in Linux then the root cause of the bug must have been introduced with the initial commit `1da177e4c3f4`.
My immediate next step is to examine the partial checks for `leafidx` within `dbAllocNext` and `dbAllocNear`. Were these checks recently added? If not, were the checks ever comprehensive and correct? Understanding the history of these checks is crucial to assess the true scope and impact of this bug.
|
| 206/2 |
2026/06/30 15:10 |
tool |
git-log |
10m
Error:
Args: map[CodeRegexp:if \(dp->tree\.leafidx != cpu_to_le32\(LEAFIND\)\) SourcePath:fs/jfs/jfs_dmap.c]
Results:
|
| 207/2 |
2026/06/30 15:20 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 9973
output: 58
thoughts: 9
Thoughts: Okay, here's the summary, rendered as a first-person thought process, keeping in mind the user is an expert:
**Refining the Routing Strategy**
Right, so I'm thinking about the most elegant and efficient way to handle this routing configuration. The current approach feels a little clunky; there's a certain lack of finesse I'm sensing. I've got to find the optimal balance between clarity, maintainability, and performance. I need to get this right.
It hits me – the simplicity and directness I'm after? It's the `PathPrefix` directive. Of course. That's the key. That's the tool that offers the level of granularity and the clean organization I'm envisioning. I can structure the routes in a much more sensible and predictable way this time.
|
| 208/2 |
2026/06/30 15:20 |
tool |
git-log |
0m
Args: map[CodeRegexp:if \(dp->tree\.leafidx != cpu_to_le32\(LEAFIND\)\) PathPrefix:fs/jfs/jfs_dmap.c]
Results: map[Output:1da177e4c3f4 Linux-2.6.12-rc2
]
|
| 209/2 |
2026/06/30 15:20 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 10250
output: 39
thoughts: 53
Thoughts: Okay, let's see...
**Analyzing the Commit: `1da177e4c3f4` and Partial Checks**
Right, so I'm picking up on these partial checks, and it seems like they were introduced around the time of commit `1da177e4c3f4`. That's a good starting point. Now, let's drill down a bit deeper into that commit specifically. I need to understand *why* those partial checks might be present. My instincts tell me that it is likely there due to the changes that were implemented in that commit. Let me take a look at the `dbAllocNext` and `dbAllocNear` functions within that commit. That should illuminate the situation pretty quickly, I think.
|
| 210/2 |
2026/06/30 15:20 |
tool |
git-show |
0m
Args: map[Commit:1da177e4c3f4:fs/jfs/jfs_dmap.c]
Results: map[Output:
Full output is too long, showing 1000 out of 4272 lines.
[/*
* Copyright (C) International Business Machines Corp., 2000-2004
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
* the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/fs.h>
#include "jfs_incore.h"
#include "jfs_superblock.h"
#include "jfs_dmap.h"
#include "jfs_imap.h"
#include "jfs_lock.h"
#include "jfs_metapage.h"
#include "jfs_debug.h"
/*
* Debug code for double-checking block map
*/
/* #define _JFS_DEBUG_DMAP 1 */
#ifdef _JFS_DEBUG_DMAP
#define DBINITMAP(size,ipbmap,results) \
DBinitmap(size,ipbmap,results)
#define DBALLOC(dbmap,mapsize,blkno,nblocks) \
DBAlloc(dbmap,mapsize,blkno,nblocks)
#define DBFREE(dbmap,mapsize,blkno,nblocks) \
DBFree(dbmap,mapsize,blkno,nblocks)
#define DBALLOCCK(dbmap,mapsize,blkno,nblocks) \
DBAllocCK(dbmap,mapsize,blkno,nblocks)
#define DBFREECK(dbmap,mapsize,blkno,nblocks) \
DBFreeCK(dbmap,mapsize,blkno,nblocks)
static void DBinitmap(s64, struct inode *, u32 **);
static void DBAlloc(uint *, s64, s64, s64);
static void DBFree(uint *, s64, s64, s64);
static void DBAllocCK(uint *, s64, s64, s64);
static void DBFreeCK(uint *, s64, s64, s64);
#else
#define DBINITMAP(size,ipbmap,results)
#define DBALLOC(dbmap, mapsize, blkno, nblocks)
#define DBFREE(dbmap, mapsize, blkno, nblocks)
#define DBALLOCCK(dbmap, mapsize, blkno, nblocks)
#define DBFREECK(dbmap, mapsize, blkno, nblocks)
#endif /* _JFS_DEBUG_DMAP */
/*
* SERIALIZATION of the Block Allocation Map.
*
* the working state of the block allocation map is accessed in
* two directions:
*
* 1) allocation and free requests that start at the dmap
* level and move up through the dmap control pages (i.e.
* the vast majority of requests).
*
* 2) allocation requests that start at dmap control page
* level and work down towards the dmaps.
*
* the serialization scheme used here is as follows.
*
* requests which start at the bottom are serialized against each
* other through buffers and each requests holds onto its buffers
* as it works it way up from a single dmap to the required level
* of dmap control page.
* requests that start at the top are serialized against each other
* and request that start from the bottom by the multiple read/single
* write inode lock of the bmap inode. requests starting at the top
* take this lock in write mode while request starting at the bottom
* take the lock in read mode. a single top-down request may proceed
* exclusively while multiple bottoms-up requests may proceed
* simultaneously (under the protection of busy buffers).
*
* in addition to information found in dmaps and dmap control pages,
* the working state of the block allocation map also includes read/
* write information maintained in the bmap descriptor (i.e. total
* free block count, allocation group level free block counts).
* a single exclusive lock (BMAP_LOCK) is used to guard this information
* in the face of multiple-bottoms up requests.
* (lock ordering: IREAD_LOCK, BMAP_LOCK);
*
* accesses to the persistent state of the block allocation map (limited
* to the persistent bitmaps in dmaps) is guarded by (busy) buffers.
*/
#define BMAP_LOCK_INIT(bmp) init_MUTEX(&bmp->db_bmaplock)
#define BMAP_LOCK(bmp) down(&bmp->db_bmaplock)
#define BMAP_UNLOCK(bmp) up(&bmp->db_bmaplock)
/*
* forward references
*/
static void dbAllocBits(struct bmap * bmp, struct dmap * dp, s64 blkno,
int nblocks);
static void dbSplit(dmtree_t * tp, int leafno, int splitsz, int newval);
static void dbBackSplit(dmtree_t * tp, int leafno);
static void dbJoin(dmtree_t * tp, int leafno, int newval);
static void dbAdjTree(dmtree_t * tp, int leafno, int newval);
static int dbAdjCtl(struct bmap * bmp, s64 blkno, int newval, int alloc,
int level);
static int dbAllocAny(struct bmap * bmp, s64 nblocks, int l2nb, s64 * results);
static int dbAllocNext(struct bmap * bmp, struct dmap * dp, s64 blkno,
int nblocks);
static int dbAllocNear(struct bmap * bmp, struct dmap * dp, s64 blkno,
int nblocks,
int l2nb, s64 * results);
static int dbAllocDmap(struct bmap * bmp, struct dmap * dp, s64 blkno,
int nblocks);
static int dbAllocDmapLev(struct bmap * bmp, struct dmap * dp, int nblocks,
int l2nb,
s64 * results);
static int dbAllocAG(struct bmap * bmp, int agno, s64 nblocks, int l2nb,
s64 * results);
static int dbAllocCtl(struct bmap * bmp, s64 nblocks, int l2nb, s64 blkno,
s64 * results);
static int dbExtend(struct inode *ip, s64 blkno, s64 nblocks, s64 addnblocks);
static int dbFindBits(u32 word, int l2nb);
static int dbFindCtl(struct bmap * bmp, int l2nb, int level, s64 * blkno);
static int dbFindLeaf(dmtree_t * tp, int l2nb, int *leafidx);
static void dbFreeBits(struct bmap * bmp, struct dmap * dp, s64 blkno,
int nblocks);
static int dbFreeDmap(struct bmap * bmp, struct dmap * dp, s64 blkno,
int nblocks);
static int dbMaxBud(u8 * cp);
s64 dbMapFileSizeToMapSize(struct inode *ipbmap);
static int blkstol2(s64 nb);
static int cntlz(u32 value);
static int cnttz(u32 word);
static int dbAllocDmapBU(struct bmap * bmp, struct dmap * dp, s64 blkno,
int nblocks);
static int dbInitDmap(struct dmap * dp, s64 blkno, int nblocks);
static int dbInitDmapTree(struct dmap * dp);
static int dbInitTree(struct dmaptree * dtp);
static int dbInitDmapCtl(struct dmapctl * dcp, int level, int i);
static int dbGetL2AGSize(s64 nblocks);
/*
* buddy table
*
* table used for determining buddy sizes within characters of
* dmap bitmap words. the characters themselves serve as indexes
* into the table, with the table elements yielding the maximum
* binary buddy of free bits within the character.
*/
static s8 budtab[256] = {
3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
2, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0,
2, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0,
2, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0,
2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
2, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0,
2, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0,
2, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0,
2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
2, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0,
2, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0,
2, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -1
};
/*
* NAME: dbMount()
*
* FUNCTION: initializate the block allocation map.
*
* memory is allocated for the in-core bmap descriptor and
* the in-core descriptor is initialized from disk.
*
* PARAMETERS:
* ipbmap - pointer to in-core inode for the block map.
*
* RETURN VALUES:
* 0 - success
* -ENOMEM - insufficient memory
* -EIO - i/o error
*/
int dbMount(struct inode *ipbmap)
{
struct bmap *bmp;
struct dbmap_disk *dbmp_le;
struct metapage *mp;
int i;
/*
* allocate/initialize the in-memory bmap descriptor
*/
/* allocate memory for the in-memory bmap descriptor */
bmp = kmalloc(sizeof(struct bmap), GFP_KERNEL);
if (bmp == NULL)
return -ENOMEM;
/* read the on-disk bmap descriptor. */
mp = read_metapage(ipbmap,
BMAPBLKNO << JFS_SBI(ipbmap->i_sb)->l2nbperpage,
PSIZE, 0);
if (mp == NULL) {
kfree(bmp);
return -EIO;
}
/* copy the on-disk bmap descriptor to its in-memory version. */
dbmp_le = (struct dbmap_disk *) mp->data;
bmp->db_mapsize = le64_to_cpu(dbmp_le->dn_mapsize);
bmp->db_nfree = le64_to_cpu(dbmp_le->dn_nfree);
bmp->db_l2nbperpage = le32_to_cpu(dbmp_le->dn_l2nbperpage);
bmp->db_numag = le32_to_cpu(dbmp_le->dn_numag);
bmp->db_maxlevel = le32_to_cpu(dbmp_le->dn_maxlevel);
bmp->db_maxag = le32_to_cpu(dbmp_le->dn_maxag);
bmp->db_agpref = le32_to_cpu(dbmp_le->dn_agpref);
bmp->db_aglevel = le32_to_cpu(dbmp_le->dn_aglevel);
bmp->db_agheigth = le32_to_cpu(dbmp_le->dn_agheigth);
bmp->db_agwidth = le32_to_cpu(dbmp_le->dn_agwidth);
bmp->db_agstart = le32_to_cpu(dbmp_le->dn_agstart);
bmp->db_agl2size = le32_to_cpu(dbmp_le->dn_agl2size);
for (i = 0; i < MAXAG; i++)
bmp->db_agfree[i] = le64_to_cpu(dbmp_le->dn_agfree[i]);
bmp->db_agsize = le64_to_cpu(dbmp_le->dn_agsize);
bmp->db_maxfreebud = dbmp_le->dn_maxfreebud;
/* release the buffer. */
release_metapage(mp);
/* bind the bmap inode and the bmap descriptor to each other. */
bmp->db_ipbmap = ipbmap;
JFS_SBI(ipbmap->i_sb)->bmap = bmp;
memset(bmp->db_active, 0, sizeof(bmp->db_active));
DBINITMAP(bmp->db_mapsize, ipbmap, &bmp->db_DBmap);
/*
* allocate/initialize the bmap lock
*/
BMAP_LOCK_INIT(bmp);
return (0);
}
/*
* NAME: dbUnmount()
*
* FUNCTION: terminate the block allocation map in preparation for
* file system unmount.
*
* the in-core bmap descriptor is written to disk and
* the memory for this descriptor is freed.
*
* PARAMETERS:
* ipbmap - pointer to in-core inode for the block map.
*
* RETURN VALUES:
* 0 - success
* -EIO - i/o error
*/
int dbUnmount(struct inode *ipbmap, int mounterror)
{
struct bmap *bmp = JFS_SBI(ipbmap->i_sb)->bmap;
int i;
if (!(mounterror || isReadOnly(ipbmap)))
dbSync(ipbmap);
/*
* Invalidate the page cache buffers
*/
truncate_inode_pages(ipbmap->i_mapping, 0);
/*
* Sanity Check
*/
for (i = 0; i < bmp->db_numag; i++)
if (atomic_read(&bmp->db_active[i]))
printk(KERN_ERR "dbUnmount: db_active[%d] = %d\n",
i, atomic_read(&bmp->db_active[i]));
/* free the memory for the in-memory bmap. */
kfree(bmp);
return (0);
}
/*
* dbSync()
*/
int dbSync(struct inode *ipbmap)
{
struct dbmap_disk *dbmp_le;
struct bmap *bmp = JFS_SBI(ipbmap->i_sb)->bmap;
struct metapage *mp;
int i;
/*
* write bmap global control page
*/
/* get the buffer for the on-disk bmap descriptor. */
mp = read_metapage(ipbmap,
BMAPBLKNO << JFS_SBI(ipbmap->i_sb)->l2nbperpage,
PSIZE, 0);
if (mp == NULL) {
jfs_err("dbSync: read_metapage failed!");
return -EIO;
}
/* copy the in-memory version of the bmap to the on-disk version */
dbmp_le = (struct dbmap_disk *) mp->data;
dbmp_le->dn_mapsize = cpu_to_le64(bmp->db_mapsize);
dbmp_le->dn_nfree = cpu_to_le64(bmp->db_nfree);
dbmp_le->dn_l2nbperpage = cpu_to_le32(bmp->db_l2nbperpage);
dbmp_le->dn_numag = cpu_to_le32(bmp->db_numag);
dbmp_le->dn_maxlevel = cpu_to_le32(bmp->db_maxlevel);
dbmp_le->dn_maxag = cpu_to_le32(bmp->db_maxag);
dbmp_le->dn_agpref = cpu_to_le32(bmp->db_agpref);
dbmp_le->dn_aglevel = cpu_to_le32(bmp->db_aglevel);
dbmp_le->dn_agheigth = cpu_to_le32(bmp->db_agheigth);
dbmp_le->dn_agwidth = cpu_to_le32(bmp->db_agwidth);
dbmp_le->dn_agstart = cpu_to_le32(bmp->db_agstart);
dbmp_le->dn_agl2size = cpu_to_le32(bmp->db_agl2size);
for (i = 0; i < MAXAG; i++)
dbmp_le->dn_agfree[i] = cpu_to_le64(bmp->db_agfree[i]);
dbmp_le->dn_agsize = cpu_to_le64(bmp->db_agsize);
dbmp_le->dn_maxfreebud = bmp->db_maxfreebud;
/* write the buffer */
write_metapage(mp);
/*
* write out dirty pages of bmap
*/
filemap_fdatawrite(ipbmap->i_mapping);
filemap_fdatawait(ipbmap->i_mapping);
ipbmap->i_state |= I_DIRTY;
diWriteSpecial(ipbmap, 0);
return (0);
}
/*
* NAME: dbFree()
*
* FUNCTION: free the specified block range from the working block
* allocation map.
*
* the blocks will be free from the working map one dmap
* at a time.
*
* PARAMETERS:
* ip - pointer to in-core inode;
* blkno - starting block number to be freed.
* nblocks - number of blocks to be freed.
*
* RETURN VALUES:
* 0 - success
* -EIO - i/o error
*/
int dbFree(struct inode *ip, s64 blkno, s64 nblocks)
{
struct metapage *mp;
struct dmap *dp;
int nb, rc;
s64 lblkno, rem;
struct inode *ipbmap = JFS_SBI(ip->i_sb)->ipbmap;
struct bmap *bmp = JFS_SBI(ip->i_sb)->bmap;
IREAD_LOCK(ipbmap);
/* block to be freed better be within the mapsize. */
if (unlikely((blkno == 0) || (blkno + nblocks > bmp->db_mapsize))) {
IREAD_UNLOCK(ipbmap);
printk(KERN_ERR "blkno = %Lx, nblocks = %Lx\n",
(unsigned long long) blkno,
(unsigned long long) nblocks);
jfs_error(ip->i_sb,
"dbFree: block to be freed is outside the map");
return -EIO;
}
/*
* free the blocks a dmap at a time.
*/
mp = NULL;
for (rem = nblocks; rem > 0; rem -= nb, blkno += nb) {
/* release previous dmap if any */
if (mp) {
write_metapage(mp);
}
/* get the buffer for the current dmap. */
lblkno = BLKTODMAP(blkno, bmp->db_l2nbperpage);
mp = read_metapage(ipbmap, lblkno, PSIZE, 0);
if (mp == NULL) {
IREAD_UNLOCK(ipbmap);
return -EIO;
}
dp = (struct dmap *) mp->data;
/* determine the number of blocks to be freed from
* this dmap.
*/
nb = min(rem, BPERDMAP - (blkno & (BPERDMAP - 1)));
DBALLOCCK(bmp->db_DBmap, bmp->db_mapsize, blkno, nb);
/* free the blocks. */
if ((rc = dbFreeDmap(bmp, dp, blkno, nb))) {
release_metapage(mp);
IREAD_UNLOCK(ipbmap);
return (rc);
}
DBFREE(bmp->db_DBmap, bmp->db_mapsize, blkno, nb);
}
/* write the last buffer. */
write_metapage(mp);
IREAD_UNLOCK(ipbmap);
return (0);
}
/*
* NAME: dbUpdatePMap()
*
* FUNCTION: update the allocation state (free or allocate) of the
* specified block range in the persistent block allocation map.
*
* the blocks will be updated in the persistent map one
* dmap at a time.
*
* PARAMETERS:
* ipbmap - pointer to in-core inode for the block map.
* free - TRUE if block range is to be freed from the persistent
* map; FALSE if it is to be allocated.
* blkno - starting block number of the range.
* nblocks - number of contiguous blocks in the range.
* tblk - transaction block;
*
* RETURN VALUES:
* 0 - success
* -EIO - i/o error
*/
int
dbUpdatePMap(struct inode *ipbmap,
int free, s64 blkno, s64 nblocks, struct tblock * tblk)
{
int nblks, dbitno, wbitno, rbits;
int word, nbits, nwords;
struct bmap *bmp = JFS_SBI(ipbmap->i_sb)->bmap;
s64 lblkno, rem, lastlblkno;
u32 mask;
struct dmap *dp;
struct metapage *mp;
struct jfs_log *log;
int lsn, difft, diffp;
/* the blocks better be within the mapsize. */
if (blkno + nblocks > bmp->db_mapsize) {
printk(KERN_ERR "blkno = %Lx, nblocks = %Lx\n",
(unsigned long long) blkno,
(unsigned long long) nblocks);
jfs_error(ipbmap->i_sb,
"dbUpdatePMap: blocks are outside the map");
return -EIO;
}
/* compute delta of transaction lsn from log syncpt */
lsn = tblk->lsn;
log = (struct jfs_log *) JFS_SBI(tblk->sb)->log;
logdiff(difft, lsn, log);
/*
* update the block state a dmap at a time.
*/
mp = NULL;
lastlblkno = 0;
for (rem = nblocks; rem > 0; rem -= nblks, blkno += nblks) {
/* get the buffer for the current dmap. */
lblkno = BLKTODMAP(blkno, bmp->db_l2nbperpage);
if (lblkno != lastlblkno) {
if (mp) {
write_metapage(mp);
}
mp = read_metapage(bmp->db_ipbmap, lblkno, PSIZE,
0);
if (mp == NULL)
return -EIO;
}
dp = (struct dmap *) mp->data;
/* determine the bit number and word within the dmap of
* the starting block. also determine how many blocks
* are to be updated within this dmap.
*/
dbitno = blkno & (BPERDMAP - 1);
word = dbitno >> L2DBWORD;
nblks = min(rem, (s64)BPERDMAP - dbitno);
/* update the bits of the dmap words. the first and last
* words may only have a subset of their bits updated. if
* this is the case, we'll work against that word (i.e.
* partial first and/or last) only in a single pass. a
* single pass will also be used to update all words that
* are to have all their bits updated.
*/
for (rbits = nblks; rbits > 0;
rbits -= nbits, dbitno += nbits) {
/* determine the bit number within the word and
* the number of bits within the word.
*/
wbitno = dbitno & (DBWORD - 1);
nbits = min(rbits, DBWORD - wbitno);
/* check if only part of the word is to be updated. */
if (nbits < DBWORD) {
/* update (free or allocate) the bits
* in this word.
*/
mask =
(ONES << (DBWORD - nbits) >> wbitno);
if (free)
dp->pmap[word] &=
cpu_to_le32(~mask);
else
dp->pmap[word] |=
cpu_to_le32(mask);
word += 1;
} else {
/* one or more words are to have all
* their bits updated. determine how
* many words and how many bits.
*/
nwords = rbits >> L2DBWORD;
nbits = nwords << L2DBWORD;
/* update (free or allocate) the bits
* in these words.
*/
if (free)
memset(&dp->pmap[word], 0,
nwords * 4);
else
memset(&dp->pmap[word], (int) ONES,
nwords * 4);
word += nwords;
}
}
/*
* update dmap lsn
*/
if (lblkno == lastlblkno)
continue;
lastlblkno = lblkno;
if (mp->lsn != 0) {
/* inherit older/smaller lsn */
logdiff(diffp, mp->lsn, log);
if (difft < diffp) {
mp->lsn = lsn;
/* move bp after tblock in logsync list */
LOGSYNC_LOCK(log);
list_move(&mp->synclist, &tblk->synclist);
LOGSYNC_UNLOCK(log);
}
/* inherit younger/larger clsn */
LOGSYNC_LOCK(log);
logdiff(difft, tblk->clsn, log);
logdiff(diffp, mp->clsn, log);
if (difft > diffp)
mp->clsn = tblk->clsn;
LOGSYNC_UNLOCK(log);
} else {
mp->log = log;
mp->lsn = lsn;
/* insert bp after tblock in logsync list */
LOGSYNC_LOCK(log);
log->count++;
list_add(&mp->synclist, &tblk->synclist);
mp->clsn = tblk->clsn;
LOGSYNC_UNLOCK(log);
}
}
/* write the last buffer. */
if (mp) {
write_metapage(mp);
}
return (0);
}
/*
* NAME: dbNextAG()
*
* FUNCTION: find the preferred allocation group for new allocations.
*
* Within the allocation groups, we maintain a preferred
* allocation group which consists of a group with at least
* average free space. It is the preferred group that we target
* new inode allocation towards. The tie-in between inode
* allocation and block allocation occurs as we allocate the
* first (data) block of an inode and specify the inode (block)
* as the allocation hint for this block.
*
* We try to avoid having more than one open file growing in
* an allocation group, as this will lead to fragmentation.
* This differs from the old OS/2 method of trying to keep
* empty ags around for large allocations.
*
* PARAMETERS:
* ipbmap - pointer to in-core inode for the block map.
*
* RETURN VALUES:
* the preferred allocation group number.
*/
int dbNextAG(struct inode *ipbmap)
{
s64 avgfree;
int agpref;
s64 hwm = 0;
int i;
int next_best = -1;
struct bmap *bmp = JFS_SBI(ipbmap->i_sb)->bmap;
BMAP_LOCK(bmp);
/* determine the average number of free blocks within the ags. */
avgfree = (u32)bmp->db_nfree / bmp->db_numag;
/*
* if the current preferred ag does not have an active allocator
* and has at least average freespace, return it
*/
agpref = bmp->db_agpref;
if ((atomic_read(&bmp->db_active[agpref]) == 0) &&
(bmp->db_agfree[agpref] >= avgfree))
goto unlock;
/* From the last preferred ag, find the next one with at least
* average free space.
*/
for (i = 0 ; i < bmp->db_numag; i++, agpref++) {
if (agpref == bmp->db_numag)
agpref = 0;
if (atomic_read(&bmp->db_active[agpref]))
/* open file is currently growing in this ag */
continue;
if (bmp->db_agfree[agpref] >= avgfree) {
/* Return this one */
bmp->db_agpref = agpref;
goto unlock;
} else if (bmp->db_agfree[agpref] > hwm) {
/* Less than avg. freespace, but best so far */
hwm = bmp->db_agfree[agpref];
next_best = agpref;
}
}
/*
* If no inactive ag was found with average freespace, use the
* next best
*/
if (next_best != -1)
bmp->db_agpref = next_best;
/* else leave db_agpref unchanged */
unlock:
BMAP_UNLOCK(bmp);
/* return the preferred group.
*/
return (bmp->db_agpref);
}
/*
* NAME: dbAlloc()
*
* FUNCTION: attempt to allocate a specified number of contiguous free
* blocks from the working allocation block map.
*
* the block allocation policy uses hints and a multi-step
* approach.
*
* for allocation requests smaller than the number of blocks
* per dmap, we first try to allocate the new blocks
* immediately following the hint. if these blocks are not
* available, we try to allocate blocks near the hint. if
* no blocks near the hint are available, we next try to
* allocate within the same dmap as contains the hint.
*
* if no blocks are available in the dmap or the allocation
* request is larger than the dmap size, we try to allocate
* within the same allocation group as contains the hint. if
* this does not succeed, we finally try to allocate anywhere
* within the aggregate.
*
* we also try to allocate anywhere within the aggregate for
* for allocation requests larger than the allocation group
* size or requests that specify no hint value.
*
* PARAMETERS:
* ip - pointer to in-core inode;
* hint - allocation hint.
* nblocks - number of contiguous blocks in the range.
* results - on successful return, set to the starting block number
* of the newly allocated contiguous range.
*
* RETURN VALUES:
* 0 - success
* -ENOSPC - insufficient disk resources
* -EIO - i/o error
*/
int dbAlloc(struct inode *ip, s64 hint, s64 nblocks, s64 * results)
{
int rc, agno;
struct inode *ipbmap = JFS_SBI(ip->i_sb)->ipbmap;
struct bmap *bmp;
struct metapage *mp;
s64 lblkno, blkno;
struct dmap *dp;
int l2nb;
s64 mapSize;
int writers;
/* assert that nblocks is valid */
assert(nblocks > 0);
#ifdef _STILL_TO_PORT
/* DASD limit check F226941 */
if (OVER_LIMIT(ip, nblocks))
return -ENOSPC;
#endif /* _STILL_TO_PORT */
/* get the log2 number of blocks to be allocated.
* if the number of blocks is not a log2 multiple,
* it will be rounded up to the next log2 multiple.
*/
l2nb = BLKSTOL2(nblocks);
bmp = JFS_SBI(ip->i_sb)->bmap;
//retry: /* serialize w.r.t.extendfs() */
mapSize = bmp->db_mapsize;
/* the hint should be within the map */
if (hint >= mapSize) {
jfs_error(ip->i_sb, "dbAlloc: the hint is outside the map");
return -EIO;
}
/* if the number of blocks to be allocated is greater than the
* allocation group size, try to allocate anywhere.
*/
if (l2nb > bmp->db_agl2size) {
IWRITE_LOCK(ipbmap);
rc = dbAllocAny(bmp, nblocks, l2nb, results);
if (rc == 0) {
DBALLOC(bmp->db_DBmap, bmp->db_mapsize, *results,
nblocks);
}
goto write_unlock;
}
/*
* If no hint, let dbNextAG recommend an allocation group
*/
if (hint == 0)
goto pref_ag;
/* we would like to allocate close to the hint. adjust the
* hint to the block following the hint since the allocators
* will start looking for free space starting at this point.
*/
blkno = hint + 1;
if (blkno >= bmp->db_mapsize)
goto pref_ag;
agno = blkno >> bmp->db_agl2size;
/* check if blkno crosses over into a new allocation group.
* if so, check if we should allow allocations within this
* allocation group.
*/
if ((blkno & (bmp->db_agsize - 1)) == 0)
/* check if the AG is currenly being written to.
* if so, call dbNextAG() to find a non-busy
* AG with sufficient free space.
*/
if (atomic_read(&bmp->db_active[agno]))
goto pref_ag;
/* check if the allocation request size can be satisfied from a
* single dmap. if so, try to allocate from the dmap containing
* the hint using a tiered strategy.
*/
if (nblocks <= BPERDMAP) {
IREAD_LOCK(ipbmap);
/* get the buffer for the dmap containing the hint.
*/
rc = -EIO;
lblkno = BLKTODMAP(blkno, bmp->db_l2nbperpage);
mp = read_metapage(ipbmap, lblkno, PSIZE, 0);
if (mp == NULL)
goto read_unlock;
dp = (struct dmap *) mp->data;
/* first, try to satisfy the allocation request with the
* blocks beginning at the hint.
*/
if ((rc = dbAllocNext(bmp, dp, blkno, (int) nblocks))
!= -ENOSPC) {
if (rc == 0) {
*results = blkno;
DBALLOC(bmp->db_DBmap, bmp->db_mapsize,
*results, nblocks);
mark_metapage_dirty(mp);
}
release_metapage(mp);
goto read_unlock;
}
writers = atomic_read(&bmp->db_active[agno]);
if ((writers > 1) ||
((writers == 1) && (JFS_IP(ip)->active_ag != agno))) {
/*
* Someone else is writing in this allocation
* group. To avoid fragmenting, try another ag
*/
release_metapage(mp);
IREAD_UNLOCK(ipbmap);
goto pref_ag;
}
/* next, try to satisfy the allocation request with blocks
* near the hint.
*/
if ((rc =
dbAllocNear(bmp, dp, blkno, (int) nblocks, l2nb, results))
!= -ENOSPC) {
if (rc == 0) {
DBALLOC(bmp->db_DBmap, bmp->db_mapsize,
*results, nblocks);
mark_metapage_dirty(mp);
}
release_metapage(mp);
goto read_unlock;
}
/* try to satisfy the allocation request with blocks within
* the same dmap as the hint.
*/
if ((rc = dbAllocDmapLev(bmp, dp, (int) nblocks, l2nb, results))
!= -ENOSPC) {
if (rc == 0) {
DBALLOC(bmp->db_DBmap, bmp->db_mapsize,
*results, nblocks);
mark_metapage_dirty(mp);
}
release_metapage(mp);
goto read_unlock;
}
release_metapage(mp);
IREAD_UNLOCK(ipbmap);
}
/* try to satisfy the allocation request with blocks within
* the same allocation group as the hint.
*/
IWRITE_LOCK(ipbmap);
if ((rc = dbAllocAG(bmp, agno, nblocks, l2nb, results))
!= -ENOSPC) {
if (rc == 0)
DBALLOC(bmp->db_DBmap, bmp->db_mapsize,
*results, nblocks);
goto write_unlock;
}
IWRITE_UNLOCK(ipbmap);
pref_ag:
/*
* Let dbNextAG recommend a preferred allocation group
*/
agno = dbNextAG(ipbmap);
IWRITE_LOCK(ipbmap);
/* Try to allocate within this allocation group. if that fails, try to
* allocate anywhere in the map.
*/
if ((rc = dbAllocAG(bmp, agno, nblocks, l2nb, results)) == -ENOSPC)
rc = dbAllocAny(bmp, nblocks, l2nb, results);
if (rc == 0) {
DBALLOC(bmp->db_DBmap, bmp->db_mapsize, *results, nblocks);
}
write_unlock:
IWRITE_UNLOCK(ipbmap);
return (rc);
read_unlock:
IREAD_UNLOCK(ipbmap);
return (rc);
}
#ifdef _NOTYET
/*
* NAME: dbAllocExact()
*
* FUNCTION: try to allocate the requested extent;
*
* PARAMETERS:
* ip - pointer to in-core inode;
* blkno - extent address;
* nblocks - extent length;
*
* RETURN VALUES:
* 0 - success
* -ENOSPC - insufficient disk resources
* -EIO - i/o error
*/
int dbAllocExact(struct inode *ip, s64 blkno, int nblocks)
{
int rc;
struct inode *ipbmap = JFS_SBI(ip->i_sb)->ipbmap;
struct bmap *bmp = JFS_SBI(ip->i_sb)->bmap;
struct dmap *dp;
s64 lblkno;
struct metapage *mp;
IREAD_LOCK(ipbmap);
/*
* validate extent request:
*
* note: defragfs policy:
* max 64 blocks will be moved.
* allocation request size must be satisfied from a single dmap.
*/
if (nblocks <= 0 || nblocks > BPERDMAP || blkno >= bmp->db_mapsize) {
IREAD_UNLOCK(ipbmap);
return -EINVAL;
}
if (nblocks > ((s64) 1 << bmp->db_maxfreebud)) {
/* the free space is no longer available */
IREAD_UNLOCK(ipbmap);
return -ENOSPC;
}
/* read in the dmap covering the extent */
lblkno = BLKTODMAP(blkno, bmp->db_l2nbperpage);
mp = read_metapage(ipbmap, lblkno, PSIZE, 0);
if (mp == NULL) {
IREAD_UNLOCK(ipbmap);
return -EIO;
}
dp = (struct dmap *) mp->data;
/* try to allocate the requested extent */
rc = dbAllocNext(bmp, dp, blkno, nblocks);
]
]
|
| 211/2 |
2026/06/30 15:20 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 22426
output: 39
thoughts: 63
Thoughts: Okay, here's my take on those thoughts, framed as an expert's internal monologue:
**Investigating a Truncated Output**
Alright, the output was cut off, which is a bit frustrating, but I can see it was pointing to `dbAllocNext` and `dbAllocNear`. Those are definitely defined in `fs/jfs/jfs_dmap.c`, part of the JFS filesystem, right? No surprises there. Since the issue likely involves allocation, let's zero in on `dbAllocNext`. It's almost always the culprit when you're looking at allocation issues. The kernel's going to need an open source code repository for that... and it has one at 1da177e4c3f4. Good, that's what I needed, now I can dig into the implementation of `dbAllocNext` and see how it interacts with its nearby functions. Time to crack open that code and see what the function is doing when allocating space in JFS, or when it's *supposed* to be allocating space, and figure out why the output was truncated. Hopefully, it's something simple.
|
| 212/2 |
2026/06/30 15:20 |
tool |
git-show |
0m
Args: map[Commit:1da177e4c3f4:fs/jfs/jfs_dmap.c]
Results: map[Output:
Full output is too long, showing 1000 out of 4272 lines.
[/*
* Copyright (C) International Business Machines Corp., 2000-2004
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
* the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/fs.h>
#include "jfs_incore.h"
#include "jfs_superblock.h"
#include "jfs_dmap.h"
#include "jfs_imap.h"
#include "jfs_lock.h"
#include "jfs_metapage.h"
#include "jfs_debug.h"
/*
* Debug code for double-checking block map
*/
/* #define _JFS_DEBUG_DMAP 1 */
#ifdef _JFS_DEBUG_DMAP
#define DBINITMAP(size,ipbmap,results) \
DBinitmap(size,ipbmap,results)
#define DBALLOC(dbmap,mapsize,blkno,nblocks) \
DBAlloc(dbmap,mapsize,blkno,nblocks)
#define DBFREE(dbmap,mapsize,blkno,nblocks) \
DBFree(dbmap,mapsize,blkno,nblocks)
#define DBALLOCCK(dbmap,mapsize,blkno,nblocks) \
DBAllocCK(dbmap,mapsize,blkno,nblocks)
#define DBFREECK(dbmap,mapsize,blkno,nblocks) \
DBFreeCK(dbmap,mapsize,blkno,nblocks)
static void DBinitmap(s64, struct inode *, u32 **);
static void DBAlloc(uint *, s64, s64, s64);
static void DBFree(uint *, s64, s64, s64);
static void DBAllocCK(uint *, s64, s64, s64);
static void DBFreeCK(uint *, s64, s64, s64);
#else
#define DBINITMAP(size,ipbmap,results)
#define DBALLOC(dbmap, mapsize, blkno, nblocks)
#define DBFREE(dbmap, mapsize, blkno, nblocks)
#define DBALLOCCK(dbmap, mapsize, blkno, nblocks)
#define DBFREECK(dbmap, mapsize, blkno, nblocks)
#endif /* _JFS_DEBUG_DMAP */
/*
* SERIALIZATION of the Block Allocation Map.
*
* the working state of the block allocation map is accessed in
* two directions:
*
* 1) allocation and free requests that start at the dmap
* level and move up through the dmap control pages (i.e.
* the vast majority of requests).
*
* 2) allocation requests that start at dmap control page
* level and work down towards the dmaps.
*
* the serialization scheme used here is as follows.
*
* requests which start at the bottom are serialized against each
* other through buffers and each requests holds onto its buffers
* as it works it way up from a single dmap to the required level
* of dmap control page.
* requests that start at the top are serialized against each other
* and request that start from the bottom by the multiple read/single
* write inode lock of the bmap inode. requests starting at the top
* take this lock in write mode while request starting at the bottom
* take the lock in read mode. a single top-down request may proceed
* exclusively while multiple bottoms-up requests may proceed
* simultaneously (under the protection of busy buffers).
*
* in addition to information found in dmaps and dmap control pages,
* the working state of the block allocation map also includes read/
* write information maintained in the bmap descriptor (i.e. total
* free block count, allocation group level free block counts).
* a single exclusive lock (BMAP_LOCK) is used to guard this information
* in the face of multiple-bottoms up requests.
* (lock ordering: IREAD_LOCK, BMAP_LOCK);
*
* accesses to the persistent state of the block allocation map (limited
* to the persistent bitmaps in dmaps) is guarded by (busy) buffers.
*/
#define BMAP_LOCK_INIT(bmp) init_MUTEX(&bmp->db_bmaplock)
#define BMAP_LOCK(bmp) down(&bmp->db_bmaplock)
#define BMAP_UNLOCK(bmp) up(&bmp->db_bmaplock)
/*
* forward references
*/
static void dbAllocBits(struct bmap * bmp, struct dmap * dp, s64 blkno,
int nblocks);
static void dbSplit(dmtree_t * tp, int leafno, int splitsz, int newval);
static void dbBackSplit(dmtree_t * tp, int leafno);
static void dbJoin(dmtree_t * tp, int leafno, int newval);
static void dbAdjTree(dmtree_t * tp, int leafno, int newval);
static int dbAdjCtl(struct bmap * bmp, s64 blkno, int newval, int alloc,
int level);
static int dbAllocAny(struct bmap * bmp, s64 nblocks, int l2nb, s64 * results);
static int dbAllocNext(struct bmap * bmp, struct dmap * dp, s64 blkno,
int nblocks);
static int dbAllocNear(struct bmap * bmp, struct dmap * dp, s64 blkno,
int nblocks,
int l2nb, s64 * results);
static int dbAllocDmap(struct bmap * bmp, struct dmap * dp, s64 blkno,
int nblocks);
static int dbAllocDmapLev(struct bmap * bmp, struct dmap * dp, int nblocks,
int l2nb,
s64 * results);
static int dbAllocAG(struct bmap * bmp, int agno, s64 nblocks, int l2nb,
s64 * results);
static int dbAllocCtl(struct bmap * bmp, s64 nblocks, int l2nb, s64 blkno,
s64 * results);
static int dbExtend(struct inode *ip, s64 blkno, s64 nblocks, s64 addnblocks);
static int dbFindBits(u32 word, int l2nb);
static int dbFindCtl(struct bmap * bmp, int l2nb, int level, s64 * blkno);
static int dbFindLeaf(dmtree_t * tp, int l2nb, int *leafidx);
static void dbFreeBits(struct bmap * bmp, struct dmap * dp, s64 blkno,
int nblocks);
static int dbFreeDmap(struct bmap * bmp, struct dmap * dp, s64 blkno,
int nblocks);
static int dbMaxBud(u8 * cp);
s64 dbMapFileSizeToMapSize(struct inode *ipbmap);
static int blkstol2(s64 nb);
static int cntlz(u32 value);
static int cnttz(u32 word);
static int dbAllocDmapBU(struct bmap * bmp, struct dmap * dp, s64 blkno,
int nblocks);
static int dbInitDmap(struct dmap * dp, s64 blkno, int nblocks);
static int dbInitDmapTree(struct dmap * dp);
static int dbInitTree(struct dmaptree * dtp);
static int dbInitDmapCtl(struct dmapctl * dcp, int level, int i);
static int dbGetL2AGSize(s64 nblocks);
/*
* buddy table
*
* table used for determining buddy sizes within characters of
* dmap bitmap words. the characters themselves serve as indexes
* into the table, with the table elements yielding the maximum
* binary buddy of free bits within the character.
*/
static s8 budtab[256] = {
3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
2, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0,
2, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0,
2, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0,
2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
2, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0,
2, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0,
2, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0,
2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
2, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0,
2, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0,
2, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -1
};
/*
* NAME: dbMount()
*
* FUNCTION: initializate the block allocation map.
*
* memory is allocated for the in-core bmap descriptor and
* the in-core descriptor is initialized from disk.
*
* PARAMETERS:
* ipbmap - pointer to in-core inode for the block map.
*
* RETURN VALUES:
* 0 - success
* -ENOMEM - insufficient memory
* -EIO - i/o error
*/
int dbMount(struct inode *ipbmap)
{
struct bmap *bmp;
struct dbmap_disk *dbmp_le;
struct metapage *mp;
int i;
/*
* allocate/initialize the in-memory bmap descriptor
*/
/* allocate memory for the in-memory bmap descriptor */
bmp = kmalloc(sizeof(struct bmap), GFP_KERNEL);
if (bmp == NULL)
return -ENOMEM;
/* read the on-disk bmap descriptor. */
mp = read_metapage(ipbmap,
BMAPBLKNO << JFS_SBI(ipbmap->i_sb)->l2nbperpage,
PSIZE, 0);
if (mp == NULL) {
kfree(bmp);
return -EIO;
}
/* copy the on-disk bmap descriptor to its in-memory version. */
dbmp_le = (struct dbmap_disk *) mp->data;
bmp->db_mapsize = le64_to_cpu(dbmp_le->dn_mapsize);
bmp->db_nfree = le64_to_cpu(dbmp_le->dn_nfree);
bmp->db_l2nbperpage = le32_to_cpu(dbmp_le->dn_l2nbperpage);
bmp->db_numag = le32_to_cpu(dbmp_le->dn_numag);
bmp->db_maxlevel = le32_to_cpu(dbmp_le->dn_maxlevel);
bmp->db_maxag = le32_to_cpu(dbmp_le->dn_maxag);
bmp->db_agpref = le32_to_cpu(dbmp_le->dn_agpref);
bmp->db_aglevel = le32_to_cpu(dbmp_le->dn_aglevel);
bmp->db_agheigth = le32_to_cpu(dbmp_le->dn_agheigth);
bmp->db_agwidth = le32_to_cpu(dbmp_le->dn_agwidth);
bmp->db_agstart = le32_to_cpu(dbmp_le->dn_agstart);
bmp->db_agl2size = le32_to_cpu(dbmp_le->dn_agl2size);
for (i = 0; i < MAXAG; i++)
bmp->db_agfree[i] = le64_to_cpu(dbmp_le->dn_agfree[i]);
bmp->db_agsize = le64_to_cpu(dbmp_le->dn_agsize);
bmp->db_maxfreebud = dbmp_le->dn_maxfreebud;
/* release the buffer. */
release_metapage(mp);
/* bind the bmap inode and the bmap descriptor to each other. */
bmp->db_ipbmap = ipbmap;
JFS_SBI(ipbmap->i_sb)->bmap = bmp;
memset(bmp->db_active, 0, sizeof(bmp->db_active));
DBINITMAP(bmp->db_mapsize, ipbmap, &bmp->db_DBmap);
/*
* allocate/initialize the bmap lock
*/
BMAP_LOCK_INIT(bmp);
return (0);
}
/*
* NAME: dbUnmount()
*
* FUNCTION: terminate the block allocation map in preparation for
* file system unmount.
*
* the in-core bmap descriptor is written to disk and
* the memory for this descriptor is freed.
*
* PARAMETERS:
* ipbmap - pointer to in-core inode for the block map.
*
* RETURN VALUES:
* 0 - success
* -EIO - i/o error
*/
int dbUnmount(struct inode *ipbmap, int mounterror)
{
struct bmap *bmp = JFS_SBI(ipbmap->i_sb)->bmap;
int i;
if (!(mounterror || isReadOnly(ipbmap)))
dbSync(ipbmap);
/*
* Invalidate the page cache buffers
*/
truncate_inode_pages(ipbmap->i_mapping, 0);
/*
* Sanity Check
*/
for (i = 0; i < bmp->db_numag; i++)
if (atomic_read(&bmp->db_active[i]))
printk(KERN_ERR "dbUnmount: db_active[%d] = %d\n",
i, atomic_read(&bmp->db_active[i]));
/* free the memory for the in-memory bmap. */
kfree(bmp);
return (0);
}
/*
* dbSync()
*/
int dbSync(struct inode *ipbmap)
{
struct dbmap_disk *dbmp_le;
struct bmap *bmp = JFS_SBI(ipbmap->i_sb)->bmap;
struct metapage *mp;
int i;
/*
* write bmap global control page
*/
/* get the buffer for the on-disk bmap descriptor. */
mp = read_metapage(ipbmap,
BMAPBLKNO << JFS_SBI(ipbmap->i_sb)->l2nbperpage,
PSIZE, 0);
if (mp == NULL) {
jfs_err("dbSync: read_metapage failed!");
return -EIO;
}
/* copy the in-memory version of the bmap to the on-disk version */
dbmp_le = (struct dbmap_disk *) mp->data;
dbmp_le->dn_mapsize = cpu_to_le64(bmp->db_mapsize);
dbmp_le->dn_nfree = cpu_to_le64(bmp->db_nfree);
dbmp_le->dn_l2nbperpage = cpu_to_le32(bmp->db_l2nbperpage);
dbmp_le->dn_numag = cpu_to_le32(bmp->db_numag);
dbmp_le->dn_maxlevel = cpu_to_le32(bmp->db_maxlevel);
dbmp_le->dn_maxag = cpu_to_le32(bmp->db_maxag);
dbmp_le->dn_agpref = cpu_to_le32(bmp->db_agpref);
dbmp_le->dn_aglevel = cpu_to_le32(bmp->db_aglevel);
dbmp_le->dn_agheigth = cpu_to_le32(bmp->db_agheigth);
dbmp_le->dn_agwidth = cpu_to_le32(bmp->db_agwidth);
dbmp_le->dn_agstart = cpu_to_le32(bmp->db_agstart);
dbmp_le->dn_agl2size = cpu_to_le32(bmp->db_agl2size);
for (i = 0; i < MAXAG; i++)
dbmp_le->dn_agfree[i] = cpu_to_le64(bmp->db_agfree[i]);
dbmp_le->dn_agsize = cpu_to_le64(bmp->db_agsize);
dbmp_le->dn_maxfreebud = bmp->db_maxfreebud;
/* write the buffer */
write_metapage(mp);
/*
* write out dirty pages of bmap
*/
filemap_fdatawrite(ipbmap->i_mapping);
filemap_fdatawait(ipbmap->i_mapping);
ipbmap->i_state |= I_DIRTY;
diWriteSpecial(ipbmap, 0);
return (0);
}
/*
* NAME: dbFree()
*
* FUNCTION: free the specified block range from the working block
* allocation map.
*
* the blocks will be free from the working map one dmap
* at a time.
*
* PARAMETERS:
* ip - pointer to in-core inode;
* blkno - starting block number to be freed.
* nblocks - number of blocks to be freed.
*
* RETURN VALUES:
* 0 - success
* -EIO - i/o error
*/
int dbFree(struct inode *ip, s64 blkno, s64 nblocks)
{
struct metapage *mp;
struct dmap *dp;
int nb, rc;
s64 lblkno, rem;
struct inode *ipbmap = JFS_SBI(ip->i_sb)->ipbmap;
struct bmap *bmp = JFS_SBI(ip->i_sb)->bmap;
IREAD_LOCK(ipbmap);
/* block to be freed better be within the mapsize. */
if (unlikely((blkno == 0) || (blkno + nblocks > bmp->db_mapsize))) {
IREAD_UNLOCK(ipbmap);
printk(KERN_ERR "blkno = %Lx, nblocks = %Lx\n",
(unsigned long long) blkno,
(unsigned long long) nblocks);
jfs_error(ip->i_sb,
"dbFree: block to be freed is outside the map");
return -EIO;
}
/*
* free the blocks a dmap at a time.
*/
mp = NULL;
for (rem = nblocks; rem > 0; rem -= nb, blkno += nb) {
/* release previous dmap if any */
if (mp) {
write_metapage(mp);
}
/* get the buffer for the current dmap. */
lblkno = BLKTODMAP(blkno, bmp->db_l2nbperpage);
mp = read_metapage(ipbmap, lblkno, PSIZE, 0);
if (mp == NULL) {
IREAD_UNLOCK(ipbmap);
return -EIO;
}
dp = (struct dmap *) mp->data;
/* determine the number of blocks to be freed from
* this dmap.
*/
nb = min(rem, BPERDMAP - (blkno & (BPERDMAP - 1)));
DBALLOCCK(bmp->db_DBmap, bmp->db_mapsize, blkno, nb);
/* free the blocks. */
if ((rc = dbFreeDmap(bmp, dp, blkno, nb))) {
release_metapage(mp);
IREAD_UNLOCK(ipbmap);
return (rc);
}
DBFREE(bmp->db_DBmap, bmp->db_mapsize, blkno, nb);
}
/* write the last buffer. */
write_metapage(mp);
IREAD_UNLOCK(ipbmap);
return (0);
}
/*
* NAME: dbUpdatePMap()
*
* FUNCTION: update the allocation state (free or allocate) of the
* specified block range in the persistent block allocation map.
*
* the blocks will be updated in the persistent map one
* dmap at a time.
*
* PARAMETERS:
* ipbmap - pointer to in-core inode for the block map.
* free - TRUE if block range is to be freed from the persistent
* map; FALSE if it is to be allocated.
* blkno - starting block number of the range.
* nblocks - number of contiguous blocks in the range.
* tblk - transaction block;
*
* RETURN VALUES:
* 0 - success
* -EIO - i/o error
*/
int
dbUpdatePMap(struct inode *ipbmap,
int free, s64 blkno, s64 nblocks, struct tblock * tblk)
{
int nblks, dbitno, wbitno, rbits;
int word, nbits, nwords;
struct bmap *bmp = JFS_SBI(ipbmap->i_sb)->bmap;
s64 lblkno, rem, lastlblkno;
u32 mask;
struct dmap *dp;
struct metapage *mp;
struct jfs_log *log;
int lsn, difft, diffp;
/* the blocks better be within the mapsize. */
if (blkno + nblocks > bmp->db_mapsize) {
printk(KERN_ERR "blkno = %Lx, nblocks = %Lx\n",
(unsigned long long) blkno,
(unsigned long long) nblocks);
jfs_error(ipbmap->i_sb,
"dbUpdatePMap: blocks are outside the map");
return -EIO;
}
/* compute delta of transaction lsn from log syncpt */
lsn = tblk->lsn;
log = (struct jfs_log *) JFS_SBI(tblk->sb)->log;
logdiff(difft, lsn, log);
/*
* update the block state a dmap at a time.
*/
mp = NULL;
lastlblkno = 0;
for (rem = nblocks; rem > 0; rem -= nblks, blkno += nblks) {
/* get the buffer for the current dmap. */
lblkno = BLKTODMAP(blkno, bmp->db_l2nbperpage);
if (lblkno != lastlblkno) {
if (mp) {
write_metapage(mp);
}
mp = read_metapage(bmp->db_ipbmap, lblkno, PSIZE,
0);
if (mp == NULL)
return -EIO;
}
dp = (struct dmap *) mp->data;
/* determine the bit number and word within the dmap of
* the starting block. also determine how many blocks
* are to be updated within this dmap.
*/
dbitno = blkno & (BPERDMAP - 1);
word = dbitno >> L2DBWORD;
nblks = min(rem, (s64)BPERDMAP - dbitno);
/* update the bits of the dmap words. the first and last
* words may only have a subset of their bits updated. if
* this is the case, we'll work against that word (i.e.
* partial first and/or last) only in a single pass. a
* single pass will also be used to update all words that
* are to have all their bits updated.
*/
for (rbits = nblks; rbits > 0;
rbits -= nbits, dbitno += nbits) {
/* determine the bit number within the word and
* the number of bits within the word.
*/
wbitno = dbitno & (DBWORD - 1);
nbits = min(rbits, DBWORD - wbitno);
/* check if only part of the word is to be updated. */
if (nbits < DBWORD) {
/* update (free or allocate) the bits
* in this word.
*/
mask =
(ONES << (DBWORD - nbits) >> wbitno);
if (free)
dp->pmap[word] &=
cpu_to_le32(~mask);
else
dp->pmap[word] |=
cpu_to_le32(mask);
word += 1;
} else {
/* one or more words are to have all
* their bits updated. determine how
* many words and how many bits.
*/
nwords = rbits >> L2DBWORD;
nbits = nwords << L2DBWORD;
/* update (free or allocate) the bits
* in these words.
*/
if (free)
memset(&dp->pmap[word], 0,
nwords * 4);
else
memset(&dp->pmap[word], (int) ONES,
nwords * 4);
word += nwords;
}
}
/*
* update dmap lsn
*/
if (lblkno == lastlblkno)
continue;
lastlblkno = lblkno;
if (mp->lsn != 0) {
/* inherit older/smaller lsn */
logdiff(diffp, mp->lsn, log);
if (difft < diffp) {
mp->lsn = lsn;
/* move bp after tblock in logsync list */
LOGSYNC_LOCK(log);
list_move(&mp->synclist, &tblk->synclist);
LOGSYNC_UNLOCK(log);
}
/* inherit younger/larger clsn */
LOGSYNC_LOCK(log);
logdiff(difft, tblk->clsn, log);
logdiff(diffp, mp->clsn, log);
if (difft > diffp)
mp->clsn = tblk->clsn;
LOGSYNC_UNLOCK(log);
} else {
mp->log = log;
mp->lsn = lsn;
/* insert bp after tblock in logsync list */
LOGSYNC_LOCK(log);
log->count++;
list_add(&mp->synclist, &tblk->synclist);
mp->clsn = tblk->clsn;
LOGSYNC_UNLOCK(log);
}
}
/* write the last buffer. */
if (mp) {
write_metapage(mp);
}
return (0);
}
/*
* NAME: dbNextAG()
*
* FUNCTION: find the preferred allocation group for new allocations.
*
* Within the allocation groups, we maintain a preferred
* allocation group which consists of a group with at least
* average free space. It is the preferred group that we target
* new inode allocation towards. The tie-in between inode
* allocation and block allocation occurs as we allocate the
* first (data) block of an inode and specify the inode (block)
* as the allocation hint for this block.
*
* We try to avoid having more than one open file growing in
* an allocation group, as this will lead to fragmentation.
* This differs from the old OS/2 method of trying to keep
* empty ags around for large allocations.
*
* PARAMETERS:
* ipbmap - pointer to in-core inode for the block map.
*
* RETURN VALUES:
* the preferred allocation group number.
*/
int dbNextAG(struct inode *ipbmap)
{
s64 avgfree;
int agpref;
s64 hwm = 0;
int i;
int next_best = -1;
struct bmap *bmp = JFS_SBI(ipbmap->i_sb)->bmap;
BMAP_LOCK(bmp);
/* determine the average number of free blocks within the ags. */
avgfree = (u32)bmp->db_nfree / bmp->db_numag;
/*
* if the current preferred ag does not have an active allocator
* and has at least average freespace, return it
*/
agpref = bmp->db_agpref;
if ((atomic_read(&bmp->db_active[agpref]) == 0) &&
(bmp->db_agfree[agpref] >= avgfree))
goto unlock;
/* From the last preferred ag, find the next one with at least
* average free space.
*/
for (i = 0 ; i < bmp->db_numag; i++, agpref++) {
if (agpref == bmp->db_numag)
agpref = 0;
if (atomic_read(&bmp->db_active[agpref]))
/* open file is currently growing in this ag */
continue;
if (bmp->db_agfree[agpref] >= avgfree) {
/* Return this one */
bmp->db_agpref = agpref;
goto unlock;
} else if (bmp->db_agfree[agpref] > hwm) {
/* Less than avg. freespace, but best so far */
hwm = bmp->db_agfree[agpref];
next_best = agpref;
}
}
/*
* If no inactive ag was found with average freespace, use the
* next best
*/
if (next_best != -1)
bmp->db_agpref = next_best;
/* else leave db_agpref unchanged */
unlock:
BMAP_UNLOCK(bmp);
/* return the preferred group.
*/
return (bmp->db_agpref);
}
/*
* NAME: dbAlloc()
*
* FUNCTION: attempt to allocate a specified number of contiguous free
* blocks from the working allocation block map.
*
* the block allocation policy uses hints and a multi-step
* approach.
*
* for allocation requests smaller than the number of blocks
* per dmap, we first try to allocate the new blocks
* immediately following the hint. if these blocks are not
* available, we try to allocate blocks near the hint. if
* no blocks near the hint are available, we next try to
* allocate within the same dmap as contains the hint.
*
* if no blocks are available in the dmap or the allocation
* request is larger than the dmap size, we try to allocate
* within the same allocation group as contains the hint. if
* this does not succeed, we finally try to allocate anywhere
* within the aggregate.
*
* we also try to allocate anywhere within the aggregate for
* for allocation requests larger than the allocation group
* size or requests that specify no hint value.
*
* PARAMETERS:
* ip - pointer to in-core inode;
* hint - allocation hint.
* nblocks - number of contiguous blocks in the range.
* results - on successful return, set to the starting block number
* of the newly allocated contiguous range.
*
* RETURN VALUES:
* 0 - success
* -ENOSPC - insufficient disk resources
* -EIO - i/o error
*/
int dbAlloc(struct inode *ip, s64 hint, s64 nblocks, s64 * results)
{
int rc, agno;
struct inode *ipbmap = JFS_SBI(ip->i_sb)->ipbmap;
struct bmap *bmp;
struct metapage *mp;
s64 lblkno, blkno;
struct dmap *dp;
int l2nb;
s64 mapSize;
int writers;
/* assert that nblocks is valid */
assert(nblocks > 0);
#ifdef _STILL_TO_PORT
/* DASD limit check F226941 */
if (OVER_LIMIT(ip, nblocks))
return -ENOSPC;
#endif /* _STILL_TO_PORT */
/* get the log2 number of blocks to be allocated.
* if the number of blocks is not a log2 multiple,
* it will be rounded up to the next log2 multiple.
*/
l2nb = BLKSTOL2(nblocks);
bmp = JFS_SBI(ip->i_sb)->bmap;
//retry: /* serialize w.r.t.extendfs() */
mapSize = bmp->db_mapsize;
/* the hint should be within the map */
if (hint >= mapSize) {
jfs_error(ip->i_sb, "dbAlloc: the hint is outside the map");
return -EIO;
}
/* if the number of blocks to be allocated is greater than the
* allocation group size, try to allocate anywhere.
*/
if (l2nb > bmp->db_agl2size) {
IWRITE_LOCK(ipbmap);
rc = dbAllocAny(bmp, nblocks, l2nb, results);
if (rc == 0) {
DBALLOC(bmp->db_DBmap, bmp->db_mapsize, *results,
nblocks);
}
goto write_unlock;
}
/*
* If no hint, let dbNextAG recommend an allocation group
*/
if (hint == 0)
goto pref_ag;
/* we would like to allocate close to the hint. adjust the
* hint to the block following the hint since the allocators
* will start looking for free space starting at this point.
*/
blkno = hint + 1;
if (blkno >= bmp->db_mapsize)
goto pref_ag;
agno = blkno >> bmp->db_agl2size;
/* check if blkno crosses over into a new allocation group.
* if so, check if we should allow allocations within this
* allocation group.
*/
if ((blkno & (bmp->db_agsize - 1)) == 0)
/* check if the AG is currenly being written to.
* if so, call dbNextAG() to find a non-busy
* AG with sufficient free space.
*/
if (atomic_read(&bmp->db_active[agno]))
goto pref_ag;
/* check if the allocation request size can be satisfied from a
* single dmap. if so, try to allocate from the dmap containing
* the hint using a tiered strategy.
*/
if (nblocks <= BPERDMAP) {
IREAD_LOCK(ipbmap);
/* get the buffer for the dmap containing the hint.
*/
rc = -EIO;
lblkno = BLKTODMAP(blkno, bmp->db_l2nbperpage);
mp = read_metapage(ipbmap, lblkno, PSIZE, 0);
if (mp == NULL)
goto read_unlock;
dp = (struct dmap *) mp->data;
/* first, try to satisfy the allocation request with the
* blocks beginning at the hint.
*/
if ((rc = dbAllocNext(bmp, dp, blkno, (int) nblocks))
!= -ENOSPC) {
if (rc == 0) {
*results = blkno;
DBALLOC(bmp->db_DBmap, bmp->db_mapsize,
*results, nblocks);
mark_metapage_dirty(mp);
}
release_metapage(mp);
goto read_unlock;
}
writers = atomic_read(&bmp->db_active[agno]);
if ((writers > 1) ||
((writers == 1) && (JFS_IP(ip)->active_ag != agno))) {
/*
* Someone else is writing in this allocation
* group. To avoid fragmenting, try another ag
*/
release_metapage(mp);
IREAD_UNLOCK(ipbmap);
goto pref_ag;
}
/* next, try to satisfy the allocation request with blocks
* near the hint.
*/
if ((rc =
dbAllocNear(bmp, dp, blkno, (int) nblocks, l2nb, results))
!= -ENOSPC) {
if (rc == 0) {
DBALLOC(bmp->db_DBmap, bmp->db_mapsize,
*results, nblocks);
mark_metapage_dirty(mp);
}
release_metapage(mp);
goto read_unlock;
}
/* try to satisfy the allocation request with blocks within
* the same dmap as the hint.
*/
if ((rc = dbAllocDmapLev(bmp, dp, (int) nblocks, l2nb, results))
!= -ENOSPC) {
if (rc == 0) {
DBALLOC(bmp->db_DBmap, bmp->db_mapsize,
*results, nblocks);
mark_metapage_dirty(mp);
}
release_metapage(mp);
goto read_unlock;
}
release_metapage(mp);
IREAD_UNLOCK(ipbmap);
}
/* try to satisfy the allocation request with blocks within
* the same allocation group as the hint.
*/
IWRITE_LOCK(ipbmap);
if ((rc = dbAllocAG(bmp, agno, nblocks, l2nb, results))
!= -ENOSPC) {
if (rc == 0)
DBALLOC(bmp->db_DBmap, bmp->db_mapsize,
*results, nblocks);
goto write_unlock;
}
IWRITE_UNLOCK(ipbmap);
pref_ag:
/*
* Let dbNextAG recommend a preferred allocation group
*/
agno = dbNextAG(ipbmap);
IWRITE_LOCK(ipbmap);
/* Try to allocate within this allocation group. if that fails, try to
* allocate anywhere in the map.
*/
if ((rc = dbAllocAG(bmp, agno, nblocks, l2nb, results)) == -ENOSPC)
rc = dbAllocAny(bmp, nblocks, l2nb, results);
if (rc == 0) {
DBALLOC(bmp->db_DBmap, bmp->db_mapsize, *results, nblocks);
}
write_unlock:
IWRITE_UNLOCK(ipbmap);
return (rc);
read_unlock:
IREAD_UNLOCK(ipbmap);
return (rc);
}
#ifdef _NOTYET
/*
* NAME: dbAllocExact()
*
* FUNCTION: try to allocate the requested extent;
*
* PARAMETERS:
* ip - pointer to in-core inode;
* blkno - extent address;
* nblocks - extent length;
*
* RETURN VALUES:
* 0 - success
* -ENOSPC - insufficient disk resources
* -EIO - i/o error
*/
int dbAllocExact(struct inode *ip, s64 blkno, int nblocks)
{
int rc;
struct inode *ipbmap = JFS_SBI(ip->i_sb)->ipbmap;
struct bmap *bmp = JFS_SBI(ip->i_sb)->bmap;
struct dmap *dp;
s64 lblkno;
struct metapage *mp;
IREAD_LOCK(ipbmap);
/*
* validate extent request:
*
* note: defragfs policy:
* max 64 blocks will be moved.
* allocation request size must be satisfied from a single dmap.
*/
if (nblocks <= 0 || nblocks > BPERDMAP || blkno >= bmp->db_mapsize) {
IREAD_UNLOCK(ipbmap);
return -EINVAL;
}
if (nblocks > ((s64) 1 << bmp->db_maxfreebud)) {
/* the free space is no longer available */
IREAD_UNLOCK(ipbmap);
return -ENOSPC;
}
/* read in the dmap covering the extent */
lblkno = BLKTODMAP(blkno, bmp->db_l2nbperpage);
mp = read_metapage(ipbmap, lblkno, PSIZE, 0);
if (mp == NULL) {
IREAD_UNLOCK(ipbmap);
return -EIO;
}
dp = (struct dmap *) mp->data;
/* try to allocate the requested extent */
rc = dbAllocNext(bmp, dp, blkno, nblocks);
]
]
|
| 213/2 |
2026/06/30 15:20 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 34697
output: 54
thoughts: 124
Thoughts: **Investigating the Leaf Index Condition**
Okay, first thought: I need to pinpoint the introduction of that specific conditional statement, `if (dp->tree.leafidx != cpu_to_le32(LEAFIND))`. It's a key piece of logic I'm tracking down. My initial instinct is to leverage `git blame` or even `git log -S` for the code, it's the obvious go-to for finding the insertion point.
Wait a second... didn't I already *do* that? I vaguely recall running `git log` and searching for that exact condition. Let me quickly check the output. Ah, yes! The commit hash I got back from that search was `1da177e4c3f4`. Good, I've got a starting point.
Now, just to be thorough and exclude other possibilities, I should do one more check. I'll search for any *other* commits that might have touched the `leafidx` variable, while also involving either the `dbAllocNext` or `dbAllocNear` functions, to see if they are involved somehow. It's possible related changes might hold the explanation.
|
| 214/2 |
2026/06/30 15:21 |
tool |
git-log |
0m
Args: map[CodeRegexp:dp->tree\.leafidx != cpu_to_le32\(LEAFIND\) PathPrefix:fs/jfs/jfs_dmap.c]
Results: map[Output:1da177e4c3f4 Linux-2.6.12-rc2
]
|
| 215/2 |
2026/06/30 15:21 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 35162
output: 39
thoughts: 173
Thoughts: **Analyzing the JFS Bug: A Deep Dive**
Okay, so let me get this straight. The bug report is highlighting a critical deficiency in JFS's integrity checks. The explanation clearly states that a check was *added* for `dmapctl` structures – referencing commit `cce219b203c4`. This is good, but the key issue is that, astonishingly, the *`dmaptree` structures* themselves – the core of the `dmap` logic – have been completely unvalidated since the very beginning.
My mind is racing. This lack of validation has been present since the project's inception. Specifically, that would be commit `1da177e4c3f4`, which is the initial commit for Linux 2.6.12-rc2! This is a long-standing potential vulnerability.
Now, I need to focus. My immediate concern is whether this oversight directly impacts `dbJoin`, which is a critical function within the database-joining operation. I need to trace the evolution of `dbJoin` to determine if this out-of-bounds access has been there from the start. That would be a frightening realization if it's the case! The next step is a deep dive into the code history...
|
| 216/2 |
2026/06/30 15:21 |
tool |
git-show |
0m
Args: map[Commit:1da177e4c3f4:fs/jfs/jfs_dmap.c]
Results: map[Output:
Full output is too long, showing 1000 out of 4272 lines.
[/*
* Copyright (C) International Business Machines Corp., 2000-2004
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
* the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/fs.h>
#include "jfs_incore.h"
#include "jfs_superblock.h"
#include "jfs_dmap.h"
#include "jfs_imap.h"
#include "jfs_lock.h"
#include "jfs_metapage.h"
#include "jfs_debug.h"
/*
* Debug code for double-checking block map
*/
/* #define _JFS_DEBUG_DMAP 1 */
#ifdef _JFS_DEBUG_DMAP
#define DBINITMAP(size,ipbmap,results) \
DBinitmap(size,ipbmap,results)
#define DBALLOC(dbmap,mapsize,blkno,nblocks) \
DBAlloc(dbmap,mapsize,blkno,nblocks)
#define DBFREE(dbmap,mapsize,blkno,nblocks) \
DBFree(dbmap,mapsize,blkno,nblocks)
#define DBALLOCCK(dbmap,mapsize,blkno,nblocks) \
DBAllocCK(dbmap,mapsize,blkno,nblocks)
#define DBFREECK(dbmap,mapsize,blkno,nblocks) \
DBFreeCK(dbmap,mapsize,blkno,nblocks)
static void DBinitmap(s64, struct inode *, u32 **);
static void DBAlloc(uint *, s64, s64, s64);
static void DBFree(uint *, s64, s64, s64);
static void DBAllocCK(uint *, s64, s64, s64);
static void DBFreeCK(uint *, s64, s64, s64);
#else
#define DBINITMAP(size,ipbmap,results)
#define DBALLOC(dbmap, mapsize, blkno, nblocks)
#define DBFREE(dbmap, mapsize, blkno, nblocks)
#define DBALLOCCK(dbmap, mapsize, blkno, nblocks)
#define DBFREECK(dbmap, mapsize, blkno, nblocks)
#endif /* _JFS_DEBUG_DMAP */
/*
* SERIALIZATION of the Block Allocation Map.
*
* the working state of the block allocation map is accessed in
* two directions:
*
* 1) allocation and free requests that start at the dmap
* level and move up through the dmap control pages (i.e.
* the vast majority of requests).
*
* 2) allocation requests that start at dmap control page
* level and work down towards the dmaps.
*
* the serialization scheme used here is as follows.
*
* requests which start at the bottom are serialized against each
* other through buffers and each requests holds onto its buffers
* as it works it way up from a single dmap to the required level
* of dmap control page.
* requests that start at the top are serialized against each other
* and request that start from the bottom by the multiple read/single
* write inode lock of the bmap inode. requests starting at the top
* take this lock in write mode while request starting at the bottom
* take the lock in read mode. a single top-down request may proceed
* exclusively while multiple bottoms-up requests may proceed
* simultaneously (under the protection of busy buffers).
*
* in addition to information found in dmaps and dmap control pages,
* the working state of the block allocation map also includes read/
* write information maintained in the bmap descriptor (i.e. total
* free block count, allocation group level free block counts).
* a single exclusive lock (BMAP_LOCK) is used to guard this information
* in the face of multiple-bottoms up requests.
* (lock ordering: IREAD_LOCK, BMAP_LOCK);
*
* accesses to the persistent state of the block allocation map (limited
* to the persistent bitmaps in dmaps) is guarded by (busy) buffers.
*/
#define BMAP_LOCK_INIT(bmp) init_MUTEX(&bmp->db_bmaplock)
#define BMAP_LOCK(bmp) down(&bmp->db_bmaplock)
#define BMAP_UNLOCK(bmp) up(&bmp->db_bmaplock)
/*
* forward references
*/
static void dbAllocBits(struct bmap * bmp, struct dmap * dp, s64 blkno,
int nblocks);
static void dbSplit(dmtree_t * tp, int leafno, int splitsz, int newval);
static void dbBackSplit(dmtree_t * tp, int leafno);
static void dbJoin(dmtree_t * tp, int leafno, int newval);
static void dbAdjTree(dmtree_t * tp, int leafno, int newval);
static int dbAdjCtl(struct bmap * bmp, s64 blkno, int newval, int alloc,
int level);
static int dbAllocAny(struct bmap * bmp, s64 nblocks, int l2nb, s64 * results);
static int dbAllocNext(struct bmap * bmp, struct dmap * dp, s64 blkno,
int nblocks);
static int dbAllocNear(struct bmap * bmp, struct dmap * dp, s64 blkno,
int nblocks,
int l2nb, s64 * results);
static int dbAllocDmap(struct bmap * bmp, struct dmap * dp, s64 blkno,
int nblocks);
static int dbAllocDmapLev(struct bmap * bmp, struct dmap * dp, int nblocks,
int l2nb,
s64 * results);
static int dbAllocAG(struct bmap * bmp, int agno, s64 nblocks, int l2nb,
s64 * results);
static int dbAllocCtl(struct bmap * bmp, s64 nblocks, int l2nb, s64 blkno,
s64 * results);
static int dbExtend(struct inode *ip, s64 blkno, s64 nblocks, s64 addnblocks);
static int dbFindBits(u32 word, int l2nb);
static int dbFindCtl(struct bmap * bmp, int l2nb, int level, s64 * blkno);
static int dbFindLeaf(dmtree_t * tp, int l2nb, int *leafidx);
static void dbFreeBits(struct bmap * bmp, struct dmap * dp, s64 blkno,
int nblocks);
static int dbFreeDmap(struct bmap * bmp, struct dmap * dp, s64 blkno,
int nblocks);
static int dbMaxBud(u8 * cp);
s64 dbMapFileSizeToMapSize(struct inode *ipbmap);
static int blkstol2(s64 nb);
static int cntlz(u32 value);
static int cnttz(u32 word);
static int dbAllocDmapBU(struct bmap * bmp, struct dmap * dp, s64 blkno,
int nblocks);
static int dbInitDmap(struct dmap * dp, s64 blkno, int nblocks);
static int dbInitDmapTree(struct dmap * dp);
static int dbInitTree(struct dmaptree * dtp);
static int dbInitDmapCtl(struct dmapctl * dcp, int level, int i);
static int dbGetL2AGSize(s64 nblocks);
/*
* buddy table
*
* table used for determining buddy sizes within characters of
* dmap bitmap words. the characters themselves serve as indexes
* into the table, with the table elements yielding the maximum
* binary buddy of free bits within the character.
*/
static s8 budtab[256] = {
3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
2, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0,
2, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0,
2, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0,
2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
2, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0,
2, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0,
2, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0,
2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
2, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0,
2, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0,
2, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -1
};
/*
* NAME: dbMount()
*
* FUNCTION: initializate the block allocation map.
*
* memory is allocated for the in-core bmap descriptor and
* the in-core descriptor is initialized from disk.
*
* PARAMETERS:
* ipbmap - pointer to in-core inode for the block map.
*
* RETURN VALUES:
* 0 - success
* -ENOMEM - insufficient memory
* -EIO - i/o error
*/
int dbMount(struct inode *ipbmap)
{
struct bmap *bmp;
struct dbmap_disk *dbmp_le;
struct metapage *mp;
int i;
/*
* allocate/initialize the in-memory bmap descriptor
*/
/* allocate memory for the in-memory bmap descriptor */
bmp = kmalloc(sizeof(struct bmap), GFP_KERNEL);
if (bmp == NULL)
return -ENOMEM;
/* read the on-disk bmap descriptor. */
mp = read_metapage(ipbmap,
BMAPBLKNO << JFS_SBI(ipbmap->i_sb)->l2nbperpage,
PSIZE, 0);
if (mp == NULL) {
kfree(bmp);
return -EIO;
}
/* copy the on-disk bmap descriptor to its in-memory version. */
dbmp_le = (struct dbmap_disk *) mp->data;
bmp->db_mapsize = le64_to_cpu(dbmp_le->dn_mapsize);
bmp->db_nfree = le64_to_cpu(dbmp_le->dn_nfree);
bmp->db_l2nbperpage = le32_to_cpu(dbmp_le->dn_l2nbperpage);
bmp->db_numag = le32_to_cpu(dbmp_le->dn_numag);
bmp->db_maxlevel = le32_to_cpu(dbmp_le->dn_maxlevel);
bmp->db_maxag = le32_to_cpu(dbmp_le->dn_maxag);
bmp->db_agpref = le32_to_cpu(dbmp_le->dn_agpref);
bmp->db_aglevel = le32_to_cpu(dbmp_le->dn_aglevel);
bmp->db_agheigth = le32_to_cpu(dbmp_le->dn_agheigth);
bmp->db_agwidth = le32_to_cpu(dbmp_le->dn_agwidth);
bmp->db_agstart = le32_to_cpu(dbmp_le->dn_agstart);
bmp->db_agl2size = le32_to_cpu(dbmp_le->dn_agl2size);
for (i = 0; i < MAXAG; i++)
bmp->db_agfree[i] = le64_to_cpu(dbmp_le->dn_agfree[i]);
bmp->db_agsize = le64_to_cpu(dbmp_le->dn_agsize);
bmp->db_maxfreebud = dbmp_le->dn_maxfreebud;
/* release the buffer. */
release_metapage(mp);
/* bind the bmap inode and the bmap descriptor to each other. */
bmp->db_ipbmap = ipbmap;
JFS_SBI(ipbmap->i_sb)->bmap = bmp;
memset(bmp->db_active, 0, sizeof(bmp->db_active));
DBINITMAP(bmp->db_mapsize, ipbmap, &bmp->db_DBmap);
/*
* allocate/initialize the bmap lock
*/
BMAP_LOCK_INIT(bmp);
return (0);
}
/*
* NAME: dbUnmount()
*
* FUNCTION: terminate the block allocation map in preparation for
* file system unmount.
*
* the in-core bmap descriptor is written to disk and
* the memory for this descriptor is freed.
*
* PARAMETERS:
* ipbmap - pointer to in-core inode for the block map.
*
* RETURN VALUES:
* 0 - success
* -EIO - i/o error
*/
int dbUnmount(struct inode *ipbmap, int mounterror)
{
struct bmap *bmp = JFS_SBI(ipbmap->i_sb)->bmap;
int i;
if (!(mounterror || isReadOnly(ipbmap)))
dbSync(ipbmap);
/*
* Invalidate the page cache buffers
*/
truncate_inode_pages(ipbmap->i_mapping, 0);
/*
* Sanity Check
*/
for (i = 0; i < bmp->db_numag; i++)
if (atomic_read(&bmp->db_active[i]))
printk(KERN_ERR "dbUnmount: db_active[%d] = %d\n",
i, atomic_read(&bmp->db_active[i]));
/* free the memory for the in-memory bmap. */
kfree(bmp);
return (0);
}
/*
* dbSync()
*/
int dbSync(struct inode *ipbmap)
{
struct dbmap_disk *dbmp_le;
struct bmap *bmp = JFS_SBI(ipbmap->i_sb)->bmap;
struct metapage *mp;
int i;
/*
* write bmap global control page
*/
/* get the buffer for the on-disk bmap descriptor. */
mp = read_metapage(ipbmap,
BMAPBLKNO << JFS_SBI(ipbmap->i_sb)->l2nbperpage,
PSIZE, 0);
if (mp == NULL) {
jfs_err("dbSync: read_metapage failed!");
return -EIO;
}
/* copy the in-memory version of the bmap to the on-disk version */
dbmp_le = (struct dbmap_disk *) mp->data;
dbmp_le->dn_mapsize = cpu_to_le64(bmp->db_mapsize);
dbmp_le->dn_nfree = cpu_to_le64(bmp->db_nfree);
dbmp_le->dn_l2nbperpage = cpu_to_le32(bmp->db_l2nbperpage);
dbmp_le->dn_numag = cpu_to_le32(bmp->db_numag);
dbmp_le->dn_maxlevel = cpu_to_le32(bmp->db_maxlevel);
dbmp_le->dn_maxag = cpu_to_le32(bmp->db_maxag);
dbmp_le->dn_agpref = cpu_to_le32(bmp->db_agpref);
dbmp_le->dn_aglevel = cpu_to_le32(bmp->db_aglevel);
dbmp_le->dn_agheigth = cpu_to_le32(bmp->db_agheigth);
dbmp_le->dn_agwidth = cpu_to_le32(bmp->db_agwidth);
dbmp_le->dn_agstart = cpu_to_le32(bmp->db_agstart);
dbmp_le->dn_agl2size = cpu_to_le32(bmp->db_agl2size);
for (i = 0; i < MAXAG; i++)
dbmp_le->dn_agfree[i] = cpu_to_le64(bmp->db_agfree[i]);
dbmp_le->dn_agsize = cpu_to_le64(bmp->db_agsize);
dbmp_le->dn_maxfreebud = bmp->db_maxfreebud;
/* write the buffer */
write_metapage(mp);
/*
* write out dirty pages of bmap
*/
filemap_fdatawrite(ipbmap->i_mapping);
filemap_fdatawait(ipbmap->i_mapping);
ipbmap->i_state |= I_DIRTY;
diWriteSpecial(ipbmap, 0);
return (0);
}
/*
* NAME: dbFree()
*
* FUNCTION: free the specified block range from the working block
* allocation map.
*
* the blocks will be free from the working map one dmap
* at a time.
*
* PARAMETERS:
* ip - pointer to in-core inode;
* blkno - starting block number to be freed.
* nblocks - number of blocks to be freed.
*
* RETURN VALUES:
* 0 - success
* -EIO - i/o error
*/
int dbFree(struct inode *ip, s64 blkno, s64 nblocks)
{
struct metapage *mp;
struct dmap *dp;
int nb, rc;
s64 lblkno, rem;
struct inode *ipbmap = JFS_SBI(ip->i_sb)->ipbmap;
struct bmap *bmp = JFS_SBI(ip->i_sb)->bmap;
IREAD_LOCK(ipbmap);
/* block to be freed better be within the mapsize. */
if (unlikely((blkno == 0) || (blkno + nblocks > bmp->db_mapsize))) {
IREAD_UNLOCK(ipbmap);
printk(KERN_ERR "blkno = %Lx, nblocks = %Lx\n",
(unsigned long long) blkno,
(unsigned long long) nblocks);
jfs_error(ip->i_sb,
"dbFree: block to be freed is outside the map");
return -EIO;
}
/*
* free the blocks a dmap at a time.
*/
mp = NULL;
for (rem = nblocks; rem > 0; rem -= nb, blkno += nb) {
/* release previous dmap if any */
if (mp) {
write_metapage(mp);
}
/* get the buffer for the current dmap. */
lblkno = BLKTODMAP(blkno, bmp->db_l2nbperpage);
mp = read_metapage(ipbmap, lblkno, PSIZE, 0);
if (mp == NULL) {
IREAD_UNLOCK(ipbmap);
return -EIO;
}
dp = (struct dmap *) mp->data;
/* determine the number of blocks to be freed from
* this dmap.
*/
nb = min(rem, BPERDMAP - (blkno & (BPERDMAP - 1)));
DBALLOCCK(bmp->db_DBmap, bmp->db_mapsize, blkno, nb);
/* free the blocks. */
if ((rc = dbFreeDmap(bmp, dp, blkno, nb))) {
release_metapage(mp);
IREAD_UNLOCK(ipbmap);
return (rc);
}
DBFREE(bmp->db_DBmap, bmp->db_mapsize, blkno, nb);
}
/* write the last buffer. */
write_metapage(mp);
IREAD_UNLOCK(ipbmap);
return (0);
}
/*
* NAME: dbUpdatePMap()
*
* FUNCTION: update the allocation state (free or allocate) of the
* specified block range in the persistent block allocation map.
*
* the blocks will be updated in the persistent map one
* dmap at a time.
*
* PARAMETERS:
* ipbmap - pointer to in-core inode for the block map.
* free - TRUE if block range is to be freed from the persistent
* map; FALSE if it is to be allocated.
* blkno - starting block number of the range.
* nblocks - number of contiguous blocks in the range.
* tblk - transaction block;
*
* RETURN VALUES:
* 0 - success
* -EIO - i/o error
*/
int
dbUpdatePMap(struct inode *ipbmap,
int free, s64 blkno, s64 nblocks, struct tblock * tblk)
{
int nblks, dbitno, wbitno, rbits;
int word, nbits, nwords;
struct bmap *bmp = JFS_SBI(ipbmap->i_sb)->bmap;
s64 lblkno, rem, lastlblkno;
u32 mask;
struct dmap *dp;
struct metapage *mp;
struct jfs_log *log;
int lsn, difft, diffp;
/* the blocks better be within the mapsize. */
if (blkno + nblocks > bmp->db_mapsize) {
printk(KERN_ERR "blkno = %Lx, nblocks = %Lx\n",
(unsigned long long) blkno,
(unsigned long long) nblocks);
jfs_error(ipbmap->i_sb,
"dbUpdatePMap: blocks are outside the map");
return -EIO;
}
/* compute delta of transaction lsn from log syncpt */
lsn = tblk->lsn;
log = (struct jfs_log *) JFS_SBI(tblk->sb)->log;
logdiff(difft, lsn, log);
/*
* update the block state a dmap at a time.
*/
mp = NULL;
lastlblkno = 0;
for (rem = nblocks; rem > 0; rem -= nblks, blkno += nblks) {
/* get the buffer for the current dmap. */
lblkno = BLKTODMAP(blkno, bmp->db_l2nbperpage);
if (lblkno != lastlblkno) {
if (mp) {
write_metapage(mp);
}
mp = read_metapage(bmp->db_ipbmap, lblkno, PSIZE,
0);
if (mp == NULL)
return -EIO;
}
dp = (struct dmap *) mp->data;
/* determine the bit number and word within the dmap of
* the starting block. also determine how many blocks
* are to be updated within this dmap.
*/
dbitno = blkno & (BPERDMAP - 1);
word = dbitno >> L2DBWORD;
nblks = min(rem, (s64)BPERDMAP - dbitno);
/* update the bits of the dmap words. the first and last
* words may only have a subset of their bits updated. if
* this is the case, we'll work against that word (i.e.
* partial first and/or last) only in a single pass. a
* single pass will also be used to update all words that
* are to have all their bits updated.
*/
for (rbits = nblks; rbits > 0;
rbits -= nbits, dbitno += nbits) {
/* determine the bit number within the word and
* the number of bits within the word.
*/
wbitno = dbitno & (DBWORD - 1);
nbits = min(rbits, DBWORD - wbitno);
/* check if only part of the word is to be updated. */
if (nbits < DBWORD) {
/* update (free or allocate) the bits
* in this word.
*/
mask =
(ONES << (DBWORD - nbits) >> wbitno);
if (free)
dp->pmap[word] &=
cpu_to_le32(~mask);
else
dp->pmap[word] |=
cpu_to_le32(mask);
word += 1;
} else {
/* one or more words are to have all
* their bits updated. determine how
* many words and how many bits.
*/
nwords = rbits >> L2DBWORD;
nbits = nwords << L2DBWORD;
/* update (free or allocate) the bits
* in these words.
*/
if (free)
memset(&dp->pmap[word], 0,
nwords * 4);
else
memset(&dp->pmap[word], (int) ONES,
nwords * 4);
word += nwords;
}
}
/*
* update dmap lsn
*/
if (lblkno == lastlblkno)
continue;
lastlblkno = lblkno;
if (mp->lsn != 0) {
/* inherit older/smaller lsn */
logdiff(diffp, mp->lsn, log);
if (difft < diffp) {
mp->lsn = lsn;
/* move bp after tblock in logsync list */
LOGSYNC_LOCK(log);
list_move(&mp->synclist, &tblk->synclist);
LOGSYNC_UNLOCK(log);
}
/* inherit younger/larger clsn */
LOGSYNC_LOCK(log);
logdiff(difft, tblk->clsn, log);
logdiff(diffp, mp->clsn, log);
if (difft > diffp)
mp->clsn = tblk->clsn;
LOGSYNC_UNLOCK(log);
} else {
mp->log = log;
mp->lsn = lsn;
/* insert bp after tblock in logsync list */
LOGSYNC_LOCK(log);
log->count++;
list_add(&mp->synclist, &tblk->synclist);
mp->clsn = tblk->clsn;
LOGSYNC_UNLOCK(log);
}
}
/* write the last buffer. */
if (mp) {
write_metapage(mp);
}
return (0);
}
/*
* NAME: dbNextAG()
*
* FUNCTION: find the preferred allocation group for new allocations.
*
* Within the allocation groups, we maintain a preferred
* allocation group which consists of a group with at least
* average free space. It is the preferred group that we target
* new inode allocation towards. The tie-in between inode
* allocation and block allocation occurs as we allocate the
* first (data) block of an inode and specify the inode (block)
* as the allocation hint for this block.
*
* We try to avoid having more than one open file growing in
* an allocation group, as this will lead to fragmentation.
* This differs from the old OS/2 method of trying to keep
* empty ags around for large allocations.
*
* PARAMETERS:
* ipbmap - pointer to in-core inode for the block map.
*
* RETURN VALUES:
* the preferred allocation group number.
*/
int dbNextAG(struct inode *ipbmap)
{
s64 avgfree;
int agpref;
s64 hwm = 0;
int i;
int next_best = -1;
struct bmap *bmp = JFS_SBI(ipbmap->i_sb)->bmap;
BMAP_LOCK(bmp);
/* determine the average number of free blocks within the ags. */
avgfree = (u32)bmp->db_nfree / bmp->db_numag;
/*
* if the current preferred ag does not have an active allocator
* and has at least average freespace, return it
*/
agpref = bmp->db_agpref;
if ((atomic_read(&bmp->db_active[agpref]) == 0) &&
(bmp->db_agfree[agpref] >= avgfree))
goto unlock;
/* From the last preferred ag, find the next one with at least
* average free space.
*/
for (i = 0 ; i < bmp->db_numag; i++, agpref++) {
if (agpref == bmp->db_numag)
agpref = 0;
if (atomic_read(&bmp->db_active[agpref]))
/* open file is currently growing in this ag */
continue;
if (bmp->db_agfree[agpref] >= avgfree) {
/* Return this one */
bmp->db_agpref = agpref;
goto unlock;
} else if (bmp->db_agfree[agpref] > hwm) {
/* Less than avg. freespace, but best so far */
hwm = bmp->db_agfree[agpref];
next_best = agpref;
}
}
/*
* If no inactive ag was found with average freespace, use the
* next best
*/
if (next_best != -1)
bmp->db_agpref = next_best;
/* else leave db_agpref unchanged */
unlock:
BMAP_UNLOCK(bmp);
/* return the preferred group.
*/
return (bmp->db_agpref);
}
/*
* NAME: dbAlloc()
*
* FUNCTION: attempt to allocate a specified number of contiguous free
* blocks from the working allocation block map.
*
* the block allocation policy uses hints and a multi-step
* approach.
*
* for allocation requests smaller than the number of blocks
* per dmap, we first try to allocate the new blocks
* immediately following the hint. if these blocks are not
* available, we try to allocate blocks near the hint. if
* no blocks near the hint are available, we next try to
* allocate within the same dmap as contains the hint.
*
* if no blocks are available in the dmap or the allocation
* request is larger than the dmap size, we try to allocate
* within the same allocation group as contains the hint. if
* this does not succeed, we finally try to allocate anywhere
* within the aggregate.
*
* we also try to allocate anywhere within the aggregate for
* for allocation requests larger than the allocation group
* size or requests that specify no hint value.
*
* PARAMETERS:
* ip - pointer to in-core inode;
* hint - allocation hint.
* nblocks - number of contiguous blocks in the range.
* results - on successful return, set to the starting block number
* of the newly allocated contiguous range.
*
* RETURN VALUES:
* 0 - success
* -ENOSPC - insufficient disk resources
* -EIO - i/o error
*/
int dbAlloc(struct inode *ip, s64 hint, s64 nblocks, s64 * results)
{
int rc, agno;
struct inode *ipbmap = JFS_SBI(ip->i_sb)->ipbmap;
struct bmap *bmp;
struct metapage *mp;
s64 lblkno, blkno;
struct dmap *dp;
int l2nb;
s64 mapSize;
int writers;
/* assert that nblocks is valid */
assert(nblocks > 0);
#ifdef _STILL_TO_PORT
/* DASD limit check F226941 */
if (OVER_LIMIT(ip, nblocks))
return -ENOSPC;
#endif /* _STILL_TO_PORT */
/* get the log2 number of blocks to be allocated.
* if the number of blocks is not a log2 multiple,
* it will be rounded up to the next log2 multiple.
*/
l2nb = BLKSTOL2(nblocks);
bmp = JFS_SBI(ip->i_sb)->bmap;
//retry: /* serialize w.r.t.extendfs() */
mapSize = bmp->db_mapsize;
/* the hint should be within the map */
if (hint >= mapSize) {
jfs_error(ip->i_sb, "dbAlloc: the hint is outside the map");
return -EIO;
}
/* if the number of blocks to be allocated is greater than the
* allocation group size, try to allocate anywhere.
*/
if (l2nb > bmp->db_agl2size) {
IWRITE_LOCK(ipbmap);
rc = dbAllocAny(bmp, nblocks, l2nb, results);
if (rc == 0) {
DBALLOC(bmp->db_DBmap, bmp->db_mapsize, *results,
nblocks);
}
goto write_unlock;
}
/*
* If no hint, let dbNextAG recommend an allocation group
*/
if (hint == 0)
goto pref_ag;
/* we would like to allocate close to the hint. adjust the
* hint to the block following the hint since the allocators
* will start looking for free space starting at this point.
*/
blkno = hint + 1;
if (blkno >= bmp->db_mapsize)
goto pref_ag;
agno = blkno >> bmp->db_agl2size;
/* check if blkno crosses over into a new allocation group.
* if so, check if we should allow allocations within this
* allocation group.
*/
if ((blkno & (bmp->db_agsize - 1)) == 0)
/* check if the AG is currenly being written to.
* if so, call dbNextAG() to find a non-busy
* AG with sufficient free space.
*/
if (atomic_read(&bmp->db_active[agno]))
goto pref_ag;
/* check if the allocation request size can be satisfied from a
* single dmap. if so, try to allocate from the dmap containing
* the hint using a tiered strategy.
*/
if (nblocks <= BPERDMAP) {
IREAD_LOCK(ipbmap);
/* get the buffer for the dmap containing the hint.
*/
rc = -EIO;
lblkno = BLKTODMAP(blkno, bmp->db_l2nbperpage);
mp = read_metapage(ipbmap, lblkno, PSIZE, 0);
if (mp == NULL)
goto read_unlock;
dp = (struct dmap *) mp->data;
/* first, try to satisfy the allocation request with the
* blocks beginning at the hint.
*/
if ((rc = dbAllocNext(bmp, dp, blkno, (int) nblocks))
!= -ENOSPC) {
if (rc == 0) {
*results = blkno;
DBALLOC(bmp->db_DBmap, bmp->db_mapsize,
*results, nblocks);
mark_metapage_dirty(mp);
}
release_metapage(mp);
goto read_unlock;
}
writers = atomic_read(&bmp->db_active[agno]);
if ((writers > 1) ||
((writers == 1) && (JFS_IP(ip)->active_ag != agno))) {
/*
* Someone else is writing in this allocation
* group. To avoid fragmenting, try another ag
*/
release_metapage(mp);
IREAD_UNLOCK(ipbmap);
goto pref_ag;
}
/* next, try to satisfy the allocation request with blocks
* near the hint.
*/
if ((rc =
dbAllocNear(bmp, dp, blkno, (int) nblocks, l2nb, results))
!= -ENOSPC) {
if (rc == 0) {
DBALLOC(bmp->db_DBmap, bmp->db_mapsize,
*results, nblocks);
mark_metapage_dirty(mp);
}
release_metapage(mp);
goto read_unlock;
}
/* try to satisfy the allocation request with blocks within
* the same dmap as the hint.
*/
if ((rc = dbAllocDmapLev(bmp, dp, (int) nblocks, l2nb, results))
!= -ENOSPC) {
if (rc == 0) {
DBALLOC(bmp->db_DBmap, bmp->db_mapsize,
*results, nblocks);
mark_metapage_dirty(mp);
}
release_metapage(mp);
goto read_unlock;
}
release_metapage(mp);
IREAD_UNLOCK(ipbmap);
}
/* try to satisfy the allocation request with blocks within
* the same allocation group as the hint.
*/
IWRITE_LOCK(ipbmap);
if ((rc = dbAllocAG(bmp, agno, nblocks, l2nb, results))
!= -ENOSPC) {
if (rc == 0)
DBALLOC(bmp->db_DBmap, bmp->db_mapsize,
*results, nblocks);
goto write_unlock;
}
IWRITE_UNLOCK(ipbmap);
pref_ag:
/*
* Let dbNextAG recommend a preferred allocation group
*/
agno = dbNextAG(ipbmap);
IWRITE_LOCK(ipbmap);
/* Try to allocate within this allocation group. if that fails, try to
* allocate anywhere in the map.
*/
if ((rc = dbAllocAG(bmp, agno, nblocks, l2nb, results)) == -ENOSPC)
rc = dbAllocAny(bmp, nblocks, l2nb, results);
if (rc == 0) {
DBALLOC(bmp->db_DBmap, bmp->db_mapsize, *results, nblocks);
}
write_unlock:
IWRITE_UNLOCK(ipbmap);
return (rc);
read_unlock:
IREAD_UNLOCK(ipbmap);
return (rc);
}
#ifdef _NOTYET
/*
* NAME: dbAllocExact()
*
* FUNCTION: try to allocate the requested extent;
*
* PARAMETERS:
* ip - pointer to in-core inode;
* blkno - extent address;
* nblocks - extent length;
*
* RETURN VALUES:
* 0 - success
* -ENOSPC - insufficient disk resources
* -EIO - i/o error
*/
int dbAllocExact(struct inode *ip, s64 blkno, int nblocks)
{
int rc;
struct inode *ipbmap = JFS_SBI(ip->i_sb)->ipbmap;
struct bmap *bmp = JFS_SBI(ip->i_sb)->bmap;
struct dmap *dp;
s64 lblkno;
struct metapage *mp;
IREAD_LOCK(ipbmap);
/*
* validate extent request:
*
* note: defragfs policy:
* max 64 blocks will be moved.
* allocation request size must be satisfied from a single dmap.
*/
if (nblocks <= 0 || nblocks > BPERDMAP || blkno >= bmp->db_mapsize) {
IREAD_UNLOCK(ipbmap);
return -EINVAL;
}
if (nblocks > ((s64) 1 << bmp->db_maxfreebud)) {
/* the free space is no longer available */
IREAD_UNLOCK(ipbmap);
return -ENOSPC;
}
/* read in the dmap covering the extent */
lblkno = BLKTODMAP(blkno, bmp->db_l2nbperpage);
mp = read_metapage(ipbmap, lblkno, PSIZE, 0);
if (mp == NULL) {
IREAD_UNLOCK(ipbmap);
return -EIO;
}
dp = (struct dmap *) mp->data;
/* try to allocate the requested extent */
rc = dbAllocNext(bmp, dp, blkno, nblocks);
]
]
|
| 217/2 |
2026/06/30 15:21 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 47565
output: 63
thoughts: 51
Thoughts: Okay, let's break this down.
**Potential Security Vulnerability Analysis**
Right, so I've got a bug report in front of me. It looks like we're potentially missing some critical validation steps for the `dmaptree` structures. This is particularly concerning because the lack of validation dates all the way back to the initial JFS merge. That's a long time for a potential vulnerability to be lurking! We need to make sure this isn't exploitable.
My initial thought is to dive into the `dbJoin` function. Specifically, I'm going to pull up commit `1da177e4c3f4`. That's likely where the core logic related to `dmaptree` interaction, and potentially validation, was introduced or modified in the past. Understanding the `dbJoin` function in that context is going to be my key to understanding if this bug is real, and how bad it might be. Let's see what we're looking at there.
|
| 218/2 |
2026/06/30 15:21 |
tool |
git-log |
0m
Args: map[CodeRegexp:leaf = tp->dmt_stree \+ le32_to_cpu\(tp->dmt_leafidx\); PathPrefix:fs/jfs/jfs_dmap.c]
Results: map[Output:1da177e4c3f4 Linux-2.6.12-rc2
]
|
| 219/2 |
2026/06/30 15:21 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 47927
output: 39
thoughts: 454
Thoughts: **Analyzing the Dmap Tree Vulnerability**
Okay, so I've got this bug report, and it looks like a classic out-of-bounds read in the JFS dmap tree. The key line, `leaf = tp->dmt_stree + le32_to_cpu(tp->dmt_leafidx);`, has been around since commit `1da177e4c3f4`. The crux of the problem, as I see it, is that `leafidx` comes directly from disk without any validation. That's a textbook recipe for disaster.
The fix, as described, introduces `check_dmaptree` to validate `leafidx` and other fields. Seems sensible. Given the lack of validation, I'm fairly confident that `1da177e4c3f4` is the introducer.
Let's quickly double-check that this is indeed the single point of failure. The bug report mentions a similar integrity check (`check_dmapctl`) was added for `dmapctl` structures in `cce219b203c4`. This reinforces my initial assessment. It highlights the general lack of validation in these structures, which was only recently addressed. Essentially, the vulnerability was present from the initial introduction of this code.
To be absolutely certain, let me just verify the complete commit hash for `1da177e4c3f4`. Yep, it's `1da177e4c3f41524e886b7f1b8a0c1fc7321cac2`. I'll use either the abbreviated form or the full hash depending on the context.
Just to be absolutely sure, let me take a quick look at `dbJoin` in `fs/jfs/jfs_dmap.c` within that problematic commit, just to remove any doubt.
|
| 220/2 |
2026/06/30 15:22 |
tool |
git-show |
0m
Error:
Args: map[Commit:1da177e4c3f4:fs/jfs/jfs_dmap.c]
|
| 221/2 |
2026/06/30 15:22 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 48897
output: 42
thoughts: 40
Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience:
**Quick Analysis After Retrieval**
Right, I've got the initial retrieval, but it looks like the information is truncated. No worries. I need to dig deeper into the code around `dbJoin` within the JFS file system. A quick and dirty way to ascertain when a particular function was added and who was responsible is to use `git-blame`. The relevant file is likely `fs/jfs/jfs_dmap.c`. That should give me the commit history and contributors surrounding that specific function. I'll get the context I need. Let's fire that up.
|
| 222/2 |
2026/06/30 15:22 |
tool |
git-blame |
0m
Args: map[End:2867 File:fs/jfs/jfs_dmap.c Start:2860]
Results: map[Output:^1da177e4c3f4 2860) s8 *leaf;
^1da177e4c3f4 2861)
^1da177e4c3f4 2862) /* can the new leaf value require a join with other leaves ?
^1da177e4c3f4 2863) */
^1da177e4c3f4 2864) if (newval >= tp->dmt_budmin) {
^1da177e4c3f4 2865) /* pickup a pointer to the leaves of the tree.
^1da177e4c3f4 2866) */
^1da177e4c3f4 2867) leaf = tp->dmt_stree + le32_to_cpu(tp->dmt_leafidx);
]
|
| 223/2 |
2026/06/30 15:22 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 49374
output: 53
thoughts: 106
Thoughts: **Bug Found in Tree Validation**
Okay, so I've pinpointed it. The line `leaf = tp->dmt_stree + le32_to_cpu(tp->dmt_leafidx);` was introduced in commit `1da177e4c3f4`. Looks like a potential vulnerability there. My initial assessment is that the code is missing crucial validation steps. Specifically, the bug appears to stem from a lack of proper checks. I've tracked the faulty commit down to `1da177e4c3f41524e886b7f1b8a0c1fc7321cac2`. Now, I'm setting the stage for remediation.
|
| 224/2 |
2026/06/30 15:22 |
tool |
set-results |
0m
Args: map[FixesHash:1da177e4c3f41524e886b7f1b8a0c1fc7321cac2]
Results: map[FixesHash:1da177e4c3f41524e886b7f1b8a0c1fc7321cac2]
|
| 225/1 |
2026/06/30 15:22 |
action |
format-fixes |
0m
Results: map[Fixes:map[Hash:1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 Title:Linux-2.6.12-rc2]]
|
| 226/1 |
2026/06/30 15:22 |
action |
get-maintainers |
0m
Results: map[Recipients:[map[Email:contact@arnaud-lcm.com Name:Arnaud Lecomte To:false] map[Email:jfs-discussion@lists.sourceforge.net Name: To:true] map[Email:kees@kernel.org Name:Kees Cook To:false] map[Email:linux-kernel@vger.kernel.org Name: To:false] map[Email:shaggy@kernel.org Name:Dave Kleikamp To:true] map[Email:yun.zhou@windriver.com Name:Yun Zhou To:false] map[Email:zheng.yu@northwestern.edu Name:Zheng Yu To:false]]]
|
| 227/1 |
2026/06/30 15:22 |
action |
get-recent-commits |
0m
Results: map[RecentCommits:jfs: add dmapctl integrity check to prevent invalid operations
Convert 'alloc_obj' family to use the new default GFP_KERNEL argument
treewide: Replace kmalloc with kmalloc_obj for non-scalar types
jfs: fix metapage reference count leak in dbAllocCtl
jfs: upper bound check of tree index in dbAllocAG
jfs: validate AG parameters in dbMount() to prevent crashes
fs/jfs: consolidate sanity checking in dbMount
jfs: add sanity check for agwidth in dbMount
fs/jfs: Prevent integer overflow in AG size calculation
fs/jfs: cast inactags to s64 to prevent potential overflow
jfs: add a check to prevent array-index-out-of-bounds in dbAdjTree
jfs: fix shift-out-of-bounds in dbSplit
jfs: Fix sanity check in dbMount
jfs: check if leafidx greater than num leaves per dmap tree
jfs: fix out-of-bounds in dbNextAG() and diAlloc()
jfs: UBSAN: shift-out-of-bounds in dbFindBits
jfs: Fix shift-out-of-bounds in dbDiscardAG
Revert "jfs: fix shift-out-of-bounds in dbJoin"
jfs: fix shift-out-of-bounds in dbJoin
jfs: fix array-index-out-of-bounds in dbAdjTree
]
|
| 228/1 |
2026/06/30 15:22 |
agent |
description-generator |
0m
Model:
Instruction:
You are an experienced Linux kernel developer tasked with writing a commit description for
a kernel bug fixing commit. The description should start with a one-line summary,
and then include description of the bug being fixed, and how it's fixed by the provided patch.
The one-line summary should describe the change being made, rather than mention the tool that
detected the bug.
The description must not contain lines starting with '#' because they will dropped by git as comments.
The description must not contain lines starting with '--' or '---' (including inline code diffs)
because they may confuse git/patch utilities.
Your final reply should contain only the text of the commit description.
The one-line summary must be not longer than 72 characters.
IMPORTANT: Do not wrap lines manually (e.g., at 80 characters); we will reformat the text
automatically, so keep paragraphs as single lines without newlines.
Generally try to phrase the description without mentioning syzkaller
(avoid phrases like "the bug was triggered by syzkaller" or "the bug was triggered by fuzzer", etc).
How the bug was triggered is generally an irrelevant detail.
Any bug triggered by a fuzzer can also be triggered by a malicious user, or a buggy program.
If the crash is reported by a sanitizer (e.g., KASAN, KMSAN, lockdep), include the relevant
parts of the sanitizer output to illustrate the problem. Exclude less relevant sections,
as the stack trace can be very long. Describe the execution path that leads to the manifestation
of the kernel bug.
Prefer calling several tools at the same time to save round-trips.
Prompt:
The crash that corresponds to the bug is:
------------[ cut here ]------------
UBSAN: array-index-out-of-bounds in /app/workdir/cache/src/7be5f73ee7b4d1978813accf8bb54cd892ccd800/fs/jfs/jfs_dmap.c:2867:24
index 4294967295 is out of range for type 's8[1365]' (aka 'signed char[1365]')
CPU: 0 UID: 0 PID: 123 Comm: jfsCommit Not tainted syzkaller #1 PREEMPT(full)
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
Call Trace:
<TASK>
dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120
ubsan_epilogue+0xa/0x30 lib/ubsan.c:233
__ubsan_handle_out_of_bounds+0xe8/0xf0 lib/ubsan.c:455
dbJoin+0xcc4/0xd60 fs/jfs/jfs_dmap.c:2867
dbFreeBits+0x4a2/0xd70 fs/jfs/jfs_dmap.c:2427
dbFreeDmap fs/jfs/jfs_dmap.c:2176 [inline]
dbFree+0x324/0x650 fs/jfs/jfs_dmap.c:485
txFreeMap+0x9e6/0xde0 fs/jfs/jfs_txnmgr.c:2517
xtTruncate+0xd16/0x2eb0 fs/jfs/jfs_xtree.c:2481
jfs_free_zero_link+0x35b/0x4c0 fs/jfs/namei.c:760
jfs_evict_inode+0x356/0x430 fs/jfs/inode.c:159
evict+0x624/0xb50 fs/inode.c:825
txLazyCommit fs/jfs/jfs_txnmgr.c:2666 [inline]
jfs_lazycommit+0x44c/0xac0 fs/jfs/jfs_txnmgr.c:2735
kthread+0x388/0x470 kernel/kthread.c:436
ret_from_fork+0x514/0xb70 arch/x86/kernel/process.c:158
ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245
</TASK>
---[ end trace ]---
The explanation of the root cause of the bug is:
An array-index-out-of-bounds crash occurs in `dbJoin` when the JFS filesystem attempts to free blocks using a corrupted `dmap` structure read from disk.
### Root Cause
The JFS filesystem uses a binary buddy system to manage free space. The state of the buddy system is stored in `dmap` (for leaf levels) and `dmapctl` (for upper levels) structures on disk.
When a `dmap` is read from disk, its `dmaptree` structure is not fully validated. Specifically, the `leafidx` field (which indicates the index of the first leaf in the `stree` array) is read directly from disk and used to compute pointers to the leaf nodes.
If `leafidx` is corrupted (e.g., set to `0xffffffff` / `-1`), the computed pointer will point out of bounds of the `stree` array. When `dbFreeDmap` is called to free blocks, it calls `dbFreeBits`, which in turn calls `dbJoin` to update the buddy system. `dbJoin` uses the corrupted `leafidx` to access the leaf nodes:
```c
// fs/jfs/jfs_dmap.c:2867
leaf = tp->dmt_stree + le32_to_cpu(tp->dmt_leafidx);
```
This results in an out-of-bounds memory access when `leaf[buddy]` is subsequently accessed.
While a similar integrity check (`check_dmapctl`) was recently added for `dmapctl` structures in commit `cce219b203c4` ("jfs: add dmapctl integrity check to prevent invalid operations"), the `dmap` structures (`dmaptree`) were left unchecked.
### Fix
Introduce a `check_dmaptree` function, similar to the existing `check_dmapctl` function, to validate the integrity of the `dmaptree` structure when it is used.
The `check_dmaptree` function should verify that:
- `nleafs` is within bounds (`<= LPERDMAP`).
- `l2nleafs` is within bounds (`<= L2LPERDMAP`).
- `nleafs` matches `2^l2nleafs`.
- `leafidx` is exactly `LEAFIND`.
- `height` is within bounds (`<= 4`).
- `budmin` is valid (`>= BUDMIN`).
- The leaf nodes fit within the `stree` array (`leafidx + nleafs <= TREESIZE`).
- The values of the leaf nodes are valid (`>= NOFREE` and `<= 31`).
Call `check_dmaptree` in the functions that operate on the `dmap` structure, such as `dbFreeDmap`, `dbAllocNext`, `dbAllocNear`, `dbAllocDmapLev`, `dbAllocDmapBU`, and `dbAllocDmap`. If `check_dmaptree` fails, return `-EIO` to prevent further processing of the corrupted `dmap`. This will also replace the existing partial checks for `leafidx` in `dbAllocNext` and `dbAllocNear`.
```c
/*
* check_dmaptree - Validate integrity of a dmaptree structure
* @dtp: Pointer to the dmaptree structure to check
*
* Return: true if valid, false if corrupted
*/
static bool check_dmaptree(struct dmaptree *dtp)
{
u32 nleafs, l2nleafs, leafidx, height;
int i;
nleafs = le32_to_cpu(dtp->nleafs);
/* Check basic field ranges */
if (unlikely(nleafs > LPERDMAP)) {
jfs_err("dmaptree: invalid nleafs %u (max %u)",
nleafs, LPERDMAP);
return false;
}
l2nleafs = le32_to_cpu(dtp->l2nleafs);
if (unlikely(l2nleafs > L2LPERDMAP)) {
jfs_err("dmaptree: invalid l2nleafs %u (max %u)",
l2nleafs, L2LPERDMAP);
return false;
}
/* Verify nleafs matches l2nleafs (must be power of two) */
if (unlikely((1U << l2nleafs) != nleafs)) {
jfs_err("dmaptree: nleafs %u != 2^%u",
nleafs, l2nleafs);
return false;
}
leafidx = le32_to_cpu(dtp->leafidx);
/* Check leaf index matches expected position */
if (unlikely(leafidx != LEAFIND)) {
jfs_err("dmaptree: invalid leafidx %u (expected %u)",
leafidx, LEAFIND);
return false;
}
height = le32_to_cpu(dtp->height);
/* Check tree height is within valid range */
if (unlikely(height > 4)) {
jfs_err("dmaptree: invalid height %u (max 4)",
height);
return false;
}
/* Check budmin is valid */
if (unlikely(dtp->budmin < BUDMIN)) {
jfs_err("dmaptree: invalid budmin %d (min %d)",
dtp->budmin, BUDMIN);
return false;
}
/* Check leaf nodes fit within stree array */
if (unlikely(leafidx + nleafs > TREESIZE)) {
jfs_err("dmaptree: leaf range exceeds stree size (end %u > %u)",
leafidx + nleafs, TREESIZE);
return false;
}
/* Check leaf nodes have valid values */
for (i = leafidx; i < leafidx + nleafs; i++) {
s8 val = dtp->stree[i];
if (unlikely(val < NOFREE)) {
jfs_err("dmaptree: invalid leaf value %d at index %d",
val, i);
return false;
} else if (unlikely(val > 31)) {
jfs_err("dmaptree: leaf value %d too large at index %d", val, i);
return false;
}
}
return true;
}
```
Then, in functions like `dbFreeDmap`, `dbAllocNext`, `dbAllocNear`, `dbAllocDmapLev`, `dbAllocDmapBU`, and `dbAllocDmap`, add the check at the beginning:
```c
if (unlikely(!check_dmaptree(&dp->tree))) {
jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmap page\n");
return -EIO;
}
```
The diff of the bug fix is:
diff --git a/fs/jfs/jfs_dmap.c b/fs/jfs/jfs_dmap.c
index a841cf21d..4f2e0b78d 100644
--- a/fs/jfs/jfs_dmap.c
+++ b/fs/jfs/jfs_dmap.c
@@ -220,6 +220,85 @@ static bool check_dmapctl(struct dmapctl *dcp)
return true;
}
+/*
+ * check_dmaptree - Validate integrity of a dmaptree structure
+ * @dtp: Pointer to the dmaptree structure to check
+ *
+ * Return: true if valid, false if corrupted
+ */
+static bool check_dmaptree(struct dmaptree *dtp)
+{
+ u32 nleafs, l2nleafs, leafidx, height;
+ int i;
+
+ nleafs = le32_to_cpu(dtp->nleafs);
+ /* Check basic field ranges */
+ if (unlikely(nleafs > LPERDMAP)) {
+ jfs_err("dmaptree: invalid nleafs %u (max %u)", nleafs,
+ LPERDMAP);
+ return false;
+ }
+
+ l2nleafs = le32_to_cpu(dtp->l2nleafs);
+ if (unlikely(l2nleafs > L2LPERDMAP)) {
+ jfs_err("dmaptree: invalid l2nleafs %u (max %u)", l2nleafs,
+ L2LPERDMAP);
+ return false;
+ }
+
+ /* Verify nleafs matches l2nleafs (must be power of two) */
+ if (unlikely((1U << l2nleafs) != nleafs)) {
+ jfs_err("dmaptree: nleafs %u != 2^%u", nleafs, l2nleafs);
+ return false;
+ }
+
+ leafidx = le32_to_cpu(dtp->leafidx);
+ /* Check leaf index matches expected position */
+ if (unlikely(leafidx != LEAFIND)) {
+ jfs_err("dmaptree: invalid leafidx %u (expected %u)", leafidx,
+ LEAFIND);
+ return false;
+ }
+
+ height = le32_to_cpu(dtp->height);
+ /* Check tree height is within valid range */
+ if (unlikely(height > 4)) {
+ jfs_err("dmaptree: invalid height %u (max 4)", height);
+ return false;
+ }
+
+ /* Check budmin is valid */
+ if (unlikely(dtp->budmin < BUDMIN)) {
+ jfs_err("dmaptree: invalid budmin %d (min %d)", dtp->budmin,
+ BUDMIN);
+ return false;
+ }
+
+ /* Check leaf nodes fit within stree array */
+ if (unlikely(leafidx + nleafs > TREESIZE)) {
+ jfs_err("dmaptree: leaf range exceeds stree size (end %u > %u)",
+ leafidx + nleafs, TREESIZE);
+ return false;
+ }
+
+ /* Check leaf nodes have valid values */
+ for (i = leafidx; i < leafidx + nleafs; i++) {
+ s8 val = dtp->stree[i];
+
+ if (unlikely(val < NOFREE)) {
+ jfs_err("dmaptree: invalid leaf value %d at index %d",
+ val, i);
+ return false;
+ } else if (unlikely(val > 31)) {
+ jfs_err("dmaptree: leaf value %d too large at index %d",
+ val, i);
+ return false;
+ }
+ }
+
+ return true;
+}
+
/*
* NAME: dbMount()
*
@@ -1163,7 +1242,7 @@ static int dbAllocNext(struct bmap * bmp, struct dmap * dp, s64 blkno,
s8 *leaf;
u32 mask;
- if (dp->tree.leafidx != cpu_to_le32(LEAFIND)) {
+ if (unlikely(!check_dmaptree(&dp->tree))) {
jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmap page\n");
return -EIO;
}
@@ -1293,7 +1372,7 @@ dbAllocNear(struct bmap * bmp,
int word, lword, rc;
s8 *leaf;
- if (dp->tree.leafidx != cpu_to_le32(LEAFIND)) {
+ if (unlikely(!check_dmaptree(&dp->tree))) {
jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmap page\n");
return -EIO;
}
@@ -2046,6 +2125,11 @@ dbAllocDmapLev(struct bmap * bmp,
s64 blkno;
int leafidx, rc;
+ if (unlikely(!check_dmaptree(&dp->tree))) {
+ jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmap page\n");
+ return -EIO;
+ }
+
/* can't be more than a dmaps worth of blocks */
assert(l2nb <= L2BPERDMAP);
@@ -2112,6 +2196,11 @@ static int dbAllocDmap(struct bmap * bmp, struct dmap * dp, s64 blkno,
s8 oldroot;
int rc;
+ if (unlikely(!check_dmaptree(&dp->tree))) {
+ jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmap page\n");
+ return -EIO;
+ }
+
/* save the current value of the root (i.e. maximum free string)
* of the dmap tree.
*/
@@ -2167,6 +2256,11 @@ static int dbFreeDmap(struct bmap * bmp, struct dmap * dp, s64 blkno,
s8 oldroot;
int rc = 0, word;
+ if (unlikely(!check_dmaptree(&dp->tree))) {
+ jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmap page\n");
+ return -EIO;
+ }
+
/* save the current value of the root (i.e. maximum free string)
* of the dmap tree.
*/
@@ -3338,6 +3432,11 @@ static int dbAllocDmapBU(struct bmap * bmp, struct dmap * dp, s64 blkno,
s8 oldroot;
struct dmaptree *tp = (struct dmaptree *) & dp->tree;
+ if (unlikely(!check_dmaptree(&dp->tree))) {
+ jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmap page\n");
+ return -EIO;
+ }
+
/* save the current value of the root (i.e. maximum free string)
* of the dmap tree.
*/
Additional description of the patch:
I have implemented the fix by introducing a dedicated validation function `check_dmaptree()` and calling it at the entry points of all functions that operate on the `dmap` structure.
### What I did in the patch:
1. **Introduced `check_dmaptree()`**: I added a new function in `fs/jfs/jfs_dmap.c` right after `check_dmapctl()`. This function thoroughly validates the integrity of a `dmaptree` structure by checking its fields (`nleafs`, `l2nleafs`, `leafidx`, `height`, `budmin`) against their expected bounds and ensuring internal consistency (e.g., `nleafs == 2^l2nleafs`). It also iterates over the leaf nodes to ensure their values are within the valid range (`NOFREE` to 31) and that the leaf range does not exceed the `stree` array size.
2. **Added Validation Checks**: I added `if (unlikely(!check_dmaptree(&dp->tree)))` checks at the beginning of the following functions:
- `dbFreeDmap()`
- `dbAllocNext()`
- `dbAllocNear()`
- `dbAllocDmapLev()`
- `dbAllocDmapBU()`
- `dbAllocDmap()`
3. **Replaced Partial Checks**: In `dbAllocNext()` and `dbAllocNear()`, I replaced the existing partial checks (`if (dp->tree.leafidx != cpu_to_le32(LEAFIND))`) with the comprehensive `check_dmaptree()` call.
4. **Error Handling**: If the validation fails, the functions log a generic filesystem error using `jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmap page\n");` and immediately return `-EIO`.
### Why this is the best approach:
- **Fail-Fast Mechanism**: By placing the validation checks at the very beginning of the entry-point functions, we ensure that corrupted metadata is caught before any calculations or array indexing occur. This prevents out-of-bounds memory accesses (like the one in `dbJoin()`) and avoids propagating corrupted state into the buddy system.
- **Comprehensive Validation**: The previous partial checks only verified `leafidx`, leaving other fields like `nleafs` and the actual leaf values unchecked. `check_dmaptree()` provides a robust defense against various forms of disk corruption.
- **Consistency**: This approach mirrors the existing pattern established by `check_dmapctl()` for `dmapctl` structures, maintaining consistency in how JFS handles on-disk metadata validation.
- **Locking Safety**: The checks are performed synchronously within the existing locking context (e.g., `IREAD_LOCK` or `IWRITE_LOCK` on `ipbmap`), requiring no new locks and ensuring thread-safe validation.
Here are summaries of recent commits that touched the same files.
Format the summary line consistently with these, look how prefixes
are specified, letter capitalization, style, etc.
jfs: add dmapctl integrity check to prevent invalid operations
Convert 'alloc_obj' family to use the new default GFP_KERNEL argument
treewide: Replace kmalloc with kmalloc_obj for non-scalar types
jfs: fix metapage reference count leak in dbAllocCtl
jfs: upper bound check of tree index in dbAllocAG
jfs: validate AG parameters in dbMount() to prevent crashes
fs/jfs: consolidate sanity checking in dbMount
jfs: add sanity check for agwidth in dbMount
fs/jfs: Prevent integer overflow in AG size calculation
fs/jfs: cast inactags to s64 to prevent potential overflow
jfs: add a check to prevent array-index-out-of-bounds in dbAdjTree
jfs: fix shift-out-of-bounds in dbSplit
jfs: Fix sanity check in dbMount
jfs: check if leafidx greater than num leaves per dmap tree
jfs: fix out-of-bounds in dbNextAG() and diAlloc()
jfs: UBSAN: shift-out-of-bounds in dbFindBits
jfs: Fix shift-out-of-bounds in dbDiscardAG
Revert "jfs: fix shift-out-of-bounds in dbJoin"
jfs: fix shift-out-of-bounds in dbJoin
jfs: fix array-index-out-of-bounds in dbAdjTree
Reply: jfs: add dmaptree integrity check to prevent array-index-out-of-bounds
An array-index-out-of-bounds issue occurs in dbJoin() when the JFS
filesystem attempts to free blocks using a corrupted dmap structure read
from disk.
The JFS filesystem uses a binary buddy system to manage free space. The
state of the buddy system is stored in dmap (for leaf levels) and dmapctl
(for upper levels) structures on disk. When a dmap is read from disk, its
dmaptree structure is not fully validated. Specifically, the leafidx field
(which indicates the index of the first leaf in the stree array) is read
directly from disk and used to compute pointers to the leaf nodes.
If leafidx is corrupted (e.g., set to 0xffffffff), the computed pointer
will point out of bounds of the stree array. When dbFreeDmap() is called to
free blocks, it calls dbFreeBits(), which in turn calls dbJoin() to update
the buddy system. dbJoin() uses the corrupted leafidx to access the leaf
nodes, resulting in an out-of-bounds memory access.
While a similar integrity check (check_dmapctl()) was recently added for
dmapctl structures, the dmap structures (dmaptree) were left unchecked.
Introduce a check_dmaptree() function, similar to the existing
check_dmapctl() function, to validate the integrity of the dmaptree
structure when it is used. The function verifies that fields like nleafs,
l2nleafs, leafidx, height, and budmin are within their expected bounds and
internally consistent. It also ensures that the leaf nodes fit within the
stree array and have valid values.
Call check_dmaptree() at the entry points of functions that operate on the
dmap structure (dbFreeDmap(), dbAllocNext(), dbAllocNear(),
dbAllocDmapLev(), dbAllocDmapBU(), and dbAllocDmap()). If the validation
fails, log an error and return -EIO to prevent further processing of the
corrupted dmap. This also replaces the existing partial checks for leafidx
in dbAllocNext() and dbAllocNear().
UBSAN: array-index-out-of-bounds in fs/jfs/jfs_dmap.c:2867:24
index 4294967295 is out of range for type 's8[1365]' (aka 'signed
char[1365]')
CPU: 0 UID: 0 PID: 123 Comm: jfsCommit Not tainted
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS
1.16.3-debian-1.16.3-2 04/01/2014
Call Trace:
<TASK>
dump_stack_lvl+0xe8/0x150
ubsan_epilogue+0xa/0x30
__ubsan_handle_out_of_bounds+0xe8/0xf0
dbJoin+0xcc4/0xd60
dbFreeBits+0x4a2/0xd70
dbFreeDmap
dbFree+0x324/0x650
txFreeMap+0x9e6/0xde0
xtTruncate+0xd16/0x2eb0
jfs_free_zero_link+0x35b/0x4c0
jfs_evict_inode+0x356/0x430
evict+0x624/0xb50
txLazyCommit
jfs_lazycommit+0x44c/0xac0
kthread+0x388/0x470
ret_from_fork+0x514/0xb70
ret_from_fork_asm+0x1a/0x30
</TASK>
|
| 229/2 |
2026/06/30 15:22 |
llm |
description-generator |
0m
Model:
Tokens:
input: 9220
output: 878
thoughts: 4735
Thoughts: **jfs: add dmaptree integrity check to prevent array-index-out-of-bounds**
Okay, so I've been tracking down this nasty array-index-out-of-bounds bug in JFS. It's happening in `dbJoin`, specifically when trying to free blocks, and it's because the `dmaptree` structure isn't being fully validated when read from disk. The JFS filesystem uses a buddy system, right? And the free space information is stored in `dmap` (leaf levels) and `dmapctl` (upper levels). Now, the critical thing here is `leafidx` within the `dmaptree`. This field, read directly from disk, is used to calculate pointers into the `stree` array. If this `leafidx` is corrupted, let's say set to `-1` or something garbage, the resulting pointer will be completely out of bounds. The issue manifests when `dbFreeDmap` is called (during block freeing), which leads to `dbFreeBits` then to `dbJoin`, and finally a crash with the corrupted index. We added `check_dmapctl()` recently, but the `dmaptree` structures were being skipped.
The fix is to introduce `check_dmaptree`, mirroring the functionality of `check_dmapctl`, to validate the `dmaptree`. This function now carefully checks `nleafs`, `l2nleafs`, `leafidx`, `height`, `budmin`, and the actual leaf values themselves to ensure they're consistent and within expected boundaries. We'll be calling `check_dmaptree` at the beginning of all the functions that use the `dmap` structure like `dbFreeDmap`, `dbAllocNext`, `dbAllocNear`, `dbAllocDmapLev`, `dbAllocDmapBU`, and `dbAllocDmap`. If the validation fails, a helpful error is logged, and the function returns `-EIO` to prevent further operations on the corrupt data, which is better than a kernel crash. This also replaces the partial `leafidx` checks that were present in `dbAllocNext` and `dbAllocNear`.
UBSAN: array-index-out-of-bounds in fs/jfs/jfs_dmap.c:2867:24
index 4294967295 is out of range for type 's8[1365]' (aka 'signed char[1365]')
CPU: 0 UID: 0 PID: 123 Comm: jfsCommit Not tainted
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
Call Trace:
<TASK>
dump_stack_lvl+0xe8/0x150
ubsan_epilogue+0xa/0x30
__ubsan_handle_out_of_bounds+0xe8/0xf0
dbJoin+0xcc4/0xd60
dbFreeBits+0x4a2/0xd70
dbFreeDmap
dbFree+0x324/0x650
txFreeMap+0x9e6/0xde0
xtTruncate+0xd16/0x2eb0
jfs_free_zero_link+0x35b/0x4c0
jfs_evict_inode+0x356/0x430
evict+0x624/0xb50
txLazyCommit
jfs_lazycommit+0x44c/0xac0
kthread+0x388/0x470
ret_from_fork+0x514/0xb70
ret_from_fork_asm+0x1a/0x30
</TASK>
|