Re: [BUG] ocfs2: NULL osb->journal deref via ocfs2_checkpoint_inode on unmount

From: Joseph Qi

Date: Fri May 29 2026 - 06:22:18 EST




On 5/29/26 1:39 PM, Farhad Alemi wrote:
> Hello Joseph, Mark, Joel, and the ocfs2 team,
>
> I am reporting a KASAN null-ptr-deref GPF in the ocfs2 unmount path
> found by syzkaller as part of research at the SEFCOM Lab at ASU.
>
> Summary:
> During unmount, ocfs2_clear_inode() calls ocfs2_checkpoint_inode()
> without checking that osb->journal is still live. At
> fs/ocfs2/inode.c:1223-1224:
>
> if (!(oi->ip_flags & OCFS2_INODE_DELETED))
> ocfs2_checkpoint_inode(inode);
>
> ocfs2_checkpoint_inode() (fs/ocfs2/journal.h:192) reaches
> ocfs2_ci_fully_checkpointed() (fs/ocfs2/journal.h:91), which loads
> osb->journal and dereferences it:
>
> struct ocfs2_journal *journal =
> OCFS2_SB(ocfs2_metadata_cache_get_super(ci))->journal;
> spin_lock(&trans_inc_lock);
> ret = time_after(journal->j_trans_id, ci->ci_last_trans); <-- :98
>
> On the unmount-side slot-info eviction, ocfs2_journal_shutdown() has
> already set osb->journal = NULL (fs/ocfs2/journal.c:1130) before
> ocfs2_delete_osb() evicts the slot inodes, so the journal->j_trans_id
> load at journal.h:98 faults. The KASAN range is [0x70, 0x77] (see
> crash-report.txt); the RIP chain is:
>
> ocfs2_ci_fully_checkpointed fs/ocfs2/journal.h:98 [inline]
> ocfs2_checkpoint_inode fs/ocfs2/journal.h:199 [inline]
> ocfs2_clear_inode fs/ocfs2/inode.c:1224 [inline]
> ocfs2_evict_inode+0x244c/0x43c0 fs/ocfs2/inode.c:1303
>
> Observed on:
> - Linux v7.1-rc3-200-g70eda68668d1-dirty,
> x86_64, QEMU Q35
> - KASAN enabled
> - The only local dirty file in my tree is
> drivers/tty/serial/serial_core.c, a ttyS0 console guard for the
> fuzzing harness, unrelated to fs/ocfs2/.
> - Source inspection of linus/master at commit e8c2f9fdadee
> (v7.1-rc4-754-ge8c2f9fdadee) shows the structure is unchanged:
> ocfs2_clear_inode() at fs/ocfs2/inode.c:1223-1224 still calls
> ocfs2_checkpoint_inode() with no osb->journal check, and
> ocfs2_ci_fully_checkpointed() at fs/ocfs2/journal.h:98 still
> dereferences journal->j_trans_id unconditionally. As no reproducer
> is available for this seed, I have not re-run it against e8c2f9fdadee.
>
> Impact:
> An ocfs2 unmount kills the kernel with a GPF / KASAN null-ptr-deref.
> The full Oops header, register dump, and call trace are in
> crash-report.txt.
>
> Expected behavior:
> Either ocfs2_clear_inode() should skip ocfs2_checkpoint_inode() when
> osb->journal is NULL (the same shape as the existing guard at
> fs/ocfs2/inode.c:1286), or ocfs2_ci_fully_checkpointed() should treat
> a NULL journal as "fully checkpointed".
>
> Reproducer:
> A standalone .syz or C reproducer was not produced for this seed; the
> crash fired during automated ocfs2 mount/unmount fuzzing. The console
> report is attached as crash-report.txt.
>
> Novelty check:
> I searched the syzbot dashboard's upstream open, fixed, stable, and
> invalid (per-subsystem ocfs2) namespaces; the Android dashboard; the
> marc.info linux-fsdevel archive; and lore, for
> "ocfs2_ci_fully_checkpointed", "ocfs2_checkpoint_inode",
> "ocfs2_clear_inode" + journal, and "ocfs2_evict_inode" + GPF. I did
> not find a prior report of this specific dereference.
>
> There are three closely related syzbot reports, all titled by the
> outer frame:
> "general protection fault in ocfs2_evict_inode" (id 9d4aa7d1...),
> "general protection fault in ocfs2_clear_inode" (id 9eca3ca3...),
> and "...ocfs2_clear_inode (2)" (id 9e34a0dc...). They share this
> unmount slot-info eviction reach path but fault at offset 0x8 of the
> NULL pointer (KASAN range [0x8, 0xf]); this crash faults at offset
> 0x70, inside ocfs2_ci_fully_checkpointed() at journal.h:98 (the
> journal->j_trans_id read) -- a distinct dereference site. Commit
> f46e8ef8bb7b ("ocfs2: prevent release journal inode after journal
> shutdown", first in v6.17-rc5) added an osb->journal guard before the
> jbd2_journal_release_jbd_inode() call at fs/ocfs2/inode.c:1286, but
> the earlier ocfs2_checkpoint_inode() call in the same
> ocfs2_clear_inode() is not guarded.
>
>
> I appreciate your time and consideration, and I'm grateful for your
> work on this subsystem. I'd be glad to test any candidate patches.
>
A simple fix, please have a try:

diff --git a/fs/ocfs2/journal.h b/fs/ocfs2/journal.h
index 6397170f302f..f8b3b2a3d630 100644
--- a/fs/ocfs2/journal.h
+++ b/fs/ocfs2/journal.h
@@ -196,6 +196,9 @@ static inline void ocfs2_checkpoint_inode(struct inode *inode)
if (ocfs2_mount_local(osb))
return;

+ if (!osb->journal)
+ return;
+
if (!ocfs2_ci_fully_checkpointed(INODE_CACHE(inode))) {
/* WARNING: This only kicks off a single
* checkpoint. If someone races you and adds more