Linux EXT4 FS development
 help / color / mirror / Atom feed
From: Sasha Levin <sashal@kernel.org>
To: patches@lists.linux.dev, stable@vger.kernel.org
Cc: Zhang Yi <yi.zhang@huawei.com>, Jan Kara <jack@suse.cz>,
	Theodore Ts'o <tytso@mit.edu>, Sasha Levin <sashal@kernel.org>,
	linux-ext4@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: [PATCH AUTOSEL 6.18-5.10] ext4: check return value of ext4_get_block() in ext4_load_tail_bh()
Date: Mon, 19 Oct 2026 09:47:12 -0400	[thread overview]
Message-ID: <20261019135441.1420968-141-sashal@kernel.org> (raw)
In-Reply-To: <20261019135441.1420968-1-sashal@kernel.org>

From: Zhang Yi <yi.zhang@huawei.com>

[ Upstream commit 705a3fd3bac167f1dabe3d3bba46cd484c5528dd ]

ext4_load_tail_bh() ignores the return value of ext4_get_block(), so an
I/O or allocation failure is silently discarded. buffer_mapped(bh) stays
false and the function returns NULL, which callers such as
ext4_block_do_zero_range() treat as "nothing to do" and return success.
This can mask real failures during zero-range, truncate, or punch-hole
operations, potentially exposing stale data if the block was not
actually a hole and needed zeroing. So propagate the error to the
callers.

Signed-off-by: Zhang Yi <yi.zhang@huawei.com>
Reviewed-by: Jan Kara <jack@suse.cz>
Link: https://patch.msgid.link/20260714080044.4038124-4-yi.zhang@huaweicloud.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:

## Local tree

This checkout is **v7.3-rc4** (`7.3.0-rc4`, HEAD `f0100363d8c37`).
`ext4_load_tail_bh()` exists here (since v7.1-rc1). The candidate
`705a3fd3bac16` is already an ancestor of HEAD (merged in v7.3-rc1). The
verdict is whether this fix belongs in **this** tree on technical merit,
not because it was already applied.

---

## Phase 1: Commit message forensics

**Step 1.1 — Subject**
- Record: **[ext4] [check] check return value of `ext4_get_block()` in
  `ext4_load_tail_bh()`**
- Verb “check” here means propagate a previously ignored error.

**Step 1.2 — Tags**
- Record:
  - `Signed-off-by: Zhang Yi <yi.zhang@huawei.com>` (author; ext4
    reviewer in MAINTAINERS)
  - `Reviewed-by: Jan Kara <jack@suse.cz>` (ext4 reviewer)
  - `Link: https://patch.msgid.link/20260714080044.4038124-4-
    yi.zhang@huaweicloud.com` (v4 3/9)
  - `Signed-off-by: Theodore Ts'o <tytso@mit.edu>` (ext4 maintainer)
  - No `Fixes:`, no `Cc: stable`, no `Reported-by`, no syzbot. Absence
    of those tags is expected for this review.

**Step 1.3 — Body**
- Record: **Bug:** `ext4_load_tail_bh()` ignores `ext4_get_block()`
  errors. On failure `buffer_mapped(bh)` stays false, the helper returns
  `NULL`, and callers such as `ext4_block_do_zero_range()` treat that as
  “hole / nothing to do” and return success.
- **Symptom:** Silent success of zero-range, truncate, or punch-hole
  while the tail block was not zeroed.
- **Failure mode:** Stale data can later be exposed if the block was not
  a hole.
- **Root cause:** Error conflated with “unmapped hole”.
- **No version range** in the message.

**Step 1.4 — Hidden bug fix?**
- Record: **Not hidden.** It is an explicit error-propagation / stale-
  data fix. Cover letter later confirms patches 2 and 3 were “pre-
  existing fixes … originally going to be sent separately,” then folded
  into the WRITE_ZEROES series.

---

## Phase 2: Diff analysis

**Step 2.1 — Inventory**
- Record: **One file**, `fs/ext4/inode.c`: **+3 / −1**. One function:
  `ext4_load_tail_bh()`. Surgical, single-file.

**Step 2.2 — Flow**
- Record: **Before:** unmapped bh → `ext4_get_block(..., create=0)`
  return ignored → if still unmapped, `goto unlock` with `err == 0` →
  `return NULL`.
