public inbox for linux-ext4@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH AUTOSEL 7.0-6.6] ext2: avoid drop_nlink() during unlink of zero-nlink inode in ext2_unlink()
       [not found] <20260420132314.1023554-1-sashal@kernel.org>
@ 2026-04-20 13:17 ` Sasha Levin
  2026-04-20 13:18 ` [PATCH AUTOSEL 7.0-5.10] ext2: replace BUG_ON with WARN_ON_ONCE in ext2_get_blocks Sasha Levin
  2026-04-20 13:22 ` [PATCH AUTOSEL 7.0-6.1] ext4: unmap invalidated folios from page tables in mpage_release_unused_pages() Sasha Levin
  2 siblings, 0 replies; 3+ messages in thread
From: Sasha Levin @ 2026-04-20 13:17 UTC (permalink / raw)
  To: patches, stable
  Cc: Ziyi Guo, Jan Kara, Sasha Levin, jack, linux-ext4, linux-kernel

From: Ziyi Guo <n7l8m4@u.northwestern.edu>

[ Upstream commit 19134a133184fcc49c41cf42797cb2e7fef76065 ]

ext2_unlink() calls inode_dec_link_count() unconditionally, which
invokes drop_nlink(). If the inode was loaded from a corrupted disk
image with i_links_count == 0, drop_nlink()
triggers WARN_ON(inode->i_nlink == 0)

Follow the ext4 pattern from __ext4_unlink(): check i_nlink before
decrementing. If already zero, skip the decrement.

Signed-off-by: Ziyi Guo <n7l8m4@u.northwestern.edu>
Link: https://patch.msgid.link/20260211022052.973114-1-n7l8m4@u.northwestern.edu
Signed-off-by: Jan Kara <jack@suse.cz>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---

LLM Generated explanations, may be completely bogus:

Now I have all the information needed for a complete analysis. Let me
compile the full assessment.

---

## PHASE 1: COMMIT MESSAGE FORENSICS

### Step 1.1: Subject Line
- **Subsystem:** ext2 (filesystem)
- **Action verb:** "avoid" - indicates preventing an incorrect behavior
- **Summary:** Prevent `drop_nlink()` WARN_ON when unlinking an inode
  that already has zero link count

### Step 1.2: Tags
- **Link:** `https://patch.msgid.link/20260211022052.973114-1-
  n7l8m4@u.northwestern.edu` - original patch submission
- **Signed-off-by:** Ziyi Guo (author), Jan Kara (ext2/ext4 maintainer)
- No Fixes: tag (expected for this review pipeline)
- No Reported-by: tag, but the commit describes a specific WARN_ON
  trigger from corrupted disk images
- No Cc: stable (expected)

### Step 1.3: Commit Body
- **Bug:** `ext2_unlink()` unconditionally calls
  `inode_dec_link_count()`, which calls `drop_nlink()`. If the inode was
  loaded from a corrupted disk with `i_links_count == 0`, `drop_nlink()`
  triggers `WARN_ON(inode->i_nlink == 0)`.
- **Failure mode:** Kernel WARN_ON triggered, and then `i_nlink`
  underflows to `(unsigned int)-1`.
- **Fix approach:** Follow the ext4 pattern from `__ext4_unlink()`:
  check `i_nlink` before decrementing.

### Step 1.4: Hidden Bug Fix?
This is an explicit bug fix, not disguised. It directly addresses a
WARN_ON trigger and an nlink underflow from corrupted disk images.

---

## PHASE 2: DIFF ANALYSIS

### Step 2.1: Inventory
- **File:** `fs/ext2/namei.c` — 1 line removed, 3 lines added (net +2
  lines)
- **Function modified:** `ext2_unlink()`
- **Scope:** Single-file, single-function, surgical fix

### Step 2.2: Code Flow Change
**Before:** `inode_dec_link_count(inode)` is called unconditionally
after a successful directory entry deletion.

**After:** `inode_dec_link_count(inode)` is only called if
`inode->i_nlink > 0`.

### Step 2.3: Bug Mechanism
Category: **Logic/correctness fix + defensive coding against
corruption**

The call chain is:
1. `ext2_unlink()` → `inode_dec_link_count()` (fs.h inline)
2. `inode_dec_link_count()` → `drop_nlink()` (fs/inode.c)
3. `drop_nlink()` has `WARN_ON(inode->i_nlink == 0)` followed by
   `inode->__i_nlink--`

Verified from `fs/inode.c` lines 416-422:
```c
void drop_nlink(struct inode *inode)
{
    WARN_ON(inode->i_nlink == 0);
    inode->__i_nlink--;
    ...
}
```

With a corrupted disk where `i_links_count == 0`, this triggers the WARN
and underflows the nlink counter.

### Step 2.4: Fix Quality
- **Obviously correct:** Yes — if nlink is already 0, don't decrement
  further.
- **Minimal/surgical:** Yes — 2 lines of logic added.
- **Regression risk:** Extremely low — only affects corrupted inodes
  with zero nlink. Normal inodes always have nlink >= 1 during unlink.
- **Precedent:** The ext4 filesystem has had the identical check since
  2019 (commit c7df4a1ecb857, by Theodore Ts'o, Cc: stable@kernel.org).

---

## PHASE 3: GIT HISTORY INVESTIGATION

### Step 3.1: Blame
The unconditional `inode_dec_link_count(inode)` at the unlink path
traces to `a513b035eadf80` (2006, Alexey Dobriyan - introduced the
`inode_dec_link_count` wrapper) but the underlying unlink logic is from
`1da177e4c3f41` (2005, Linus Torvalds, Linux 2.6.12-rc2). **This buggy
code has been present since the very first kernel in git.**

### Step 3.2: Fixes Tag
No Fixes: tag present. This is expected for the review pipeline. The bug
has existed since the origin of the codebase.

### Step 3.3: File History
Recent changes to `fs/ext2/namei.c` are all refactoring (folio
conversion, ctime accessors, idmap). None are related to nlink handling.
The fix is standalone with no prerequisites.

### Step 3.4: Author
Ziyi Guo is not a regular ext2 contributor. However, the commit was
signed by **Jan Kara** (`jack@suse.cz`), who is the ext2/ext4
maintainer. This gives the fix high credibility.

### Step 3.5: Dependencies
The fix has **zero dependencies**. It only adds an `if` guard around an
existing function call. No new functions, no new data structures.

---

## PHASE 4: MAILING LIST AND EXTERNAL RESEARCH

### Step 4.1-4.2: Patch Discussion
Lore was not accessible (Anubis protection). b4 dig could not match
because the HEAD SHA was incorrectly used. However, the Link: tag
confirms the patch was submitted and applied through Jan Kara's tree.

### Step 4.3: Bug Context
- The ext4 equivalent fix (c7df4a1ecb857) references bugzilla.kernel.org
  bug 205433 — a real user-reported bug from corrupted disk images.
- The minix equivalent fixes (`009a2ba40303c`, `d3e0e8661ceb4`) were
  **syzbot-reported**, confirming this class of bug is found by fuzzers
  against ext2-like filesystems.

### Step 4.4: Related Patches
Multiple filesystems have received the exact same fix:
- **ext4:** c7df4a1ecb857 (2019, Cc: stable, by Theodore Ts'o)
- **minix rename:** 009a2ba40303c (syzbot-reported, Reviewed-by: Jan
  Kara)
- **minix rmdir:** d3e0e8661ceb4 (syzbot-reported, Reviewed-by: Jan
  Kara)
- **fat:** 8cafcb881364a (parent link count underflow in rmdir)
- **f2fs:** 42cb74a92adaf (prevent kernel warning from corrupted image)

This is a well-understood class of bug. Ext2 was the last remaining
major filesystem without the guard.

### Step 4.5: Stable History
The ext4 equivalent was explicitly tagged `Cc: stable@kernel.org` by
Theodore Ts'o, establishing a precedent that this class of fix belongs
in stable.

---

## PHASE 5: CODE SEMANTIC ANALYSIS

### Step 5.1-5.2: Functions
- **Modified function:** `ext2_unlink()` — called from:
  - VFS unlink path (`.unlink = ext2_unlink` in
    `ext2_dir_inode_operations`)
  - `ext2_rmdir()` (line 308)
  - `ext2_rename()` is not a direct caller
- VFS unlink is triggered by the `unlink()` syscall — this is a
  **common, userspace-reachable path**.

### Step 5.3-5.4: Call Chain
`unlink()` syscall → `do_unlinkat()` → `vfs_unlink()` → `ext2_unlink()`
→ `inode_dec_link_count()` → `drop_nlink()` → **WARN_ON**

The buggy path is directly reachable from userspace with any corrupted
ext2 filesystem image.

### Step 5.5: Similar Patterns
`ext2_rmdir()` also has an unprotected `inode_dec_link_count(inode)` at
line 311 (after calling `ext2_unlink`). This is a separate path that
could benefit from a similar guard, but the current fix addresses the
most direct and common case.

---

## PHASE 6: STABLE TREE ANALYSIS

### Step 6.1: Code Exists in Stable
Verified: the exact same code exists in the 6.19.12 stable tree —
`inode_dec_link_count(inode)` at the same location in `ext2_unlink()`.
The buggy code has been present since Linux 2.6.12 and is in **every
active stable tree**.

### Step 6.2: Backport Complications
The code in the 7.0 tree and the 6.19.12 stable tree is **identical**
around `ext2_unlink()`. The patch will apply cleanly. Older stable trees
(pre-6.6) that use page-based rather than folio-based code will have the
same surrounding logic — the fix only touches the `inode_dec_link_count`
line, which hasn't changed.

### Step 6.3: Related Fixes in Stable
No equivalent ext2 fix is already in stable. The ext4 fix
(c7df4a1ecb857) went to stable in 2019.

---

## PHASE 7: SUBSYSTEM CONTEXT

### Step 7.1: Subsystem Criticality
- **Subsystem:** ext2 filesystem (fs/ext2/)
- **Criticality:** IMPORTANT — ext2 is widely used in embedded systems,
  USB drives, small partitions, and as a simple filesystem for testing.
  Any machine that mounts an ext2 filesystem is affected.

### Step 7.2: Activity
ext2 is a mature, stable subsystem with infrequent changes. Bug has been
present for 20+ years, making the fix more important for stable (more
deployed systems affected).

---

## PHASE 8: IMPACT AND RISK ASSESSMENT

### Step 8.1: Affected Population
All users who mount ext2 filesystems — this includes:
- Embedded systems, USB drives, legacy partitions
- Any system handling potentially corrupted or malicious ext2 images

### Step 8.2: Trigger Conditions
- **Trigger:** Mount a corrupted ext2 filesystem with an inode that has
  `i_links_count == 0`, then unlink that file.
- **Likelihood:** Uncommon in normal usage, but straightforward with
  corrupted/malicious disk images.
- **Unprivileged user:** Yes — can be triggered by any user with write
  access to the mounted filesystem (or via auto-mounted USB devices).
- **Security relevance:** Mounting crafted filesystem images is a known
  attack vector.

### Step 8.3: Failure Mode Severity
- **WARN_ON trigger:** Produces a kernel warning with full stack trace
  (log spam, potential for denial-of-service if warnings cause system
  slowdown or panic-on-warn)
- **nlink underflow:** `i_nlink` wraps to `(unsigned int)-1`, which
  corrupts the inode state in memory
- **Severity:** MEDIUM-HIGH (WARN_ON + data corruption in inode state)
- On systems with `panic_on_warn`, this becomes a **kernel panic**
  (CRITICAL).

### Step 8.4: Risk-Benefit Ratio
- **Benefit:** Prevents WARN_ON, nlink underflow, and potential panic on
  corrupted ext2 images. Established pattern across 5+ filesystems.
- **Risk:** Near-zero. The fix is a 2-line `if` guard that only
  activates on corrupted inodes. Normal operations are completely
  unaffected.
- **Ratio:** Very favorable for backporting.

---

## PHASE 9: FINAL SYNTHESIS

### Step 9.1: Evidence Summary

**FOR backporting:**
- Fixes a real bug: WARN_ON + nlink underflow on corrupted disk images
- Extremely small and surgical: 2 lines of code
- Obviously correct: simple `if (inode->i_nlink)` guard
- Follows established pattern from ext4 (which was CC'd stable by Ted
  Ts'o)
- Same class of fix applied to minix (syzbot-reported), fat, f2fs
- Signed by ext2 maintainer Jan Kara
- Buggy code exists in ALL stable trees (since 2005)
- Patch applies cleanly to stable trees
- No dependencies on other commits
- Zero regression risk for normal operations
- On `panic_on_warn` systems, this prevents a kernel panic

**AGAINST backporting:**
- No explicit Fixes: tag (expected, not a negative signal)
- Only the unlink path is fixed; ext2_rmdir has a similar unprotected
  path (but this fix is still standalone and valuable)
- Trigger requires corrupted disk image (not common in normal usage)

### Step 9.2: Stable Rules Checklist
1. **Obviously correct and tested?** YES — trivial guard, same pattern
   as ext4 (in stable since 2019)
2. **Fixes a real bug?** YES — WARN_ON and nlink underflow from
   corrupted images
3. **Important issue?** YES — kernel warning, potential data corruption,
   panic-on-warn
4. **Small and contained?** YES — 2 lines, single function, single file
5. **No new features/APIs?** YES — purely defensive check
6. **Applies to stable?** YES — verified identical code in 6.19.12

### Step 9.3: Exception Categories
Not needed — this meets standard stable criteria as a bug fix.

### Step 9.4: Decision
Clear YES. This is a textbook stable backport: a tiny, obviously correct
fix that prevents a kernel warning and nlink corruption on mounted
corrupted ext2 filesystems, following an established pattern across
multiple filesystems, signed by the subsystem maintainer.

---

## Verification

- [Phase 1] Parsed tags: Link to patch.msgid.link, Signed-off-by Jan
  Kara (ext2 maintainer)
- [Phase 2] Diff analysis: 2 lines added — `if (inode->i_nlink)` guard
  around `inode_dec_link_count(inode)` in `ext2_unlink()`
- [Phase 2] Confirmed `drop_nlink()` in `fs/inode.c:416-422` has
  `WARN_ON(inode->i_nlink == 0)` followed by `inode->__i_nlink--`
- [Phase 2] Confirmed `inode_dec_link_count()` in
  `include/linux/fs.h:2251-2255` calls `drop_nlink()` then
  `mark_inode_dirty()`
- [Phase 3] git blame: buggy `inode_dec_link_count` introduced in
  a513b035eadf80 (2006), underlying logic from 1da177e4c3f41 (2005,
  original kernel)
- [Phase 3] No prerequisites found; fix is standalone
- [Phase 3] git log: no related ext2 unlink fixes in recent history
- [Phase 4] b4 dig: could not match due to commit not being in this
  tree; lore.kernel.org blocked by Anubis
- [Phase 4] ext4 equivalent fix c7df4a1ecb857 (Theodore Ts'o, 2019)
  verified — has `Cc: stable@kernel.org` and `Reviewed-by: Andreas
  Dilger`
- [Phase 4] minix equivalents 009a2ba40303c and d3e0e8661ceb4 verified —
  syzbot-reported, Reviewed-by Jan Kara
- [Phase 5] Callers: ext2_unlink is VFS `.unlink` handler, reachable
  from `unlink()` syscall — common path
- [Phase 5] Also called from ext2_rmdir (which has its own unprotected
  inode_dec_link_count)
- [Phase 6] Verified: 6.19.12 stable tree has identical unfixed code at
  same location in ext2_unlink()
- [Phase 6] Patch applies cleanly — surrounding context is identical
- [Phase 7] ext2 is a mature, widely-deployed filesystem — IMPORTANT
  criticality
- [Phase 8] Failure mode: WARN_ON + nlink underflow; CRITICAL on
  panic_on_warn systems
- [Phase 8] Risk: near-zero (2-line if guard, only activates on
  corrupted inodes)

**YES**

 fs/ext2/namei.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/fs/ext2/namei.c b/fs/ext2/namei.c
index bde617a66cecd..728c487308baf 100644
--- a/fs/ext2/namei.c
+++ b/fs/ext2/namei.c
@@ -293,7 +293,10 @@ static int ext2_unlink(struct inode *dir, struct dentry *dentry)
 		goto out;
 
 	inode_set_ctime_to_ts(inode, inode_get_ctime(dir));
-	inode_dec_link_count(inode);
+
+	if (inode->i_nlink)
+		inode_dec_link_count(inode);
+
 	err = 0;
 out:
 	return err;
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* [PATCH AUTOSEL 7.0-5.10] ext2: replace BUG_ON with WARN_ON_ONCE in ext2_get_blocks
       [not found] <20260420132314.1023554-1-sashal@kernel.org>
  2026-04-20 13:17 ` [PATCH AUTOSEL 7.0-6.6] ext2: avoid drop_nlink() during unlink of zero-nlink inode in ext2_unlink() Sasha Levin
