DF-3699 / verdict.json
{ "finding_id": "DF-3699", "status": "inconclusive", "reproduced": 0, "impact": "none", "confidence": "likely", "verdict": "Source-certain memory-corruption race, runtime-untestable on this guest (no Intel GPU, no i915 module). DragonFlys lkpi queue_work() (sys/dev/drm/linux_workqueue.c:120-148) selects the worker by the callers cpu and takes only that per-cpu workers lock while test-and-setting the shared work->on_queue/running/canceled fields; system_wq has ncpus workers (linux_workqueue.c:176,209-213), so two CPUs enqueueing the same work item can both see on_queue==false and both STAILQ_INSERT_TAIL it into two different lists that alias one ws_entries.stqe_next link -> cross-list traversal, double/ghost execution, work->func(work) on mistyped objects. Unprivileged trigger: DRM_IOCTL_I915_GEM_WAIT on an in-flight request -> gen6_rps_boost -> schedule_work(&rps->work) (intel_pm.c:6671, i915_gem.c:515-517) racing the RPS hardirqs schedule_work(&rps->work) (i915_irq.c:1838). The trigger compiled cleanly on the guest (BUILD_OK in build.log) and correctly failed with no /dev/dri/card* (run.log): the guest VGA is Bochs 0x1234:1111 and /boot/kernel has only drm.ko. On real SNB+ SMP hardware this is a plausible unprivileged kernel-corruption primitive; the insert-vs-insert window is one store-visibility interval, so reliability is moderate.", "exploit_chain": "unprivileged GPU client submits batch + GEM_WAIT loop -> schedule_work(&rps->work) on user cpu while RPS MSI fires on irq cpu -> double STAILQ insert of one work_struct -> system_wq list aliasing -> corrupted list head -> work->func indirect call on mistyped object / double free of adjacent work items (grooming-dependent) -> crash (DoS) at minimum, RIP-control potential", "evidence": "VERDICT.md sections 1-3; linux_workqueue.c:129-148 annotated interleaving; build.log (BUILD_OK on DragonFly 6.5 cc); run.log (FAIL: no /dev/dri/card0 - device absent); env.txt (hw.ncpu=6, Bochs VGA, drm.ko only)", "kernel_refs": [ "sys/dev/drm/linux_workqueue.c:120", "sys/dev/drm/linux_workqueue.c:129", "sys/dev/drm/linux_workqueue.c:138", "sys/dev/drm/linux_workqueue.c:71", "sys/dev/drm/i915/i915_irq.c:1838", "sys/dev/drm/i915/i915_irq.c:1829", "sys/dev/drm/i915/intel_pm.c:6671", "sys/dev/drm/i915/i915_gem.c:515" ], "poc_changes": "seed was prose-only; wrote raw-ioctl trigger (no libdrm), fixed DFly build: added stdint typedefs for __u32/__u64, removed __user annotation, added <time.h> include", "attempts": 2, "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64", "runtime_sec": 480, "guest_dirty": 0, "build_cmd": "cc -O2 -Wall -o rps_queue_race rps_queue_race.c", "run_cmd": "./rps_queue_race 60", "code_hash": "80090b117f7398e63a3a9ce4e66d0883e616b7479c9de95204c060f32bad45b4", "notes": "Guest is virtio/Bochs only - no i915 hardware or module exists; verification limited to build + presence checks + source trace. Negative-result proof for the stack-UAF variant via delayed work: this branchs callout_cancel is synchronous (kern_timeout.c _callout_cancel_or_stop sync=1), so __init_wedge/__fini_wedge (i915_irq.c:2944-2961) is NOT exploitable via the callout path.", "recommended_fix": "Bind work items to one worker in lkpi queue_work() (queue to work->worker once set) or add a per-workqueue enqueue lock so cross-CPU enqueues of one work_struct cannot double-insert into two STAILQs.", "fix_status": "not_testable", "fix_baseline_reproduced": 0, "fix_patched_reproduced": 0, "fix_verdict": "fix.diff authored (VERDICT.md section 5): bind work->worker; cannot runtime-validate on a guest without i915 hardware", "fix_evidence": "findings/poc/DF-3699/VERDICT.md#5-minimal-patch" } |