- **After:** negative return sets `err` and `goto unlock` → existing
  `return err ? ERR_PTR(err) : NULL`.
- Path: mapping lookup on the tail block during partial-block zeroing
  (error path only).

**Step 2.3 — Bug mechanism**
- Record: **Logic / correctness (ignored error → false success).**
  Category closest to “return value checking / missing error handling.”
  Not UAF, leak, or race.
- Mechanism: I/O or allocation failure from `ext4_get_block()` is
  treated as a hole, so truncate/punch/zero-range skip tail zeroing and
  still succeed.

**Step 2.4 — Fix quality**
- Record: **Obviously correct.** Callers already do `IS_ERR_OR_NULL(bh)`
  / `PTR_ERR_OR_ZERO(bh)` (present since the helper was factored out).
  Unlock/put already runs on the `unlock` path. Regression risk is low
  **in this tree** because parent `2fff82f081401` skips inline-data EOF
  zeroing (otherwise append writes on inline files could see `-ERANGE`).
  No API change.

---

## Phase 3: Git history

**Step 3.1 — Blame**
- Record: Ignored `ext4_get_block()` call dates to **`d863dc3614e489`
  (2013, Lukas Czerner)** inside `__ext4_block_zero_page_range`. Helper
  **`ext4_load_tail_bh`** was factored out in **`3b312a6f510ca` (“ext4:
  factor out journalled block zeroing range”, Zhang Yi, 2026-03)** and
  kept the same ignored return. First in this lineage: **v7.1-rc1**.

**Step 3.2 — Fixes: tag**
- Record: **None.** Introducing commit for the helper is
  `3b312a6f510ca`, ancestor of this HEAD. The ignored-return bug is
  older than the helper.

**Step 3.3 — File history**
- Record: Same v4 series around this commit:
  1. `e1cf7b5f5c8ad` FGP_WRITEBEGIN
  2. `2fff82f081401` skip inline-data tail zeroing **(prerequisite)**
  3. **`705a3fd3bac16` this fix**
  4. `b16e9d27a643a` move partial zeroing earlier
  5. `012e6a2c9ff4d` comment-only return-semantics clarification
  6–9. WRITE_ZEROES edge handling (some of those have `Cc: stable`)
- This patch is **standalone** given (2). (5) is comments only; 6–9 are
  WRITE_ZEROES, not required for this error check.

**Step 3.4 — Author**
- Record: **Zhang Yi** is an ext4 **Reviewer** in MAINTAINERS; 178 ext4
  commits in this tree. Not a drive-by.

**Step 3.5 — Dependencies**
- Record: Needs `ext4_load_tail_bh()` + `ERR_PTR` return +
  `IS_ERR_OR_NULL` callers — all present since `3b312a6f510ca` (v7.1).
  Needs **`2fff82f081401`** so inline-data append does not start
  failing. Both are in this tree. **Applies standalone on top of that
  parent.**

---

## Phase 4: Mailing list

**Step 4.1 — Original discussion**
- Record: `b4 dig -c 705a3fd3bac16` → **https://patch.msgid.link/2026071
  4080044.4038124-4-yi.zhang@huaweicloud.com**
- Series: **[PATCH v4 0/9] ext4: fix unaligned edge handling in
  FALLOC_FL_WRITE_ZEROES**
- Revisions: v1 → v4. v4 vs v3: **no code change** on this patch; v3
  collected Jan’s R-b.
- Cover letter: patches 2 and 3 were **pre-existing fixes folded in**,
  not WRITE_ZEROES-only prep.
- No NAK found on this patch. Ted merged it.

**Step 4.2 — Recipients (`b4 dig -w`)**
- Record: `linux-ext4`, `linux-fsdevel`, **tytso@mit.edu**,
  **jack@suse.cz**, adilger, libaokun, ojaswin, ritesh.list —
  maintainers/reviewers were on Cc. Jan reviewed; Ted committed.

**Step 4.3 — Bug report**
- Record: **No external bugzilla/syzbot report.** Issue found by code
  inspection (author). Severity is argued in the commit: silent success
  + possible stale data.

