[lvc-project] [PATCH 6.1] gfs2: Improve resource group validation and error handling
Makar Semyonov
m.semenov at tssltd.ru
Tue Aug 25 16:06:13 MSK 2026
Validate the resource group boundaries, check against the device size, simplify
the initialization logic, and check for resource group overlaps.
Reported-by: syzbot+9d20c3ad7d29227de28d at syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=9d20c3ad7d29227de28d
Tested-by: syzbot+9d20c3ad7d29227de28d at syzkaller.appspotmail.com
Signed-off-by: Andreas Gruenbacher <agruenba at redhat.com>
Fixes: feaa7bba026c ("[GFS2] Fix unlinked file handling")
Signed-off-by: Makar Semyonov <m.semenov at tssltd.ru>
---
fs/gfs2/rgrp.c | 128 ++++++++++++++++++++++++++-----------------------
1 file changed, 69 insertions(+), 59 deletions(-)
diff --git a/fs/gfs2/rgrp.c b/fs/gfs2/rgrp.c
index f602fb844951..66f34e08a346 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,77 @@ 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 = kcalloc(rgd->rd_length, sizeof(struct gfs2_bitmap), GFP_NOFS);
- rgd->rd_bits = kcalloc(length, sizeof(struct gfs2_bitmap), 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\n"
- "ri_length = %u\n"
- "ri_data0 = %llu\n"
- "ri_data = %u\n"
- "ri_bitbytes = %u\n"
- "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 +858,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 +877,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 +936,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 +952,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);
@@ -2306,7 +2316,7 @@ void gfs2_rgrp_dump(struct seq_file *seq, struct gfs2_rgrpd *rgd,
(unsigned long long)rgd->rd_addr, rgd->rd_flags,
rgd->rd_free, rgd->rd_free_clone, rgd->rd_dinodes,
rgd->rd_requested, rgd->rd_reserved, rgd->rd_extfail_pt);
- if (rgd->rd_sbd->sd_args.ar_rgrplvb) {
+ if (rgd->rd_sbd->sd_args.ar_rgrplvb && rgd->rd_rgl) {
struct gfs2_rgrp_lvb *rgl = rgd->rd_rgl;
gfs2_print_dbg(seq, "%s L: f:%02x b:%u i:%u\n", fs_id_buf,
--
2.43.0
More information about the lvc-project
mailing list