# DF-4741 VERDICT — drm_sched_fini leaves armed TDR / pending jobs on teardown

## Status: REPRODUCED (panic via softclock on freed scheduler; fix validated)

## Root cause

`drm_sched_fini()` — sys/dev/drm/scheduler/sched_main.c:652-656:

```c
void drm_sched_fini(struct drm_gpu_scheduler *sched)
{
	if (sched->thread)
		kthread_stop(sched->thread);
}
```

It neither cancels `sched->work_tdr` (armed by `drm_sched_start_timeout`
whenever jobs sit on `ring_mirror_list`, sched_main.c:194-199) nor drains the
job list, and takes no reference-participation in pending `finish_work`
items. Every in-tree caller frees the scheduler's container right after:
`amdgpu_fence_driver_fini()` calls `drm_sched_fini(&ring->sched)`
(amdgpu_fence.c:509) and the enclosing `struct amdgpu_ring` is released with
the device during teardown. The pending delayed work / callout then executes
against freed memory.

## Reproduction (guest, harness test 2 — df4740.c)

From a fresh reset (panic.txt):

1. Scheduler initialized with a 2 s timeout; a hung job sits on
   `ring_mirror_list`, so `job_begin` armed `work_tdr` (sched_main.c:246).
2. `drm_sched_fini()` called (thread stopped; work_tdr STILL armed).
3. Scheduler `kfree()`d and the block reclaimed with zeroed allocations
   (reclaim block 0 landed exactly on the freed scheduler address) — the
   stand-in for amdgpu freeing `struct amdgpu_ring`.
4. ~2 s later the callout fired on the freed object:
   `panic: _callout ... verifier ... (expected ...) ... softclock_handler`
   — guest down at db>.

Even when the callout verifier does not trip first, the queued
`drm_sched_job_timedout` work item itself runs on freed memory (list walk,
`lockmgr(&sched->job_list_lock)` inside the freed block, `ops->timedout_job`
indirect call) — the same class with RIP-control-grade consequences on this
guest.

Severity context: teardown is reached via driver unload/rebind (privileged)
or device removal/reset-failure paths, hence Medium rather than High; the
panic and the freed-memory execution are unconditional once fini runs with a
pending TDR.

## Fix

`fix.diff` hunk 3 (shared with DF-4740): `cancel_delayed_work_sync(&sched->work_tdr)`
after `kthread_stop` in `drm_sched_fini`. Validated: patched amdgpu.ko, test 2
re-run → no callout fire, no panic, guest stays up (run_fixed.log). A full
upstream-grade fix should additionally drain `ring_mirror_list` (signal +
free leftover jobs) as Linux 5.x does.

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