DF-2680 / run.log
DF-2680 runtime evidence (DragonFly 6.5-DEVELOPMENT #0 X86_64_GENERIC, stock kernel) $ sh run_seq.sh == control (live async_proc) == victim2: pid=1961 armed, sleeping 20 victim2: GOT_SIGIO (live path works) pid=1961 == stale (victim exits, async_proc dangles) == victim: pid=1993 enabled FIOASYNC on /dev/devctl, exiting -- result: GOT_SIGIO pid=1994 (never opened devctl) Repetition (5 rounds, victim exits each time, fresh catchers each time): round 2: GOT_SIGIO pid=2032 (never opened devctl) round 3: GOT_SIGIO pid=2048 (never opened devctl) round 4: GOT_SIGIO pid=2075 (never opened devctl) round 5: GOT_SIGIO pid=2091 (never opened devctl) Interpretation: - victim (root) opened /dev/devctl (0600, SYSCAP_RESTRICTEDROOT gated) and issued FIOASYNC=1: devioctl() stored raw curproc in devsoftc.async_proc (sys/kern/subr_bus.c:412-417) with no reference taken. - victim exited; devclose() (subr_bus.c:352-361) reset inuse but left async_proc pointing at the reaped-and-freed struct proc (kfree(p, M_PROC) at sys/kern/kern_exit.c:1336). - The next fork (catcher, pid = victim+1) recycled the freed proc chunk. - Each devctl event ran devctl_queue_data() -> ksignal(stale proc, SIGIO) (subr_bus.c:521-523) -> lwpsignal() executed PHOLD(p), lwkt_gettoken(&p->p_token) and the lwp walk on the recycled memory and delivered SIGIO to a process that never opened /dev/devctl. - With a non-proc kernel allocation recycling the chunk instead, the same PHOLD/token writes land in foreign kernel objects (memory corruption).