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

METEORSSIGNAL stores curproc without reference causing UAF via fork+child-exit+DMA interrupt

Summary

METEORSSIGNAL at cxm.c:2760: sc->enc_proc=curproc with NO PHOLD. Consumed asynchronously from interrupt: ksignal(sc->enc_proc,sc->enc_signal) at :1363-1364 -> phold() -> atomic_add_int(&p->p_lock,1). Only cleared in cxm_close (:2197). fork: child registers via METEORSSIGNAL, child exits, parent keeps fd open -> cxm_close NOT called -> stale struct proc -> next DMA interrupt calls ksignal on freed slab -> UAF write. /dev/cxm0 mode 0444 (unprivileged). Trigger: fork, child METEORSSIGNAL+METEORCAPTUR+exit, parent read() loop. Fix: store pid_t, use pfind()+PRELE() at delivery.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-1295 Β· 8 files
FileTypeDescriptionSize
README.md readme finding summary + UAF trigger 3.1 KB ↓ raw
VERDICT.md verdict mechanism + citations + fix 2.3 KB ↓ raw
fix.diff suggested-fix PHOLD curproc when storing enc_proc; PRELE on clear in ioctl and cxm_close 645 B view raw
build.sh build-script no PoC binary 357 B view raw
run.sh run-script no runtime PoC (cxm absent) 333 B view raw
env.txt environment guest PCI/kld/uname 862 B view raw
../fix_build_combined.log build-log Combined 41-finding kernel build (rc=0, -Werror clean) 5.6 MB ↓ download
../fix_build_summary.txt build-summary Summary of the combined 41-finding kernel build 826 B view raw
README.md readme finding summary + UAF trigger
↓ download raw

DF-1295 β€” cxm METEORSSIGNAL stores curproc without PHOLD β†’ UAF

Finding

cxm_ioctl (METEORSSIGNAL) at sys/dev/video/cxm/cxm.c:2760 stores curproc into sc->enc_proc with no PHOLD:

sc->enc_proc = sig ? curproc : NULL;

The stored pointer is consumed asynchronously from the encoder-DMA interrupt at cxm.c:1363-1364:

if (sc->enc_proc)
    ksignal(sc->enc_proc, sc->enc_signal);

ksignal β†’ lwpsignal (sys/kern/kern_sig.c:1150) does PHOLD(p) which increments p->p_lock β€” a write into the proc struct.

sc->enc_proc is cleared only in cxm_close (cxm.c:2197). The exploitation pattern: a child process registers via METEORSSIGNAL then exits while the parent keeps the device fd open. cxm_close is not called for the child (the fd is still open in the parent), so sc->enc_proc retains a pointer to the child's now-freed struct proc. The next DMA interrupt calls ksignal on the freed slab β‡’ UAF write.

Why we did not reproduce at runtime

The cxm driver attaches only to Conexant CX23416/CX2388x PCI video capture cards. The audit guest has no such device. The driver is built as a module (not in X86_64_GENERIC, not loaded β€” kldstat shows no cxm). Without the device, /dev/cxm0 does not exist and METEORSSIGNAL is unreachable.

The brief claims /dev/cxm0 is mode 0444 β€” that is the default cdev mode for cxm; with the card present, an unprivileged user can open() (read-only satisfies the permission check) and issue METEORSSIGNAL via ioctl. The trigger is therefore plausibly unprivileged when the card is present.

Source-level confirmation

  • cxm.c:2760 β€” sc->enc_proc = sig ? curproc : NULL; β€” no PHOLD.
  • cxm.c:2761 β€” sc->enc_signal = sig;
  • cxm.c:1363-1364 β€” if (sc->enc_proc) ksignal(sc->enc_proc, sc->enc_signal); β€” async consumer.
  • kern_sig.c:1118-1121 β€” ksignal(p, sig) { lwpsignal(p, NULL, sig); }.
  • kern_sig.c:1150 β€” PHOLD(p); inside lwpsignal β€” write to p->p_lock.
  • cxm.c:2197 β€” sc->enc_proc = NULL; only on cxm_close.
  • cxm.h:235 β€” struct proc *enc_proc; β€” raw pointer, no refcount.

