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: b46acd6a6a ("fs/ntfs3: Add NTFS journal")
Cc: stable@vger.kernel.org
Reported-by: Weiming Shi <bestswngs@gmail.com>
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Xiang Mei <xmei5@asu.edu>
[almaz.alexandrovich@paragon-software.com: original patch contained changes to the problem already handled, applied partly]
Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
This commit is contained in:
Konstantin Komarov 2026-07-24 13:42:28 +02:00
parent 006cb7713d
commit 6f7b9dbdc1
No known key found for this signature in database
GPG Key ID: A9B0331F832407B6

View File

@ -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;