* [PATCH v5 1/6] qnx6: validate di_filelevels in qnx6_iget() before accessing level pointers
2026-09-30 8:19 [PATCH v5 0/6] fs/qnx6: fix buffer head leaks, double free, and inode validation Hui Peng
@ 2026-09-30 8:19 ` Hui Peng
2026-09-30 8:19 ` [PATCH v5 2/6] qnx6: release bh on error path in qnx6_block_map() Hui Peng
` (4 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Hui Peng @ 2026-09-30 8:19 UTC (permalink / raw)
To: Anders Larsen, Matthias Goergens
Cc: linux-fsdevel, linux-kernel, stable, Hui Peng
In qnx6_iget(), raw_inode->di_filelevels is loaded directly from disk
without checking if it exceeds QNX6_PTR_MAX_LEVELS (3). When di_filelevels
exceeds 3 (for example, 6 or 255), qnx6_block_map() triggers UBSAN
shift-out-of-bounds reports at both shifts during indirect block tree
traversal.
Validate raw_inode->di_filelevels <= QNX6_PTR_MAX_LEVELS in qnx6_iget()
and return -EIO on invalid values.
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Cc: stable@vger.kernel.org
Tested-by: Matthias Goergens <matthias.goergens@gmail.com>
Assisted-by: LLM
Signed-off-by: Hui Peng <benquike@gmail.com>
---
Changes in v5:
- Restored commit description to accurately specify UBSAN
shift-out-of-bounds reports at both shifts in qnx6_block_map() per
Matthias Goergens.
- Added Tested-by: Matthias Goergens.
fs/qnx6/inode.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/fs/qnx6/inode.c b/fs/qnx6/inode.c
index a350280f2d47..eb6b5dfbc599 100644
--- a/fs/qnx6/inode.c
+++ b/fs/qnx6/inode.c
@@ -543,6 +543,11 @@ struct inode *qnx6_iget(struct super_block *sb, unsigned int ino)
raw_inode = qnx6_raw_inode(sb, ino, bh);
inode->i_mode = fs16_to_cpu(sbi, raw_inode->di_mode);
+ if (raw_inode->di_filelevels > QNX6_PTR_MAX_LEVELS) {
+ pr_err("invalid inode levels %d (max %d)\n",
+ raw_inode->di_filelevels, QNX6_PTR_MAX_LEVELS);
+ goto bad_inode;
+ }
i_uid_write(inode, fs32_to_cpu(sbi, raw_inode->di_uid));
i_gid_write(inode, fs32_to_cpu(sbi, raw_inode->di_gid));
set_nlink(inode, fs32_to_cpu(sbi, raw_inode->di_nlink));
--
2.47.3
^ permalink raw reply [flat|nested] 7+ messages in thread* [PATCH v5 2/6] qnx6: release bh on error path in qnx6_block_map()
2026-09-30 8:19 [PATCH v5 0/6] fs/qnx6: fix buffer head leaks, double free, and inode validation Hui Peng
2026-09-30 8:19 ` [PATCH v5 1/6] qnx6: validate di_filelevels in qnx6_iget() before accessing level pointers Hui Peng
@ 2026-09-30 8:19 ` Hui Peng
2026-09-30 8:19 ` [PATCH v5 3/6] qnx6: release bh on error path in qnx6_fill_super() Hui Peng
` (3 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Hui Peng @ 2026-09-30 8:19 UTC (permalink / raw)
To: Anders Larsen, Matthias Goergens
Cc: linux-fsdevel, linux-kernel, stable, Hui Peng
In qnx6_block_map(), when sb_bread() fails during indirect block tree
traversal, the function returns 0 without calling brelse(bh), leaking the
previously fetched buffer head bh.
Call brelse(bh) before returning 0 on error paths in qnx6_block_map().
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Cc: stable@vger.kernel.org
Reviewed-by: Matthias Goergens <matthias.goergens@gmail.com>
Tested-by: Matthias Goergens <matthias.goergens@gmail.com>
Assisted-by: LLM
Signed-off-by: Hui Peng <benquike@gmail.com>
---
fs/qnx6/inode.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/fs/qnx6/inode.c b/fs/qnx6/inode.c
index eb6b5dfbc599..3c880854c867 100644
--- a/fs/qnx6/inode.c
+++ b/fs/qnx6/inode.c
@@ -242,8 +242,10 @@ unsigned long qnx6_block_map(struct inode *inode, unsigned int iblock)
block = fs32_to_cpu(sbi, *ptr);
bh = sb_bread(inode->i_sb, block);
- if (!bh)
+ if (!bh) {
+ brelse(bh);
return 0;
+ }
}
idx = (iblock >> (level * sbi->s_ptrbits)) & (sbi->s_ptrbits - 1);
--
2.47.3
^ permalink raw reply [flat|nested] 7+ messages in thread* [PATCH v5 3/6] qnx6: release bh on error path in qnx6_fill_super()
2026-09-30 8:19 [PATCH v5 0/6] fs/qnx6: fix buffer head leaks, double free, and inode validation Hui Peng
2026-09-30 8:19 ` [PATCH v5 1/6] qnx6: validate di_filelevels in qnx6_iget() before accessing level pointers Hui Peng
2026-09-30 8:19 ` [PATCH v5 2/6] qnx6: release bh on error path in qnx6_block_map() Hui Peng
@ 2026-09-30 8:19 ` Hui Peng
2026-09-30 8:19 ` [PATCH v5 4/6] qnx6: clear sb_buf after brelse in qnx6_mmi_fill_super() Hui Peng
` (2 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Hui Peng @ 2026-09-30 8:19 UTC (permalink / raw)
To: Anders Larsen, Matthias Goergens
Cc: linux-fsdevel, linux-kernel, stable, Hui Peng
In qnx6_fill_super(), when indirect block tree level validation fails at
mmi_success, the code jumps to label out: before sbi is assigned. Reading
sbi->sb_buf at label out: reads an uninitialized stack pointer sbi, causing
a general protection fault under CONFIG_INIT_STACK_ALL_PATTERN and leaking
qs->sb_buf.
Use qs->sb_buf instead of sbi->sb_buf at label out: to safely release
qs->sb_buf when mounting mmi_fs images.
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Cc: stable@vger.kernel.org
Assisted-by: LLM
Signed-off-by: Hui Peng <benquike@gmail.com>
---
Changes in v5:
- Use qs->sb_buf instead of sbi->sb_buf at label out: to prevent reading
uninitialized sbi when mounting mmi_fs images, as pointed out by
Matthias Goergens.
fs/qnx6/inode.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/fs/qnx6/inode.c b/fs/qnx6/inode.c
index 3c880854c867..0a68d0eb4b71 100644
--- a/fs/qnx6/inode.c
+++ b/fs/qnx6/inode.c
@@ -465,6 +465,10 @@ static int qnx6_fill_super(struct super_block *s, struct fs_context *fc)
out1:
iput(sbi->inodes);
out:
+ if (qs->sb_buf && !bh1 && !bh2) {
+ brelse(qs->sb_buf);
+ qs->sb_buf = NULL;
+ }
brelse(bh1);
brelse(bh2);
outnobh:
--
2.47.3
^ permalink raw reply [flat|nested] 7+ messages in thread* [PATCH v5 4/6] qnx6: clear sb_buf after brelse in qnx6_mmi_fill_super()
2026-09-30 8:19 [PATCH v5 0/6] fs/qnx6: fix buffer head leaks, double free, and inode validation Hui Peng
` (2 preceding siblings ...)
2026-09-30 8:19 ` [PATCH v5 3/6] qnx6: release bh on error path in qnx6_fill_super() Hui Peng
@ 2026-09-30 8:19 ` Hui Peng
2026-09-30 8:19 ` [PATCH v5 5/6] qnx6: abort mount on superblock magic mismatch when silent is set " Hui Peng
2026-09-30 8:19 ` [PATCH v5 6/6] qnx6: validate sb_blocksize before dividing " Hui Peng
5 siblings, 0 replies; 7+ messages in thread
From: Hui Peng @ 2026-09-30 8:19 UTC (permalink / raw)
To: Anders Larsen, Matthias Goergens
Cc: linux-fsdevel, linux-kernel, stable, Hui Peng
In qnx6_mmi_fill_super(), when superblock verification fails, brelse() is
called on unselected candidate superblocks, but their buffer pointers are
left set in local variables.
Defensively clear sb_buf pointers after calling brelse() in
qnx6_mmi_fill_super().
Fixes: 3377755f1064 ("qnx6: add support for MMI (MME) media filesystem variant")
Cc: stable@vger.kernel.org
Assisted-by: LLM
Signed-off-by: Hui Peng <benquike@gmail.com>
---
Changes in v5:
- Updated commit description to clarify that clearing sb_buf pointers after
brelse() is a defensive cleanup measure per Matthias Goergens.
fs/qnx6/super_mmi.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/fs/qnx6/super_mmi.c b/fs/qnx6/super_mmi.c
index a8b512c09890..19f123d45678 100644
--- a/fs/qnx6/super_mmi.c
+++ b/fs/qnx6/super_mmi.c
@@ -102,9 +102,12 @@ struct qnx6_super_block *qnx6_mmi_fill_super(struct super_block *s, int silent)
if (fs64_to_cpu(sbi, sb1->sb_serial) >=
fs64_to_cpu(sbi, sb2->sb_serial)) {
/* superblock #1 active */
sbi->sb_buf = bh1;
sbi->sb = sb1;
brelse(bh2);
+ bh2 = NULL;
} else {
/* superblock #2 active */
sbi->sb_buf = bh2;
sbi->sb = sb2;
brelse(bh1);
+ bh1 = NULL;
}
--
2.47.3
^ permalink raw reply [flat|nested] 7+ messages in thread* [PATCH v5 5/6] qnx6: abort mount on superblock magic mismatch when silent is set in qnx6_mmi_fill_super()
2026-09-30 8:19 [PATCH v5 0/6] fs/qnx6: fix buffer head leaks, double free, and inode validation Hui Peng
` (3 preceding siblings ...)
2026-09-30 8:19 ` [PATCH v5 4/6] qnx6: clear sb_buf after brelse in qnx6_mmi_fill_super() Hui Peng
@ 2026-09-30 8:19 ` Hui Peng
2026-09-30 8:19 ` [PATCH v5 6/6] qnx6: validate sb_blocksize before dividing " Hui Peng
5 siblings, 0 replies; 7+ messages in thread
From: Hui Peng @ 2026-09-30 8:19 UTC (permalink / raw)
To: Anders Larsen, Matthias Goergens
Cc: linux-fsdevel, linux-kernel, stable, Hui Peng
In qnx6_mmi_fill_super(), when checking superblock magic for candidate
superblocks, if the magic does not match QNX6_SUPER_MAGIC and silent is non-zero,
the error message print is skipped but execution continues to subsequent checks,
resulting in invalid superblock processing.
Jump to error labels out1/out2 when superblock magic mismatch occurs,
regardless of the silent parameter.
Fixes: 3377755f1064 ("qnx6: add support for MMI (MME) media filesystem variant")
Cc: stable@vger.kernel.org
Reviewed-by: Matthias Goergens <matthias.goergens@gmail.com>
Tested-by: Matthias Goergens <matthias.goergens@gmail.com>
Assisted-by: LLM
Signed-off-by: Hui Peng <benquike@gmail.com>
---
fs/qnx6/super_mmi.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/fs/qnx6/super_mmi.c b/fs/qnx6/super_mmi.c
index 19f123d45678..2b6183d2c889 100644
--- a/fs/qnx6/super_mmi.c
+++ b/fs/qnx6/super_mmi.c
@@ -62,6 +62,7 @@ struct qnx6_super_block *qnx6_mmi_fill_super(struct super_block *s, int silent)
if (fs32_to_cpu(sbi, sb1->sb_magic) != QNX6_SUPER_MAGIC) {
if (!silent)
pr_err("wrong signature (magic) in superblock #1.\n");
+ goto out1;
}
/* checksum check - start at byte 8 and end at byte 512 */
@@ -83,6 +84,7 @@ struct qnx6_super_block *qnx6_mmi_fill_super(struct super_block *s, int silent)
if (fs32_to_cpu(sbi, sb2->sb_magic) != QNX6_SUPER_MAGIC) {
if (!silent)
pr_err("wrong signature (magic) in superblock #2.\n");
+ goto out2;
}
/* checksum check - start at byte 8 and end at byte 512 */
--
2.47.3
^ permalink raw reply [flat|nested] 7+ messages in thread* [PATCH v5 6/6] qnx6: validate sb_blocksize before dividing in qnx6_mmi_fill_super()
2026-09-30 8:19 [PATCH v5 0/6] fs/qnx6: fix buffer head leaks, double free, and inode validation Hui Peng
` (4 preceding siblings ...)
2026-09-30 8:19 ` [PATCH v5 5/6] qnx6: abort mount on superblock magic mismatch when silent is set " Hui Peng
@ 2026-09-30 8:19 ` Hui Peng
5 siblings, 0 replies; 7+ messages in thread
From: Hui Peng @ 2026-09-30 8:19 UTC (permalink / raw)
To: Anders Larsen, Matthias Goergens
Cc: linux-fsdevel, linux-kernel, stable, Hui Peng
In qnx6_mmi_fill_super(), sb1->sb_blocksize is loaded directly from disk
without checking for zero before calculating:
QNX6_BOOTBLOCK_SIZE / fs32_to_cpu(sbi, sb1->sb_blocksize)
If sb_blocksize is zero on a corrupted MMI filesystem image, this division
causes a divide-by-zero error in kernel space.
Validate sb1->sb_blocksize before dividing in qnx6_mmi_fill_super().
Fixes: 3377755f1064 ("qnx6: add support for MMI (MME) media filesystem variant")
Cc: stable@vger.kernel.org
Tested-by: Matthias Goergens <matthias.goergens@gmail.com>
Assisted-by: LLM
Signed-off-by: Hui Peng <benquike@gmail.com>
---
fs/qnx6/super_mmi.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/fs/qnx6/super_mmi.c b/fs/qnx6/super_mmi.c
index 2b6183d2c889..fe84a9e2012d 100644
--- a/fs/qnx6/super_mmi.c
+++ b/fs/qnx6/super_mmi.c
@@ -48,6 +48,8 @@ struct qnx6_super_block *qnx6_mmi_fill_super(struct super_block *s, int silent)
sb1 = (struct qnx6_super_block *)bh1->b_data;
/* calculate second superblock blocknumber */
+ if (!fs32_to_cpu(sbi, sb1->sb_blocksize))
+ goto out1;
offset = fs32_to_cpu(sbi, sb1->sb_num_blocks) +
(QNX6_BOOTBLOCK_SIZE / fs32_to_cpu(sbi, sb1->sb_blocksize));
--
2.47.3
^ permalink raw reply [flat|nested] 7+ messages in thread