Re: [syzbot] [mm?] [gfs2?] WARNING in read_rindex_entry

Andreas Gruenbacher <[email protected]>
Newsgroups dev.linux.lists.gfs2
Message-ID <[email protected]>
On Thu, Aug 20, 2026 at 2:24 AM Andrew Morton <[email protected]> wrote:
> On Wed, 19 Aug 2026 15:49:42 -0700 syzbot <[email protected]> wrote:
> > Hello,
> >
> > syzbot found the following issue on:
> >
> > HEAD commit:    15ef2f78c49d Merge tag 'input-for-v7.2-rc7' of git://git.k..
> > git tree:       upstream
> > console+strace: https://syzkaller.appspot.com/x/log.txt?x=10871279580000
> > kernel config:  https://syzkaller.appspot.com/x/.config?x=a0fdc3b566746377
> > dashboard link: https://syzkaller.appspot.com/bug?extid=9d20c3ad7d29227de28d
> > compiler:       gcc (Debian 14.2.0-19) 14.2.0, GNU ld (GNU Binutils for Debian) 2.44
> > syz repro:      https://syzkaller.appspot.com/x/repro.syz?x=14871279580000
> > C reproducer:   https://syzkaller.appspot.com/x/repro.c?x=100d2a25580000
> >
> > Downloadable assets:
> > disk image: https://storage.googleapis.com/syzbot-assets/c2a8c7d3a889/disk-15ef2f78.raw.xz
> > vmlinux: https://storage.googleapis.com/syzbot-assets/2da78afed345/vmlinux-15ef2f78.xz
> > kernel image: https://storage.googleapis.com/syzbot-assets/c4ffd97e4708/bzImage-15ef2f78.xz
> > mounted in repro: https://storage.googleapis.com/syzbot-assets/eaa4120812b6/mount_0.gz
> >   fsck result: failed (log: https://syzkaller.appspot.com/x/fsck.log?x=17f52a25580000)
> >
> > IMPORTANT: if you fix the issue, please add the following tag to the commit:
> > Reported-by: [email protected]
>
> Thanks.  Mounting an intentionally corrupted fs image, I assume.
>
> It appears that GFS2 forgot to validate the `ri_length' which it read
> from disk.  That gets used in compute_bitstructs()->kzalloc_objs() and
> the memory allocator didn't like the excessively-sized allocation
> attempt.

Thanks for looking into this.  This commit (attached below) from the
for-later branch should prevent that from happening:

https://gitlab.com/redhat/centos-stream/src/kernel/centos-stream-9/-/merge_requests/8684#note_3716004816

Thanks,
Andreas

--

gfs2: Improve resource group validation and error handling

Validate the resource group boundaries, check against the device size, simplify
the initialization logic, and check for resource group overlaps.

Reported-by: [email protected]
Closes: https://syzkaller.appspot.com/bug?extid=9d20c3ad7d29227de28d
Tested-by: [email protected]
Signed-off-by: Andreas Gruenbacher <[email protected]>
---
 fs/gfs2/rgrp.c | 123 ++++++++++++++++++++++++++-----------------------
 1 file changed, 65 insertions(+), 58 deletions(-)

diff --git a/fs/gfs2/rgrp.c b/fs/gfs2/rgrp.c
index f6048a73e5c3..3ac751eea055 100644
--- a/fs/gfs2/rgrp.c
+++ b/fs/gfs2/rgrp.c
@@ -745,6 +745,7 @@ void gfs2_clear_rgrpd(struct gfs2_sbd *sdp)
 
 /**
  * compute_bitstructs - Compute the bitmap sizes
+ * @sb: The superblock
  * @rgd: The resource group descriptor
  *
  * Calculates bitmap descriptors, one for each block that contains bitmap data
@@ -752,84 +753,74 @@ void gfs2_clear_rgrpd(struct gfs2_sbd *sdp)
  * Returns: errno
  */
 
-static int compute_bitstructs(struct gfs2_rgrpd *rgd)
+static int compute_bitstructs(struct super_block *sb, struct gfs2_rgrpd *rgd)
 {
 	struct gfs2_sbd *sdp = rgd->rd_sbd;
 	struct gfs2_bitmap *bi;
-	u32 length = rgd->rd_length; /* # blocks in hdr & bitmap */
+	u32 expected_length;
 	u32 bytes_left, bytes;
+	u64 data_end;
 	int x;
 
-	if (!length)
-		return -EINVAL;
+	/*
+	 * The first resource group block has a gfs2_rgrp header; the remaining
+	 * blocks have a gfs2_meta_header header.  The rest of each block is
+	 * filled with bitmap data.
+	 */
+
+	if (rgd->rd_addr <= (GFS2_SB_ADDR >> sdp->sd_fsb2bb_shift)) {
+		gfs2_consist_rgrpd(rgd);
+		return -EIO;
+	}
+	if (check_add_overflow(rgd->rd_data0, rgd->rd_data, &data_end) ||
+	    rgd->rd_data == 0 || data_end > sb_bdev_nr_blocks(sb)) {
+		gfs2_consist_rgrpd(rgd);
+		return -EIO;
+	}
+	if (rgd->rd_bitbytes != DIV_ROUND_UP(rgd->rd_data, GFS2_NBBY)) {
+		gfs2_consist_rgrpd(rgd);
+		return -EIO;
+	}
+	expected_length = DIV_ROUND_UP(rgd->rd_bitbytes +
+		sizeof(struct gfs2_rgrp) - sizeof(struct gfs2_meta_header),
+		sdp->sd_sb.sb_bsize - sizeof(struct gfs2_meta_header));
+	if (rgd->rd_length != expected_length) {
+		gfs2_consist_rgrpd(rgd);
+		return -EIO;
+	}
+	if (rgd->rd_data0 < rgd->rd_addr + rgd->rd_length) {
+		gfs2_consist_rgrpd(rgd);
+		return -EIO;
+	}
 
-	rgd->rd_bits = kzalloc_objs(struct gfs2_bitmap, length, GFP_NOFS);
+	rgd->rd_bits = kzalloc_objs(struct gfs2_bitmap, rgd->rd_length, GFP_NOFS);
 	if (!rgd->rd_bits)
 		return -ENOMEM;
 
 	bytes_left = rgd->rd_bitbytes;
 
-	for (x = 0; x < length; x++) {
+	for (x = 0; x < rgd->rd_length; x++) {
 		bi = rgd->rd_bits + x;
 
 		bi->bi_flags = 0;
-		/* small rgrp; bitmap stored completely in header block */
-		if (length == 1) {
-			bytes = bytes_left;
-			bi->bi_offset = sizeof(struct gfs2_rgrp);
+		if (x == 0) {
+			/* header block */
 			bi->bi_start = 0;
-			bi->bi_bytes = bytes;
-			bi->bi_blocks = bytes * GFS2_NBBY;
-		/* header block */
-		} else if (x == 0) {
-			bytes = sdp->sd_sb.sb_bsize - sizeof(struct gfs2_rgrp);
 			bi->bi_offset = sizeof(struct gfs2_rgrp);
-			bi->bi_start = 0;
-			bi->bi_bytes = bytes;
-			bi->bi_blocks = bytes * GFS2_NBBY;
-		/* last block */
-		} else if (x + 1 == length) {
-			bytes = bytes_left;
-			bi->bi_offset = sizeof(struct gfs2_meta_header);
-			bi->bi_start = rgd->rd_bitbytes - bytes_left;
-			bi->bi_bytes = bytes;
-			bi->bi_blocks = bytes * GFS2_NBBY;
-		/* other blocks */
 		} else {
-			bytes = sdp->sd_sb.sb_bsize -
-				sizeof(struct gfs2_meta_header);
+			/* bitmap-only block */
+			struct gfs2_bitmap *prev = bi - 1;
+
+			bi->bi_start = prev->bi_start + prev->bi_bytes;
 			bi->bi_offset = sizeof(struct gfs2_meta_header);
-			bi->bi_start = rgd->rd_bitbytes - bytes_left;
-			bi->bi_bytes = bytes;
-			bi->bi_blocks = bytes * GFS2_NBBY;
 		}
-
+		bytes = sdp->sd_sb.sb_bsize - bi->bi_offset;
+		if (bytes > bytes_left)
+			bytes = bytes_left;
+		bi->bi_bytes = bytes;
+		bi->bi_blocks = bytes * GFS2_NBBY;
 		bytes_left -= bytes;
 	}
-
-	if (bytes_left) {
-		gfs2_consist_rgrpd(rgd);
-		return -EIO;
-	}
-	bi = rgd->rd_bits + (length - 1);
-	if ((bi->bi_start + bi->bi_bytes) * GFS2_NBBY != rgd->rd_data) {
-		gfs2_lm(sdp,
-			"ri_addr=%llu "
-			"ri_length=%u "
-			"ri_data0=%llu "
-			"ri_data=%u "
-			"ri_bitbytes=%u "
-			"start=%u len=%u offset=%u\n",
-			(unsigned long long)rgd->rd_addr,
-			rgd->rd_length,
-			(unsigned long long)rgd->rd_data0,
-			rgd->rd_data,
-			rgd->rd_bitbytes,
-			bi->bi_start, bi->bi_bytes, bi->bi_offset);
-		gfs2_consist_rgrpd(rgd);
-		return -EIO;
-	}
-
 	return 0;
 }
 
@@ -864,6 +855,7 @@ static int rgd_insert(struct gfs2_rgrpd *rgd)
 {
 	struct gfs2_sbd *sdp = rgd->rd_sbd;
 	struct rb_node **newn = &sdp->sd_rindex_tree.rb_node, *parent = NULL;
+	struct rb_node *prevn;
 
 	/* Figure out where to put new node */
 	while (*newn) {
@@ -882,6 +874,19 @@ static int rgd_insert(struct gfs2_rgrpd *rgd)
 	rb_link_node(&rgd->rd_node, parent, newn);
 	rb_insert_color(&rgd->rd_node, &sdp->sd_rindex_tree);
 	sdp->sd_rgrps++;
+
+	prevn = rb_prev(&rgd->rd_node);
+	if (prevn) {
+		struct gfs2_rgrpd *prev =
+			rb_entry(prevn, struct gfs2_rgrpd, rd_node);
+
+		if (prev->rd_data0 + prev->rd_data > rgd->rd_addr) {
+			fs_err(sdp, "overlapping resource groups.\n");
+			rb_erase(&rgd->rd_node, &sdp->sd_rindex_tree);
+			return -ENOENT;
+		}
+	}
+
 	return 0;
 }
 
@@ -928,7 +933,7 @@ static int read_rindex_entry(struct gfs2_inode *ip)
 	if (error)
 		goto fail;
 
-	error = compute_bitstructs(rgd);
+	error = compute_bitstructs(sdp->sd_vfs, rgd);
 	if (error)
 		goto fail_glock;
 
@@ -944,7 +949,9 @@ static int read_rindex_entry(struct gfs2_inode *ip)
 		return 0;
 	}
 
-	error = 0; /* someone else read in the rgrp; free it and ignore it */
+	/* If someone else read in the rgrp, free it and ignore it. */
+	if (error == -EEXIST)
+		error = 0;
 fail_glock:
 	gfs2_glock_put(rgd->rd_gl);
 
-- 
2.55.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.