DF-4262 / verdict.json
{ "finding_id": "DF-4262", "status": "untested", "reproduced": 0, "impact": "none", "confidence": "certain", "verdict": "Code-certain unprivileged kernel NULL-deref panic: amdgpu_cs_parser_init() returns early SUCCESS for num_chunks==0 (amdgpu_cs.c:114-115) before job/ctx/entity are set, so amdgpu_cs_ib_fill() falls straight through its skipped loop to `to_amdgpu_ring(parser->entity->rq->sched)` (amdgpu_cs.c:1048) with parser->entity==NULL (zero-init at amdgpu_cs.c:1280) -> kernel load from 0x10 -> fatal trap. AMDGPU_CS is DRM_AUTH|DRM_RENDER_ALLOW (amdgpu_kms.c:1206) so any local render-node user triggers it with one ioctl. Runtime reproduction on this audit guest is impossible: QEMU has no AMD GPU (vgapci chip 0x11111234; amdgpu.ko loads but binds nothing; /dev/dri absent - env.txt/run.log), so status=untested (hardware-gated, not a code dead-end: no device-dependent branch between :114 and :1048). Fix compiles in-tree with -Werror.", "exploit_chain": "N/A - NULL-page deref (offset 0x10) is not mappable by userland on DragonFly; impact ceiling is local DoS/panic.", "evidence": "cs_zero_chunks.c (self-contained trigger); run.log (open /dev/dri fails ENOENT - absent HW); env.txt (pciconf/kldstat); kmod_baseline_build.log + fix_build.log (amdgpu_cs.c compiles rc=0 with -Werror before and after fix.diff); VERDICT.md has the full line-cited chain.", "kernel_refs": [ "sys/dev/drm/amd/amdgpu/amdgpu_cs.c:114", "sys/dev/drm/amd/amdgpu/amdgpu_cs.c:1280", "sys/dev/drm/amd/amdgpu/amdgpu_cs.c:1296", "sys/dev/drm/amd/amdgpu/amdgpu_cs.c:991", "sys/dev/drm/amd/amdgpu/amdgpu_cs.c:1048", "sys/dev/drm/amd/amdgpu/amdgpu_job.c:55", "sys/dev/drm/include/drm/gpu_scheduler.h:81", "sys/dev/drm/amd/amdgpu/amdgpu_kms.c:1206" ], "poc_changes": "Trigger written from scratch (no seed): self-contained UAPI structs because the guest has no drm user headers; ioctl nr computed via _IOWR(d, 0x40+0x04, 24-byte union).", "attempts": 1, "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": 90, "guest_dirty": 0, "build_cmd": "cc -O2 -Wall -o cs_zero_chunks cs_zero_chunks.c [in-tree fix-compile: cd /usr/src/sys/dev/drm/amd/amdgpu && make KERNBUILDDIR=/usr/obj/usr/src/sys/X86_64_GENERIC amdgpu_cs.o]", "run_cmd": "./cs_zero_chunks", "code_hash": "0f3488034305ef8c710c327a40816facc97f56ea7d7d18618c41f398582e4fe4", "notes": "poc status untested = runtime verification blocked by absent AMD GPU on the audit guest (would be not_testable if the schema allowed it). Trigger build + run + baseline/patched in-tree compiles all exercised on the guest; guest reset with-src before return. amdgpu.ko kldload experiment: module loads, binds nothing, no /dev/dri created.", "recommended_fix": "Reject CS submissions with zero chunks: return -EINVAL at amdgpu_cs.c:114 instead of early success.", "fix_status": "not_testable", "fix_baseline_reproduced": 0, "fix_patched_reproduced": 0, "fix_verdict": "fix.diff (one-line -EINVAL) applied to guest /usr/src via patch -p1 (hunk @112) and the patched amdgpu_cs.c compiled in-tree with -Werror rc=0 (fix_build.log). Runtime before/after comparison impossible on this guest (no AMD GPU, baseline PoC cannot execute), so fix validation is compile-only; the change is provably sufficient by inspection: the early return is the sole path that leaves parser->entity NULL into amdgpu_cs_ib_fill().", "fix_evidence": "fix_build.log (patched compile rc=0); kmod_baseline_build.log (unpatched compile rc=0); guest patch output in task log: Hunk #1 succeeded at 112." } |