# DF-2936 — module_register_init MOD_LOAD-failure path desynchronizes module lifecycle from linker-file lifecycle

## What this demonstrates (baseline, stock kernel)

`module_register_init()` (sys/kern/kern_module.c:110-116) destroys the module
registry entry — `module_unload()` + `module_release()` — when the module's
MOD_LOAD handler returns an error. But the caller chain cannot observe the
failure: the SYSINIT framework discards the error, so `linker_load_file()`
(sys/kern/kern_linker.c:346-352) marks the file LINKED and reports success,
and `sys_kldload` (kern_linker.c:820-826) returns the file id with rc=0.

Observed on the guest (kernel #0, stock):

1. `kldload ./boom.ko` → **rc=0** (success) even though MOD_LOAD returned 5.
2. `kldstat` still lists `boom.ko` as loaded (Refs 1).
3. `kldstat -m boom` → **"can't find module boom"** — the module was
   unregistered and freed while its code stays resident.
4. dmesg order: `MOD_LOAD -> deliberately failing` → `MOD_UNLOAD dispatched`
   → `module_register_init: MOD_LOAD (...) error 5` — MOD_UNLOAD is
   dispatched at *load* time.
5. `kldunload boom` → rc=0, **no second MOD_UNLOAD** (the registry entry is
   gone; linker_file_unload's module loop at kern_linker.c:505-522 never sees
   it), but the module's **SYSUNINIT runs at kldunload for the module that
   was destroyed at load time** (linker_file_sysuninit, kern_linker.c:208).

Impact: a KLD whose MOD_LOAD fails (e.g. driver probe failure on
hardware-absent systems) silently becomes a *zombie*: its code, sysctls and
SYSUNINITs stay resident forever, invisible to modstat(2)/kldstat module
enumeration, and its teardown ordering is inverted (SYSUNINIT after the
MOD_UNLOAD that already ran). Any module whose SYSUNINIT assumes MOD_LOAD
completed (or whose MOD_UNLOAD already cleaned up) can crash or corrupt state
at kldunload time. Trigger requires kldload privilege (SYSCAP_NOKLD) or a
boot-time MOD_LOAD failure, hence Low severity.

## Build (in guest, as root)

    cd /tmp/df2936 && make       # boom.ko — needs /usr/src + /usr/obj kernel tree

## Run (in guest, as root)

    kldload ./boom.ko            # rc=0 (bug)
    kldstat | grep boom          # file loaded
    kldstat -m boom              # module ABSENT (bug)
    kldunload boom               # SYSUNINIT for dead module (bug)
    dmesg | tail                 # event ordering proof

`build.sh` / `run.sh` encode the exact commands.

## Expected output (bug present)

See `run.log` — decisive lines: `KLDLOAD_RC=0`, `kldstat: can't find module
boom`, dmesg triple `MOD_LOAD -> deliberately failing` / `MOD_UNLOAD
dispatched` / `error 5`, then `SYSUNINIT running (module destroyed at load
time!)` after kldunload.

## Fix validation

`fix.diff` removes the unload+release from the failure path. After
`make nativekernel` + reboot (kernel #1, `run.fix.log`):

- `kldstat -m boom` → module PRESENT (Id 325, Refs 1)
- no MOD_UNLOAD at load time (dmesg)
- `kldunload` → MOD_UNLOAD dispatched once at unload time + SYSUNINIT,
  module properly released (registry clean afterwards)

The remaining "kldload reports success despite MOD_LOAD failure" is a
separate kern_linker.c issue (SYSINIT errors cannot propagate through
linker_file_sysinit) — noted in the finding, out of scope for this file.
