ntfs: fail the mount when $MFT's data size exceeds its allocation

ntfs_read_inode_mount() takes $MFT's i_size from data_size without
checking it against allocated_size, unlike the resident case in
ntfs_read_locked_inode().  mft records between the two are not on disk.

On a crafted volume with 512-byte clusters whose $MFT is cut to 4
clusters while data_size still says 27 records, reading the folio
holding records 0-3 finds the end of the runlist at vcn 4.  An unpatched
kernel hangs there on the folio lock, as in "ntfs: fail the mount when
$MFT needs its own extent records".  With the previous patch vcn 4-7 are
past the allocation and so read as a hole: records 2 and 3 come back as
zeros and the mount carries on until check_mft_mirror() finds the zeroed
record 2.

Refuse such a $MFT before anything is read through it.  The mount now
fails with:

  ntfs: (device vda): ntfs_read_inode_mount(): $MFT data size 27648
    exceeds its allocated size 2048. $MFT is corrupt. Run chkdsk.

fs/ntfs3 rejects any non-resident attribute whose data_size exceeds its
allocated size.  This patch checks only $MFT; the next one covers the
other non-resident attributes.

Fixes: b041ca5625 ("ntfs: update iomap and address space operations")
Cc: stable@vger.kernel.org
Signed-off-by: Matthias Goergens <matthias.goergens@gmail.com>
Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
This commit is contained in:
Matthias Goergens 2026-09-30 11:48:34 +08:00 committed by Namjae Jeon
parent e727ea7fc4
commit 82a5e003ef

View File

@ -2143,6 +2143,16 @@ int ntfs_read_inode_mount(struct inode *vi)
vi->i_size = le64_to_cpu(a->data.non_resident.data_size);
ni->initialized_size = le64_to_cpu(a->data.non_resident.initialized_size);
ni->allocated_size = le64_to_cpu(a->data.non_resident.allocated_size);
/*
* Records between allocated_size and data_size are not
* on disk, and would be read as zeros.
*/
if (vi->i_size > ni->allocated_size) {
ntfs_error(sb,
"$MFT data size %lld exceeds its allocated size %lld. $MFT is corrupt. Run chkdsk.",
vi->i_size, ni->allocated_size);
goto put_err_out;
}
/*
* Verify the number of mft records does not exceed
* 2^32 - 1.