Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

hfsplus: fix slab-out-of-bounds read in hfsplus_uni2asc()

BUG: KASAN: slab-out-of-bounds in hfsplus_uni2asc+0xa71/0xb90 fs/hfsplus/unicode.c:186
Read of size 2 at addr ffff8880289ef218 by task syz.6.248/14290

CPU: 0 UID: 0 PID: 14290 Comm: syz.6.248 Not tainted 6.16.4 #1 PREEMPT(full)
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.15.0-1 04/01/2014
Call Trace:
<TASK>
__dump_stack lib/dump_stack.c:94 [inline]
dump_stack_lvl+0x116/0x1b0 lib/dump_stack.c:120
print_address_description mm/kasan/report.c:378 [inline]
print_report+0xca/0x5f0 mm/kasan/report.c:482
kasan_report+0xca/0x100 mm/kasan/report.c:595
hfsplus_uni2asc+0xa71/0xb90 fs/hfsplus/unicode.c:186
hfsplus_listxattr+0x5b6/0xbd0 fs/hfsplus/xattr.c:738
vfs_listxattr+0xbe/0x140 fs/xattr.c:493
listxattr+0xee/0x190 fs/xattr.c:924
filename_listxattr fs/xattr.c:958 [inline]
path_listxattrat+0x143/0x360 fs/xattr.c:988
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0xcb/0x4c0 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7fe0e9fae16d
Code: 02 b8 ff ff ff ff c3 66 0f 1f 44 00 00 f3 0f 1e fa 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 a8 ff ff ff f7 d8 64 89 01 48
RSP: 002b:00007fe0eae67f98 EFLAGS: 00000246 ORIG_RAX: 00000000000000c3
RAX: ffffffffffffffda RBX: 00007fe0ea205fa0 RCX: 00007fe0e9fae16d
RDX: 0000000000000000 RSI: 0000000000000000 RDI: 0000200000000000
RBP: 00007fe0ea0480f0 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007fe0ea206038 R14: 00007fe0ea205fa0 R15: 00007fe0eae48000
</TASK>

Allocated by task 14290:
kasan_save_stack+0x24/0x50 mm/kasan/common.c:47
kasan_save_track+0x14/0x30 mm/kasan/common.c:68
poison_kmalloc_redzone mm/kasan/common.c:377 [inline]
__kasan_kmalloc+0xaa/0xb0 mm/kasan/common.c:394
kasan_kmalloc include/linux/kasan.h:260 [inline]
__do_kmalloc_node mm/slub.c:4333 [inline]
__kmalloc_noprof+0x219/0x540 mm/slub.c:4345
kmalloc_noprof include/linux/slab.h:909 [inline]
hfsplus_find_init+0x95/0x1f0 fs/hfsplus/bfind.c:21
hfsplus_listxattr+0x331/0xbd0 fs/hfsplus/xattr.c:697
vfs_listxattr+0xbe/0x140 fs/xattr.c:493
listxattr+0xee/0x190 fs/xattr.c:924
filename_listxattr fs/xattr.c:958 [inline]
path_listxattrat+0x143/0x360 fs/xattr.c:988
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0xcb/0x4c0 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f

When hfsplus_uni2asc is called from hfsplus_listxattr,
it actually passes in a struct hfsplus_attr_unistr*.
The size of the corresponding structure is different from that of hfsplus_unistr,
so the previous fix (94458781aee6) is insufficient.
The pointer on the unicode buffer is still going beyond the allocated memory.

This patch introduces two warpper functions hfsplus_uni2asc_xattr_str and
hfsplus_uni2asc_str to process two unicode buffers,
struct hfsplus_attr_unistr* and struct hfsplus_unistr* respectively.
When ustrlen value is bigger than the allocated memory size,
the ustrlen value is limited to an safe size.

Fixes: 94458781aee6 ("hfsplus: fix slab-out-of-bounds read in hfsplus_uni2asc()")
Signed-off-by: Kang Chen <k.chen@smail.nju.edu.cn>
Reviewed-by: Viacheslav Dubeyko <slava@dubeyko.com>
Signed-off-by: Viacheslav Dubeyko <slava@dubeyko.com>
Link: https://lore.kernel.org/r/20250909031316.1647094-1-k.chen@smail.nju.edu.cn
Signed-off-by: Viacheslav Dubeyko <slava@dubeyko.com>

authored by

