DF-4546 / verdict.json
{ "finding_id": "DF-4546", "status": "reproduced", "reproduced": 1, "impact": "panic", "confidence": "certain", "verdict": "lm_detach() (lm78.c:445-458) unconditionally calls sensordev_deinstall() on a ksensordev that was never installed: wb_match() claims the W83791SD without a sensor table (lm78.c:590-592, the only chip case with no lm_setup_sensors), so numsensors==0 makes lm_attach() return early (lm78.c:427-429) before sensordev_install(). sensordev_deinstall (kern_sensors.c:153-176) runs TAILQ_REMOVE (sys/sys/queue.h:646-662) on the zeroed never-inserted node: head->tqh_last = NULL (global sensordev_list corrupted) followed by *(tqe_prev==NULL) = tqe_next -- an immediate NULL-pointer WRITE, i.e. a kernel panic on any later driver detach (kldunload/devctl) of the bound lm(4) device. Reproduced with a macro-faithful lifted-copy harness (verbatim TAILQ_REMOVE from queue.h, struct ksensordev per sensors.h:156-166, zeroed softc): BUG case faults writing to 0x0 inside the deinstall; CONTROL (numsensors=19) and PATCHED (fix.diff guard) detach cleanly. In-kernel path hardware-gated (no LM78-class chip on the guest); trigger is privileged detach on rare hardware -> Low severity, root-triggered DoS.", "exploit_chain": "", "evidence": "run.log (BUG case: >>> SIGSEGV: kernel-equivalent NULL-pointer WRITE at 0x0 inside sensordev_deinstall; CONTROL + PATCHED clean); run.2.log (repeat); fix_build.log (kernel-toolchain -fsyntax-only of patched lm78.c: PATCHED_RC=0, deliberate-error negative control NEGCTL_RC=1); fix.diff (git apply --check OK)", "kernel_refs": [ "sys/dev/powermng/lm/lm78.c:590", "sys/dev/powermng/lm/lm78.c:427", "sys/dev/powermng/lm/lm78.c:450", "sys/kern/kern_sensors.c:153", "sys/kern/kern_sensors.c:160", "sys/sys/queue.h:646", "sys/sys/sensors.h:156", "sys/dev/powermng/lm/lm78var.h:126" ], "poc_changes": "authored fresh this run (pass 2); no seed PoC existed", "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": 180, "guest_dirty": 0, "build_cmd": "sh build.sh (cc -O2 -Wall -o lm_detach_nullwrite lm_detach_nullwrite.c on the guest)", "run_cmd": "sh run.sh (./lm_detach_nullwrite)", "code_hash": "2efe60dd3d3e7e77e4d78e9061d741869c72f6fd87686a3d9562507cada2ddef", "notes": "macro-level lifted-copy harness: the sensor task framework itself is NOT racy here (sensor_task_thread runs st->func holding thr->lock, kern_sensors.c:307-341, so sensor_task_unregister blocks and no post-unregister refresh can run -- negative-checked this pass). Same zero-sensor shape would fire for any future match entry added without a sensor table.", "recommended_fix": "guard lm_detach with if (sc->numsensors == 0) return 0; (fix.diff) -- nothing was installed, so nothing may be deinstalled", "fix_status": "not_testable", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_verdict": "Patched kernel not runnable against real W83791SD hardware on the guest (no LM78-class chip). fix.diff is git-apply-able, compiles clean under the kernel toolchain in the X86_64_GENERIC objdir (-fsyntax-only, RC=0, with a deliberate-error negative control failing RC=1), and the harness PATCHED control detaches the zero-sensor device without a fault.", "fix_evidence": "fix.diff; fix_build.log (PATCHED_RC=0, NEGCTL_RC=1); run.log PATCHED case (clean detach)", "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #0 x86_64 (kernel objdir syntax validation)" } |