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