mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH v2] fs/ntfs3: reject an oversized resident attribute on the inline iomap path
@ 2026-07-11 15:26 Michael Bommarito
  2026-09-28 17:23 ` Konstantin Komarov
  0 siblings, 1 reply; 2+ messages in thread
From: Michael Bommarito @ 2026-07-11 15:26 UTC (permalink / raw)
  To: Konstantin Komarov, ntfs3
  Cc: Mihai Brodschi, linux-fsdevel, stable, linux-kernel

attr_data_get_block_locked() maps a resident attribute as a one-page
IOMAP_INLINE extent: it allocates a single page, copies the resident
value into it with memcpy(), and hands that page to ntfs_iomap_begin()
as the inline data.  The copy length is the on-disk resident value
length (res.data_size), and mi_enum_attr() only bounds that length
against the MFT record size.  A corrupted volume with MFT records larger
than a page can therefore present a resident $DATA whose length exceeds
PAGE_SIZE, and the memcpy() then writes past the single destination page.

Impact: reading or writing a resident file on a mounted crafted NTFS
volume whose MFT records are larger than a page overflows the one-page
inline buffer in attr_data_get_block_locked() with attacker-controlled
bytes.

Reject such an attribute in attr_data_get_block_locked(), before the
page is allocated and the copy is made.

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 <michael.bommarito@gmail.com>
---
Changes since v1:
- Reworded the code comment and changelog.  The oversized resident length
  overflows the single alloc_page() page that attr_data_get_block_locked()
  copies the resident value into; v1 described the older
  iomap_write_end_inline() BUG_ON(!iomap_inline_data_valid()), which was
  removed in 7.2-rc1.  Thanks to Mihai Brodschi for catching the stale
  reference.  The fix itself is unchanged.

The unbounded resident length dates to the iomap conversion; only the
manifestation changed when the inline buffer became a single alloc_page():
  - 7.2-rc: the memcpy() into the one-page buffer overflows the page
    (out-of-bounds write, below).
  - 7.0..7.1: the buffer was kmemdup()ed at the real size and the oversized
    length instead tripped BUG_ON(!iomap_inline_data_valid()) in
    iomap_write_end_inline() (a denial of service).
The same data_size > PAGE_SIZE check prevents both, so it is the correct fix
across the stable range.

Reproduced under QEMU + KASAN on 7.2-rc2.  A resident $DATA value length
above PAGE_SIZE, as a volume with MFT records larger than a page presents,
was supplied while the alloc_page() + memcpy() path ran unchanged; the stock
kernel faults with a KASAN out-of-bounds write in
attr_data_get_block_locked():

  BUG: KASAN: out-of-bounds in attr_data_get_block_locked
  Write of size <data_size> by task ...
   __asan_memcpy
   attr_data_get_block_locked
   attr_data_get_block
   ntfs_iomap_begin
   iomap_iter
   iomap_file_buffered_write

With the patch the same access returns -EINVAL and does not fault.  An
in-bounds resident file (control) and a non-resident file are unaffected on
both the stock and patched kernels.
 fs/ntfs3/attrib.c | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/fs/ntfs3/attrib.c b/fs/ntfs3/attrib.c
index c621a4c582f9e..2f73714a2db97 100644
--- a/fs/ntfs3/attrib.c
+++ b/fs/ntfs3/attrib.c
@@ -1034,6 +1034,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 copied into a single page below
+		 * (alloc_page() + memcpy()) and mapped as a one-page
+		 * IOMAP_INLINE extent by ntfs_iomap_begin().  mi_enum_attr()
+		 * only bounds the resident value length against the MFT record
+		 * size, so a corrupted volume whose records are larger than a
+		 * page can report data_size > PAGE_SIZE; copying that many bytes
+		 * would overflow the single destination page.  Reject it.
+		 */
+		if (data_size > PAGE_SIZE) {
+			err = -EINVAL;
+			goto out;
+		}
+
 		*lcn = RESIDENT_LCN;
 		*len = data_size;
 		if (res && data_size) {
-- 
2.53.0


^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re: [PATCH v2] fs/ntfs3: reject an oversized resident attribute on the inline iomap path
  2026-07-11 15:26 [PATCH v2] fs/ntfs3: reject an oversized resident attribute on the inline iomap path Michael Bommarito
@ 2026-09-28 17:23 ` Konstantin Komarov
  0 siblings, 0 replies; 2+ messages in thread
