mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH v5 0/6] fs/qnx6: fix buffer head leaks, double free, and inode validation
@ 2026-09-30  8:19 Hui Peng
  2026-09-30  8:19 ` [PATCH v5 1/6] qnx6: validate di_filelevels in qnx6_iget() before accessing level pointers Hui Peng
                   ` (5 more replies)
  0 siblings, 6 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

This series addresses buffer head memory leaks, uninitialized pointer reads,
out-of-bounds array access, and superblock validation issues in the qnx6
filesystem driver.

All patches have been ported to the latest tip of mainline (551c722f4080)
and verified in QEMU KVM with CONFIG_KASAN=y and UBSAN enabled.

Changes in v5:
- 1/6: Restored commit description to accurately specify UBSAN
  shift-out-of-bounds reports at both shifts in qnx6_block_map() as
  requested by Matthias Goergens.
- 3/6: Used qs->sb_buf instead of sbi->sb_buf at label out: in
  qnx6_fill_super() to prevent reading uninitialized sbi when mounting
  mmi_fs images, fixing a general protection fault reported by Matthias.
- 4/6: Updated commit description to state that clearing sb_buf after
  brelse() is defensive cleanup.
- Collected Tested-by and Reviewed-by tags from Matthias Goergens.

Hui Peng (6):
  qnx6: validate di_filelevels in qnx6_iget() before accessing level
    pointers
  qnx6: release bh on error path in qnx6_block_map()
  qnx6: release bh on error path in qnx6_fill_super()
  qnx6: clear sb_buf after brelse in qnx6_mmi_fill_super()
  qnx6: abort mount on superblock magic mismatch when silent is set in
    qnx6_mmi_fill_super()
  qnx6: validate sb_blocksize before dividing in qnx6_mmi_fill_super()

 fs/qnx6/inode.c     | 18 +++++++++++++++---
 fs/qnx6/super_mmi.c |  7 ++++++-
 2 files changed, 21 insertions(+), 4 deletions(-)

-- 
2.47.3

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [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

end of thread, other threads:[~2026-09-30  8:19 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
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 ` [PATCH v5 3/6] qnx6: release bh on error path in qnx6_fill_super() Hui Peng
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 ` [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

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®