* [PATCH AUTOSEL 7.0-5.15] fs/ntfs3: increase CLIENT_REC name field size
[not found] <20260428104133.2858589-1-sashal@kernel.org>
@ 2026-04-28 10:40 ` Sasha Levin
2026-04-28 10:40 ` [PATCH AUTOSEL 7.0-5.15] fs/ntfs3: fix potential double iput on d_make_root() failure Sasha Levin
` (4 subsequent siblings)
5 siblings, 0 replies; 6+ messages in thread
From: Sasha Levin @ 2026-04-28 10:40 UTC (permalink / raw)
To: patches, stable; +Cc: Konstantin Komarov, rtm, Sasha Levin, ntfs3, linux-kernel
From: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
[ Upstream commit 81ad9e67eccc0b094a6eef55a19ee56c761416dc ]
This patch increases the size of the CLIENT_REC name field from 32 utf-16
chars to 64 utf-16 chars. It fixes the buffer overflow problem in
log_replay() reported by Robbert Morris.
Reported-by: <rtm@csail.mit.edu>
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:
Based on my investigation, let me compose the full analysis.
---
## PHASE 1: COMMIT MESSAGE FORENSICS
**Step 1.1 - Subject line parsing:**
Record: Subsystem `fs/ntfs3`; action verb "increase" (which supports a
fix per body text); summary: enlarges CLIENT_REC.name to fix a buffer
overflow in log_replay().
**Step 1.2 - Tags:**
Record:
- `Reported-by: <rtm@csail.mit.edu>` — Robert (Robbert) Morris, MIT
CSAIL; a researcher who regularly reports filesystem memory-safety
bugs (he has multiple previous ntfs3 reports landed: `1b7dd28e14c47`
and `652cfeb43d6b9`, both credited to him).
- `Signed-off-by: Konstantin Komarov <...paragon-software.com>` — author
is the ntfs3 subsystem maintainer.
- No Fixes:, no Cc: stable (expected — that's why we review).
**Step 1.3 - Body analysis:**
Record: Bug description is explicit — "fixes the buffer overflow problem
in log_replay()". Failure mode: memory corruption when mounting a
malicious NTFS image (confirmed by KASAN). The on-disk Windows
`$LogFile` CLIENT_REC truly has `name[64]` (not 32); Linux's undersized
struct causes the size check in `is_rst_area_valid()` to under-reject a
crafted restart area.
**Step 1.4 - Hidden fix detection:**
Record: Not hidden — commit message openly says "buffer overflow". No
disguise.
---
## PHASE 2: DIFF ANALYSIS
**Step 2.1 - Inventory:**
Record: 1 file (`fs/ntfs3/fslog.c`), +2/-2 lines. Only `struct
CLIENT_REC` and the `static_assert` on its size change. Scope: single-
file surgical fix.
**Step 2.2 - Code-flow change:**
Record:
- Before: `__le16 name[32]` → `sizeof(CLIENT_REC) == 0x60`.
- After: `__le16 name[64]` → `sizeof(CLIENT_REC) == 0xa0`.
The struct is used (1) as the on-disk client record stride in
`$LogFile`, and (2) in the validator `is_rst_area_valid()` at
`fs/ntfs3/fslog.c:487`:
```464:529:fs/ntfs3/fslog.c
off = le16_to_cpu(ra->client_off);
if (!IS_ALIGNED(off, 8) || ro + off > SECTOR_SIZE -
sizeof(short))
return false;
off += cl * sizeof(struct CLIENT_REC);
if (off > sys_page)
return false;
...
if (le16_to_cpu(rhdr->ra_off) + le16_to_cpu(ra->ra_len) >
sys_page ||
off > le16_to_cpu(ra->ra_len)) {
return false;
}
```
With the new size, malformed RAs that pack `client_off + cl*0x60 <=
ra_len` but would still overflow the target buffer are now rejected
(`off = client_off + cl*0xa0 > ra_len`).
**Step 2.3 - Bug mechanism:**
Record: Memory-safety fix (h: struct size / validation, with (d)
downstream memory safety). Root cause: incorrect on-disk layout
definition caused `is_rst_area_valid()` to under-validate, letting a
crafted log file reach the overflowing `memcpy` in `log_replay()`:
```4033:4048:fs/ntfs3/fslog.c
t16 = le16_to_cpu(ra2->client_off);
if (t16 == offsetof(struct RESTART_AREA, clients)) {
memcpy(ra, ra2, log->ra_size);
} else {
memcpy(ra, ra2, offsetof(struct RESTART_AREA, clients));
memcpy(ra->clients, Add2Ptr(ra2, t16),
le16_to_cpu(ra2->ra_len) - t16);
```
Concretely (from the reporter's reproducer): attacker sets `ra_len=112,
client_off=16, log_clients=1`. Old size: `16 + 1*0x60 = 112` not `>
112`, check passes. Then the memcpy writes 64 + 96 = 160 bytes into a
112-byte `ra`, smashing 48 bytes past it. New size: `16 + 1*0xa0 = 176 >
112`, so `is_rst_area_valid()` rejects the RA; `ra2` becomes NULL
(`fslog.c:3885–3888`) and `log_replay()` returns `-EINVAL` at line
3919-3921 before the unsafe memcpy runs.
**Step 2.4 - Fix quality:**
Record: Very small, obviously correct structural change. Risk from the
fix itself is low: `is_rst_area_valid()` becomes *stricter*; the only
regression surface is rejecting a restart area formerly written by buggy
Linux ntfs3 (which had `ra_len = 0x40 + 0x60 = 0xa0`). But that path is
only hit when a log is *dirty* (`client_idx[1] != LFS_NO_CLIENT_LE`);
cleanly unmounted volumes skip the memcpy path entirely
(`fslog.c:3890-3916`). Windows-written logs already use 0xa0 records, so
the new size matches the genuine on-disk format. This is a net positive.
---
## PHASE 3: GIT HISTORY
**Step 3.1 - Blame:**
Record: The `name[32]` / `static_assert(... == 0x60)` lines were
introduced by `b46acd6a6a627 ("fs/ntfs3: Add NTFS journal")` authored
2021-08-13 by Konstantin Komarov. `git describe --contains` reports
`v5.15-rc1~94^2~29`, i.e. present since v5.15 (the very commit that
introduced ntfs3). Thus the bug affects every stable tree that ships
ntfs3.
**Step 3.2 - Fixes: tag:**
Record: No explicit Fixes: tag, but by content the bug traces to
`b46acd6a6a627` (v5.15).
**Step 3.3 - File history:**
Record: Other related security-hardening fixes in `fslog.c` from the
same reporter (`rtm@csail.mit.edu`): `1b7dd28e14c47 ("fs/ntfs3: Correct
function is_rst_area_valid")` and the journal-replay series. This commit
is standalone; not part of a dependent series (confirmed by `b4 dig -a`:
only v1, 1 patch).
**Step 3.4 - Author:**
Record: Konstantin Komarov is the ntfs3 maintainer; routinely lands
fixes for rtm's reports. Strong authority signal.
**Step 3.5 - Dependencies:**
Record: None. Diff only changes a struct and a static_assert. No new
functions referenced.
---
## PHASE 4: MAILING LIST RESEARCH
**Step 4.1 - b4 dig:**
Record:
- `b4 dig -c 81ad9e67eccc0` found: https://lore.kernel.org/all/202603191
35855.15200-1-almaz.alexandrovich@paragon-software.com/
- `b4 dig -c ... -a`: single revision (v1), applied as-is.
- Thread content (via mbox): plain PATCH, no review comments, went
straight into the maintainer tree.
**Step 4.2 - Recipients:**
Record (b4 dig -w): `ntfs3@lists.linux.dev`, `linux-
kernel@vger.kernel.org`, `linux-fsdevel@vger.kernel.org`,
`rtm@csail.mit.edu`. Right lists, right reporter.
**Step 4.3 - Bug report:**
Record: Found original report at
`https://lore.kernel.org/ntfs3/42774.1769379272@localhost/`
(2026-01-25). It is a detailed reproducer with:
- A downloadable crafted image
(`http://www.rtmrtm.org/rtm/ntfs30a.img.gz`)
- A KASAN SLUB trace: "Right Redzone overwritten ... BUG kmalloc-128
(Not tainted): Object corrupt"
- Exact pointer math matching `log_replay()`'s memcpy path.
Konstantin replied (Feb 9 2026) confirming he'd reproduce and fix. Patch
appeared ~6 weeks later.
**Step 4.4 - Series context:**
Record: Single-patch fix, not part of a larger series.
**Step 4.5 - Stable-specific discussion:**
Record: Not found. Also on Apr 20 2026, Konstantin's `[GIT PULL] ntfs3:
bugfixes for 7.1` explicitly lists this commit under **"Fixed: ...
increase CLIENT_REC name field size to prevent buffer overflow"** — the
maintainer classifies it as a bug fix.
---
## PHASE 5: CODE SEMANTIC ANALYSIS
**Step 5.1 - Key functions:** `struct CLIENT_REC` size (used by
`is_rst_area_valid`, `log_replay`, `log_create_ra`, array indexing
everywhere the stride appears).
**Step 5.2 - Callers / reachability:**
Record: `log_replay()` is invoked during `ntfs_fill_super()` mount path
(`fs/ntfs3/super.c`). Triggered on every `mount -t ntfs3` with a dirty
log. Trigger condition: need CAP_SYS_ADMIN (or equivalent) to mount, but
this is a common vector (auto-mount of USB drives, containers with mount
caps, etc.).
**Step 5.3 - Callees:** `is_rst_area_valid()` is called from
`log_read_rst()` before any RA data is trusted; with the new sizeof it
now rejects the pathological `client_off + cl*sizeof` combinations.
**Step 5.4 - Call chain:** `mount` → `ntfs_fill_super` → `log_replay` →
`log_read_rst` → `is_rst_area_valid` / memcpy path. Entirely reachable
from user-triggerable mount.
**Step 5.5 - Similar patterns:** Several sibling checks
(`is_client_area_valid`, `check_client_area`) also iterate with
`sizeof(CLIENT_REC)` stride; the fix corrects all of them simultaneously
because they all reference the same type.
---
## PHASE 6: STABLE TREES
**Step 6.1 - Code presence:**
Record: `git grep "name\[32\]" stable/linux-{5.15,6.1,6.6,6.12,6.19}.y
-- fs/ntfs3/fslog.c` confirms the buggy `__le16 name[32]` and
`static_assert(... == 0x60)` are present in every active stable tree.
ntfs3 was added in v5.15.
**Step 6.2 - Backport complications:**
Record: The 2-line struct edit applies cleanly to every stable branch. I
verified the surrounding struct definition is byte-identical across
5.15.y, 6.1.y, 6.6.y, 6.12.y, 6.19.y.
**Step 6.3 - Existing stable fix?**
Record: No prior fix for this bug exists in any stable branch (no
`CLIENT_REC` commits on any of the stable branches checked).
---
## PHASE 7: SUBSYSTEM CONTEXT
**Step 7.1 - Subsystem/criticality:**
Record: `fs/ntfs3` — IMPORTANT. Default NTFS driver since v5.15, used by
countless distros to mount Windows-format media (USB drives, external
disks, dual-boot partitions).
**Step 7.2 - Activity:**
Record: Active — many fixes land per cycle, and this commit is in the
7.1 pull request alongside ~16 other ntfs3 fixes.
---
## PHASE 8: IMPACT AND RISK
**Step 8.1 - Who is affected:**
Record: Anyone mounting an untrusted NTFS image on a kernel with ntfs3
compiled in. This includes desktop Linux, NAS devices, embedded systems,
containers with mount privileges, and auto-mount frameworks that honor
USB insertion.
**Step 8.2 - Trigger conditions:**
Record: A single `mount -t ntfs3` of a crafted image is sufficient.
Reporter supplied a working reproducer. Requires root/mount cap but this
is a standard attack surface for local privilege escalation via
malicious removable media.
**Step 8.3 - Failure mode:**
Record: CRITICAL — heap out-of-bounds write (48 bytes past a kmalloc-128
slab object, as KASAN demonstrates). Consequences range from kernel
panic to targeted memory corruption / potential privilege escalation
depending on what sits next to that slab.
**Step 8.4 - Risk/benefit:**
Record:
- Benefit: very high — closes a reachable heap buffer overflow with a
public reproducer in the default Linux NTFS driver.
- Risk: very low — two-line struct-size change; only potential side
effect is stricter validation that can reject RA headers previously
written by the same buggy Linux driver on dirty logs, which is far
better than memory corruption.
---
## PHASE 9: SYNTHESIS
**Step 9.1 - Evidence:**
FOR:
- KASAN-confirmed heap buffer overflow with a publicly reproducible
image.
- Reported by an external security researcher; acknowledged and fixed by
the subsystem maintainer.
- Bug exists in every active stable tree (5.15.y and later).
- Maintainer's pull request explicitly labels this as "prevent buffer
overflow."
- 2-line structural change, no dependencies.
AGAINST: None of substance.
UNRESOLVED: Whether kernels written with old Linux ntfs3 will fail to
replay a *dirty* log after upgrading. This edge case is (a) narrow
(dirty logs only) and (b) acceptable given the severity of the bug.
**Step 9.2 - Stable rules checklist:**
1. Obviously correct and tested: yes — trivial struct size change,
upstream-tested, matches Windows on-disk layout.
2. Real bug: yes — KASAN trace, public reproducer.
3. Important: yes — heap OOB via mount.
4. Small/contained: yes — +2/-2 in one file.
5. No new features: yes — corrects existing layout.
6. Applies to stable: yes — clean apply confirmed for 5.15.y through
6.19.y.
**Step 9.3 - Exception category:** Not needed; this meets core stable-
kernel-rules on its own merits (security / memory-corruption fix).
**Step 9.4 - Decision:** YES.
---
## Verification
- [Phase 1] Parsed tags from commit message: found `Reported-by:
rtm@csail.mit.edu`, `Signed-off-by: Konstantin Komarov`. Commit
message explicitly mentions "buffer overflow in log_replay()".
- [Phase 2] Diff: +2/-2, changes only `CLIENT_REC.name[32]→[64]` and
`static_assert(==0x60)→(==0xa0)` in `fs/ntfs3/fslog.c`.
- [Phase 2] Read `fs/ntfs3/fslog.c` lines 464-529 and 4026-4048 to trace
how the struct size feeds `is_rst_area_valid()` and the subsequent
memcpy; verified that the attacker-controlled `client_off` + `ra_len`
combination from the bug report passes the old check and fails the new
one.
- [Phase 3] `git describe --contains b46acd6a6a627` returned
`v5.15-rc1~94^2~29`, confirming the bug has been present since ntfs3
was introduced in v5.15.
- [Phase 3] `git log --grep="rtm@csail" -- fs/ntfs3` showed prior fixes
by the same maintainer for reports from this reporter
(`1b7dd28e14c47`, `652cfeb43d6b9`).
- [Phase 4] `b4 dig -c 81ad9e67eccc0` resolved to https://lore.kernel.or
g/all/20260319135855.15200-1-almaz.alexandrovich@paragon-
software.com/.
- [Phase 4] `b4 dig -c 81ad9e67eccc0 -a` shows only v1; patch applied
as-submitted.
- [Phase 4] `b4 dig -c 81ad9e67eccc0 -w` showed correct recipients:
ntfs3@lists.linux.dev, linux-fsdevel, linux-kernel, rtm.
- [Phase 4] Fetched and read original bug report at
`lore.kernel.org/ntfs3/42774.1769379272@localhost/raw`: KASAN "Right
Redzone overwritten" report, reproducer image URL, exact line-number
match with the memcpy overflow.
- [Phase 4] Fetched maintainer reply at
`lore.kernel.org/ntfs3/f9e6330b-.../raw` confirming acknowledgment
(Feb 9, 2026).
- [Phase 4] Fetched 7.1 pull request (`20260420150756.6058-1-...`):
commit listed under "Fixed: ... to prevent buffer overflow".
- [Phase 6] `git grep` on each active stable branch (5.15.y, 6.1.y,
6.6.y, 6.12.y, 6.19.y) confirmed all still have `name[32]` and `sizeof
== 0x60`.
- [Phase 6] `git log --grep="CLIENT_REC" -- fs/ntfs3/fslog.c` on each
stable branch returned empty — no prior fix in stable.
- [Phase 7] Confirmed `fs/ntfs3` is the default NTFS driver since v5.15
(same commit that introduced the bug).
- [Phase 8] Verified `log_replay()` is called from the mount path; the
`memcpy` overflow is reached on a *dirty* log with attacker-controlled
`client_off` and `ra_len`; reporter provided a working image.
- UNVERIFIED: I did not personally run the provided malicious image
against pre-fix and post-fix kernels to independently confirm
behavior; I relied on the KASAN trace, the pointer arithmetic in the
public source, and the maintainer's agreement.
This is a small, obviously correct memory-corruption fix with a public
reproducer and maintainer acknowledgement, affecting all stable trees
that carry ntfs3. Textbook stable material.
**YES**
fs/ntfs3/fslog.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/fs/ntfs3/fslog.c b/fs/ntfs3/fslog.c
index 272e452761436..10dbe9922bf13 100644
--- a/fs/ntfs3/fslog.c
+++ b/fs/ntfs3/fslog.c
@@ -45,10 +45,10 @@ struct CLIENT_REC {
__le16 seq_num; // 0x14:
u8 align[6]; // 0x16:
__le32 name_bytes; // 0x1C: In bytes.
- __le16 name[32]; // 0x20: Name of client.
+ __le16 name[64]; // 0x20: Name of client.
};
-static_assert(sizeof(struct CLIENT_REC) == 0x60);
+static_assert(sizeof(struct CLIENT_REC) == 0xa0);
/* Two copies of these will exist at the beginning of the log file */
struct RESTART_AREA {
--
2.53.0
^ permalink raw reply related [flat|nested] 6+ messages in thread* [PATCH AUTOSEL 7.0-5.15] fs/ntfs3: fix potential double iput on d_make_root() failure
[not found] <20260428104133.2858589-1-sashal@kernel.org>
2026-04-28 10:40 ` [PATCH AUTOSEL 7.0-5.15] fs/ntfs3: increase CLIENT_REC name field size Sasha Levin
@ 2026-04-28 10:40 ` Sasha Levin
2026-04-28 10:40 ` [PATCH AUTOSEL 7.0-5.15] ntfs3: reject inodes with zero non-DOS link count Sasha Levin
` (3 subsequent siblings)
5 siblings, 0 replies; 6+ messages in thread
From: Sasha Levin @ 2026-04-28 10:40 UTC (permalink / raw)
To: patches, stable
Cc: Zhan Xusheng, Zhan Xusheng, Konstantin Komarov, Sasha Levin,
ntfs3, linux-kernel
From: Zhan Xusheng <zhanxusheng1024@gmail.com>
[ Upstream commit d1062683bf6b560b31f287eb0ebde4841bc72376 ]
d_make_root() consumes the reference to the passed inode: it either
attaches it to the newly created dentry on success, or drops it via
iput() on failure.
In the error path, the code currently does:
sb->s_root = d_make_root(inode);
if (!sb->s_root)
goto put_inode_out;
which leads to a second iput(inode) in put_inode_out. This results in
a double iput and may trigger a use-after-free if the inode gets freed
after the first iput().
Fix this by jumping directly to the common cleanup path, avoiding the
extra iput(inode).
Signed-off-by: Zhan Xusheng <zhanxusheng@xiaomi.com>
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 enough information to complete a comprehensive analysis.
# Phase-by-Phase Analysis
## PHASE 1: COMMIT MESSAGE FORENSICS
**Step 1.1 — Subject line parsing**
Record: subsystem = `fs/ntfs3`; action verb = `fix`; summary = "fix
potential double iput on d_make_root() failure".
**Step 1.2 — All commit message tags**
Record:
- No `Fixes:` tag (absence is expected — reason for manual review).
- No `Reported-by:` / `Tested-by:` / `Reviewed-by:` / `Acked-by:`.
- No `Link:`.
- No `Cc: stable`.
- `Signed-off-by:` Zhan Xusheng (author) and Konstantin Komarov (ntfs3
maintainer who applied it).
**Step 1.3 — Commit body**
Record: Author describes the VFS contract for `d_make_root()` — it
consumes the inode reference on both success and failure (on failure it
calls `iput()` internally). The ntfs3 error path jumps to the
`put_inode_out` label, which calls `iput(inode)` again, giving a double
`iput`. Consequence: "may trigger a use-after-free if the inode gets
freed after the first iput()". Root cause is a misunderstanding of
`d_make_root()` semantics.
**Step 1.4 — Hidden bug fix detection**
Record: Not hidden — explicitly "fix". Author correctly identifies the
VFS-API contract violation.
## PHASE 2: DIFF ANALYSIS
**Step 2.1 — Inventory**
Record: Single file `fs/ntfs3/super.c`, one function
`ntfs_fill_super()`, one line changed (`goto put_inode_out` → `goto
out`). Net diff: 1 insertion, 1 deletion. Scope: surgical single-line
fix.
**Step 2.2 — Code flow change**
Record:
- BEFORE: if `d_make_root()` returns NULL, jump to `put_inode_out:`
which does `iput(inode);` then falls through to `out:`.
- AFTER: if `d_make_root()` returns NULL, jump directly to `out:`,
skipping the extra `iput()`.
**Step 2.3 — Bug mechanism**
Record: Category = (d) Memory safety / double-free. Verified
`d_make_root()` semantics against `fs/dcache.c`:
```2042:2054:fs/dcache.c
struct dentry *d_make_root(struct inode *root_inode)
{
struct dentry *res = NULL;
if (root_inode) {
res = d_alloc_anon(root_inode->i_sb);
if (res)
d_instantiate(res, root_inode);
else
iput(root_inode);
}
return res;
}
```
When `d_alloc_anon()` fails, `d_make_root()` calls `iput(root_inode)`
and returns NULL. The caller must not call `iput()` again. The ntfs3
code was violating this, producing a double `iput`.
**Step 2.4 — Fix quality**
Record: Obviously correct. Minimal. Zero regression risk: it only
removes an extra `iput()` that is already performed inside
`d_make_root()`. The only behavior change is replacing a guaranteed
double-`iput`/UAF with the correct single release.
## PHASE 3: GIT HISTORY
**Step 3.1 — Blame**
Record: The buggy `goto put_inode_out` on `d_make_root()` failure was
introduced in `9b75450d6c580` ("fs/ntfs3: Fix memory leak if fill_super
failed", 2021-09-28). `git describe --contains 9b75450d6c580` =
`v5.15-rc6~33^2~6`, i.e., the bug has been present since the very first
released kernel that contained ntfs3 (v5.15).
**Step 3.2 — Fixes: tag**
Record: No `Fixes:` tag in the commit, but via `git log -L` I confirmed
the buggy construct was introduced by `9b75450d6c580`. That commit is in
v5.15 and therefore exists in every currently maintained stable tree.
**Step 3.3 — Related changes in file**
Record: `put_inode_out` was specifically reintroduced around the same
timeframe and further exercised by `c1ca8ef0262b2` ("fs/ntfs3: Add null
pointer check for inode operations", 2022). Not part of a series.
Standalone fix.
**Step 3.4 — Author**
Record: Author Zhan Xusheng has several small fixes merged previously
(timers, staging, sched/fair). Not the ntfs3 maintainer, but the
maintainer (Konstantin Komarov) explicitly applied the patch.
**Step 3.5 — Dependencies**
Record: None. Single-line edit inside pre-existing error handling. Self-
contained.
## PHASE 4: MAILING LIST RESEARCH
**Step 4.1 — Original patch discussion**
Record: `b4 dig -c d1062683bf6b5` matched by patch-id to the single v1
submission at `https://lore.kernel.org/all/20260326091232.92760-1-
zhanxusheng@xiaomi.com/`. I downloaded the full thread to
`/tmp/ntfs3_double_iput.mbox` and read it.
Key review comments:
- **Al Viro** (VFS maintainer) responded on 2026-04-04 confirming the
mechanism and went further: "Matter of fact, the whole `put_inode_out`
should go; if you *ever* get an inode with NULL ->i_op, it's a bug."
Al did NOT NAK the patch — he pointed at an additional, deeper problem
that is outside the scope of this fix.
- **Konstantin Komarov** (ntfs3 maintainer) replied on 2026-04-07:
"Thanks for the patch. It was applied. I'm going to take a closer look
at this problem."
**Step 4.2 — Reviewers**
Record: `b4 dig -w` shows recipients: Konstantin Komarov (ntfs3
maintainer), linux-kernel, Zhan Xusheng. Review came from both the
subsystem maintainer and Al Viro (VFS maintainer).
**Step 4.3 — Bug report**
Record: No linked syzbot / bugzilla report — the bug was found by code
inspection of the `d_make_root()` contract.
**Step 4.4 — Related patches**
Record: `b4 dig -a` shows only v1 applied. Standalone, not part of a
multi-patch series.
**Step 4.5 — Stable mailing list**
Record: N/A (web access blocked by Anubis), but not needed — evidence
from the lore thread is conclusive.
## PHASE 5: CODE SEMANTIC ANALYSIS
**Step 5.1 — Key functions**
Record: Only `ntfs_fill_super()` is touched.
**Step 5.2 — Callers**
Record: `ntfs_fill_super()` is the standard
`fs_context_operations::get_tree` implementation, invoked through
`get_tree_bdev()`, ultimately reachable from the `mount(2)` /
`fsopen(2)` / `fsmount(2)` syscalls. Reachable from any privileged user
(CAP_SYS_ADMIN) that can mount NTFS, plus increasingly from unprivileged
contexts on some distro setups (e.g., mount helpers) and automounters
handling removable NTFS media.
**Step 5.3 — Callees**
Record: The affected branch is purely error-path cleanup:
`d_make_root()` → (fails) → `iput(inode)` was being called twice. No
locks held across the double `iput`.
**Step 5.4 — Reachability**
Record: The failure path requires `d_make_root()` → `d_alloc_anon()` to
return NULL, which happens under memory pressure (kmem allocation
failure). Real, not theoretical; any fuzzer that injects allocation
faults (fail_nth/FAULT_INJECTION) at mount time can hit it.
**Step 5.5 — Similar patterns**
Record: A semantically identical fix was already applied to erofs back
in 2019 (`94832d9399217` "staging: erofs: fix potential double iput in
erofs_read_super()"). This confirms the pattern as a recognized,
backport-worthy bug class.
## PHASE 6: STABLE TREE ANALYSIS
**Step 6.1 — Does the buggy code exist in stable?**
Record: Verified via `git show vTAG:fs/ntfs3/super.c` for v5.15, v6.1,
v6.6, v6.12 — the exact `goto put_inode_out;` pattern on `d_make_root()`
failure is present in all of them. ntfs3 did not exist before v5.15, so
no older stable tree is affected.
**Step 6.2 — Backport complications**
Record: The lines around the change are stable across the tags; the fix
is a 1-line modification to an unchanged region of code. Should apply
cleanly to all stable trees ≥5.15 with zero or trivial conflict
resolution.
**Step 6.3 — Related fixes already in stable?**
Record: None found. This is the first fix for this specific bug.
## PHASE 7: SUBSYSTEM CONTEXT
**Step 7.1 — Subsystem**
Record: `fs/ntfs3` = filesystem driver. Criticality = IMPORTANT (widely
used by distros for NTFS support on removable media and dual-boot
systems).
**Step 7.2 — Activity**
Record: Actively maintained (Konstantin Komarov regularly submits
patches, per `git log`).
## PHASE 8: IMPACT/RISK
**Step 8.1 — Affected users**
Record: Users mounting NTFS volumes with the ntfs3 driver under memory
pressure, plus any fault-injection testing of the mount path. Stable
users on 5.15.y / 6.1.y / 6.6.y / 6.12.y all affected.
**Step 8.2 — Trigger conditions**
Record: Requires `d_alloc_anon()` allocation failure during NTFS mount
(memory pressure or fault injection). Rare in typical use but certainly
reachable; also a reliable fuzzer target.
**Step 8.3 — Failure mode severity**
Record: Double `iput()` → `iput_final()` → `evict()` on an already-freed
inode → use-after-free / kernel crash / potential exploitation. Severity
= HIGH (memory safety bug in privileged mount path).
**Step 8.4 — Risk vs benefit**
Record: BENEFIT = eliminate a real UAF on a clean reachable error path;
also removes the only reviewer-identified memory-safety issue from that
code. RISK = essentially zero: a single label change that removes an
extra `iput()`, which is strictly correct per the VFS API contract.
Endorsed implicitly by Al Viro. Excellent ratio.
## PHASE 9: SYNTHESIS
**Step 9.1 — Evidence**
- FOR: real double-iput / UAF bug, present since v5.15 in all stable
trees, one-line surgical fix, correct per VFS contract (verified in
`fs/dcache.c`), applied by subsystem maintainer, acknowledged by VFS
maintainer Al Viro, same pattern previously backported for erofs,
reachable from the `mount(2)` syscall, no dependencies.
- AGAINST: No `Fixes:` tag (expected for review); no `Cc: stable` tag
(expected for review); trigger is allocation failure in
`d_alloc_anon()` (rare but real). None of these are technical reasons
to reject.
**Step 9.2 — Stable rules**
1. Obviously correct and tested: YES (trivially correct, applied
upstream, VFS-contract-conformant).
2. Fixes a real bug: YES (double `iput` → UAF).
3. Important issue: YES (memory safety).
4. Small and contained: YES (1 line).
5. No new features/APIs: YES.
6. Applies to stable trees: YES (verified the surrounding code is
identical in 5.15/6.1/6.6/6.12).
**Step 9.3 — Exception category**
Not needed — this is a standard bug fix under criterion #3 (serious
crash / memory safety).
**Step 9.4 — Decision**
All evidence supports backporting.
---
## Verification
- [Phase 1] Read commit message; no tags present other than two `Signed-
off-by:`. Verified no `Fixes:` / `Cc: stable` / `Reported-by:` tags.
- [Phase 2] Inspected the diff — 1 insertion, 1 deletion, one label
change in error path.
- [Phase 2] Verified `d_make_root()` consumes the inode reference on
failure by reading `fs/dcache.c:2042-2054` — it calls
`iput(root_inode)` when `d_alloc_anon()` returns NULL.
- [Phase 2] Read `fs/ntfs3/super.c:1670-1717` to confirm the actual
`put_inode_out:` label does `iput(inode);` unconditionally, proving
the double-iput.
- [Phase 3] `git log --oneline -S "put_inode_out" -- fs/ntfs3/super.c`
and `git log -L` on the changed code block identified `9b75450d6c580`
as the commit that introduced the buggy `goto put_inode_out`.
- [Phase 3] `git describe --contains 9b75450d6c580` = `v5.15-rc6~33^2~6`
→ bug lives in all stable trees from v5.15 onward.
- [Phase 3] `git describe --contains 82cae269cfa95` =
`v5.15-rc1~94^2~34` — confirms ntfs3 first appeared in v5.15 (no
earlier stable affected).
- [Phase 4] `b4 dig -c d1062683bf6b5` found single v1 submission at lore
URL `.../20260326091232.92760-1-zhanxusheng@xiaomi.com/`.
- [Phase 4] `b4 dig -c d1062683bf6b5 -a` confirmed only one series
revision (v1).
- [Phase 4] `b4 dig -c d1062683bf6b5 -w` confirmed original recipients
include maintainer Konstantin Komarov.
- [Phase 4] Read the downloaded mbox `/tmp/ntfs3_double_iput.mbox`:
found Al Viro (VFS maintainer) acknowledging the bug mechanism and
Konstantin Komarov confirming "It was applied".
- [Phase 5] Confirmed identical pattern was fixed for erofs in
`94832d9399217` — pre-existing backport precedent.
- [Phase 6] Verified the buggy `goto put_inode_out;` on `d_make_root()`
failure is literally present at v5.15, v6.1, v6.6, v6.12 by `git show
vTAG:fs/ntfs3/super.c` on each tag.
- [Phase 6] Confirmed fix applies cleanly — only a label change on an
unchanged surrounding block.
- [Phase 8] Failure mode verified: `iput()` twice on the same inode
after the first may have dropped the last reference → UAF. Severity
HIGH.
- UNVERIFIED: The exact frequency of `d_alloc_anon()` returning NULL in
the wild — not relevant to stable decision because fault-injection and
OOM paths can both trigger it.
---
## Decision rationale
This is a textbook stable candidate:
- 1-line, provably correct fix based directly on a well-known VFS API
contract.
- Addresses a double-`iput`/UAF memory safety bug reachable from the
`mount(2)` syscall.
- Bug present in every currently maintained stable tree (5.15.y, 6.1.y,
6.6.y, 6.12.y and newer).
- Validated by the ntfs3 maintainer (applied it) and confirmed by VFS
maintainer Al Viro.
- Identical pattern was previously treated as stable material in other
filesystems.
- Zero regression risk — it removes an extra `iput` that is already
performed by `d_make_root()`.
**YES**
fs/ntfs3/super.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/fs/ntfs3/super.c b/fs/ntfs3/super.c
index 174a7cb202a08..d0dad15076ca2 100644
--- a/fs/ntfs3/super.c
+++ b/fs/ntfs3/super.c
@@ -1673,7 +1673,7 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc)
sb->s_root = d_make_root(inode);
if (!sb->s_root) {
err = -ENOMEM;
- goto put_inode_out;
+ goto out;
}
if (boot2) {
--
2.53.0
^ permalink raw reply related [flat|nested] 6+ messages in thread* [PATCH AUTOSEL 7.0-5.15] ntfs3: reject inodes with zero non-DOS link count
[not found] <20260428104133.2858589-1-sashal@kernel.org>
2026-04-28 10:40 ` [PATCH AUTOSEL 7.0-5.15] fs/ntfs3: increase CLIENT_REC name field size Sasha Levin
2026-04-28 10:40 ` [PATCH AUTOSEL 7.0-5.15] fs/ntfs3: fix potential double iput on d_make_root() failure Sasha Levin
@ 2026-04-28 10:40 ` Sasha Levin
2026-04-28 10:40 ` [PATCH AUTOSEL 7.0-5.15] fs/ntfs3: fix $LXDEV xattr lookup Sasha Levin
` (2 subsequent siblings)
5 siblings, 0 replies; 6+ messages in thread
From: Sasha Levin @ 2026-04-28 10:40 UTC (permalink / raw)
To: patches, stable
Cc: Ziyi Guo, Konstantin Komarov, Sasha Levin, ntfs3, linux-kernel
From: Ziyi Guo <n7l8m4@u.northwestern.edu>
[ Upstream commit e10e72f69734a90c8719d160e8efb164ce5d9e26 ]
ntfs_read_mft() counts file name attributes into two variables:
names (all names including DOS 8.3) and links (non-DOS names
only). The validation at line 424 checks names but set_nlink()
at line 436 uses links. A corrupted NTFS image where all file
name attributes have type FILE_NAME_DOS passes the names check
but results in set_nlink(inode, 0).
When such an inode is loaded via a code path that passes name=NULL
to ntfs_iget5() and the nlink=0 inode enters the VFS. The subsequent
unlink, rmdir, or rename targeting this inode calls drop_nlink()
which triggers WARN_ON(inode->i_nlink == 0) in fs/inode.c.
An all-DOS-name MFT record cannot exist on a valid NTFS volume.
Reject such records by checking for links == 0 before
calling set_nlink().
Signed-off-by: Ziyi Guo <n7l8m4@u.northwestern.edu>
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 enough information to write the analysis. Let me finalize my
review.
## Analysis
### PHASE 1: COMMIT MESSAGE FORENSICS
**Step 1.1 — Subject line parse**
Record: subsystem `ntfs3`, verb `reject`, intent: reject inodes with
zero non-DOS link count (validation check).
**Step 1.2 — Tags**
Record: Only two SoBs (Ziyi Guo, Konstantin Komarov). No Fixes:, no Cc:
stable, no Reported-by:, no Link:, no Reviewed-by:. Absence is expected
per the rules.
**Step 1.3 — Body text**
Record: Author explains a precise bug mechanism: `names` counts all file
name attrs (incl. FILE_NAME_DOS) while `links` counts only non-DOS. The
existing guard `if (!names)` can pass while `set_nlink(inode, links)`
still sets nlink=0 when every ATTR_NAME is DOS-type. Subsequent
unlink/rmdir/rename of such an inode hits `drop_nlink()` which fires
`WARN_ON(inode->i_nlink == 0)` in `fs/inode.c`. Trigger:
corrupted/malicious NTFS image; no version info given.
**Step 1.4 — Hidden fix detection**
Record: Explicitly a fix ("reject"). Not hidden.
### PHASE 2: DIFF ANALYSIS
**Step 2.1 — Inventory**
Record: 1 file `fs/ntfs3/inode.c`, +5/-0, one function
`ntfs_read_mft()`. Single-file surgical fix.
**Step 2.2 — Code flow**
Record: Before: after `names` validation, code falls through to
`set_nlink(inode, links)`. After: additional `if (!links) { err =
-EINVAL; goto out; }` short-circuits before `set_nlink` for records
whose only names are DOS 8.3.
**Step 2.3 — Bug mechanism**
Record: Category (g) Logic/correctness + defensive validation for on-
disk corruption. Closes an inconsistency between the validator (`names`)
and the consumer (`links`). Prevents VFS invariant violation
(inode->i_nlink==0 for a living inode), which is what the WARN_ON in
`drop_nlink()` guards.
**Step 2.4 — Quality**
Record: Obviously correct; the branch targets the existing `out:` which
does `iget_failed(inode)` and returns `ERR_PTR(err)`. Adds no lock
changes, no allocation changes. Zero regression risk on valid NTFS
(valid records have `links >= 1`, author states an all-DOS-name MFT
record cannot exist on a valid NTFS volume).
### PHASE 3: GIT HISTORY INVESTIGATION
**Step 3.1 — Blame**
Record: `git blame` on the area around the fix shows `set_nlink(inode,
links)` was introduced by `110b24eb1a749b` ("fs/ntfs3: Taking DOS names
into account during link counting", Apr 17 2024, Konstantin Komarov).
**Step 3.2 — Fixes: target**
Record: No Fixes: tag in the commit, but the root cause (splitting
`names` vs `links` and using `links` for nlink while validating `names`)
was introduced by `110b24eb1a749b`. That commit had `Cc:
stable@vger.kernel.org` and `Fixes: 82cae269cfa95 ("fs/ntfs3: Add
initialization of super block")`.
**Step 3.3 — File history**
Record: Standalone commit; not part of any series. Close neighbors in
ntfs3 are independent fixes (e.g. `06909b2549d63`, `4b90f16e4bb56`
handling corrupted metadata).
**Step 3.4 — Author history**
Record: Ziyi Guo has 17 commits in the tree, all small defensive
validation / lock fixes across drivers (net, wifi, USB, ASoC, power) —
typical profile of a researcher/fuzzer-finder. Not the subsystem
maintainer, but the patch was applied by maintainer Konstantin Komarov.
**Step 3.5 — Dependencies**
Record: No dependency. Uses existing locals `links`/`names`, the
existing `out:` label, and `err = -EINVAL`. Self-contained.
### PHASE 4: MAILING LIST AND EXTERNAL RESEARCH
**Step 4.1 — b4 dig / thread**
Record: `b4 dig -c e10e72f69734a` → thread at `https://lore.kernel.org/a
ll/20260210155634.380168-1-n7l8m4@u.northwestern.edu/`. Single revision
(v1). Maintainer Konstantin Komarov replied Feb 24 2026: "Patch looks
good — applied. Thanks." No NAK, no stable nomination, no concerns.
**Step 4.2 — Reviewers**
Record: `b4 dig -w` shows the patch was sent to Konstantin Komarov
(maintainer), ntfs3@lists.linux.dev, linux-kernel@vger.kernel.org.
Appropriate recipients.
**Step 4.3 — Bug report**
Record: No Reported-by:/Link: tags. The commit wording ("A corrupted
NTFS image") and the author's fuzzer-style track record strongly suggest
this was found via fuzzing, though not attributed to syzbot on the list.
**Step 4.4 — Related patches**
Record: No series. Standalone.
**Step 4.5 — Stable discussion**
Record: None found.
### PHASE 5: CODE SEMANTIC ANALYSIS
**Step 5.1 — Key function**
Record: `ntfs_read_mft()`.
**Step 5.2 — Callers**
Record: `ntfs_read_mft` is only called from `ntfs_iget5()`. `ntfs_iget5`
is called with `name=NULL` from many user-reachable paths:
`fs/ntfs3/dir.c:265` (lookup via `indx_find`), `dir.c:337` (readdir /
`ntfs_dir_emit`), `fs/ntfs3/namei.c:371` (`ntfs3_get_parent` — NFS
export / open-by-handle), `fs/ntfs3/frecord.c:2945,3064`, plus super-
block metadata loads. In particular the dir.c:337 readdir path and the
namei.c:371 get_parent path exactly match the commit description's
"name=NULL to ntfs_iget5()".
**Step 5.3 — Callees affected by fix**
Record: Only `set_nlink(inode, links)` is gated behind the new check.
The `out:` path was already correct.
**Step 5.4 — Reachability**
Record: Reachable from userspace via mount + readdir/lookup on a crafted
NTFS image. `drop_nlink()` path reachable via `ntfs_unlink()` and
`ntfs3_rmdir()` in `fs/ntfs3/namei.c` / `fs/ntfs3/inode.c:1838`. End-to-
end chain confirmed.
**Step 5.5 — Similar patterns**
Record: Many recent ntfs3 commits follow the same pattern of hardening
against corrupted on-disk structures (`0dc7117da8f92`, `06909b2549d63`,
`4b90f16e4bb56`, `1732053c8a6b3`, `7443753436620`). Community actively
accepts and backports these.
### PHASE 6: CROSS-REFERENCING AND STABLE TREE ANALYSIS
**Step 6.1 — Bug present in stable?**
Record: The faulty `set_nlink(inode, links)` (without the `!links`
guard) was backported to:
- `stable/linux-5.15.y` as `7ab0c256964ef`
- `stable/linux-6.1.y` as `df40783dc3773`
- `stable/linux-6.6.y` as `e4fd2dce71fbd`
- `stable/linux-6.12.y` as `110b24eb1a749` (same SHA)
So the bug exists in 5.15+, 6.1+, 6.6+, 6.12+ stable trees.
**Step 6.2 — Backport complications**
Record: Verified the code context in `stable/linux-5.15.y` and
`stable/linux-6.6.y` — the surrounding lines (`names !=
le16_to_cpu(rec->hard_links)` block and `set_nlink(inode, links)`) are
identical to mainline. Patch applies cleanly; no rework expected for
5.15.y/6.1.y/6.6.y/6.12.y and newer active LTS branches. Branches
without 110b24eb's equivalent (older than 5.15 or before the April 2024
backport) do not have the bug and do not need this fix.
**Step 6.3 — Existing related stable fixes**
Record: None — no other "reject inodes with zero links" fix exists.
### PHASE 7: SUBSYSTEM AND MAINTAINER CONTEXT
**Step 7.1 — Subsystem criticality**
Record: `fs/ntfs3` — filesystem. IMPORTANT. Users of NTFS read-write
support (Windows dual-boot, removable media), plus security surface for
mountable filesystem images.
**Step 7.2 — Activity**
Record: Very active. Multiple corruption-hardening fixes in every recent
merge window.
### PHASE 8: IMPACT AND RISK ASSESSMENT
**Step 8.1 — Affected users**
Record: Users of ntfs3 who can mount attacker-controlled (or naturally
corrupted) NTFS images. Also anyone who auto-mounts removable media.
Filesystem-specific, not universal.
**Step 8.2 — Trigger**
Record: Crafted NTFS image where an MFT record has only FILE_NAME_DOS
attributes (and no non-DOS name). Valid NTFS never produces this, so the
trigger requires a malformed image. On systems allowing unprivileged
mounting (user namespaces + fuse-style setups, some distros' udisks
configs) the trigger can be reached without root.
**Step 8.3 — Failure mode severity**
Record: `WARN_ON(inode->i_nlink == 0)` → kernel warning with stack. On
`panic_on_warn` systems (common in hardened / KASAN / fuzzing
environments) it becomes a panic / DoS. Also a reliable way to trigger
VFS invariants violation for further exploration. Severity: MEDIUM-HIGH
(DoS via crafted FS image, security-relevant).
**Step 8.4 — Risk/benefit**
Record: Benefit: prevents a WARN/panic on malformed NTFS images, closes
a small attack surface. Risk: extremely low — 5 additional lines on the
error path, using the same `out:` cleanup already there; impossible to
affect a valid NTFS volume per the filesystem semantics described.
### PHASE 9: FINAL SYNTHESIS
**Step 9.1 — Evidence compile**
FOR:
- Small (5 lines), surgical, single-function, no lock/alloc churn.
- Fixes a concrete bug mechanism with clear reachability from user-
triggerable paths (readdir, lookup, NFS get_parent).
- Bug introduced by `110b24eb1a749b` (already in
5.15.y/6.1.y/6.6.y/6.12.y stable), so active LTS trees are vulnerable.
- Maintainer applied it with explicit approval ("Patch looks good —
applied").
- Same pattern (corrupted-FS validation) is standard stable material for
ntfs3.
- Failure mode is a WARN (or panic with panic_on_warn), security-
relevant for mountable FS images.
- Applies cleanly to stable branches (verified identical context in
5.15.y and 6.6.y).
AGAINST:
- No Fixes: / Cc: stable tags, no Reported-by:/Link:, no syzbot
attribution in message (absence of tags is expected per rules and not
a negative signal).
**Step 9.2 — Stable rules checklist**
1. Obviously correct & tested? Yes — maintainer-applied, trivial gate
check before existing code path.
2. Real bug? Yes — WARN triggers on documented code path.
3. Important? Yes — corrupted/malicious FS image → kernel WARN,
potential DoS; security-relevant.
4. Small/contained? Yes — 5 LOC, one function.
5. No new features/APIs? Correct.
6. Applies to stable? Yes — verified identical context in 5.15.y /
6.6.y.
**Step 9.3 — Exception**
Record: Fits the "corruption hardening" pattern commonly accepted for
filesystems.
**Step 9.4 — Decision**
The fix is an obvious, minimal validation that prevents a VFS invariant
violation reachable via a crafted NTFS image on all LTS trees that have
commit `110b24eb1a749b` backported (5.15, 6.1, 6.6, 6.12). Benefit
clearly outweighs negligible risk.
## Verification
- [Phase 1] Parsed commit message and tags directly from the commit
body; confirmed no Fixes:/Cc: stable/Reported-by: tags. No hidden
framing — patch explicitly says "reject".
- [Phase 2] Read the relevant portion of `fs/ntfs3/inode.c` (lines
140–230 and 400–500) to confirm `names` is incremented for every
ATTR_NAME, `links` only for non-DOS names, and the fix goto-targets
the existing `out:` handler.
- [Phase 3] `git blame` on `fs/ntfs3/inode.c` lines 420–440:
`set_nlink(inode, links)` originates in `110b24eb1a749b` ("fs/ntfs3:
Taking DOS names into account during link counting", 2024-04-17); that
commit has `Cc: stable@vger.kernel.org` and `Fixes: 82cae269cfa95`.
- [Phase 3] `git show 110b24eb1a749b`: confirmed the stable-tagged
commit that introduced the vulnerability pattern.
- [Phase 3] `git log --author="Ziyi Guo"`: 17 small validation / locking
fixes across subsystems; not subsystem maintainer.
- [Phase 4] `b4 dig -c e10e72f69734a`: found single-revision thread at `
https://lore.kernel.org/all/20260210155634.380168-1-
n7l8m4@u.northwestern.edu/`; saved mbox to `/tmp/thread.mbox` and read
the maintainer's "applied" reply.
- [Phase 4] `b4 dig -c e10e72f69734a -w`: confirmed recipients
(Konstantin Komarov, ntfs3@lists.linux.dev, linux-
kernel@vger.kernel.org).
- [Phase 4] Verified via spinics mirror that only one version was sent
and only one reply ("applied") was received.
- [Phase 5] `rg ntfs_iget5` in `fs/ntfs3`: enumerated all callers and
confirmed multiple user-reachable paths pass `name=NULL` (dir.c:337,
namei.c:371, frecord.c:2945/3064).
- [Phase 5] Read `drop_nlink()` at `fs/inode.c:416-422` to confirm the
exact `WARN_ON(inode->i_nlink == 0)` location.
- [Phase 5] Located `drop_nlink(inode)` call sites in ntfs3
(`namei.c:155`, `inode.c:1838`), confirming user unlink/rmdir/rename →
`drop_nlink` chain.
- [Phase 6] Queried each stable branch directly for the "Taking DOS
names" backport: found `7ab0c256964ef` in 5.15.y, `df40783dc3773` in
6.1.y, `e4fd2dce71fbd` in 6.6.y, `110b24eb1a749` in 6.12.y — the buggy
change is in those stables.
- [Phase 6] `git show stable/linux-6.6.y:fs/ntfs3/inode.c` and
`stable/linux-5.15.y:fs/ntfs3/inode.c` around the target lines:
context is identical to mainline, confirming clean application.
- [Phase 6] Searched each stable branch for the fix ("reject inodes" /
"non-DOS link" in inode.c); not present in any stable, so no
duplicate.
- [Phase 7] File path identifies subsystem as `fs/ntfs3`; `git log
--oneline fs/ntfs3` shows active subsystem with frequent corruption-
hardening fixes.
- [Phase 8] Failure mode verified by reading `drop_nlink()` source and
tracing the reachable ntfs3 call chain; severity assessed as MEDIUM-
HIGH (WARN → panic_on_warn).
- UNVERIFIED: Whether a syzbot report specifically mentions this
signature (no Reported-by in the commit; could not confirm a direct
syzkaller report link). This does not affect the decision because the
mechanism is verified by code reading.
The fix is small, obviously correct, fixes a real reachable bug, is
already applied by the maintainer, and applies cleanly to LTS trees that
have the buggy precursor commit. It meets all stable-kernel criteria.
**YES**
fs/ntfs3/inode.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/fs/ntfs3/inode.c b/fs/ntfs3/inode.c
index 6e65066ebcc1a..398913595a551 100644
--- a/fs/ntfs3/inode.c
+++ b/fs/ntfs3/inode.c
@@ -432,6 +432,11 @@ static struct inode *ntfs_read_mft(struct inode *inode,
ni->mi.dirty = true;
}
+ if (!links) {
+ err = -EINVAL;
+ goto out;
+ }
+
set_nlink(inode, links);
if (S_ISDIR(mode)) {
--
2.53.0
^ permalink raw reply related [flat|nested] 6+ messages in thread* [PATCH AUTOSEL 7.0-5.15] fs/ntfs3: fix $LXDEV xattr lookup
[not found] <20260428104133.2858589-1-sashal@kernel.org>
` (2 preceding siblings ...)
2026-04-28 10:40 ` [PATCH AUTOSEL 7.0-5.15] ntfs3: reject inodes with zero non-DOS link count Sasha Levin
@ 2026-04-28 10:40 ` Sasha Levin
2026-04-28 10:41 ` [PATCH AUTOSEL 7.0-6.1] ntfs3: fix OOB write in attr_wof_frame_info() Sasha Levin
2026-04-28 10:41 ` [PATCH AUTOSEL 7.0-5.15] ntfs3: fix memory leak in indx_create_allocate() Sasha Levin
5 siblings, 0 replies; 6+ messages in thread
From: Sasha Levin @ 2026-04-28 10:40 UTC (permalink / raw)
To: patches, stable
Cc: Zhan Xusheng, Zhan Xusheng, Konstantin Komarov, Sasha Levin,
ntfs3, linux-kernel
From: Zhan Xusheng <zhanxusheng1024@gmail.com>
[ Upstream commit bb82fe0872de867f87fd4f64c9cb157903ac78db ]
Use correct xattr name ("$LXDEV") and buffer size when calling
ntfs_get_ea(), otherwise the attribute may not be read.
Signed-off-by: Zhan Xusheng <zhanxusheng@xiaomi.com>
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 enough information to make a thorough assessment. Let me
compile the analysis.
## Analysis: fs/ntfs3: fix $LXDEV xattr lookup
### PHASE 1: COMMIT MESSAGE FORENSICS
**Step 1.1 (Subject):**
Record: Subsystem `fs/ntfs3:`, action verb `fix`, summary: fix $LXDEV
extended attribute lookup failing due to wrong name_len passed to
`ntfs_get_ea()`.
**Step 1.2 (Tags):**
Record: Present tags:
- `Signed-off-by: Zhan Xusheng <zhanxusheng@xiaomi.com>` (author)
- `Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-
software.com>` (ntfs3 maintainer)
- No `Fixes:`, no `Reported-by:`, no `Link:`, no `Cc: stable`, no
`Reviewed-by:`, no `Tested-by:`, no syzbot.
**Step 1.3 (Body):**
Record: Commit body states two things: (a) wrong xattr *name* is used
via `sizeof("$$LXDEV") - 1`; (b) this may cause the attribute not to be
read. No crash/stack trace, no version info, no reproducer;
straightforward bug description.
**Step 1.4 (Hidden fix check):**
Record: Not hidden — verb is "fix" and described as a correctness bug.
Confirmed by diff.
### PHASE 2: DIFF ANALYSIS
**Step 2.1 (Inventory):**
Record: One file `fs/ntfs3/xattr.c`, one function `ntfs_get_wsl_perm`, 1
line changed (+1/-1). Single-file, single-char surgical fix.
**Step 2.2 (Flow change):**
Record: Before: `ntfs_get_ea(inode, "$LXDEV", sizeof("$$LXDEV") - 1, …)`
passed `name_len = 7` to `ntfs_get_ea()`. After: `sizeof("$LXDEV") - 1 =
6`. Only the name-length argument is altered; everything else (name
pointer, buffer, size, return check) remains identical.
**Step 2.3 (Bug mechanism):**
Record: Category = logic/correctness bug (typo). `find_ea()`
(fs/ntfs3/xattr.c:45-66) matches entries by:
```45:62:fs/ntfs3/xattr.c
static inline bool find_ea(const struct EA_FULL *ea_all, u32 bytes,
const char *name, u8 name_len, u32 *off, u32
*ea_sz)
{
...
if (ea->name_len == name_len &&
!memcmp(ea->name, name, name_len)) {
```
`ntfs_save_wsl_perm()` stores `$LXDEV` with `name_len = 6`
(fs/ntfs3/xattr.c:1001), but `ntfs_get_wsl_perm()` was querying with
`name_len = 7`. Since the first check (`ea->name_len == name_len`)
requires exact equality, the lookup **always fails**. Consequence:
`inode->i_rdev` is never populated from the `$LXDEV` xattr for WSL-
created character/block device nodes on NTFS.
**Step 2.4 (Quality):**
Record: Obviously correct — the other three `$LXUID/$LXGID/$LXMOD` calls
and the corresponding `ntfs_set_ea(…, "$LXDEV", sizeof("$LXDEV") - 1,
…)` all use the correct form. The fix makes the read path consistent
with the write path. No risk of regression: the old path was a
guaranteed miss; the new path can only succeed more often. Not touching
locks, memory mgmt, or any hot path.
### PHASE 3: GIT HISTORY INVESTIGATION
**Step 3.1 (Blame):**
Record: `git blame` on lines 1034-1036 shows the exact line was
introduced by `be71b5cba2e64` ("fs/ntfs3: Add attrib operations",
Konstantin Komarov, 2021-08-13). `git log -S '"$$LXDEV"'` confirms this
is the single origin. The bug is a birth defect of the ntfs3 xattr code.
**Step 3.2 (Fixes: tag):**
Record: No `Fixes:` tag in the commit. Based on blame, the logical Fixes
target is `be71b5cba2e64` (pre-upstream). First kernel version that
contains the bug: **v5.15** (verified via `git tag --contains
be71b5cba2e64` returning `v5.15`, `v5.15-rc1`, etc.).
**Step 3.3 (File history):**
Record: Recent commits to `fs/ntfs3/xattr.c` in 7.0 tree are unrelated
(delalloc support, d_compare fixes, posix_acl changes, ACL-mode
reduction). No prerequisite refactoring is required.
**Step 3.4 (Author):**
Record: First patch by `zhanxusheng1024@gmail.com` /
`zhanxusheng@xiaomi.com` — not a maintainer, but the patch goes through
Konstantin Komarov, the ntfs3 maintainer, who applied it (second SoB).
**Step 3.5 (Dependencies):**
Record: No dependencies — the changed function `ntfs_get_ea()` has the
same signature across all stable trees with ntfs3 (v5.15+).
### PHASE 4: MAILING LIST AND EXTERNAL RESEARCH
**Step 4.1 / 4.2 / 4.4 (b4 dig):**
Record:
- `b4 dig -c bb82fe0872de8` → found submission at https://lore.kernel.or
g/all/20260327032454.101579-1-zhanxusheng@xiaomi.com/
- `b4 dig -a` → only **v1** exists; the applied version is the latest.
- `b4 dig -w` → recipients: Konstantin Komarov (maintainer), linux-
kernel@vger.kernel.org, Zhan Xusheng.
- Full thread (saved via `-m`) shows Konstantin Komarov replied on 7 Apr
2026: "Thanks, your patch is applied." No stable nomination was
requested, no NAKs or concerns raised.
**Step 4.3 (Bug reports):**
Record: No Link: tags, no Reported-by, no bugzilla. No syzbot report.
The author evidently found this by reading code or hitting WSL-created
device nodes on NTFS.
**Step 4.5 (Stable ML):**
Record: No stable-list discussion was found; not relevant here.
### PHASE 5: CODE SEMANTIC ANALYSIS
**Step 5.1 (Functions):**
Record: Only `ntfs_get_wsl_perm()` is modified.
**Step 5.2 (Callers):**
Record: `ntfs_get_wsl_perm()` is called once, from
`fs/ntfs3/inode.c:381` inside MFT attribute processing:
```373:384:fs/ntfs3/inode.c
case ATTR_EA_INFO:
if (!attr->name_len &&
resident_data_ex(attr, sizeof(struct EA_INFO))) {
ni->ni_flags |= NI_FLAG_EA;
/*
- ntfs_get_wsl_perm updates inode->i_uid, inode->i_gid,
inode->i_mode
*/
inode->i_mode = mode;
ntfs_get_wsl_perm(inode);
```
Triggered whenever ntfs3 loads an inode that carries EA_INFO (any NTFS
inode with extended attributes, typical for WSL-managed files).
**Step 5.3 (Callees):**
Record: Calls `ntfs_get_ea()` (and through it `find_ea()` → `memcmp`).
No locking/memory-management changes.
**Step 5.4 (Reachability):**
Record: Reachable from `ntfs_iget5 -> ntfs_read_mft` — the standard
inode-read path. Any open/stat/readdir of a WSL-special file on NTFS
triggers the buggy code. Fully reachable from unprivileged userspace
with an NTFS mount.
**Step 5.5 (Similar patterns):**
Record: `sizeof("$LXUID")`, `sizeof("$LXGID")`, `sizeof("$LXMOD")` calls
are correct. The `ntfs_set_ea(… "$LXDEV", sizeof("$LXDEV") - 1 …)` at
line 1001 is also correct. The `$$LXDEV` typo is unique to this one
`get_ea` call — no other instances.
### PHASE 6: CROSS-REFERENCING AND STABLE TREE ANALYSIS
**Step 6.1 (Code exists in stable?):**
Record: Yes, the buggy code exists identically in all active stable
branches — verified by `git show`:
- `stable/linux-5.15.y` line 1024
- `stable/linux-6.6.y` line 1012
- `stable/linux-6.12.y` line 1013
- `stable/linux-6.19.y` line 1038
All contain `sizeof("$$LXDEV") - 1`.
**Step 6.2 (Backport complications):**
Record: None. The context around the change is essentially identical
across all stable trees; only the line number varies. Patch applies
cleanly (or with trivial offset).
**Step 6.3 (Related stable fixes):**
Record: No prior `$LXDEV` fix has landed in stable — the bug has existed
unmodified since v5.15.
### PHASE 7: SUBSYSTEM AND MAINTAINER CONTEXT
**Step 7.1 (Subsystem):**
Record: fs/ntfs3 — filesystem driver. Criticality: **IMPORTANT**
(filesystem affecting data correctness for NTFS users, particularly
those using NTFS cross-booted with Windows/WSL).
**Step 7.2 (Activity):**
Record: ntfs3 is actively maintained by Paragon Software; steady trickle
of small fixes.
### PHASE 8: IMPACT AND RISK ASSESSMENT
**Step 8.1 (Who is affected):**
Record: Users of ntfs3 who have NTFS volumes containing WSL-created
character/block device special files. Niche but real (WSL users who
mount the same NTFS on Linux).
**Step 8.2 (Trigger):**
Record: Triggers whenever an ntfs3 inode for a special device file with
WSL attributes is loaded. No privilege needed beyond access to a mounted
NTFS volume. Deterministic (not a race).
**Step 8.3 (Severity):**
Record: No crash; no data corruption of on-disk data. The user-visible
symptom is that `S_ISCHR`/`S_ISBLK` files have `i_rdev = 0` (or
uninitialized) — device nodes are effectively broken. **Severity:
LOW–MEDIUM** (functional correctness bug, not a safety bug).
**Step 8.4 (Risk-Benefit):**
Record:
- Benefit: LOW-MEDIUM — fixes functional correctness for a specific
interop feature (WSL<->Linux on NTFS).
- Risk: VERY LOW — 1-character fix, obviously correct, cannot make the
read path worse; write path is unchanged.
- Ratio: clearly favorable for stable.
### PHASE 9: FINAL SYNTHESIS
**Step 9.1 (Evidence):**
- FOR: Genuine bug with zero controversy; obvious typo; present since
v5.15 in all stable trees; maintainer-accepted; minimal scope (1
char); fix makes read symmetric with write; trivial backport.
- AGAINST: Low user impact (niche WSL interop); no `Cc: stable` /
`Fixes:`; no Reported-by/syzbot; no crash or data corruption.
- Unresolved: None material.
**Step 9.2 (Stable rules):**
1. Obviously correct and tested? **Yes** — one-character change, read
symmetry with the write path proves correctness.
2. Fixes a real bug? **Yes** — `$LXDEV` xattr lookup is guaranteed to
fail.
3. Important issue (crash/security/corruption/deadlock)? **No** —
functional correctness only, no crash or data loss.
4. Small and contained? **Yes** — 1 line, 1 character.
5. No new features or APIs? **Correct — no new features.**
6. Applies to stable trees? **Yes** — verified identical context in
5.15.y..6.19.y.
**Step 9.3 (Exceptions):**
Not an exception category, but the fix is a trivial, obvious correctness
fix (closest to "correctness typo fix") — a class the stable tree
routinely accepts.
**Step 9.4 (Decision):**
The only soft point against is that this is not an obvious "important"
crash/security/corruption fix per rule #3. However, stable *does*
routinely take obviously-correct, low-risk functional fixes,
particularly when they restore feature functionality broken since a
feature's introduction. The risk of regression is essentially nil (the
fix cannot make the read path fail *more* often than it already does —
it fails 100% of the time today). Benefit is concrete: WSL device-node
interop on NTFS begins working. Given extreme simplicity and clarity, I
lean YES.
### Verification
- [Phase 1] Parsed commit message and tags: no Fixes:, no Cc: stable, no
Reported-by, two SoBs (author + ntfs3 maintainer).
- [Phase 2] Diff inspection: 1 line changed; argument `name_len` goes
from 7 → 6.
- [Phase 2] Read `find_ea` at `fs/ntfs3/xattr.c:45-66` confirming exact
`ea->name_len == name_len` comparison that guarantees miss with wrong
length.
- [Phase 2] Read `ntfs_save_wsl_perm` at `fs/ntfs3/xattr.c:1001` —
storage side correctly uses `sizeof("$LXDEV") - 1` (=6), confirming
asymmetry.
- [Phase 3] `git log -S '"$$LXDEV"'` → buggy code introduced in
`be71b5cba2e64` (2021-08-13, "fs/ntfs3: Add attrib operations").
- [Phase 3] `git blame` confirms the single origin commit.
- [Phase 3] `git tag --contains be71b5cba2e64 | rg '^v'` → first kernel
is v5.15.
- [Phase 4] `b4 dig -c bb82fe0872de8` → lore URL `https://lore.kernel.or
g/all/20260327032454.101579-1-zhanxusheng@xiaomi.com/`.
- [Phase 4] `b4 dig -a` → only v1, which is the applied version.
- [Phase 4] `b4 dig -w` → recipients: Konstantin Komarov, linux-
kernel@vger.kernel.org.
- [Phase 4] Read full thread mbox: maintainer reply "Thanks, your patch
is applied"; no stable nomination, no NAKs, no concerns.
- [Phase 5] Grepped `ntfs_get_wsl_perm` → single caller at
`fs/ntfs3/inode.c:381` in MFT processing; reachable from every inode
load that has EA_INFO.
- [Phase 6] Verified stable branches 5.15.y, 6.6.y, 6.12.y, 6.19.y all
contain `sizeof("$$LXDEV") - 1` — fix applies cleanly everywhere.
- [Phase 8] Reasoned failure mode from code structure: `find_ea` always
returns false → `ntfs_get_ea` returns -ENODATA → `i_rdev` never set
for WSL char/block nodes.
- UNVERIFIED: No runtime reproducer was executed; impact inferred from
code reading. Author cites no user-visible trace, but the code path is
deterministic.
Summary: a small, obviously-correct typo fix to ntfs3 that has been
dormant since v5.15. Not a crash/security fix, but functionally restores
WSL device-node metadata on NTFS with negligible regression risk. On
balance, this fits the class of tiny, safe correctness fixes that are
appropriate for stable.
**YES**
fs/ntfs3/xattr.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/fs/ntfs3/xattr.c b/fs/ntfs3/xattr.c
index 3fffda7848922..9eeac0ab2b714 100644
--- a/fs/ntfs3/xattr.c
+++ b/fs/ntfs3/xattr.c
@@ -1031,7 +1031,7 @@ void ntfs_get_wsl_perm(struct inode *inode)
i_gid_write(inode, (gid_t)le32_to_cpu(value[1]));
inode->i_mode = le32_to_cpu(value[2]);
- if (ntfs_get_ea(inode, "$LXDEV", sizeof("$$LXDEV") - 1,
+ if (ntfs_get_ea(inode, "$LXDEV", sizeof("$LXDEV") - 1,
&value[0], sizeof(value),
&sz) == sizeof(value[0])) {
inode->i_rdev = le32_to_cpu(value[0]);
--
2.53.0
^ permalink raw reply related [flat|nested] 6+ messages in thread* [PATCH AUTOSEL 7.0-6.1] ntfs3: fix OOB write in attr_wof_frame_info()
[not found] <20260428104133.2858589-1-sashal@kernel.org>
` (3 preceding siblings ...)
2026-04-28 10:40 ` [PATCH AUTOSEL 7.0-5.15] fs/ntfs3: fix $LXDEV xattr lookup Sasha Levin
@ 2026-04-28 10:41 ` Sasha Levin
2026-04-28 10:41 ` [PATCH AUTOSEL 7.0-5.15] ntfs3: fix memory leak in indx_create_allocate() Sasha Levin
5 siblings, 0 replies; 6+ messages in thread
From: Sasha Levin @ 2026-04-28 10:41 UTC (permalink / raw)
To: patches, stable
Cc: 0xkato, Konstantin Komarov, Sasha Levin, ntfs3, linux-kernel
From: 0xkato <0xkkato@gmail.com>
[ Upstream commit 859d777646b56dd878b136392f3d03fb8153b559 ]
In attr_wof_frame_info(), the offset-table read range for a nonresident
WofCompressedData stream is:
u64 from = vbo[i] & ~(u64)(PAGE_SIZE - 1);
u64 to = min(from + PAGE_SIZE, wof_size);
...
ntfs_read_run(sbi, run, addr, from, to - from);
A crafted image sets WofCompressedData.nres.data_size to 0xfff while the
file is large enough to request frame 1024 (offset 0x400000). This gives
from=0x1000, to=0xfff. The unsigned (to - from) wraps to 0xffffffffffffffff
and ntfs_read_write_run() overflows the single-page offs_folio via memcpy.
Triggered by pread() on a mounted NTFS image. Depending on adjacent
memory layout at the time of the overflow, KASAN reports this as
slab-out-of-bounds, use-after-free, or slab-use-after-free all at
ntfs_read_write_run(). Secondary corruption/panic paths were also observed.
Reject the read when the offset-table page is outside the stream.
Signed-off-by: 0xkato <0xkkato@gmail.com>
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:
fs/ntfs3/attrib.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/fs/ntfs3/attrib.c b/fs/ntfs3/attrib.c
index 6cb9bc5d605c2..89921e5091c9a 100644
--- a/fs/ntfs3/attrib.c
+++ b/fs/ntfs3/attrib.c
@@ -1576,6 +1576,12 @@ int attr_wof_frame_info(struct ntfs_inode *ni, struct ATTRIB *attr,
u64 from = vbo[i] & ~(u64)(PAGE_SIZE - 1);
u64 to = min(from + PAGE_SIZE, wof_size);
+ if (from >= wof_size) {
+ _ntfs_bad_inode(&ni->vfs_inode);
+ err = -EINVAL;
+ goto out1;
+ }
+
err = attr_load_runs_range(ni, ATTR_DATA, WOF_NAME,
ARRAY_SIZE(WOF_NAME), run,
from, to);
--
2.53.0
^ permalink raw reply related [flat|nested] 6+ messages in thread* [PATCH AUTOSEL 7.0-5.15] ntfs3: fix memory leak in indx_create_allocate()
[not found] <20260428104133.2858589-1-sashal@kernel.org>
` (4 preceding siblings ...)
2026-04-28 10:41 ` [PATCH AUTOSEL 7.0-6.1] ntfs3: fix OOB write in attr_wof_frame_info() Sasha Levin
@ 2026-04-28 10:41 ` Sasha Levin
5 siblings, 0 replies; 6+ messages in thread
From: Sasha Levin @ 2026-04-28 10:41 UTC (permalink / raw)
To: patches, stable
Cc: Deepanshu Kartikey, syzbot+7adcddaeeb860e5d3f2f,
Deepanshu Kartikey, Konstantin Komarov, Sasha Levin, ntfs3,
linux-kernel
From: Deepanshu Kartikey <kartikey406@gmail.com>
[ Upstream commit 87ac077d6ea8613b7c1debdf3b5e92c78618fd23 ]
When indx_create_allocate() fails after
attr_allocate_clusters() succeeds, run_deallocate()
frees the disk clusters but never frees the memory
allocated by run_add_entry() via kvmalloc() for the
runs_tree structure.
Fix this by adding run_close() at the out: label to
free the run.runs memory on all error paths. The
success path is unaffected as it returns 0 directly
without going through out:, transferring ownership
of the run memory to indx->alloc_run via memcpy().
Reported-by: syzbot+7adcddaeeb860e5d3f2f@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=7adcddaeeb860e5d3f2f
Signed-off-by: Deepanshu Kartikey <Kartikey406@gmail.com>
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 enough information to provide the complete analysis.
## Complete 9-Phase Analysis
### PHASE 1: COMMIT MESSAGE FORENSICS
**Step 1.1 - Subject Line**
- Record: `[ntfs3]` `[fix]` memory leak in `indx_create_allocate()`.
Clear, unambiguous fix language.
**Step 1.2 - Commit Message Tags**
- Record:
- Reported-by: `syzbot+7adcddaeeb860e5d3f2f@syzkaller.appspotmail.com`
(syzkaller fuzzer-found, reproducible)
- Closes:
`https://syzkaller.appspot.com/bug?extid=7adcddaeeb860e5d3f2f`
(syzbot link)
- Signed-off-by: Deepanshu Kartikey (author) and Konstantin Komarov
(ntfs3 maintainer)
- No Fixes: tag (noted as expected for review candidates)
- No Cc: stable (noted as expected for review candidates)
**Step 1.3 - Commit Body Analysis**
- Record: Bug description: When `indx_create_allocate()` fails after
`attr_allocate_clusters()` has succeeded, `run_deallocate()` only
frees the on-disk clusters but NOT the memory that `run_add_entry()`
allocated via `kvmalloc()` for the `runs_tree.runs` array. Failure
mode: kernel memory leak (reachable via unprivileged syscall). Author
correctly identifies the root cause and explains why the success path
is unaffected.
**Step 1.4 - Hidden Bug Fix Detection**
- Record: Not a hidden fix - explicitly labeled "fix memory leak".
### PHASE 2: DIFF ANALYSIS
**Step 2.1 - Inventory**
- Record: 1 file, 1 line added, 0 lines removed. Single-file surgical
fix. Only `indx_create_allocate()` is modified.
**Step 2.2 - Code Flow Change**
- Record: Before: The `out:` label only contained `return err;`. After:
`run_close(&run);` is invoked before return. `run_close()` does
`kvfree(run->runs); memset(run, 0, sizeof(*run));` - this frees the
allocated runs array. The success path returns via `return 0` at line
1475 BEFORE the `out:` label, after doing `memcpy(&indx->alloc_run,
&run, sizeof(run));` to transfer ownership.
**Step 2.3 - Bug Mechanism**
- Record: Category (a) + (c): error path / resource leak fix,
specifically a missing deallocation. `run_add_entry()` at
`fs/ntfs3/run.c:390` calls `kvmalloc(bytes, GFP_KERNEL)` storing
pointer in `run->runs`. `run_deallocate()` at `fs/ntfs3/fsntfs.c:2550`
only frees on-disk clusters via `mark_as_free_ex()` - verified it does
NOT touch `run->runs`. Therefore every error path that goes through
`out:` (3 of them: the direct `attr_allocate_clusters` failure plus
the `out1` and `out2` fallthroughs) leaks the kvmalloc allocation.
`run_close()` is safe on a `run_init`'d (all-zero) run because
`kvfree(NULL)` is a no-op.
**Step 2.4 - Fix Quality**
- Record: Fix quality is excellent. Obviously correct: `run_close` is
idempotent/safe on NULL, so adding it unconditionally at `out:` cannot
introduce regressions. No API changes, no new locking, no new
allocations. Zero regression risk.
### PHASE 3: GIT HISTORY INVESTIGATION
**Step 3.1 - Blame**
- Record: The `out:` label with `return err;` structure and the
`runs_tree run; run_init(&run);` pattern are from commit
`f7464060f7ab9a` (Linus Torvalds, 2021-09-04) - the initial ntfs3
merge for v5.15-rc1. Thus the bug has existed since v5.15 and affects
every stable tree that includes ntfs3.
**Step 3.2 - Fixes: tag**
- Record: No Fixes: tag in the commit. Root-cause commit (first ntfs3
merge) is `f7464060f7ab9a`, which is v5.15-rc1~94 and obviously
present in all stable trees ≥ 5.15.
**Step 3.3 - File History**
- Record: `fs/ntfs3/index.c` has 37 commits since v5.15. Immediate prior
change was `3a2141b2f1c34 fs/ntfs3: resolve compare function in public
index APIs` (unrelated). The `out:` sequence itself has been untouched
since initial merge. Related historical fixes include `b8155e95de38b
fs/ntfs3: Fix error handling in indx_insert_into_root()` (different
function, similar class of bug) and `ccc4e86d1c242 fs/ntfs3: Prevent
memory leaks in add sub record` (another recent syzbot memory leak
fix). This is a standalone patch, NOT part of a series.
**Step 3.4 - Author's Other Commits**
- Record: Deepanshu Kartikey has submitted multiple fuzzer-found bug
fixes across ntfs3, ext4, gfs2, netfs, mac80211, atm, comedi, etc. A
regular kernel contributor who focuses on bug fixes rather than new
features. Patch was applied by Konstantin Komarov - the ntfs3
maintainer.
**Step 3.5 - Dependencies**
- Record: Standalone, no dependencies. The fix uses `run_close()` which
is a stable inline helper in `fs/ntfs3/ntfs_fs.h` from the initial
ntfs3 merge. No new APIs used.
### PHASE 4: MAILING LIST RESEARCH
**Step 4.1 - Original Patch Discussion**
- Record: b4 dig found submission at `https://lore.kernel.org/all/202603
21050143.1117500-1-kartikey406@gmail.com`. v1 is the only revision;
the committed version is unchanged from v1 (same diff). Submission
2026-03-21, committed 2026-03-23.
**Step 4.2 - Recipients**
- Record: Original recipients per b4 dig -w: Konstantin Komarov
(almaz.alexandrovich@paragon-software.com - ntfs3 maintainer),
ntfs3@lists.linux.dev, linux-kernel@vger.kernel.org, syzbot reporter.
Correct audience.
**Step 4.3 - Bug Report**
- Record: Syzbot report at
https://syzkaller.appspot.com/bug?extid=7adcddaeeb860e5d3f2f confirms:
- Title: "memory leak in run_add_entry (2)"
- Has syz repro AND C repro
- Reproducible stack: `__x64_sys_link -> filename_linkat -> vfs_link
-> ntfs_link -> ntfs_link_inode -> ni_add_name -> indx_insert_entry
-> indx_insert_into_root -> indx_create_allocate ->
attr_allocate_clusters -> run_add_entry`
- Syzbot marks this commit as the fix and confirms it's patched
- Reachable via `link(2)` syscall → unprivileged userspace trigger
**Step 4.4 - Series Context**
- Record: b4 dig -a shows only one revision (v1) - single standalone
patch, not a series.
**Step 4.5 - Stable Discussion**
- Record: Thread discussion (from saved mbox): Konstantin's reply on
2026-04-01 "Your patch is being tested internally. I'll follow up" and
on 2026-04-02 "Your patch is applied, thanks for your work." No
reviewer asked for Cc: stable explicitly, but no objections or
concerns were raised either. No NAKs. Maintainer tested before
applying.
### PHASE 5: CODE SEMANTIC ANALYSIS
**Step 5.1 - Key Functions**
- Record: `indx_create_allocate()` is the only function modified.
**Step 5.2 - Callers**
- Record: `indx_create_allocate()` called from exactly one place:
`indx_insert_into_root()` at `fs/ntfs3/index.c:1705`.
`indx_insert_into_root()` is called from `indx_insert_entry()`, which
is called from directory operations including `ni_add_name()` (from
link/rename/create operations). Thus this code is reachable from
common filesystem syscalls on any ntfs3-mounted volume.
**Step 5.3 - Callees**
- Record: Key callees: `run_init`, `attr_allocate_clusters` (calls
`run_add_entry` which does the `kvmalloc`), `ni_insert_nonresident`,
`ni_insert_resident`, `run_deallocate`, `run_close` (the newly added
fix).
**Step 5.4 - Call Chain (Reachability)**
- Record: Confirmed userspace reachable: `link(2)` → `__x64_sys_link` →
`vfs_link` → `ntfs_link` → ... → `indx_create_allocate()`. Syzbot's C
reproducer triggers the leak from an unprivileged process. Memory
exhaustion attack vector exists.
**Step 5.5 - Similar Patterns**
- Record: Several recent ntfs3 memory leak fixes follow the same pattern
(e.g., `ccc4e86d1c242 fs/ntfs3: Prevent memory leaks in add sub
record` - another syzbot-reported ntfs3 leak). The ntfs3 driver has a
history of these error-path resource leak fixes, which is common in
newer filesystems.
### PHASE 6: STABLE TREE ANALYSIS
**Step 6.1 - Code in Stable**
- Record: ntfs3 was merged in v5.15 (`f7464060f7ab9a`). The buggy
`indx_create_allocate()` exists in ALL stable trees from 5.15.y
onward: 5.15.y, 6.1.y, 6.6.y, 6.12.y, etc.
**Step 6.2 - Backport Difficulty**
- Record: The `out:` block `run_deallocate(sbi, &run, false); out:
return err;` has been unchanged since initial merge. The patch adds
one line at the out: label. Expected clean application to all stable
trees with no adjustments needed. Minor surrounding line-number
differences are possible but the hunk context (`run_deallocate(...)`
just above `out:` above `return err`) is stable.
**Step 6.3 - Related Fixes in Stable**
- Record: No earlier/alternative fix for this specific bug exists - the
syzbot report is fresh (first crash 31 days before patched per the
syzbot dashboard).
### PHASE 7: SUBSYSTEM CONTEXT
**Step 7.1 - Subsystem**
- Record: fs/ntfs3/ - Read-Write NTFS filesystem driver. Criticality:
IMPORTANT (used by many dual-boot setups, external NTFS-formatted
drives). Filesystem bugs especially leaks/corruptions directly impact
users.
**Step 7.2 - Subsystem Activity**
- Record: ntfs3 is actively developed and receives regular fixes
(including security/fuzzer-found issues). 37 commits to index.c alone
since v5.15.
### PHASE 8: IMPACT AND RISK
**Step 8.1 - Who is Affected**
- Record: All ntfs3 users on stable kernels 5.15+. Trigger requires a
mounted ntfs3 filesystem plus the ability to create/link files that
trigger index allocation.
**Step 8.2 - Trigger Conditions**
- Record: Syzbot reproducer uses `link(2)` syscall on an ntfs3
filesystem; bug triggers when `indx_create_allocate()` takes any of
its three error paths after `run_add_entry` has allocated memory.
Unprivileged user on a system with ntfs3 mounted (e.g., USB drive) can
trigger. Repeated triggering leaks kernel memory → potential DoS.
**Step 8.3 - Failure Mode Severity**
- Record: Failure mode: kernel memory leak. Severity: MEDIUM (kmemleak
report, no immediate crash, but exploitable for kernel-memory
exhaustion DoS from unprivileged user on systems with writable ntfs3
mounts). Not critical per-trigger but real security relevance.
**Step 8.4 - Risk/Benefit**
- Record:
- Benefit: Fixes a syzbot-confirmed, reproducible leak triggerable
from userspace. Medium-to-high benefit for stable users (especially
those with ntfs3 mounted by default, like many distros).
- Risk: Very low. 1-line addition of `kvfree` helper at error-path
return. Fix cannot regress because `run_close` on a zero-initialized
run is a no-op. No locking changes, no allocation, no behavioral
differences on the success path.
- Ratio: Strongly favors backporting.
### PHASE 9: SYNTHESIS
**Evidence FOR backporting:**
- Reported by syzbot with a C reproducer (fuzzer-confirmed,
reproducible)
- Reachable from unprivileged userspace via `link(2)` syscall
- Buggy code present since v5.15 (affects all active stable branches)
- Fix is 1 line, obviously correct, maintainer-tested before apply
- Fix path (`run_close` on init'd/NULL run) is trivially safe
- Clean applies expected across all stable trees (stable hunk context)
- Fits the "resource leak in error paths" pattern that's classic stable
material
- Reviewed/applied by subsystem maintainer (Konstantin Komarov)
**Evidence AGAINST backporting:**
- No Fixes: tag or Cc: stable in original submission (expected - this is
precisely the review-candidate category)
- Leak is not catastrophic per-event (gradual kernel memory consumption)
- No Reviewed-by/Tested-by tags in-tree (but syzbot confirmed the fix
works, and maintainer stated it was internally tested)
**Stable Rules Checklist:**
1. Obviously correct and tested: YES (trivial fix; syzbot confirmed;
maintainer tested)
2. Fixes a real bug affecting users: YES (syzbot reproducible from
unprivileged syscall)
3. Important issue: YES (kernel memory leak exploitable by unprivileged
user = DoS vector)
4. Small and contained: YES (1 line, 1 file)
5. No new features/APIs: YES (uses existing `run_close` helper)
6. Applies to stable: YES (clean apply expected)
**Exception category:** Not needed - this is a straightforward bug fix.
## Verification
- [Phase 1] Parsed tags: confirmed Reported-by: syzbot, Closes link to
syzkaller. No Fixes/Cc:stable tags (expected).
- [Phase 2] Read diff via `git show 87ac077d6ea86`: confirmed +1 line
(`run_close(&run);`) at `out:` label, nothing else.
- [Phase 2] Read `fs/ntfs3/ntfs_fs.h:1002-1006`: verified `run_close` =
`kvfree(run->runs); memset(run,0,...);` - safe on init'd run.
- [Phase 2] Read `fs/ntfs3/run.c:310-403`: verified `run_add_entry` at
line 390 calls `kvmalloc(bytes, GFP_KERNEL)` into `run->runs`.
- [Phase 2] Read `fs/ntfs3/fsntfs.c:2550-2564`: verified
`run_deallocate` only frees on-disk clusters, does NOT free
`run->runs`.
- [Phase 3] `git blame` on `fs/ntfs3/index.c` lines 1430-1485: buggy
structure present since `f7464060f7ab9a` (Linus, 2021-09-04).
- [Phase 3] `git describe --contains f7464060f7ab9a`: confirmed
v5.15-rc1~94 → bug present from v5.15 onward.
- [Phase 3] `git log --author="Deepanshu Kartikey"`: confirmed author is
a prolific bug-fix contributor.
- [Phase 4] `b4 dig -c 87ac077d6ea86`: found submission at
`lore.kernel.org/all/20260321050143.1117500-1-kartikey406@gmail.com`.
- [Phase 4] `b4 dig -a`: only 1 revision (v1).
- [Phase 4] `b4 dig -w`: recipients include ntfs3 maintainer and ML.
- [Phase 4] WebFetch syzkaller dashboard: confirmed reproducible (C
repro), commit is marked as the fix, patched across all syzbot
managers.
- [Phase 4] Read saved mbox thread: confirmed maintainer tested
internally and applied; no NAKs, no stable nomination requests.
- [Phase 5] `grep indx_create_allocate` in fs/ntfs3: only 1 caller
(`indx_insert_into_root` at line 1705).
- [Phase 5] Syzkaller stack trace confirms reachability:
`__x64_sys_link` → ... → `run_add_entry`.
- [Phase 6] `git log -- fs/ntfs3/index.c`: `out:` block unchanged since
initial merge → clean stable backport expected.
- [Phase 6] Similar fixes in `ccc4e86d1c242` and `b8155e95de38b` show
this class of patch is routinely accepted.
- [Phase 8] Severity assessment grounded in: syzbot kmemleak reporter,
unprivileged-user reachability, repeatable trigger.
- UNVERIFIED: Did not test actual `git am` of the patch onto each stable
branch; assessed clean-apply by context analysis only.
- UNVERIFIED: Exact behavior of per-distro ntfs3 default-mount policy
(minor impact on "who's affected" scope).
## Decision Rationale
This is a textbook stable-tree candidate: a one-line fix for a syzbot-
reproducible memory leak that is reachable from an unprivileged
`link(2)` syscall on ntfs3. The bug is present in every stable tree from
v5.15 forward, the fix is obviously correct (adds a safe idempotent
cleanup call on the error path while leaving the success path
untouched), it was applied by the subsystem maintainer after internal
testing, and the risk of regression is essentially zero. These are
exactly the criteria the stable rules prioritize: small, contained,
fixes a real bug, and obviously correct.
**YES**
fs/ntfs3/index.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/fs/ntfs3/index.c b/fs/ntfs3/index.c
index 97f06c26fe1a6..11f59d7b9ea45 100644
--- a/fs/ntfs3/index.c
+++ b/fs/ntfs3/index.c
@@ -1481,6 +1481,7 @@ static int indx_create_allocate(struct ntfs_index *indx, struct ntfs_inode *ni,
run_deallocate(sbi, &run, false);
out:
+ run_close(&run);
return err;
}
--
2.53.0
^ permalink raw reply related [flat|nested] 6+ messages in thread