From: Alexander Bluhm Subject: syzkaller dt ioctl record race To: tech@openbsd.org Date: Thu, 31 Jul 2025 01:12:01 +0200 Hi, syzkaller finds a crash in dt_ioctl_record_stop(). https://syzkaller.appspot.com/bug?extid=34f860f29dc941cfb548 Note that this also happens on single processor kernel. The code is protected by kernel lock. But that means there should be no sleeps as they release the kernel lock. rw_enter_write(&dt_lock) may sleep which breaks the locking assumptions. So move the rw_enter_write() at the beginning. This gives the benefit that more variables are locked by this. ok? bluhm Index: dev/dt/dt_dev.c =================================================================== RCS file: /data/mirror/openbsd/cvs/src/sys/dev/dt/dt_dev.c,v diff -u -p -r1.44 dt_dev.c --- dev/dt/dt_dev.c 3 Jun 2025 00:20:31 -0000 1.44 +++ dev/dt/dt_dev.c 30 Jul 2025 12:10:19 -0000 @@ -86,14 +86,20 @@ #define DPRINTF(x...) /* nothing */ /* - * Per-CPU Event States - * - * Locks used to protect struct members: + * Locks used to protect struct members and variables in this file: + * a atomic + * I invariant after initialization + * K kernel lock + * D dtrace rw-lock dt_lock * r owned by thread doing read(2) * c owned by CPU * s sliced ownership, based on read/write indexes * p written by CPU, read by thread doing read(2) */ + +/* + * Per-CPU Event States + */ struct dt_cpubuf { unsigned int dc_prod; /* [r] read index */ unsigned int dc_cons; /* [c] write index */ @@ -110,12 +116,6 @@ struct dt_cpubuf { /* * Descriptor associated with each program opening /dev/dt. It is used * to keep track of enabled PCBs. - * - * Locks used to protect struct members in this file: - * a atomic - * K kernel lock - * r owned by thread doing read(2) - * I invariant after initialization */ struct dt_softc { SLIST_ENTRY(dt_softc) ds_next; /* [K] descriptor list */ @@ -124,7 +124,7 @@ struct dt_softc { void *ds_si; /* [I] to defer wakeup(9) */ struct dt_pcb_list ds_pcbs; /* [K] list of enabled PCBs */ - int ds_recording; /* [K] currently recording? */ + int ds_recording; /* [D] currently recording? */ unsigned int ds_evtcnt; /* [a] # of readable evts */ struct dt_cpubuf ds_cpu[MAXCPUS]; /* [I] Per-cpu event states */ @@ -141,7 +141,7 @@ unsigned int dt_nprobes; /* [I] # of p SIMPLEQ_HEAD(, dt_probe) dt_probe_list; /* [I] list of probes */ struct rwlock dt_lock = RWLOCK_INITIALIZER("dtlk"); -volatile uint32_t dt_tracing = 0; /* [K] # of processes tracing */ +volatile uint32_t dt_tracing = 0; /* [D] # of processes tracing */ int allowdt; /* [a] */ @@ -519,15 +519,20 @@ dt_ioctl_record_start(struct dt_softc *s { uint64_t now; struct dt_pcb *dp; + int error = 0; - if (sc->ds_recording) - return EBUSY; + rw_enter_write(&dt_lock); + if (sc->ds_recording) { + error = EBUSY; + goto out; + } KERNEL_ASSERT_LOCKED(); - if (TAILQ_EMPTY(&sc->ds_pcbs)) - return ENOENT; + if (TAILQ_EMPTY(&sc->ds_pcbs)) { + error = ENOENT; + goto out; + } - rw_enter_write(&dt_lock); now = nsecuptime(); TAILQ_FOREACH(dp, &sc->ds_pcbs, dp_snext) { struct dt_probe *dtp = dp->dp_dtp; @@ -543,12 +548,12 @@ dt_ioctl_record_start(struct dt_softc *s now + dp->dp_nsecs); } } - rw_exit_write(&dt_lock); - sc->ds_recording = 1; dt_tracing++; - return 0; + out: + rw_exit_write(&dt_lock); + return error; } void @@ -556,15 +561,16 @@ dt_ioctl_record_stop(struct dt_softc *sc { struct dt_pcb *dp; - if (!sc->ds_recording) + rw_enter_write(&dt_lock); + if (!sc->ds_recording) { + rw_exit_write(&dt_lock); return; + } DPRINTF("dt%d: pid %d disable\n", sc->ds_unit, sc->ds_pid); dt_tracing--; sc->ds_recording = 0; - - rw_enter_write(&dt_lock); TAILQ_FOREACH(dp, &sc->ds_pcbs, dp_snext) { struct dt_probe *dtp = dp->dp_dtp;