public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/3] ocfs2: stop BUG_ON crashes in suballoc invalid-dinode paths
@ 2026-04-03  6:30 ZhengYuan Huang
  2026-04-03  6:30 ` [PATCH 1/3] ocfs2: handle invalid dinode in reserve_suballoc_bits ZhengYuan Huang
                   ` (3 more replies)
  0 siblings, 4 replies; 5+ messages in thread
From: ZhengYuan Huang @ 2026-04-03  6:30 UTC (permalink / raw)
  To: mark, jlbec, joseph.qi
  Cc: ocfs2-devel, linux-kernel, baijiaju1990, r33s3n6, zzzccc427,
	ZhengYuan Huang

commit 10995aa2451a ("ocfs2: Morph the haphazard
OCFS2_IS_VALID_DINODE() checks.") converted several OCFS2 dinode
corruption checks from graceful error handling to BUG_ON() under the
assumption that every caller only sees validated inode buffers.

That assumption does not always hold for JBD-managed buffers. The common
inode read path can still hand suballoc code an invalid dinode, which turns
crafted filesystem corruption into a kernel panic instead of a normal OCFS2
filesystem error.

This series restores graceful corruption handling at the three
independently reachable BUG_ON() sites in fs/ocfs2/suballoc.c:

1. reserve_suballoc_bits()
2. claim_suballoc_bits()
3. _ocfs2_free_suballoc_bits()

The series is split per crash site so each patch fixes one bug. A broader
follow-up could harden structural validation for JBD-managed inode reads,
but that change touches a much wider read-side contract and is kept out of
scope here.

ZhengYuan Huang (3):
  ocfs2: handle invalid dinode in reserve_suballoc_bits
  ocfs2: handle invalid dinode in claim_suballoc_bits
  ocfs2: handle invalid dinode in _ocfs2_free_suballoc_bits

 fs/ocfs2/suballoc.c | 33 +++++++++++++++++++++------------
 1 file changed, 21 insertions(+), 12 deletions(-)

-- 
2.43.0


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

* [PATCH 1/3] ocfs2: handle invalid dinode in reserve_suballoc_bits
  2026-04-03  6:30 [PATCH 0/3] ocfs2: stop BUG_ON crashes in suballoc invalid-dinode paths ZhengYuan Huang
@ 2026-04-03  6:30 ` ZhengYuan Huang
  2026-04-03  6:30 ` [PATCH 2/3] ocfs2: handle invalid dinode in claim_suballoc_bits ZhengYuan Huang
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 5+ messages in thread
From: ZhengYuan Huang @ 2026-04-03  6:30 UTC (permalink / raw)
  To: mark, jlbec, joseph.qi
  Cc: ocfs2-devel, linux-kernel, baijiaju1990, r33s3n6, zzzccc427,
	ZhengYuan Huang

[BUG]
A crafted filesystem can feed an invalid dinode into
ocfs2_reserve_suballoc_bits() and trip:

kernel BUG at fs/ocfs2/suballoc.c:806!
Oops: invalid opcode: 0000 [#1] SMP KASAN NOPTI
RIP: 0010:ocfs2_reserve_suballoc_bits+0xccd/0x3e00 fs/ocfs2/suballoc.c:806
Code: c0fe488b 9d58ffff ff4885db 740de8fa
Call Trace:
 <TASK>
 ocfs2_reserve_cluster_bitmap_bits+0xe5/0x1c0 fs/ocfs2/suballoc.c:1134
 ocfs2_local_alloc_reserve_for_window fs/ocfs2/localalloc.c:1108 [inline]
 ocfs2_local_alloc_slide_window+0x2cb/0x1570 fs/ocfs2/localalloc.c:1244
 ocfs2_reserve_local_alloc_bits+0x654/0xa10 fs/ocfs2/localalloc.c:669
 ocfs2_reserve_clusters_with_limit+0x785/0xe40 fs/ocfs2/suballoc.c:1168
 ocfs2_reserve_clusters fs/ocfs2/suballoc.c:1229 [inline]
 ocfs2_lock_allocators+0x319/0x520 fs/ocfs2/suballoc.c:2772
 ocfs2_write_begin_nolock+0x256a/0x5f30 fs/ocfs2/aops.c:1719
 ocfs2_write_begin+0x1b6/0x2e0 fs/ocfs2/aops.c:1884
 generic_perform_write+0x409/0x8c0 mm/filemap.c:4255
 __generic_file_write_iter+0x1bb/0x200 mm/filemap.c:4372
 ocfs2_file_write_iter+0xa87/0x1e10 fs/ocfs2/file.c:2469
 do_iter_readv_writev+0x61d/0x850 fs/read_write.c:827
 vfs_writev+0x323/0xca0 fs/read_write.c:1057
 do_pwritev+0x193/0x250 fs/read_write.c:1153
 __do_sys_pwritev2 fs/read_write.c:1211 [inline]
 __se_sys_pwritev2 fs/read_write.c:1202 [inline]
 __x64_sys_pwritev2+0xe8/0x160 fs/read_write.c:1202
 ...

[CAUSE]
ocfs2_reserve_suballoc_bits() assumes ocfs2_inode_lock() always returns
an already validated dinode buffer. commit 10995aa2451a ("ocfs2: Morph
the haphazard OCFS2_IS_VALID_DINODE() checks.") replaced the old
corruption handling with BUG_ON() under that assumption. However,
JBD-managed buffers can still bypass inode validation in the read path,
so corrupted dinode data can reach this function.

[FIX]
Treat an invalid dinode as filesystem corruption and return through the
existing bail-out path instead of BUG()ing. This matches the nearby
OCFS2_CHAIN_FL handling and keeps allocator cleanup unchanged.

Fixes: 10995aa2451a ("ocfs2: Morph the haphazard OCFS2_IS_VALID_DINODE() checks.")
Signed-off-by: ZhengYuan Huang <gality369@gmail.com>
 fs/ocfs2/suballoc.c | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/fs/ocfs2/suballoc.c b/fs/ocfs2/suballoc.c
index 6ac4dcd54588..12ac2bb3f10b 100644
--- a/fs/ocfs2/suballoc.c
+++ b/fs/ocfs2/suballoc.c
@@ -801,9 +801,13 @@ static int ocfs2_reserve_suballoc_bits(struct ocfs2_super *osb,
 
 	fe = (struct ocfs2_dinode *) bh->b_data;
 
-	/* The bh was validated by the inode read inside
-	 * ocfs2_inode_lock().  Any corruption is a code bug. */
-	BUG_ON(!OCFS2_IS_VALID_DINODE(fe));
+	/* JBD-managed buffers can bypass inode validation. */
+	if (!OCFS2_IS_VALID_DINODE(fe)) {
+		status = ocfs2_error(alloc_inode->i_sb,
+				     "Invalid dinode #%llu\n",
+				     (unsigned long long)OCFS2_I(alloc_inode)->ip_blkno);
+		goto bail;
+	}
 
 	if (!(fe->i_flags & cpu_to_le32(OCFS2_CHAIN_FL))) {
 		status = ocfs2_error(alloc_inode->i_sb,
-- 
2.43.0


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

* [PATCH 2/3] ocfs2: handle invalid dinode in claim_suballoc_bits
  2026-04-03  6:30 [PATCH 0/3] ocfs2: stop BUG_ON crashes in suballoc invalid-dinode paths ZhengYuan Huang
  2026-04-03  6:30 ` [PATCH 1/3] ocfs2: handle invalid dinode in reserve_suballoc_bits ZhengYuan Huang
@ 2026-04-03  6:30 ` ZhengYuan Huang
  2026-04-03  6:30 ` [PATCH 3/3] ocfs2: handle invalid dinode in _ocfs2_free_suballoc_bits ZhengYuan Huang
  2026-04-03  9:30 ` [PATCH 0/3] ocfs2: stop BUG_ON crashes in suballoc invalid-dinode paths Joseph Qi
  3 siblings, 0 replies; 5+ messages in thread
From: ZhengYuan Huang @ 2026-04-03  6:30 UTC (permalink / raw)
  To: mark, jlbec, joseph.qi
  Cc: ocfs2-devel, linux-kernel, baijiaju1990, r33s3n6, zzzccc427,
	ZhengYuan Huang

[BUG]
A crafted filesystem can feed an invalid dinode into
ocfs2_claim_suballoc_bits() and trip:

  kernel BUG at fs/ocfs2/suballoc.c:1966

[CAUSE]
ocfs2_claim_suballoc_bits() trusts ac->ac_bh, but that buffer is not
limited to the reserve_suballoc_bits() path: local allocation can also
hand in osb->local_alloc_bh directly. JBD-managed buffers can bypass
inode validation, so invalid dinode data can still reach this function.

[FIX]
Report an invalid dinode as filesystem corruption and unwind through the
existing bail path instead of BUG()ing. This keeps the allocation logic
unchanged while removing the fatal failure mode.

Fixes: 10995aa2451a ("ocfs2: Morph the haphazard OCFS2_IS_VALID_DINODE() checks.")
Signed-off-by: ZhengYuan Huang <gality369@gmail.com>
---
 fs/ocfs2/suballoc.c | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/fs/ocfs2/suballoc.c b/fs/ocfs2/suballoc.c
index 12ac2bb3f10b..b99870aeaf88 100644
--- a/fs/ocfs2/suballoc.c
+++ b/fs/ocfs2/suballoc.c
@@ -1965,9 +1965,13 @@ static int ocfs2_claim_suballoc_bits(struct ocfs2_alloc_context *ac,
 
 	fe = (struct ocfs2_dinode *) ac->ac_bh->b_data;
 
-	/* The bh was validated by the inode read during
-	 * ocfs2_reserve_suballoc_bits().  Any corruption is a code bug. */
-	BUG_ON(!OCFS2_IS_VALID_DINODE(fe));
+	/* JBD-managed buffers can bypass inode validation. */
+	if (!OCFS2_IS_VALID_DINODE(fe)) {
+		status = ocfs2_error(ac->ac_inode->i_sb,
+				     "Invalid dinode #%llu\n",
+				     (unsigned long long)OCFS2_I(ac->ac_inode)->ip_blkno);
+		goto bail;
+	}
 
 	if (le32_to_cpu(fe->id1.bitmap1.i_used) >=
 	    le32_to_cpu(fe->id1.bitmap1.i_total)) {
-- 
2.43.0


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

* [PATCH 3/3] ocfs2: handle invalid dinode in _ocfs2_free_suballoc_bits
  2026-04-03  6:30 [PATCH 0/3] ocfs2: stop BUG_ON crashes in suballoc invalid-dinode paths ZhengYuan Huang
  2026-04-03  6:30 ` [PATCH 1/3] ocfs2: handle invalid dinode in reserve_suballoc_bits ZhengYuan Huang
  2026-04-03  6:30 ` [PATCH 2/3] ocfs2: handle invalid dinode in claim_suballoc_bits ZhengYuan Huang
@ 2026-04-03  6:30 ` ZhengYuan Huang
  2026-04-03  9:30 ` [PATCH 0/3] ocfs2: stop BUG_ON crashes in suballoc invalid-dinode paths Joseph Qi
  3 siblings, 0 replies; 5+ messages in thread
From: ZhengYuan Huang @ 2026-04-03  6:30 UTC (permalink / raw)
  To: mark, jlbec, joseph.qi
  Cc: ocfs2-devel, linux-kernel, baijiaju1990, r33s3n6, zzzccc427,
	ZhengYuan Huang

[BUG]
A crafted filesystem can feed an invalid dinode into
_ocfs2_free_suballoc_bits() and trip:

  kernel BUG at fs/ocfs2/suballoc.c:2568

[CAUSE]
The free path trusts alloc_bh returned from locked allocator reads, but
JBD-managed buffers can bypass inode validation before that buffer is
handed to _ocfs2_free_suballoc_bits().

[FIX]
Handle an invalid dinode as filesystem corruption and exit through the
existing bail path before touching any allocator accounting. This keeps
all cleanup and rollback logic intact while avoiding BUG().

Fixes: 10995aa2451a ("ocfs2: Morph the haphazard OCFS2_IS_VALID_DINODE() checks.")
Signed-off-by: ZhengYuan Huang <gality369@gmail.com>
---
 fs/ocfs2/suballoc.c | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/fs/ocfs2/suballoc.c b/fs/ocfs2/suballoc.c
index b99870aeaf88..34bdc18200f2 100644
--- a/fs/ocfs2/suballoc.c
+++ b/fs/ocfs2/suballoc.c
@@ -2868,13 +2868,14 @@ static int _ocfs2_free_suballoc_bits(handle_t *handle,
 	struct ocfs2_group_desc *group;
 	struct ocfs2_chain_rec *rec;
 	__le16 old_bg_contig_free_bits = 0;
 
-	/* The alloc_bh comes from ocfs2_free_dinode() or
-	 * ocfs2_free_clusters().  The callers have all locked the
-	 * allocator and gotten alloc_bh from the lock call.  This
-	 * validates the dinode buffer.  Any corruption that has happened
-	 * is a code bug. */
-	BUG_ON(!OCFS2_IS_VALID_DINODE(fe));
+	/* JBD-managed buffers can bypass inode validation. */
+	if (!OCFS2_IS_VALID_DINODE(fe)) {
+		status = ocfs2_error(alloc_inode->i_sb,
+				     "Invalid dinode #%llu\n",
+				     (unsigned long long)OCFS2_I(alloc_inode)->ip_blkno);
+		goto bail;
+	}
 	BUG_ON((count + start_bit) > ocfs2_bits_per_group(cl));
 
 	trace_ocfs2_free_suballoc_bits(
-- 
2.43.0


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

* Re: [PATCH 0/3] ocfs2: stop BUG_ON crashes in suballoc invalid-dinode paths
  2026-04-03  6:30 [PATCH 0/3] ocfs2: stop BUG_ON crashes in suballoc invalid-dinode paths ZhengYuan Huang
                   ` (2 preceding siblings ...)
  2026-04-03  6:30 ` [PATCH 3/3] ocfs2: handle invalid dinode in _ocfs2_free_suballoc_bits ZhengYuan Huang
@ 2026-04-03  9:30 ` Joseph Qi
  3 siblings, 0 replies; 5+ messages in thread
From: Joseph Qi @ 2026-04-03  9:30 UTC (permalink / raw)
  To: ZhengYuan Huang
  Cc: ocfs2-devel, linux-kernel, baijiaju1990, r33s3n6, zzzccc427,
	Mark Fasheh, Joel Becker, akpm



On 4/3/26 2:30 PM, ZhengYuan Huang wrote:
> commit 10995aa2451a ("ocfs2: Morph the haphazard
> OCFS2_IS_VALID_DINODE() checks.") converted several OCFS2 dinode
> corruption checks from graceful error handling to BUG_ON() under the
> assumption that every caller only sees validated inode buffers.
> 
> That assumption does not always hold for JBD-managed buffers. The common
> inode read path can still hand suballoc code an invalid dinode, which turns
> crafted filesystem corruption into a kernel panic instead of a normal OCFS2
> filesystem error.
> 

When inode first read from disk, it will call ocfs2_validate_inode_block()
to validate if it is valid.
So it seems this is a code bug once the buffer is modified? Or how it
happens?

Thanks,
Joseph

> This series restores graceful corruption handling at the three
> independently reachable BUG_ON() sites in fs/ocfs2/suballoc.c:
> 
> 1. reserve_suballoc_bits()
> 2. claim_suballoc_bits()
> 3. _ocfs2_free_suballoc_bits()
> 
> The series is split per crash site so each patch fixes one bug. A broader
> follow-up could harden structural validation for JBD-managed inode reads,
> but that change touches a much wider read-side contract and is kept out of
> scope here.
> 
> ZhengYuan Huang (3):
>   ocfs2: handle invalid dinode in reserve_suballoc_bits
>   ocfs2: handle invalid dinode in claim_suballoc_bits
>   ocfs2: handle invalid dinode in _ocfs2_free_suballoc_bits
> 
>  fs/ocfs2/suballoc.c | 33 +++++++++++++++++++++------------
>  1 file changed, 21 insertions(+), 12 deletions(-)
> 


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

end of thread, other threads:[~2026-04-03  9:30 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-03  6:30 [PATCH 0/3] ocfs2: stop BUG_ON crashes in suballoc invalid-dinode paths ZhengYuan Huang
2026-04-03  6:30 ` [PATCH 1/3] ocfs2: handle invalid dinode in reserve_suballoc_bits ZhengYuan Huang
2026-04-03  6:30 ` [PATCH 2/3] ocfs2: handle invalid dinode in claim_suballoc_bits ZhengYuan Huang
2026-04-03  6:30 ` [PATCH 3/3] ocfs2: handle invalid dinode in _ocfs2_free_suballoc_bits ZhengYuan Huang
2026-04-03  9:30 ` [PATCH 0/3] ocfs2: stop BUG_ON crashes in suballoc invalid-dinode paths Joseph Qi

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