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

aureal: attach failure after mixer_init() leaves the live 0666 /dev/mixerN over the freed au softc β€” DF-4156 mixer-UAF family, 15th instance (premise live-verified on the guest)

Summary

mixer_init publishes the 0666 cdev holding codec->au as devinfo; bus_dma_tag_create and pcm_register can still fail and bad: frees au without mixer_uninit - and DF newbus never detaches a failed attach (premise LIVE-verified on the 6.5 guest with a make_dev-then-ENXIO KLD: node persisted 0666 and unpriv uid=1001 open reached module code). Any later mixer ioctl dispatches through freed softc (UAF r/w). 15th family instance; driver not in default builds. mixer_uninit-in-unwind fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4724 Β· 11 files
FileTypeDescriptionSize
README.md β€” 1.9 KB ↓ raw
VERDICT.md β€” 4.1 KB ↓ raw
premtest.c β€” 3.1 KB view raw
Makefile β€” 125 B ↓ download
build_and_load.sh β€” 283 B view raw
build.log β€” 832 B view raw
run.log β€” 935 B view raw
dmesg_premtest.txt β€” 979 B view raw
env.txt β€” 205 B view raw
fix.diff β€” 1.3 KB view raw
verdict.json β€” 2.9 KB view raw

DF-4724 β€” PoC evidence pack

Finding: aureal attach failure after mixer_init() leaves live 0666 /dev/mixerN over freed au β€” DF-4156 mixer-UAF family, 15th instance.

Contents

  • premtest.c + Makefile + build_and_load.sh β€” premise-verification KLD: a driver whose attach() does make_dev(0666) then returns ENXIO, exactly the aureal mixer_init-success + goto-bad shape. Probe only matches the unbound PIIX4 PM function (0x7113:8086) so it cannot hijack real devices.
  • build.log β€” full guest compiler output (successful KLD build).
  • run.log β€” decisive runtime output: attach failed, node persisted 0666, unprivileged open() reached module code, clean unload.
  • dmesg_premtest.txt, env.txt β€” guest kernel messages / environment.
  • VERDICT.md β€” full narrative with path:line citations.
  • verdict.json β€” machine verdict (status: untested; hw-gated, premise verified).
  • fix.diff β€” git-apply-able fix (mixer_uninit on post-mixer failure paths); authored against the read-only sys/ tree, never applied there.

Reproduce

# on a DragonFly guest with /usr/src:
make -C /tmp/premtest SYSDIR=/usr/src/sys    # build
kldload ./premtest.ko                        # attach runs, then FAILS ENXIO
ls -l /dev/premtest*                         # node persists, crw-rw-rw-
# as an unprivileged user:
sh -c 'exec 3<>/dev/premtest0'; echo $?      # -> 0
dmesg | tail                                 # -> open() reached module code
kldunload premtest                           # cleanup (real driver has none)

Expected

The /dev node survives the failed attach with 0666 perms and dispatches to the module β€” in the real aureal driver those dispatches would dereference the already-freed au softc (au_rdcd/au_wrcd β†’ au_rd/au_wr via au->st[]/au->sh[]), a UAF read/write. The physical AU8820 card required for the end-to-end UAF is not present in QEMU; that part is code-verified only (see VERDICT.md).

VERDICT.md
↓ download raw

DF-4724 β€” aureal: attach failure after mixer_init() leaves live 0666 /dev/mixerN over freed au (mixer-UAF family, 15th instance)

File: sys/dev/sound/pci/aureal.c Β· Severity: Low Β· Bucket: memcorrupt Β· Confidence: certain Family: DF-4156 / DF-4193 / DF-4299 / DF-4334 / DF-4344 / DF-4421 / DF-4457 / DF-4468 / DF-4504 / DF-4537 / DF-4550 / DF-4610 / DF-4651 (13 filed prior; this is the next instance of the same pattern in a driver the family sweep had not yet reached)

Root cause (path:line)

au_pci_attach() publishes the mixer before the last fallible steps and has no cleanup for it on failure:

  • sys/dev/sound/pci/aureal.c:634 β€” mixer_init(dev, ac97_getmixerclass(), codec) succeeds β†’ mixer.c:make_dev(&mixer_ops, ..., UID_ROOT, GID_WHEEL, 0666, "mixer%d", unit) creates a world-writable /dev/mixerN whose mixer object keeps codec (β†’ au) as its devinfo.
  • Two later steps can still fail and goto bad:
  • sys/dev/sound/pci/aureal.c:636-645 β€” bus_dma_tag_create(...) != 0
  • sys/dev/sound/pci/aureal.c:651 β€” pcm_register(dev, au, 1, 1)
  • bad: (sys/dev/sound/pci/aureal.c:658-664) does free(au, M_DEVBUF) and releases bus resources β€” it never calls mixer_uninit(dev)/ac97_destroy(codec).

