# DF-4623 — virtqueue used-ring completion ownership not validated (duplicate/foreign id)

- **File**: `sys/dev/virtual/virtio/virtio/virtqueue.c`
- **Class**: memcorrupt (host-trust, same threat model as DF-1767 but a distinct
  failure mode: an *in-bounds* duplicate/foreign used-ring id — no OOB involved)
- **Kernel**: DragonFly 6.5-DEVELOPMENT X86_64_GENERIC (INVARIANTS on), guest boots
  on virtqueue (vtblk0 root disk, vtnet0)

## What it demonstrates

The virtqueue engine trusts used-ring completion ids written by the host with
only debug-time (`VQASSERT`/KASSERT) enforcement. A single forged used-ring
entry — a **duplicate** of an already-completed chain head (perfectly valid
bounds-wise, so DF-1767's index problem does not apply) — drives
`vq_ring_free_chain()` into:

1. `dxp->ndescs--` underflow (`0 -> 65535`, uint16_t) at virtqueue.c:786-787
2. re-linking the freed chain tail onto the free-list head → **cycle** in
   `desc[].next` (virtqueue.c:803-804)
3. on INVARIANTS kernels: `panic: vq_ring_free_chain: ... failed to free entire
   desc chain` (virtqueue.c:796); on production kernels: silent corruption —
   free-list cycle makes `vq_desc_head_idx` never advance, every subsequent
   `virtqueue_enqueue()` re-hands in-flight descriptors and overwrites
   `vq_descx[head].cookie` (completions mis-attributed to the wrong driver
   object), `vq_free_cnt` drifts past `vq_nentries`
4. with `F_NEXT` forged on the cycle tail, the `while (dp->flags &
   VRING_DESC_F_NEXT)` walk at virtqueue.c:790-794 never terminates → kernel
   spins forever holding the driver serializer → hard hang.

## Build (in guest, as root — pack copied to /root/df4623)

    sh /root/df4623/build.sh          # generates device_if.h/bus_if.h, kld

## Run

Baseline (stock kernel):

    kldload /root/df4623/df4623.ko                    # safe mode: sanity + projection
    kenv hw.df4623.fire=1
    kldload /root/df4623/df4623.ko                    # FIRE -> panic (see panic.txt)

Fixed kernel (after applying fix.diff + nativekernel + installkernel + reboot):

    kenv hw.df4623.fire=1
    kldload /root/df4623/df4623.ko                    # -> "dropping bogus used entry id=0", NO panic

## Expected

- Baseline fire: `panic: vq_ring_free_chain: df4623-probe - failed to free
  entire desc chain`, backtrace `virtqueue_dequeue → vq_ring_free_chain`,
  guest wedged in ddb (ssh dies).
- Patched fire: `vtblk0: df4623-probe: dropping bogus used entry id=0`,
  ring state stays consistent (free=64=head=0), legit completions still
  return the right cookie, module unloads cleanly.

The module uses the kernel's own `virtqueue_alloc()` on the real vtblk0 child
device (feature negotiation is live) with an unregistered queue index, and
writes used-ring entries exactly the way the host does via DMA. No real disk
I/O is involved.
