[PATCH] fs/ntfs3: fix out-of-bounds write in ni_create_attr_list()
Xiang Mei <[email protected]> Tue, 23 Jun 2026 22:30:09 -0700
| Newsgroups | dev.linux.lists.ntfs3 |
|---|---|
| Message-ID | <[email protected]> |
From: Weiming Shi <[email protected]> ni_create_attr_list() allocates the attribute-list buffer with a fixed size of al_aligned(record_size) and then fills it in a loop with one ATTR_LIST_ENTRY per attribute in the base record, without checking the buffer bound: le = kzalloc(al_aligned(rs), GFP_NOFS); ... for (; (attr = mi_enum_attr(ni, &ni->mi, attr)); le = Add2Ptr(le, sz)) { sz = le_size(attr->name_len); le->type = attr->type; /* OOB write */ A minimal resident attribute is SIZEOF_RESIDENT (0x18) bytes in the record but le_size(0) is 0x20 bytes in the list, so every attribute costs 8 more bytes in the list than in the record. A base record from a crafted volume, packed with many small attributes, makes the list outgrow the buffer and the loop writes past it. Bail out with -EINVAL before writing an entry that would cross the buffer end. BUG: KASAN: slab-out-of-bounds in ni_create_attr_list (fs/ntfs3/frecord.c:788) Write of size 4 at addr ffff88802b333c00 by task exploit/5015 ni_create_attr_list (fs/ntfs3/frecord.c:788) ni_ins_attr_ext (fs/ntfs3/frecord.c:928) ni_insert_attr (fs/ntfs3/frecord.c:1095) ni_insert_resident (fs/ntfs3/frecord.c:1479) ntfs_set_ea (fs/ntfs3/xattr.c:449) ntfs_setxattr (fs/ntfs3/xattr.c:973) vfs_setxattr (fs/xattr.c:339) __x64_sys_setxattr do_syscall_64 The buggy address is located 0 bytes to the right of allocated 1024-byte region in cache kmalloc-1k. Fixes: 4342306f0f0d ("fs/ntfs3: Add file operations and implementation") Reported-by: Xiang Mei <[email protected]> Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Weiming Shi <[email protected]> --- fs/ntfs3/frecord.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/fs/ntfs3/frecord.c b/fs/ntfs3/frecord.c index 2b49bc077558..f77b729c24d0 100644 --- a/fs/ntfs3/frecord.c +++ b/fs/ntfs3/frecord.c @@ -755,7 +755,7 @@ int ni_create_attr_list(struct ntfs_inode *ni) u32 lsize; struct ATTRIB *attr; struct ATTRIB *arr_move[7]; - struct ATTR_LIST_ENTRY *le, *le_b[7]; + struct ATTR_LIST_ENTRY *le, *le_b[7], *le_end; struct MFT_REC *rec; bool is_mft; CLST rno = 0; @@ -775,6 +775,8 @@ int ni_create_attr_list(struct ntfs_inode *ni) if (!le) return -ENOMEM; + le_end = Add2Ptr(le, al_aligned(rs)); + mi_get_ref(&ni->mi, &le->ref); ni->attr_list.le = le; @@ -785,6 +787,13 @@ int ni_create_attr_list(struct ntfs_inode *ni) for (; (attr = mi_enum_attr(ni, &ni->mi, attr)); le = Add2Ptr(le, sz)) { sz = le_size(attr->name_len); + + /* A corrupted record can pack more attributes than fit. */ + if (Add2Ptr(le, sz) > (void *)le_end) { + err = -EINVAL; + goto out; + } + le->type = attr->type; le->size = cpu_to_le16(sz); le->name_len = attr->name_len; -- 2.43.0