[syzbot] [hfs?] KMSAN: uninit-value in hfsplus_strcasecmp (2)

7 views
Skip to first unread message

syzbot

unread,
Jan 19, 2026, 4:34:34 PMJan 19
to fran...@vivo.com, glau...@physik.fu-berlin.de, linux-...@vger.kernel.org, linux-...@vger.kernel.org, sl...@dubeyko.com, syzkall...@googlegroups.com
Hello,

syzbot found the following issue on:

HEAD commit: 603c05a1639f Merge tag 'nfs-for-6.19-2' of git://git.linux..
git tree: upstream
console output: https://syzkaller.appspot.com/x/log.txt?x=178b339a580000
kernel config: https://syzkaller.appspot.com/x/.config?x=46b5f80a6e7aaa5c
dashboard link: https://syzkaller.appspot.com/bug?extid=d80abb5b890d39261e72
compiler: Debian clang version 20.1.8 (++20250708063551+0c9f909b7976-1~exp1~20250708183702.136), Debian LLD 20.1.8
syz repro: https://syzkaller.appspot.com/x/repro.syz?x=157be39a580000
C reproducer: https://syzkaller.appspot.com/x/repro.c?x=12625a3a580000

Downloadable assets:
disk image: https://storage.googleapis.com/syzbot-assets/f5064e5f9c76/disk-603c05a1.raw.xz
vmlinux: https://storage.googleapis.com/syzbot-assets/c466bcf334e3/vmlinux-603c05a1.xz
kernel image: https://storage.googleapis.com/syzbot-assets/4e1318b36fb1/bzImage-603c05a1.xz
mounted in repro: https://storage.googleapis.com/syzbot-assets/a0364f040b52/mount_0.gz

IMPORTANT: if you fix the issue, please add the following tag to the commit:
Reported-by: syzbot+d80abb...@syzkaller.appspotmail.com

loop0: detected capacity change from 0 to 1024
=====================================================
BUG: KMSAN: uninit-value in case_fold fs/hfsplus/unicode.c:26 [inline]
BUG: KMSAN: uninit-value in hfsplus_strcasecmp+0x63a/0x980 fs/hfsplus/unicode.c:67
case_fold fs/hfsplus/unicode.c:26 [inline]
hfsplus_strcasecmp+0x63a/0x980 fs/hfsplus/unicode.c:67
hfsplus_cat_case_cmp_key+0xb9/0x190 fs/hfsplus/catalog.c:26
hfs_find_rec_by_key+0xae/0x240 fs/hfsplus/bfind.c:89
__hfsplus_brec_find+0x274/0x840 fs/hfsplus/bfind.c:124
hfsplus_brec_find+0x4ec/0xa10 fs/hfsplus/bfind.c:190
hfsplus_find_cat+0x3b0/0x4f0 fs/hfsplus/catalog.c:220
hfsplus_iget+0x815/0xc30 fs/hfsplus/super.c:96
hfsplus_fill_super+0x1550/0x2580 fs/hfsplus/super.c:548
get_tree_bdev_flags+0x6e6/0x920 fs/super.c:1691
get_tree_bdev+0x38/0x50 fs/super.c:1714
hfsplus_get_tree+0x35/0x40 fs/hfsplus/super.c:680
vfs_get_tree+0xb3/0x5c0 fs/super.c:1751
fc_mount fs/namespace.c:1199 [inline]
do_new_mount_fc fs/namespace.c:3636 [inline]
do_new_mount+0x879/0x1700 fs/namespace.c:3712
path_mount+0x749/0x1fb0 fs/namespace.c:4022
do_mount fs/namespace.c:4035 [inline]
__do_sys_mount fs/namespace.c:4224 [inline]
__se_sys_mount+0x6f7/0x7e0 fs/namespace.c:4201
__x64_sys_mount+0xe4/0x150 fs/namespace.c:4201
x64_sys_call+0x38cb/0x3e70 arch/x86/include/generated/asm/syscalls_64.h:166
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0xd3/0xf80 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f

Uninit was stored to memory at:
hfsplus_cat_build_key_uni fs/hfsplus/catalog.c:77 [inline]
hfsplus_find_cat+0x356/0x4f0 fs/hfsplus/catalog.c:217
hfsplus_iget+0x815/0xc30 fs/hfsplus/super.c:96
hfsplus_fill_super+0x1550/0x2580 fs/hfsplus/super.c:548
get_tree_bdev_flags+0x6e6/0x920 fs/super.c:1691
get_tree_bdev+0x38/0x50 fs/super.c:1714
hfsplus_get_tree+0x35/0x40 fs/hfsplus/super.c:680
vfs_get_tree+0xb3/0x5c0 fs/super.c:1751
fc_mount fs/namespace.c:1199 [inline]
do_new_mount_fc fs/namespace.c:3636 [inline]
do_new_mount+0x879/0x1700 fs/namespace.c:3712
path_mount+0x749/0x1fb0 fs/namespace.c:4022
do_mount fs/namespace.c:4035 [inline]
__do_sys_mount fs/namespace.c:4224 [inline]
__se_sys_mount+0x6f7/0x7e0 fs/namespace.c:4201
__x64_sys_mount+0xe4/0x150 fs/namespace.c:4201
x64_sys_call+0x38cb/0x3e70 arch/x86/include/generated/asm/syscalls_64.h:166
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0xd3/0xf80 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f

