DF-4366 / verdict.json
{ "finding_id": "DF-4366", "status": "inconclusive", "reproduced": 0, "impact": "none", "confidence": "certain", "verdict": "Deterministic kernel NULL-deref panic in the pp_power_profile_mode sysfs store handler amdgpu_set_pp_power_profile_mode (sys/dev/drm/amd/amdgpu/amdgpu_pm.c:916-975, the dispatch entry for pp_set_power_profile_mode / amd_powerplay.c:888-910). The CUSTOM-profile token walk copies count-i bytes into buf_cpy[128] with no explicit NUL termination (:944) and, after each strsep with the 3-delimiter set { , \\n, \\0} (:948), unconditionally executes while (isspace(*tmp_str)) tmp_str++; (:955-956). DragonFly strsep (sys/libkern/strsep.c:63-65) sets *stringp = NULL when the remainder holds no delimiter, so any write NOT ending in or \\n - e.g. printf %s \"5 1 2 3\" > pp_power_profile_mode (no echo newline) - makes tmp_str NULL and isspace(*tmp_str) (sys/sys/ctype.h:43) dereferences address 0. Secondary: for i==0 input shapes the copy omits even the accidental sysfs-sentinel termination and the walk continues into uninitialized kernel stack. Mechanism PROVEN live on the guest on the first attempt with a kernel-module replica running the verbatim delimiter walk on the exact byte sequence that write delivers: Fatal trap 12 ... fault virtual address = 0x0 ... Stopped at run_parse.part.1+0xe0: movzbl (%rax),%edx - the faulting instruction is precisely the isspace byte load from NULL. Guest sat in DDB and was reset. Driver-level entry is hardware-gated on this guest (no AMD GPU; attribute exists only on amdgpu devices, S_IWUSR root-only), hence inconclusive per hardware-gated convention; the parse-loop primitive itself is fully reproduced. The sibling handler amdgpu_set_pp_od_clk_voltage (:543-606) copies count+1 bytes (sentinel included) and cannot reach 64 tokens, confirming the profile-mode loop is the defect.", "exploit_chain": "", "evidence": "findings/poc/DF-4366/panic.txt (full serial console: walking 6-byte content, last byte 0x00; Fatal trap 12; fault virtual address 0x0; movzbl (%rax),%edx; db> prompt); build.log (module built clean under -Werror); amdgpu_parser_replica.c (verbatim strsep/isspace walk replica); VERDICT.md; fix.diff", "kernel_refs": [ "sys/dev/drm/amd/amdgpu/amdgpu_pm.c:916", "sys/dev/drm/amd/amdgpu/amdgpu_pm.c:944", "sys/dev/drm/amd/amdgpu/amdgpu_pm.c:948", "sys/dev/drm/amd/amdgpu/amdgpu_pm.c:955", "sys/libkern/strsep.c:63", "sys/sys/ctype.h:43" ], "poc_changes": "No seed PoC. Authored amdgpu_parser_replica.ko (out-of-tree kmodule) because the guest has no AMD GPU: it runs the exact amdgpu_pm.c:946-957 delimiter walk (same 3-delimiter strsep set, same isspace skip, same count-i copy shape) on the bytes printf 5 1 2 3 > would deliver. kstrtol is stood in for by a digit-accepting check so the module does not need the DRM compat headers; the crash is in the delimiter walk, not the numeric conversion.", "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": 420, "guest_dirty": 0, "build_cmd": "cd findings/poc/DF-4366 && ./build.sh (KMOD=amdgpu_parser_replica out-of-tree under bsd.kmod.mk, -Werror)", "run_cmd": "vm.sh run_root kldload /root/kmod/amdgpu_parser_replica.ko && sysctl kern.amdgparun=1 (panics; capture from dfbsd-qemu/boot.log)", "code_hash": "292dd1444cfdcabe64add0b943d6bb06afea3bc1ee5788db7d7fd983ed29b9c9", "notes": "Found during the pass-2 dispatch-surface audit of sys/dev/drm/amd/powerplay/amd_powerplay.c (this is the store entry that feeds pp_set_power_profile_mode). amdgpu_pm.c had ZERO pass-1 findings. amdgpu hardware gated on this guest. Guest reset to with-src snapshot after the panic capture.", "recommended_fix": "NUL-terminate buf_cpy explicitly, bound parameter_size < 64, and guard the skip loop with tmp_str != NULL (fix.diff).", "fix_status": "inconclusive", "fix_baseline_reproduced": 0, "fix_patched_reproduced": 0, "fix_verdict": "fix.diff authored against the read-only sys/ tree (git apply --check clean): explicit buf_cpy termination + parameter_size bound + NULL-guarded isspace skip removes both the NULL deref and the uninitialized-stack walk by construction. Driver-level validation impossible on this guest (no AMD GPU).", "fix_evidence": "findings/poc/DF-4366/fix.diff" } |