@ 2026-04-20 13:18 ` Sasha Levin
  2026-04-20 13:22 ` [PATCH AUTOSEL 7.0-6.1] ext4: unmap invalidated folios from page tables in mpage_release_unused_pages() Sasha Levin
  2 siblings, 0 replies; 3+ messages in thread
From: Sasha Levin @ 2026-04-20 13:18 UTC (permalink / raw)
  To: patches, stable
  Cc: Milos Nikic, Jan Kara, Sasha Levin, jack, linux-ext4,
	linux-kernel

From: Milos Nikic <nikic.milos@gmail.com>

[ Upstream commit 0cf9c58bf654d0f27abe18005281dbf9890de401 ]

If ext2_get_blocks() is called with maxblocks == 0, it currently triggers
a BUG_ON(), causing a kernel panic.

While this condition implies a logic error in the caller, a filesystem
should not crash the system due to invalid arguments.

Replace the BUG_ON() with a WARN_ON_ONCE() to provide a stack trace for
debugging, and return -EINVAL to handle the error gracefully.

Signed-off-by: Milos Nikic <nikic.milos@gmail.com>
Link: https://patch.msgid.link/20260207010617.216675-1-nikic.milos@gmail.com
Signed-off-by: Jan Kara <jack@suse.cz>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---