Local variable tmp created at:
hfsplus_find_cat+0x43/0x4f0 fs/hfsplus/catalog.c:197
hfsplus_iget+0x815/0xc30 fs/hfsplus/super.c:96

CPU: 1 UID: 0 PID: 6037 Comm: syz.0.17 Not tainted syzkaller #0 PREEMPT(none)
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 10/25/2025
=====================================================


---
This report is generated by a bot. It may contain errors.
See https://goo.gl/tpsmEJ for more information about syzbot.
syzbot engineers can be reached at syzk...@googlegroups.com.

syzbot will keep track of this issue. See:
https://goo.gl/tpsmEJ#status for how to communicate with syzbot.

If the report is already addressed, let syzbot know by replying with:
#syz fix: exact-commit-title

If you want syzbot to run the reproducer, reply with:
#syz test: git://repo/address.git branch-or-commit-hash
If you attach or paste a git patch, syzbot will apply it before testing.

If you want to overwrite report's subsystems, reply with:
#syz set subsystems: new-subsystem
(See the list of subsystem names on the web dashboard)

If the report is a duplicate of another one, reply with:
#syz dup: exact-subject-of-another-report

If you want to undo deduplication, reply with:
#syz undup

syzbot

unread,
Jan 19, 2026, 11:33:47 PMJan 19
to linux-...@vger.kernel.org, syzkall...@googlegroups.com
For archival purposes, forwarding an incoming command email to
linux-...@vger.kernel.org, syzkall...@googlegroups.com.

***

Subject: [PATCH] hfsplus: fix uninit-value in hfsplus_strcasecmp
Author: karti...@gmail.com

#syz test: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git master

Syzbot reported a KMSAN uninit-value issue in hfsplus_strcasecmp() during
filesystem mount operations. The root cause is that hfsplus_find_cat()
declares a local hfsplus_cat_entry variable without initialization before
passing it to hfs_brec_read().

If hfs_brec_read() doesn't completely fill the entire structure (e.g., when
the on-disk data is shorter than sizeof(hfsplus_cat_entry)), the padding
bytes in tmp.thread.nodeName remain uninitialized. These uninitialized
bytes are then copied by hfsplus_cat_build_key_uni() into the search key,
and subsequently accessed by hfsplus_strcasecmp() during catalog lookups,
triggering the KMSAN warning.

Fix this by zeroing the tmp variable before use to ensure all padding
bytes are initialized.

Reported-by: syzbot+d80abb...@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=d80abb5b890d39261e72
Signed-off-by: Deepanshu Kartikey <karti...@gmail.com>
---
fs/hfsplus/catalog.c | 1 +
1 file changed, 1 insertion(+)

