* [PATCH RESEND] fs/ntfs3: fix out-of-bounds read in the index root ops of do_action()
@ 2026-09-29 20:41 Stanley Shen
0 siblings, 0 replies; only message in thread
From: Stanley Shen @ 2026-09-29 20:41 UTC (permalink / raw)
To: Konstantin Komarov; +Cc: ntfs3, linux-kernel
The five index root redo handlers in do_action() (AddIndexEntryRoot,
DeleteIndexEntryRoot, SetIndexEntryVcnRoot, UpdateFileNameRoot and
UpdateRecordDataRoot) read the index root before anything has validated
the offset the attribute was derived from:
root = resident_data(attr);
hdr = &root->ihdr;
used = le32_to_cpu(hdr->used);
if (!check_if_index_root(rec, lrh) ||
!check_if_root_index(attr, hdr, lrh)) {
attr is Add2Ptr(rec, le16_to_cpu(lrh->record_off)) and rec is the
record_size-sized buffer allocated in mi_init(), while record_off comes
verbatim from the on-disk log record header. check_if_index_root() is
what bounds it, by walking the attribute chain from rec->attr_off (which
check_file_record() has already validated) and accepting record_off only
when it is the offset of an ATTR_ROOT of that chain. Until it has run,
both the attr->res.data_off read inside resident_data() and the hdr->used
read that follows can land outside the allocation; record_off and
data_off are both __le16, so a crafted log record can push the access
almost 128K past a 1K or 4K record.
Once check_if_index_root() has passed, check_attr() has already bounded
the attribute and its resident data inside the record, so run it first in
all five handlers. check_if_root_index() needs hdr and stays put.
Fixes: b46acd6a6a62 ("fs/ntfs3: Add NTFS journal")
Assisted-by: LLM
Signed-off-by: Stanley Shen <stanleyshen1886@gmail.com>
---
fs/ntfs3/fslog.c | 39 +++++++++++++++++++++++++--------------
1 file changed, 25 insertions(+), 14 deletions(-)
diff --git a/fs/ntfs3/fslog.c b/fs/ntfs3/fslog.c
index ed50c1d0c23..c7c2b477643 100644
--- a/fs/ntfs3/fslog.c
+++ b/fs/ntfs3/fslog.c
@@ -3471,12 +3471,19 @@ static int do_action(struct ntfs_log *log, struct OPEN_ATTR_ENRTY *oe,
case AddIndexEntryRoot:
e = (struct NTFS_DE *)data;
esize = le16_to_cpu(e->size);
+
+ /*
+ * check_if_index_root() is what validates lrh->record_off, so
+ * it has to run before attr is dereferenced.
+ */
+ if (!check_if_index_root(rec, lrh))
+ goto dirty_vol;
+
root = resident_data(attr);
hdr = &root->ihdr;
used = le32_to_cpu(hdr->used);
- if (!check_if_index_root(rec, lrh) ||
- !check_if_root_index(attr, hdr, lrh) ||
+ if (!check_if_root_index(attr, hdr, lrh) ||
Add2Ptr(data, esize) > Add2Ptr(lrh, rec_len) ||
esize > le32_to_cpu(rec->total) - le32_to_cpu(rec->used)) {
goto dirty_vol;
@@ -3498,14 +3505,15 @@ static int do_action(struct ntfs_log *log, struct OPEN_ATTR_ENRTY *oe,
break;
case DeleteIndexEntryRoot:
+ if (!check_if_index_root(rec, lrh))
+ goto dirty_vol;
+
root = resident_data(attr);
hdr = &root->ihdr;
used = le32_to_cpu(hdr->used);
- if (!check_if_index_root(rec, lrh) ||
- !check_if_root_index(attr, hdr, lrh)) {
+ if (!check_if_root_index(attr, hdr, lrh))
goto dirty_vol;
- }
e1 = Add2Ptr(attr, le16_to_cpu(lrh->attr_off));
esize = le16_to_cpu(e1->size);
@@ -3526,13 +3534,14 @@ static int do_action(struct ntfs_log *log, struct OPEN_ATTR_ENRTY *oe,
break;
case SetIndexEntryVcnRoot:
+ if (!check_if_index_root(rec, lrh))
+ goto dirty_vol;
+
root = resident_data(attr);
hdr = &root->ihdr;
- if (!check_if_index_root(rec, lrh) ||
- !check_if_root_index(attr, hdr, lrh)) {
+ if (!check_if_root_index(attr, hdr, lrh))
goto dirty_vol;
- }
e = Add2Ptr(attr, le16_to_cpu(lrh->attr_off));
@@ -3541,13 +3550,14 @@ static int do_action(struct ntfs_log *log, struct OPEN_ATTR_ENRTY *oe,
break;
case UpdateFileNameRoot:
+ if (!check_if_index_root(rec, lrh))
+ goto dirty_vol;
+
root = resident_data(attr);
hdr = &root->ihdr;
- if (!check_if_index_root(rec, lrh) ||
- !check_if_root_index(attr, hdr, lrh)) {
+ if (!check_if_root_index(attr, hdr, lrh))
goto dirty_vol;
- }
e = Add2Ptr(attr, le16_to_cpu(lrh->attr_off));
fname = (struct ATTR_FILE_NAME *)(e + 1);
@@ -3556,13 +3566,14 @@ static int do_action(struct ntfs_log *log, struct OPEN_ATTR_ENRTY *oe,
break;
case UpdateRecordDataRoot:
+ if (!check_if_index_root(rec, lrh))
+ goto dirty_vol;
+
root = resident_data(attr);
hdr = &root->ihdr;
- if (!check_if_index_root(rec, lrh) ||
- !check_if_root_index(attr, hdr, lrh)) {
+ if (!check_if_root_index(attr, hdr, lrh))
goto dirty_vol;
- }
e = Add2Ptr(attr, le16_to_cpu(lrh->attr_off));
--
2.50.1 (Apple Git-155)
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2026-09-29 20:41 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-29 20:41 [PATCH RESEND] fs/ntfs3: fix out-of-bounds read in the index root ops of do_action() Stanley Shen
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®