The bug is the missing PHOLD/PRELE (or pid_t + pfind) lifecycle. Confirmed real.

Realistic impact ceiling

UAF write (atomic increment of p_lock) on a freed struct proc slab. Could be groomed into a more useful primitive on a non-debug kernel; on default GENERIC (INVARIANTS ON) the slab poisoning (WEIRD_ADDR 0xdeadc0de) would catch the deref of a freed proc and panic before exploitation lands. Preconditions: cxm card present + admin-chowned /dev/cxm0 (or default 0444 sufficient for read-only open) + fork/exit dance. Plausibly unprivileged local β†’ kernel memory corruption when the HW is present.

Fix

fix.diff adds a PHOLD(curproc) when storing and PRELE(sc->enc_proc) when clearing in both cxm_ioctl(METEORSSIGNAL) and cxm_close. This pins the proc struct until the driver is done with it, eliminating the UAF. The alternative (pid_t + pfind/PRELE at delivery) is more invasive; the PHOLD/PRELE pair is the minimal correct fix and matches the pattern used by sigio in kern/kern_sig.c.

VERDICT.md verdict mechanism + citations + fix
↓ download raw

DF-1295 β€” cxm METEORSSIGNAL stores curproc without PHOLD β†’ UAF

Verdict

NOT REPRODUCED β€” real source-level bug confirmed; unreachable on this guest (no Conexant video capture card; cxm module not loaded).

Mechanism (verified)

  • cxm.c:2760 β€” sc->enc_proc = sig ? curproc : NULL; β€” stores raw struct proc * with no PHOLD.
  • cxm.c:1363-1364 β€” if (sc->enc_proc) ksignal(sc->enc_proc, sc->enc_signal); β€” async consumer from encoder-DMA interrupt.
  • kern_sig.c:1118-1120 β€” ksignal β†’ lwpsignal.
  • kern_sig.c:1150 β€” PHOLD(p); inside lwpsignal β‡’ atomic_add_int(&p->p_lock, 1) β‡’ write into the proc slab.
  • cxm.c:2197 β€” sc->enc_proc = NULL; only in cxm_close.
  • cxm.h:235 β€” struct proc *enc_proc; raw pointer, no refcount.

Trigger (when HW present): child registers via METEORSSIGNAL, child exits, parent keeps fd open β‡’ cxm_close not called for the child β‡’ sc->enc_proc retains stale pointer β‡’ next DMA interrupt β‡’ ksignal on freed slab β‡’ UAF write.

Confirmed real. The missing PHOLD/PRELE is the lifecycle defect.

Why not triggered on this guest

  • pciconf -l (env.txt): no Conexant capture device. Only virtio + Intel PIIX3/PIIX4.
  • kldstat -v | grep cxm: empty β€” module not loaded.
  • cxm is not in X86_64_GENERIC (it is a loadable module: sys/dev/video/cxm/Makefile).

Phase 4(d): genuinely not reachable on this kernel. With the card present the trigger is plausibly unprivileged (/dev/cxm0 default mode 0444 β‡’ read-only open succeeds β‡’ ioctl(METEORSSIGNAL) permitted).

Fix

fix.diff: 1. cxm_ioctl(METEORSSIGNAL): PRELE the previous enc_proc if being replaced; PHOLD(curproc) when storing. 2. cxm_close: PRELE(sc->enc_proc) before clearing.

This pins the proc until the driver releases it, closing the UAF. Both PHOLD/PRELE macros (sys/sys/proc.h:482-483) are already in scope via the cxm.c includes.

Fix validation

Compiles cleanly in the unified 5-fix kernel build (fix_build.log). Runtime before/after is not_testable β€” no cxm device.

Realistic impact

UAF write (atomic inc of p_lock) on a freed struct proc slab, gated on cxm HW being present + a fork/exit dance. Likely panic on default GENERIC (INVARIANTS); potentially groomable on a non-debug kernel.

Fix verification

not_testable

compile validated

nativekernel rc=0

Confirmed kernel references

β€”

Detail

Exploit chain

none

Evidence (decisive lines)

β€”

Verdict

Source-confirmed. cxm METEORSSIGNAL stores curproc without PHOLD -> ksignal on freed proc slab UAF. cxm module, no Conexant HW.