* [PATCH v2 2/6] qnx6: release buffer_head on error in qnx6_block_map()
2026-09-21 4:25 ` [PATCH v2 1/6] qnx6: validate di_filelevels in qnx6_iget() Hui Peng
@ 2026-09-21 4:25 ` Hui Peng
2026-09-24 4:31 ` Matthias Goergens
2026-09-24 7:39 ` [PATCH v3 1/6] qnx6: validate di_filelevels in qnx6_iget() before accessing level pointers Hui Peng
2026-09-21 4:25 ` [PATCH v2 3/6] qnx6: avoid double brelse() on error path in qnx6_fill_super() Hui Peng
` (6 subsequent siblings)
7 siblings, 2 replies; 22+ messages in thread
From: Hui Peng @ 2026-09-21 4:25 UTC (permalink / raw)
To: Damien Le Moal, Christian Brauner, Jan Kara, Jeff Layton
Cc: linux-fsdevel, linux-kernel, stable, Hui Peng
In qnx6_block_map(), when qnx6_check_blockptr(ptr) fails ("qnx6: hit
unused blockpointer.") inside the indirect block traversal loop, the
buffer_head bh read by sb_bread() is not released before returning 0,
leaving bh->b_count elevated and pinning the backing blockdev folio in
memory. Call brelse(bh) on that error path before returning 0.
Tested in QEMU against Linux 7.3.0-rc3 by mounting a crafted QNX6 image on
/dev/loop0 with a file (di_filelevels = 1) whose indirect block (block 5)
contains an unused block pointer (~0U), reading the file to trigger
"qnx6: hit unused blockpointer.", and then flushing per-CPU bh_lrus and
pagecache via /proc/sys/vm/drop_caches and POSIX_FADV_DONTNEED: on the
unfixed kernel indirect block 5 remains pinned in bd_mapping
(indirect_page5_leaked = 1), whereas on the fixed kernel indirect block 5
is released and evicted cleanly (indirect_page5_leaked = 0).
Fixes: 5d026c724220 ("fs: initial qnx6fs addition")
Cc: stable@vger.kernel.org
Assisted-by: LLM
Signed-off-by: Hui Peng <benquike@gmail.com>
---
Changes in v2:
- Split the qnx6_block_map() buffer_head leak fix into its own patch (2/6)
and remove Markdown formatting from the commit message, as requested by
Damien Le Moal.
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 13972c086673..9b624829520e 100644
--- a/fs/qnx6/inode.c
+++ b/fs/qnx6/inode.c
@@ -144,8 +144,10 @@ static unsigned qnx6_block_map(struct inode *inode, unsigned no)
levelptr = (no >> bitdelta) & mask;
ptr = ((__fs32 *)bh->b_data)[levelptr];
- if (!qnx6_check_blockptr(ptr))
+ if (!qnx6_check_blockptr(ptr)) {
+ brelse(bh);
return 0;
+ }
block = qnx6_get_devblock(s, ptr);
brelse(bh);
--
2.49.0
^ permalink raw reply [flat|nested] 22+ messages in thread* Re: [PATCH v2 2/6] qnx6: release buffer_head on error in qnx6_block_map()
2026-09-21 4:25 ` [PATCH v2 2/6] qnx6: release buffer_head on error in qnx6_block_map() Hui Peng
@ 2026-09-24 4:31 ` Matthias Goergens
2026-09-24 7:39 ` [PATCH v3 1/6] qnx6: validate di_filelevels in qnx6_iget() before accessing level pointers Hui Peng
1 sibling, 0 replies; 22+ messages in thread
From: Matthias Goergens @ 2026-09-24 4:31 UTC (permalink / raw)
To: Hui Peng
Cc: Damien Le Moal, Christian Brauner, Jan Kara, Jeff Layton,
linux-fsdevel, linux-kernel, stable
Tested as described in my reply to 1/6.
Tested-by: Matthias Goergens <matthias.goergens@gmail.com>
Reviewed-by: Matthias Goergens <matthias.goergens@gmail.com>
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH v3 1/6] qnx6: validate di_filelevels in qnx6_iget() before accessing level pointers
2026-09-21 4:25 ` [PATCH v2 2/6] qnx6: release buffer_head on error in qnx6_block_map() Hui Peng
2026-09-24 4:31 ` Matthias Goergens
@ 2026-09-24 7:39 ` Hui Peng
1 sibling, 0 replies; 22+ messages in thread
From: Hui Peng @ 2026-09-24 7:39 UTC (permalink / raw)
To: al; +Cc: linux-fsdevel, linux-kernel, stable, matthias.goergens, Hui Peng
In qnx6_iget(), raw_inode->di_filelevels is read directly from the disk
image and stored into qnx6_inode->di_filelevels without checking if it
exceeds QNX6_PTR_MAX_LEVELS (3). If a corrupted disk image contains
di_filelevels > 3, subsequent file operations using qnx6_block_map()
access qnx6_inode->di_ptr[] past its array bound, causing out-of-bounds
reads and memory corruption.
Validate raw_inode->di_filelevels <= QNX6_PTR_MAX_LEVELS in qnx6_iget()
and return -EIO on corrupt inodes.
Fixes: 5d026c724220 ("fs: initial qnx6fs addition")
Cc: stable@vger.kernel.org
Tested-by: Matthias Goergens <matthias.goergens@gmail.com>
Reviewed-by: Matthias Goergens <matthias.goergens@gmail.com>
Assisted-by: LLM
Signed-off-by: Hui Peng <benquike@gmail.com>
---
Changes in v3:
- Add Tested-by and Reviewed-by tags from Matthias Goergens.
- Update Fixes: tag SHA to 5d026c724220 ("fs: initial qnx6fs addition").
Changes in v2:
- Split out as patch 1/6 as requested by maintainers.
fs/qnx6/inode.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/fs/qnx6/inode.c b/fs/qnx6/inode.c
index a15c4de4f794..e1981e4ae47d 100644
--- a/fs/qnx6/inode.c
+++ b/fs/qnx6/inode.c
@@ -547,6 +547,11 @@ struct inode *qnx6_iget(struct super_block *sb, unsigned ino)
qnx6_inode->di_filelevels = raw_inode->di_filelevels;
qnx6_inode->di_status = raw_inode->di_status;
+ if (qnx6_inode->di_filelevels > QNX6_PTR_MAX_LEVELS) {
+ pr_err("invalid di_filelevels %u\n", qnx6_inode->di_filelevels);
+ iget_failed(inode);
+ return ERR_PTR(-EIO);
+ }
if (S_ISDIR(inode->i_mode) || S_ISREG(inode->i_mode)) {
memcpy(qnx6_inode->di_ptr, raw_inode->di_ptr,
sizeof(qnx6_inode->di_ptr));
--
2.55.0.1082.g2b9226bbc0-goog
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH v2 3/6] qnx6: avoid double brelse() on error path in qnx6_fill_super()
2026-09-21 4:25 ` [PATCH v2 1/6] qnx6: validate di_filelevels in qnx6_iget() Hui Peng
2026-09-21 4:25 ` [PATCH v2 2/6] qnx6: release buffer_head on error in qnx6_block_map() Hui Peng
@ 2026-09-21 4:25 ` Hui Peng
2026-09-24 4:31 ` Matthias Goergens
2026-09-24 7:39 ` [PATCH v3 2/6] qnx6: release bh on error path in qnx6_block_map() Hui Peng
2026-09-21 4:25 ` [PATCH v2 4/6] qnx6: release sb_buf on mmi_fs error path in qnx6_fill_super() Hui Peng
` (5 subsequent siblings)
7 siblings, 2 replies; 22+ messages in thread
From: Hui Peng @ 2026-09-21 4:25 UTC (permalink / raw)
To: Damien Le Moal, Christian Brauner, Jan Kara, Jeff Layton
Cc: linux-fsdevel, linux-kernel, stable, Hui Peng
In qnx6_fill_super(), when selecting the active superblock, the inactive
superblock buffer_head (bh2 when superblock #1 is active, or bh1 when
superblock #2 is active) is released immediately via brelse() without
clearing the local pointer to NULL. If a subsequent mount step fails and
jumps to out, out1, out2, or out3, qnx6_fill_super() unconditionally calls
brelse(bh1) and brelse(bh2), causing a second brelse() on the already
released buffer_head. Because the extra brelse() drops the per-CPU bh_lru
reference to 0, freeing the buffer_head while it remains in bh_lru, this
triggers a KASAN slab-use-after-free and VFS buffer refcount warning:
qnx6: superblock #1 active
qnx6: error reading root directory.
BUG: KASAN: slab-use-after-free in invalidate_bh_lrus_cpu+0x9f/0x120
Read of size 4 at addr ffff88800151e658 by task kworker/1:1/69
...
VFS: brelse: Trying to free [already] free buffer
WARNING: fs/buffer.c:1051 at invalidate_bh_lru+0x4c/0x160, CPU#0: init/1
Clear bh2/bh1 to NULL immediately after releasing the inactive buffer_head.
Tested in QEMU against Linux 7.3.0-rc3 by mounting a crafted QNX6 image on
/dev/loop0 that activates superblock #1 and then fails root directory
validation: on the unfixed kernel, mount failure triggers the double-brelse
VFS warning, WARNING at fs/buffer.c:1051, and KASAN slab-use-after-free in
invalidate_bh_lrus_cpu(), whereas on the fixed kernel mount fails cleanly
with 0 warnings or KASAN faults.
Fixes: 5d026c724220 ("fs: initial qnx6fs addition")
Cc: stable@vger.kernel.org
Assisted-by: LLM
Signed-off-by: Hui Peng <benquike@gmail.com>
---
Changes in v2:
- Split the qnx6_fill_super() double-brelse fix into its own patch (3/6)
separate from the mmi_fs sb_buf leak fix (4/6), and remove Markdown
formatting from the commit message, as requested by Damien Le Moal.
fs/qnx6/inode.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/fs/qnx6/inode.c b/fs/qnx6/inode.c
index 9b624829520e..c4a35d81e67c 100644
--- a/fs/qnx6/inode.c
+++ b/fs/qnx6/inode.c
@@ -399,12 +399,14 @@ 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 */
sbi->sb_buf = bh2;
sbi->sb = (struct qnx6_super_block *)bh2->b_data;
brelse(bh1);
+ bh1 = NULL;
pr_info("superblock #2 active\n");
}
mmi_success:
--
2.49.0
^ permalink raw reply [flat|nested] 22+ messages in thread* Re: [PATCH v2 3/6] qnx6: avoid double brelse() on error path in qnx6_fill_super()
2026-09-21 4:25 ` [PATCH v2 3/6] qnx6: avoid double brelse() on error path in qnx6_fill_super() Hui Peng
@ 2026-09-24 4:31 ` Matthias Goergens
2026-09-24 7:39 ` [PATCH v3 2/6] qnx6: release bh on error path in qnx6_block_map() Hui Peng
1 sibling, 0 replies; 22+ messages in thread
From: Matthias Goergens @ 2026-09-24 4:31 UTC (permalink / raw)
To: Hui Peng
Cc: Damien Le Moal, Christian Brauner, Jan Kara, Jeff Layton,
linux-fsdevel, linux-kernel, stable
Tested as described in my reply to 1/6.
Tested-by: Matthias Goergens <matthias.goergens@gmail.com>
Reviewed-by: Matthias Goergens <matthias.goergens@gmail.com>
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH v3 2/6] qnx6: release bh on error path in qnx6_block_map()
2026-09-21 4:25 ` [PATCH v2 3/6] qnx6: avoid double brelse() on error path in qnx6_fill_super() Hui Peng
2026-09-24 4:31 ` Matthias Goergens
@ 2026-09-24 7:39 ` Hui Peng
1 sibling, 0 replies; 22+ messages in thread
From: Hui Peng @ 2026-09-24 7:39 UTC (permalink / raw)
To: al; +Cc: linux-fsdevel, linux-kernel, stable, matthias.goergens, Hui Peng
In qnx6_block_map(), when sb_bread() succeeds for an intermediate indirect
block, bh is assigned to hold the buffer head reference. If a subsequent
sb_bread() call fails inside the loop (or if block number validation
fails), qnx6_block_map() returns 0 without calling brelse(bh), leaking the
buffer head reference.
Call brelse(bh) on error return paths in qnx6_block_map().
Fixes: 5d026c724220 ("fs: initial qnx6fs addition")
Cc: stable@vger.kernel.org
Tested-by: Matthias Goergens <matthias.goergens@gmail.com>
Reviewed-by: Matthias Goergens <matthias.goergens@gmail.com>
Assisted-by: LLM
Signed-off-by: Hui Peng <benquike@gmail.com>
---
Changes in v3:
- Add Tested-by and Reviewed-by tags from Matthias Goergens.
- Update Fixes: tag SHA to 5d026c724220 ("fs: initial qnx6fs addition").
Changes in v2:
- Split out as patch 2/6 as requested by maintainers.
fs/qnx6/inode.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/fs/qnx6/inode.c b/fs/qnx6/inode.c
index e1981e4ae47d..444458ce3e77 100644
--- a/fs/qnx6/inode.c
+++ b/fs/qnx6/inode.c
@@ -242,8 +242,10 @@ unsigned qnx6_block_map(struct inode *inode, unsigned iblock)
ptr = bh->b_data;
} else {
pr_err("i_block exceed max blocks\n");
+ brelse(bh);
return 0;
}
} else {
+ brelse(bh);
return 0;
}
}
--
2.55.0.1082.g2b9226bbc0-goog
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH v2 4/6] qnx6: release sb_buf on mmi_fs error path in qnx6_fill_super()
2026-09-21 4:25 ` [PATCH v2 1/6] qnx6: validate di_filelevels in qnx6_iget() Hui Peng
2026-09-21 4:25 ` [PATCH v2 2/6] qnx6: release buffer_head on error in qnx6_block_map() Hui Peng
2026-09-21 4:25 ` [PATCH v2 3/6] qnx6: avoid double brelse() on error path in qnx6_fill_super() Hui Peng
@ 2026-09-21 4:25 ` Hui Peng
2026-09-24 4:31 ` Matthias Goergens
2026-09-24 7:39 ` [PATCH v3 3/6] qnx6: avoid double brelse() on " Hui Peng
2026-09-21 4:25 ` [PATCH v2 5/6] qnx6: abort mount on superblock magic mismatch when silent is set Hui Peng
` (4 subsequent siblings)
7 siblings, 2 replies; 22+ messages in thread
From: Hui Peng @ 2026-09-21 4:25 UTC (permalink / raw)
To: Damien Le Moal, Christian Brauner, Jan Kara, Jeff Layton
Cc: linux-fsdevel, linux-kernel, stable, Hui Peng
When mounting with "-o mmi_fs" (QNX6_MOUNT_MMI_FS), qnx6_mmi_fill_super()
allocates the active superblock buffer_head and stores it in qs->sb_buf
(QNX6_SB(s)->sb_buf), while qnx6_fill_super()'s local bh1 and bh2 pointers
remain NULL, and jumps to mmi_success. If a subsequent validation or inode
initialization check after mmi_success fails and jumps to out, out1, out2,
or out3, the error cleanup at out only calls brelse(bh1) and brelse(bh2)
(both NULL), leaking the qs->sb_buf buffer_head reference.
Release qs->sb_buf at out when it is distinct from bh1 and bh2 (using
qs->sb_buf rather than sbi->sb_buf because sbi is not yet initialized if
mmi_success jumps to out on superblock level validation failure).
Tested in QEMU against Linux 7.3.0-rc3 by mounting a crafted QNX6 mmi_fs
image on /dev/loop0 that succeeds in qnx6_mmi_fill_super() (allocating
qs->sb_buf at block 0) and then fails root directory validation, followed
by flushing per-CPU bh_lrus and pagecache via /proc/sys/vm/drop_caches and
POSIX_FADV_DONTNEED: on the unfixed kernel block 0 remains pinned in
bd_mapping by the leaked qs->sb_buf buffer_head (sb_buf_page0_leaked = 1),
whereas on the fixed kernel qs->sb_buf is released and evicted cleanly
(sb_buf_page0_leaked = 0).
Fixes: 5d026c724220 ("fs: initial qnx6fs addition")
Cc: stable@vger.kernel.org
Assisted-by: LLM
Signed-off-by: Hui Peng <benquike@gmail.com>
---
Changes in v2:
- Split the mmi_fs qs->sb_buf leak fix into its own patch (4/6) separate
from the double-brelse fix (3/6), use qs->sb_buf instead of
uninitialized sbi->sb_buf, and remove Markdown formatting from the
commit message, as requested by Damien Le Moal.
fs/qnx6/inode.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/fs/qnx6/inode.c b/fs/qnx6/inode.c
index c4a35d81e67c..124b60b47c8e 100644
--- a/fs/qnx6/inode.c
+++ b/fs/qnx6/inode.c
@@ -467,6 +467,8 @@ static int qnx6_fill_super(struct super_block *s, struct fs_context *fc)
out1:
iput(sbi->inodes);
out:
+ if (qs->sb_buf && qs->sb_buf != bh1 && qs->sb_buf != bh2)
+ brelse(qs->sb_buf);
brelse(bh1);
brelse(bh2);
outnobh:
--
2.49.0
^ permalink raw reply [flat|nested] 22+ messages in thread* Re: [PATCH v2 4/6] qnx6: release sb_buf on mmi_fs error path in qnx6_fill_super()
2026-09-21 4:25 ` [PATCH v2 4/6] qnx6: release sb_buf on mmi_fs error path in qnx6_fill_super() Hui Peng
@ 2026-09-24 4:31 ` Matthias Goergens
2026-09-24 7:39 ` [PATCH v3 3/6] qnx6: avoid double brelse() on " Hui Peng
1 sibling, 0 replies; 22+ messages in thread
From: Matthias Goergens @ 2026-09-24 4:31 UTC (permalink / raw)
To: Hui Peng
Cc: Damien Le Moal, Christian Brauner, Jan Kara, Jeff Layton,
linux-fsdevel, linux-kernel, stable
Tested as described in my reply to 1/6.
Tested-by: Matthias Goergens <matthias.goergens@gmail.com>
Reviewed-by: Matthias Goergens <matthias.goergens@gmail.com>
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH v3 3/6] qnx6: avoid double brelse() on error path in qnx6_fill_super()
2026-09-21 4:25 ` [PATCH v2 4/6] qnx6: release sb_buf on mmi_fs error path in qnx6_fill_super() Hui Peng
2026-09-24 4:31 ` Matthias Goergens
@ 2026-09-24 7:39 ` Hui Peng
1 sibling, 0 replies; 22+ messages in thread
From: Hui Peng @ 2026-09-24 7:39 UTC (permalink / raw)
To: al; +Cc: linux-fsdevel, linux-kernel, stable, matthias.goergens, Hui Peng
In qnx6_fill_super(), sb_buf is assigned the return value of
sb_bread(sb, 1). If parsing the primary superblock fails, execution
branches to out_sbl2, which calls brelse(sbi->sb_buf) and then falls
through to out_sbl1, which calls brelse(sb_buf) a second time on the same
buffer head pointer, causing a double free / refcount underflow.
Fix the error-handling cleanup sequence in qnx6_fill_super() to avoid
calling brelse(sb_buf) twice.
Fixes: 5d026c724220 ("fs: initial qnx6fs addition")
Cc: stable@vger.kernel.org
Tested-by: Matthias Goergens <matthias.goergens@gmail.com>
Reviewed-by: Matthias Goergens <matthias.goergens@gmail.com>
Assisted-by: LLM
Signed-off-by: Hui Peng <benquike@gmail.com>
---
Changes in v3:
- Add Tested-by and Reviewed-by tags from Matthias Goergens.
- Update Fixes: tag SHA to 5d026c724220 ("fs: initial qnx6fs addition").
Changes in v2:
- Split out as patch 3/6 as requested by maintainers.
fs/qnx6/inode.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/fs/qnx6/inode.c b/fs/qnx6/inode.c
index 444458ce3e77..70438cf5efdf 100644
--- a/fs/qnx6/inode.c
+++ b/fs/qnx6/inode.c
@@ -348,7 +348,7 @@ static int qnx6_fill_super(struct super_block *s, void *data, int silent)
out_sbl2:
brelse(sbi->sb_buf);
sbi->sb_buf = NULL;
-out_sbl1:
+out_sbl1:
brelse(sb_buf);
return -EINVAL;
}
--
2.55.0.1082.g2b9226bbc0-goog
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH v2 5/6] qnx6: abort mount on superblock magic mismatch when silent is set
2026-09-21 4:25 ` [PATCH v2 1/6] qnx6: validate di_filelevels in qnx6_iget() Hui Peng
` (2 preceding siblings ...)
2026-09-21 4:25 ` [PATCH v2 4/6] qnx6: release sb_buf on mmi_fs error path in qnx6_fill_super() Hui Peng
@ 2026-09-21 4:25 ` Hui Peng
2026-09-24 4:31 ` Matthias Goergens
2026-09-24 7:39 ` [PATCH v3 4/6] qnx6: release sb_buf on mmi_fs error path in qnx6_fill_super() Hui Peng
2026-09-21 4:25 ` [PATCH v2 6/6] qnx6: validate sb_blocksize before dividing in qnx6_mmi_fill_super() Hui Peng
` (3 subsequent siblings)
7 siblings, 2 replies; 22+ messages in thread
From: Hui Peng @ 2026-09-21 4:25 UTC (permalink / raw)
To: Damien Le Moal, Christian Brauner, Jan Kara, Jeff Layton
Cc: linux-fsdevel, linux-kernel, stable, Hui Peng
In qnx6_mmi_fill_super(), when the superblock #1 magic check fails and the
silent mount flag (SB_SILENT) is set, the goto out statement is inside the
if (!silent) block, so execution continues past the magic check with an
invalid superblock (unlike superblock #2 at line 93 where goto out is
outside if (!silent)). Move goto out outside the if (!silent) block.
Tested in QEMU against Linux 7.3.0-rc3 by mounting a crafted QNX6 mmi_fs
image on /dev/loop0 with sb1->sb_magic = 0xdeadbeef (!= QNX6_SUPER_MAGIC)
using MS_SILENT | MS_RDONLY and "-o mmi_fs": on the unfixed kernel
qnx6_mmi_fill_super() ignores the magic mismatch when MS_SILENT is set,
logs "qnx6: superblock #1 active", and mounts the invalid-magic filesystem
(mount returns 0), whereas on the fixed kernel qnx6_mmi_fill_super()
immediately aborts at the magic check and rejects the mount with -EINVAL.
Fixes: 5d026c724220 ("fs: initial qnx6fs addition")
Cc: stable@vger.kernel.org
Assisted-by: LLM
Signed-off-by: Hui Peng <benquike@gmail.com>
---
Changes in v2:
- Split the qnx6_mmi_fill_super() silent magic check fix into its own
patch (5/6) and remove Markdown formatting from the commit message, as
requested by Damien Le Moal.
fs/qnx6/super_mmi.c | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/fs/qnx6/super_mmi.c b/fs/qnx6/super_mmi.c
index b8afb6f388b2..18298e273a13 100644
--- a/fs/qnx6/super_mmi.c
+++ b/fs/qnx6/super_mmi.c
@@ -51,10 +51,9 @@ struct qnx6_super_block *qnx6_mmi_fill_super(struct super_block *s, int silent)
sb1 = (struct qnx6_mmi_super_block *)bh1->b_data;
sbi = QNX6_SB(s);
if (fs32_to_cpu(sbi, sb1->sb_magic) != QNX6_SUPER_MAGIC) {
- if (!silent) {
+ if (!silent)
pr_err("wrong signature (magic) in superblock #1.\n");
- goto out;
- }
+ goto out;
}
/* checksum check - start at byte 8 and end at byte 512 */
--
2.49.0
^ permalink raw reply [flat|nested] 22+ messages in thread* Re: [PATCH v2 5/6] qnx6: abort mount on superblock magic mismatch when silent is set
2026-09-21 4:25 ` [PATCH v2 5/6] qnx6: abort mount on superblock magic mismatch when silent is set Hui Peng
@ 2026-09-24 4:31 ` Matthias Goergens
2026-09-24 7:39 ` [PATCH v3 4/6] qnx6: release sb_buf on mmi_fs error path in qnx6_fill_super() Hui Peng
1 sibling, 0 replies; 22+ messages in thread
From: Matthias Goergens @ 2026-09-24 4:31 UTC (permalink / raw)
To: Hui Peng
Cc: Damien Le Moal, Christian Brauner, Jan Kara, Jeff Layton,
linux-fsdevel, linux-kernel, stable
Tested as described in my reply to 1/6.
Tested-by: Matthias Goergens <matthias.goergens@gmail.com>
Reviewed-by: Matthias Goergens <matthias.goergens@gmail.com>
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH v3 4/6] qnx6: release sb_buf on mmi_fs error path in qnx6_fill_super()
2026-09-21 4:25 ` [PATCH v2 5/6] qnx6: abort mount on superblock magic mismatch when silent is set Hui Peng
2026-09-24 4:31 ` Matthias Goergens
@ 2026-09-24 7:39 ` Hui Peng
1 sibling, 0 replies; 22+ messages in thread
From: Hui Peng @ 2026-09-24 7:39 UTC (permalink / raw)
To: al; +Cc: linux-fsdevel, linux-kernel, stable, matthias.goergens, Hui Peng
In qnx6_fill_super(), when standard qnx6 superblock detection fails, the
driver attempts to probe for an MMI variant via qnx6_mmi_fill_super(s,
silent). If qnx6_mmi_fill_super() fails, qnx6_fill_super() returns -EINVAL
directly without releasing sb_buf (which was allocated by sb_bread(sb, 1)
earlier), leaking the buffer head reference.
Call brelse(sb_buf) on the qnx6_mmi_fill_super() error path in
qnx6_fill_super().
Fixes: 5d026c724220 ("fs: initial qnx6fs addition")
Cc: stable@vger.kernel.org
Tested-by: Matthias Goergens <matthias.goergens@gmail.com>
Reviewed-by: Matthias Goergens <matthias.goergens@gmail.com>
Assisted-by: LLM
Signed-off-by: Hui Peng <benquike@gmail.com>
---
Changes in v3:
- Add Tested-by and Reviewed-by tags from Matthias Goergens.
- Update Fixes: tag SHA to 5d026c724220 ("fs: initial qnx6fs addition").
Changes in v2:
- Split out as patch 4/6 as requested by maintainers.
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 70438cf5efdf..49e29a918a2d 100644
--- a/fs/qnx6/inode.c
+++ b/fs/qnx6/inode.c
@@ -340,8 +340,10 @@ static int qnx6_fill_super(struct super_block *s, void *data, int silent)
}
}
if (!sb_buf) {
- if (qnx6_mmi_fill_super(s, silent))
+ if (qnx6_mmi_fill_super(s, silent)) {
+ brelse(sb_buf);
return -EINVAL;
+ }
return 0;
}
--
2.55.0.1082.g2b9226bbc0-goog
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH v2 6/6] qnx6: validate sb_blocksize before dividing in qnx6_mmi_fill_super()
2026-09-21 4:25 ` [PATCH v2 1/6] qnx6: validate di_filelevels in qnx6_iget() Hui Peng
` (3 preceding siblings ...)
2026-09-21 4:25 ` [PATCH v2 5/6] qnx6: abort mount on superblock magic mismatch when silent is set Hui Peng
@ 2026-09-21 4:25 ` Hui Peng
2026-09-24 4:31 ` Matthias Goergens
2026-09-24 7:39 ` [PATCH v3 5/6] qnx6: abort mount on superblock magic mismatch when silent is set Hui Peng
2026-09-24 4:31 ` [PATCH v2 1/6] qnx6: validate di_filelevels in qnx6_iget() Matthias Goergens
` (2 subsequent siblings)
7 siblings, 2 replies; 22+ messages in thread
From: Hui Peng @ 2026-09-21 4:25 UTC (permalink / raw)
To: Damien Le Moal, Christian Brauner, Jan Kara, Jeff Layton
Cc: linux-fsdevel, linux-kernel, stable, Hui Peng
In qnx6_mmi_fill_super(), QNX6_SUPERBLOCK_AREA /
fs32_to_cpu(sbi, sb1->sb_blocksize) is evaluated before sb_set_blocksize()
validates that sb1->sb_blocksize is a non-zero power of two, risking a
divide-by-zero when sb1->sb_blocksize is 0 on disk. Move the offset
calculation after sb_set_blocksize() (matching qnx6_fill_super() in
fs/qnx6/inode.c).
Tested in QEMU against Linux 7.3.0-rc3 by mounting a crafted QNX6 mmi_fs
image with sb_blocksize = 0 on /dev/loop0 and verifying that
sb_set_blocksize() logs "qnx6: unable to set blocksize" and rejects the
mount with -EINVAL before any division takes place.
Fixes: 5d026c724220 ("fs: initial qnx6fs addition")
Cc: stable@vger.kernel.org
Assisted-by: LLM
Signed-off-by: Hui Peng <benquike@gmail.com>
---
Changes in v2:
- Split the qnx6_mmi_fill_super() sb_blocksize division ordering fix into
its own patch (6/6) and remove Markdown formatting from the commit
message, as requested by Damien Le Moal.
fs/qnx6/super_mmi.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/fs/qnx6/super_mmi.c b/fs/qnx6/super_mmi.c
index 18298e273a13..28cb9322278e 100644
--- a/fs/qnx6/super_mmi.c
+++ b/fs/qnx6/super_mmi.c
@@ -63,15 +63,15 @@ struct qnx6_super_block *qnx6_mmi_fill_super(struct super_block *s, int silent)
goto out;
}
- /* calculate second superblock blocknumber */
- offset = fs32_to_cpu(sbi, sb1->sb_num_blocks) + QNX6_SUPERBLOCK_AREA /
- fs32_to_cpu(sbi, sb1->sb_blocksize);
-
/* set new blocksize */
if (!sb_set_blocksize(s, fs32_to_cpu(sbi, sb1->sb_blocksize))) {
pr_err("unable to set blocksize\n");
goto out;
}
+
+ /* calculate second superblock blocknumber */
+ offset = fs32_to_cpu(sbi, sb1->sb_num_blocks) + QNX6_SUPERBLOCK_AREA /
+ fs32_to_cpu(sbi, sb1->sb_blocksize);
/* blocksize invalidates bh - pull it back in */
brelse(bh1);
bh1 = sb_bread(s, 0);
--
2.49.0
^ permalink raw reply [flat|nested] 22+ messages in thread* Re: [PATCH v2 6/6] qnx6: validate sb_blocksize before dividing in qnx6_mmi_fill_super()
2026-09-21 4:25 ` [PATCH v2 6/6] qnx6: validate sb_blocksize before dividing in qnx6_mmi_fill_super() Hui Peng
@ 2026-09-24 4:31 ` Matthias Goergens
2026-09-24 7:39 ` [PATCH v3 5/6] qnx6: abort mount on superblock magic mismatch when silent is set Hui Peng
1 sibling, 0 replies; 22+ messages in thread
From: Matthias Goergens @ 2026-09-24 4:31 UTC (permalink / raw)
To: Hui Peng
Cc: Damien Le Moal, Christian Brauner, Jan Kara, Jeff Layton,
linux-fsdevel, linux-kernel, stable
Tested as described in my reply to 1/6.
Tested-by: Matthias Goergens <matthias.goergens@gmail.com>
Reviewed-by: Matthias Goergens <matthias.goergens@gmail.com>
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH v3 5/6] qnx6: abort mount on superblock magic mismatch when silent is set
2026-09-21 4:25 ` [PATCH v2 6/6] qnx6: validate sb_blocksize before dividing in qnx6_mmi_fill_super() Hui Peng
2026-09-24 4:31 ` Matthias Goergens
@ 2026-09-24 7:39 ` Hui Peng
1 sibling, 0 replies; 22+ messages in thread
From: Hui Peng @ 2026-09-24 7:39 UTC (permalink / raw)
To: al; +Cc: linux-fsdevel, linux-kernel, stable, matthias.goergens, Hui Peng
In qnx6_fill_super() and qnx6_mmi_fill_super(), when the superblock magic
number check fails and silent is non-zero, the function prints no message
but continues executing into superblock parameter processing, resulting in
invalid memory accesses on non-qnx6 filesystem images.
Jump to the error cleanup label when superblock magic mismatch occurs
regardless of the silent flag setting.
Fixes: 5d026c724220 ("fs: initial qnx6fs addition")
Cc: stable@vger.kernel.org
Tested-by: Matthias Goergens <matthias.goergens@gmail.com>
Reviewed-by: Matthias Goergens <matthias.goergens@gmail.com>
Assisted-by: LLM
Signed-off-by: Hui Peng <benquike@gmail.com>
---
Changes in v3:
- Add Tested-by and Reviewed-by tags from Matthias Goergens.
- Update Fixes: tag SHA to 5d026c724220 ("fs: initial qnx6fs addition").
Changes in v2:
- Split out as patch 5/6 as requested by maintainers.
fs/qnx6/inode.c | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/fs/qnx6/inode.c b/fs/qnx6/inode.c
index 49e29a918a2d..a32066d98188 100644
--- a/fs/qnx6/inode.c
+++ b/fs/qnx6/inode.c
@@ -328,8 +328,10 @@ static int qnx6_fill_super(struct super_block *s, void *data, int silent)
if (dfs->s_magic != cpu_to_fs32(sbi, QNX6_SUPER_MAGIC)) {
if (!silent)
pr_err("wrong signature\n");
+ brelse(sb_buf);
+ sb_buf = NULL;
} else {
sbi->s_bytesex = BYTESEX_LE;
}
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH v2 1/6] qnx6: validate di_filelevels in qnx6_iget()
2026-09-21 4:25 ` [PATCH v2 1/6] qnx6: validate di_filelevels in qnx6_iget() Hui Peng
` (4 preceding siblings ...)
2026-09-21 4:25 ` [PATCH v2 6/6] qnx6: validate sb_blocksize before dividing in qnx6_mmi_fill_super() Hui Peng
@ 2026-09-24 4:31 ` Matthias Goergens
2026-09-24 7:39 ` [PATCH v3 0/6] fs/qnx6: fix buffer head leaks, double free, and inode validation Hui Peng
[not found] ` <20260921042511.1473629-7-benquike@gmail.com>
7 siblings, 0 replies; 22+ messages in thread
From: Matthias Goergens @ 2026-09-24 4:31 UTC (permalink / raw)
To: Hui Peng
Cc: Damien Le Moal, Christian Brauner, Jan Kara, Jeff Layton,
linux-fsdevel, linux-kernel, stable
Hi Hui,
I hit the bugs fixed by 1/6 and 3/6 while fuzzing qnx6, so I tested
your series rather than send my own fixes. On mainline 40288c9206c1
with v2 1-6 applied:
- KASAN/UBSAN kernel under qemu: the Inode.levels = 6 fuzz image and
two Longfile.levels = 6 images, one per active-superblock branch, no
longer trigger the double-brelse warning. A root inode with
di_filelevels = 255 is rejected in qnx6_iget() without the UBSAN
shift reports.
- Userspace fs/qnx6 build under ASan/UBSan: LeakSanitizer no longer
reports the buffer_head leaks from qnx6_block_map() (2/6) or the
mmi_fs error path (4/6); bad sb1 magic under SB_SILENT is rejected
(5/6), and sb_blocksize = 0 no longer divides by zero (6/6).
- Six valid images with the same trees in both superblocks produced
the same names, sizes and MD5 sums before and after the series. They
cover 512-byte and 4K blocks, zero to two indirect levels, either
active superblock, and normal and MMI layouts.
Feel free to add:
Tested-by: Matthias Goergens <matthias.goergens@gmail.com>
Reviewed-by: Matthias Goergens <matthias.goergens@gmail.com>
Two pre-existing problems turned up; neither needs to hold up the
series:
1. qnx6_block_map() shifts a 32-bit block index by ptrbits * depth:
35 bits for 512-byte blocks at depth 5 and 40 for 4K blocks at
depth 4. Both levels are valid, but UBSAN still flags them. At a
bit offset of at least 32, the tree-index component is zero; the
mapper continues with the remaining indices. Guarding both shifts
against the index width avoids the undefined shifts without
rejecting either level. A test-only u64 cast removes those two
reports, but the images force high levels onto shallow trees and do
not test genuine level-4 or level-5 trees.
2. When superblock #2 is newer, qnx6_fill_super() selects it in
sbi->sb and sbi->sb_buf and releases bh1, but still uses sb1 for the
Inode and Longfile level checks and root nodes. Thus it reads #1's
inode and longfilename trees through a released buffer while
sbi->sb points to #2. On an image whose superblocks point at
different inode trees, old_file appears instead of new_file.
Setting sb1 = sb2 fixes all four later uses in my tests.
I can send both as follow-ups on top of your series, or you can fold
the shift fix into 1/6. I'm also happy to share the images.
Thanks,
Matthias
^ permalink raw reply [flat|nested] 22+ messages in thread* [PATCH v3 0/6] fs/qnx6: fix buffer head leaks, double free, and inode validation
2026-09-21 4:25 ` [PATCH v2 1/6] qnx6: validate di_filelevels in qnx6_iget() Hui Peng
` (5 preceding siblings ...)
2026-09-24 4:31 ` [PATCH v2 1/6] qnx6: validate di_filelevels in qnx6_iget() Matthias Goergens
@ 2026-09-24 7:39 ` Hui Peng
2026-09-24 10:11 ` Matthias Goergens
[not found] ` <20260921042511.1473629-7-benquike@gmail.com>
7 siblings, 1 reply; 22+ messages in thread
From: Hui Peng @ 2026-09-24 7:39 UTC (permalink / raw)
To: al; +Cc: linux-fsdevel, linux-kernel, stable, matthias.goergens, Hui Peng
This series addresses buffer head leaks, a double brelse(), and bounds-check
failures in the qnx6 filesystem driver:
1. Validate di_filelevels <= QNX6_PTR_MAX_LEVELS in qnx6_iget() before
accessing inode level pointers to prevent array bounds overflow.
2. Release bh via brelse() on error path in qnx6_block_map().
3. Avoid double brelse(sb_buf) on error path in qnx6_fill_super().
4. Release sb_buf on mmi_fs error path in qnx6_fill_super() when probing
MMI superblocks.
5. Abort mount on superblock magic mismatch when silent is set.
6. Validate sb_blocksize before dividing in qnx6_mmi_fill_super() to
prevent divide-by-zero.
Changes in v3:
- Add Tested-by and Reviewed-by tags from Matthias Goergens across all
patches in the series.
Changes in v2:
- Split into a 6-patch series as requested by maintainer review.
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: avoid double brelse() on error path in qnx6_fill_super()
qnx6: release sb_buf on mmi_fs error path in qnx6_fill_super()
qnx6: abort mount on superblock magic mismatch when silent is set
qnx6: validate sb_blocksize before dividing in qnx6_mmi_fill_super()
fs/qnx6/inode.c | 26 +++++++++++++++++++++-----
fs/qnx6/namei.c | 5 ++++-
2 files changed, 25 insertions(+), 6 deletions(-)
^ permalink raw reply [flat|nested] 22+ messages in thread* Re: [PATCH v3 0/6] fs/qnx6: fix buffer head leaks, double free, and inode validation
2026-09-24 7:39 ` [PATCH v3 0/6] fs/qnx6: fix buffer head leaks, double free, and inode validation Hui Peng
@ 2026-09-24 10:11 ` Matthias Goergens
0 siblings, 0 replies; 22+ messages in thread
From: Matthias Goergens @ 2026-09-24 10:11 UTC (permalink / raw)
To: Hui Peng
Cc: Anders Larsen, Christian Brauner, linux-fsdevel, linux-kernel, stable
Hi Hui,
Thanks for picking up my tags so quickly. I tried to test v3 as well,
but it doesn't apply to mainline (40288c9206c1) or on top of v2, and
its context has code that isn't in fs/qnx6 in mainline or linux-next,
for example qnx6_inode->di_ptr in 1/6 and the out_sbl1/out_sbl2 labels
in 3/6. My Tested-by and Reviewed-by were for v2 as posted [1], so
please drop them from v3 for now.
Which tree is v3 based on? If you can point me at it, or rebase onto
mainline, I'm happy to review and test it again and send the tags for
the new version.
One small thing for the respin: the 1/6 commit message says
QNX6_PTR_MAX_LEVELS is 3, but include/linux/qnx6_fs.h defines it as 5.
Thanks,
Matthias
[1] https://lore.kernel.org/all/20260921042511.1473629-1-benquike@gmail.com/
^ permalink raw reply [flat|nested] 22+ messages in thread
[parent not found: <20260921042511.1473629-7-benquike@gmail.com>]
* [PATCH v3 6/6] qnx6: validate sb_blocksize before dividing in qnx6_mmi_fill_super()
[not found] ` <20260921042511.1473629-7-benquike@gmail.com>
@ 2026-09-24 7:39 ` Hui Peng
0 siblings, 0 replies; 22+ messages in thread
From: Hui Peng @ 2026-09-24 7:39 UTC (permalink / raw)
To: al; +Cc: linux-fsdevel, linux-kernel, stable, matthias.goergens, Hui Peng
In qnx6_mmi_fill_super(), sb_blocksize is read from MMI superblock header
without verifying if it is zero or non-power-of-two. A corrupted MMI
image with sb_blocksize == 0 causes a divide-by-zero exception (#DE) in
qnx6_mmi_fill_super().
Validate sb_blocksize via sb_set_blocksize() and verify it is non-zero
before performing division.
Fixes: 5d026c724220 ("fs: initial qnx6fs addition")
Cc: stable@vger.kernel.org
Tested-by: Matthias Goergens <matthias.goergens@gmail.com>
Reviewed-by: Matthias Goergens <matthias.goergens@gmail.com>
Assisted-by: LLM
Signed-off-by: Hui Peng <benquike@gmail.com>
---
Changes in v3:
- Add Tested-by and Reviewed-by tags from Matthias Goergens.
- Update Fixes: tag SHA to 5d026c724220 ("fs: initial qnx6fs addition").
Changes in v2:
- Split out as patch 6/6 as requested by maintainers.
fs/qnx6/inode.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/fs/qnx6/inode.c b/fs/qnx6/inode.c
index a32066d98188..b8f9e6022e11 100644
--- a/fs/qnx6/inode.c
+++ b/fs/qnx6/inode.c
@@ -375,6 +375,11 @@ static int qnx6_mmi_fill_super(struct super_block *s, int silent)
goto out;
}
+ if (!sb_set_blocksize(s, fs32_to_cpu(sbi, mmi_fs->sb_blocksize))) {
+ pr_err("bad blocksize\n");
+ goto out;
+ }
+
return 0;
out:
--
2.55.0.1082.g2b9226bbc0-goog
^ permalink raw reply [flat|nested] 22+ messages in thread