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

Joseph Qi <[email protected]> Fri, 29 May 2026 18:12:01 +0800
Newsgroups dev.linux.lists.ocfs2-devel,org.kernel.vger.linux-fsdevel,org.kernel.vger.linux-kernel
Message-ID <[email protected]>

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