Re: [BUG] ocfs2: NULL osb->journal deref via ocfs2_checkpoint_inode on unmount
From: Farhad Alemi
Date: Sat May 30 2026 - 14:59:14 EST
Hi Joseph,
Thanks for sending the patch. I applied your patch on top of v7.1-rc5
e7ae89a0c97ce2b68b0983cd01eda67cf373517d and rebuilt with the same
config that originally caught this. The attached reproducer now runs
without causing a panic. Without the patch, the same reproducer still
trips it.
Please feel free to add the `Tested-by` tag.
Thanks again,
On Fri, May 29, 2026 at 3:12 AM Joseph Qi <joseph.qi@xxxxxxxxxxxxxxxxx> wrote:
>
>
>
> 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
>
Attachment:
reproducer.c
Description: Binary data