[PATCH v4 3/6] qnx6: avoid double brelse() and fix sb_buf leak on error path in qnx6_fill_super()

From: Hui Peng

Date: Tue Sep 29 2026 - 23:16:43 EST


In qnx6_fill_super(), when active superblock selection chooses sb1 or
sb2, it calls brelse() on the inactive buffer head without setting its
pointer to NULL. If an error occurs later (e.g. Inode.levels validation
failure), label out: calls brelse(bh1) and brelse(bh2), resulting in a
double free of the inactive buffer head.

Additionally, if mmi_fs path is used, sbi->sb_buf is set by
qnx6_mmi_fill_super(), but on failure after mmi_success:,
brelse(sbi->sb_buf) is never called.

Set bh2 = NULL or bh1 = NULL after releasing the inactive buffer head,
and release sbi->sb_buf on error paths at label out:.

Tested in QEMU against tip of mainline commit 62f4c998b297 ("Merge tag 'parisc-for-7.3-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/deller/parisc-linux")
using a loop-device reproducer mounting with mmi_fs option: on the
unfixed kernel, failure after mmi_success leaked sbi->sb_buf
(sb_buf_page0_leaked=1); whereas with this fix applied, sbi->sb_buf is
released on error (sb_buf_page0_leaked=0).

Fixes: 5d026c724220 ("fs: initial qnx6fs addition")
Cc: stable@xxxxxxxxxxxxxxx
Assisted-by: LLM
Signed-off-by: Hui Peng <benquike@xxxxxxxxx>
---
Changes in v4:
- Rebased cleanly onto upstream mainline commit 62f4c998b297.
- Added QEMU test procedure and verification details in commit message body.

fs/qnx6/inode.c | 6 ++++++
1 file changed, 6 insertions(+)

diff --git a/fs/qnx6/inode.c b/fs/qnx6/inode.c
index 080f7698a5e0..d425daee090c 100644
--- a/fs/qnx6/inode.c
+++ b/fs/qnx6/inode.c
@@ -399,6 +399,7 @@ static int qnx6_fill_super(struct super_block *s, struct fs_context *fc)
sbi->sb_buf = bh1;
sbi->sb = (struct qnx6_super_block *)bh1->b_data;
brelse(bh2);
+ bh2 = NULL;
pr_info("superblock #1 active\n");
} else {
/* superblock #2 active */
@@ -405,5 +406,6 @@ static int qnx6_fill_super(struct super_block *s, struct fs_context *fc)
sbi->sb = (struct qnx6_super_block *)bh2->b_data;
brelse(bh1);
+ bh1 = NULL;
pr_info("superblock #2 active\n");
}
mmi_success:
@@ -467,6 +469,10 @@ static int qnx6_fill_super(struct super_block *s, struct fs_context *fc)
out1:
iput(sbi->inodes);
out:
+ if (sbi && sbi->sb_buf && !bh1 && !bh2) {
+ brelse(sbi->sb_buf);
+ sbi->sb_buf = NULL;
+ }
brelse(bh1);
brelse(bh2);
outnobh:
--
2.55.0.1082.g2b9226bbc0-goog