# DF-2429 VERDICT — XPT_CALC_GEOMETRY divide-by-zero in bt(4)

## Bottom line

**REPRODUCED — kernel panic (`Fatal trap 18`, integer divide fault) inside
the production `btaction` handler, dispatched through the real CAM XPT
layer.** One honest correction to the finding: the `block_size == 0`
variant named in the summary never reaches bt.c, because the CAM layer
filters it (cam_xpt.c:3063-3071, "Filter out garbage").  The second
variant — `block_size > 1 MiB`, which zeroes the *inner* quotient
`(1024*1024)/block_size` and then divides `volume_size` by zero — passes
that filter and panics exactly as claimed.

## How it was reproduced

Rig = `dfbt_rig.c` (details in README.md): real production `btaction`
resolved from `/boot/kernel/kernel` (no KASLR; prologue bytes verified),
real registered `cam_sim` + real `xpt_action` dispatch over a fabricated
post-attach softc.  `xpt_action(XPT_CALC_GEOMETRY)` calls
`sim->sim_action` directly (cam_xpt.c:3073), so the CCB reaches
production `btaction` unmodified.

Run (`run.log`, `panic.txt`):

```
$R calcgeom 0 4194304
[rig] CALCGEOM: xpt_action(block_size=0 volume_size=4194304)
[rig] CALCGEOM survived: heads=0 spt=0 cyl=0 status=0x1     <- CAM filter
$R calcgeom 2097152 4194304
[rig] CALCGEOM: xpt_action(block_size=2097152 volume_size=4194304)
Fatal trap 18: integer divide fault while in kernel mode
instruction pointer = 0x8:0xffffffff80430bcf
kernel: type 18 trap, code=0
Stopped at      btaction+0x4ff: divq    %rsi,%eax
```

`btaction+0x4ff` disassembles to `mov %rcx,%rax; divq %rsi;
cmp $0x3ff,%eax` — the `size_mb = volume_size / ((1024*1024)/block_size)`
computation at bt.c:1157-1158 followed by the `size_mb >= 1024` test at
bt.c:1160.  The divisor `%rsi` holds `(1024*1024)/2097152 == 0`.

## Root cause (path:line)

- sys/dev/disk/buslogic/bt.c:1157-1158 — division by a device-fed
  `block_size`-derived value with no zero check in the driver.
- sys/bus/cam/cam_xpt.c:3063-3071 — the CAM-layer filter covers only
  `block_size == 0`/`volume_size == 0`, not `block_size > 1 MiB`.
- Feed path: sys/bus/cam/scsi/scsi_da.c:1831 (READ CAPACITY length →
  `block_size`) → scsi_da.c:2302 (`ccg->block_size = dp->secsize`).

## Impact

Kernel panic (local DoS) from a hostile/lying SCSI target's READ
CAPACITY data on real bt(4) hardware.  Precondition: a BusLogic
MultiMaster HBA with an attached target reporting secsize > 1 MiB
(pass(4) CAMIOCOMMAND with a crafted CCB reaches the same handler on
any CAM bus whose SIM is bt(4)).

## Why the ==0 variant is filtered (negative result)

`xpt_action(XPT_CALC_GEOMETRY)` at cam_xpt.c:3062-3071 zeroes the
geometry and completes with CAM_REQ_CMP when `block_size == 0` — the
demonstrated `calcgeom 0 4194304` survived with status 0x1 (run.log).
A direct `btaction` dispatch with block_size == 0 would still divide by
zero (same bt.c:1157 line), but no in-tree caller can deliver it through
the XPT.

## Fix validation

`fix.diff` guards `block_size == 0 || block_size > (1024*1024)` with
`CAM_REQ_INVALID` before the division.  The patched bt.c + btreg.h were
compiled with the X86_64_GENERIC kernel's own flags and headers on the
guest: **compiles clean under -Werror** (`FIXED-COMPILES-CLEAN`,
`bt.fixed.o` produced).  No runtime A/B kernel build: this is a panic
class finding, not memory corruption, so a fix kernel rebuild is not
mandated by the audit contract; the fix is a two-line guard directly on
the proven faulting line.

## 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.
