[PATCH RESEND] fs/ntfs3: fix log replay index walkers running past the entry chain
From: Guikun Yang
Date: Tue Oct 20 2026 - 07:07:25 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. They only re-check the buffer boundary at the top of
each iteration, and they do not stop at the NTFS_IE_LAST entry that
check_index_header() treats as the end of the validated chain.
The gap between the end of the validated chain and hdr->used is inside
the buffer but is never validated as directory entries. 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":
- 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.
Observed on v6.6.12 with KASAN (kasan_multi_shot=1), mounting the
crafted image read-write:
BUG: KASAN: slab-out-of-bounds in log_replay+0x9744/0xe850
Write of size 8 at addr ffff88804b097f40 by task mount/8065
Call Trace:
kasan_report
log_replay
ntfs_loadlog_and_replay
ntfs_fill_super
The buggy address belongs to the object at ffff88804b097f00
which belongs to the cache kmalloc-64 of size 64
The buggy address is located 0 bytes to the right of
allocated 64-byte region [ffff88804b097f00, ffff88804b097f40)
Allocated by task 8065:
kmemdup
log_replay+0x55bf/0xe850
The write repeats at consecutive addresses while the same log record
is replayed; 54 reports were collected in a single mount. Without
KASAN there is no message and the mount appears to succeed.
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 ("failed to replay log file. Can't mount
rw!") and no KASAN report is produced. An ordinary NTFS image still
mounts read-write, accepts writes and is readable after remount.
The image is 63 MiB (197 KiB gzipped). I can send it if you want to
reproduce it, or it can be regenerated: the crafted image only rewrites
the root directory index record and the $LogFile restart area of a stock
NTFS volume created with mkntfs -s 512 -c 4096.
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