# DF-2936 VERDICT — reproduced / fixed

**Finding:** `module_register_init()` MOD_LOAD-failure path destroys the
module registry entry (`module_unload` + `module_release`,
sys/kern/kern_module.c:112-113 in the stock tree) while every caller up the
chain reports success: SYSINIT return values are discarded by
`linker_file_sysinit()` (sys/kern/kern_linker.c:176-189), so
`linker_load_file()` (kern_linker.c:346-352) sets LINKER_FILE_LINKED and
returns 0, and `sys_kldload` (kern_linker.c:820-826) hands back the file id.

## How it was reproduced (baseline, kernel #0)

Built `boom.ko` in-guest: `DECLARE_MODULE(boom, ...)`, MOD_LOAD returns 5,
plus a `SYSUNINIT` kprintf tag (sources: `boom.c`, `Makefile`).

1. `kldload ./boom.ko` → **rc=0** — load reported successful.
2. `kldstat` lists `boom.ko` (id 8, Refs 1) — file resident.
3. `kldstat -m boom` → "can't find module boom" — module freed and removed
   from both TAILQs (`module_release`, kern_module.c:177-183) at *load* time.
4. dmesg: `MOD_LOAD -> deliberately failing` then `MOD_UNLOAD dispatched`
   then `module_register_init: MOD_LOAD (boom, ffffffff82600000, 0) error 5`
   — the handler address is also printed to the log (kern_module.c:114-115,
   error-path KVA print, gated on privileged trigger; the kldstat/kldsym
   pointer-leak family is DF-0025).
5. `kldunload boom` → rc=0, **no second MOD_UNLOAD** (module no longer on
   `file->modules`, kern_linker.c:505-522 never dispatches to it), but the
   module's **SYSUNINIT runs for the module destroyed at load time**
   (`linker_file_sysuninit`, kern_linker.c:208-243).

Full output: `run.log`.

## Root cause (path:line)

- sys/kern/kern_module.c:110-116 — failure path tears the registration down
  behind the linker's back; the registration reference created by
  `module_register` (kern_module.c:134,141,146) belongs to the *file*, whose
  teardown (`linker_file_unload` → `module_release`, kern_linker.c:521) is
  the only legitimate dropper.
- sys/kern/kern_linker.c:350 — `linker_file_sysinit()` return value ignored;
  load reported successful regardless of SYSINIT/MOD_LOAD failure.

## Impact

Privileged trigger only (kldload, `caps_priv_check_self(SYSCAP_NOKLD)`,
kern_linker.c:806, or a boot-time MOD_LOAD failure of a static module — in
the static case the module vanishes from modstat silently, which is merely
cosmetic). For KLD modules: permanent zombie state — code/sysctls/SYSUNINITs
resident, module invisible to modstat(2)/kldstat, teardown-order inversion
(SYSUNINIT runs after the already-dispatched MOD_UNLOAD; MOD_UNLOAD runs
before other SYSINITs of the same file have executed). A driver whose
SYSUNINIT assumes successful MOD_LOAD, or whose MOD_UNLOAD already freed
everything, crashes or double-frees at kldunload. No unprivileged reach; no
memory corruption demonstrated in-tree → severity Low, bucket base:kern.

## Fix validation (kernel #1, `fix.diff` applied in-guest, `make nativekernel`)

`run.fix.log`:

- `kldload ./boom.ko` → rc=0 (unchanged — success-reporting is the separate
  kern_linker.c defect), **module stays registered**: `kldstat -m boom` →
  `Id 325 Refs 1 boom`.
- dmesg: `MOD_LOAD -> deliberately failing` then the error kprintf — **no
  MOD_UNLOAD at load time**.
- `kldunload boom` → rc=0: `MOD_UNLOAD dispatched` (once, at unload time),
  `SYSUNINIT running` (symmetric), `kldstat -m boom` afterwards → not found
  (registration correctly dropped by `linker_file_unload` → `module_release`,
  kern_linker.c:521).

All three baseline-observed bad behaviors are gone on the patched kernel:
registry entry survives the failed MOD_LOAD; MOD_UNLOAD is dispatched exactly
once at file-unload time; SYSUNINIT runs for a registered module.
fix_status = fixed.

## Classification

- status: reproduced (baseline behavior confirmed exactly as traced)
- impact: none direct (privileged-trigger lifecycle/logic flaw; no memory
  corruption reachable from the registry side in-tree)
- confidence: certain
- exploit chain: n/a (not a memory-corruption primitive; ceiling is
  module-dependent crash at kldunload)
