# DF-2276 verification pack — DAIOCTRIM infinite loop / negative b_bcount

Bug: `sys/bus/cam/scsi/scsi_da.c:436-481` — the DAIOCTRIM handler reads the
user `off_t` pair SIGNED, checks only 512-alignment, then computes

	int byte_count;                       /* scsi_da.c:421 */
	...
	byte_count = MIN(bytes_left, 0x7FFF8000);   /* off_t result -> int  */
	bp->b_bcount = byte_count;                 /* struct buf b_bcount is int */
	...
	bytes_left -= byte_count;                  /* loop variable */

* `del_num[1] = LLONG_MIN` is a multiple of 512 (passes both asserts);
  `MIN(LLONG_MIN, 0x7FFF8000) = LLONG_MIN` truncates to int 0 -> the loop
  dispatches zero-block TRIMs forever, `bytes_left` never changes, one
  `getnewbuf` allocation per iteration, the ioctl never returns.
* Smaller negatives (e.g. -0x1000) truncate to a NEGATIVE int
  `bp->b_bcount = 0xFFFFF000` handed to `dev_dstrategy()`.

**Verdict: REPRODUCED exactly as claimed — plus one aggravating discovery
(see VERDICT.md): through the REAL dastrategy/dastart the negative
b_bcount is reinterpreted by an unsigned division into a ~4 GiB TRIM.**

## Rig

Same harness kernel as DF-2275 (see ../DF-2275/rig): verbatim scsi_da.c
(sha256-gated) compiled in as the only da driver, fabricated second disk
da99, fake transport. All measurements below came from the REAL code path
(userland ioctl -> mapped_ioctl copyin -> dev_dioctl -> daioctl ->
dev_dstrategy -> dastrategy -> xpt_schedule -> dastart -> xpt_action ->
dadone -> biodone -> biowait). A harness watchdog (debug.dfda.watchdog)
forces the EIO error exit after N bios so the infinite loop terminates
with counters.

## Build

    cd ../DF-2275/rig && ./build.sh

## Run (baseline = vulnerable)

    ./run.log sections correspond to:
      sysctl debug.dfda.setup=1; sysctl debug.dfda.mode=1
      /root/da_user neg       # {0, -4096}   full real strategy path
      sysctl debug.dfda.mode=0
      /root/da_user neg       # capture: what the dispatch itself sends
      sysctl debug.dfda.watchdog=200000;  /root/da_user llmin   # full path
      sysctl debug.dfda.watchdog=1000000; /root/da_user llmin   # capture rate
      sysctl debug.dfda.stats; sysctl vfs.getnewbufcalls

## Expected (vulnerable kernel) — all observed

* neg {0,-4096}:  ioctl returns SUCCESS; the dispatched buf carries
  `b_bcount = -4096 (0xfffff000)` (cap[0] line in run.log); the real
  dastart turns it into a 129-range (~4 GiB) XPT_TRIM
  (dxfer=1536 = ((129+63)/64)*512) — decoded in VERDICT.md.
* llmin {0,LLONG_MIN}: every bio has b_bcount=0 and bio_offset=0
  (bytes_start never advances); 200001 bios in 0.092 s through the FULL
  real path (200000 zero-range XPT_TRIM CCBs, dxfer=0, ranges=0) and
  1000001 bios in 0.105 s in capture mode (~2.2M / ~9.5M iterations per
  second); vfs.getnewbufcalls grew by 200006 (one buffer allocation per
  iteration). Without the watchdog this runs forever: the syscall never
  returns, the thread never checks signals (unkillable), one CPU pinned.

## Fixed kernel (A/B)

    (same kernel as DF-2275 fix) ./run_fixed.sh

    neg   -> r=-1 errno=22 (EINVAL), nothing dispatched (strat_count=0)
    llmin -> r=-1 errno=22 (EINVAL), instant, nothing dispatched

Impact ceiling: privileged-reach (SYSCAP_RESTRICTEDROOT at daopen,
scsi_da.c:505) kernel DoS — an unkillable in-kernel loop from one ioctl —
plus, on real da hardware, up to ~4 GiB of misdirected TRIM
(data destruction) for mid-range negative lengths. No kernel memory
corruption (dastart structurally bounds the range writes; full analysis
in VERDICT.md). No unprivileged route.

Artifacts: run.log, build.log, fix.diff, fix_build.log, fix_run.log,
da_user.c, getnewbuf_before.txt / getnewbuf_after.txt, env.txt;
rig shared with DF-2275 (../DF-2275/rig).
