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

dcons_crom_detach half-clears the global dcons state: dcons_conf->dma_map is left dangling over the map freed two lines below and the module-static dcons_paddr is never reset β€” a re-attached dcons_crom takes dmamap_cb's 'already set' early-return and can never re-wire dcons_conf, silently disabling DMA sync; memory safety rests solely on the incidental dma_tag!=NULL guards in dcons_os.c

Summary

Only dma_tag is cleared on detach while dma_map dangles and dcons_paddr stays non-NULL - re-attach hits the paddr!=0 early-return leaving dma_tag NULL forever (all bus_dmamap_syncs silently skipped, coherency lost) with the dangling map one refactor away from UAF. Clear-both-globals fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-5118 Β· 6 files
FileTypeDescriptionSize
README.md β€” 599 B ↓ raw
VERDICT.md β€” 2.9 KB ↓ raw
trigger.sh β€” 875 B view raw
env.txt β€” 1.0 KB view raw
fix.diff β€” 520 B view raw
verdict.json β€” 2.6 KB view raw

DF-5118 evidence pack

Finding: dcons_crom_detach half-clears global state β€” only dcons_conf->dma_tag is NULLed; dcons_conf->dma_map is left dangling (map freed two lines later) and dcons_paddr is never reset, so a re-attach takes dmamap_cb's "already set" early-return (dcons_crom.c:139-143) and can never re-wire dcons_conf. Memory safety today rests solely on the dma_tag != NULL guards in dcons_os.c:176-206.

Status: untested / Info β€” needs FireWire hardware (guest has none).

Contents: VERDICT.md, trigger.sh (hardware-gated), env.txt, fix.diff (2-line detach fix).

VERDICT.md
↓ download raw

DF-5118 β€” dcons_crom detach half-clears the global dcons state: dcons_conf->dma_map left dangling, dcons_paddr stale forever, re-attach can never re-wire

Verdict: UNTESTED (needs FireWire hardware) β€” Info/hardening. The state-machine defect is certain in code; memory safety today is only preserved by an unrelated NULL-guard.

Where

The defect

    /* XXX */
    if (dcons_conf->dma_tag == sc->dma_tag)
        dcons_conf->dma_tag = NULL;        /* tag cleared ...      */
                                              /* ... but dma_map NOT */
    bus_dmamap_unload(sc->dma_tag, sc->dma_map);
    bus_dmamap_destroy(sc->dma_tag, sc->dma_map);   /* map freed  */
    bus_dma_tag_destroy(sc->dma_tag);

After detach:

  1. dcons_conf->dma_map is a dangling pointer to freed map memory, while dcons_conf->dma_tag is NULL. Today the only thing preventing a UAF is the if (dg.dma_tag != NULL) guards in dcons_os.c:176-206 β€” one refactor that reorders those checks away from a NULL tag makes every console getc/putc call bus_dmamap_sync(NULL-tag, freed-map).
  2. The module-static dcons_paddr (dcons_crom.c:63) is never reset, and dmamap_cb early-returns when it is already set (dcons_crom.c:139-143): a re-attach of dcons_crom (device re-added under a live firewire bus without module unload) skips dcons_conf->dma_tag/dma_map = ... (:144-145) entirely β€” the dcons core stays with dma_tag == NULL forever β†’ bus_dmamap_sync coherency silently disabled β€” while post_busreset keeps publishing the stale dcons_paddr into the ROM (:120-121). On x86 the stale physical address happens to still denote the same contigmalloc'd buffer (no IOMMU translation), so it is latent rather than actively corrupting; on any translated busdma backend it would advertise a dead mapping.

Fix

Clear both globals and dcons_paddr together in detach β€” fix.diff. With dcons_paddr = 0, the re-attach path re-wires dcons_conf correctly and the early-return retains its intended second-controller semantics.

Why no PoC run

Requires a FireWire controller for dcons_crom to instantiate (guest has none β€” env.txt). Pure state-machine hardening.

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

not_testable
baseline no→ patch + rebuild →patched clean

fix.diff authored statically (2-line detach fix); cannot be validated on this guest - dcons_crom never instantiates without FireWire hardware.

fix.diff; VERDICT.md
↓ fix.diffper-fix-DF-5118

Confirmed kernel references

Detail

Evidence (decisive lines)

VERDICT.md (state trace across detach/re-attach); env.txt (no FireWire hardware on guest)

PoC changes

n/a - no FireWire controller on the guest; trigger.sh documents the hardware-gated sequence

Verified recommended fix

In dcons_crom_detach, clear dcons_conf->dma_map and reset dcons_paddr alongside dcons_conf->dma_tag so re-attach re-wires the globals instead of running on stale state.

Verdict

State-machine defect is certain in code: dcons_crom_detach (dcons_crom.c:199-204) clears only dcons_conf->dma_tag, leaving dcons_conf->dma_map dangling (the map is destroyed two lines below) and the module-static dcons_paddr stale. Because dmamap_cb early-returns when dcons_paddr != 0 (dcons_crom.c:139-143), a re-attach of dcons_crom (device re-added without module unload) never re-wires dcons_conf->dma_tag/dma_map - the dcons core runs with dma_tag==NULL forever (bus_dmamap_sync silently skipped, dcons_os.c:176-206) while post_busreset keeps publishing the stale dcons_paddr into the ConfigROM (dcons_crom.c:120-121). Memory safety today is preserved solely by the dma_tag!=NULL guards in dcons_os.c:176-206 sitting in front of the unguarded dma_map use - a fragile invariant one refactor away from a UAF. Filed as Info hardening; untested because the guest has no FireWire controller (env.txt).