Kang Chen and committed by
Viacheslav Dubeyko
bea3e1d4 18b07c44

+29 -11
+1 -1
fs/hfsplus/dir.c
··· 204 204 fd.entrylength); 205 205 type = be16_to_cpu(entry.type); 206 206 len = NLS_MAX_CHARSET_SIZE * HFSPLUS_MAX_STRLEN; 207 - err = hfsplus_uni2asc(sb, &fd.key->cat.name, strbuf, &len); 207 + err = hfsplus_uni2asc_str(sb, &fd.key->cat.name, strbuf, &len); 208 208 if (err) 209 209 goto out; 210 210 if (type == HFSPLUS_FOLDER) {
+6 -2
fs/hfsplus/hfsplus_fs.h
··· 521 521 const struct hfsplus_unistr *s2); 522 522 int hfsplus_strcmp(const struct hfsplus_unistr *s1, 523 523 const struct hfsplus_unistr *s2); 524 - int hfsplus_uni2asc(struct super_block *sb, const struct hfsplus_unistr *ustr, 525 - char *astr, int *len_p); 524 + int hfsplus_uni2asc_str(struct super_block *sb, 525 + const struct hfsplus_unistr *ustr, char *astr, 526 + int *len_p); 527 + int hfsplus_uni2asc_xattr_str(struct super_block *sb, 528 + const struct hfsplus_attr_unistr *ustr, 529 + char *astr, int *len_p); 526 530 int hfsplus_asc2uni(struct super_block *sb, struct hfsplus_unistr *ustr, 527 531 int max_unistr_len, const char *astr, int len); 528 532 int hfsplus_hash_dentry(const struct dentry *dentry, struct qstr *str);
+19 -5
fs/hfsplus/unicode.c
··· 119 119 return NULL; 120 120 } 121 121 122 - int hfsplus_uni2asc(struct super_block *sb, 123 - const struct hfsplus_unistr *ustr, 124 - char *astr, int *len_p) 122 + static int hfsplus_uni2asc(struct super_block *sb, const struct hfsplus_unistr *ustr, 123 + int max_len, char *astr, int *len_p) 125 124 { 126 125 const hfsplus_unichr *ip; 127 126 struct nls_table *nls = HFSPLUS_SB(sb)->nls; ··· 133 134 ip = ustr->unicode; 134 135 135 136 ustrlen = be16_to_cpu(ustr->length); 136 - if (ustrlen > HFSPLUS_MAX_STRLEN) { 137 - ustrlen = HFSPLUS_MAX_STRLEN; 137 + if (ustrlen > max_len) { 138 + ustrlen = max_len; 138 139 pr_err("invalid length %u has been corrected to %d\n", 139 140 be16_to_cpu(ustr->length), ustrlen); 140 141 } ··· 253 254 out: 254 255 *len_p = (char *)op - astr; 255 256 return res; 257 + } 258 + 259 + inline int hfsplus_uni2asc_str(struct super_block *sb, 260 + const struct hfsplus_unistr *ustr, char *astr, 261 + int *len_p) 262 + { 263 + return hfsplus_uni2asc(sb, ustr, HFSPLUS_MAX_STRLEN, astr, len_p); 264 + } 265 + 266 + inline int hfsplus_uni2asc_xattr_str(struct super_block *sb, 267 + const struct hfsplus_attr_unistr *ustr, 268 + char *astr, int *len_p) 269 + { 270 + return hfsplus_uni2asc(sb, (const struct hfsplus_unistr *)ustr, 271 + HFSPLUS_ATTR_MAX_STRLEN, astr, len_p); 256 272 } 257 273 258 274 /*
+3 -3
fs/hfsplus/xattr.c
··· 735 735 goto end_listxattr; 736 736 737 737 xattr_name_len = NLS_MAX_CHARSET_SIZE * HFSPLUS_ATTR_MAX_STRLEN; 738 - if (hfsplus_uni2asc(inode->i_sb, 739 - (const struct hfsplus_unistr *)&fd.key->attr.key_name, 740 - strbuf, &xattr_name_len)) { 738 + if (hfsplus_uni2asc_xattr_str(inode->i_sb, 739 + &fd.key->attr.key_name, strbuf, 740 + &xattr_name_len)) { 741 741 pr_err("unicode conversion failed\n"); 742 742 res = -EIO; 743 743 goto end_listxattr;