[PATCH AUTOSEL 6.18-6.6] ntfs: set dummy blocksize to read boot_block when mounting

Sasha Levin
2025-12-06T14:03:39Z

From: Pedro Demarchi Gomes <pedrodemargomes@gmail.com>

[ Upstream commit d1693a7d5a38acf6424235a6070bcf5b186a360d ]

When mounting, sb->s_blocksize is used to read the boot_block without
being defined or validated. Set a dummy blocksize before attempting to
read the boot_block.

The issue can be triggered with the following syz reproducer:

mkdirat(0xffffffffffffff9c, &(0x7f0000000080)='./file1\x00', 0x0)
r4 = openat$nullb(0xffffffffffffff9c, &(0x7f0000000040), 0x121403, 0x0)
ioctl$FS_IOC_SETFLAGS(r4, 0x40081271, &(0x7f0000000980)=0x4000)
mount(&(0x7f0000000140)=@nullb, &(0x7f0000000040)='./cgroup\x00',
&(0x7f0000000000)='ntfs3\x00', 0x2208004, 0x0)
syz_clone(0x88200200, 0x0, 0x0, 0x0, 0x0, 0x0)

Here, the ioctl sets the bdev block size to 16384. During mount,
get_tree_bdev_flags() calls sb_set_blocksize(sb, block_size(bdev)),
but since block_size(bdev) > PAGE_SIZE, sb_set_blocksize() leaves
sb->s_blocksize at zero.

Later, ntfs_init_from_boot() attempts to read the boot_block while
sb->s_blocksize is still zero, which triggers the bug.

Reported-by: syzbot+f4f84b57a01d6b8364ad@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=f4f84b57a01d6b8364ad
Signed-off-by: Pedro Demarchi Gomes <pedrodemargomes@gmail.com>
[almaz.alexandrovich@paragon-software.com: changed comment style, added
return value handling]
Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---

LLM Generated explanations, may be completely bogus:

Now I have a complete picture of the issue. Let me summarize my
analysis:

## Summary of Analysis

### The Bug

The commit fixes a bug in ntfs3's `ntfs_init_from_boot()` function where
`ntfs_bread()` is called to read the boot block **before**
`sb->s_blocksize` is properly set.

### Root Cause Analysis

1. **Mount Flow**: When mounting an ntfs3 filesystem:
- `get_tree_bdev()` calls `setup_bdev_super()` which calls
`sb_set_blocksize(sb, block_size(bdev))`
- If `block_size(bdev) > PAGE_SIZE` (which can happen via ioctl
`FS_IOC_SETFLAGS` setting bdev block size to 16384 as shown in the
reproducer), and the filesystem doesn't have `FS_LBS` flag,
`sb_set_blocksize()` returns 0 and leaves `sb->s_blocksize = 0`

2. **The Crash Path**: In `ntfs_init_from_boot()`:
```c
sbi->volume.blocks = dev_size >> PAGE_SHIFT;
// sb->s_blocksize is 0 here!
bh = ntfs_bread(sb, boot_block); // Uses sb->s_blocksize = 0
```

3. **Cascade to Infinite Loop**:
- `ntfs_bread()` → `sb_bread_unmovable()` → `__bread_gfp()` with
size=0
- `bdev_getblk()` → `__getblk_slow()` → `grow_buffers()` →
`grow_dev_folio()` → `folio_alloc_buffers()`
- In `folio_alloc_buffers()`: `while ((offset -= size) >= 0)` with
size=0 causes infinite loop

### The Fix

The fix adds a call to `sb_min_blocksize(sb, PAGE_SIZE)` before
attempting to read the boot block:

```c
/* Set dummy blocksize to read boot_block. */
if (!sb_min_blocksize(sb, PAGE_SIZE)) {
return -EINVAL;
}
```

This ensures:
1. `sb->s_blocksize` is set to at least the device's logical block size,
capped at PAGE_SIZE
2. If this fails, mount fails gracefully with `-EINVAL` instead of
hanging

### Backport Assessment

**STRONG YES signals:**
1. ✅ **Fixes a real crash/hang** - System hangs due to infinite loop in
`folio_alloc_buffers()`
2. ✅ **Syzbot reported** - Has syzkaller reproducer
(`f4f84b57a01d6b8364ad`)
3. ✅ **Small, surgical fix** - Only 4 lines added
4. ✅ **Clear, obvious fix** - Sets blocksize before using it for reads
5. ✅ **Uses well-established API** - `sb_min_blocksize()` is a standard
helper used by many filesystems
6. ✅ **Affects production users** - ntfs3 is widely used (included since
5.15)
7. ✅ **Denial of Service potential** - A local user can trigger the hang

**Risk Assessment:**
- **Very Low Risk**: The fix adds a safety check before an I/O operation
- **No behavioral change** for normal cases - the blocksize would have
been set to PAGE_SIZE anyway after successful boot read
- **Graceful failure** if `sb_min_blocksize()` fails (which would be
very rare in practice)

**Stable Trees Affected:**
- All stable trees with ntfs3: 5.15.y, 6.1.y, 6.6.y, 6.10.y, 6.11.y,
etc.
- The bug has existed since ntfs3 was introduced in v5.15
- The recent `FS_LBS` change (March 2025) made it easier to trigger but
the underlying issue predates it

**Dependencies:**
- The fix is standalone and uses existing kernel APIs
- No other patches required for backport
- `sb_min_blocksize()` has been available since at least 2.6.x

**YES**

fs/ntfs3/super.c | 5 +++++
1 file changed, 5 insertions(+)

diff --git a/fs/ntfs3/super.c b/fs/ntfs3/super.c
index ddff94c091b8c..e6c0908e27c29 100644
--- a/fs/ntfs3/super.c
+++ b/fs/ntfs3/super.c
@@ -933,6 +933,11 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size,

sbi->volume.blocks = dev_size >> PAGE_SHIFT;

+ /* Set dummy blocksize to read boot_block. */
+ if (!sb_min_blocksize(sb, PAGE_SIZE)) {
+ return -EINVAL;
+ }
+
read_boot:
bh = ntfs_bread(sb, boot_block);
if (!bh)
--
2.51.0