# DF-5285 VERDICT — reproduced (kernel panic / local DoS) and fix validated

## What was claimed

`sastrategy()` divides by `softc->min_blk`; a device-driven
`min_blk == 0` combined with fixed mode and a non-power-of-2 blocksize
(`blk_mask == ~0`) turns the next read(2)/write(2) into an integer
divide-by-zero in kernel mode.

## Reproduced? Yes — twice, decisively.

* Stock INVARIANTS kernel (fresh `vm.sh reset with-src`): `panic.txt` —
  `Fatal trap 18: integer divide fault while in kernel mode ... Stopped
  at sastrategy+0x289: divl %ecx,%eax`, all CPUs stopped, system in DDB.
* Same-source no-INVARIANTS kernel: `panic.noinv.txt` — identical trap,
  `sastrategy+0x279: divl %ecx,%eax`, `current process 868`
  (poc_setblk).

## Trigger chain (evilsa KLD + 30-line userland program)

1. evilsa reports READ BLOCK LIMITS `{gran=0, max=0xFFFFFF, min=0}` —
   exactly the response the driver expects from RBL-less drives (the
   failure path at `scsi_sa.c:2003-2005` also sets `min_blk = 0`
   explicitly).
2. `open("/dev/nsa0")` mounts in variable mode: `min_blk = 0`
   survives every step (`scsi_sa.c:2019`, no lower clamp anywhere).
3. `MTSETBSIZ 3` (the ordinary "set block size to 3" that an operator
   issues when a drive reports odd blocking): `sasetparams()` mode
   select succeeds, then `scsi_sa.c:1208-1217` sets FIXED +
   `blk_mask = ~0` (3 is not a power of two) — `min_blk` untouched.
4. `read(fd, buf, 512)` → `sastrategy()` evaluates
   `(bp->b_bcount % softc->min_blk)` at `scsi_sa.c:756` → #DE → panic.

The device fully controls the enabling state (RBL minimum); the
triggering I/O is normal API use. Opening the sa node requires
SYSCAP_RESTRICTEDROOT, so the local actor is privileged/operator —
hence Medium (DoS), not High.

## Why this is not memory corruption

The fault is a pure divide error (#DE, trap 18) — no overwrite, no
controlled redirect. Impact ceiling: reliable kernel panic of any
system with the tape open (all CPUs stopped).

## Fix validation

`fix.diff`:
* clamps `min_blk` to >= 1 in `samount()` after RBL parsing (covers
  both the parsed value and the RBL-failure path),
* guards the modulo in `sastrategy()` (`min_blk > 0`),
* (same diff) `M_ZERO` for the 8192-byte RBL scratch (DF-5286) and for
  `ccomp` (DF-5284).

Same-source NOINV kernel rebuilt incrementally (`fix_build.log`),
installed, rebooted; identical PoC re-run:

```
MTSETBSIZ 3 accepted: FIXED mode, blk_mask=~0, min_blk still 0
issuing read(512) -> sastrategy: 512 % min_blk(0) ...
NO PANIC: read returned 512
```

Guest stayed up (`run.patched.log`). Baseline on the identical
unpatched kernel panicked (`panic.noinv.txt`).

## Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest `make -j6 nativekernel KERNCONF=X86_64_GENERIC` completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
