* FAILED: patch "[PATCH] fs/ntfs3: bound copy_lcns dp->page_lcns[] index in analysis" failed to apply to 5.15-stable tree
@ 2026-07-20 14:25 gregkh
2026-07-24 10:48 ` [PATCH 5.15.y 1/4] fs/ntfs3: Make ntfs_update_mftmirr return void Sasha Levin
0 siblings, 1 reply; 5+ messages in thread
From: gregkh @ 2026-07-20 14:25 UTC (permalink / raw)
To: michael.bommarito, 20260502154252.164586-1-jhapavitra98,
almaz.alexandrovich
Cc: stable
The patch below does not apply to the 5.15-stable tree.
If someone wants it applied there, or to any other stable or longterm
tree, then please email the backport, including the original git commit
id to <stable@vger.kernel.org>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-5.15.y
git checkout FETCH_HEAD
git cherry-pick -x 5e7b598660cfa8e5af172cf4c65cffc126333307
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<stable@vger.kernel.org>' --in-reply-to '2026072042-litter-retold-6915@gregkh' --subject-prefix 'PATCH 5.15.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 5e7b598660cfa8e5af172cf4c65cffc126333307 Mon Sep 17 00:00:00 2001
From: Michael Bommarito <michael.bommarito@gmail.com>
Date: Fri, 15 May 2026 12:34:05 -0400
Subject: [PATCH] fs/ntfs3: bound copy_lcns dp->page_lcns[] index in analysis
pass
In log_replay()'s analysis pass, after find_dp() returns a
valid DIR_PAGE_ENTRY for the (target_attr, target_vcn) tuple,
the copy_lcns block walks lrh->lcns_follow further entries:
t16 = le16_to_cpu(lrh->lcns_follow);
for (i = 0; i < t16; i++) {
size_t j = (size_t)(le64_to_cpu(lrh->target_vcn) -
le64_to_cpu(dp->vcn));
dp->page_lcns[j + i] = lrh->page_lcns[i];
}
find_dp() only validates that target_vcn falls within
[dp->vcn, dp->vcn + dp->lcns_follow), i.e., that the FIRST
cluster is covered. The walk through the further entries is
not bounded against dp->lcns_follow. For a malformed LRH
where target_vcn = dp->vcn + dp->lcns_follow - 1 and
lrh->lcns_follow > 1, the i > 0 writes overflow the dp's
allocated page_lcns[] array.
Add the missing j + lrh->lcns_follow <= dp->lcns_follow guard.
Reproduced under UML+KASAN on mainline 8d90b09e6741 as a
slab-out-of-bounds write of size 8 from log_replay+0x68d4 on
the mount path.
This is distinct from Pavitra Jha's 2026-05-02 patch
("fs/ntfs3: validate lcns_follow in log_replay conversion",
<20260502154252.164586-1-jhapavitra98@gmail.com>) which
addresses the separate version-0 dirty-page-table conversion
path's memmove(&dp->vcn, ...) call. The two fixes are
complementary; both should land.
Fixes: b46acd6a6a62 ("fs/ntfs3: Add NTFS journal")
Cc: stable@vger.kernel.org
Assisted-by: Claude:claude-opus-4-7
Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
[almaz.alexandrovich@paragon-software.com: clang-formatted the changes,
fixed conflicts]
Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
diff --git a/fs/ntfs3/fslog.c b/fs/ntfs3/fslog.c
index 2bd8754ef26d..81a305d4bcf2 100644
--- a/fs/ntfs3/fslog.c
+++ b/fs/ntfs3/fslog.c
@@ -3394,8 +3394,8 @@ static int do_action(struct ntfs_log *log, struct OPEN_ATTR_ENRTY *oe,
if (run_get_highest_vcn(le64_to_cpu(attr->nres.svcn),
attr_run(attr),
- le32_to_cpu(attr->size) -
- le16_to_cpu(attr->nres.run_off),
+ le32_to_cpu(attr->size) -
+ le16_to_cpu(attr->nres.run_off),
&t64)) {
goto dirty_vol;
}
@@ -4585,22 +4585,34 @@ int log_replay(struct ntfs_inode *ni, bool *initialized)
* whole routine a loop, case Lcns do not fit below.
*/
t16 = le16_to_cpu(lrh->lcns_follow);
- t32 = le32_to_cpu(dp->lcns_follow);
- if (le64_to_cpu(lrh->target_vcn) < le64_to_cpu(dp->vcn)) {
- err = -EINVAL;
- goto out;
- }
+ t32 = le32_to_cpu(dp->lcns_follow);
+ if (le64_to_cpu(lrh->target_vcn) < le64_to_cpu(dp->vcn)) {
+ err = -EINVAL;
+ goto out;
+ }
- for (i = 0; i < t16; i++) {
- size_t j = (size_t)(le64_to_cpu(lrh->target_vcn) -
- le64_to_cpu(dp->vcn));
- if (j >= t32 || i >= t32 - j) {
- err = -EINVAL;
- goto out;
- }
- dp->page_lcns[j + i] = lrh->page_lcns[i];
- }
+ /*
+ * find_dp() only validates that target_vcn is the first
+ * cluster covered by dp. The walk through lrh->lcns_follow
+ * further entries must stay within the allocated
+ * dp->page_lcns[] array, which is sized by dp->lcns_follow.
+ */
+ if (le64_to_cpu(lrh->target_vcn) - le64_to_cpu(dp->vcn) + t16 >
+ le32_to_cpu(dp->lcns_follow)) {
+ err = -EINVAL;
+ log->set_dirty = true;
+ goto out;
+ }
+ for (i = 0; i < t16; i++) {
+ size_t j = (size_t)(le64_to_cpu(lrh->target_vcn) -
+ le64_to_cpu(dp->vcn));
+ if (j >= t32 || i >= t32 - j) {
+ err = -EINVAL;
+ goto out;
+ }
+ dp->page_lcns[j + i] = lrh->page_lcns[i];
+ }
goto next_log_record_analyze;
case DeleteDirtyClusters: {
^ permalink raw reply related [flat|nested] 5+ messages in thread* [PATCH 5.15.y 1/4] fs/ntfs3: Make ntfs_update_mftmirr return void 2026-07-20 14:25 FAILED: patch "[PATCH] fs/ntfs3: bound copy_lcns dp->page_lcns[] index in analysis" failed to apply to 5.15-stable tree gregkh @ 2026-07-24 10:48 ` Sasha Levin 2026-07-24 10:48 ` [PATCH 5.15.y 2/4] fs/ntfs3: extend ni_insert_nonresident to return inserted ATTR_LIST_ENTRY Sasha Levin ` (2 more replies) 0 siblings, 3 replies; 5+ messages in thread From: Sasha Levin @ 2026-07-24 10:48 UTC (permalink / raw) To: stable; +Cc: Pavel Skripkin, Konstantin Komarov, Sasha Levin From: Pavel Skripkin <paskripkin@gmail.com> [ Upstream commit e66af07ca2b57e01dbf6001ae565ab40037b2df3 ] None of callers check the return value of ntfs_update_mftmirr(), so make it return void to make code simpler. Signed-off-by: Pavel Skripkin <paskripkin@gmail.com> Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com> Stable-dep-of: 5e7b598660cf ("fs/ntfs3: bound copy_lcns dp->page_lcns[] index in analysis pass") Signed-off-by: Sasha Levin <sashal@kernel.org> --- fs/ntfs3/fsntfs.c | 20 +++++++------------- fs/ntfs3/ntfs_fs.h | 2 +- 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/fs/ntfs3/fsntfs.c b/fs/ntfs3/fsntfs.c index 566dd9083b8ff4..ab9c75e092c299 100644 --- a/fs/ntfs3/fsntfs.c +++ b/fs/ntfs3/fsntfs.c @@ -830,7 +830,7 @@ int ntfs_refresh_zone(struct ntfs_sb_info *sbi) /* * ntfs_update_mftmirr - Update $MFTMirr data. */ -int ntfs_update_mftmirr(struct ntfs_sb_info *sbi, int wait) +void ntfs_update_mftmirr(struct ntfs_sb_info *sbi, int wait) { int err; struct super_block *sb = sbi->sb; @@ -839,12 +839,12 @@ int ntfs_update_mftmirr(struct ntfs_sb_info *sbi, int wait) u32 bytes; if (!sb) - return -EINVAL; + return; blocksize = sb->s_blocksize; if (!(sbi->flags & NTFS_FLAGS_MFTMIRR)) - return 0; + return; err = 0; bytes = sbi->mft.recs_mirr << sbi->record_bits; @@ -855,16 +855,13 @@ int ntfs_update_mftmirr(struct ntfs_sb_info *sbi, int wait) struct buffer_head *bh1, *bh2; bh1 = sb_bread(sb, block1++); - if (!bh1) { - err = -EIO; - goto out; - } + if (!bh1) + return; bh2 = sb_getblk(sb, block2++); if (!bh2) { put_bh(bh1); - err = -EIO; - goto out; + return; } if (buffer_locked(bh2)) @@ -884,13 +881,10 @@ int ntfs_update_mftmirr(struct ntfs_sb_info *sbi, int wait) put_bh(bh2); if (err) - goto out; + return; } sbi->flags &= ~NTFS_FLAGS_MFTMIRR; - -out: - return err; } /* diff --git a/fs/ntfs3/ntfs_fs.h b/fs/ntfs3/ntfs_fs.h index d93cba03a65aae..a03dce278221e8 100644 --- a/fs/ntfs3/ntfs_fs.h +++ b/fs/ntfs3/ntfs_fs.h @@ -599,7 +599,7 @@ int ntfs_look_free_mft(struct ntfs_sb_info *sbi, CLST *rno, bool mft, void ntfs_mark_rec_free(struct ntfs_sb_info *sbi, CLST rno, bool is_mft); int ntfs_clear_mft_tail(struct ntfs_sb_info *sbi, size_t from, size_t to); int ntfs_refresh_zone(struct ntfs_sb_info *sbi); -int ntfs_update_mftmirr(struct ntfs_sb_info *sbi, int wait); +void ntfs_update_mftmirr(struct ntfs_sb_info *sbi, int wait); enum NTFS_DIRTY_FLAGS { NTFS_DIRTY_CLEAR = 0, NTFS_DIRTY_DIRTY = 1, -- 2.53.0 ^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH 5.15.y 2/4] fs/ntfs3: extend ni_insert_nonresident to return inserted ATTR_LIST_ENTRY 2026-07-24 10:48 ` [PATCH 5.15.y 1/4] fs/ntfs3: Make ntfs_update_mftmirr return void Sasha Levin @ 2026-07-24 10:48 ` Sasha Levin 2026-07-24 10:48 ` [PATCH 5.15.y 3/4] fs/ntfs3: Fix very fragmented case in attr_punch_hole Sasha Levin 2026-07-24 10:49 ` [PATCH 5.15.y 4/4] fs/ntfs3: bound copy_lcns dp->page_lcns[] index in analysis pass Sasha Levin 2 siblings, 0 replies; 5+ messages in thread From: Sasha Levin @ 2026-07-24 10:48 UTC (permalink / raw) To: stable; +Cc: Konstantin Komarov, Sasha Levin From: Konstantin Komarov <almaz.alexandrovich@paragon-software.com> [ Upstream commit c1e0ab3789215a3dfbe95f226955e93ea4803391 ] Fixes xfstest generic/300 Fixes: 4534a70b7056 ("fs/ntfs3: Add headers and misc files") Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com> Stable-dep-of: 5e7b598660cf ("fs/ntfs3: bound copy_lcns dp->page_lcns[] index in analysis pass") Signed-off-by: Sasha Levin <sashal@kernel.org> --- fs/ntfs3/attrib.c | 33 ++++++++++++++++++++------------- fs/ntfs3/frecord.c | 4 ++-- fs/ntfs3/fslog.c | 25 +++++++++++++++++++------ fs/ntfs3/index.c | 2 +- fs/ntfs3/ntfs_fs.h | 5 +++-- fs/ntfs3/run.c | 9 +++++++-- 6 files changed, 52 insertions(+), 26 deletions(-) diff --git a/fs/ntfs3/attrib.c b/fs/ntfs3/attrib.c index 872586e9598a77..ca496499db71cb 100644 --- a/fs/ntfs3/attrib.c +++ b/fs/ntfs3/attrib.c @@ -327,7 +327,7 @@ int attr_make_nonresident(struct ntfs_inode *ni, struct ATTRIB *attr, err = ni_insert_nonresident(ni, attr_s->type, attr_name(attr_s), attr_s->name_len, run, 0, alen, - attr_s->flags, &attr, NULL); + attr_s->flags, &attr, NULL, NULL); if (err) goto out3; @@ -644,7 +644,7 @@ int attr_set_size(struct ntfs_inode *ni, enum ATTR_TYPE type, /* Insert new attribute segment. */ err = ni_insert_nonresident(ni, type, name, name_len, run, next_svcn, vcn - next_svcn, - attr_b->flags, &attr, &mi); + attr_b->flags, &attr, &mi, NULL); if (err) goto out; @@ -862,7 +862,7 @@ int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn, goto out; } - asize = le64_to_cpu(attr_b->nres.alloc_size) >> sbi->cluster_bits; + asize = le64_to_cpu(attr_b->nres.alloc_size) >> cluster_bits; if (vcn >= asize) { err = -EINVAL; goto out; @@ -1054,7 +1054,7 @@ int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn, if (evcn1 > next_svcn) { err = ni_insert_nonresident(ni, ATTR_DATA, NULL, 0, run, next_svcn, evcn1 - next_svcn, - attr_b->flags, &attr, &mi); + attr_b->flags, &attr, &mi, NULL); if (err) goto out; } @@ -1673,7 +1673,7 @@ int attr_allocate_frame(struct ntfs_inode *ni, CLST frame, size_t compr_size, if (evcn1 > next_svcn) { err = ni_insert_nonresident(ni, ATTR_DATA, NULL, 0, run, next_svcn, evcn1 - next_svcn, - attr_b->flags, &attr, &mi); + attr_b->flags, &attr, &mi, NULL); if (err) goto out; } @@ -1840,18 +1840,12 @@ int attr_collapse_range(struct ntfs_inode *ni, u64 vbo, u64 bytes) err = ni_insert_nonresident( ni, ATTR_DATA, NULL, 0, run, next_svcn, evcn1 - eat - next_svcn, a_flags, &attr, - &mi); + &mi, &le); if (err) goto out; /* Layout of records maybe changed. */ attr_b = NULL; - le = al_find_ex(ni, NULL, ATTR_DATA, NULL, 0, - &next_svcn); - if (!le) { - err = -EINVAL; - goto out; - } } /* Free all allocated memory. */ @@ -1969,9 +1963,10 @@ int attr_punch_hole(struct ntfs_inode *ni, u64 vbo, u64 bytes, u32 *frame_size) struct ATTRIB *attr = NULL, *attr_b; struct ATTR_LIST_ENTRY *le, *le_b; struct mft_inode *mi, *mi_b; - CLST svcn, evcn1, vcn, len, end, alen, dealloc; + CLST svcn, evcn1, vcn, len, end, alen, dealloc, next_svcn; u64 total_size, alloc_size; u32 mask; + __le16 a_flags; if (!bytes) return 0; @@ -2034,6 +2029,7 @@ int attr_punch_hole(struct ntfs_inode *ni, u64 vbo, u64 bytes, u32 *frame_size) svcn = le64_to_cpu(attr_b->nres.svcn); evcn1 = le64_to_cpu(attr_b->nres.evcn) + 1; + a_flags = attr_b->flags; if (svcn <= vcn && vcn < evcn1) { attr = attr_b; @@ -2081,6 +2077,17 @@ int attr_punch_hole(struct ntfs_inode *ni, u64 vbo, u64 bytes, u32 *frame_size) err = mi_pack_runs(mi, attr, run, evcn1 - svcn); if (err) goto out; + next_svcn = le64_to_cpu(attr->nres.evcn) + 1; + if (next_svcn < evcn1) { + err = ni_insert_nonresident(ni, ATTR_DATA, NULL, + 0, run, next_svcn, + evcn1 - next_svcn, + a_flags, &attr, &mi, + &le); + if (err) + goto out; + /* Layout of records maybe changed. */ + } } /* Free all allocated memory. */ run_truncate(run, 0); diff --git a/fs/ntfs3/frecord.c b/fs/ntfs3/frecord.c index 62874e7f8d8f17..81fc99f20c1169 100644 --- a/fs/ntfs3/frecord.c +++ b/fs/ntfs3/frecord.c @@ -1420,7 +1420,7 @@ int ni_insert_nonresident(struct ntfs_inode *ni, enum ATTR_TYPE type, const __le16 *name, u8 name_len, const struct runs_tree *run, CLST svcn, CLST len, __le16 flags, struct ATTRIB **new_attr, - struct mft_inode **mi) + struct mft_inode **mi, struct ATTR_LIST_ENTRY **le) { int err; CLST plen; @@ -1453,7 +1453,7 @@ int ni_insert_nonresident(struct ntfs_inode *ni, enum ATTR_TYPE type, } err = ni_insert_attr(ni, type, name, name_len, asize, name_off, svcn, - &attr, mi, NULL); + &attr, mi, le); if (err) goto out; diff --git a/fs/ntfs3/fslog.c b/fs/ntfs3/fslog.c index 9b12d5a7ac8d5e..09048409fe981d 100644 --- a/fs/ntfs3/fslog.c +++ b/fs/ntfs3/fslog.c @@ -3367,7 +3367,10 @@ static int do_action(struct ntfs_log *log, struct OPEN_ATTR_ENRTY *oe, memmove(Add2Ptr(attr, aoff), data, dlen); if (run_get_highest_vcn(le64_to_cpu(attr->nres.svcn), - attr_run(attr), &t64)) { + attr_run(attr), + le32_to_cpu(attr->size) - + le16_to_cpu(attr->nres.run_off), + &t64)) { goto dirty_vol; } @@ -4544,11 +4547,21 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) * whole routine a loop, case Lcns do not fit below. */ t16 = le16_to_cpu(lrh->lcns_follow); - for (i = 0; i < t16; i++) { - size_t j = (size_t)(le64_to_cpu(lrh->target_vcn) - - le64_to_cpu(dp->vcn)); - dp->page_lcns[j + i] = lrh->page_lcns[i]; - } + t32 = le32_to_cpu(dp->lcns_follow); + if (le64_to_cpu(lrh->target_vcn) < le64_to_cpu(dp->vcn)) { + err = -EINVAL; + goto out; + } + + for (i = 0; i < t16; i++) { + size_t j = (size_t)(le64_to_cpu(lrh->target_vcn) - + le64_to_cpu(dp->vcn)); + if (j >= t32 || i >= t32 - j) { + err = -EINVAL; + goto out; + } + dp->page_lcns[j + i] = lrh->page_lcns[i]; + } goto next_log_record_analyze; diff --git a/fs/ntfs3/index.c b/fs/ntfs3/index.c index b0aa5cc63b5079..9f2a1d41dfb5a0 100644 --- a/fs/ntfs3/index.c +++ b/fs/ntfs3/index.c @@ -1460,7 +1460,7 @@ static int indx_create_allocate(struct ntfs_index *indx, struct ntfs_inode *ni, goto out; err = ni_insert_nonresident(ni, ATTR_ALLOC, in->name, in->name_len, - &run, 0, len, 0, &alloc, NULL); + &run, 0, len, 0, &alloc, NULL, NULL); if (err) goto out1; diff --git a/fs/ntfs3/ntfs_fs.h b/fs/ntfs3/ntfs_fs.h index a03dce278221e8..85abc99a1e3bda 100644 --- a/fs/ntfs3/ntfs_fs.h +++ b/fs/ntfs3/ntfs_fs.h @@ -533,7 +533,7 @@ int ni_insert_nonresident(struct ntfs_inode *ni, enum ATTR_TYPE type, const __le16 *name, u8 name_len, const struct runs_tree *run, CLST svcn, CLST len, __le16 flags, struct ATTRIB **new_attr, - struct mft_inode **mi); + struct mft_inode **mi, struct ATTR_LIST_ENTRY **le); int ni_insert_resident(struct ntfs_inode *ni, u32 data_size, enum ATTR_TYPE type, const __le16 *name, u8 name_len, struct ATTRIB **new_attr, struct mft_inode **mi, @@ -802,7 +802,8 @@ int run_unpack_ex(struct runs_tree *run, struct ntfs_sb_info *sbi, CLST ino, #else #define run_unpack_ex run_unpack #endif -int run_get_highest_vcn(CLST vcn, const u8 *run_buf, u64 *highest_vcn); +int run_get_highest_vcn(CLST vcn, const u8 *run_buf, size_t run_buf_size, + u64 *highest_vcn); /* Globals from super.c */ void *ntfs_set_shared(void *ptr, u32 bytes); diff --git a/fs/ntfs3/run.c b/fs/ntfs3/run.c index 10b1a2b08723b3..672115481fb07e 100644 --- a/fs/ntfs3/run.c +++ b/fs/ntfs3/run.c @@ -1105,18 +1105,23 @@ int run_unpack_ex(struct runs_tree *run, struct ntfs_sb_info *sbi, CLST ino, * Return the highest vcn from a mapping pairs array * it used while replaying log file. */ -int run_get_highest_vcn(CLST vcn, const u8 *run_buf, u64 *highest_vcn) +int run_get_highest_vcn(CLST vcn, const u8 *run_buf, size_t run_buf_size, + u64 *highest_vcn) { + const u8 *run_last = run_buf + run_buf_size; u64 vcn64 = vcn; u8 size_size; - while ((size_size = *run_buf & 0xF)) { + while (run_buf < run_last && (size_size = *run_buf & 0xF)) { u8 offset_size = *run_buf++ >> 4; u64 len; if (size_size > 8 || offset_size > 8) return -EINVAL; + if (run_buf + size_size + offset_size > run_last) + return -EINVAL; + len = run_unpack_s64(run_buf, size_size, 0); if (!len) return -EINVAL; -- 2.53.0 ^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH 5.15.y 3/4] fs/ntfs3: Fix very fragmented case in attr_punch_hole 2026-07-24 10:48 ` [PATCH 5.15.y 1/4] fs/ntfs3: Make ntfs_update_mftmirr return void Sasha Levin 2026-07-24 10:48 ` [PATCH 5.15.y 2/4] fs/ntfs3: extend ni_insert_nonresident to return inserted ATTR_LIST_ENTRY Sasha Levin @ 2026-07-24 10:48 ` Sasha Levin 2026-07-24 10:49 ` [PATCH 5.15.y 4/4] fs/ntfs3: bound copy_lcns dp->page_lcns[] index in analysis pass Sasha Levin 2 siblings, 0 replies; 5+ messages in thread From: Sasha Levin @ 2026-07-24 10:48 UTC (permalink / raw) To: stable; +Cc: Konstantin Komarov, Sasha Levin From: Konstantin Komarov <almaz.alexandrovich@paragon-software.com> [ Upstream commit 560f7736b94622c077344e2c541c43c63b4e90e4 ] In some cases we need to ni_find_attr attr_b Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com> Stable-dep-of: 5e7b598660cf ("fs/ntfs3: bound copy_lcns dp->page_lcns[] index in analysis pass") Signed-off-by: Sasha Levin <sashal@kernel.org> --- fs/ntfs3/attrib.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/fs/ntfs3/attrib.c b/fs/ntfs3/attrib.c index ca496499db71cb..69e9ece120c8bd 100644 --- a/fs/ntfs3/attrib.c +++ b/fs/ntfs3/attrib.c @@ -2087,6 +2087,7 @@ int attr_punch_hole(struct ntfs_inode *ni, u64 vbo, u64 bytes, u32 *frame_size) if (err) goto out; /* Layout of records maybe changed. */ + attr_b = NULL; } } /* Free all allocated memory. */ @@ -2106,6 +2107,14 @@ int attr_punch_hole(struct ntfs_inode *ni, u64 vbo, u64 bytes, u32 *frame_size) } total_size -= (u64)dealloc << sbi->cluster_bits; + if (!attr_b) { + attr_b = ni_find_attr(ni, NULL, NULL, ATTR_DATA, NULL, 0, NULL, + &mi_b); + if (!attr_b) { + err = -EINVAL; + goto out; + } + } attr_b->nres.total_size = cpu_to_le64(total_size); mi_b->dirty = true; @@ -2116,8 +2125,10 @@ int attr_punch_hole(struct ntfs_inode *ni, u64 vbo, u64 bytes, u32 *frame_size) out: up_write(&ni->file.run_lock); - if (err) + if (err) { + ntfs_set_state(sbi, NTFS_DIRTY_ERROR); make_bad_inode(&ni->vfs_inode); + } return err; } -- 2.53.0 ^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH 5.15.y 4/4] fs/ntfs3: bound copy_lcns dp->page_lcns[] index in analysis pass 2026-07-24 10:48 ` [PATCH 5.15.y 1/4] fs/ntfs3: Make ntfs_update_mftmirr return void Sasha Levin 2026-07-24 10:48 ` [PATCH 5.15.y 2/4] fs/ntfs3: extend ni_insert_nonresident to return inserted ATTR_LIST_ENTRY Sasha Levin 2026-07-24 10:48 ` [PATCH 5.15.y 3/4] fs/ntfs3: Fix very fragmented case in attr_punch_hole Sasha Levin @ 2026-07-24 10:49 ` Sasha Levin 2 siblings, 0 replies; 5+ messages in thread From: Sasha Levin @ 2026-07-24 10:49 UTC (permalink / raw) To: stable; +Cc: Michael Bommarito, Konstantin Komarov, Sasha Levin From: Michael Bommarito <michael.bommarito@gmail.com> [ Upstream commit 5e7b598660cfa8e5af172cf4c65cffc126333307 ] In log_replay()'s analysis pass, after find_dp() returns a valid DIR_PAGE_ENTRY for the (target_attr, target_vcn) tuple, the copy_lcns block walks lrh->lcns_follow further entries: t16 = le16_to_cpu(lrh->lcns_follow); for (i = 0; i < t16; i++) { size_t j = (size_t)(le64_to_cpu(lrh->target_vcn) - le64_to_cpu(dp->vcn)); dp->page_lcns[j + i] = lrh->page_lcns[i]; } find_dp() only validates that target_vcn falls within [dp->vcn, dp->vcn + dp->lcns_follow), i.e., that the FIRST cluster is covered. The walk through the further entries is not bounded against dp->lcns_follow. For a malformed LRH where target_vcn = dp->vcn + dp->lcns_follow - 1 and lrh->lcns_follow > 1, the i > 0 writes overflow the dp's allocated page_lcns[] array. Add the missing j + lrh->lcns_follow <= dp->lcns_follow guard. Reproduced under UML+KASAN on mainline 8d90b09e6741 as a slab-out-of-bounds write of size 8 from log_replay+0x68d4 on the mount path. This is distinct from Pavitra Jha's 2026-05-02 patch ("fs/ntfs3: validate lcns_follow in log_replay conversion", <20260502154252.164586-1-jhapavitra98@gmail.com>) which addresses the separate version-0 dirty-page-table conversion path's memmove(&dp->vcn, ...) call. The two fixes are complementary; both should land. Fixes: b46acd6a6a62 ("fs/ntfs3: Add NTFS journal") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com> [almaz.alexandrovich@paragon-software.com: clang-formatted the changes, fixed conflicts] Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com> Signed-off-by: Sasha Levin <sashal@kernel.org> --- fs/ntfs3/fslog.c | 46 +++++++++++++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/fs/ntfs3/fslog.c b/fs/ntfs3/fslog.c index 09048409fe981d..8653c05e7b13e0 100644 --- a/fs/ntfs3/fslog.c +++ b/fs/ntfs3/fslog.c @@ -3368,8 +3368,8 @@ static int do_action(struct ntfs_log *log, struct OPEN_ATTR_ENRTY *oe, if (run_get_highest_vcn(le64_to_cpu(attr->nres.svcn), attr_run(attr), - le32_to_cpu(attr->size) - - le16_to_cpu(attr->nres.run_off), + le32_to_cpu(attr->size) - + le16_to_cpu(attr->nres.run_off), &t64)) { goto dirty_vol; } @@ -4547,22 +4547,34 @@ int log_replay(struct ntfs_inode *ni, bool *initialized) * whole routine a loop, case Lcns do not fit below. */ t16 = le16_to_cpu(lrh->lcns_follow); - t32 = le32_to_cpu(dp->lcns_follow); - if (le64_to_cpu(lrh->target_vcn) < le64_to_cpu(dp->vcn)) { - err = -EINVAL; - goto out; - } - - for (i = 0; i < t16; i++) { - size_t j = (size_t)(le64_to_cpu(lrh->target_vcn) - - le64_to_cpu(dp->vcn)); - if (j >= t32 || i >= t32 - j) { - err = -EINVAL; - goto out; - } - dp->page_lcns[j + i] = lrh->page_lcns[i]; - } + t32 = le32_to_cpu(dp->lcns_follow); + if (le64_to_cpu(lrh->target_vcn) < le64_to_cpu(dp->vcn)) { + err = -EINVAL; + goto out; + } + + /* + * find_dp() only validates that target_vcn is the first + * cluster covered by dp. The walk through lrh->lcns_follow + * further entries must stay within the allocated + * dp->page_lcns[] array, which is sized by dp->lcns_follow. + */ + if (le64_to_cpu(lrh->target_vcn) - le64_to_cpu(dp->vcn) + t16 > + le32_to_cpu(dp->lcns_follow)) { + err = -EINVAL; + log->set_dirty = true; + goto out; + } + for (i = 0; i < t16; i++) { + size_t j = (size_t)(le64_to_cpu(lrh->target_vcn) - + le64_to_cpu(dp->vcn)); + if (j >= t32 || i >= t32 - j) { + err = -EINVAL; + goto out; + } + dp->page_lcns[j + i] = lrh->page_lcns[i]; + } goto next_log_record_analyze; case DeleteDirtyClusters: { -- 2.53.0 ^ permalink raw reply related [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-07-24 10:49 UTC | newest] Thread overview: 5+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-07-20 14:25 FAILED: patch "[PATCH] fs/ntfs3: bound copy_lcns dp->page_lcns[] index in analysis" failed to apply to 5.15-stable tree gregkh 2026-07-24 10:48 ` [PATCH 5.15.y 1/4] fs/ntfs3: Make ntfs_update_mftmirr return void Sasha Levin 2026-07-24 10:48 ` [PATCH 5.15.y 2/4] fs/ntfs3: extend ni_insert_nonresident to return inserted ATTR_LIST_ENTRY Sasha Levin 2026-07-24 10:48 ` [PATCH 5.15.y 3/4] fs/ntfs3: Fix very fragmented case in attr_punch_hole Sasha Levin 2026-07-24 10:49 ` [PATCH 5.15.y 4/4] fs/ntfs3: bound copy_lcns dp->page_lcns[] index in analysis pass Sasha Levin
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox