[PATCH AUTOSEL 6.18-5.15] ntfs3: fix info-leak in ntfs_rename()

From: Sasha Levin

Date: Mon Oct 19 2026 - 11:53:07 EST


From: Konstantin Komarov <almaz.alexandrovich@xxxxxxxxxxxxxxxxxxxx>

[ Upstream commit 2fa56613b25d0c983ef3ff6d3e865d4254280abe ]

Hard to say about copy_to_user_iter(), but at least the first splat
looks correct. At the end of fill_name_de(), data layout is:

struct NTFS_DE *e = buf;
...

|<- data_size + sizeof(struct NTFS_DE) ->|<- XXX ->|
buf |-----------------------------------------------------------
|<- ALIGN(data_size, 8) + sizeof(struct NTFS_DE) ->| ;; e->size

If 'buf' was allocated with kmalloc(), XXX remains uninitialized and
passed as such to memcpy() called from hdr_insert_de().

So using kzalloc() for all buffers passed to fill_name_de() looks
the simplest and most safe solution. OTOH if someone would have
said that an overhead of PAGE_SIZE'd memset() is too large, more
fine-granted solution is to memset() XXX only.

Reported-by: syzbot+905d785c4923bea2c1db@xxxxxxxxxxxxxxxxxxxxxxxxx
Closes: https://syzkaller.appspot.com/bug?extid=905d785c4923bea2c1db
Signed-off-by: Dmitry Antipov <dmantipov@xxxxxxxxx>
Signed-off-by: Konstantin Komarov <almaz.alexandrovich@xxxxxxxxxxxxxxxxxxxx>
Signed-off-by: Sasha Levin <sashal@xxxxxxxxxx>
---

LLM Generated explanations, may be completely bogus:

This is a real KMSAN kernel infoleak in ntfs3 rename: 8-byte alignment
padding in directory entries is copied from uninitialized `kmalloc()`
memory onto the volume. The local tree is **7.3.0-rc4**
(`v7.3-rc4-27-gf0100363d8c37`); ntfs3 and `fill_name_de()` exist here,
and the fix is small, correct, and reachable from `rename(2)`.

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

**Step 1.1: PARSE THE SUBJECT LINE**
Record: **[ntfs3] [fix] info-leak in `ntfs_rename()`**

**Step 1.2: PARSE ALL COMMIT MESSAGE TAGS**
Record:
- **Reported-by:**
`syzbot+905d785c4923bea2c1db@xxxxxxxxxxxxxxxxxxxxxxxxx` (syzbot /
KMSAN)
- **Closes:**
`https://syzkaller.appspot.com/bug?extid=905d785c4923bea2c1db`
- **Signed-off-by:** Dmitry Antipov (author of the analysis/patch);
Konstantin Komarov (ntfs3 maintainer, applied it)
- **No Fixes:** tag on the committed version (v1 on-list had `Fixes:
ca2a04e84af7`, dropped later)
- **No Cc: stable** (expected for this review)
- **No Tested-by / Reviewed-by / Acked-by / Link:** (Closes URL is the
bug tracker)

Notable: syzbot + maintainer SOB.

**Step 1.3: ANALYZE THE COMMIT BODY TEXT**
Record:
- **Bug:** After `fill_name_de()`, `e->size` is 8-byte-aligned
(`ALIGN(data_size, 8) + sizeof(NTFS_DE)`). The pad between `data_size
+ sizeof(NTFS_DE)` and that aligned size (`XXX`) is never written if
`buf` came from `kmalloc()`.
- **Failure mode:** That pad is `memcpy()`’d in `hdr_insert_de()`,
written out with the index/MFT record, then observed as a **KMSAN
kernel-infoleak** when the block is later read into userspace
(`copy_to_user_iter` / `filemap_read`).
- **Root cause (author):** uninitialized alignment padding, not a
`rename()` copy_to_user of the name buffer itself.
- **Fix chosen:** memset only `XXX` (0–7 bytes), instead of
`kzalloc(PATH_MAX)` for every caller.
- **Version info in message:** none.

**Step 1.4: DETECT HIDDEN BUG FIXES**
Record: Not hidden. Subject and body describe a KMSAN infoleak. The
“fine-grained memset” wording is the fix, not cleanup.

===============================================================
PHASE 2: DIFF ANALYSIS
===============================================================

**Step 2.1: INVENTORY THE CHANGES**
Record:
- **File:** `fs/ntfs3/namei.c` only (`+7 / -2`)
- **Function:** `fill_name_de()`
- **Scope:** single-file surgical fix

**Step 2.2: UNDERSTAND THE CODE FLOW CHANGE**
Record:
- **Before:** `e->size = cpu_to_le16(ALIGN(data_size, 8) + sizeof(struct
NTFS_DE));` with no write of the pad.
- **After:** compute `real_size` and `aligned_size`; if they differ,
`memset` the pad; then set `e->size` to `aligned_size`.
- **Path:** every `fill_name_de()` caller (rename still uses
`kmalloc(PATH_MAX)`; create/link/unlink already `kzalloc`).

**Step 2.3: IDENTIFY THE BUG MECHANISM**
Record:
- **Category:** uninitialized data / kernel infoleak (KMSAN)
- **Mechanism:** `hdr_insert_de()` does `memcpy(before, de, de_size)`
with `de_size = le16_to_cpu(de->size)` including alignment pad. Pad
bytes are slab leftovers. They land in the directory index, get
written via `ni_write_inode` → `ntfs_write_bh`, then leak through
`blkdev_read_iter` / `filemap_read`.

**Step 2.4: ASSESS THE FIX QUALITY**
Record:
- Obviously correct: pad length is `ALIGN(x,8)-x` ∈ [0,7]; memset only
when needed.
- Minimal; no API/locking/refcount changes.
- Regression risk very low: those bytes were garbage; NTFS index entries
are 8-byte aligned and pad should be zero.
- Completeness: this matches the KMSAN “Bytes 404-407 of 1536” (4-byte
pad). `ni_add_name()` still fills `ref`/`home`/`dup` after
`fill_name_de()`, so this is not trying to zero the whole
`ATTR_FILE_NAME`.

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

**Step 3.1: BLAME THE CHANGED LINES**
Record:
- Alignment-in-`e->size` comes from **4342306f0f0d5** (`fs/ntfs3: Add
file operations and implementation`, 2021-08-13; originally
`QuadAlign(data_size)`).
- **fa3cacf544636** only switched `QuadAlign` to `ALIGN`.
- Bug present since ntfs3 was added (**v5.15-era**).

**Step 3.2: FOLLOW THE FIXES: TAG**
Record: Committed version has **no Fixes:** tag. On-list v1 used `Fixes:
ca2a04e84af7` (`ntfs: ->d_compare() must not block`), which swapped
`__getname()` for `kmalloc()` — both uninitialized. That tag is
**wrong**; the leak is as old as `fill_name_de()`. `ca2a04e84af79`
**is** an ancestor of this HEAD, but is **not** a prerequisite for the
memset.

**Step 3.3: CHECK FILE HISTORY**
Record: Recent `namei.c` work is ADS, mkdir, vfs `->create`. **No
follow-up** to this infoleak. Not patch X/Y. Standalone.

**Step 3.4: CHECK THE AUTHOR'S OTHER COMMITS**
Record: Dmitry Antipov has no other recent ntfs3 commits in this
history. Konstantin Komarov is the ntfs3 maintainer and applied the
memset form.

**Step 3.5: CHECK FOR DEPENDENT/PREREQUISITE COMMITS**
Record: Uses existing `fill_name_de`, `ALIGN`, `fname_full_size`,
`NTFS_DE`. **Applies standalone.** `ntfs_rename` can keep `kmalloc`.

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

**Step 4.1: FIND THE ORIGINAL PATCH DISCUSSION**
Record:
- **b4 dig:**
https://patch.msgid.link/20260609123618.712961-1-dmantipov@xxxxxxxxx
- **Series:** v1 only (`[PATCH] ntfs3: fix info-leak in ntfs_rename()`),
2026-06-09
- **v1 diff:** `kmalloc` → `kzalloc` in `ntfs_rename` only
- **Al Viro** asked how that buffer reaches `copy_to_user_iter()`, then
guessed a deeper metadata/bh reuse bug
- **Dmitry** explained the pad/`hdr_insert_de` path and posted the
memset version (what landed)
- **Konstantin (2026-07-15):** “The patch is applied, thank you.” No
NAK. No Cc: stable in-thread.

**Step 4.2: CHECK WHO REVIEWED THE PATCH**
Record (**b4 dig -w**): Dmitry Antipov, Konstantin Komarov, Al Viro,
`ntfs3@xxxxxxxxxxxxxxx`, `lvc-project@xxxxxxxxxxxxxxxx`, syzbot.
Maintainer applied it after VFS questions.

**Step 4.3: SEARCH FOR THE BUG REPORT**
Record (syzkaller):
- Title: **KMSAN: kernel-infoleak in filemap_read**
- First crash 2023-12-20; C repro; **9090** crashes; prio:high
- Uninit created at `kmalloc` in `ntfs_rename`; stored in
`hdr_insert_de` → `indx_insert_into_root` → `ni_rename`; written via
`ntfs_write_bh`; copied out via `blkdev_read_iter` (sample: **udevd**)
- Syzbot lists **2fa56613b25d** as the fix commit

**Step 4.4: CHECK FOR RELATED PATCHES AND SERIES**
Record: Standalone. Related local infoleak: `35d1ea92c7d94` (LZNT
decompress) — different path.

**Step 4.5: CHECK STABLE MAILING LIST HISTORY**
Record: **UNVERIFIED** — lore.kernel.org/stable returned 403. No local
evidence this was rejected for stable.

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

**Step 5.1: KEY FUNCTIONS**
Record: `fill_name_de()`; leak sink `hdr_insert_de()`; user entry
`ntfs_rename()` → `ni_rename()` → `ni_add_name()` →
`indx_insert_entry()`.

**Step 5.2: TRACE CALLERS**
Record (`git grep`):
- `ntfs_rename()` — `rename(2)`; `kmalloc(PATH_MAX)` (**the syzbot
path**)
- `ntfs_create_inode()` — create/mkdir/symlink; already `kzalloc`
- `ntfs_link_inode()` — `link(2)`; already `kzalloc`
- `ntfs_unlink_inode()` — unlink/rmdir; already `kzalloc`

**Step 5.3: TRACE CALLEES**
Record: `ntfs_nls_to_utf16` / `memcpy` for the name; `fname_full_size`;
then `memset` of pad.

**Step 5.4: FOLLOW THE CALL CHAIN**
Record: Userspace `rename()` on ntfs3 → directory index insert →
MFT/index write → later `read()` of the block device (udev) copies pad
to userspace. **Reachable from a normal syscall** on a mounted ntfs3
volume.

**Step 5.5: SEARCH FOR SIMILAR PATTERNS**
Record: Other `fill_name_de` callers already `kzalloc`. Rename is the
remaining `kmalloc` caller; fixing `fill_name_de` covers all of them.

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

**Step 6.1: DOES THE BUGGY CODE EXIST IN THIS TREE?**
Record:
- Tree: **7.3.0-rc4** (`git describe`: `v7.3-rc4-27-gf0100363d8c37`)
- `fs/ntfs3/namei.c` and `fill_name_de()` exist
- Bug introduced **4342306f0f0d5** (ntfs3 add, ~5.15); **is ancestor of
HEAD**
- Current `fill_name_de()` **already contains** this memset (`git merge-
base --is-ancestor 2fa56613b25d0 HEAD` → yes)
- `ntfs_rename` still does `de = kmalloc(PATH_MAX, GFP_KERNEL)` — the
memset is what stops the leak

**Step 6.2: CHECK FOR BACKPORT COMPLICATIONS**
Record: Hunk is local to `fill_name_de` and matches the long-standing
function shape (`QuadAlign`/`ALIGN` + `e->size`). **Clean apply** on any
tree with original `fill_name_de`. No structural prereq.

**Step 6.3: CHECK IF RELATED FIXES ARE ALREADY HERE**
Record: This **is** the fix (`2fa56613b25d0`). No later replacement.
LZNT infoleak is separate.

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

**Step 7.1: SUBSYSTEM AND CRITICALITY**
Record: **fs/ntfs3** — **IMPORTANT** (in-tree RW NTFS since 5.15; not
core VFS, but a real production FS). Not staging.

**Step 7.2: SUBSYSTEM ACTIVITY**
Record: Actively maintained (Paragon / Komarov); mix of features and
sanitizer/bounds fixes.

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

**Step 8.1: WHO IS AFFECTED**
Record: **Filesystem-specific** — `CONFIG_NTFS3_FS` users who rename on
ntfs3. Trigger is rename, not an obscure ioctl.

**Step 8.2: TRIGGER CONDITIONS**
Record: `rename(2)` when the new name is inserted into a directory
index. Common. Unprivileged users with write access to an ntfs3 mount
can write slab leftovers into on-disk index entries. The syzbot read
path is `blkdev_read_iter` (typically privileged udev), but the
**confidentiality failure is writing kernel heap into the volume**.

**Step 8.3: FAILURE MODE SEVERITY**
Record: **KMSAN kernel-infoleak** of uninitialized kmalloc memory onto
disk / into a later userspace read. **Severity: HIGH** (security
infoleak; not a panic). Syzbot prio:high; C repro; thousands of crashes.

**Step 8.4: RISK-BENEFIT RATIO**
Record:
- **Benefit:** high — stops a real, fuzzer-reproduced kernel infoleak on
a syscall path
- **Risk:** very low — 5-line memset of 0–7 pad bytes
- **Ratio:** strongly favors taking the fix

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

**Step 9.1: COMPILE THE EVIDENCE**

FOR:
- Syzbot KMSAN kernel-infoleak with C repro and a clear `kmalloc` →
`hdr_insert_de` → disk → `copy_to_user` chain
- Small, obviously correct memset of alignment padding
- ntfs3 maintainer applied it after discussion with Al Viro
- Bug present since ntfs3 introduction; code exists in this tree
- No new API, no locking, no series dependencies
- Rename still uses `kmalloc`; without this, the leak remains

AGAINST:
- Al Viro first wondered if `copy_to_user` meant a deeper metadata-reuse
bug; Dmitry’s pad explanation is what was applied, and it matches the
first splat
- Infoleak, not a crash (still stable-worthy)
- lore.kernel.org HTTP 403; stable-list history not independently
fetched

UNRESOLVED:
- Whether older stable series already carried a different zeroing
approach (not this 7.3 tree)
- Whether KMSAN instances still listed as “missing” are stale vs. a
second leak

**Step 9.2: STABLE RULES CHECKLIST**
1. Obviously correct and tested? **PASS** — pad math is local; syzbot +
maintainer apply
2. Fixes a real bug that affects users? **PASS** — KMSAN infoleak, C
repro, udev read of written metadata
3. Important (crash/security/corruption/deadlock)? **PASS** — kernel
infoleak / security, HIGH
4. Small and contained? **PASS** — `fill_name_de` only, ~5 lines
5. No new features or APIs? **PASS**
6. Can apply to the local tree? **PASS** — same function exists; no
missing helpers

**Step 9.3: EXCEPTION CATEGORIES**
Record: None (not an ID/quirk/DT/build/doc exception). It is a normal
memory-safety fix.

**Step 9.4: DECISION**
This tree has ntfs3 and the buggy `fill_name_de`/`ntfs_rename` design.
The change is a contained KMSAN infoleak fix with a documented syscall
trigger. It meets stable criteria on technical merit.

Verification:
- [Phase 1] Tags: syzbot Reported-by + Closes URL; maintainer SOB; no
Fixes on committed form
- [Phase 2] Diff: +7/-2 in `fill_name_de`; memset of `aligned_size -
real_size`
- [Phase 2] `hdr_insert_de` (index.c:873): `memcpy(before, de, de_size)`
with `de_size` from `de->size`
- [Phase 3] Blame: 4342306f0f0d5 introduced QuadAlign size;
fa3cacf544636 switched to ALIGN
- [Phase 3] `git merge-base --is-ancestor 4342306f0f0d5 HEAD` → 0 (bug
origin in this tree)
- [Phase 3] `git merge-base --is-ancestor 2fa56613b25d0 HEAD` → 0 (this
SHA is in HEAD)
- [Phase 3] Original v1 Fixes: ca2a04e84af79 is in HEAD but is not
required for the memset
- [Phase 4] `b4 dig -c 2fa56613b25d0`: lore msgid
`20260609123618.712961-1-dmantipov@xxxxxxxxx`
- [Phase 4] `b4 dig -a`: v1 only; committed code is Dmitry’s follow-up
memset, not v1 kzalloc
- [Phase 4] `b4 dig -w`: ntfs3 list, Komarov, Al Viro, syzbot, lvc-
project
- [Phase 4] mbox: Viro questions; Dmitry pad diagram; Komarov “patch is
applied”
- [Phase 4] WebFetch lore.kernel.org → 403; used b4 mbox instead
- [Phase 4] syzbot: KMSAN infoleak, C repro, uninit at ntfs_rename
kmalloc, store at hdr_insert_de
- [Phase 5] Callers: rename (kmalloc), create/link/unlink (kzalloc)
- [Phase 5] `ni_add_name` fills ref/home/dup after fill_name_de; then
`indx_insert_entry`
- [Phase 6] `git describe HEAD` = v7.3-rc4-27-gf0100363d8c37; `make
kernelversion` = 7.3.0-rc4
- [Phase 6] Current namei.c lines 56–61 already have the memset
- [Phase 8] Failure mode: kernel heap bytes written into NTFS index and
readable via blkdev read
- UNVERIFIED: stable@ mailing-list discussion (lore 403)

**YES**

fs/ntfs3/namei.c | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/fs/ntfs3/namei.c b/fs/ntfs3/namei.c
index 64cde1a856f44..f547ce82520f8 100644
--- a/fs/ntfs3/namei.c
+++ b/fs/ntfs3/namei.c
@@ -22,7 +22,7 @@ int fill_name_de(struct ntfs_sb_info *sbi, void *buf, const struct qstr *name,
{
int err;
struct NTFS_DE *e = buf;
- u16 data_size;
+ u16 data_size, real_size, aligned_size;
struct ATTR_FILE_NAME *fname = (struct ATTR_FILE_NAME *)(e + 1);

#ifndef CONFIG_NTFS3_64BIT_CLUSTER
@@ -53,7 +53,12 @@ int fill_name_de(struct ntfs_sb_info *sbi, void *buf, const struct qstr *name,
fname->type = FILE_NAME_POSIX;
data_size = fname_full_size(fname);

- e->size = cpu_to_le16(ALIGN(data_size, 8) + sizeof(struct NTFS_DE));
+ real_size = data_size + sizeof(struct NTFS_DE);
+ aligned_size = ALIGN(data_size, 8) + sizeof(struct NTFS_DE);
+ if (aligned_size > real_size)
+ memset((char *)buf + real_size, 0, aligned_size - real_size);
+
+ e->size = cpu_to_le16(aligned_size);
e->key_size = cpu_to_le16(data_size);
e->flags = 0;
e->res = 0;
--
2.53.0