[PATCH AUTOSEL 6.18-5.15] fs/ntfs3: Fix memory leak in indx_find_sort()

From: Sasha Levin

Date: Mon Oct 19 2026 - 10:41:13 EST


From: Konstantin Komarov <almaz.alexandrovich@xxxxxxxxxxxxxxxxxxxx>

[ Upstream commit 194491be54e1c9e4d29d3ee777b05ff031c4b670 ]

When popping a level from the index lookup stack, indx_find_sort() frees
the struct indx_node but not the index buffer it owns. Every call that
descends and then pops a level leaks that allocation.

Free n->index before freeing the node itself.

Signed-off-by: Konstantin Komarov <almaz.alexandrovich@xxxxxxxxxxxxxxxxxxxx>
Signed-off-by: Sasha Levin <sashal@xxxxxxxxxx>
---

LLM Generated explanations, may be completely bogus:

I have enough to decide: this is a one-line leak fix in
`indx_find_sort()`, the buggy code is in this 7.3-rc4 tree, and it meets
the stable rules.

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

**Step 1.1: PARSE THE SUBJECT LINE**
Record: **[fs/ntfs3] [fix] memory leak in `indx_find_sort()`** —
subsystem `fs/ntfs3`, action verb **fix**, claimed intent is to stop
leaking the index buffer when a lookup level is popped.

**Step 1.2: PARSE ALL COMMIT MESSAGE TAGS**
Commit `194491be54e1c9e4d29d3ee777b05ff031c4b670` (Author: Konstantin
Komarov, 2026-07-30):

- **Fixes:** none (expected for this review)
- **Reported-by:** none
- **Tested-by:** none
- **Reviewed-by:** none
- **Acked-by:** none
- **Link:** none in the commit message
- **Cc: stable:** none (expected)
- **Signed-off-by:** Konstantin Komarov \<almaz.alexandrovich@paragon-
software.com\> (author/maintainer)

No syzbot, no multi-user reports, no explicit stable nomination. Author
is the NTFS3 maintainer.

**Step 1.3: ANALYZE THE COMMIT BODY TEXT**
Record:
- **Bug:** When popping a level from the index lookup stack,
`indx_find_sort()` frees `struct indx_node` but not the index buffer
it owns (`n->index`).
- **Symptom:** Every call that descends into a child index block and
later pops that level leaks that allocation (typically a 4 KiB
`kmalloc` of `INDEX_BUFFER`).
- **Failure mode:** kernel memory leak on NTFS3 `$SDH` index walks.
- **Version info:** none in the message.
- **Root cause (author):** `kfree(n)` without `kfree(n->index)`.
- **Fix:** free `n->index` before freeing the node.

**Step 1.4: DETECT HIDDEN BUG FIXES**
Record: Not disguised. Subject and body state a leak fix. It is a
classic missing-`kfree` resource-leak fix, not cleanup-only.

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

**Step 2.1: INVENTORY THE CHANGES**
Record:
- File: `fs/ntfs3/index.c` — **+1 / −0**
- Function: `indx_find_sort()`
- Scope: **single-file surgical fix** (one line in the `pop_level` path)

**Step 2.2: UNDERSTAND THE CODE FLOW CHANGE**
Record (before → after):
- Before: `fnd_pop(fnd); kfree(n);` — node struct freed; `n->index` (and
buffer heads in `n->nb`) left allocated.
- After: `fnd_pop(fnd); kfree(n->index); kfree(n);` — index buffer and
node struct freed.
- Path: **normal** pop-level path when the current directory entry is
the last in that index block and the walk moves to the parent. Not
init-only.

**Step 2.3: IDENTIFY THE BUG MECHANISM**
Record: **[Memory leak / resource leak]**
- `indx_read()` / `indx_read_ra()` allocates `struct indx_node` and
`in->index` (`kmalloc(bytes, GFP_NOFS)` where `bytes = 1u <<
indx->index_bits`, typically 4096).
- Correct teardown is `put_indx_node()`: `kfree(in->index);
nb_put(&in->nb); kfree(in);`.
- Sibling `indx_find_raw()` already does `fnd_pop()` +
`put_indx_node(n)`.
- `indx_find_sort()` used `kfree(n)` only. This patch adds
`kfree(n->index)` on the pop path.
- Mechanism: missing free of owned `kmalloc` buffer.

