[PATCH] ocfs2: validate truncate log dinode before caching
From: ZhengYuan Huang
Date: Wed Jul 22 2026 - 05:14:28 EST
[BUG]
A corrupted truncate log dinode can pass through mount initialization and
reach the delayed flush worker, where it triggers:
kernel BUG at fs/ocfs2/alloc.c:6019!
Oops: invalid opcode: 0000 [#1] SMP KASAN NOPTI
RIP: 0010:__ocfs2_flush_truncate_log+0xa87/0xf10 fs/ocfs2/alloc.c:6019
Call Trace:
ocfs2_flush_truncate_log fs/ocfs2/alloc.c:6084 [inline]
ocfs2_truncate_log_worker+0xa9/0x180 fs/ocfs2/alloc.c:6097
process_one_work+0x8e0/0x1980 kernel/workqueue.c:3263
...
[CAUSE]
ocfs2_get_truncate_log_info() assumes that ocfs2_read_inode_block()
always validates the returned dinode. However, ocfs2_read_blocks() skips
the validation callback for JBD-managed buffers. The function then reads
truncate log fields and exposes the invalid buffer to callers, allowing
ocfs2_truncate_log_init() to retain it in osb->osb_tl_bh.
[FIX]
Check the dinode signature in ocfs2_get_truncate_log_info() immediately
after the inode read. Reject an invalid dinode as filesystem corruption
before reading truncate log fields or returning the inode and buffer to
the caller. This keeps invalid state out of the long-lived truncate log
cache and preserves the validated-buffer invariant in append, flush, and
recovery paths.
Fixes: 10995aa2451a ("ocfs2: Morph the haphazard OCFS2_IS_VALID_DINODE() checks.")
Assisted-by: Codex:gpt-5.6-sol
Signed-off-by: ZhengYuan Huang <gality369@xxxxxxxxx>
---
fs/ocfs2/alloc.c | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/fs/ocfs2/alloc.c b/fs/ocfs2/alloc.c
index be09e766ac1f..e36ae2e522d3 100644
--- a/fs/ocfs2/alloc.c
+++ b/fs/ocfs2/alloc.c
@@ -6192,6 +6192,19 @@ static int ocfs2_get_truncate_log_info(struct ocfs2_super *osb,
}
di = (struct ocfs2_dinode *)bh->b_data;
+ /*
+ * A JBD-managed buffer may skip the read validation callback. Check
+ * the signature before exposing the truncate log to callers.
+ */
+ if (!OCFS2_IS_VALID_DINODE(di)) {
+ status = ocfs2_error(osb->sb,
+ "Invalid truncate log dinode #%llu\n",
+ (unsigned long long)bh->b_blocknr);
+ iput(inode);
+ brelse(bh);
+ goto bail;
+ }
+
tl = &di->id2.i_dealloc;
tl_count = le16_to_cpu(tl->tl_count);
tl_used = le16_to_cpu(tl->tl_used);
--
2.43.0