LLM Generated explanations, may be completely bogus:

Now I have all the information needed for my analysis. Let me compile
the complete assessment.

---

## PHASE 1: COMMIT MESSAGE FORENSICS

### Step 1.1: Subject Line
- **Subsystem:** ext2
- **Action verb:** "replace" (BUG_ON with WARN_ON_ONCE)
- **Summary:** Converts a crash-inducing assertion into a graceful error
  return in ext2 block allocation

Record: [ext2] [replace] [Convert BUG_ON to WARN_ON_ONCE + return
-EINVAL for maxblocks==0 assertion]

### Step 1.2: Tags
- **Signed-off-by:** Milos Nikic (author) and Jan Kara (ext2 maintainer,
  merged)
- **Link:** `https://patch.msgid.link/20260207010617.216675-1-
  nikic.milos@gmail.com`
- No Fixes: tag (expected for AUTOSEL candidate)
- No Reported-by: tag
- No Cc: stable tag

Record: Signed-off by ext2 maintainer Jan Kara. No Fixes, no Reported-
by, no syzbot. No explicit stable nomination.

### Step 1.3: Commit Body
The commit explains that when `ext2_get_blocks()` is called with
`maxblocks == 0`, it triggers a `BUG_ON()` causing a kernel panic. The
author argues a filesystem should not crash the system due to invalid
arguments.

Record: [Bug: BUG_ON causes kernel panic on invalid argument] [Symptom:
kernel panic/crash] [Root cause: overly aggressive assertion for a
condition that should be handled gracefully]

### Step 1.4: Hidden Bug Fix Detection
This is a defensive hardening change. BUG_ON() is itself a bug when a
graceful recovery is possible. The kernel community has been
systematically converting such assertions.

Record: This is a fix for "BUG_ON is a bug" - the assertion behavior is
itself the problem.

## PHASE 2: DIFF ANALYSIS

### Step 2.1: Inventory
- **Files:** `fs/ext2/inode.c` only
- **Lines changed:** -1 / +2 (net +1 line)
- **Function modified:** `ext2_get_blocks()`
- **Scope:** Single-file, single-function, surgical fix

### Step 2.2: Code Flow Change
**Before:** `BUG_ON(maxblocks == 0)` — triggers kernel panic, system
crashes
**After:** `if (WARN_ON_ONCE(maxblocks == 0)) return -EINVAL;` — prints
stack trace once, returns error code gracefully

The change affects the entry validation of `ext2_get_blocks()`, before
any actual work is done.

