# DF-1109 — I2C `I2CRDWR` ioctl TOCTOU double-fetch of `d->nmsgs`

## Verdict (this run)
**SOURCE-CONFIRMED, NOT REPRODUCED AT RUNTIME on this guest** — the six-fold
re-fetch of `d->nmsgs` from user memory is traced line-by-line in compiled
module source (`iic.ko` ships in `/boot/kernel`), but the I2C driver never
attaches here (no i2c controller, no `/dev/iic*`). The fix (`fix.diff`)
**applies and compiles** (`iic.ko` rebuilt clean under `-Werror`).

Additional finding-derived caveat: `/dev/iicN` is created mode `0600
root:wheel` (`iic.c:129`), so the bug is **root→kernel** (no unprivileged
boundary crossing); realistic impact is a root-driven TOCTOU heap overflow plus
an OOM DoS (`nmsgs=UINT32_MAX` → ~24 GB `M_WAITOK` allocation).

## How to reproduce (HW-equipped host, as root)
1. `./build.sh && ./run.sh /dev/iic0` — two threads: one issues
   `ioctl(I2CRDWR)` in a loop, the other flips `nmsgs` between the allocation
   size and an overflow size so a bump lands between the `kmalloc` and the
   `copyin`/loops. A successful race overflows `buf` (16 B/excess) / `usrbufs`
   (8 B/excess).
2. Requires: an i2c controller so `/dev/iic0` exists, and root to open it.

## Why not on this guest
No i2c controller hardware; `iic.ko` not loaded; no `/dev/iic*`. The trigger
cannot `open()` a device. Dormant code path, not absent.

## Files
- `poc_iic_toctou.c` — intended racing-threads trigger (documented).
- `fix.diff` — snapshot `nmsgs` into a local once, cap at 65536, use local
  everywhere.
- `iic_fix_build.log` — proof the fix compiles (`-Werror`, rc=0).
- `VERDICT.md` — full line-by-line trace.