**Step 4.4 — Series**
- Record: 9 patches. This one is independently useful. Sibling patches
  7–9 carry `Cc: stable` for WRITE_ZEROES; this one does not. That is
  **not** used as a NO vote (tags are expected to be absent here).

**Step 4.5 — stable@**
- Record: **UNVERIFIED.** lore.kernel.org WebFetch returned 403; no
  separate stable-thread confirmation. `b4 dig` matched the ext4 thread.

---

## Phase 5: Semantic / call graph

**Step 5.1 — Functions**
- Record: **`ext4_load_tail_bh`** only.

**Step 5.2 — Callers**
- Record (this tree):
  - `ext4_block_do_zero_range()` → `IS_ERR_OR_NULL` / `PTR_ERR_OR_ZERO`
  - `ext4_block_journalled_zero_range()` → same
  - via `ext4_block_zero_range()`:
    - `ext4_block_zero_eof()` — truncate (`ext4_truncate`), setattr
      grow, append (`ext4_write_checks` in `fs/ext4/file.c`)
    - `ext4_zero_partial_blocks()` — punch-hole (`ext4_punch_hole`),
      `ext4_zero_range()` / WRITE_ZEROES in `fs/ext4/extents.c`

**Step 5.3 — Callees**
- Record: `__filemap_get_folio`, `ext4_get_block` (lookup only),
  `ext4_read_bh_lock`, folio lock/put.

**Step 5.4 — Reachability**
- Record: **Yes, from userspace:** `truncate`/`ftruncate`, `fallocate`
  (punch/zero/WRITE_ZEROES), append `write`. Unprivileged processes on
  their own files can hit the path. The **error** itself needs
  `ext4_get_block()` failure (I/O error, ENOMEM, corruption), not a
  normal happy path.

**Step 5.5 — Similar patterns**
- Record: Same ignored return lived in `__ext4_block_zero_page_range`
  since 2013. No second copy of this exact bug in current
  `ext4_load_tail_bh`.

---

## Phase 6: This checkout

**Step 6.1 — Buggy code here?**
- Record: **Yes.** Helper is in `fs/ext4/inode.c` (~4033). Introduced
  **v7.1-rc1** (`3b312a6f510ca`). This 7.3-rc4 tree has that code. The
  ignored-return bug existed until `705a3fd3bac16` (v7.3-rc1).

**Step 6.2 — Backport difficulty**
- Record: **Clean apply** onto the parent in this tree
  (`2fff82f081401`). Three lines. Later `b4e409c18c797` removed fs-layer
  fscrypt decrypt from this function; that is after/around the same
  merge window and does not change the get_block check.

**Step 6.3 — Related fixes already here?**
- Record: Prerequisite `2fff82f081401` is in HEAD. Follow-up
  `012e6a2c9ff4d` is comments only. No competing alternate fix.

---

## Phase 7: Subsystem

**Step 7.1**
- Record: **ext4 (`fs/ext4`)** — **IMPORTANT** (default Linux
  filesystem; data integrity).

**Step 7.2**
- Record: **Active** in 7.3 (WRITE_ZEROES series, iomap, fastcommit,
  etc.). Helper itself is recent (7.1) but the ignored-return pattern is
  old.

---

## Phase 8: Impact and risk

**Step 8.1 — Who**
- Record: **Filesystem-specific:** ext4 users doing truncate, punch-
  hole, zero-range, append, WRITE_ZEROES.

**Step 8.2 — Trigger**
- Record: Partial-block zeroing **plus** `ext4_get_block()` failure.
  Uncommon (I/O error / ENOMEM / bad metadata). Unprivileged users can
  invoke the operations; they cannot usually force get_block failure
  except under memory/IO pressure.

**Step 8.3 — Failure mode**
- Record: **Stale data / false success** on truncate/punch/zero-range.
  Not a panic. **Severity: HIGH** for a filesystem (classic “truncate
  succeeded but tail not zeroed → later extend exposes old data”). Not
  CRITICAL (no oops/deadlock).

**Step 8.4 — Ratio**
- Record: **Benefit: high** for ext4 data integrity on error paths.
  **Risk: very low** (3 lines, existing ERR_PTR path, Jan + Ted).
  Prerequisite for inline data is already in this tree.

---