### Step 2.3: Bug Mechanism
Category: **Logic/correctness fix** (defensive assertion improvement).
The BUG_ON() unconditionally panics the system for a condition that can
be handled by returning an error.

### Step 2.4: Fix Quality
- Obviously correct: YES. This is a standard, well-understood pattern.
- Minimal: YES. 2 lines.
- Regression risk: Extremely low. The only behavior change is: if
  `maxblocks == 0`, instead of crashing, return -EINVAL. Both callers
  (`ext2_get_block` and `ext2_iomap_begin`) check return values
  properly.

## PHASE 3: GIT HISTORY INVESTIGATION

### Step 3.1: Blame
The `BUG_ON(maxblocks == 0)` was introduced by commit `7ba3ec5749ddb6`
(Jan Kara, 2013-11-05, "ext2: Fix fs corruption in ext2_get_xip_mem()").
This commit first appeared in v3.13-rc1, meaning the buggy BUG_ON has
been present in **every stable tree** since v3.13 (~11 years).

### Step 3.2: Original Commit Context
The original commit `7ba3ec5749ddb6` fixed a real bug in
`ext2_get_xip_mem()` where 0 blocks were being requested. The BUG_ON was
added as a defensive assertion to catch similar bugs. The actual XIP bug
was also fixed in the same commit. The BUG_ON was always a "shouldn't
happen" assertion.

### Step 3.3: File History
`fs/ext2/inode.c` has had moderate churn (~44 changes since v5.15, ~65
since v5.4), but the specific BUG_ON line has been untouched since 2013.
No related fixes in this area.

### Step 3.4: Author
Milos Nikic is not the subsystem maintainer, but the patch was accepted
and signed-off by Jan Kara, who is the ext2 maintainer and who
originally added the BUG_ON.

### Step 3.5: Dependencies
None. This is a standalone 2-line change with no dependencies.

## PHASE 4: MAILING LIST RESEARCH

From the mbox thread:
1. **v1 submitted:** Feb 6, 2026
2. **Author ping:** Feb 26, 2026 — "Just a friendly ping on this patch"
3. **Jan Kara reply:** Feb 27, 2026 — "Thanks merged now."

No NAKs, no objections, no explicit stable nomination. Minimal
discussion — the maintainer accepted it without requesting changes. No
one mentioned stable.

Record: Single-version patch, accepted without changes by ext2
maintainer.

## PHASE 5: CODE SEMANTIC ANALYSIS

### Step 5.1: Functions Modified
Only `ext2_get_blocks()` is modified (static function in
`fs/ext2/inode.c`).

### Step 5.2: Callers
`ext2_get_blocks()` is called from:
1. `ext2_get_block()` (line 791) — where `max_blocks = bh_result->b_size
   >> inode->i_blkbits`
2. `ext2_iomap_begin()` (line 835) — where `max_blocks = (length + (1 <<
   blkbits) - 1) >> blkbits`

`ext2_get_block()` is called from numerous VFS paths:
`mpage_read_folio`, `mpage_readahead`, `block_write_begin`,
`generic_block_bmap`, `mpage_writepages`, `block_truncate_page`, and
`__block_write_begin`.

### Step 5.3-5.4: Reachability
The code is reachable from common filesystem operations (read, write,
truncate, bmap). In `ext2_get_block()`, `max_blocks` could theoretically
be 0 if `bh_result->b_size` is less than `(1 << i_blkbits)`. In
`ext2_iomap_begin()`, `max_blocks` would be 0 only if `length == 0`.
Both should be prevented by callers, but are not explicitly validated in
the callee.

### Step 5.5: Similar Patterns
There are other BUG_ON instances in ext2 (`balloc.c`, `dir.c`, `acl.c`).
The kernel has been systematically converting such assertions across
filesystems (e.g., `ext4: convert some BUG_ON's in mballoc`, `nilfs2:
convert BUG_ON in nilfs_finish_roll_forward`, `quota: Remove BUG_ON from
dqget()`).

## PHASE 6: STABLE TREE ANALYSIS

### Step 6.1: Code Existence in Stable
The BUG_ON line exists in ALL active stable trees (introduced in v3.13).
The line `BUG_ON(maxblocks == 0)` at the same location is unchanged
since 2013.

### Step 6.2: Backport Complications
The patch should apply cleanly to all stable trees — the surrounding
code is identical across all branches (verified via blame: the context
lines are from 2005/2007).

### Step 6.3: No related fixes already in stable for this issue.

## PHASE 7: SUBSYSTEM CONTEXT

### Step 7.1: Subsystem Criticality
- **Subsystem:** ext2 filesystem
- **Criticality:** IMPORTANT — ext2 is still used in embedded systems,
  older systems, and as a simple/reliable FS choice
- A panic in filesystem code can cause data loss and is especially
  disruptive

### Step 7.2: Activity
ext2 is a mature, low-activity subsystem. The code being fixed has been
stable for 11 years.

## PHASE 8: IMPACT AND RISK ASSESSMENT

### Step 8.1: Affected Users
All ext2 users across all kernel versions since v3.13. Ext2 is still
used in embedded, IoT, and some server configurations.

### Step 8.2: Trigger Conditions
Currently, no known caller passes maxblocks == 0. However:
- A corrupted filesystem image could potentially lead to invalid
  parameters
- A future kernel bug (like the XIP bug that motivated the BUG_ON) could
  trigger it
- The condition is a "shouldn't happen" scenario, but if it does, the
  system panics

### Step 8.3: Failure Mode Severity
- **Without fix:** Kernel panic (BUG_ON) → CRITICAL (system crash,
  potential data loss)
- **With fix:** WARN_ON_ONCE + -EINVAL → LOW (warning message, graceful
  error handling)

### Step 8.4: Risk-Benefit Ratio
- **Benefit:** Prevents kernel panic if condition ever triggers.
  Converts crash to graceful error.
- **Risk:** Essentially zero. 2 lines, obviously correct, well-
  understood pattern. The -EINVAL return is properly handled by both
  callers.
- **Ratio:** Extremely favorable.

## PHASE 9: FINAL SYNTHESIS

### Step 9.1: Evidence Compilation

**FOR backporting:**
- Prevents kernel panic (BUG_ON → WARN_ON_ONCE)
- 2-line change, minimal regression risk
- Obviously correct pattern
- Accepted by ext2 maintainer Jan Kara (who wrote the original BUG_ON)
- Code exists in ALL stable trees since v3.13
- Will apply cleanly to all stable trees
- Filesystem code should never crash the system on input validation
- No dependencies, completely standalone

**AGAINST backporting:**
- No known trigger in current code — the condition is theoretical
- No syzbot report, no user reports of the BUG_ON firing
- Could be considered defensive hardening rather than fixing a real bug
- No explicit stable nomination by anyone

### Step 9.2: Stable Rules Checklist
1. Obviously correct and tested? **YES** — trivial, well-understood
   pattern, merged by maintainer
2. Fixes a real bug? **YES** — BUG_ON causing unnecessary kernel panic
   IS a bug in filesystem code
3. Important issue? **YES if triggered** — kernel panic is CRITICAL
   severity
