* Forwarded: [PATCH] hfsplus: fix uninit-value in hfsplus_strcasecmp
2026-01-19 21:34 [syzbot] [hfs?] KMSAN: uninit-value in hfsplus_strcasecmp (2) syzbot
@ 2026-01-20 4:33 ` syzbot
2026-01-21 5:15 ` Forwarded: [PATCH] hfsplus: add debug printk to show uninitialized values in case_fold syzbot
` (8 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: syzbot @ 2026-01-20 4:33 UTC (permalink / raw)
To: linux-kernel, syzkaller-bugs
For archival purposes, forwarding an incoming command email to
linux-kernel@vger.kernel.org, syzkaller-bugs@googlegroups.com.
***
Subject: [PATCH] hfsplus: fix uninit-value in hfsplus_strcasecmp
Author: kartikey406@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+d80abb5b890d39261e72@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=d80abb5b890d39261e72
Signed-off-by: Deepanshu Kartikey <kartikey406@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
^ permalink raw reply related [flat|nested] 11+ messages in thread* Forwarded: [PATCH] hfsplus: add debug printk to show uninitialized values in case_fold
2026-01-19 21:34 [syzbot] [hfs?] KMSAN: uninit-value in hfsplus_strcasecmp (2) syzbot
2026-01-20 4:33 ` Forwarded: [PATCH] hfsplus: fix uninit-value in hfsplus_strcasecmp syzbot
@ 2026-01-21 5:15 ` syzbot
2026-01-21 5:24 ` syzbot
` (7 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: syzbot @ 2026-01-21 5:15 UTC (permalink / raw)
To: linux-kernel, syzkaller-bugs
For archival purposes, forwarding an incoming command email to
linux-kernel@vger.kernel.org, syzkaller-bugs@googlegroups.com.
***
Subject: [PATCH] hfsplus: add debug printk to show uninitialized values in case_fold
Author: kartikey406@gmail.com
#syz test: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git master
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 <kartikey406@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
^ permalink raw reply related [flat|nested] 11+ messages in thread* Forwarded: [PATCH] hfsplus: add debug printk to show uninitialized values in case_fold
2026-01-19 21:34 [syzbot] [hfs?] KMSAN: uninit-value in hfsplus_strcasecmp (2) syzbot
2026-01-20 4:33 ` Forwarded: [PATCH] hfsplus: fix uninit-value in hfsplus_strcasecmp syzbot
2026-01-21 5:15 ` Forwarded: [PATCH] hfsplus: add debug printk to show uninitialized values in case_fold syzbot
@ 2026-01-21 5:24 ` syzbot
2026-01-21 5:50 ` syzbot
` (6 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: syzbot @ 2026-01-21 5:24 UTC (permalink / raw)
To: linux-kernel, syzkaller-bugs
For archival purposes, forwarding an incoming command email to
linux-kernel@vger.kernel.org, syzkaller-bugs@googlegroups.com.
***
Subject: [PATCH] hfsplus: add debug printk to show uninitialized values in case_fold
Author: kartikey406@gmail.com
#syz test: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git master
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 <kartikey406@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
^ permalink raw reply related [flat|nested] 11+ messages in thread* Forwarded: [PATCH] hfsplus: add debug printk to show uninitialized values in case_fold
2026-01-19 21:34 [syzbot] [hfs?] KMSAN: uninit-value in hfsplus_strcasecmp (2) syzbot
` (2 preceding siblings ...)
2026-01-21 5:24 ` syzbot
@ 2026-01-21 5:50 ` syzbot
2026-01-22 8:42 ` Forwarded: [PATCH] hfsplus: add debug printk to show partial reads in hfs_brec_read syzbot
` (5 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: syzbot @ 2026-01-21 5:50 UTC (permalink / raw)
To: linux-kernel, syzkaller-bugs
For archival purposes, forwarding an incoming command email to
linux-kernel@vger.kernel.org, syzkaller-bugs@googlegroups.com.
***
Subject: [PATCH] hfsplus: add debug printk to show uninitialized values in case_fold
Author: kartikey406@gmail.com
#syz test: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git master
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 <kartikey406@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
^ permalink raw reply related [flat|nested] 11+ messages in thread* Forwarded: [PATCH] hfsplus: add debug printk to show partial reads in hfs_brec_read
2026-01-19 21:34 [syzbot] [hfs?] KMSAN: uninit-value in hfsplus_strcasecmp (2) syzbot
` (3 preceding siblings ...)
2026-01-21 5:50 ` syzbot
@ 2026-01-22 8:42 ` syzbot
2026-01-22 23:34 ` syzbot
` (4 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: syzbot @ 2026-01-22 8:42 UTC (permalink / raw)
To: linux-kernel, syzkaller-bugs
For archival purposes, forwarding an incoming command email to
linux-kernel@vger.kernel.org, syzkaller-bugs@googlegroups.com.
***
Subject: [PATCH] hfsplus: add debug printk to show partial reads in hfs_brec_read
Author: kartikey406@gmail.com
#syz test: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git master
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
Link: https://syzkaller.appspot.com/bug?extid=d80abb5b890d39261e72
Signed-off-by: Deepanshu Kartikey <kartikey406@gmail.com>
---
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
^ permalink raw reply related [flat|nested] 11+ messages in thread* Forwarded: [PATCH] hfsplus: add debug printk to show partial reads in hfs_brec_read
2026-01-19 21:34 [syzbot] [hfs?] KMSAN: uninit-value in hfsplus_strcasecmp (2) syzbot
` (4 preceding siblings ...)
2026-01-22 8:42 ` Forwarded: [PATCH] hfsplus: add debug printk to show partial reads in hfs_brec_read syzbot
@ 2026-01-22 23:34 ` syzbot
2026-02-11 15:05 ` Forwarded: [PATCH] hfsplus: fix uninit-value by validating catalog record size syzbot
` (3 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: syzbot @ 2026-01-22 23:34 UTC (permalink / raw)
To: linux-kernel, syzkaller-bugs
For archival purposes, forwarding an incoming command email to
linux-kernel@vger.kernel.org, syzkaller-bugs@googlegroups.com.
***
Subject: [PATCH] hfsplus: add debug printk to show partial reads in hfs_brec_read
Author: kartikey406@gmail.com
#syz test: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git master
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
Link: https://syzkaller.appspot.com/bug?extid=d80abb5b890d39261e72
Signed-off-by: Deepanshu Kartikey <kartikey406@gmail.com>
---
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
^ permalink raw reply related [flat|nested] 11+ messages in thread* Forwarded: [PATCH] hfsplus: fix uninit-value by validating catalog record size
2026-01-19 21:34 [syzbot] [hfs?] KMSAN: uninit-value in hfsplus_strcasecmp (2) syzbot
` (5 preceding siblings ...)
2026-01-22 23:34 ` syzbot
@ 2026-02-11 15:05 ` syzbot
2026-02-13 4:50 ` Forwarded: [PATCH v4] " syzbot
` (2 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: syzbot @ 2026-02-11 15:05 UTC (permalink / raw)
To: linux-kernel, syzkaller-bugs
For archival purposes, forwarding an incoming command email to
linux-kernel@vger.kernel.org, syzkaller-bugs@googlegroups.com.
***
Subject: [PATCH] hfsplus: fix uninit-value by validating catalog record size
Author: kartikey406@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
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+d80abb5b890d39261e72@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=d80abb5b890d39261e72
Debugged-by: Viacheslav Dubeyko <slava@dubeyko.com>
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Signed-off-by: Deepanshu Kartikey <kartikey406@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
^ permalink raw reply related [flat|nested] 11+ messages in thread* Forwarded: [PATCH v4] hfsplus: fix uninit-value by validating catalog record size
2026-01-19 21:34 [syzbot] [hfs?] KMSAN: uninit-value in hfsplus_strcasecmp (2) syzbot
` (6 preceding siblings ...)
2026-02-11 15:05 ` Forwarded: [PATCH] hfsplus: fix uninit-value by validating catalog record size syzbot
@ 2026-02-13 4:50 ` syzbot
2026-02-21 4:59 ` Forwarded: [PATCH] " syzbot
2026-03-07 0:25 ` Forwarded: [PATCH v6] " syzbot
9 siblings, 0 replies; 11+ messages in thread
From: syzbot @ 2026-02-13 4:50 UTC (permalink / raw)
To: linux-kernel, syzkaller-bugs
For archival purposes, forwarding an incoming command email to
linux-kernel@vger.kernel.org, syzkaller-bugs@googlegroups.com.
***
Subject: [PATCH v4] hfsplus: fix uninit-value by validating catalog record size
Author: kartikey406@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
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+d80abb5b890d39261e72@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=d80abb5b890d39261e72
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Tested-by: syzbot+d80abb5b890d39261e72@syzkaller.appspotmail.com
Link: https://lore.kernel.org/all/20260120051114.1281285-1-kartikey406@gmail.com/ [v1]
Link: https://lore.kernel.org/all/20260121063109.1830263-1-kartikey406@gmail.com/ [v2]
Link: https://lore.kernel.org/all/20260212014233.2422046-1-kartikey406@gmail.com/ [v3]
Signed-off-by: Deepanshu Kartikey <kartikey406@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;
}
+
+/**
+ * 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..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);
+}
+
+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
^ permalink raw reply related [flat|nested] 11+ messages in thread* Forwarded: [PATCH] hfsplus: fix uninit-value by validating catalog record size
2026-01-19 21:34 [syzbot] [hfs?] KMSAN: uninit-value in hfsplus_strcasecmp (2) syzbot
` (7 preceding siblings ...)
2026-02-13 4:50 ` Forwarded: [PATCH v4] " syzbot
@ 2026-02-21 4:59 ` syzbot
2026-03-07 0:25 ` Forwarded: [PATCH v6] " syzbot
9 siblings, 0 replies; 11+ messages in thread
From: syzbot @ 2026-02-21 4:59 UTC (permalink / raw)
To: linux-kernel, syzkaller-bugs
For archival purposes, forwarding an incoming command email to
linux-kernel@vger.kernel.org, syzkaller-bugs@googlegroups.com.
***
Subject: [PATCH] hfsplus: fix uninit-value by validating catalog record size
Author: kartikey406@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+d80abb5b890d39261e72@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=d80abb5b890d39261e72
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Tested-by: syzbot+d80abb5b890d39261e72@syzkaller.appspotmail.com
Reviewed-by: Viacheslav Dubeyko <slava@dube.com>
Tested-by: Viacheslav Dubeyko <slava@dubey.com>
Link: https://lore.kernel.org/all/20260120051114.1281285-1-kartikey406@gmail.com/ [v1]
Link: https://lore.kernel.org/all/20260121063109.1830263-1-kartikey406@gmail.com/ [v2]
Link: https://lore.kernel.org/all/20260212014233.2422046-1-kartikey406@gmail.com/ [v3]
Link: https://lore.kernel.org/all/20260214002100.436125-1-kartikey406@gmail.com/T/ [v4]
Signed-off-by: Deepanshu Kartikey <kartikey406@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)
fd->bnode = bnode;
return res;
}
+
+/**
+ * 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:
+ /* 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);
+ return -EIO;
+ }
+ 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 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)
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
^ permalink raw reply related [flat|nested] 11+ messages in thread* Forwarded: [PATCH v6] hfsplus: fix uninit-value by validating catalog record size
2026-01-19 21:34 [syzbot] [hfs?] KMSAN: uninit-value in hfsplus_strcasecmp (2) syzbot
` (8 preceding siblings ...)
2026-02-21 4:59 ` Forwarded: [PATCH] " syzbot
@ 2026-03-07 0:25 ` syzbot
9 siblings, 0 replies; 11+ messages in thread
From: syzbot @ 2026-03-07 0:25 UTC (permalink / raw)
To: linux-kernel, syzkaller-bugs
For archival purposes, forwarding an incoming command email to
linux-kernel@vger.kernel.org, syzkaller-bugs@googlegroups.com.
***
Subject: [PATCH v6] hfsplus: fix uninit-value by validating catalog record size
Author: kartikey406@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 against HFSPLUS_MIN_THREAD_SZ 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+d80abb5b890d39261e72@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=d80abb5b890d39261e72
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Tested-by: syzbot+d80abb5b890d39261e72@syzkaller.appspotmail.com
Link: https://lore.kernel.org/all/20260120051114.1281285-1-kartikey406@gmail.com/ [v1]
Link: https://lore.kernel.org/all/20260121063109.1830263-1-kartikey406@gmail.com/ [v2]
Link: https://lore.kernel.org/all/20260212014233.2422046-1-kartikey406@gmail.com/ [v3]
Link: https://lore.kernel.org/all/20260214002100.436125-1-kartikey406@gmail.com/T/ [v4]
Link: https://lore.kernel.org/all/20260221061626.15853-1-kartikey406@gmail.com/T/ [v5]
Signed-off-by: Deepanshu Kartikey <kartikey406@gmail.com>
---
Changes in v6:
- Use HFSPLUS_MIN_THREAD_SZ constant instead of offsetof() calculation
for thread record size check, as suggested by Charalampos Mitrodimas
Changes in v5:
- Add minimum size check for thread records before reading nodeName.length
to avoid reading uninitialized data
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 | 51 +++++++++++++++++++++++++++++++++++++++++
fs/hfsplus/catalog.c | 4 ++--
fs/hfsplus/dir.c | 2 +-
fs/hfsplus/hfsplus_fs.h | 9 ++++++++
fs/hfsplus/super.c | 2 +-
5 files changed, 64 insertions(+), 4 deletions(-)
diff --git a/fs/hfsplus/bfind.c b/fs/hfsplus/bfind.c
index 336d654861c5..9a55fa6d5294 100644
--- a/fs/hfsplus/bfind.c
+++ b/fs/hfsplus/bfind.c
@@ -287,3 +287,54 @@ int hfs_brec_goto(struct hfs_find_data *fd, int cnt)
fd->bnode = bnode;
return res;
}
+
+/**
+ * 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:
+ /* Ensure we have at least the fixed fields before reading nodeName.length */
+ if (fd->entrylength < HFSPLUS_MIN_THREAD_SZ) {
+ pr_err("thread record too short (got %u)\n", fd->entrylength);
+ return -EIO;
+ }
+ 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 d559bf8625f8..25535592234c 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 7229a8ae89f9..5ef0f71b1a33 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)
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
^ permalink raw reply related [flat|nested] 11+ messages in thread