## Phase 9: Synthesis

**Step 9.1 — Evidence**

**For:**
- Real correctness bug: mapping lookup failure reported as success.
- Concrete data-integrity outcome on truncate/punch/zero-range.
- Userspace-reachable (common ext4 ops).
- 3-line, obviously correct; callers already handle `ERR_PTR`.
- Reviewed by Jan Kara, merged by Ted Ts'o; author is an ext4 reviewer.
- Cover letter: intended as a standalone pre-existing fix.
- Buggy helper **is in this 7.3 tree** (since 7.1).
- Prerequisite inline-data skip **is in this tree**.

**Against:**
- Error-path only; no syzbot/user `Reported-by`.
- Bundled in a 9-patch WRITE_ZEROES series (mitigated: explicitly called
  out as a separate pre-existing fix).
- Trigger requires get_block failure (rare).

**Unresolved:** stable@ archive (lore 403); full v1 review text beyond
the on-commit `Reviewed-by`.

**Step 9.2 — Stable rules**
1. Obviously correct and tested? **PASS** (trivial check; R-b Jan; in
   7.3-rc1)
2. Real bug affecting users? **PASS** (false success + possible stale
   data)
3. Important (crash/security/corruption/deadlock)? **PASS** (stale data
   / integrity, HIGH)
4. Small and contained? **PASS** (+3/−1, one function)
5. No new features/APIs? **PASS**
6. Can apply to this tree? **PASS** (helper + callers + prerequisite
   present)

**Step 9.3 — Exceptions**
- Record: **None** (not a device ID/quirk/DT/build/doc exception).
  Ordinary bugfix.

**Step 9.4 — Decision**
This is a small, reviewed ext4 integrity fix for code that exists in
this 7.3-rc4 tree. Callers already propagate `ERR_PTR`. The inline-data
prerequisite is present, so the change does not turn append-on-inline-
data into `-ERANGE`. It meets stable criteria for **this** tree.

---

## Why it matters here

On this 7.3 tree, `ext4_load_tail_bh()` is the shared tail-zero helper
for truncate, punch-hole, fallocate zero/WRITE_ZEROES, and post-EOF
append. Ignoring `ext4_get_block()` makes a failed extent lookup look
like a hole, so those operations can succeed without zeroing a mapped
tail. Later grow/read can then expose stale bytes. Checking the return
and using the existing `ERR_PTR` path is the right, minimal fix.

Risk is low: three lines, no locking/API change, prerequisite
`2fff82f081401` already in this checkout.

---

## Verification

- **[Phase 1]** Parsed commit
  `705a3fd3bac167f1dabe3d3bba46cd484c5528dd`: Zhang Yi, R-b Jan Kara,
  SOB Ted Ts'o, Link msgid `…4038124-4…`. No Fixes/Cc:stable/Reported-
  by.
- **[Phase 2]** `git show 705a3fd3bac16`: +3/−1 in `ext4_load_tail_bh`;
  `err = ext4_get_block(...); if (err < 0) goto unlock`.
- **[Phase 2]** Confirmed unlock already returns `err ? ERR_PTR(err) :
  NULL` before the patch (`git show 705a3fd3bac16^:fs/ext4/inode.c`).
- **[Phase 2]** Callers at the fix: `IS_ERR_OR_NULL` / `PTR_ERR_OR_ZERO`
  (`git grep` on `705a3fd3bac16`).
- **[Phase 3]** `git blame 705a3fd3bac16^` around the call:
  `d863dc3614e489` (2013).
- **[Phase 3]** `git log -S ext4_load_tail_bh -- fs/ext4/inode.c` →
  `3b312a6f510ca`; `git describe --contains` → **v7.1-rc1**.
- **[Phase 3]** `git merge-base --is-ancestor 3b312a6f510ca HEAD` true;
  `705a3fd3bac16` ancestor of HEAD true; **not** in v7.2; **in**
  v7.3-rc1.
- **[Phase 3]** Parent `2fff82f081401` documents `-ERANGE` on inline
  data if this check is added without the skip; that skip is in HEAD.
- **[Phase 4]** `b4 dig -c 705a3fd3bac16` → v4 3/9 lore URL; `-a` showed
  v3 and v4; `-w` listed ext4 maintainers.
