# DF-3797 — ENA device-supplied register offsets (CREATE_SQ/CREATE_CQ replies) used unvalidated as kernel write targets

## What (one paragraph)
`ena_com_create_io_sq()` stores `cmd_completion.sq_doorbell_offset` and
`ena_com_create_io_cq()` stores `cq_interrupt_unmask_register_offset`,
`cq_head_db_register_offset` and `numa_node_register_offset` — all raw
32-bit values from device DMA replies — as register pointers, with **no
bounds validation** (sys/dev/virtual/amazon/ena/ena-com/ena_com.c:1156-1159,
1333-1346). On this DragonFly port `ena_dev->reg_bar` is never assigned
(ena.c:3671-3675 dropped FreeBSD's `reg_bar = reg_bar_h` line), so the
pointers are the raw device offsets, and `ENA_REG_WRITE32`
(ena-com/ena_plat.h:325-329 → bus_space_write_4, sys/cpu/x86_64/include/bus_dma.h:404-410)
expands to a plain CPU store at **(BAR KVA + attacker-chosen u32)**.
Every SQ doorbell (`ena_com_write_sq_doorbell`, called from ena.c:1049,
2967, 2978) and every interrupt unmask (`ena_com_unmask_intr`, called from
ena.c:1813, 2168) then writes attacker-placed kernel addresses. A malicious
"device" (compromised nitro card / hostile hypervisor on the EC2
host-trust boundary) gets a **guest-CPU-executed, arbitrary-offset u32
write** into kernel virtual memory — immune to IOMMU containment because
it is not a device DMA at all.

## Threat model
Amazon ENA is a paravirtual NIC: the peer is the host/nitro card. Same
boundary as vmxnet3. A malicious peer that controls admin-completion
content controls these offsets.

## Reproduce (guest is KVM without ENA hardware — protocol-replica method)
```
# host side
cd findings/poc/DF-3797
tar czf /tmp/enarep.tgz Makefile Makefile.full enarep.c enafull.c ena-com ena-com-fix
scp -F dfbsd-qemu/config /tmp/enarep.tgz dfbsd:/tmp/
# guest side (root)
mkdir -p /root/kmod/enarep && cd /root/kmod/enarep && tar xzf /tmp/enarep.tgz
make                                      # inline replica
kldload ./enarep.ko
sysctl hw.enarep=1     # CQ-unmask write lands on out-of-BAR victim mapping
sysctl hw.enarep=2     # SQ-doorbell value channel (tail counter)
sysctl hw.enarep=3     # FATAL: unmapped-KVA write -> kernel panic
# full-path replica: REAL ena_com_create_io_cq through an emulated admin queue
make -f Makefile.full && kldload ./enafull.ko && sysctl hw.enafull=1
# fix validation
mkdir -p ../fixbuild && cp -r ena-com-fix enafull.c Makefile.full ../fixbuild/
( cd ../fixbuild && make -f Makefile.full FIXED=1 )
kldload ../fixbuild/enafull.ko && sysctl hw.enafull=1   # -> EINVAL, no write
```

## Expected
- `hw.enarep=1/2`: `enarep: after victim ...: <written value>` — an object
  in a kernel mapping *outside* the BAR stand-in is modified by the real
  driver inline functions, offset fully chosen by the emulated device.
- `hw.enarep=3`: `Fatal trap 12: page fault while in kernel mode`,
  `fault code = supervisor write data`, guest down (see panic.txt).
- full-path baseline: `ena_com_create_io_cq() = 0 (io_cq.idx=7
  unmask_reg=0x86fc0008)` then `VICTIM OVERWRITTEN via device-chosen
  register offset — primitive confirmed`.
- fixed build: `ena_com_create_io_cq() = 22 ... NO WRITE PERFORMED (fix
  active)`, guest stays up.

## Fix
`fix.diff` — remember the register BAR extent
(`ena_dev->reg_bar_len = rman_get_size()`, ena.c) and reject CREATE_SQ /
CREATE_CQ replies whose offsets fall outside it. Validated baseline vs
patched on the guest (see VERDICT.md).

## Value/address constraints (honest exploitation assessment)
- unmask / head-db / numa sinks write mostly fixed values
  (`intr_control = 0x40190014` with the driver's constant intervals).
- the SQ-doorbell sink writes `io_sq->tail`, a u16 ring position that the
  peer can advance by an arbitrary amount via delivered TX traffic — a
  groomable small-integer value.
- offset: any u32 in [0, 4G) relative to the register BAR's KVA.
No uid0 chain was built (needs a live malicious device to drive the ring);
the primitive itself — arbitrary-offset kernel write executed by the guest
CPU — is proven end to end.
