attr_data_get_block_locked() maps a resident attribute as an IOMAP_INLINE extent using the resident value length taken straight from the on-disk attribute, without checking that it fits within the single page that backs an inline extent. mi_enum_attr() only bounds that length against the MFT record size, so a corrupted volume with MFT records larger than a page can present a resident $DATA whose length exceeds PAGE_SIZE; on a buffered write the inline extent then fails the page-fit invariant and iomap_write_end_inline() trips BUG_ON(!iomap_inline_data_valid()). Impact: writing to a resident file on a mounted crafted NTFS volume whose MFT records are larger than a page oopses the kernel at fs/iomap/buffered-io.c:1061. Reject such an attribute in attr_data_get_block_locked(), before the inline buffer is allocated and the size reaches the iomap core. Fixes: 099ef9ab9203 ("fs/ntfs3: implement iomap-based file operations") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Michael Bommarito --- The oops on current mainline (the inline buffer is kmemdup()ed), reproduced on 7.1.0-rc4 with KASAN by writing to a resident file on a crafted volume whose resident $DATA value length exceeds PAGE_SIZE (reachable only with MFT records >= 8 KiB): kernel BUG at fs/iomap/buffered-io.c:1061! RIP: 0010:iomap_write_end+0x48e/0x5c0 iomap_file_buffered_write ntfs_file_write_iter vfs_write This also matters for the queued "ntfs3: Allocate iomap inline_data using alloc_page" change: there the same oversized data_size makes memcpy(page_address(page), resident_data(attr_b), data_size) write past the single alloc_page() page. Bounding data_size here prevents both. With this patch the offending write returns -EINVAL; normal resident and non-resident writes are unaffected. fs/ntfs3/attrib.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/fs/ntfs3/attrib.c b/fs/ntfs3/attrib.c index e61c5bf7e27e4..d41ca930daebc 100644 --- a/fs/ntfs3/attrib.c +++ b/fs/ntfs3/attrib.c @@ -1039,6 +1039,21 @@ int attr_data_get_block_locked(struct ntfs_inode *ni, CLST vcn, CLST clen, if (!attr_b->non_res) { u32 data_size = le32_to_cpu(attr_b->res.data_size); + + /* + * A resident attribute is mapped as an IOMAP_INLINE extent, + * which must fit within a single page: iomap_write_end_inline() + * asserts iomap_inline_data_valid(), and the inline buffer is a + * single page. mi_enum_attr() only bounds the resident value + * length against the MFT record size, so a corrupted volume with + * records larger than a page can report data_size > PAGE_SIZE. + * Reject it here, before it overflows the inline page or trips + * the iomap BUG_ON. + */ + if (data_size > PAGE_SIZE) { + err = -EINVAL; + goto out; + } *lcn = RESIDENT_LCN; *len = data_size; if (res && data_size) { -- 2.53.0