From 6f7b9dbdc1b7520206abce0049bdd143eb536e75 Mon Sep 17 00:00:00 2001 From: Konstantin Komarov Date: Fri, 24 Jul 2026 13:42:28 +0200 Subject: [PATCH] fs/ntfs3: bound page_lcns[] index by the log record The copy_lcns loop and the redo shorten loop index page_lcns[] at j + i, where i runs up to the log record's lcns_follow. That count is checked only against the record's own length, not the target entry, so check_dp_table() (which validates the entry's lcns_follow) does not cover it: the copy_lcns entry may even be freshly allocated after that check, and find_dp() bounds j but not i. A crafted record thus overflows page_lcns[] of an otherwise valid entry. Add dp_range_ok() and reject, before each loop, any record whose run does not fit the entry. These are the only two page_lcns[] accesses indexed by the record rather than the entry, so together with the entry validation every access is now bounded. Fixes: b46acd6a6a62 ("fs/ntfs3: Add NTFS journal") Cc: stable@vger.kernel.org Reported-by: Weiming Shi Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Xiang Mei [almaz.alexandrovich@paragon-software.com: original patch contained changes to the problem already handled, applied partly] Signed-off-by: Konstantin Komarov --- fs/ntfs3/fslog.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/fs/ntfs3/fslog.c b/fs/ntfs3/fslog.c index db5eec0aab61..a7cddce30136 100644 --- a/fs/ntfs3/fslog.c +++ b/fs/ntfs3/fslog.c @@ -647,6 +647,14 @@ static inline void *enum_rstbl(struct RESTART_TABLE *t, void *c) return NULL; } +/* + * dp_range_ok - true if [j, j + count) fits in a page_lcns[cap] array. + */ +static inline bool dp_range_ok(size_t j, u32 count, u32 cap) +{ + return j < cap && count <= cap - j; +} + /* * find_dp - Search for a @vcn in Dirty Page Table. */ @@ -5104,6 +5112,13 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) /* Shorten length by any Lcns which were deleted. */ saved_len = dlen; + if (!dp_range_ok(le64_to_cpu(lrh->target_vcn) - le64_to_cpu(dp->vcn), + le16_to_cpu(lrh->lcns_follow), + le32_to_cpu(dp->lcns_follow))) { + err = -EINVAL; + goto out; + } + for (i = le16_to_cpu(lrh->lcns_follow); i; i--) { size_t j; u32 alen, voff;