* [PATCH] fs/ntfs3: fix integer overflow in find_log_rec() causing OOB read
@ 2026-07-08 22:46 Ibrahim Hashimov
0 siblings, 0 replies; only message in thread
From: Ibrahim Hashimov @ 2026-07-08 22:46 UTC (permalink / raw)
To: Konstantin Komarov; +Cc: ntfs3, linux-kernel, stable
find_log_rec() validates the on-disk LFS_RECORD_HDR.client_data_len
field (`len`, a raw u32 read straight from a mounted $LogFile page)
against the log's total available space:
len = le32_to_cpu(rh->client_data_len);
rec_len = len + log->record_header_len;
if (rec_len >= log->total_avail)
return -EINVAL;
The addition is unchecked. For len in [0xffffffd0, 0xffffffff] (i.e.
log->record_header_len away from UINT_MAX), `rec_len` wraps to a small
u32 value that trivially clears the `rec_len >= log->total_avail`
guard, so a record whose declared length is actually ~4 GiB is
reported as valid and in-page.
Because find_log_rec() wrongly accepts the record, its caller
read_log_rec_lcb() hands it back to log_replay() as good. On the
transaction-table replay leg, log_replay() re-derives the record
length directly from the same raw, still-unclamped client_data_len
field and uses it to size the restart-table blob:
rec_len = le32_to_cpu(frh->client_data_len); /* ~0xffffffff */
...
t16 = le16_to_cpu(lrh->redo_off);
rt = Add2Ptr(lrh, t16);
t32 = rec_len - t16; /* still ~4 GiB */
if (!check_rstbl(rt, t32))
return -EINVAL;
check_rstbl()'s own `bytes < ts` bounds guard is defeated because the
caller-supplied `bytes` (t32) is itself the poisoned ~4 GiB value, so
the guard can never trigger. The subsequent entry-validation loop then
walks `rt->used` (also attacker-controlled) entries of
sizeof(struct RESTART_TABLE) == 0x18 bytes each straight past the end
of the kmalloc(log->page_size) log-page buffer allocated in
read_log_page(), producing an out-of-bounds read that is reachable
simply by mounting a crafted $LogFile (no genuine dirty-page /
crash-recovery state is required). Confirmed via KASAN as a
slab-out-of-bounds read in check_rstbl(), called from log_replay() ->
ntfs_loadlog_and_replay() -> ntfs_fill_super() -> mount(2).
Fix the root cause instead of hardening every downstream consumer of
the poisoned length: make the addition in find_log_rec() overflow-safe
with check_add_overflow(), mirroring the pattern fs/ntfs3/run.c
already uses to validate other attacker-controlled on-disk
length/offset arithmetic decoded from MAPPING_PAIRS runs, e.g.:
if (check_add_overflow(vcn64, len, &next_vcn))
return -EINVAL;
...
if (check_add_overflow(lcn, len, &lcn_end))
return -EINVAL;
(fs/ntfs3/run.c, run_unpack()).
With the overflow rejected instead of silently wrapped, `rec_len` is
only ever a true, non-wrapped sum, so the existing
`rec_len >= log->total_avail` check now correctly rejects any
oversized record up front. log_replay()'s transaction-table leg then
never gets a chance to re-read the poisoned client_data_len, and
check_rstbl() is never invoked with an attacker-inflated `bytes` for
this path.
Verified on a v6.19 KASAN build: mounting a crafted $LogFile whose
client_data_len overflows trips a KASAN slab-out-of-bounds-read
report in check_rstbl() before this fix; with check_add_overflow()
applied, the same image is cleanly rejected during log replay and no
KASAN report fires.
Fixes: b46acd6a6a62 ("fs/ntfs3: Add NTFS journal")
Cc: stable@vger.kernel.org
Signed-off-by: Ibrahim Hashimov <security@auditcode.ai>
Assisted-by: AuditCode-AI:2026.07
---
fs/ntfs3/fslog.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/fs/ntfs3/fslog.c b/fs/ntfs3/fslog.c
index f038c799e7ac..e5b63a02b827 100644
--- a/fs/ntfs3/fslog.c
+++ b/fs/ntfs3/fslog.c
@@ -7,6 +7,7 @@
#include <linux/blkdev.h>
#include <linux/fs.h>
+#include <linux/overflow.h>
#include <linux/random.h>
#include <linux/slab.h>
@@ -2424,7 +2425,8 @@ static int find_log_rec(struct ntfs_log *log, u64 lsn, struct lcb *lcb)
* Check that the length field isn't greater than the total
* available space the log file.
*/
- rec_len = len + log->record_header_len;
+ if (check_add_overflow(len, log->record_header_len, &rec_len))
+ return -EINVAL;
if (rec_len >= log->total_avail)
return -EINVAL;
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] only message in thread
only message in thread, other threads:[~2026-07-08 22:46 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-08 22:46 [PATCH] fs/ntfs3: fix integer overflow in find_log_rec() causing OOB read Ibrahim Hashimov
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox