From: "Cen Zhang (Microsoft Security FORGE Labs)" <cenzhang@linux.microsoft.com>
To: almaz.alexandrovich@paragon-software.com
Cc: ntfs3@lists.linux.dev, linux-kernel@vger.kernel.org,
stable@vger.kernel.org, AutonomousCodeSecurity@microsoft.com,
xmei5@asu.edu, tgopinath@linux.microsoft.com, kys@microsoft.com,
"Cen Zhang (Microsoft Security FORGE Labs)"
<cenzhang@linux.microsoft.com>
Subject: [PATCH] fs/ntfs3: validate target_attr in log_replay()
Date: Tue, 1 Sep 2026 13:49:34 -0400 [thread overview]
Message-ID: <20260901174934.6275-1-cenzhang@linux.microsoft.com> (raw)
log_replay() treats lrh->target_attr as a byte offset to an
OPEN_ATTR_ENRTY in the open-attribute restart table. A valid offset is
the table header size plus an integral number of entries. The redo
lookup checks only target_attr < bytes_per_rt(oatbl); its earlier
alignment check subtracts the header size without first checking the
lower bound, so the subtraction can wrap.
The undo lookup has no table-bound check, and its earlier alignment
check is skipped when lcns_follow is zero. Unlike the redo lookup, it
also does not reject unallocated entries or NULL entry pointers.
A crafted $LogFile can therefore point into the table header, the middle
of an entry, or past the table and trigger an out-of-bounds access.
BUG: KASAN: slab-out-of-bounds in log_replay+0x7d44/0x9d80
fs/ntfs3/fslog.c:5235 log_replay()
ntfs_loadlog_and_replay()
ntfs_fill_super()
path_mount()
Add rstbl_entry_valid() to require a nonzero entry size and an offset
between the table header and end that is aligned to the entry size. Use
it before both lookups, and make the undo lookup match the redo lookup
by rejecting unallocated entries and NULL entry pointers. Skip
targetless records with no LCN work before the lookup so their unused
target_attr is not subjected to the new validation.
Fixes: b46acd6a6a62 ("fs/ntfs3: Add NTFS journal")
Reported-by: Xiang Mei (Microsoft) <xmei5@asu.edu>
Cc: AutonomousCodeSecurity@microsoft.com
Cc: stable@vger.kernel.org
Signed-off-by: Cen Zhang (Microsoft Security FORGE Labs) <cenzhang@linux.microsoft.com>
---
fs/ntfs3/fslog.c | 33 +++++++++++++++++++++++++++++++--
1 file changed, 31 insertions(+), 2 deletions(-)
diff --git a/fs/ntfs3/fslog.c b/fs/ntfs3/fslog.c
index ed50c1d0c23e..1ddff43dbe87 100644
--- a/fs/ntfs3/fslog.c
+++ b/fs/ntfs3/fslog.c
@@ -384,6 +384,14 @@ static inline u32 bytes_per_rt(const struct RESTART_TABLE *rt)
sizeof(struct RESTART_TABLE);
}
+static inline bool rstbl_entry_valid(const struct RESTART_TABLE *rt, u32 off)
+{
+ u16 size = le16_to_cpu(rt->size);
+
+ return size && off >= sizeof(*rt) && off < bytes_per_rt(rt) &&
+ !((off - sizeof(*rt)) % size);
+}
+
/* Log record length. */
static inline u32 lrh_length(const struct LOG_REC_HDR *lr)
{
@@ -5086,7 +5094,7 @@ int log_replay(struct ntfs_inode *ni, bool *initialized)
goto read_next_log_do_action;
t16 = le16_to_cpu(lrh->target_attr);
- if (t16 >= bytes_per_rt(oatbl)) {
+ if (!rstbl_entry_valid(oatbl, t16)) {
err = -EINVAL;
goto out;
}
@@ -5231,8 +5239,29 @@ int log_replay(struct ntfs_inode *ni, bool *initialized)
if (lrh->undo_op == cpu_to_le16(Noop))
goto read_next_log_undo_action;
- oe = Add2Ptr(oatbl, le16_to_cpu(lrh->target_attr));
+ /* Skip records with neither target nor LCN work. */
+ t16 = le16_to_cpu(lrh->undo_op);
+ if (!lrh->lcns_follow && !is_target_required(t16) &&
+ can_skip_action(t16))
+ goto read_next_log_undo_action;
+
+ t16 = le16_to_cpu(lrh->target_attr);
+ if (!rstbl_entry_valid(oatbl, t16)) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ oe = Add2Ptr(oatbl, t16);
+ if (oe->next != RESTART_ENTRY_ALLOCATED_LE) {
+ err = -EINVAL;
+ goto out;
+ }
+
oa = oe->ptr;
+ if (!oa) {
+ err = -EINVAL;
+ goto out;
+ }
t16 = le16_to_cpu(lrh->lcns_follow);
if (!t16)
base-commit: cee9395acd8043be0644b25c34bfa86623f2b935
--
2.55.0
next reply other threads:[~2026-09-01 17:49 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-01 17:49 Cen Zhang (Microsoft Security FORGE Labs) [this message]
2026-09-02 5:53 ` Greg KH
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260901174934.6275-1-cenzhang@linux.microsoft.com \
--to=cenzhang@linux.microsoft.com \
--cc=AutonomousCodeSecurity@microsoft.com \
--cc=almaz.alexandrovich@paragon-software.com \
--cc=kys@microsoft.com \
--cc=linux-kernel@vger.kernel.org \
--cc=ntfs3@lists.linux.dev \
--cc=stable@vger.kernel.org \
--cc=tgopinath@linux.microsoft.com \
--cc=xmei5@asu.edu \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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®