# DF-4546 — lm78 lm_detach on a zero-sensor attach (W83791SD) → NULL-pointer write + global sensordev_list corruption

**Severity: Low (privileged detach trigger, obscure chip) | Confidence: certain**
**File: `sys/dev/powermng/lm/lm78.c:590-592,427-429,445-458`**

## What

`wb_match()` claims the Winbond **W83791SD** without setting up any sensor
table (lm78.c:590-592 — the only match case with no `lm_setup_sensors`).
`sc->numsensors` stays 0, so `lm_attach()` bails at its guard (lm78.c:427-429)
and **`sensordev_install()` never runs**. `lm_detach()` (lm78.c:445-458) then
**unconditionally** calls `sensordev_deinstall(&sc->sensordev)`.

`sensordev_deinstall` (kern_sensors.c:153-176) executes
`TAILQ_REMOVE(&sensordev_list, sensdev, list)` on a node that was never
inserted. The newbus softc is zeroed, so per the DragonFly TAILQ_REMOVE
(sys/sys/queue.h:646-662):

```c
if ((TAILQ_NEXT(elm, field)) != NULL) ...       /* tqe_next == NULL -> skip */
else (head)->tqh_last = (elm)->field.tqe_prev;  /* global list tqh_last = NULL */
*(elm)->field.tqe_prev = TAILQ_NEXT(elm, field);/* *(NULL) = NULL  <-- WRITE to 0x0 */
```

i.e. an immediate **write through a NULL pointer** (kernel panic) that first
also clobbers the global `sensordev_list.tqh_last` (any later
`TAILQ_INSERT_TAIL` would itself write through NULL).

Trigger: driver detach of a bound lm(4) on W83791SD hardware —
`kldunload`/devctl detach/unbind (root). Reachable at runtime, not
exploitable for anything but DoS; filed because the teardown path is
fundamentally unguarded and the same shape fires for any future zero-sensor
match entry.

## Reproduce (lifted-copy harness — macro-level)

The in-kernel path needs the physical chip (guest has no LM78-class
device), so the harness replicates `struct ksensordev` in a zeroed softc,
the verbatim `TAILQ_REMOVE` from sys/sys/queue.h:646, and the
install/deinstall list logic from kern_sensors.c.

```
./build.sh && ./run.sh
```

Expected (run.log):
- CONTROL normal chip (numsensors=19, e.g. W83792D) → install → attach →
  detach clean, list intact
- BUG zero-sensor attach (W83791SD path) → detach →
  **SIGSEGV: kernel-equivalent NULL-pointer WRITE at 0x0**
- PATCHED (fix.diff: `numsensors == 0` guard) → same zero-sensor attach
  detaches cleanly

## Fix validation

- `fix.diff` — `git apply --check` OK; one guard
- kernel-toolchain `-fsyntax-only` compile of the patched lm78.c in the
  X86_64_GENERIC kernel objdir: **PATCHED_RC=0** with a deliberate-error
  negative control failing (NEGCTL_RC=1) — the check is real
  (fix_build.log); guest /usr/src restored

## Files
- `lm_detach_nullwrite.c` (sha256
  2efe60dd3d3e7e77e4d78e9061d741869c72f6fd87686a3d9562507cada2ddef)
- `build.sh` / `run.sh`, `build.log` / `run.log` / `run.2.log` / `fix_run.log`
- `fix.diff`, `fix_build.log`, `env.txt`, `VERDICT.md`, `manifest.json`, `verdict.json`