diff --git a/fs/hfsplus/catalog.c b/fs/hfsplus/catalog.c
index 02c1eee4a4b8..9c75d1736427 100644
--- a/fs/hfsplus/catalog.c
+++ b/fs/hfsplus/catalog.c
@@ -199,6 +199,7 @@ int hfsplus_find_cat(struct super_block *sb, u32 cnid,
u16 type;

hfsplus_cat_build_key_with_cnid(sb, fd->search_key, cnid);
+ memset(&tmp, 0, sizeof(tmp));
err = hfs_brec_read(fd, &tmp, sizeof(hfsplus_cat_entry));
if (err)
return err;
--
2.43.0

syzbot

unread,
Jan 20, 2026, 12:05:05 AMJan 20
to karti...@gmail.com, linux-...@vger.kernel.org, syzkall...@googlegroups.com
Hello,

syzbot has tested the proposed patch and the reproducer did not trigger any issue:

Reported-by: syzbot+d80abb...@syzkaller.appspotmail.com
Tested-by: syzbot+d80abb...@syzkaller.appspotmail.com

Tested on:

commit: 24d479d2 Linux 6.19-rc6
git tree: upstream
console output: https://syzkaller.appspot.com/x/log.txt?x=13b44d22580000
kernel config: https://syzkaller.appspot.com/x/.config?x=46b5f80a6e7aaa5c
dashboard link: https://syzkaller.appspot.com/bug?extid=d80abb5b890d39261e72
compiler: Debian clang version 20.1.8 (++20250708063551+0c9f909b7976-1~exp1~20250708183702.136), Debian LLD 20.1.8
patch: https://syzkaller.appspot.com/x/patch.diff?x=16444d22580000

Note: testing is done by a robot and is best-effort only.

syzbot

unread,
Jan 21, 2026, 12:15:21 AMJan 21
to linux-...@vger.kernel.org, syzkall...@googlegroups.com
For archival purposes, forwarding an incoming command email to
linux-...@vger.kernel.org, syzkall...@googlegroups.com.

***

Subject: [PATCH] hfsplus: add debug printk to show uninitialized values in case_fold
This is a debug patch to demonstrate the uninit-value bug reported by
syzbot. Added printk in case_fold() to show what uninitialized values
are being read from the unicode array and used as array indices.

This patch intentionally does NOT include the fix (no initialization of
tmp variable) to demonstrate the bug behavior.

Link: https://syzkaller.appspot.com/bug?extid=d80abb5b890d39261e72
Signed-off-by: Deepanshu Kartikey <karti...@gmail.com>
---
fs/hfsplus/unicode.c | 13 +++++++++----
1 file changed, 9 insertions(+), 4 deletions(-)

diff --git a/fs/hfsplus/unicode.c b/fs/hfsplus/unicode.c
index d3a142f4518b..418806c27943 100644
--- a/fs/hfsplus/unicode.c
+++ b/fs/hfsplus/unicode.c
@@ -11,7 +11,6 @@

#include <linux/types.h>
#include <linux/nls.h>
-
#include <kunit/visibility.h>

#include "hfsplus_fs.h"
@@ -22,12 +21,18 @@
static inline u16 case_fold(u16 c)
{
u16 tmp;
-
+ pr_alert("CASE_FOLD: Input c=0x%04x, c>>8=0x%02x (this is array index!)\n", c, c >> 8);
tmp = hfsplus_case_fold_table[c >> 8];
- if (tmp)
+ pr_alert("CASE_FOLD: table[0x%02x]=0x%04x\n", c >> 8, tmp);
+
+ if (tmp) {
+ pr_alert("CASE_FOLD: Second lookup: table[0x%04x + 0x%02x]\n",tmp, c & 0xff);
tmp = hfsplus_case_fold_table[tmp + (c & 0xff)];
- else
+ } else {
tmp = c;
+ }
+ pr_alert("CASE_FOLD: Final result=0x%04x\n", tmp);
+
return tmp;
}

--
2.43.0

syzbot

unread,
Jan 21, 2026, 12:18:02 AMJan 21
to karti...@gmail.com, linux-...@vger.kernel.org, syzkall...@googlegroups.com
Hello,

syzbot tried to test the proposed patch but the build/boot failed:

failed to checkout kernel repo git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/master: failed to run ["git" "checkout" "FETCH_HEAD" "--force"]: exit status 128


Tested on:

commit: [unknown
git tree: upstream
patch: https://syzkaller.appspot.com/x/patch.diff?x=161eee3a580000

syzbot

unread,
Jan 21, 2026, 12:24:59 AMJan 21
to linux-...@vger.kernel.org, syzkall...@googlegroups.com

syzbot

unread,
Jan 21, 2026, 12:35:05 AMJan 21
to karti...@gmail.com, linux-...@vger.kernel.org, syzkall...@googlegroups.com

syzbot

unread,
Jan 21, 2026, 12:50:04 AMJan 21
to linux-...@vger.kernel.org, syzkall...@googlegroups.com

syzbot

unread,
Jan 21, 2026, 1:03:04 AMJan 21
to karti...@gmail.com, linux-...@vger.kernel.org, syzkall...@googlegroups.com

syzbot

unread,
Jan 22, 2026, 3:42:32 AMJan 22
to linux-...@vger.kernel.org, syzkall...@googlegroups.com
For archival purposes, forwarding an incoming command email to
linux-...@vger.kernel.org, syzkall...@googlegroups.com.

***

Subject: [PATCH] hfsplus: add debug printk to show partial reads in hfs_brec_read
Debug patch to demonstrate how hfs_brec_read() can read less data than
requested when filesystem is corrupted, leaving uninitialized data in
the destination buffer.

NOT-FOR-MERGE: debug only
fs/hfsplus/bfind.c | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/fs/hfsplus/bfind.c b/fs/hfsplus/bfind.c
index 336d654861c5..9b89dce00ee9 100644
--- a/fs/hfsplus/bfind.c
+++ b/fs/hfsplus/bfind.c
@@ -217,9 +217,19 @@ int hfs_brec_read(struct hfs_find_data *fd, void *rec, u32 rec_len)
res = hfs_brec_find(fd, hfs_find_rec_by_key);
if (res)
return res;
- if (fd->entrylength > rec_len)
+ printk(KERN_ALERT "HFSPLUS_BREC_READ: rec_len=%u, fd->entrylength=%u\n",rec_len, fd->entrylength);
+ if (fd->entrylength > rec_len) {
+ printk(KERN_ALERT "HFSPLUS_BREC_READ: ERROR - entrylength > rec_len\n");
return -EINVAL;
+ }
+
+ if (fd->entrylength < rec_len) {
+ printk(KERN_ALERT "HFSPLUS_BREC_READ: WARNING - entrylength (%u) < rec_len (%u) - PARTIAL READ!\n",fd->entrylength, rec_len);
+ }
+
hfs_bnode_read(fd->bnode, rec, fd->entryoffset, fd->entrylength);
+
+ printk(KERN_ALERT "HFSPLUS_BREC_READ: Successfully read %u bytes (expected %u)\n",fd->entrylength, rec_len);
return 0;
}

--
2.43.0

syzbot

unread,
Jan 22, 2026, 3:46:04 AMJan 22
to karti...@gmail.com, linux-...@vger.kernel.org, syzkall...@googlegroups.com

syzbot

unread,
Jan 22, 2026, 6:34:18 PMJan 22
to linux-...@vger.kernel.org, syzkall...@googlegroups.com

syzbot

unread,
Jan 22, 2026, 8:14:06 PMJan 22
to karti...@gmail.com, linux-...@vger.kernel.org, syzkall...@googlegroups.com
Hello,

syzbot has tested the proposed patch but the reproducer is still triggering an issue:
KMSAN: uninit-value in hfsplus_strcasecmp

HFSPLUS_BREC_READ: rec_len=520, fd->entrylength=26
HFSPLUS_BREC_READ: WARNING - entrylength (26) < rec_len (520) - PARTIAL READ!
HFSPLUS_BREC_READ: Successfully read 26 bytes (expected 520)
=====================================================
BUG: KMSAN: uninit-value in case_fold fs/hfsplus/unicode.c:26 [inline]
BUG: KMSAN: uninit-value in hfsplus_strcasecmp+0x62a/0x970 fs/hfsplus/unicode.c:67
case_fold fs/hfsplus/unicode.c:26 [inline]
hfsplus_strcasecmp+0x62a/0x970 fs/hfsplus/unicode.c:67
hfsplus_cat_case_cmp_key+0xb9/0x190 fs/hfsplus/catalog.c:26
hfs_find_rec_by_key+0xae/0x250 fs/hfsplus/bfind.c:89
__hfsplus_brec_find+0x274/0x850 fs/hfsplus/bfind.c:124
hfsplus_brec_find+0x4ec/0xa10 fs/hfsplus/bfind.c:190
hfsplus_find_cat+0x3b0/0x4f0 fs/hfsplus/catalog.c:220
hfsplus_iget+0x815/0xc30 fs/hfsplus/super.c:96
hfsplus_fill_super+0x1550/0x2580 fs/hfsplus/super.c:548
get_tree_bdev_flags+0x6e6/0x920 fs/super.c:1691
get_tree_bdev+0x38/0x50 fs/super.c:1714
hfsplus_get_tree+0x35/0x40 fs/hfsplus/super.c:680
vfs_get_tree+0xb3/0x5c0 fs/super.c:1751
fc_mount fs/namespace.c:1199 [inline]
do_new_mount_fc fs/namespace.c:3636 [inline]
do_new_mount+0x879/0x1700 fs/namespace.c:3712
path_mount+0x777/0x1fe0 fs/namespace.c:4022
do_mount fs/namespace.c:4035 [inline]
__do_sys_mount fs/namespace.c:4224 [inline]
__se_sys_mount+0x6f7/0x7e0 fs/namespace.c:4201
__x64_sys_mount+0xe4/0x150 fs/namespace.c:4201
x64_sys_call+0x38cb/0x3e70 arch/x86/include/generated/asm/syscalls_64.h:166
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0xc9/0xf80 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f

Uninit was stored to memory at:
hfsplus_cat_build_key_uni fs/hfsplus/catalog.c:77 [inline]
hfsplus_find_cat+0x356/0x4f0 fs/hfsplus/catalog.c:217
hfsplus_iget+0x815/0xc30 fs/hfsplus/super.c:96
hfsplus_fill_super+0x1550/0x2580 fs/hfsplus/super.c:548
get_tree_bdev_flags+0x6e6/0x920 fs/super.c:1691
get_tree_bdev+0x38/0x50 fs/super.c:1714
hfsplus_get_tree+0x35/0x40 fs/hfsplus/super.c:680
vfs_get_tree+0xb3/0x5c0 fs/super.c:1751
fc_mount fs/namespace.c:1199 [inline]
do_new_mount_fc fs/namespace.c:3636 [inline]
do_new_mount+0x879/0x1700 fs/namespace.c:3712
path_mount+0x777/0x1fe0 fs/namespace.c:4022
do_mount fs/namespace.c:4035 [inline]
__do_sys_mount fs/namespace.c:4224 [inline]
__se_sys_mount+0x6f7/0x7e0 fs/namespace.c:4201
__x64_sys_mount+0xe4/0x150 fs/namespace.c:4201
x64_sys_call+0x38cb/0x3e70 arch/x86/include/generated/asm/syscalls_64.h:166
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0xc9/0xf80 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f

Local variable tmp created at:
hfsplus_find_cat+0x43/0x4f0 fs/hfsplus/catalog.c:197
hfsplus_iget+0x815/0xc30 fs/hfsplus/super.c:96

CPU: 1 UID: 0 PID: 6576 Comm: syz.0.17 Not tainted syzkaller #0 PREEMPT(none)
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/13/2026
=====================================================


Tested on:

commit: a66191c5 Merge tag 'hyperv-fixes-signed-20260121' of g..
git tree: upstream
console output: https://syzkaller.appspot.com/x/log.txt?x=12e48452580000
kernel config: https://syzkaller.appspot.com/x/.config?x=62c21fde37118981
dashboard link: https://syzkaller.appspot.com/bug?extid=d80abb5b890d39261e72
compiler: Debian clang version 21.1.8 (++20251221033036+2078da43e25a-1~exp1~20251221153213.50), Debian LLD 21.1.8
patch: https://syzkaller.appspot.com/x/patch.diff?x=14150bfa580000

syzbot

unread,
Feb 11, 2026, 10:05:52 AM (10 days ago) Feb 11
to linux-...@vger.kernel.org, syzkall...@googlegroups.com
For archival purposes, forwarding an incoming command email to
linux-...@vger.kernel.org, syzkall...@googlegroups.com.

***

Subject: [PATCH] hfsplus: fix uninit-value by validating catalog record size
Syzbot reported a KMSAN uninit-value issue in hfsplus_strcasecmp(). The
root cause is that hfs_brec_read() doesn't validate that the on-disk
record size matches the expected size for the record type being read.

When mounting a corrupted filesystem, hfs_brec_read() may read less data
than expected. For example, when reading a catalog thread record, the
debug output showed:

HFSPLUS_BREC_READ: rec_len=520, fd->entrylength=26
HFSPLUS_BREC_READ: WARNING - entrylength (26) < rec_len (520) - PARTIAL READ!

hfs_brec_read() only validates that entrylength is not greater than the
buffer size, but doesn't check if it's less than expected. It successfully
reads 26 bytes into a 520-byte structure and returns success, leaving 494
bytes uninitialized.

This uninitialized data in tmp.thread.nodeName then gets copied by
hfsplus_cat_build_key_uni() and used by hfsplus_strcasecmp(), triggering
the KMSAN warning when the uninitialized bytes are used as array indices
in case_fold().

Fix by introducing hfsplus_brec_read_cat() wrapper that:
1. Calls hfs_brec_read() to read the data
2. Validates the record size based on the type field:
- Fixed size for folder and file records
- Variable size for thread records (depends on string length)
3. Returns -EIO if size doesn't match expected

Also initialize the tmp variable in hfsplus_find_cat() as defensive
programming to ensure no uninitialized data even if validation is
bypassed.
Debugged-by: Viacheslav Dubeyko <sl...@dubeyko.com>
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Signed-off-by: Deepanshu Kartikey <karti...@gmail.com>
---
fs/hfsplus/bfind.c | 59 +++++++++++++++++++++++++++++++++++++++++
fs/hfsplus/catalog.c | 4 +--
fs/hfsplus/dir.c | 2 +-
fs/hfsplus/hfsplus_fs.h | 3 +++
fs/hfsplus/super.c | 2 +-
5 files changed, 66 insertions(+), 4 deletions(-)

diff --git a/fs/hfsplus/bfind.c b/fs/hfsplus/bfind.c
index 9b89dce00ee9..fe75f3f2c17a 100644
--- a/fs/hfsplus/bfind.c
+++ b/fs/hfsplus/bfind.c
@@ -297,3 +297,62 @@ int hfs_brec_goto(struct hfs_find_data *fd, int cnt)
fd->bnode = bnode;
return res;
}
+
+/**
+ * hfsplus_cat_thread_size - calculate expected size of a catalog thread record
+ * @thread: pointer to the thread record
+ *
+ * Returns the expected size based on the string length
+ */
+u32 hfsplus_cat_thread_size(const struct hfsplus_cat_thread *thread)
+{
+ return offsetof(struct hfsplus_cat_thread, nodeName) +
+ offsetof(struct hfsplus_unistr, unicode) +
+ be16_to_cpu(thread->nodeName.length) * sizeof(hfsplus_unichr);
+}
+
+/**
+ * hfsplus_brec_read_cat - read and validate a catalog record
+ * @fd: find data structure
+ * @entry: pointer to catalog entry to read into
+ *
+ * Reads a catalog record and validates its size matches the expected
+ * size based on the record type.
+ *
+ * Returns 0 on success, or negative error code on failure.
+ */
+int hfsplus_brec_read_cat(struct hfs_find_data *fd, hfsplus_cat_entry *entry)
+{
+ int res;
+ u32 expected_size;
+
+ res = hfs_brec_read(fd, entry, sizeof(hfsplus_cat_entry));
+ if (res)
+ return res;
+
+ /* Validate catalog record size based on type */
+ switch (be16_to_cpu(entry->type)) {
+ case HFSPLUS_FOLDER:
+ expected_size = sizeof(struct hfsplus_cat_folder);
+ break;
+ case HFSPLUS_FILE:
+ expected_size = sizeof(struct hfsplus_cat_file);
+ break;
+ case HFSPLUS_FOLDER_THREAD:
+ case HFSPLUS_FILE_THREAD:
+ expected_size = hfsplus_cat_thread_size(&entry->thread);
+ break;
+ default:
+ pr_err("unknown catalog record type %d\n",
+ be16_to_cpu(entry->type));
+ return -EIO;
+ }
+
+ if (fd->entrylength != expected_size) {
+ pr_err("catalog record size mismatch (type %d, got %u, expected %u)\n",
+ be16_to_cpu(entry->type), fd->entrylength, expected_size);
+ return -EIO;
+ }
+
+ return 0;
+}
diff --git a/fs/hfsplus/catalog.c b/fs/hfsplus/catalog.c
index 02c1eee4a4b8..6c8380f7208d 100644
--- a/fs/hfsplus/catalog.c
+++ b/fs/hfsplus/catalog.c
@@ -194,12 +194,12 @@ static int hfsplus_fill_cat_thread(struct super_block *sb,
int hfsplus_find_cat(struct super_block *sb, u32 cnid,
struct hfs_find_data *fd)
{
- hfsplus_cat_entry tmp;
+ hfsplus_cat_entry tmp = {0};
int err;
u16 type;

hfsplus_cat_build_key_with_cnid(sb, fd->search_key, cnid);
- err = hfs_brec_read(fd, &tmp, sizeof(hfsplus_cat_entry));
+ err = hfsplus_brec_read_cat(fd, &tmp);
if (err)
return err;

diff --git a/fs/hfsplus/dir.c b/fs/hfsplus/dir.c
index cadf0b5f9342..d86e2f7b289c 100644
--- a/fs/hfsplus/dir.c
+++ b/fs/hfsplus/dir.c
@@ -49,7 +49,7 @@ static struct dentry *hfsplus_lookup(struct inode *dir, struct dentry *dentry,
if (unlikely(err < 0))
goto fail;
again:
- err = hfs_brec_read(&fd, &entry, sizeof(entry));
+ err = hfsplus_brec_read_cat(&fd, &entry);
if (err) {
if (err == -ENOENT) {
hfs_find_exit(&fd);
diff --git a/fs/hfsplus/hfsplus_fs.h b/fs/hfsplus/hfsplus_fs.h
index 45fe3a12ecba..5efb5d176cd9 100644
--- a/fs/hfsplus/hfsplus_fs.h
+++ b/fs/hfsplus/hfsplus_fs.h
@@ -506,6 +506,9 @@ int hfsplus_submit_bio(struct super_block *sb, sector_t sector, void *buf,
void **data, blk_opf_t opf);
int hfsplus_read_wrapper(struct super_block *sb);

+u32 hfsplus_cat_thread_size(const struct hfsplus_cat_thread *thread);
+int hfsplus_brec_read_cat(struct hfs_find_data *fd, hfsplus_cat_entry *entry);
+
/*
* time helpers: convert between 1904-base and 1970-base timestamps
*
diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c
index aaffa9e060a0..e59611a664ef 100644
--- a/fs/hfsplus/super.c
+++ b/fs/hfsplus/super.c
@@ -567,7 +567,7 @@ static int hfsplus_fill_super(struct super_block *sb, struct fs_context *fc)
err = hfsplus_cat_build_key(sb, fd.search_key, HFSPLUS_ROOT_CNID, &str);
if (unlikely(err < 0))
goto out_put_root;
- if (!hfs_brec_read(&fd, &entry, sizeof(entry))) {
+ if (!hfsplus_brec_read_cat(&fd, &entry)) {
hfs_find_exit(&fd);
if (entry.type != cpu_to_be16(HFSPLUS_FOLDER)) {
err = -EIO;
--
2.43.0

syzbot

unread,
Feb 12, 2026, 11:50:44 PM (9 days ago) Feb 12
to linux-...@vger.kernel.org, syzkall...@googlegroups.com
For archival purposes, forwarding an incoming command email to
linux-...@vger.kernel.org, syzkall...@googlegroups.com.

***

Subject: [PATCH v4] hfsplus: fix uninit-value by validating catalog record size
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Tested-by: syzbot+d80abb...@syzkaller.appspotmail.com
Link: https://lore.kernel.org/all/20260120051114.128...@gmail.com/ [v1]
Link: https://lore.kernel.org/all/20260121063109.183...@gmail.com/ [v2]
Link: https://lore.kernel.org/all/20260212014233.242...@gmail.com/ [v3]
Signed-off-by: Deepanshu Kartikey <karti...@gmail.com>
---
Changes in v4:
- Move hfsplus_cat_thread_size() as static inline to header file as
suggested by Viacheslav Dubeyko

Changes in v3:
- Introduced hfsplus_brec_read_cat() wrapper function for catalog-specific
validation instead of modifying generic hfs_brec_read()
- Added hfsplus_cat_thread_size() helper to calculate variable-size thread
record sizes
- Use exact size match (!=) instead of minimum size check (<)
- Use sizeof(hfsplus_unichr) instead of hardcoded value 2
- Updated all catalog record read sites to use new wrapper function
- Addressed review feedback from Viacheslav Dubeyko

Changes in v2:
- Use structure initialization (= {0}) instead of memset()
- Improved commit message to clarify how uninitialized data is used
---
fs/hfsplus/bfind.c | 46 +++++++++++++++++++++++++++++++++++++++++
fs/hfsplus/catalog.c | 4 ++--
fs/hfsplus/dir.c | 2 +-
fs/hfsplus/hfsplus_fs.h | 9 ++++++++
fs/hfsplus/super.c | 2 +-
5 files changed, 59 insertions(+), 4 deletions(-)

diff --git a/fs/hfsplus/bfind.c b/fs/hfsplus/bfind.c
index 9b89dce00ee9..4c5fd21585ef 100644
--- a/fs/hfsplus/bfind.c
+++ b/fs/hfsplus/bfind.c
@@ -297,3 +297,49 @@ int hfs_brec_goto(struct hfs_find_data *fd, int cnt)
fd->bnode = bnode;
return res;
}
+
+/**
index 45fe3a12ecba..e811d33861af 100644
--- a/fs/hfsplus/hfsplus_fs.h
+++ b/fs/hfsplus/hfsplus_fs.h
@@ -506,6 +506,15 @@ int hfsplus_submit_bio(struct super_block *sb, sector_t sector, void *buf,
void **data, blk_opf_t opf);
int hfsplus_read_wrapper(struct super_block *sb);

+static inline u32 hfsplus_cat_thread_size(const struct hfsplus_cat_thread *thread)
+{
+ return offsetof(struct hfsplus_cat_thread, nodeName) +
+ offsetof(struct hfsplus_unistr, unicode) +
+ be16_to_cpu(thread->nodeName.length) * sizeof(hfsplus_unichr);
+}
+

syzbot

unread,
Feb 20, 2026, 11:59:34 PM (13 hours ago) Feb 20
to linux-...@vger.kernel.org, syzkall...@googlegroups.com
For archival purposes, forwarding an incoming command email to
linux-...@vger.kernel.org, syzkall...@googlegroups.com.

***

Subject: [PATCH] hfsplus: fix uninit-value by validating catalog record size
Author: karti...@gmail.com

#syz test: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git master

Syzbot reported a KMSAN uninit-value issue in hfsplus_strcasecmp(). The
root cause is that hfs_brec_read() doesn't validate that the on-disk
record size matches the expected size for the record type being read.

When mounting a corrupted filesystem, hfs_brec_read() may read less data
than expected. For example, when reading a catalog thread record, the
debug output showed:

HFSPLUS_BREC_READ: rec_len=520, fd->entrylength=26
HFSPLUS_BREC_READ: WARNING - entrylength (26) < rec_len (520) - PARTIAL READ!

hfs_brec_read() only validates that entrylength is not greater than the
buffer size, but doesn't check if it's less than expected. It successfully
reads 26 bytes into a 520-byte structure and returns success, leaving 494
bytes uninitialized.

This uninitialized data in tmp.thread.nodeName then gets copied by
hfsplus_cat_build_key_uni() and used by hfsplus_strcasecmp(), triggering
the KMSAN warning when the uninitialized bytes are used as array indices
in case_fold().

Fix by introducing hfsplus_brec_read_cat() wrapper that:
1. Calls hfs_brec_read() to read the data
2. Validates the record size based on the type field:
- Fixed size for folder and file records
- Variable size for thread records (depends on string length)
3. Returns -EIO if size doesn't match expected

For thread records, check minimum size before reading nodeName.length to
avoid reading uninitialized data at call sites that don't zero-initialize
the entry structure.

Also initialize the tmp variable in hfsplus_find_cat() as defensive
programming to ensure no uninitialized data even if validation is
bypassed.

Reported-by: syzbot+d80abb...@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=d80abb5b890d39261e72
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Tested-by: syzbot+d80abb...@syzkaller.appspotmail.com
Reviewed-by: Viacheslav Dubeyko <sl...@dube.com>
Tested-by: Viacheslav Dubeyko <sl...@dubey.com>
Link: https://lore.kernel.org/all/20260214002100.43...@gmail.com/T/ [v4]
Signed-off-by: Deepanshu Kartikey <karti...@gmail.com>
---
Changes in v5:
- Add minimum size check for thread records before reading nodeName.length
to avoid reading uninitialized data, as suggested by Charalampos Mitrodimas

Changes in v4:
- Move hfsplus_cat_thread_size() as static inline to header file

Changes in v3:
- Introduced hfsplus_brec_read_cat() wrapper function for catalog-specific
validation instead of modifying generic hfs_brec_read()
- Added hfsplus_cat_thread_size() helper to calculate variable-size thread
record sizes
- Use exact size match (!=) instead of minimum size check (<)
- Use sizeof(hfsplus_unichr) instead of hardcoded value 2
- Updated all catalog record read sites to use new wrapper function

Changes in v2:
- Use structure initialization (= {0}) instead of memset()
- Improved commit message to clarify how uninitialized data is used
---
fs/hfsplus/bfind.c | 52 +++++++++++++++++++++++++++++++++++++++++
fs/hfsplus/catalog.c | 4 ++--
fs/hfsplus/dir.c | 2 +-
fs/hfsplus/hfsplus_fs.h | 9 +++++++
fs/hfsplus/super.c | 2 +-
5 files changed, 65 insertions(+), 4 deletions(-)

diff --git a/fs/hfsplus/bfind.c b/fs/hfsplus/bfind.c
index 336d654861c5..2b9152c3107b 100644
--- a/fs/hfsplus/bfind.c
+++ b/fs/hfsplus/bfind.c
@@ -287,3 +287,55 @@ int hfs_brec_goto(struct hfs_find_data *fd, int cnt)
+ /* Ensure we have at least the fixed fields before reading nodeName.length */
+ if (fd->entrylength < offsetof(struct hfsplus_cat_thread, nodeName) +
+ offsetof(struct hfsplus_unistr, unicode)) {
+ pr_err("thread record too short (got %u)\n", fd->entrylength);
index ca5f74a140ec..8aeb861969d3 100644
--- a/fs/hfsplus/dir.c
+++ b/fs/hfsplus/dir.c
@@ -49,7 +49,7 @@ static struct dentry *hfsplus_lookup(struct inode *dir, struct dentry *dentry,
if (unlikely(err < 0))
goto fail;
again:
- err = hfs_brec_read(&fd, &entry, sizeof(entry));
+ err = hfsplus_brec_read_cat(&fd, &entry);
if (err) {
if (err == -ENOENT) {
hfs_find_exit(&fd);
diff --git a/fs/hfsplus/hfsplus_fs.h b/fs/hfsplus/hfsplus_fs.h
index 5f891b73a646..61d52091dd28 100644
--- a/fs/hfsplus/hfsplus_fs.h
+++ b/fs/hfsplus/hfsplus_fs.h
@@ -509,6 +509,15 @@ int hfsplus_submit_bio(struct super_block *sb, sector_t sector, void *buf,
void **data, blk_opf_t opf);
int hfsplus_read_wrapper(struct super_block *sb);

+static inline u32 hfsplus_cat_thread_size(const struct hfsplus_cat_thread *thread)
+{
+ return offsetof(struct hfsplus_cat_thread, nodeName) +
+ offsetof(struct hfsplus_unistr, unicode) +
+ be16_to_cpu(thread->nodeName.length) * sizeof(hfsplus_unichr);
+}
+
+int hfsplus_brec_read_cat(struct hfs_find_data *fd, hfsplus_cat_entry *entry);
+
/*
* time helpers: convert between 1904-base and 1970-base timestamps
*
diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c
index 592d8fbb748c..dcb4357aae3e 100644
--- a/fs/hfsplus/super.c
+++ b/fs/hfsplus/super.c
@@ -571,7 +571,7 @@ static int hfsplus_fill_super(struct super_block *sb, struct fs_context *fc)
Reply all
Reply to author
Forward
0 new messages