Re: [PATCH] fs/ntfs3: validate ef->size covers the record's name and value

Konstantin Komarov <[email protected]>
Newsgroups dev.linux.lists.ntfs3,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
On 6/25/26 06:00, Weiming Shi wrote:

> [You don't often get email from [email protected]. Learn why this is important at https://aka.ms/LearnAboutSenderIdentification ]
>
> When an EA record has a non-zero ef->size, ntfs_read_ea() only checks
> that the record fits in the remaining buffer (ea_size > bytes), not that
> ef->size is large enough to hold the record's own name_len + 1 + elength.
>
> A crafted image can pass validation with, e.g., ef->size = 24 but
> elength = 0xffff. ntfs_get_ea() then trusts elength and copies it out of
> the undersized record, reading past the kmalloc(info->size) allocation
> and leaking heap memory to userspace via getxattr():
>
>   BUG: KASAN: slab-out-of-bounds in ntfs_get_ea (fs/ntfs3/xattr.c:302)
>   Read of size 65535 at addr ffff888100794550 by task exploit
>    __asan_memcpy (mm/kasan/shadow.c:105)
>    ntfs_get_ea (fs/ntfs3/xattr.c:302)
>    ntfs_getxattr (fs/ntfs3/xattr.c:848)
>    __vfs_getxattr (fs/xattr.c:441)
>    vfs_getxattr (fs/xattr.c:474)
>    do_getxattr (fs/xattr.c:800)
>    path_getxattrat (fs/xattr.c:868)
>    do_syscall_64 (arch/x86/entry/syscall_64.c:94)
>
>   The buggy address is located 80 bytes inside of
>    allocated 84-byte region in cache kmalloc-96
>
> Compute the size the record needs and require ef->size to cover it.
>
> Fixes: 0e8235d28f3a ("fs/ntfs3: Check fields while reading")
> Reported-by: Xiang Mei <[email protected]>
> Assisted-by: Claude:claude-opus-4-8
> Signed-off-by: Weiming Shi <[email protected]>
> ---
>   fs/ntfs3/xattr.c | 21 ++++++++++++---------
>   1 file changed, 12 insertions(+), 9 deletions(-)
>
> diff --git a/fs/ntfs3/xattr.c b/fs/ntfs3/xattr.c
> index 9eeac0ab2b71..9097ded7d7dc 100644
> --- a/fs/ntfs3/xattr.c
> +++ b/fs/ntfs3/xattr.c
> @@ -146,26 +146,29 @@ static int ntfs_read_ea(struct ntfs_inode *ni, struct EA_FULL **ea,
>          for (off = 0; off < size; off += ea_size) {
>                  const struct EA_FULL *ef = Add2Ptr(ea_p, off);
>                  u32 bytes = size - off;
> +               size_t need;
>
>                  /* Check if we can use field ea->size. */
>                  if (bytes < sizeof(ef->size))
>                          goto out1;
>
> +               /* Check if we can use fields ef->name_len and ef->elength. */
> +               if (bytes < offsetof(struct EA_FULL, name))
> +                       goto out1;
> +
> +               /* Size needed to hold this record's name and value. */
> +               need = struct_size(ef, name,
> +                                  1 + ef->name_len + le16_to_cpu(ef->elength));
> +
>                  if (ef->size) {
>                          ea_size = le32_to_cpu(ef->size);
> -                       if (ea_size > bytes)
> +                       /* ef->size must fit the list and cover the record. */
> +                       if (ea_size > bytes || ea_size < need)
>                                  goto out1;
>                          continue;
>                  }
>
> -               /* Check if we can use fields ef->name_len and ef->elength. */
> -               if (bytes < offsetof(struct EA_FULL, name))
> -                       goto out1;
> -
> -               ea_size = ALIGN(struct_size(ef, name,
> -                                           1 + ef->name_len +
> -                                                   le16_to_cpu(ef->elength)),
> -                               4);
> +               ea_size = ALIGN(need, 4);
>                  if (ea_size > bytes)
>                          goto out1;
>          }
> --
> 2.43.0

Hello,

Sorry for the delay.
The patch was applied, thanks.

Regards,
Konstantin
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.