Re: [BUG] ocfs2: NULL osb->journal deref via ocfs2_checkpoint_inode on unmount
Farhad Alemi <[email protected]> Sat, 30 May 2026 11:51:06 -0700
| Newsgroups | dev.linux.lists.ocfs2-devel,org.kernel.vger.linux-fsdevel,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <CA+0ovCgtzGEUKNoX2GRpdPWkaOC5qzDw34fBdfpBzLmO-ZXixw@mail.gmail.com> |
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 <[email protected]> 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 >
reproducer.c
(application/octet-stream, 109.9 KB) - not displayed