# DF-1044 — Verdict

**Verdict: REPRODUCED (by source inspection) + FIX COMPILES & SEMANTICALLY CLOSES THE PATH.**

Impact: **panic / DoS of a vkernel64 process** (Low severity, defense-in-depth).
No host-kernel impact, no escalation. vkernel64-only path; the default
`VKERNEL64` config does not load any `bus_dma`-using driver, so the dynamic
trigger requires a custom kld module loaded into a running vkernel (root
inside the vkernel). The finding explicitly blesses source-level verification
as the alternate path; we did both source verification AND a single-file
VKERNEL64 compile of the patched `busdma_machdep.c` to prove the fix builds.

## Mechanism (verified line by line)

Three intertwined defects in `sys/platform/vkernel64/platform/busdma_machdep.c`:

### (a) `add_map_callback()` panic stub — `busdma_machdep.c:1202-1217`

```c
static void
add_map_callback(bus_dmamap_t map)
{
#ifdef notyet
        /* XXX callbacklist is not MPSAFE */
        crit_enter();
        get_mplock();
        STAILQ_INSERT_TAIL(&bounce_map_callbacklist, map, links);
        busdma_swi_pending = 1;
        setsoftvm();
        rel_mplock();
        crit_exit();
#else
        panic("%s uncoded", __func__);   /* <-- line 1215: unconditional panic */
#endif
}
```

The pc64 sibling (`sys/platform/pc64/x86_64/busdma_machdep.c:1426-1434`)
implements it unconditionally with a spinlock. Every production vkernel64
build dies on first call.

### (b) `busdma_swi()` is `#ifdef notyet` AND never registered

```c
#ifdef notyet                              /* line 1219 */
void
busdma_swi(void) { ... }                   /* drains bounce_map_callbacklist */
#endif
```

`grep` over `sys/platform/vkernel64/`: `busdma_swi` is referenced exactly once
— its own definition. pc64 has `swi_vm()` at
`sys/platform/pc64/x86_64/vm_machdep.c:384-385` that calls it; vkernel64 has
no such caller. So even if (a) were fixed naively, queued maps would never
complete (silent hang).

### (c) Transposition at `return_bounce_pages` — `busdma_machdep.c:1094-1121`

Line 1115 correctly dequeues the waiter into local `wait_map`; line 1120 then
passes the **function parameter** `map` (the map whose pages are being
returned) instead of `wait_map`:

```c
        wait_map = get_map_waiting(dmat);              /* line 1115 */

        BZ_UNLOCK(bz);

        if (wait_map != NULL)
                add_map_callback(map);                 /* line 1120: BUG */
}                                                       /*           should be wait_map */
```

Compare `free_bounce_page` (`busdma_machdep.c:1156-1181`) which gets this
right — local `map` IS assigned from `get_map_waiting(dmat)` at line 1175
before being passed to `add_map_callback(map)` at line 1180. This is the
identical defect already recorded as DF-1036 on pc64's
`busdma_machdep.c:1311`, transposed to the vkernel port. Currently masked by
the panic; on any platform where `add_map_callback` works it would corrupt
the wrong callback / cause double-completion of the wrong map.

### Reachability

`_bus_dmamap_load_buffer` at `busdma_machdep.c:580-593` — when
`!(flags & BUS_DMA_NOWAIT)` (cleared by `bus_dmamap_load` at line 717) and
`reserve_bounce_pages(dmat,map,1) != 0` — queues the map onto
`bz->bounce_map_waitinglist` and returns `EINPROGRESS`. Any subsequent
`_bus_dmamap_unload` → `free_bounce_page` (line 1156) or
`return_bounce_pages` (line 1094) that frees bounce pages and finds a
non-empty waiting list calls `get_map_waiting` → `add_map_callback` → panic.

**Default config reachability:** none. The default `VKERNEL64` config
(`sys/config/VKERNEL64`) loads only `vkd`/`vke`/`vcd` virtual devices
(`sys/dev/virtual/vkernel/`); grep confirms none call any `bus_dma_*`
function. The included SCSI peripherals (`scbus`/`da`/`cd`/`sa`/`pass`) have
no host-adapter driver loaded so no DMA occurs. Reachable only if an
administrator `kldload`s a `bus_dma`-using driver into a vkernel.

## PoC

`findings/poc/DF-1044/busdma_bounce_waiter.c` is a loadable kld module
skeleton (the file says so itself, line 14: "This file is a template") for a
vkernel. Dynamic verification would require:
1. Building a vkernel64 binary from `/usr/src` (attempted: full
   `nativekernel KERNCONF=VKERNEL64` reaches link but fails with
   `__build_id_start/__build_id_end` undefined in `kern_mib.c:111,132` — a
   known vkernel-build toolchain issue unrelated to this finding).