DragonFly newbus does not run device_detach() when attach returns an error (sys/kern/subr_bus.c:2095-2117, device_doattach() β€” on error it only prints "attach returned %d", clears the driver, and marks the device NOTPRESENT), so nothing anywhere destroys the devfs node or the mixer object.

Impact

After such a failed attach, /dev/mixerN (mode 0666) stays registered forever. Any unprivileged local user opening it and issuing a mixer ioctl dispatches through au_rdcd()/au_wrcd() (aureal.c:126-169) with the freed au as arg: au_wr/au_rd dereference au->st[mapno]/au->sh[mapno] (aureal.c:93-122) β€” freed, heap-groomable kernel memory driving bus_space_{read,write} β€” a UAF read/write primitive (plus the codec object itself is leaked dangling). Ceiling: kernel memory corruption; floor: panic.

Preconditions (why Low, per family convention): an Aureal Vortex 8820 PCI card (PCI ID 0x12eb:0x0001) must be present, the driver must be compiled in β€” note aureal.c is not wired into any build file (sys/conf/files has no entry; sys/dev/sound/driver/Makefile SUBDIR lacks it; no snd_aureal.ko ships in the default build) β€” and one of the post-mixer_init allocations must fail (kmem pressure). Not user-triggerable on demand.

Verification (what ran on the QEMU guest, 2026-09-23)

The full UAF cannot run in QEMU (no Aureal PCI device emulation). The load-bearing premise was verified live on DragonFly 6.5-DEVELOPMENT:

  1. Built a minimal KLD (premtest.c) whose attach() does make_dev(..., 0666, ...) and then returns ENXIO β€” the exact shape of mixer_init()-success followed by goto bad.
  2. kldload β†’ dmesg: PREMTEST: attach() ... FAIL ENXIO / device_probe_and_attach: premtest0 attach returned 6 β€” attach failed.
  3. /dev/premtest0 persisted, crw-rw-rw- root wheel.
  4. As unprivileged uid=1001: exec 3<>/dev/premtest0 β†’ OPEN_RC=0, and the module's d_open handler executed (PREMTEST: open() reached module code) β€” i.e. an unprivileged user reaches module code that, in the real driver, dereferences the freed softc.
  5. Unloaded; guest left clean.

Newbus premise additionally proven from source: sys/kern/subr_bus.c:2095-2117.

On every failure path after mixer_init() succeeds, run mixer_uninit(dev) (which destroys the cdev and the codec via ac97mix_uninit→ac97_destroy) before freeing au. See fix.diff. (First wire the driver into the build or retire it — see notes.)

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 not kernel-validated: the bug itself cannot be triggered on this guest (no Aureal PCI hardware), so no baseline panic/corruption exists to flip.

fix.diff authored against read-only sys/ after line-level verification; not applied to sys/.
↓ fix.diffper-fix-DF-4724

Confirmed kernel references

Detail

Evidence (decisive lines)

run.log (attach returned 6 + persistent crw-rw-rw- node + unpriv OPEN_RC=0 + 'open() reached module code'), VERDICT.md, dmesg_premtest.txt, build.log, premtest.c

PoC changes

n/a (premise KLD written fresh; no seed existed)

Verified recommended fix

Call mixer_uninit(dev) on every failure after mixer_init() succeeds; also unwind in correct order (teardown intr, release irq/regs, free au last)

Verdict

15th instance of the DF-4156 mixer-UAF family: au_pci_attach publishes the 0666 /dev/mixerN (mixer_init, aureal.c:634) before bus_dma_tag_create (aureal.c:636-645) and pcm_register (aureal.c:651) can still fail; the bad: unwind (aureal.c:658-664) frees au with no mixer_uninit, and DragonFly newbus never detaches a failed attach (subr_bus.c:2095-2117), so the node dangles into freed memory and any unprivileged user's mixer ioctl reaches au_rdcd/au_wrcd over the freed au (aureal.c:126-169, 93-122). End-to-end UAF not runnable in QEMU (no Aureal PCI device); the load-bearing premise WAS verified live on the 6.5-DEVELOPMENT guest with a make_dev-then-fail KLD: /dev node persisted 0666 after 'attach returned 6' and unprivileged uid=1001 open() reached module code (see run.log, dmesg_premtest.txt). Note aureal.c is not wired into any build file today (no sys/conf/files entry, not in sys/dev/sound/driver/Makefile SUBDIR, no snd_aureal.ko in default builds), further gating reachability.