# DF-4919 VERDICT — REPRODUCED (posthumous work execution after cancel_work_sync() returned; panic proof; UAF-class contract violation)

## The defect (source-level)

* `process_all_work()` — linux_workqueue.c:74 sets `on_queue=false`
  (dequeue) and only :87 sets `running=true`, the whole transition under
  `worker_lock`; `queue_work()` publishes `on_queue/worker` under the
  target worker's lock (:134-146).
* `_cancel_work()` reads both flags **unlocked** (:349, :364) and
  `flush_work()` likewise (:331).  A load pair straddling either mutation
  window yields `(on_queue==false && running==false)` → **return while
  `work->func()` is about to execute**.
* The API contract above `cancel_work_sync()` (:390-394) explicitly
  permits freeing the work after return → the caller frees, the worker
  calls a function pointer out of freed/stack-reused memory.  (Linux
  makes these races coherent with the atomic WORK_STRUCT_PENDING claim;
  the shim has no equivalent and the flags are guarded by different or no
  locks.)

## Reproduction on the audit guest (DragonFly 6.5-DEVELOPMENT, 6 CPUs)

Kernel module `df4919.ko` (drm.ko provides the shim): a feeder thread
keeps exactly one work instance in flight (quiescent per instance:
publish → wait for resolution → grace), two canceller threads hammer
`cancel_work_sync()`.  The work function samples the `alive` flag — which
each canceller clears *after* its `cancel_work_sync()` returns — **at
entry**.  `alive==0` at entry means a cancel returned before the function
started for an instance that was published: posthumous execution.

Evidence chain (all logs in this pack):

1. **Baseline hit (stock drm.ko)**: `any_hit=1` — first posthumous
   execution after ~174k cancel ops (run.log, quiescent-feeder version),
   and the final harness produced 6+ posthumous entries in one 90 s run
   (run.log tail, HIT ring lines with the `5:0` = func-entry-with-alive=0
   event adjacent to cancel false-returns).
2. **Kernel-level crash proof (twice)**: `sysctl hw.df4919_test=2` →
   `panic: DF-4919: work executed AFTER cancel_work_sync()/flush_work()
   returned — process_all_work called func on work the caller was told is
   dead (linux_workqueue.c:74 vs :364)` — trace `wk_func() <-
   wq_worker_thread()`, cpuid 1 (panic.txt).  In a real caller this
   moment is the UAF detonation: `work->func(work)` on memory the caller
   was contractually allowed to free (heap kfree or dead stack frame).
3. **Narrowness measurements** (honesty): a strictly single-driver
   variant (queue fully completes, then one cancel — only the ~2 ns
   dequeue→start transition is exposed) logged **zero** hits in ~150k
   cycles: that specific window is narrower than the canceller's
   load-to-load gap on this CPU.  The readily-hit window is the insert
   publication vs the unlocked reads (concurrent `queue_work()` vs
   `cancel_work_sync()` on the same work — a pattern Linux handles
   coherently via the PENDING bit and in-tree DRM drivers do use).  Both
   windows are the same root cause and the same contract violation.

## Fix validation (fix.diff: pending-claim protocol + flags spinlock)

`fix.diff` spans `include/linux/workqueue.h` (adds
`volatile u_int wq_claim` to `struct work_struct`; INIT_WORK clears it)
and `linux_workqueue.c`:

* `queue_work()` atomically **claims** the right to make the work pending
  before taking any lock (Linux WORK_STRUCT_PENDING equivalent);
* the worker's dequeue→start transition and post-run flag updates, and
  the insert publication, run under a `work_flags_lock` spinlock (lock
  order: worker_lock → work_flags_lock), and the claim is released at
  `running=true` / on removal / on the drop paths;
* `_cancel_work()`'s and `flush_work()`'s idle verdicts require both the
  claim (cmpset) and coherent flag reads — an idle verdict is no longer
  renderable while an insert or the transition is in flight.

Applied to the guest's /usr/src, drm.ko rebuilt, identical harness:

* baseline (stock): multiple posthumous executions per 90 s run; the
  panic variant crashes the guest in seconds-to-minutes of hammering.
* patched: **no classifier-confirmed hit in 2.8M-3.5M cancel operations
  across repeated 90 s runs** (run_fixed.log; the residual single
  `real=1` per multi-million ops is at the harness's own measurement
  noise floor — pre-claim verdict stores skewing past the feeder's
  fenced snapshot; the same classifier also flags noise on stock).  The
  guest stayed healthy through full-length hammering runs, and the shim's
  normal operation (runs/cancels/queues counters) kept functioning.

## Exploitability note

The demonstrated primitive is the contract violation itself: work runs
after the caller was told it is dead.  The escalation to arbitrary call
is caller-pattern-dependent (caller must free or leave-stack after the
sync-cancel/flush — exactly what drm_framebuffer.c:475-481 does after
`flush_work()` on an `INIT_WORK_ONSTACK` work reached from the
unprivileged DRM_IOCTL_MODE_RMFB path).  No end-to-end uid=0 chain was
built for this shim bug in this run: the racing window at its widest is
the insert-publication window, requiring a concurrent queuer; the
sequential-only path (stack work + flush_work as in rmfb) rides the ~2 ns
transition window, measured effectively un-hittable at ~150k samples.
Impact therefore recorded as the demonstrated panic-class manifestation
with a UAF/arbitrary-call ceiling.

## Kernel references

* sys/dev/drm/linux_workqueue.c:74,87 (locked dequeue→start transition)
* sys/dev/drm/linux_workqueue.c:134-146 (insert publication under worker_lock)
* sys/dev/drm/linux_workqueue.c:331 (flush_work unlocked flag loop)
* sys/dev/drm/linux_workqueue.c:349,364 (_cancel_work unlocked reads/return)
* sys/dev/drm/linux_workqueue.c:390-394 (the free-after-cancel contract)
* sys/dev/drm/drm_framebuffer.c:475-481 (stack work + flush_work, unpriv ioctl)
* sys/dev/drm/i915/i915_irq.c:2952-2961,3180 (stack delayed_work cancel site)

## 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.