Note (verified, not a reason to reject): the patch does **not** call
`nb_put(&n->nb)`, so buffer_head refs can still leak on this path. Two
other `kfree(n)` sites in the same function (invalid-entry error paths
at the `de_has_vcn_ex` loop) are also unchanged.

**Step 2.4: ASSESS THE FIX QUALITY**
Record:
- Obviously correct for the stated leak: `kfree(n->index)` matches the
first action of `put_indx_node()`; `kfree(NULL)` is safe; `fnd_pop()`
NULLs the stack slot so later `fnd_clear()`/`put_indx_node()` will not
double-free.
- Minimal: one line, no unrelated edits, no API change.
- Regression risk: **very low**. Does not change lookup results,
locking, or on-disk layout.
- Residual risk: incomplete vs `put_indx_node()` (BH leak remains). That
is an unfixed sibling leak, not a regression introduced by this line.

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

**Step 3.1: BLAME THE CHANGED LINES**
Record:
- Pop-level `kfree(n)` / `ntfs_free(n)` present since
**`82cae269cfa95`** (“fs/ntfs3: Add initialization of super block”,
2021-08-13) — NTFS3 introduction.
- `ntfs_free(n)` → `kfree(n)` in **`195c52bdd5d5e`** (“Do not use driver
own alloc wrappers”, 2021-08-24); `ntfs_free` was just `kfree`, so the
leak is original, not introduced by that conversion.
- This leak has existed for the entire life of NTFS3 in mainline (~5.15
onward).

**Step 3.2: FOLLOW THE FIXES: TAG**
Record: No `Fixes:` tag. Buggy code is the original `indx_find_sort()`
implementation in `82cae269cfa95`, which **is an ancestor of HEAD**.

**Step 3.3: CHECK FILE HISTORY FOR RELATED CHANGES**
Record:
- Recent `fs/ntfs3/index.c` work is mostly bounds checks and cosmetics.
- Related leak: `87ac077d6ea86` “ntfs3: fix memory leak in
indx_create_allocate()” — different function.
- No later commit after `194491be54e1c` changes this site (`git log
194491be54e1c..HEAD -- fs/ntfs3/index.c` is empty).
- Standalone 1-line patch, not “X/Y” of a series.

**Step 3.4: CHECK THE AUTHOR'S OTHER COMMITS**
Record: Konstantin Komarov is the NTFS3 maintainer (Paragon). Many
recent NTFS3 commits are his. Weight: high.

**Step 3.5: CHECK FOR DEPENDENT/PREREQUISITE COMMITS**
Record: None. The patch only uses existing `kfree`, `fnd_pop`, and
`n->index`. Those exist in this tree. **Can apply standalone.**

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

**Step 4.1: FIND THE ORIGINAL PATCH DISCUSSION**
Record:
- `b4 dig -c 194491be54e1c` → https://patch.msgid.link/20260724111443.14
728-1-almaz.alexandrovich@xxxxxxxxxxxxxxxxxxxx
- `b4 dig -a`: single revision **v1** only (2026-07-24). Committed
version matches v1.
- lore.kernel.org fetch returned 403; patch text confirmed via
Openwall/IU mirrors. No replies found on those archives (no NAK, no
Cc: stable in-thread).
- Earlier independent patch (Jan 2026) from Jiasheng Jiang: “[PATCH v2]
fs/ntfs3: Fix memory and resource leak in indx_find_sort” replacing
**all three** `kfree(n)` with `put_indx_node(n)` and adding `Fixes:
82cae269cfa9`. That fuller patch was **not** merged; the maintainer
later landed this narrower 1-line version.

**Step 4.2: CHECK WHO REVIEWED THE PATCH**
Record (`b4 dig -w`): To/Cc: Konstantin Komarov,
`ntfs3@xxxxxxxxxxxxxxx`, `linux-kernel@xxxxxxxxxxxxxxx`, `linux-
fsdevel@xxxxxxxxxxxxxxx`. No extra reviewer SOB on the commit.
Maintainer self-applied.

