[PATCH AUTOSEL 6.18-5.10] ext4: reduce max cluster size to match documented 256MB limit

From: Sasha Levin

Date: Mon Oct 19 2026 - 12:27:33 EST


From: Baokun Li <libaokun@xxxxxxxxxxxxxxxxx>

[ Upstream commit afa3caf2fbc417f755f36e271c4b3c2d45ccda9d ]

The mke2fs man page documents:

Valid cluster-size values are from 2048 to 256M bytes per cluster.

but EXT4_MAX_CLUSTER_LOG_SIZE was set to 30 (1GB), allowing crafted
filesystem images to specify cluster sizes up to 1GB.

On 32-bit systems with bigalloc enabled, the consistency check in
ext4_handle_clustersize():

s_blocks_per_group == s_clusters_per_group * (clustersize / blocksize)

can overflow when the cluster ratio is large enough. Since
s_blocks_per_group is not range-checked in the bigalloc path, the
wrapped product can pass the consistency check, leading to inconsistent
group geometry and potential out-of-bounds block allocation.

Reduce EXT4_MAX_CLUSTER_LOG_SIZE to 28 to match the documented 256MB
limit. With this cap, the maximum product is:

(blocksize * 8) * (256M / blocksize) = 2^31

which fits safely in a 32-bit unsigned long for all block sizes.

Reported-by: Sashiko <sashiko-bot@xxxxxxxxxx>
Closes: https://sashiko.dev/#/patchset/20260608061112.392391-1-libaokun%40linux.alibaba.com
Signed-off-by: Baokun Li <libaokun@xxxxxxxxxxxxxxxxx>
Reviewed-by: Andreas Dilger <adilger@xxxxxxxxx>
Link: https://patch.msgid.link/20260608111150.827117-3-libaokun@xxxxxxxxxxxxxxxxx
Signed-off-by: Theodore Ts'o <tytso@xxxxxxx>
Signed-off-by: Sasha Levin <sashal@xxxxxxxxxx>
---

LLM Generated explanations, may be completely bogus:

Local tree is **v7.3-rc4** (kernel 7.3.0-rc4, detached HEAD). The change
is a one-line cap on ext4 bigalloc cluster size to stop a 32-bit
overflow on crafted images.

===============================================================
PHASE 1: COMMIT MESSAGE FORENSICS
===============================================================

**Step 1.1: PARSE THE SUBJECT LINE**

Record: **[ext4]** **[reduce]** reduce `EXT4_MAX_CLUSTER_LOG_SIZE` from
30 (1GB) to 28 (256MB) to match the documented mke2fs limit and stop a
32-bit overflow.

**Step 1.2: PARSE ALL COMMIT MESSAGE TAGS**

Record:
- **Reported-by:** Sashiko `<sashiko-bot@xxxxxxxxxx>` (automated kernel
review, not syzbot)
- **Closes:** https://sashiko.dev/#/patchset/20260608061112.392391-1-
libaokun%40linux.alibaba.com (v1 series)
- **Reviewed-by:** Andreas Dilger `<adilger@xxxxxxxxx>` (ext4 co-
maintainer)
- **Link:** https://patch.msgid.link/20260608111150.827117-3-
libaokun@xxxxxxxxxxxxxxxxx
- **Signed-off-by:** Baokun Li `<libaokun@xxxxxxxxxxxxxxxxx>` (author)
- **Signed-off-by:** Theodore Ts'o `<tytso@xxxxxxx>` (ext4 maintainer;
applied the series)
- **Fixes:** none (expected for this review pipeline)
- **Cc: stable:** none (expected)
- Ignore pipeline markers: none present in the supplied message beyond
the usual evaluation context

**Step 1.3: ANALYZE THE COMMIT BODY TEXT**

