]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
ntfs: validate MFT attrs_offset against bytes_in_use
authorDaeMyung Kang <charsyam@gmail.com>
Sat, 9 May 2026 06:12:35 +0000 (15:12 +0900)
committerNamjae Jeon <linkinjeon@kernel.org>
Sat, 9 May 2026 15:42:17 +0000 (00:42 +0900)
ntfs_mft_record_check() verifies that attrs_offset is aligned and that
the resulting pointer stays within the allocated MFT record buffer, but
it does not check that the first attribute header starts within the
bytes_in_use area.

A malformed record with attrs_offset greater than bytes_in_use can pass
this check as long as attrs_offset is still within bytes_allocated.  The
attribute parser then computes the remaining record space by subtracting
the attribute pointer from bytes_in_use.  Because that value is unsigned,
the subtraction can underflow and allow bytes after bytes_in_use to be
interpreted as an attribute.

Reject records where attrs_offset is outside bytes_in_use or where the
used area does not even contain the four-byte attribute type/AT_END
terminator at attrs_offset.

A small userspace model with attrs_offset=128 and bytes_in_use=64 shows
the current check accepts the record and the parser space calculation
underflows to 0xffffffc0.  With this change the same malformed record is
rejected before the attribute walker is entered.

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Signed-off-by: DaeMyung Kang <charsyam@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
fs/ntfs/mft.c

index 68f6fc8b7b62bdaac34ebd58888ac3168238170e..729b259974eb05a7b343fe0d9c0030ee79641740 100644 (file)
@@ -30,6 +30,8 @@ int ntfs_mft_record_check(const struct ntfs_volume *vol, struct mft_record *m,
 {
        struct attr_record *a;
        struct super_block *sb = vol->sb;
+       u16 attrs_offset;
+       u32 bytes_in_use;
 
        if (!ntfs_is_file_record(m->magic)) {
                ntfs_error(sb, "Record %llu has no FILE magic (0x%x)\n",
@@ -65,7 +67,16 @@ int ntfs_mft_record_check(const struct ntfs_volume *vol, struct mft_record *m,
                goto err_out;
        }
 
-       a = (struct attr_record *)((char *)m + le16_to_cpu(m->attrs_offset));
+       attrs_offset = le16_to_cpu(m->attrs_offset);
+       bytes_in_use = le32_to_cpu(m->bytes_in_use);
+
+       if (attrs_offset > bytes_in_use ||
+           bytes_in_use - attrs_offset < sizeof_field(struct attr_record, type)) {
+               ntfs_error(sb, "Record %llu has corrupt attribute offset\n", mft_no);
+               goto err_out;
+       }
+
+       a = (struct attr_record *)((char *)m + attrs_offset);
        if ((char *)a < (char *)m || (char *)a > (char *)m + vol->mft_record_size) {
                ntfs_error(sb, "Record %llu is corrupt\n", mft_no);
                goto err_out;