β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-5372

_bus_dmamap_sync() switches on a bitmask op: combined BUS_DMASYNC_PREWRITE|BUS_DMASYNC_POSTREAD matches no case and silently performs no bounce copy in either direction

Summary

switch(op) equality dispatch on a bitmask: the legal combined PREWRITE|POSTREAD (0x06) matches no case - no pre-write copy (device DMAs stale cross-buffer bounce contents) and no post-read copy (device data never reaches the client). pc64 replaced the switch with bitmask if-tests; the fork never got it. Proven IN A LIVE VKERNEL: combined op leaves BOTH buffers untouched ('SILENT NO-OP (bug confirmed)') with the bounce page pre-planted; bitmask if-form fix validated ('combined ... honored'). Gated on a bus_dma driver present.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-5372 Β· 13 files
FileTypeDescriptionSize
dfbusdma.c β€” 13.8 KB view raw
build_kernel.sh β€” 958 B view raw
build_image.sh β€” 1.1 KB view raw
run.sh β€” 118 B view raw
fix.diff β€” 1012 B view raw
build.log β€” 934.3 KB ↓ download
fix-build.log β€” 25.0 KB view raw
run.log β€” 3.6 KB view raw
run.fixed.log β€” 3.2 KB view raw
env.txt β€” 348 B view raw
VERDICT.md β€” 2.3 KB ↓ raw
manifest.json β€” 838 B view raw
verdict.json β€” 4.3 KB view raw
VERDICT.md
↓ download raw

DF-5372 VERDICT β€” REPRODUCED (combined bus_dmamap_sync op is a silent no-op on vkernel64)

Root cause

sys/platform/vkernel64/platform/busdma_machdep.c:913-938: _bus_dmamap_sync() dispatches on switch (op). bus_dmasync_op_t is a bitmask (sys/sys/bus_dma.h:118-121), so the legal combined operation BUS_DMASYNC_PREWRITE|BUS_DMASYNC_POSTREAD (0x06) matches no case and both bcopy loops are skipped. pc64's implementation uses if (op & ...) (pc64 busdma_machdep.c ~:1044) β€” the vkernel64 fork predates/misplaced that fix.

Reproduction (VKERNEL64 vkernel, -m 512m, guest DragonFly 6.5-DEVELOPMENT)

Driver (dfbusdma.c, compiled in, SYSINIT): 8192-alignment bounce tag (default hw.busdma.bounce_alignment=1 confirmed = 1), misaligned 64-byte buffer loaded via bus_dmamap_load (bounce page attached, datacount=64 verified), then:

  1. control bus_dmamap_sync(tag, map, BUS_DMASYNC_PREWRITE) β€” client pattern appears in the bounce page ("PREWRITE alone: copy ok").
  2. bug case: bounce page pre-planted with "PLANTED-IN-BOUNCE!!", client buffer set to "COMBINED-OP-PATTERN!!", then bus_dmamap_sync(tag, map, BUS_DMASYNC_PREWRITE|BUS_DMASYNC_POSTREAD). Result: bounce page still holds PLANTED, client still holds PATTERN β€” neither direction copied anything ("SILENT NO-OP (bug confirmed)", "VERDICT PROVEN"). Vkernel stayed ALIVE.

Fix validation

fix.diff converts the switch to bitmask tests (pc64 form). With it applied (and DF-5370's fix, layered earlier): "combined PREWRITE|POSTREAD: honored", "VERDICT NOT PROVEN" β€” i.e. the previously-observed no-op is gone; both copies execute.

Notes

  • No stock VKERNEL64 driver calls bus_dma (vkd/vke/vcd do not), so this is a latent data-integrity bug for any kldloaded bus_dma driver using combined sync ops β€” Low severity.
  • PREWRITE-skip means the device would DMA stale cross-buffer contents from the shared bounce pool (data confusion); POSTREAD-skip means device data never lands (silent corruption).

Fix status (batch validation)

Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.

Fix verification

fixed
baseline reproduced→ patch + rebuild →patched clean

fix.diff applied (bitmask dispatch), VKERNEL64 rebuilt. Baseline: combined op SILENT NO-OP, VERDICT PROVEN (bug present). Patched: 'combined PREWRITE|POSTREAD: honored', VERDICT NOT PROVEN - both copies execute; control PREWRITE still copies; vkernel ALIVE.

run.fixed.log: 'DF5372: combined PREWRITE|POSTREAD: honored' + 'DF5372: VERDICT NOT PROVEN'; fix-build.log full rebuild output; fix.diff git-apply-able (independent hunk)
↓ fix.diffper-fix-DF-5372

Confirmed kernel references

Detail

Exploit chain

bounce-attached map (e.g. 8192-alignment tag under default hw.busdma.bounce_alignment=1, or lowaddr-constrained tag) -> driver issues bus_dmamap_sync(tag, map, BUS_DMASYNC_PREWRITE|BUS_DMASYNC_POSTREAD) -> switch matches no case -> no bcopy in either direction -> device reads stale bounce contents / device writes never copied back (data corruption and cross-buffer data confusion within the vkernel)

Evidence (decisive lines)

run.log (baseline): 'DF5372: PREWRITE alone: copy ok' (control), 'DF5372: combined PREWRITE|POSTREAD: SILENT NO-OP (bug confirmed)', 'DF5372: VERDICT PROVEN - combined op copies nothing'. run.fixed.log (after fix.diff): 'DF5372: combined PREWRITE|POSTREAD: honored', 'DF5372: VERDICT NOT PROVEN'. env.txt: hw.busdma.bounce_alignment=1.

PoC changes

authored fresh (shared rig with DF-5370). Notables: the driver peeks the private struct bounce_page via a mirror (first word of map->bpages.stqh_first -> vaddr) to observe bounce contents directly; DFly STAILQ_HEAD has two pointers (sizeof(bus_dmamap)=72).

Verified recommended fix

Replace switch (op) with bitmask tests: if (op & BUS_DMASYNC_PREWRITE) {pre-write bcopy loop}; if (op & BUS_DMASYNC_POSTREAD) {post-read bcopy loop} - mirrors pc64; see fix.diff (validated)

Verdict

_bus_dmamap_sync() (sys/platform/vkernel64/platform/busdma_machdep.c:903-940) dispatches the sync operation with switch (op), but bus_dmasync_op_t is a bitmask (sys/sys/bus_dma.h:118-121): the legal combined operation BUS_DMASYNC_PREWRITE|BUS_DMASYNC_POSTREAD (0x06) matches no case and BOTH bcopy loops are skipped - a silent no-op that drops the pre-write copy (device then DMAs stale, previously-bounced data from the shared bounce pool) and the post-read copy (device-written data never reaches the client buffer). Proven in a live VKERNEL64 vkernel with a compiled-in driver: control sync(PREWRITE) copies correctly, while sync(PREWRITE|POSTREAD) leaves both the planted bounce-page contents and the client buffer untouched ('SILENT NO-OP (bug confirmed)', 'VERDICT PROVEN'). pc64 fixed this with bitmask if-tests (pc64 busdma_machdep.c ~:1044); the vkernel64 fork never received the change. fix.diff (bitmask form, mirroring pc64) validated: combined op then reports 'honored' and the VERDICT flips to NOT PROVEN (bug gone). Recorded impact dos = strongest demonstrated end state (functional corruption of DMA'd data); no stock VKERNEL64 driver uses bus_dma, so reachability needs a kldloaded bus_dma driver using combined sync ops - Low severity.