β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-4476

IOCATADETACH/ATTACH/REINIT dispatched unserialized: double channel teardown (double bus_teardown_intr/bus_release_resource/lockuninit) -> sysref_put panic

Summary

The three channel-control ioctls share no lock; ata_detach check-then-acts on ch->r_irq, so concurrent DETACH runs teardown twice (483x unregister_int warnings with corrupted handler names = freed-memory reads), and racing REINIT uses locks a detach destroyed. REPRODUCED on guest: sysref_put assertion panic via destroy_only_dev on channel 1 at ~30s. Distinct from DF-1643: even legitimate privileged concurrent use is unsafe. Fix validated PARTIAL: channel 0 fully clean; channel 1 residual = async disk(9)-core thread needing subr_disk.c synchronization (documented).

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4476 Β· 7 files
FileTypeDescriptionSize
ata_detach_race.c poc 2x detach + attach + reinit threads 2.8 KB view raw
serial_a2_panic.log panic sysref_put assertion capture 78.0 KB view raw
run.log run-log 3 baseline runs 1.0 KB view raw
fix.diff suggested-fix ioctl serialization lock 2.3 KB view raw
fix_run_ch0.log run-log patched ch0: 0 unregister_int 1.2 KB view raw
fix_run_ch1_panic.serial.txt panic patched ch1 residual variant 1001 B view raw
env.txt env guest/kernel info 314 B view raw

Fix verification

fix_failed
baseline reproduced→ patch + rebuild →patched reproduced

PARTIAL: fix.diff adds ata_channel_ctl_lock across the three ioctl bodies (build rc=0). Channel 0: baseline 483 unregister_int warnings -> 0 on patched kernel, 356/355/163 racing ops survive. Channel 1: STILL panics via destroy_only_dev <- disk_msg_core (asynchronous disk(9)-core thread racing the newbus child teardown) - a second actor the ioctl-level lock cannot serialize; complete fix requires disk-layer synchronization in kern/subr_disk.c.

fix_run_ch0.log (clean); fix_run_ch1_panic.serial.txt (residual panic trace)
↓ fix.diffper-fix-DF-4476

Confirmed kernel references

Detail

Exploit chain

operator/root opens /dev/ata -> 2x IOCATADETACH + IOCATAATTACH + IOCATAREINIT threads on channel 1 -> racing device_delete_child/bus_teardown_intr/lockuninit -> double destroy_only_dev on the same cdev -> panic at db>. Corruption artifacts (garbage handler names) show freed-memory reads in the IRQ teardown path.

Evidence (decisive lines)

serial_a2_panic.log (baseline sysref panic + destroy_only_dev stack + 483 unregister_int lines); run.log (3 baseline runs incl. corrupted-name warnings); fix_run_ch0.log (patched: 0 unregister_int, survived); fix_run_ch1_panic.serial.txt (patched: still panics via disk_msg_core variant)

PoC changes

authored fresh; ioctl constants built with _IOC/_IOW verified against sys/sys/nata.h

Verified recommended fix

Serialize the channel-control ioctls AND synchronize child cdev destruction with the disk-core thread (ioctl lock alone is partial - see fix validation)

Verdict

IOCATADETACH/IOCATAATTACH/IOCATAREINIT are dispatched with no serialization (ata-all.c:369-447); ata_detach is an unlocked check-then-act on ch->r_irq. Concurrent ioctls double-teardown the channel (483 unregister_int warnings, later with corrupted device names = freed-memory reads) and on channels with disk children hit destroy_only_dev twice -> panic: sysref_put SRF_PUTAWAY assertion, 30s into the channel-1 run.