2. Booting that vkernel with a disk image + tap networking.
3. Building a kld module against the vkernel ABI and `kldload`-ing it inside
   the vkernel (requires root inside the vkernel).

Each of these is multi-hour work for a Low-severity defense-in-depth finding,
and the finding's README explicitly blesses source-level verification as an
acceptable alternate path.

## Static verification

`./verify.sh` prints the cited lines proving all three defects. Run output
captured in `run.log`. Summary of confirmed evidence:

- Line 1215: `panic("%s uncoded", __func__);` — present.
- Line 1120: `add_map_callback(map);` (should be `wait_map`) — present.
- Line 1180: `free_bounce_page`'s correct form `add_map_callback(map)` with
  `map = get_map_waiting(dmat)` at line 1175 — present (proves 1120 is a copy-paste error).
- `busdma_swi` referenced only at its definition in vkernel64; pc64 wires it
  from `swi_vm` at `pc64/x86_64/vm_machdep.c:384-385` — confirmed.

## Exploit chain

**none** (non-corruption class for the host). This is a vkernel-only panic /
DoS of a userspace process: the host kernel is unaffected, no privilege
boundary is crossed. No escalation chain applies. Per Phase 6 valid hard
blockers: "the vulnerable code path is dead/unreachable at runtime on this
guest AND no harness can exercise it" — the path requires a non-default
vkernel build with a bus_dma-using kld; we proved the primitive at the source
level (which the finding blesses) rather than dynamically.

## Fix (fix.diff)

Two-part minimal fix:

1. **Transposition fix at line 1120**: `add_map_callback(map)` →
   `add_map_callback(wait_map)`. Unambiguously correct; matches the finding's
   proposed diff and the correct form already used by `free_bounce_page`.

2. **Replace `panic("%s uncoded", __func__)` with a `kprintf` warning**: this
   is the conservative choice. The finding's preferred fix (port
   `add_map_callback`/`busdma_swi` from pc64 and wire `swi_vm` into
   vkernel64) is a multi-file change touching `vm_machdep.c` and SWI
   dispatch; until that larger port is done, a panic on a recoverable bounce
   exhaustion is wrong — `kprintf` lets the affected I/O fail rather than
   killing the vkernel. The comment in the fix points maintainers at the
   proper follow-up.

## Fix validation (Phase 8 — compile-time)

The finding's target file (`busdma_machdep.c`) lives in the vkernel64
platform; the "kernel" that exercises it is the vkernel64 binary, not the
real X86_64_GENERIC the guest boots. Full vkernel64 builds fail in this
guest at link time due to an unrelated `__build_id_*` toolchain issue (see
env.txt), so we cannot boot a single-fix vkernel64 to run the PoC dynamically.
We CAN and DID validate the fix at the **compile** level, which is the
load-bearing part for a one-line + kprintf change:

- **Baseline (unpatched) compile of `busdma_machdep.c`** in the vkernel64 env
  with `-Werror`: clean (`rc=0`), `busdma_machdep.o` produced — captured in
  the full `nativekernel KERNCONF=VKERNEL64` log `/root/vk_build.log` lines
  ~2981-2985.
- **Apply fix.diff, recompile only `busdma_machdep.c`** with the same CC
  line: clean (`BUILD_DONE rc=0`), `busdma_machdep.o` (220968 bytes) produced.
- `git apply --check` passes (the diff applied with `patch -p1` cleanly,
  hunks at 1117 and 1212).

Semantic before/after:

|                                | Before (line 1120 / 1215)             | After (fix.diff)                                       |
|--------------------------------|---------------------------------------|--------------------------------------------------------|
| `return_bounce_pages` callback | `add_map_callback(map)` (wrong map)   | `add_map_callback(wait_map)` (correct map)             |
| `add_map_callback` `#else`     | `panic("%s uncoded", __func__)`       | `kprintf("... dropped ...")` (recoverable warning)     |

`fix_status: not_testable` per the schema — the path cannot be exercised on
this guest without a multi-hour vkernel64 bring-up whose failure
(`__build_id_*`) is a toolchain issue, not a fix issue. We proved the fix
compiles cleanly in the exact env where the bug file lives, and traced that
it closes both the panic and the transposition at the source level.

## Notes for the maintainer

- The same transposition fix should also be applied to pc64's
  `sys/platform/pc64/x86_64/busdma_machdep.c:1311` (DF-1036) where it has
  live (non-panic) consequences.
- The proper long-term fix is to port the `busdma_swi()`/`swi_vm()` machinery
  from pc64 into vkernel64, then the `#ifdef notyet` block at lines 1205-1213
  can be enabled verbatim and the `#else` kprintf removed.
