Re: [PATCH] fs/ntfs3: bound attr_off in UpdateResidentValue against data_off

Konstantin Komarov <[email protected]> Tue, 2 Jun 2026 15:55:51 +0200
Newsgroups dev.linux.lists.ntfs3,org.kernel.vger.linux-fsdevel,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
On 5/15/26 18:33, Michael Bommarito wrote:

> In do_action()'s UpdateResidentValue case (fslog.c:3307),
> lrh->attr_off and lrh->redo_len come from the on-disk LRH.
> When they satisfy aoff + dlen < attr->res.data_off, the
> assignment
>
> 	attr->res.data_size = cpu_to_le32(aoff + dlen - data_off);
>
> underflows to ~4 GiB (e.g. 0xFFFFFFF9 when aoff=0x10, dlen=1,
> data_off=0x18).  Subsequent code that reads attr->res.data_size
> to walk the resident attribute payload would then read up to
> 4 GiB past the 1024-byte MFT record allocation.
>
> The existing mi_enum_attr() defense in fs/ntfs3/record.c:287
> catches the corrupted data_size on the next attribute walk
> and fails the mount, but only on the path that walks all
> attributes.  A read site that picks an attribute by name and
> reads its data_size without re-validating is not covered.
> Validate aoff against data_off and asize at the source.
>
> Reproduced under UML+KASAN on mainline 8d90b09e6741 via
> pr_warn-only probe: with aoff=0x10 and data_off=0x18, the
> post-assignment data_size is 0xfffffff9 (mount then fails
> at -22 from mi_enum_attr).
>
> Fixes: b46acd6a6a62 ("fs/ntfs3: Add NTFS journal")
> Cc: [email protected]
> Assisted-by: Claude:claude-opus-4-7
> Signed-off-by: Michael Bommarito <[email protected]>
> ---
>   fs/ntfs3/fslog.c | 11 +++++++++++
>   1 file changed, 11 insertions(+)
>
> diff --git a/fs/ntfs3/fslog.c b/fs/ntfs3/fslog.c
> index acfa18b84401e..4977ac1f62027 100644
> --- a/fs/ntfs3/fslog.c
> +++ b/fs/ntfs3/fslog.c
> @@ -3325,6 +3325,17 @@ static int do_action(struct ntfs_log *log, struct OPEN_ATTR_ENRTY *oe,
>   		nsize = ALIGN(nsize, 8);
>   		data_off = le16_to_cpu(attr->res.data_off);
>   
> +		/*
> +		 * aoff comes from the on-disk lrh->attr_off.  Forbid
> +		 * writes that begin below the resident attribute's
> +		 * data_off (which would overwrite the resident header),
> +		 * and forbid aoff + dlen < data_off, which would make
> +		 * the data_size assignment below underflow to ~4 GiB.
> +		 */
> +		if (aoff < data_off || aoff + dlen < data_off ||
> +		    aoff + dlen > asize)
> +			goto dirty_vol;
> +
>   		if (nsize < asize) {
>   			memmove(Add2Ptr(attr, aoff), data, dlen);
>   			data = NULL; // To skip below memmove().

Hello,

The patch was applied, thank you.

Regards,
Konstantin