**Step 4.3: SEARCH FOR THE BUG REPORT**
Record: No `Reported-by` / bugzilla / syzbot. Bug established from code
(missing free vs `put_indx_node` / `indx_find_raw`). User-report count:
**unverified / none found**.

**Step 4.4: CHECK FOR RELATED PATCHES AND SERIES**
Record: Jiang’s Jan 2026 patch is the more complete form of the same
fix. This commit is a subset (pop path `n->index` only). No series
dependency for applying this one line.

**Step 4.5: CHECK STABLE MAILING LIST HISTORY**
Record: lore.kernel.org/stable search was 403; web search did not turn
up a stable-list thread for this subject. **No verified stable-list NAK
or prior selection discussion.**

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

**Step 5.1: IDENTIFY KEY FUNCTIONS IN THE DIFF**
Record: `indx_find_sort()` only.

**Step 5.2: TRACE CALLERS**
Record: Only caller in this tree is `ntfs_insert_security()` in
`fs/ntfs3/fsntfs.c` (SDH index walk when inserting a security
descriptor). That is called from:
- `inode.c` file/inode create when a new default/dir security id must be
stored
- `xattr.c` when setting a security descriptor xattr

Not interrupt context. Process context, under `ni->ni_lock`. Finder is
released with `fnd_put()` → `fnd_clear()` → `put_indx_node()` for
**remaining** stack nodes; popped nodes are supposed to be freed in
`indx_find_sort` itself.

**Step 5.3: TRACE CALLEES**
Record: `fnd_pop`, `kfree`, `indx_read`/`hdr_first_de`/`de_is_last` on
the surrounding path. `indx_read_ra` allocates node + index buffer +
buffer_heads.

**Step 5.4: FOLLOW THE CALL CHAIN**
Record: Userspace NTFS3 create/`setxattr` → `ntfs_insert_security` →
`indx_find` then loop `indx_find_sort`. **Reachable from userspace** on
a mounted ntfs3 volume. Not every I/O; triggered when inserting (or
searching duplicate) security descriptors in `$Secure::$SDH`. If the SDH
B-tree is deeper than root, pop_level runs and leaked before this fix.

**Step 5.5: SEARCH FOR SIMILAR PATTERNS**
Record: Same function still has `kfree(n)` without `kfree(n->index)` on
two **error** paths. `indx_find_raw` already uses `put_indx_node`.
Pattern is systematic in `indx_find_sort`, only partly fixed here.

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

**Step 6.1: DOES THE BUGGY CODE EXIST IN THIS TREE?**
Record:
- Local tree: **`v7.3-rc4-27-gf0100363d8c37`**, `make kernelversion` =
**7.3.0-rc4**.
- `fs/ntfs3/index.c` `indx_find_sort()` exists. Pre-image is `fnd_pop` +
`kfree(n)` without freeing `n->index`.
- Bug introduced with NTFS3 in **`82cae269cfa95` (2021)**, present in
this tree (`git merge-base --is-ancestor 82cae269cfa95 HEAD`
succeeded).
- This is not “new code after this branch point.”

**Step 6.2: CHECK FOR BACKPORT COMPLICATIONS**
Record: **Clean apply.** Surrounding function text is the original
structure; the hunk is one added line. No conflicting refactor of
`indx_find_sort` in this tree. `put_indx_node()` already exists if a
slightly richer backport were used, but this exact diff applies as-is.

**Step 6.3: CHECK IF RELATED FIXES ARE ALREADY HERE**
Record: No earlier alternative (Jiang `put_indx_node`) in this history.
This commit **is** the fix that landed (`ntfs3_for_7.3` merge
`dc83d18cdd904`). No competing fix for the same `kfree(n)` pop site.

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

**Step 7.1: IDENTIFY THE SUBSYSTEM AND ITS CRITICALITY**
Record: **[fs/ntfs3] [IMPORTANT]** — real RW filesystem
(CONFIG_NTFS3_FS), not staging. Not core VFS/mm, but used on Windows
volumes, dual-boot, and some appliances. Leak is in metadata index walk
for `$Secure`.

**Step 7.2: ASSESS SUBSYSTEM ACTIVITY**
Record: Active (many 7.3 NTFS3 fixes). The leak itself is old (2021),
not a bug in brand-new code.

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

**Step 8.1: DETERMINE WHO IS AFFECTED**
Record: **filesystem-specific / config-specific** — `CONFIG_NTFS3_FS`
users who insert security descriptors (file create with new/default SD,
security xattr). Not universal.

**Step 8.2: DETERMINE THE TRIGGER CONDITIONS**
Record:
- Trigger: `indx_find_sort()` after a descent into an index allocation
block, then last-entry pop.
- How common: not every syscall; **per unique-SD insert / SDH hash-chain
walk**, not a once-per-boot init-only leak. Repeated unique ACLs leak
repeatedly.
- Unprivileged users: if they can create files or set security xattrs on
ntfs3, yes. Typical create after `def_security_id` is cached may not
re-enter insert; still reachable.

**Step 8.3: DETERMINE THE FAILURE MODE SEVERITY**
Record: **[kernel memory leak of index buffers (and remaining BH refs)]
[HIGH for the kmalloc leak on a live FS path; not panic/corruption].**
Unbounded across many unique SD inserts; can contribute to memory
pressure. Not a crash or data-corruption bug.

**Step 8.4: CALCULATE RISK-BENEFIT RATIO**
Record:
- **Benefit:** medium-high for ntfs3 users — stops a real leak on a used
lookup path; 4 KiB+ per popped level.
- **Risk:** very low — 1 line, same pattern as `put_indx_node`’s first
free, no lock/API change.
- **Ratio:** strongly in favor of taking the fix.

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

**Step 9.1: COMPILE THE EVIDENCE**

FOR:
- Real leak: `kfree(n)` drops the node without freeing `n->index`
allocated in `indx_read_ra()`.
- Normal pop path, not style-only.
- 1 line, obviously correct, maintainer-authored.
- Code and bug exist in this 7.3-rc4 tree since NTFS3 was added.
- Applies cleanly; no prerequisites.
- Same class as many stable leak fixes; sibling `indx_find_raw` already
frees via `put_indx_node()`.
- Reachable from userspace ntfs3 create/xattr.

AGAINST:
- Not a crash, deadlock, or corruption issue.
- Incomplete: no `nb_put()`, two error-path `kfree(n)` sites remain.
- Single caller (`ntfs_insert_security`), not a hot readdir path.
- No syzbot/user `Reported-by`, no in-commit tests.

UNRESOLVED:
- Could not fetch lore.kernel.org (403) or confirm stable-list
discussion.
- No public reply thread found on mirrors for the July v1 mail.

**Step 9.2: APPLY THE STABLE RULES CHECKLIST**
1. Obviously correct and tested? **PASS** — correctness is visible from
`put_indx_node()` / `indx_find_raw()`. Testing is only implied by
maintainer merge, not a Tested-by tag.
2. Fixes a real bug that affects users? **PASS** — leaked kernel
allocations on ntfs3 SD insert/lookup.
3. Important issue (crash, security, corruption, deadlock)? **PASS** —
memory leak on a live FS path (stable routinely takes these). Not
CRITICAL severity, but it is a real resource leak, not cosmetic.
4. Small and contained? **PASS** — +1 line, one function.
5. No new features or APIs? **PASS**.
6. Can apply to the local tree? **PASS** — context matches; ntfs3 and
`indx_find_sort()` are present.

**Step 9.3: CHECK FOR EXCEPTION CATEGORIES**
Record: Not a device-ID/quirk/DT/build/docs exception. It is a normal
leak fix.

**Step 9.4: MAKE YOUR DECISION**
This tree is **7.3.0-rc4**. The leak exists in `indx_find_sort()` here
and has since NTFS3 landed. The patch is a one-line `kfree(n->index)` on
pop, matching the node’s ownership. Residual BH/error-path leaks do not
make this line wrong or risky. Benefit outweighs risk.

===============================================================
What problem this solves for this tree
===============================================================
`indx_find_sort()` walks the NTFS `$SDH` B-tree. After `indx_read()`
fills a child `indx_node`, popping that level used to `kfree` only the
node object. The owned index buffer stayed allocated for every descent-
then-pop. NTFS3 users inserting security descriptors (file create /
security xattr) leak that memory. This commit frees `n->index` first,
which is the correct teardown for that allocation.

It meets stable rules: obviously correct, real leak, small, no new API,
applies here. Risk is a single `kfree` on a pointer the node exclusively
owns after `fnd_pop()`.

Concerns: the fuller `put_indx_node()` replacement was proposed earlier
and would also drop buffer heads and fix two error paths; this commit is
a partial fix. That is acceptable for stable — it still plugs the main
pop-path kmalloc leak without depending on other commits.

===============================================================
Verification
===============================================================
- [Phase 1] Parsed commit `194491be54e1c`: subject/body leak fix; tags =
author SOB only; no Fixes/Reported-by/Cc: stable.
- [Phase 2] Diff: +1 line `kfree(n->index)` before `kfree(n)` in
`indx_find_sort()` pop_level; confirmed `put_indx_node()` does
`kfree(in->index); nb_put(); kfree(in)`.
- [Phase 2] Confirmed `indx_read_ra()` `kmalloc`s `ib` and assigns
`in->index = ib`.
- [Phase 2] Confirmed `indx_find_raw()` uses `put_indx_node(n)` after
`fnd_pop`.
- [Phase 3] `git blame`: pop path from `82cae269cfa95` (2021);
`kfree(n)` from `195c52bdd5d5e`; this line from `194491be54e1c`.
- [Phase 3] `git merge-base --is-ancestor 82cae269cfa95 HEAD` and
`194491be54e1c HEAD`: both true.
- [Phase 3] `git log 194491be54e1c..HEAD -- fs/ntfs3/index.c`: no later
change to this file.
- [Phase 3] No `Fixes:` tag to follow.
- [Phase 4] `b4 dig -c 194491be54e1c`: lore msgid
`20260724111443.14728-1-almaz.alexandrovich@xxxxxxxxxxxxxxxxxxxx`;
`-a` v1 only; `-w` ntfs3/fsdevel/lkml.
- [Phase 4] Openwall/IU mirrors: patch text matches commit; no replies
found.
- [Phase 4] Found unmerged Jan 2026 Jiasheng Jiang `put_indx_node` patch
(lkml.iu.edu / gnuweeb).
- [Phase 4] lore.kernel.org WebFetch: 403. Stable-list hit:
**UNVERIFIED**.
- [Phase 5] `git grep indx_find_sort`: only `ntfs_insert_security` +
declaration.
- [Phase 5] `ntfs_insert_security` `out:` calls `fnd_put(fnd_sdh)`.
- [Phase 6] `git describe HEAD` = `v7.3-rc4-27-gf0100363d8c37`; `make
kernelversion` = 7.3.0-rc4.
- [Phase 6] Pre-fix source at `194491be54e1c^` still has `kfree(n)`
without `kfree(n->index)`.
- [Phase 8] Failure mode: kmalloc leak of index buffers on SDH pop;
severity HIGH for leak class, not oops/corruption.
- UNVERIFIED: whether any user actually hit OOM from this; mailing-list
review comments beyond the isolated patch mail.

The fix is small, surgical, and meets the stable criteria for this tree.

**YES**

fs/ntfs3/index.c | 1 +
1 file changed, 1 insertion(+)

diff --git a/fs/ntfs3/index.c b/fs/ntfs3/index.c
index edfcd28573ef9..03ae5a817c1b4 100644
--- a/fs/ntfs3/index.c
+++ b/fs/ntfs3/index.c
@@ -1325,6 +1325,7 @@ int indx_find_sort(struct ntfs_index *indx, struct ntfs_inode *ni,
/* Pop one level. */
if (n) {
fnd_pop(fnd);
+ kfree(n->index);
kfree(n);
}

--
2.53.0