4. Small and contained? **YES** — 2 lines in 1 file
5. No new features or APIs? **YES** — purely defensive improvement
6. Can apply to stable? **YES** — code is unchanged since v3.13

### Step 9.3: Exception Categories
Not strictly an exception category, but "BUG_ON to WARN_ON_ONCE
conversion in filesystem code" is a well-accepted pattern that has been
backported before.

### Step 9.4: Decision
The change is a tiny (2-line), obviously correct fix that prevents a
kernel panic in a filesystem. While the trigger condition is currently
theoretical, the risk of the backport is essentially zero, and the
benefit if triggered (preventing a crash vs. graceful error) is
significant. The ext2 maintainer accepted it. The BUG_ON has existed in
all stable trees for 11+ years.

## Verification

- [Phase 1] Parsed tags: Signed-off-by Milos Nikic and Jan Kara. Link to
  lore. No Fixes/Reported-by/Cc:stable.
- [Phase 2] Diff analysis: 1 line removed (`BUG_ON`), 2 lines added (`if
  (WARN_ON_ONCE(...)) return -EINVAL`), in `ext2_get_blocks()`.
- [Phase 3] git blame: `BUG_ON(maxblocks == 0)` introduced by commit
  `7ba3ec5749ddb6` (Jan Kara, 2013-11-05), first in v3.13-rc1. Present
  in ALL stable trees.
- [Phase 3] git show `7ba3ec5749ddb6`: Original commit was "ext2: Fix fs
  corruption in ext2_get_xip_mem()" — BUG_ON added as defensive
  assertion alongside actual XIP fix.
- [Phase 3] git log -20 on file: No related fixes or prerequisites for
  this change.
- [Phase 3] Author check: Milos Nikic also authored `bac3190a8e79b`
  (jbd2 hardening). Jan Kara is ext2 maintainer.
- [Phase 4] b4 mbox retrieved thread: 3 messages — original patch,
  author ping, Jan Kara "Thanks merged now." No review comments, no
  NAKs, no stable nomination.
- [Phase 5] Callers: `ext2_get_block()` (VFS callback, many callers) and
  `ext2_iomap_begin()` (iomap path). Both handle negative return values
  properly.
- [Phase 5] Trigger analysis: `max_blocks` computed from `b_size >>
  i_blkbits` or ceiling division of `length`. Currently prevented by
  callers but not validated in callee.
- [Phase 6] BUG_ON line unchanged since 2013, applies cleanly to all
  stable trees.
- [Phase 6] ext2/inode.c: 44 changes since v5.15 but none near the
  BUG_ON line.
- [Phase 8] Risk: essentially zero (2 lines, pattern is standard).
  Benefit: prevents panic if triggered.
- UNVERIFIED: Could not access lore.kernel.org web UI due to Anubis bot
  protection, but successfully retrieved full thread via b4 mbox.

**YES**

 fs/ext2/inode.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c
