⬢ DragonFlyBSD Kernel Audit
DF-4532 / trigger.sh
← back to finding ↓ download raw
#!/bin/sh
# DF-4532 — device_busy() leak in drm_open() blocks driver detach forever
#
# drm_file.c:351 calls device_busy(dev->dev->bsddev) on every successful
# open, but NO live path ever calls device_unbusy(): drm_close() lacks it,
# and the only device_unbusy() in sys/dev/drm (drm_drv.c:1230) sits in
# drm_cdevpriv_dtor(), which is never registered (no devfs_set_cdevpriv()
# caller exists anywhere in the tree).  device_busy() increments
# dev->busy and forces dev->state = DS_BUSY (subr_bus.c:1844-1853);
# device_detach() refuses to run for DS_BUSY devices (subr_bus.c:2129ff).
# Net effect: after the FIRST open of /dev/dri/cardN, the drm device (and
# its recursively-busied PCI parent) can never detach or kldunload again.
#
# This script demonstrates the observable half (needs a DRM device):
#  1. baseline: kldunload of the drm driver succeeds before any open
#  2. open /dev/dri/cardN once, close it
#  3. kldunload now fails EBUSY forever despite zero open fds
#
# Hardware-gated on the QEMU audit guest (no /dev/dri). Run as root on a
# system with a DRM device.
set -e
DRIVER=${1:-i915}

echo "== baseline detach attempt (no drm fd ever opened) =="
kldunload "$DRIVER" 2>&1 || true
kldload "$DRIVER" 2>&1 || true

echo "== single open/close of /dev/dri/card0 =="
perl -e 'open(F, "</dev/dri/card0") or die "no drm device: $!\n"; close(F)' \
	|| true

echo "== detach attempt after the one open/close =="
if kldunload "$DRIVER" 2>&1; then
	echo "UNEXPECTED: unloaded (bug not reproduced)"
else
	echo "REPRODUCED: kldunload fails (device wedged in DS_BUSY forever)"
fi