# DF-2776 evidence pack — unregister_int dereferences the stale caller cookie

Finding: `sys/kern/kern_intr.c:425` — `intr = ((intrec_t)id)->intr;` reads
through the caller-supplied cookie BEFORE any validation (the only prior
check is the cpuid range assert), and `kern_intr.c:489-490` prints
`((intrec_t)id)->name` with `%s` on the not-found path. If a driver calls
`unregister_int` with a stale (already-freed) cookie — e.g. a
double-unregister bug — the kernel reads `intr` from freed heap memory and
prints a NUL-terminated string from whatever address the freed cookie's
`name` field now holds.

## Rig

Kernel harness (`src/df_harness.c`, sysctl `dfh.run2776`): register an intrec
on softint 253 cpu0, call the REAL `unregister_int` (frees name+rec), probe
`kmalloc(56, M_DEVBUF)` (LIFO reclaim of the freed rec — reuse=YES observed),
fill the cookie memory as an attacker-influenced reallocation would
(`intr=253`, `name=&df_token_2776`), then call the real `unregister_int`
again with the stale cookie.

## Expected / observed

- Stock (`df2776_groomed.dmesg`, `run.log`):
  `warning: unregister_int: int 253 handler for DF2776-STALE-COOKIE-READ-4b77d1 not found`
  — `intr` read from freed memory selected the walk; the `%s` read disclosed
  the string at the chosen kernel address to the console/dmesg.
- Wild variant (`dfh.wild2776=1`, `name=0x5151515151515150`): panic
  `Stopped at strlen: cmpb $0,(%rdi)` **mid-print** (`panic.txt`).
- Fixed (`df2776_fixed.dmesg`): `warning: unregister_int: int handler
  0xfffff8004f164290 not found` — pointer only, no cookie dereference; the
  wild variant returns cleanly with no panic.

Build/run: same kernel-harness procedure as DF-2775 (README there);
`sysctl dfh.wild2776=0 dfh.run2776=1` then `sysctl dfh.wild2776=1 dfh.run2776=1`.
`fix.diff` = canonical combined fix (DF-2776 hunk: the not-found kprintf).