Record:
- **Bug:** `EXT4_MAX_CLUSTER_LOG_SIZE` was 30, so crafted bigalloc
images can claim cluster sizes up to 1GB. mke2fs only documents
2048–256MB.
- **Symptom/failure mode:** On 32-bit, `s_clusters_per_group *
(clustersize / blocksize)` in `ext4_handle_clustersize()` wraps.
`s_blocks_per_group` is not range-checked on the bigalloc path, so the
wrapped product can pass the consistency check. That yields
inconsistent group geometry and possible out-of-bounds block
allocation.
- **Version info:** none beyond “32-bit systems with bigalloc”.
- **Root cause (author):** cluster-size cap is too high; product can
exceed 32-bit `unsigned long`.

**Step 1.4: DETECT HIDDEN BUG FIXES**

Record: Not a disguised cleanup. The message names overflow, crafted
images, and OOB allocation. It is an explicit mount-time sanity fix.

===============================================================
PHASE 2: DIFF ANALYSIS - LINE BY LINE
===============================================================

**Step 2.1: INVENTORY THE CHANGES**

Record:
- **File:** `fs/ext4/ext4.h` — 1 line changed (`30` → `28`)
- **Functions:** none; only `#define EXT4_MAX_CLUSTER_LOG_SIZE`
- **Scope:** single-file, one-constant surgical fix

**Step 2.2: UNDERSTAND THE CODE FLOW CHANGE**

Record:
- **Before:** `__ext4_fill_super()` rejects `s_log_cluster_size` only if
it is greater than `30 - 10 = 20`, so `clustersize = BLOCK_SIZE << log
= 1024 << 20 = 1GB` is accepted.
- **After:** max log is `28 - 10 = 18`, so max cluster size is `1024 <<
18 = 256MB`.
- **Path:** mount-time superblock validation in `__ext4_fill_super()`,
before `ext4_handle_clustersize()`.

Verified use site:

```5196:5202:fs/ext4/super.c
if (le32_to_cpu(es->s_log_cluster_size) >
(EXT4_MAX_CLUSTER_LOG_SIZE - EXT4_MIN_BLOCK_LOG_SIZE)) {
ext4_msg(sb, KERN_ERR,
"Invalid log cluster size: %u",
le32_to_cpu(es->s_log_cluster_size));
goto out;
}
```

**Step 2.3: IDENTIFY THE BUG MECHANISM**

Record:
- **Category:** integer overflow in mount-time geometry check, leading
to inconsistent geometry / possible OOB block allocation (memory-
safety / filesystem-image robustness).
- **Mechanism:** `s_blocks_per_group` and `s_clusters_per_group` are
`unsigned long` (`fs/ext4/ext4.h`). The check is:

```4500:4506:fs/ext4/super.c
if (sbi->s_blocks_per_group !=
(sbi->s_clusters_per_group * (clustersize /
sb->s_blocksize))) {
ext4_msg(sb, KERN_ERR,
"blocks per group (%lu) and clusters per group
(%lu) inconsistent",
sbi->s_blocks_per_group,
sbi->s_clusters_per_group);
return -EINVAL;
}
```

`s_clusters_per_group` is capped at `blocksize * 8`. Max product is
`(blocksize * 8) * (clustersize / blocksize) = 8 * clustersize`.
- With 1GB clusters: `8 * 2^30 = 2^33` → wraps on 32-bit `unsigned
long`.
- With 256MB clusters: `8 * 2^28 = 2^31` → fits in 32-bit `unsigned
long`.

The `s_blocks_per_group > sb->s_blocksize * 8` range check is only on
the **non-bigalloc** branch (verified in `ext4_handle_clustersize()`).

**Step 2.4: ASSESS THE FIX QUALITY**

Record:
- **Quality:** obviously correct. Tightens an existing mount-time bound
to the documented mkfs limit; the overflow math is checkable by
inspection.
- **Regression risk:** very low. mke2fs documents max 256MB; Andreas
Dilger noted real-world >1GB clusters are implausible. A 512MB (log
29) filesystem would also be rejected; Jan Kara said 28 or 29 were
both acceptable. **29 is not overflow-safe:** `8 * 512MB = 2^32` wraps
to 0 on 32-bit `unsigned long`. 28 is the right cap. Ted applied 28.

===============================================================
PHASE 3: GIT HISTORY INVESTIGATION
===============================================================

**Step 3.1: BLAME THE CHANGED LINES**

Record:
- Current line (already 28 in this checkout): `afa3caf2fbc41` Baokun Li,
2026-06-08 — the candidate itself.
- Previous value 30 introduced by **`9e47a4c9fc580`** (“ext4: sanity
check the block and cluster size at mount time”, Ted Ts'o,
2016-11-18), first in **v4.10-rc1**. Duplicate in-tree copy:
`8cdf3372fe836`.
- That original commit was **Cc: stable** and cited security refs
(SecurityFocus 539661, RH BZ 1332506). The 1GB cap has been in place
since then.

**Step 3.2: FOLLOW THE FIXES: TAG**

Record: No `Fixes:` tag. The overflow is in the interaction of the 2016
cap (`9e47a4c9fc580`, in this tree) and the bigalloc consistency
multiply (bigalloc from `281b59959707d`, **v3.2-rc1**). Both ancestors
of HEAD.

**Step 3.3: CHECK FILE HISTORY FOR RELATED CHANGES**

Record:
- This is **v2 2/3** of “ext4: tighten mount-time superblock geometry
validation”.
- Sibling **1/3:** `d42dc7726a003` — 8-alignment of clusters/inodes per
group (bitmap csum).
- Sibling **3/3:** `a34de48f329b2` — inodes-per-group multiple of
inodes-per-block.
- Cover letter (Ted applied 2026-07-23): patches 2 and 3 were added in
v2 for additional Sashiko issues and are **independent of patch 1**.
- This commit is standalone: only the `#define` in `ext4.h`. No
prerequisite from 1/3 or 3/3.

**Step 3.4: CHECK THE AUTHOR'S OTHER COMMITS**

Record: Baokun Li is a regular, high-volume ext4 contributor (many
recent ext4 commits in this tree). Series was reviewed by
Dilger/Kara/Zhang Yi and applied by Ted Ts'o.

**Step 3.5: CHECK FOR DEPENDENT/PREREQUISITE COMMITS**

Record: None required. `EXT4_MAX_CLUSTER_LOG_SIZE` and the
`s_log_cluster_size` check exist here. The define is the only consumer
besides `ext4.h` itself (`git grep` shows only `fs/ext4/ext4.h` and
`fs/ext4/super.c`). Applies standalone.

===============================================================
PHASE 4: MAILING LIST AND EXTERNAL RESEARCH
===============================================================

**Step 4.1: FIND THE ORIGINAL PATCH DISCUSSION**

Record:
- **b4 dig -c afa3caf2fbc41:** matched `[PATCH v2 2/3]` at https://patch
.msgid.link/20260608111150.827117-3-libaokun@xxxxxxxxxxxxxxxxx
- **b4 dig -a:** series is **v2** “ext4: tighten mount-time superblock
geometry validation” (2026-06-08). v1 was only patch 1; patches 2–3
are new in v2.
- **Jan Kara** (2026-06-09): no NAK; defer 28 vs 29 to Ted; both fine if
consistent.
- **Andreas Dilger** (2026-06-08): Reviewed-by; “pretty safe”; doubts
anyone uses >1GiB clusters.
- **Ted Ts'o** (2026-07-23): “Applied, thanks!” with this SHA.
- No NAKs. No reviewer `Cc: stable` in the thread (absence expected).

**Step 4.2: CHECK WHO REVIEWED THE PATCH**

Record (**b4 dig -w**): linux-ext4, tytso, adilger, jack@xxxxxxx,
yi.zhang@xxxxxxxxxx, ojaswin@xxxxxxxxxxxxx, ritesh.list@xxxxxxxxx,
Sashiko. Maintainers and regular reviewers were on the To/Cc list.
Dilger R-b; Ted applied.

**Step 4.3: SEARCH FOR THE BUG REPORT**

Record: Sashiko is an LF/Google automated review bot, not a user crash
or syzbot repro. The Closes URL is the v1 patchset page (SPA; fetch
returned the Sashiko landing page, not the finding text). Cover letter:
patches 2 and 3 fix additional Sashiko-reported issues. Severity from
the patch itself: crafted-image overflow → inconsistent geometry / OOB
allocation.

