 = (sbi->s_nzones - sbi->s_firstdatazone) <<
sbi->s_log_zone_size;
```

The `s_log_zone_size` field is read directly from the on-disk superblock
(`__u16` type, values 0-65535) and stored in `sbi->s_log_zone_size`
(unsigned long) **without any validation**. When a crafted/corrupt minix
image provides a large value (e.g., 768, as shown in the syzbot crash),
this causes a UBSAN shift-out-of-bounds because the shift exponent
exceeds 64 bits.

The same vulnerability also exists in `minix_count_free_blocks()` in
`bitmap.c`:

```102:103:fs/minix/bitmap.c
        return (count_free(sbi->s_zmap, sb->s_blocksize, bits)
                << sbi->s_log_zone_size);
```

Additionally, `sbi->s_nzones - sbi->s_firstdatazone` can **underflow**
(unsigned subtraction wrapping around) if `s_firstdatazone >= s_nzones`,
affecting both `minix_statfs()` and `minix_count_free_blocks()`.

**What the fix does (line by line)**:

1. **Validates `s_log_zone_size == 0`**: The Linux minix implementation
   doesn't support zone sizes different from block sizes. This is
   consistent with `mkfs.minix` in util-linux. This single check
   prevents the UBSAN shift-out-of-bounds.

2. **Validates `s_ninodes >= 1`**: Prevents issues with zero-inode
   filesystems.

3. **Validates `s_firstdatazone > 4`**: The minimum layout of a minix FS
   requires: boot block (0), superblock (1), at least 1 imap block (2),
   at least 1 zmap block (3), at least 1 inode table block (4), so first
   data zone must be >= 5.

4. **Validates `s_firstdatazone < s_nzones`**: Prevents unsigned
   underflow in `s_nzones - s_firstdatazone` used in multiple places.

5. **Moves `minix_blocks_needed()` checks earlier**: Moves existing
   imap/zmap block validation from after bitmap buffer allocation (in
   `minix_fill_super()`) to before allocation (in
   `minix_check_superblock()`). This is an improvement because it
   rejects bad images before doing unnecessary I/O (sb_bread calls for
   bitmap blocks).

6. **Removes the old `s_imap_blocks == 0 || s_zmap_blocks == 0` check**:
   This is subsumed by the new `minix_blocks_needed()` checks — if
   either block count is 0 while `s_ninodes >= 1`, the blocks_needed
   check will catch it.

### 3. CLASSIFICATION

This is clearly a **bug fix** — specifically a UBSAN: shift-out-of-
bounds fix triggered by crafted filesystem images. It also fixes
potential unsigned integer underflow. There are zero new features.

### 4. SCOPE AND RISK ASSESSMENT

- **Lines changed**: 29 additions, 21 deletions — **small and
  contained**
- **Files touched**: 1 file (`fs/minix/inode.c`)
- **Complexity**: Low — all changes are straightforward value
  comparisons
- **Risk of regression**: Very low — the changes only add validation at
  mount time. A valid minix filesystem would pass all checks. A
  filesystem rejected by these checks was always corrupt/invalid.
- **Subsystem**: minix filesystem — mature, rarely modified, not in a
  hot development path

### 5. USER IMPACT

- **Severity**: UBSAN/undefined behavior triggered from userspace (mount
  syscall with crafted image)
- **Attack surface**: Any user who can mount a minix filesystem (often
  root, but in some configurations could be unprivileged)
- **Syzbot confirmed affected stable trees**: linux-5.15 and linux-6.1
  both have the same bug (syzbot "Similar bugs" section shows them as
  unpatched: "0/3")
- **Reproducible**: Yes, syzbot provides a C reproducer
- **Duration**: The bug has been open since December 2021 — over 4 years

### 6. STABILITY INDICATORS

- Reviewed-by: Jan Kara (experienced FS developer)
- Signed-off-by: Christian Brauner (VFS maintainer)
- Tested by syzbot (patch testing returned OK)
- The commit has been in mainline since the 6.19 merge window

### 7. DEPENDENCY CHECK

- `minix_check_superblock()` was introduced in commit 270ef41094e9f (Aug
  2020, with `Cc: stable`), so it **exists in all current stable
  trees**.
- The function signature was changed to `struct super_block *sb` in
  32ac86efff91a (also Aug 2020, also in stable), so the same signature
  is used everywhere.
- `minix_blocks_needed()` has existed since 2011 (commit 016e8d44bc06d).
- The mount API conversion (7cd7bfe593287, Mar 2024) may mean older
  stable trees (5.15, 6.1, 6.6) need minor context adjustments, but the
  core changes to `minix_check_superblock()` apply cleanly regardless.
- **No other commits are needed** — this is fully self-contained.

### 8. CONCLUSION

This commit fixes a **long-standing syzbot-reported UBSAN bug** (1521+
days old) in the minix filesystem's superblock validation. The bug is
triggered by crafted filesystem images and causes undefined behavior
(shift-out-of-bounds) that is reproducible with a C reproducer. The fix
is small (29 additions, 21 deletions in 1 file), self-contained,
obviously correct, reviewed by an experienced FS developer, and signed
off by the VFS maintainer. The affected code exists in all stable trees,
and syzbot confirms the bug is present and unfixed in at least
linux-5.15 and linux-6.1. The risk of regression is minimal since the
changes only reject invalid filesystem images at mount time. This is a
textbook stable backport candidate.

**YES**

 fs/minix/inode.c | 50 ++++++++++++++++++++++++++++--------------------
 1 file changed, 29 insertions(+), 21 deletions(-)

diff --git a/fs/minix/inode.c b/fs/minix/inode.c
index 51ea9bdc813f7..c8c6b2135abe7 100644
--- a/fs/minix/inode.c
+++ b/fs/minix/inode.c
@@ -170,10 +170,38 @@ static int minix_reconfigure(struct fs_context *fc)
 static bool minix_check_superblock(struct super_block *sb)
 {
 	struct minix_sb_info *sbi = minix_sb(sb);
+	unsigned long block;
 
-	if (sbi->s_imap_blocks == 0 || sbi->s_zmap_blocks == 0)
+	if (sbi->s_log_zone_size != 0) {
+		printk("minix-fs error: zone size must equal block size. "
+		       "s_log_zone_size > 0 is not supported.\n");
+		return false;
+	}
+
+	if (sbi->s_ninodes < 1 || sbi->s_firstdatazone <= 4 ||
+	    sbi->s_firstdatazone >= sbi->s_nzones)
 		return false;
 
+	/* Apparently minix can create filesystems that allocate more blocks for
+	 * the bitmaps than needed.  We simply ignore that, but verify it didn't
+	 * create one with not enough blocks and bail out if so.
+	 */
+	block = minix_blocks_needed(sbi->s_ninodes, sb->s_blocksize);
+	if (sbi->s_imap_blocks < block) {
+		printk("MINIX-fs: file system does not have enough "
+		       "imap blocks allocated. Refusing to mount.\n");
+		return false;
+	}
+
+	block = minix_blocks_needed(
+			(sbi->s_nzones - sbi->s_firstdatazone + 1),
+			sb->s_blocksize);
+	if (sbi->s_zmap_blocks < block) {
+		printk("MINIX-fs: file system does not have enough "
+		       "zmap blocks allocated. Refusing to mount.\n");
+		return false;
+	}
+
 	/*
 	 * s_max_size must not exceed the block mapping limitation.  This check
 	 * is only needed for V1 filesystems, since V2/V3 support an extra level
@@ -293,26 +321,6 @@ static int minix_fill_super(struct super_block *s, struct fs_context *fc)
 	minix_set_bit(0,sbi->s_imap[0]->b_data);
 	minix_set_bit(0,sbi->s_zmap[0]->b_data);
 
-	/* Apparently minix can create filesystems that allocate more blocks for
-	 * the bitmaps than needed.  We simply ignore that, but verify it didn't
-	 * create one with not enough blocks and bail out if so.
-	 */
-	block = minix_blocks_needed(sbi->s_ninodes, s->s_blocksize);
-	if (sbi->s_imap_blocks < block) {
-		printk("MINIX-fs: file system does not have enough "
-				"imap blocks allocated.  Refusing to mount.\n");
-		goto out_no_bitmap;
-	}
-
-	block = minix_blocks_needed(
-			(sbi->s_nzones - sbi->s_firstdatazone + 1),
-			s->s_blocksize);
-	if (sbi->s_zmap_blocks < block) {
-		printk("MINIX-fs: file system does not have enough "
-				"zmap blocks allocated.  Refusing to mount.\n");
-		goto out_no_bitmap;
-	}
-
 	/* set up enough so that it can read an inode */
 	s->s_op = &minix_sops;
 	s->s_time_min = 0;
-- 
2.51.0[PATCH AUTOSEL 6.19-5.10] minix: Add required sanity checking to minix_check_superblock()Sasha Levin <sashal@kernel.org> undefinedpatches@lists.linux.dev, stable@vger.kernel.org undefined undefined undefined undefined undefined undefined undefined undefined undefined