All of lore.kernel.org
 help / color / mirror / Atom feed
From: Hongling Zeng <zhongling0719@126.com>
To: linkinjeon@kernel.org
Cc: Hongling Zeng <zenghongling@kylinos.cn>,
	hyc.lee@gmail.com,  alexandro.calo@nozominetworks.com,
	ntfs@lists.linux.dev,  linux-kernel@vger.kernel.org,
	stable@vger.kernel.org
Subject: Re: [PATCH v2] ntfs: fix name offset validation in ntfs_non_resident_attr_value_is_valid
Date: Thu, 06 Aug 2026 10:38:33 +0800	[thread overview]
Message-ID: <6A73F3A9.7020608@126.com> (raw)
In-Reply-To: <20260806013903.8526-1-zenghongling@kylinos.cn>

Hi:

  Thank you for the review. You're absolutely right on both points:

   1. name_length is u8, not __le16 - my mistake.

   2. Moving the check outside the if (name_length) block is better as
      it covers both named and unnamed attributes.

   However, I have a concern about using ATTR_COMPRESSION_MASK here.

   In ntfs_attr_update_meta() line 3610, the actual conversion check is:

       if (sparse && !(a->flags & (ATTR_IS_SPARSE | ATTR_IS_COMPRESSED)))

   This uses ATTR_IS_COMPRESSED (0x0001), not ATTR_COMPRESSION_MASK (0x00ff).

   If a malicious MFT record sets flags = 0x0002:
   - Your check would skip (0x0002 & 0x00ff is true)
   - But ntfs_attr_update_meta() would still do the forward shift
     (0x0002 & 0x0001 is false)

   Should we use ATTR_IS_COMPRESSED instead to match the actual conversion
   predicate? Or is there another reason for the mask that I'm missing?

   Otherwise, your suggested approach of checking:
       attr_len - mp_offset < sizeof(a->data.non_resident.compressed_size)
   is much cleaner than checking name_end + 8.

在 2026年08月06日 09:39, Hongling Zeng 写道:
> ntfs_attr_update_meta() performs memmove operations on attribute names
> when converting between sparse and non-sparse attributes:
>
> - Converting to sparse shifts the name forward by 8 bytes
>    (name_offset + 8)
> - Converting from sparse shifts the name backward by 8 bytes
>    (name_offset - 8)
>
> However, the validator does not check that name_offset is within safe
> boundaries for these operations. A malicious MFT record could set
> name_offset such that:
>
> 1. The name is positioned at the very end of a non-sparse attribute.
>     Converting to sparse would shift the name forward by 8 bytes,
>     writing beyond the attribute boundary.
>
> 2. The name overlaps with the mapping pairs, causing corruption during
>     conversion.
>
> Add validation to ensure:
> - For named attributes, name_offset is within valid bounds
> - Name does not extend beyond the attribute or overlap with mapping pairs
> - For non-sparse attributes, name_end + 8 fits within attr_len to allow
>    room for the forward shift when becoming sparse
>
> Note: name_offset validation only applies when name_length != 0, as
> unnamed attributes use name_offset = 0 which is valid.
>
> Fixes: 7e2a1c554bc4 ("ntfs: Fix min_len for compressed/sparse attributes in ntfs_non_resident_attr_value_is_valid()")
> Cc: stable@vger.kernel.org
> Signed-off-by: Hongling Zeng <zenghongling@kylinos.cn>
> ---
> Changes in v2:
> - Read name_length directly as an 8-bit field instead of using
>    le16_to_cpu().
> - Restrict the name forward-shift bounds check to the same attribute
>    types that can actually be converted by ntfs_attr_update_meta().
> ---
>   fs/ntfs/attrib.c | 33 ++++++++++++++++++++++++++++++++-
>   1 file changed, 32 insertions(+), 1 deletion(-)
>
> diff --git a/fs/ntfs/attrib.c b/fs/ntfs/attrib.c
> index d354c3b0fae1..ffd08633970a 100644
> --- a/fs/ntfs/attrib.c
> +++ b/fs/ntfs/attrib.c
> @@ -693,6 +693,9 @@ static bool ntfs_non_resident_attr_value_is_valid(const struct attr_record *a)
>   	u32 attr_len;
>   	u32 min_len;
>   	u16 mp_offset;
> +	u16 name_offset;
> +	u8 name_len;
> +	u32 name_end;
>   
>   	attr_len = le32_to_cpu(a->length);
>   	min_len = offsetof(struct attr_record, data.non_resident.initialized_size) +
> @@ -706,7 +709,35 @@ static bool ntfs_non_resident_attr_value_is_valid(const struct attr_record *a)
>   		return false;
>   
>   	mp_offset = le16_to_cpu(a->data.non_resident.mapping_pairs_offset);
> -	return mp_offset >= min_len && mp_offset <= attr_len;
> +	if (mp_offset < min_len || mp_offset > attr_len)
> +		return false;
> +
> +	/*
> +	 * Validate name_offset for named attributes.
> +	 * may be zero for unnamed attributes.
> +	 */
> +	name_len = a->name_length;
> +	if (name_len) {
> +		name_offset = le16_to_cpu(a->name_offset);
> +
> +		if (name_offset < min_len || name_offset >= attr_len)
> +			return false;
> +
> +		name_end = name_offset + name_len * sizeof(__le16);
> +		if (name_end > attr_len || name_end > mp_offset)
> +			return false;
> +
> +		/*
> +		 * A normal non-resident attribute moves its name forward when
> +		 * it is converted to a sparse attribute.
> +		 */
> +		if (!(a->flags & (ATTR_IS_SPARSE | ATTR_COMPRESSION_MASK)) &&
> +		     name_end + 8 > attr_len)
> +			return false;
> +	}
> +
> +	return true;
> +
>   }
>   
>   static bool ntfs_attr_value_is_valid(struct ntfs_volume *vol,


  reply	other threads:[~2026-08-06  2:39 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-06  1:39 [PATCH v2] ntfs: fix name offset validation in ntfs_non_resident_attr_value_is_valid Hongling Zeng
2026-08-06  2:38 ` Hongling Zeng [this message]
2026-08-06  5:19   ` Namjae Jeon

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=6A73F3A9.7020608@126.com \
    --to=zhongling0719@126.com \
    --cc=alexandro.calo@nozominetworks.com \
    --cc=hyc.lee@gmail.com \
    --cc=linkinjeon@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=ntfs@lists.linux.dev \
    --cc=stable@vger.kernel.org \
    --cc=zenghongling@kylinos.cn \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.