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