All of lore.kernel.org
 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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.