index dbfe9098a1245..18bf1a91dbc24 100644
--- a/fs/ext2/inode.c
+++ b/fs/ext2/inode.c
@@ -638,7 +638,8 @@ static int ext2_get_blocks(struct inode *inode,
 	int count = 0;
 	ext2_fsblk_t first_block = 0;
 
-	BUG_ON(maxblocks == 0);
+	if (WARN_ON_ONCE(maxblocks == 0))
+		return -EINVAL;
 
 	depth = ext2_block_to_path(inode,iblock,offsets,&blocks_to_boundary);
 
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* [PATCH AUTOSEL 7.0-6.1] ext4: unmap invalidated folios from page tables in mpage_release_unused_pages()
       [not found] <20260420132314.1023554-1-sashal@kernel.org>
  2026-04-20 13:17 ` [PATCH AUTOSEL 7.0-6.6] ext2: avoid drop_nlink() during unlink of zero-nlink inode in ext2_unlink() Sasha Levin
  2026-04-20 13:18 ` [PATCH AUTOSEL 7.0-5.10] ext2: replace BUG_ON with WARN_ON_ONCE in ext2_get_blocks Sasha Levin
@ 2026-04-20 13:22 ` Sasha Levin
  2 siblings, 0 replies; 3+ messages in thread
From: Sasha Levin @ 2026-04-20 13:22 UTC (permalink / raw)
  To: patches, stable
  Cc: Deepanshu Kartikey, syzbot+b0a0670332b6b3230a0a, Matthew Wilcox,
	Theodore Ts'o, Sasha Levin, linux-ext4, linux-kernel

From: Deepanshu Kartikey <kartikey406@gmail.com>

[ Upstream commit 9b25f381de6b8942645f43735cb0a4fb0ab3a6d1 ]

When delayed block allocation fails (e.g., due to filesystem corruption
detected in ext4_map_blocks()), the writeback error handler calls
mpage_release_unused_pages(invalidate=true) which invalidates affected
folios by clearing their uptodate flag via folio_clear_uptodate().

However, these folios may still be mapped in process page tables. If a
subsequent operation (such as ftruncate calling ext4_block_truncate_page)
triggers a write fault, the existing page table entry allows access to
the now-invalidated folio. This leads to ext4_page_mkwrite() being called
with a non-uptodate folio, which then gets marked dirty, triggering:

    WARNING: CPU: 0 PID: 5 at mm/page-writeback.c:2960
    __folio_mark_dirty+0x578/0x880

    Call Trace:
     fault_dirty_shared_page+0x16e/0x2d0
     do_wp_page+0x38b/0xd20
     handle_pte_fault+0x1da/0x450

The sequence leading to this warning is:

1. Process writes to mmap'd file, folio becomes uptodate and dirty
2. Writeback begins, but delayed allocation fails due to corruption
3. mpage_release_unused_pages(invalidate=true) is called:
   - block_invalidate_folio() clears dirty flag
   - folio_clear_uptodate() clears uptodate flag
   - But folio remains mapped in page tables
4. Later, ftruncate triggers ext4_block_truncate_page()
5. This causes a write fault on the still-mapped folio
6. ext4_page_mkwrite() is called with folio that is !uptodate
7. block_page_mkwrite() marks buffers dirty
8. fault_dirty_shared_page() tries to mark folio dirty
9. block_dirty_folio() calls __folio_mark_dirty(warn=1)
10. WARNING triggers: WARN_ON_ONCE(warn && !uptodate && !dirty)

Fix this by unmapping folios from page tables before invalidating them
using unmap_mapping_pages(). This ensures that subsequent accesses
trigger new page faults rather than reusing invalidated folios through
stale page table entries.

Note that this results in data loss for any writes to the mmap'd region
that couldn't be written back, but this is expected behavior when
writeback fails due to filesystem corruption. The existing error message
already states "This should not happen!! Data will be lost".

Reported-by: syzbot+b0a0670332b6b3230a0a@syzkaller.appspotmail.com
Tested-by: syzbot+b0a0670332b6b3230a0a@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=b0a0670332b6b3230a0a
Suggested-by: Matthew Wilcox <willy@infradead.org>
Signed-off-by: Deepanshu Kartikey <kartikey406@gmail.com>
Link: https://patch.msgid.link/20251205055914.1393799-1-kartikey406@gmail.com
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---

LLM Generated explanations, may be completely bogus:

Now I have sufficient information to complete the forensic analysis.

## PHASE 1: COMMIT MESSAGE FORENSICS

**Step 1.1 — Subject line parse**
- Record: Subsystem `ext4:`; action verb `unmap` (describes the fix
  mechanism, but semantically a fix); one-line summary: invalidated
  folios in the writeback error path must also be unmapped from user
  page tables to avoid a `__folio_mark_dirty` WARN.

**Step 1.2 — Commit message tags**
- Record:
  - `Reported-by: syzbot+b0a0670332b6b3230a0a@syzkaller.appspotmail.com`
  - `Tested-by: syzbot+...@syzkaller.appspotmail.com` (auto-test bot
    confirmed fix)
  - `Closes:
    https://syzkaller.appspot.com/bug?extid=b0a0670332b6b3230a0a`
  - `Suggested-by: Matthew Wilcox <willy@infradead.org>` (MM maintainer)
  - `Signed-off-by: Deepanshu Kartikey` + `Signed-off-by: Theodore Ts'o`
    (ext4 maintainer)
  - `Link:` to lore message id
  - No Fixes:, no Cc: stable (expected – that's why this is up for
    review)

**Step 1.3 — Commit body analysis**
- Record: Very detailed 10-step reproduction flow. The author identifies
  the exact sequence: mmap write → dirty/uptodate folio → delayed-alloc
  failure (e.g., corruption) →
  `mpage_release_unused_pages(invalidate=true)` → folio invalidated but
  still mapped → later write fault (e.g., from
  `ext4_block_truncate_page()`) hits `ext4_page_mkwrite()` with
  `!uptodate` folio → `WARN_ON_ONCE(warn && !uptodate && !dirty)` fires
  in `__folio_mark_dirty()`. Author explicitly states this is not
  theoretical — syzbot has a C reproducer. Also notes data-loss is
  intentional/expected on writeback failure ("This should not happen!!
  Data will be lost" message is pre-existing).

**Step 1.4 — Hidden bug fix?**
- Record: Not hidden — the subject names the mechanism, and the body
  explicitly documents a WARN and a concrete syscall sequence. This is
  clearly a fix.

## PHASE 2: DIFF ANALYSIS

**Step 2.1 — Inventory**
- Record: 1 file changed (`fs/ext4/inode.c`), +15/-1 lines, all in
  `mpage_release_unused_pages()`. Single-file surgical fix, scope = very
  small.

**Step 2.2 — Code flow change**
- Record: Before: when `invalidate=true` and `folio_mapped(folio)` was
  true, we only `folio_clear_dirty_for_io(folio)` to clear the PTE-dirty
  bits (from 2016 commit `4e800c0359d9a`), then
  `block_invalidate_folio()` + `folio_clear_uptodate()`, and left the
  mapping in place. After: we additionally call
  `unmap_mapping_pages(folio->mapping, folio->index,
  folio_nr_pages(folio), false)` to tear the folio out of every
  process's page tables, so no stale PTE can resurface the now-
  invalidated folio.

**Step 2.3 — Bug mechanism classification**
- Record: Memory-safety / correctness in error path. Stale PTE pointing
  at an invalidated folio → `fault_dirty_shared_page()` reaches
  `__folio_mark_dirty()` with `!uptodate && !dirty`, firing a KERNEL
  WARN. It is a bug (WARN = kernel bug signal to syzbot) and also opens
  the door to suspicious follow-on state (dirty bits on a folio the
  filesystem has already written off).

**Step 2.4 — Fix quality**
- Record: Obvious and correct. `unmap_mapping_pages()` is the standard
  MM helper for exactly this purpose (used by truncate_pagecache,
  `filemap_fault` race handling, etc.). It runs only under
  `invalidate=true` — i.e., only on the writeback-failure path — so the
  runtime cost in the non-error case is zero. Very low regression risk:
  the worst case is forcing future access to re-fault, which is benign.

## PHASE 3: GIT HISTORY INVESTIGATION

**Step 3.1 — Blame**
- Record: The surrounding construct (`if (folio_mapped())
  folio_clear_dirty_for_io(...)`, then `block_invalidate_folio` +
  `folio_clear_uptodate`) was added by commit `4e800c0359d9a` ("ext4:
  bugfix for mmaped pages in mpage_release_unused_pages()"), released in
  v4.9-rc1 (2016). So the incomplete handling has existed since v4.9 —
  every current stable tree is affected.

**Step 3.2 — Fixes: tag**
- Record: No `Fixes:` tag is in the commit (expected — this is a
  candidate under review). The bug is logically introduced by
  `4e800c0359d9a` (v4.9), which is present in every active stable tree.

**Step 3.3 — File history**
- Record: Recent touches to `mpage_release_unused_pages()` include
  `d8be7607de039` (ext4: Move mpage_page_done() calls after error
  handling), `fb5a5be05fb45` (convert to filemap_get_folios),
  `a297b2fcee461` (unlock unused_pages timely). None address this
  specific stale-PTE issue. This patch is self-contained; not part of a
  series.

**Step 3.4 — Author**
- Record: `Deepanshu Kartikey` is a regular syzbot-driven contributor
  (many small fixes across ext4, gfs2, netfs, mac80211). Not the
  maintainer, but the commit was reviewed and applied by ext4 maintainer
  Theodore Ts'o.

**Step 3.5 — Dependencies**
- Record: Only depends on `unmap_mapping_pages()`, which exists since
  v4.16 (mm commit `977fbdcd5986c`) — verified present in every stable
  tree checked (5.10, 5.15, 6.1, 6.6, 6.12). No patch-series dependency.

## PHASE 4: MAILING LIST AND EXTERNAL RESEARCH

**Step 4.1 — Original submission**
- Record: `b4 dig -c 9b25f381...` resolved to the v3 thread at `https://
  lore.kernel.org/all/20251205055914.1393799-1-kartikey406@gmail.com/`.
  `b4 dig -a` shows this is v3 (earlier attempts v1/v2 tried to fix it
  in `ext4_page_mkwrite()` — see syzbot Discussions table linking
  `20251122015742.362444-1-...` and `20251121131305.332698-1-...`). The
  v3 approach was suggested by Matthew Wilcox and preferred by Ted Ts'o.
  Ted applied v3 directly with "Applied, thanks!" (mbox saved by b4
  shows `commit: 9b25f381de6b...`).

**Step 4.2 — Reviewers**
- Record: To/Cc from `b4 dig -w` includes `tytso@mit.edu` (ext4
  maintainer — applied), `adilger.kernel@dilger.ca` (ext4 co-
  maintainer), `willy@infradead.org` (MM maintainer — suggested the
  fix), `djwong@kernel.org`, `yi.zhang@huaweicloud.com`, `linux-
  ext4@vger.kernel.org`, `linux-kernel@vger.kernel.org`. Appropriate
  audience reviewed the change.

**Step 4.3 — Bug report**
- Record: Fetched
  https://syzkaller.appspot.com/bug?extid=b0a0670332b6b3230a0a. Syzbot
  has a C reproducer. First crash 254 days before fetch, last 5d ago.
  Label `Fix commit: 9b25f381de6b` confirms this commit closed the
  upstream bug. The sample crash shows `__folio_mark_dirty` WARN with
  call trace `block_dirty_folio → fault_dirty_shared_page → do_wp_page →
  handle_mm_fault → do_user_addr_fault` — exact match to the commit
  message. Linux-6.6 has a sibling report labeled `origin:lts-only` and
  linux-6.1 one labeled `missing-backport`, indicating stable trees
  still need a fix.

**Step 4.4 — Related patches**
- Record: This is a single-patch series (v3); v1/v2 were alternative
  approaches to the same bug, superseded. No dependent patches.

**Step 4.5 — Stable ML**
- Record: No explicit Cc: stable in the applied patch. Syzbot label
  `missing-backport` on 6.1 is effectively a public request for stable
  coverage of this bug.

## PHASE 5: CODE SEMANTIC ANALYSIS

**Step 5.1 — Functions in diff**
- Record: Only `mpage_release_unused_pages()` is modified.

**Step 5.2 — Callers**
- Record: Two call sites in `ext4_do_writepages()`:
  `mpage_release_unused_pages(mpd, false)` (normal completion, no
  invalidate) and `mpage_release_unused_pages(mpd, give_up_on_write)`
  (error path). The fix only triggers on the second (writeback-failure)
  path.

**Step 5.3 — Callees**
- Record: After fix adds `unmap_mapping_pages(folio->mapping,
  folio->index, folio_nr_pages(folio), false)` — standard MM helper that
  tears down PTEs for the given pgoff range (non-even-cows). Existing
  callees: `folio_clear_dirty_for_io`, `block_invalidate_folio`,
  `folio_clear_uptodate`, `folio_unlock`.

**Step 5.4 — Call chain / reachability**
- Record: `ext4_do_writepages` is called from the ordinary writeback
  path (syscalls such as `fsync`, `sync`, `msync`, memory-pressure-
  driven writeback). The `give_up_on_write=true` branch is taken when
  `ext4_map_blocks()` returns an error — e.g., on corruption detected by
  the extent tree. So an unprivileged user with a mmap of a corrupt ext4
  image can trigger it, which is exactly what syzbot does.

**Step 5.5 — Similar patterns**
- Record: Related earlier fix in the same function — commit
  `4e800c0359d9a` from 2016 — covered the PTE-dirty bit but not the PTE
  itself. The new patch completes that earlier partial fix. The same
  philosophy (unmap before invalidating) is used by
  `truncate_inode_pages_range()` and `invalidate_inode_pages2_range()`
  in mm/truncate.c, so this brings ext4 in line with the mm convention.

## PHASE 6: CROSS-REFERENCING AND STABLE TREE ANALYSIS

**Step 6.1 — Code exists in stable**
- Record: Verified the vulnerable pattern exists:
  - `stable/linux-6.19.y`: `folio_mapped(folio) →
    folio_clear_dirty_for_io` without unmap ✓
  - `stable/linux-6.18.y`: same ✓
  - `stable/linux-6.17.y`: same ✓
  - `stable/linux-6.12.y`: same ✓
  - `stable/linux-6.6.y`: same ✓
  - `stable/linux-6.1.y`: same ✓
  - `stable/linux-5.15.y`, `5.10.y`: same logic but pre-folio
    (`page_mapped(page) → clear_page_dirty_for_io`) — needs port to page
    API.

**Step 6.2 — Backport complications**
- Record: For 6.1..6.19 the hunk is effectively identical and should
  apply cleanly or with trivial offsets. For 5.15/5.10, the patch must
  be re-expressed using `unmap_mapping_pages(page->mapping, page->index,
  compound_nr(page), false)` or `1` for non-compound.
  `unmap_mapping_pages()` itself is available since v4.16, so available
  in all these trees.

**Step 6.3 — Already fixed?**
- Record: `git log --grep="unmap invalidated folios"` in
  `stable/linux-6.1/6.6/6.12/6.17/6.18/6.19` returned nothing. Not yet
  backported.

## PHASE 7: SUBSYSTEM AND MAINTAINER CONTEXT

**Step 7.1 — Subsystem**
- Record: `fs/ext4/` — one of the most widely deployed filesystems.
  Criticality: IMPORTANT (affects a large population of users,
  especially enterprise and Android).

**Step 7.2 — Activity**
- Record: ext4/inode.c is very actively maintained; the specific
  `mpage_release_unused_pages()` function has had targeted fixes before
  (2016, 2024). Writeback error path is exercised any time delayed
  allocation fails.

## PHASE 8: IMPACT AND RISK ASSESSMENT

**Step 8.1 — Affected users**
- Record: Any user of ext4 who has a mmapped file where delayed block
  allocation fails (FS corruption, ENOSPC under certain delalloc
  conditions, etc.). Unprivileged users can trigger it with a
  crafted/corrupt image (syzbot proved this).

**Step 8.2 — Trigger conditions**
- Record: Mmap a file on ext4, dirty it, then force writeback to fail
  (syzbot does this with a corrupt FS image). A concrete C reproducer
  exists and still crashes unpatched 6.6.y as of ~5 days ago.

**Step 8.3 — Failure mode / severity**
- Record: Kernel WARN (`WARN_ON_ONCE(warn && !uptodate && !dirty)`),
  plus the page stays accessible via stale PTEs after invalidation. On
  systems with `panic_on_warn`, this is a kernel panic (DoS). Even
  without panic_on_warn, the invariant violation signals a genuine
  state-machine bug and can mislead subsequent writeback/truncate logic.
  Severity: MEDIUM-HIGH (WARN / potential DoS / invariant violation; a
  security-relevant WARN class that syzbot tracks specifically).

**Step 8.4 — Risk-benefit**
- Record: Benefit — closes a syzbot-tracked bug with public C
  reproducer, stops WARN/panic on corrupt FS workloads, on a core
  filesystem. Risk — fix is 15 lines, only executes in the writeback-
  error path, uses a well-understood MM API, reviewed by MM + ext4
  maintainers, and has syzbot `Tested-by`. Ratio strongly favors
  backporting.

## PHASE 9: SYNTHESIS

**Step 9.1 — Evidence**
- For: syzbot-reported + reproducible, WARN on a core filesystem, tiny
  surgical change, reviewed by the MM maintainer who suggested it and
  applied by the ext4 maintainer, Tested-by syzbot, the buggy code
  exists in every active stable tree, sibling syzbot reports on 6.1.y
  and 6.6.y explicitly labeled `missing-backport` / `origin:lts-only`,
  dependency `unmap_mapping_pages()` is present since v4.16.
- Against: No `Cc: stable` tag (not a decisive signal — this is
  precisely why it's under review). For 5.10/5.15 a minor port from
  folio to page API is needed.
- Unresolved: None that would flip the decision.

**Step 9.2 — Stable rules checklist**
1. Obviously correct and tested — YES (syzbot Tested-by; straightforward
   mm API use).
2. Fixes a real bug — YES (WARN with C reproducer).
3. Important issue — YES (kernel WARN/potential panic, invariant
   violation in core filesystem).
4. Small and contained — YES (+15/-1 in one function, error path only).
5. No new features — YES.
6. Can apply to stable — YES for 6.1+ cleanly; minor hand-port for
   5.15/5.10.

**Step 9.3 — Exception category**
- Not a device-ID/quirk/DT case; this is a straightforward bug fix that
  qualifies on its own merits.

**Step 9.4 — Decision**
- Backport.

## Verification
- [Phase 1] Parsed tags from the commit body: `Reported-by:
  syzbot+b0a0670332b6b3230a0a@...`, `Tested-by: syzbot+...`, `Suggested-
  by: Matthew Wilcox`, `Signed-off-by: Theodore Ts'o`, `Closes:
  syzkaller URL`. No Fixes: or Cc: stable (expected for candidates).
- [Phase 2] Ran `git show 9b25f381de6b...` — confirmed diff is +15/-1 in
  `fs/ext4/inode.c`, only inside `mpage_release_unused_pages()`'s `if
  (invalidate)` block, adds `unmap_mapping_pages(folio->mapping,
  folio->index, folio_nr_pages(folio), false)`.
- [Phase 3] `git log --oneline --grep="mpage_release_unused_pages"` —
  found 8 historical touches including the 2016 partial fix
  `4e800c0359d9a` ("ext4: bugfix for mmaped pages..."). `git describe
  --contains 4e800c0359d9a` → v4.9-rc1 — confirms the vulnerable
  construct has been in stable trees since v4.9.
- [Phase 3] Confirmed no Fixes: tag in commit; logical predecessor is
  `4e800c0359d9a`.
- [Phase 3] `git log --author="Deepanshu Kartikey"` — author is a
  syzbot-focused contributor with many accepted small fixes across
  subsystems.
- [Phase 4] `b4 dig -c 9b25f381de6b...` returned the v3 submission URL `
  https://lore.kernel.org/all/20251205055914.1393799-1-
  kartikey406@gmail.com/`.
- [Phase 4] `b4 dig -c ... -a` showed this is v3; earlier v1/v2 took a
  different (rejected) approach in `ext4_page_mkwrite()`.
- [Phase 4] `b4 dig -c ... -w` confirmed willy, tytso, adilger, djwong,
  yi.zhang, linux-ext4 were CC'd and reviewed.
- [Phase 4] `b4 dig -c ... -m` and read the mbox — Ted Ts'o applied v3
  with "Applied, thanks!", commit `9b25f381de6b`.
- [Phase 4] Fetched syzkaller URL — confirmed public C reproducer, `Fix
  commit: 9b25f381de6b`, still first-crashed 254 days ago and last seen
  5 days ago on unpatched trees. Sibling bugs `a92b613efd5e` (linux-6.1,
  label `missing-backport`) and `d429f1fb4bc9` (linux-6.6, label
  `origin:lts-only`) indicate stable trees still need the fix.
- [Phase 5] Manually traced: only two call sites in
  `ext4_do_writepages()`, the patched branch only hits the
  `give_up_on_write` error path. Confirmed `unmap_mapping_pages` is used
  elsewhere in mm/ with same pattern (truncate/invalidate).
- [Phase 6] Read `fs/ext4/inode.c` in stable/linux-6.1.y, 6.6.y, 6.12.y,
  6.17.y, 6.18.y, 6.19.y — all contain the unchanged vulnerable `if
  (folio_mapped(folio)) folio_clear_dirty_for_io(folio);
  block_invalidate_folio(...); folio_clear_uptodate(...);` pattern. Read
  5.10.y/5.15.y — contain the pre-folio `page_mapped(page) /
  clear_page_dirty_for_io / block_invalidatepage / ClearPageUptodate`
  pattern (needs small port).
- [Phase 6] `git log stable/linux-6.{1,6,12,17,18,19}.y --grep="unmap
  invalidated folios"` — none contain this fix yet.
- [Phase 6] Confirmed `unmap_mapping_pages` is defined in `mm/memory.c`
  of every stable tree checked; `git describe --contains 977fbdcd5986c`
  = v4.16-rc1.
- [Phase 7] `fs/ext4/` is a core, widely-used filesystem;
  `mpage_release_unused_pages()` touched by multiple targeted fixes over
  the years.
- [Phase 8] Failure mode verified from the sample syzbot crash:
  `block_dirty_folio → fault_dirty_shared_page → do_wp_page →
  handle_mm_fault → exc_page_fault`, exactly matching the commit
  message's description.
- UNVERIFIED: I did not attempt an actual patch apply against
  5.10.y/5.15.y — expect a minor manual port (folio→page API), not a
  semantic change.

The fix is small, surgical, obviously correct, syzbot-reported with a
reproducer, reviewed by ext4+MM maintainers, touches only the writeback-
error path, and the vulnerable code pattern is present in every active
stable tree. It satisfies all stable-kernel rules with very low
regression risk.

**YES**

 fs/ext4/inode.c | 16 +++++++++++++++-
 1 file changed, 15 insertions(+), 1 deletion(-)

diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 1123d995494b5..025ea8f0c41bd 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -1757,8 +1757,22 @@ static void mpage_release_unused_pages(struct mpage_da_data *mpd,
 			BUG_ON(!folio_test_locked(folio));
 			BUG_ON(folio_test_writeback(folio));
 			if (invalidate) {
-				if (folio_mapped(folio))
+				if (folio_mapped(folio)) {
 					folio_clear_dirty_for_io(folio);
+					/*
+					 * Unmap folio from page
+					 * tables to prevent
+					 * subsequent accesses through
+					 * stale PTEs. This ensures
+					 * future accesses trigger new
+					 * page faults rather than
+					 * reusing the invalidated
+					 * folio.
+					 */
+					unmap_mapping_pages(folio->mapping,
+						folio->index,
+						folio_nr_pages(folio), false);
+				}
 				block_invalidate_folio(folio, 0,
 						folio_size(folio));
 				folio_clear_uptodate(folio);
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2026-04-20 13:33 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
     [not found] <20260420132314.1023554-1-sashal@kernel.org>
2026-04-20 13:17 ` [PATCH AUTOSEL 7.0-6.6] ext2: avoid drop_nlink() during unlink of zero-nlink inode in ext2_unlink() Sasha Levin
2026-04-20 13:18 ` [PATCH AUTOSEL 7.0-5.10] ext2: replace BUG_ON with WARN_ON_ONCE in ext2_get_blocks Sasha Levin
2026-04-20 13:22 ` [PATCH AUTOSEL 7.0-6.1] ext4: unmap invalidated folios from page tables in mpage_release_unused_pages() Sasha Levin

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox