# DF-5641 — isa_alloc_resource/isa_release_resource cast grandchild ivars via DEVTOISA (latent type confusion, dead-cast today)

Target file: `sys/bus/isa/x86_64/isa.c` (pass 2)
Severity: Info (defense-in-depth) · no runtime impact demonstrated — and this pack
*proves* the confused pointer is computed on a live boot and never dereferenced.

## Claim

`isa_alloc_resource()` (sys/bus/isa/x86_64/isa.c:76-124) and
`isa_release_resource()` (isa.c:126-134) unconditionally do

```c
struct isa_device* idev = DEVTOISA(child);          /* isa.c:87, :130 */
struct resource_list *rl = &idev->id_resources;
```

even when `passthrough` is true (`device_get_parent(child) != bus`, isa.c:85).
For a passthrough (grandchild) caller the child's ivars are **not** a
`struct isa_device` — they are the sub-bus's own ivar type (or NULL), so `rl`
is a type-confused / NULL pointer. It is handed to
`resource_list_alloc()`/`resource_list_release()`, which early-forward on
passthrough **without touching `rl`** (sys/kern/subr_bus.c:2683-2687 and
:2729-2732). That subr_bus.c implementation detail is the *only* reason every
DragonFly x86_64 boot with a PS/2 mouse does not panic in
`resource_list_find()` (subr_bus.c:2652 SLIST_FOREACH dereference).

## Live demonstration (guest)

Standard QEMU guest, kernel `X86_64_GENERIC` (see env.txt). One
configuration change — loader hints that attach `atkbdc0` to the ISA bus
(the in-tree `DRIVER_MODULE(atkbdc, isa, …)` attachment,
sys/dev/misc/atkbdc_layer/atkbdc_isa.c:401):

```
hint.atkbdc.0.at="isa"      # isahint.c:121-129 creates atkbdc0 on the isa bus
hint.atkbdc.0.port="0x60"
hint.atkbdc.0.irq="1"
hint.psm.0.at="atkbdc0"     # atkbdc_isa.c:290-295 adds psm0 with atkbdc_device_t ivars
hint.psm.0.irq="12"
```

psm0 is then a **grandchild** of the isa bus. `psmattach()`
(sys/dev/misc/psm/psm.c:2014) calls
`bus_alloc_legacy_irq_resource(dev=psm0, …, irq=12, RF_ACTIVE)`
(sys/kern/subr_bus.c:3302-3308), which dispatches to atkbdc's
`bus_generic_alloc_resource` (atkbdc_isa.c:83) and then to
`isa_alloc_resource(bus=isa0, child=psm0)` with:

* `passthrough == 1` (psm0's parent is atkbdc0),
* psm0's ivars = `atkbdc_device_t *` (atkbdc_isa.c:231-232) — cast to
  `struct isa_device *` at isa.c:87. `rl` = the `atkbdc_device_t` object
  reinterpreted as an SLIST head (id_resources is the first member,
  isa_common.h:53-54).

Observed (dmesg.hinted.txt, devinfo.hinted.txt, env.txt):

```
atkbdc0: <Keyboard controller (i8042)> at port 0x60,0x64 irq 1 on isa0
psm0: <PS/2 Mouse> irq 12 on atkbdc0
interrupt uses mplock: psm0
psm0: model IntelliMouse Explorer, device ID 4
```

`vmstat -i` lists `psm0` as a registered interrupt source →
`sc->intr != NULL` at psm.c:2015 → the full chain
psm.c:2014 → subr_bus.c:3302 → atkbdc_isa.c:83 → **isa.c:76-124 (type-confused
`rl` computed)** → subr_bus.c:2683 (early forward, `rl` untouched) → nexus
completed, the guest booted and stayed up. The confused pointer is *used* on
every such boot and survives only by the subr_bus.c passthrough convention.

## Reproduce

1. `./introspect.sh` (from the host) — or manually:
   append the five hints above to the guest's `/boot/loader.conf.local`,
   reboot, then `devinfo | grep -A3 atkbdc0` and `vmstat -i | grep psm0`.
2. Expected: tree shows `isa0 → atkbdc0 → psm0`, `psm0` registered as an
   interrupt, guest healthy (no panic) — the type-confused passthrough call
   executed and was harmless.
3. Remove the hints and reboot to restore (guest was left in the restored
   state; `guest_dirty: 0`).

## Why Info and not higher

No dereference of the confused `rl` exists on any reachable path today:
the only deref site (`resource_list_find`, isa.c:92) is inside
`if (!passthrough && !isdefault)`, and the two subr_bus.c helpers early-return
on passthrough before touching `rl`. All inputs to this file come from
in-tree drivers (kernel-internal bus methods at probe/attach/kldload); no
user, hint, or loader value reaches the non-default arithmetic here (hints
enter through `isa_set_resource`, isa_common.c:893-902, bounds-checked, and
are read back via `resource_list_alloc`, never via this file's checks).
The finding documents a one-edit-away landmine: hoisting the
`resource_list_find` call (as pre-fix FreeBSD once did) or adding any `rl`
use outside the passthrough guard turns every PS/2-mouse boot into a
NULL/wild dereference. FreeBSD restructured exactly this code to go through
`BUS_GET_RESOURCE_LIST()` (and gave isa a `bus_get_resource_list` method).

## Fix

`fix.diff` — compute `rl` only for direct children (`!passthrough`), where
`isa_add_child()` (isa_common.c:527-533) guarantees `struct isa_device`
ivars. Fix not applied to `sys/` (read-only audit); not kernel-rebuilt for
this Info-severity hardening (no misbehavior exists to diff — baseline
observation itself is the proof).