**Step 4.4: CHECK FOR RELATED PATCHES AND SERIES**

Record: Three independent mount-time checks. This one does not need 1/3
or 3/3 to be correct. Siblings are also real bugfixes but out of scope
for this verdict.

**Step 4.5: CHECK STABLE MAILING LIST HISTORY**

Record: lore.kernel.org/stable returned 403. Openwall/web search found
no stable-list rejection of this patch. UNVERIFIED whether anyone
already requested it on stable@.

===============================================================
PHASE 5: CODE SEMANTIC ANALYSIS
===============================================================

**Step 5.1: IDENTIFY KEY FUNCTIONS IN THE DIFF**

Record: No function body changed. Consumer: `__ext4_fill_super()`
cluster-log check; downstream user of the accepted size:
`ext4_handle_clustersize()`.

**Step 5.2: TRACE CALLERS**

Record:
- `ext4_handle_clustersize()` is static; only caller is
`__ext4_fill_super()` at mount (`fs/ext4/super.c:5451`).
- `ext4_fill_super()` is the VFS mount entry (`:5835`).
- Context: filesystem mount, not IRQ/hot path.

**Step 5.3: TRACE CALLEES**

Record: The define is only compared in the mount validator. No new
calls.

**Step 5.4: FOLLOW THE CALL CHAIN**

Record: Userspace mount of an ext4 image (`mount`, loop, USB, VM disk) →
`ext4_fill_super` → `__ext4_fill_super` → log-cluster check →
`ext4_handle_clustersize()`. Reachable from any ext4 mount of a bigalloc
image. Triggering the overflow needs a crafted superblock (untrusted
image), typically CAP_SYS_ADMIN to mount, still the usual malicious-
filesystem class.

**Step 5.5: SEARCH FOR SIMILAR PATTERNS**

Record: Same series adds two other geometry checks. Same class of
crafted-image bugs. No other `EXT4_MAX_CLUSTER_LOG_SIZE` sites.

===============================================================
PHASE 6: CROSS-REFERENCING AGAINST THE LOCAL TREE
===============================================================

**Step 6.1: DOES THE BUGGY CODE EXIST IN THIS TREE?**

Record:
- Tree: **v7.3-rc4-27-gf0100363d8c37**, `VERSION=7 PATCHLEVEL=3
SUBLEVEL=0 EXTRAVERSION=-rc4`.
- `git merge-base --is-ancestor afa3caf2fbc41 HEAD` → commit **is** in
HEAD (landed via ext4-for-linus 7.3-rc1). Current `#define` is already
28.
- That does **not** mean “already selected for stable”; it is the
mainline landing. The **bug mechanism** (32-bit multiply of
`s_clusters_per_group` by cluster ratio, uncapped `s_blocks_per_group`
on bigalloc) is still in `ext4_handle_clustersize()` here; only the
cap is 28.
- Bug class has existed since the 2016 cap (`9e47a4c9fc580` is an
ancestor). Code is not missing.

**Step 6.2: CHECK FOR BACKPORT COMPLICATIONS**

Record: Expected apply: **clean**. One-line `#define` change in
`fs/ext4/ext4.h`. Macro and check have been stable since 2016 / the 2022
`ext4_handle_clustersize()` factor-out (`4a8557b094671`).

**Step 6.3: CHECK IF RELATED FIXES ARE ALREADY HERE**

Record: This is the fix. No earlier alternate cap. No follow-up revert
or tightening of this define after `afa3caf2fbc41` in `fs/ext4/ext4.h`.

===============================================================
PHASE 7: SUBSYSTEM AND MAINTAINER CONTEXT
===============================================================

**Step 7.1: IDENTIFY THE SUBSYSTEM AND ITS CRITICALITY**

Record: **ext4** (`fs/ext4/`). **IMPORTANT** — default Linux filesystem;
crafted-image bugs affect anyone who mounts untrusted ext4 (install
media, USB, VMs, backups).

**Step 7.2: ASSESS SUBSYSTEM ACTIVITY**

Record: Actively developed (ext4_for_linus pulls this cycle). The
overflow is in long-standing mount validation, not new BS>PS work.

===============================================================
PHASE 8: IMPACT AND RISK ASSESSMENT
===============================================================

**Step 8.1: DETERMINE WHO IS AFFECTED**

Record: **Filesystem-specific (ext4)**, extra conditions: **bigalloc +
32-bit `unsigned long` + cluster size > 256MB**. 64-bit does not wrap at
1GB, but the looser cap still allows undocumented cluster sizes. 32-bit
still matters (embedded, some Android/i686).

**Step 8.2: DETERMINE THE TRIGGER CONDITIONS**

Record: Mount of a crafted bigalloc image with large
`s_log_cluster_size`. Not every boot. Not an unprivileged syscall by
itself (mount is privileged), but standard untrusted-image attack. Not a
timing race.

**Step 8.3: DETERMINE THE FAILURE MODE SEVERITY**

Record: Integer overflow bypass of geometry check → inconsistent groups
→ **potential out-of-bounds block allocation**. Severity **HIGH**
(memory corruption / OOB on crafted mount). Not a guaranteed panic on
every mount, but it is a real robustness/security class that stable
routinely takes.

**Step 8.4: CALCULATE RISK-BENEFIT RATIO**

Record:
- **Benefit:** high for 32-bit ext4 mounts of untrusted images; aligns
kernel with documented mkfs limit.
- **Risk:** very low (one constant; Dilger R-b; Ted applied). Only
theoretical regression is rejecting 512MB/1GB cluster filesystems that
mke2fs would not create.
- **Ratio:** strongly favors backport.

===============================================================
PHASE 9: FINAL SYNTHESIS
===============================================================

**Step 9.1: COMPILE THE EVIDENCE**

FOR:
- Real 32-bit overflow in mount-time geometry check; wrapped product can
pass; OOB allocation is the stated consequence.
- One-line, obviously correct cap matching mke2fs (256MB).
- 28 is the max value that keeps `8 * clustersize` ≤ 2^31 on 32-bit.
- Reviewed by Andreas Dilger; applied by Ted Ts'o; Jan Kara did not
object.
- Independent of the rest of the series.
- Affected code present since v4.10; applies cleanly.
- Same class as the original 2016 cluster-size sanity check (that one
was Cc: stable).

AGAINST:
- Trigger needs crafted bigalloc + typically 32-bit.
- No `Fixes:` / `Cc: stable` (not a negative per rules).
- Sashiko is a bot, not a production crash with a reproducer.
- Jan Kara floated 29; that value still overflows (verified); committed
value is 28.

UNRESOLVED:
- Stable@ discussion (lore/stable 403).
- Full Sashiko finding text (SPA page).

**Step 9.2: APPLY THE STABLE RULES CHECKLIST**

1. Obviously correct and tested? **PASS** — one-constant change;
maintainer-applied; Dilger R-b.
2. Fixes a real bug that affects users? **PASS** — overflow on crafted
bigalloc mount.
3. Important issue (crash, security, corruption, deadlock)? **PASS** —
HIGH: overflow → bad geometry / possible OOB allocation.
4. Small and contained? **PASS** — 1 line, 1 file.
5. No new features or APIs? **PASS** — tightens an existing limit.
6. Can apply to the local tree? **PASS** — define and check exist; patch
is a single substitution.

**Step 9.3: CHECK FOR EXCEPTION CATEGORIES**

Record: Not an ID/quirk/DT/build/docs exception. It is a normal small
correctness/security-style mount check.

**Step 9.4: MAKE YOUR DECISION**

This tree has the ext4 mount path, the overflow-prone multiply, and the
too-large historical cap. The fix is a one-line tightening to the
documented 256MB limit so the 32-bit product cannot wrap. Independent,
reviewed, and low regression risk. It should be backported here.

------------------------------------------------
What the commit solves and why it matters
------------------------------------------------
Crafted ext4 bigalloc images can set cluster size up to 1GB. On 32-bit,
the groups-per-cluster consistency multiply wraps, so a bogus
`s_blocks_per_group` can look consistent. That can produce wrong group
geometry and out-of-bounds block allocation when the image is mounted.
Capping at 256MB matches mke2fs and keeps the product at 2^31.

------------------------------------------------
Stable rules / risk vs benefit
------------------------------------------------
Meets all six criteria: real overflow, small, no API, applies cleanly.
Benefit is blocking a crafted-image OOB path; risk is rejecting
undocumented huge clusters that tools do not create.

------------------------------------------------
Concerns
------------------------------------------------
No extra series deps. No backport rework expected. 32-bit-centric, but
still a valid stable-class filesystem-image bug.

Verification:
- [Phase 1] Parsed tags: Reported-by Sashiko; Reviewed-by Andreas
Dilger; SOB Baokun Li + Ted Ts'o; Link msgid 20260608111150.827117-3;
no Fixes/Cc:stable
- [Phase 1] Cover letter (openwall/spinics): v2 2/3 independent of 1/3;
overflow on 32-bit bigalloc
- [Phase 2] Diff: 1 line in fs/ext4/ext4.h, 30→28
- [Phase 2] Read ext4_handle_clustersize(): s_blocks_per_group range
check only on non-bigalloc; multiply at lines 4500–4501
- [Phase 2] Types: s_blocks_per_group / s_clusters_per_group are
unsigned long (ext4.h:1595–1596)
- [Phase 2] Math: max product 8*clustersize; 1GB → 2^33 wrap; 256MB →
2^31; 512MB → 2^32 wrap (29 unsafe)
- [Phase 2] Consumer: __ext4_fill_super log-cluster check at
super.c:5196–5202
- [Phase 3] git blame: current 28 from afa3caf2fbc41; 30 from
9e47a4c9fc580 (2016-11-18, v4.10-rc1, Cc:stable)
- [Phase 3] git merge-base --is-ancestor 9e47a4c9fc580 HEAD: yes
- [Phase 3] git merge-base --is-ancestor afa3caf2fbc41 HEAD: yes
(mainline landing, not used as circular “already selected”)
- [Phase 3] Series siblings d42dc7726a003 and a34de48f329b2 are
independent; this patch only touches the define
- [Phase 3] git grep EXT4_MAX_CLUSTER_LOG_SIZE: only ext4.h and super.c
- [Phase 4] b4 dig -c: lore msgid 20260608111150.827117-3
- [Phase 4] b4 dig -a: v2 series 2026-06-08; v1 was patch 1 only
- [Phase 4] b4 dig -w: tytso, dilger, jack, linux-ext4 on original To/Cc
- [Phase 4] Jan Kara: 28 vs 29 up to Ted, no NAK
- [Phase 4] Andreas Dilger: Reviewed-by; safe, nobody using >1GiB
clusters
- [Phase 4] Ted: Applied 2026-07-23 as afa3caf2fbc41
- [Phase 4] lore.kernel.org/stable: 403, UNVERIFIED
- [Phase 4] sashiko.dev hash URL: landing page only, UNVERIFIED full
finding text
- [Phase 5] Only caller of ext4_handle_clustersize: __ext4_fill_super
(mount)
- [Phase 6] git describe HEAD: v7.3-rc4-27-gf0100363d8c37; make
kernelversion 7.3.0-rc4
- [Phase 6] Buggy cap/check present in this lineage since v4.10; apply
is a one-line define swap
- [Phase 8] Failure mode: 32-bit overflow → inconsistent geometry /
possible OOB alloc, severity HIGH
- UNVERIFIED: stable@ thread; full Sashiko report body

**YES**

fs/ext4/ext4.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index da6428312c36b..a9178d63a7d1b 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -334,7 +334,7 @@ struct ext4_io_submit {
#define EXT4_MAX_BLOCK_SIZE 65536
#define EXT4_MIN_BLOCK_LOG_SIZE 10
#define EXT4_MAX_BLOCK_LOG_SIZE 16
-#define EXT4_MAX_CLUSTER_LOG_SIZE 30
+#define EXT4_MAX_CLUSTER_LOG_SIZE 28
#ifdef __KERNEL__
# define EXT4_BLOCK_SIZE(s) ((s)->s_blocksize)
#else
--
2.53.0