Archive-only list for syzbot
 help / color / mirror / Atom feed
From: Slawomir Stepien <sst@poczta.fm>
To: syzbot <syzbot@kernel.org>
Cc: syzkaller-upstream-moderation@googlegroups.com, syzbot@lists.linux.dev
Subject: Re: [PATCH RFC] jfs: add dmaptree integrity check to prevent array-index-out-of-bounds
Date: Mon, 6 Jul 2026 10:19:00 +0200	[thread overview]
Message-ID: <aktk9Fb5l5uBOu9q@nr200> (raw)
In-Reply-To: <2cfad14e-3856-4dd8-b464-6b0c1e15b9fb@mail.kernel.org>

On cze 30, 2026 15:23, syzbot wrote:
> 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>
> 
> Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
> Assisted-by: Gemini:gemini-3.1-pro-preview best-expensive syzbot
> Reported-by: syzbot+667a6d667592227b1452@syzkaller.appspotmail.com
> Closes: https://syzkaller.appspot.com/bug?extid=667a6d667592227b1452
> Link: https://syzkaller.appspot.com/ai_job?id=e23ddb38-d666-490d-86a7-4d67edb1eac1
> To: <jfs-discussion@lists.sourceforge.net>
> To: "Dave Kleikamp" <shaggy@kernel.org>
> Cc: "Arnaud Lecomte" <contact@arnaud-lcm.com>
> Cc: "Kees Cook" <kees@kernel.org>
> Cc: <linux-kernel@vger.kernel.org>
> Cc: "Yun Zhou" <yun.zhou@windriver.com>
> Cc: "Zheng Yu" <zheng.yu@northwestern.edu>
> 
> ---
> 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))) {

Pointer to dp->tree is saved to tp on line above. We should use it here.

> +		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.
>  	 */
> 
> 
> base-commit: dc59e4fea9d83f03bad6bddf3fa2e52491777482

-- 
Slawomir Stepien

      reply	other threads:[~2026-07-06  8:19 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-06-30 15:23 [PATCH RFC] jfs: add dmaptree integrity check to prevent array-index-out-of-bounds syzbot
2026-07-06  8:19 ` Slawomir Stepien [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=aktk9Fb5l5uBOu9q@nr200 \
    --to=sst@poczta.fm \
    --cc=syzbot@kernel.org \
    --cc=syzbot@lists.linux.dev \
    --cc=syzkaller-upstream-moderation@googlegroups.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox