]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
fs/ntfs3: bound attr_off in UpdateResidentValue against data_off
authorKonstantin Komarov <almaz.alexandrovich@paragon-software.com>
Tue, 2 Jun 2026 13:15:47 +0000 (15:15 +0200)
committerKonstantin Komarov <almaz.alexandrovich@paragon-software.com>
Wed, 3 Jun 2026 14:36:45 +0000 (16:36 +0200)
In do_action()'s UpdateResidentValue case (fslog.c:3307),
lrh->attr_off and lrh->redo_len come from the on-disk LRH.
When they satisfy aoff + dlen < attr->res.data_off, the
assignment

attr->res.data_size = cpu_to_le32(aoff + dlen - data_off);

underflows to ~4 GiB (e.g. 0xFFFFFFF9 when aoff=0x10, dlen=1,
data_off=0x18).  Subsequent code that reads attr->res.data_size
to walk the resident attribute payload would then read up to
4 GiB past the 1024-byte MFT record allocation.

The existing mi_enum_attr() defense in fs/ntfs3/record.c:287
catches the corrupted data_size on the next attribute walk
and fails the mount, but only on the path that walks all
attributes.  A read site that picks an attribute by name and
reads its data_size without re-validating is not covered.
Validate aoff against data_off and asize at the source.

Reproduced under UML+KASAN on mainline 8d90b09e6741 via
pr_warn-only probe: with aoff=0x10 and data_off=0x18, the
post-assignment data_size is 0xfffffff9 (mount then fails
at -22 from mi_enum_attr).

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]
Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
fs/ntfs3/fslog.c

index 92a7e3a462406a562f4ed9091230c16a66ffe3e3..847048637dda45fb2b7778c27285044ca68e288b 100644 (file)
@@ -3339,6 +3339,17 @@ skip_load_parent:
                nsize = ALIGN(nsize, 8);
                data_off = le16_to_cpu(attr->res.data_off);
 
+               /*
+                * aoff comes from the on-disk lrh->attr_off.  Forbid
+                * writes that begin below the resident attribute's
+                * data_off (which would overwrite the resident header),
+                * and forbid aoff + dlen < data_off, which would make
+                * the data_size assignment below underflow to ~4 GiB.
+                */
+               if (aoff < data_off || aoff + dlen < data_off ||
+                   aoff + dlen > asize)
+                       goto dirty_vol;
+
                if (nsize < asize) {
                        memmove(Add2Ptr(attr, aoff), data, dlen);
                        data = NULL; // To skip below memmove().