# DF-4806 — ppi(4): interrupt handler registered with a `cdev_t` argument, treated as `device_t` — wild-pointer deref / instant kernel panic at first parallel-port IRQ (currently defused by a second bug: the IRQ resource allocation can never succeed)

File: `sys/dev/misc/ppi/ppi.c` lines 160-161, 266-269 vs 187-189 (pass 2)

## The two interacting bugs

**Bug A (ppi.c:160-161)** — the PERIPH_1284 interrupt resource is allocated
non-shareable:

```c
	ppi->intr_resource = bus_alloc_legacy_irq_resource(dev, &zero, irq,
	    RF_ACTIVE);
```

The ppbus parent (ppc, `sys/dev/misc/ppc/ppc.c:1882-1883`) already holds the
parallel-port IRQ as `RF_SHAREABLE`. DragonFly's rman requires matching share
type to share (`sys/kern/subr_rman.c:341`, "a candidate region must match
exactly by both size and sharing type"), and `RF_ACTIVE|RF_SHAREABLE`
allocations also fail activation against the live shareable region — so on a
stock system the allocation **always fails**: `intr_resource == NULL`, the
handler is never registered, and the whole 1284-peripheral interrupt path is
dead code. (Sibling driver lpt allocates with plain `RF_SHAREABLE`,
`sys/dev/misc/lpt/lpt.c:385-386` — that is the minimal, correct fix and it
works: verified live.)

**Bug B (ppi.c:268 vs 187)** — the registration passes the wrong pointer:

```c
	/* ppiopen(), line 245: dev = ap->a_head.a_dev  -- a cdev_t */
	BUS_SETUP_INTR(device_get_parent(ppidev), ppidev,
		       ppi->intr_resource, 0,
		       ppiintr, dev,          /* <-- cdev_t as handler arg */
		       &ppi->intr_cookie, NULL, NULL);

 static void
 ppiintr(void *arg)
 {
	device_t ppidev = (device_t)arg;            /* <-- expects device_t */
	device_t ppbus = device_get_parent(ppidev); /* cdev+0x28 = link.tqe_prev */
	...
	ppi_disable_intr(ppidev);   /* -> ppb_rctr(bogus) -> PPBUS_IO(deref) */
```

lpt passes the correct `lptdev` (`lpt.c:755`). ppi passes the character
device. On amd64, `struct bsd_device`'s `parent` field (offset 0x28,
`sys/sys/bus_private.h:109`) lands on `struct cdev`'s `link.tqe_prev`
(offset 0x28, `sys/sys/conf.h:67+`) — the cdev TAILQ linkage — so the first
use of the "bus" in the interrupt handler chases a bogus pointer chain
(`ppb_rctr` -> `PPBUS_IO(device_get_parent(bogus))`) and faults at interrupt
level.

**Interaction**: bug A defuses bug B today; fixing A alone (the obvious
"make 1284 mode work" one-liner) arms B and every parallel-port interrupt
with `/dev/ppi0` open panics the kernel. Both fixes must land together.

## Reproduce (root on the QEMU guest with ppc0 at 0x378 irq 7)

```
# 1. build modules (ppbus with PERIPH_1284 via COPTS, ppc, ppi) and trigger
sh modbuild.sh stock    # or: armed | fixed   (see arming.diff / fix.diff)
cc -O -Wall -o ppi_intr_panic ppi_intr_panic.c

# 2. load ORDER MATTERS: ppi must register before ppbus0 attaches
#    (bus_generic_identify no-ops on an attached parent)
kldload ppbus/ppbus.ko && kldload ppi/ppi.ko && kldload ppc/ppc.ko

# 3. fire
./ppi_intr_panic
```

Expected per variant (all three were run and captured):

| variant | ppi.c state | result |
|---|---|---|
| stock | RF_ACTIVE (as shipped) | 8 strobes, no handler registered (intr_resource NULL), guest healthy — see `stock_run.log` |
| armed | RF_SHAREABLE (arming.diff, lpt-identical) | **Fatal trap 12 at `ppiintr+0x41: movq (%rax),%rsi`, fault VA 0x0, interrupt context, all CPUs stopped** — see `panic.txt` |
| fixed | RF_SHAREABLE + `ppiintr, ppidev` (fix.diff) | 8 strobes survived, handler registered+running (`vmstat -i` ppc0=8), guest healthy — see `fixrun.log` |

The trigger needs `/dev/ppi0` (0600 root:wheel, verified) and `/dev/io`;
each data strobe makes QEMU's emulated printer ACK => irq 7 delivery
(the same mechanism DF-4535 used for lpt).

## Impact

Instantaneous, unrecoverable kernel panic (local DoS) from `open()` + one
parallel-port interrupt, on any kernel built with `options PERIPH_1284`
(LINT64; not in X86_64_GENERIC/GENERIC) once the IRQ allocation is
functional. The fault is a supervisor READ through uncontrolled pointers —
panic-class, not a practical write primitive. Device node is 0600
root:wheel, so the direct trigger requires root/operator-granted access;
on a config where the node is group-writable (devfs.conf relaxations for
printer groups) it is reachable by any local member.