- **[Phase 4]** Cover letter (mbox): “Fold in two pre-existing fixes
  (now patches 2 and 3)”.
- **[Phase 4]** WebFetch lore/msgid.link: **403**. Stable-list search:
  **UNVERIFIED**.
- **[Phase 5]** Grep callers: `ext4_block_do_zero_range`,
  `ext4_block_journalled_zero_range` → `ext4_block_zero_eof` /
  `ext4_zero_partial_blocks` → truncate, setattr, punch-hole,
  zero_range, `file.c` append.
- **[Phase 5]** Truncate: `ext4_truncate` → `ext4_block_zero_eof`; on
  error `goto out_trace` (does **not** keep shrinking after a failed
  zero). Punch-hole/`ext4_zero_range` return the error.
- **[Phase 6]** `git describe HEAD` = `v7.3-rc4-27-gf0100363d8c37`;
  `make kernelversion` = `7.3.0-rc4`.
- **[Phase 6]** Buggy helper present in this tree since v7.1;
  prerequisite and this commit both ancestors of HEAD.
- **[Phase 7]** MAINTAINERS: Zhang Yi is `R:` on EXT4; Jan Kara is `R:`;
  Ted is `M:`.
- **[Phase 8]** Failure mode verified from commit text + truncate/punch
  callers, not from a live repro (no reproducer in the message).
- **UNVERIFIED:** Whether `ext4_get_block` can return an error while
  leaving `BH_Mapped` set (fix still correct: any `err < 0` is
  propagated). No live I/O-error repro.

The patch is a small, reviewed ext4 integrity fix for code that exists
in this 7.3 tree, with callers already wired for `ERR_PTR` and the
inline-data prerequisite present.

**YES**

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

diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 9a5c08650bd1e..2e3f7ba1a924b 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -4119,7 +4119,9 @@ static int __ext4_block_zero_page_range(handle_t *handle,
 	}
 	if (!buffer_mapped(bh)) {
 		BUFFER_TRACE(bh, "unmapped");
-		ext4_get_block(inode, iblock, bh, 0);
+		err = ext4_get_block(inode, iblock, bh, 0);
+		if (err < 0)
+			goto unlock;
 		/* unmapped? It's a hole - nothing to do */
 		if (!buffer_mapped(bh)) {
 			BUFFER_TRACE(bh, "still unmapped");
-- 
2.53.0


  parent reply	other threads:[~2026-10-19 13:58 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <20261019135441.1420968-1-sashal@kernel.org>
2026-10-19 13:44 ` [PATCH AUTOSEL 6.18-6.6] ext4: use FGP_WRITEBEGIN for tail block zeroing Sasha Levin
2026-10-19 14:04   ` sashiko-bot
2026-10-19 13:47 ` Sasha Levin [this message]
2026-10-19 14:32   ` [PATCH AUTOSEL 6.18-5.10] ext4: check return value of ext4_get_block() in ext4_load_tail_bh() sashiko-bot
2026-10-19 13:49 ` [PATCH AUTOSEL 6.18-5.10] ext4: guard against NULL s_group_info in ext4_get_group_info Sasha Levin
2026-10-19 14:53   ` sashiko-bot
2026-10-19 13:50 ` [PATCH AUTOSEL 6.18-5.10] ext4: set EXT4_STATE_NO_EXPAND in ext4_evict_inode Sasha Levin
2026-10-19 14:55   ` sashiko-bot
2026-10-19 13:51 ` [PATCH AUTOSEL 6.18-6.12] ext4: reject mount if clusters/inodes per group are not 8-aligned Sasha Levin
2026-10-19 15:06   ` sashiko-bot
2026-10-19 13:53 ` [PATCH AUTOSEL 6.18-5.10] ext4: reduce max cluster size to match documented 256MB limit Sasha Levin
2026-10-19 15:27   ` sashiko-bot

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261019135441.1420968-141-sashal@kernel.org \
    --to=sashal@kernel.org \
    --cc=jack@suse.cz \
    --cc=linux-ext4@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=patches@lists.linux.dev \
    --cc=stable@vger.kernel.org \
    --cc=tytso@mit.edu \
    --cc=yi.zhang@huawei.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox