[PATCH] fs/ntfs3: fix log replay index walkers running past the entry chain
From: Guikun Yang
Date: Fri Oct 16 2026 - 13:51:06 EST
check_if_root_index() and check_if_alloc_index() walk the directory
entry chain of an index header until the walk offset reaches
lrh->attr_off, but they only re-check the buffer boundary at the top
of each iteration. The chain that check_index_header() validated ends
at its NTFS_IE_LAST entry; the walkers ignore that flag and keep
reading size fields from the gap between the end of the chain and
hdr->used, which is still inside the validated buffer. A crafted
$LogFile can place a size field there that jumps the walk straight to
an attr_off far outside the buffer, and the walker then reports
success.
The callers treat that result as "attr_off names a valid entry" and
act on it:
- SetIndexEntryVcnRoot/Allocation write the 8-byte log payload at
attr/ib + attr_off,
- AddIndexEntry* compute PtrOffset(e1, hdr + used) with e1 beyond
that point, which underflows to a huge length,
- Update/DeleteIndexEntry* read entry fields at the same offset.
Mounting such an image read-write therefore performs an out-of-bounds
write of controlled data at a controlled offset while replaying the
log. KASAN reports slab-out-of-bounds in log_replay() on v6.6; both
walkers are unchanged since they were introduced in b46acd6a6a62.
Stop the walk at the NTFS_IE_LAST entry so the landing offset can no
longer be driven past the end of the validated chain.
Tested on v6.6.12 and on a v7.3-rc5 based linux-next tree, both with
KASAN: with the crafted image the unpatched kernel reports
slab-out-of-bounds (write of size 8) during mount, while the patched
kernel rejects the record and the mount fails without touching memory
outside the buffer. An ordinary NTFS image still mounts read-write,
accepts writes and is readable after remount.
Fixes: b46acd6a6a62 ("fs/ntfs3: Add NTFS journal")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Guikun Yang <yangguikun19@xxxxxx>
---
fs/ntfs3/fslog.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/fs/ntfs3/fslog.c b/fs/ntfs3/fslog.c
--- a/fs/ntfs3/fslog.c
+++ b/fs/ntfs3/fslog.c
@@ -2956,6 +2956,7 @@ static inline bool check_if_root_index(const struct ATTRIB *attr,
while (o < ao) {
u16 esize;
+ bool last;
if (o >= asize)
break;
@@ -2964,8 +2965,13 @@ static inline bool check_if_root_index(const struct ATTRIB *attr,
if (!esize)
break;
+ last = de_is_last(e);
+
o += esize;
e = Add2Ptr(e, esize);
+
+ if (last)
+ break;
}
return o == ao;
@@ -2981,6 +2987,7 @@ static inline bool check_if_alloc_index(const struct INDEX_HDR *hdr,
while (o < attr_off) {
u16 esize;
+ bool last;
if (de_off >= used)
break;
@@ -2989,9 +2996,14 @@ static inline bool check_if_alloc_index(const struct INDEX_HDR *hdr,
if (!esize)
break;
+ last = de_is_last(e);
+
o += esize;
de_off += esize;
e = Add2Ptr(e, esize);
+
+ if (last)
+ break;
}
return o == attr_off;
--
2.39.5