From: Konstantin Komarov @ 2026-09-28 17:23 UTC (permalink / raw)
  To: Michael Bommarito, ntfs3
  Cc: Mihai Brodschi, linux-fsdevel, stable, linux-kernel

On 7/11/26 17:26, Michael Bommarito wrote:

> attr_data_get_block_locked() maps a resident attribute as a one-page
> IOMAP_INLINE extent: it allocates a single page, copies the resident
> value into it with memcpy(), and hands that page to ntfs_iomap_begin()
> as the inline data.  The copy length is the on-disk resident value
> length (res.data_size), and mi_enum_attr() only bounds that length
> against the MFT record size.  A corrupted volume with MFT records larger
> than a page can therefore present a resident $DATA whose length exceeds
> PAGE_SIZE, and the memcpy() then writes past the single destination page.
>
> Impact: reading or writing a resident file on a mounted crafted NTFS
> volume whose MFT records are larger than a page overflows the one-page
> inline buffer in attr_data_get_block_locked() with attacker-controlled
> bytes.
>
> Reject such an attribute in attr_data_get_block_locked(), before the
> page is allocated and the copy is made.
>
> 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 <michael.bommarito@gmail.com>
> ---
> Changes since v1:
> - Reworded the code comment and changelog.  The oversized resident length
>    overflows the single alloc_page() page that attr_data_get_block_locked()
>    copies the resident value into; v1 described the older
>    iomap_write_end_inline() BUG_ON(!iomap_inline_data_valid()), which was
>    removed in 7.2-rc1.  Thanks to Mihai Brodschi for catching the stale
>    reference.  The fix itself is unchanged.
>
> The unbounded resident length dates to the iomap conversion; only the
> manifestation changed when the inline buffer became a single alloc_page():
>    - 7.2-rc: the memcpy() into the one-page buffer overflows the page
>      (out-of-bounds write, below).
>    - 7.0..7.1: the buffer was kmemdup()ed at the real size and the oversized
>      length instead tripped BUG_ON(!iomap_inline_data_valid()) in
>      iomap_write_end_inline() (a denial of service).
> The same data_size > PAGE_SIZE check prevents both, so it is the correct fix
> across the stable range.
>
> Reproduced under QEMU + KASAN on 7.2-rc2.  A resident $DATA value length
> above PAGE_SIZE, as a volume with MFT records larger than a page presents,
> was supplied while the alloc_page() + memcpy() path ran unchanged; the stock
> kernel faults with a KASAN out-of-bounds write in
> attr_data_get_block_locked():
>
>    BUG: KASAN: out-of-bounds in attr_data_get_block_locked
>    Write of size <data_size> by task ...
>     __asan_memcpy
>     attr_data_get_block_locked
>     attr_data_get_block
>     ntfs_iomap_begin
>     iomap_iter
>     iomap_file_buffered_write
>
> With the patch the same access returns -EINVAL and does not fault.  An
> in-bounds resident file (control) and a non-resident file are unaffected on
> both the stock and patched kernels.
>   fs/ntfs3/attrib.c | 15 +++++++++++++++
>   1 file changed, 15 insertions(+)
>
> diff --git a/fs/ntfs3/attrib.c b/fs/ntfs3/attrib.c
> index c621a4c582f9e..2f73714a2db97 100644
> --- a/fs/ntfs3/attrib.c
> +++ b/fs/ntfs3/attrib.c
> @@ -1034,6 +1034,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 copied into a single page below
> +		 * (alloc_page() + memcpy()) and mapped as a one-page
> +		 * IOMAP_INLINE extent by ntfs_iomap_begin().  mi_enum_attr()
> +		 * only bounds the resident value length against the MFT record
> +		 * size, so a corrupted volume whose records are larger than a
> +		 * page can report data_size > PAGE_SIZE; copying that many bytes
> +		 * would overflow the single destination page.  Reject it.
> +		 */
> +		if (data_size > PAGE_SIZE) {
> +			err = -EINVAL;
> +			goto out;
> +		}
> +
>   		*lcn = RESIDENT_LCN;
>   		*len = data_size;
>   		if (res && data_size) {

Hello,

The patch was applied, thanks.

Regards,
Konstantin


^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-09-28 17:23 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-07-11 15:26 [PATCH v2] fs/ntfs3: reject an oversized resident attribute on the inline iomap path Michael Bommarito
2026-09-28 17:23 ` Konstantin Komarov

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®