DF-4828 / verdict.json
{ "finding_id": "DF-4828", "status": "inconclusive", "reproduced": 0, "impact": "none", "confidence": "certain", "verdict": "Driver path hardware-gated on this guest (no radeon GPU, no /dev/dri - pciconf shows only QEMU std VGA), so r600_audio_update_hdmi can never run here; the type confusion is nonetheless proven statically with exact lines (atombios_encoders.c:2860-2872 installs a 280-byte radeon_encoder_lvds as enc_priv for LCD-carrying KLDSCP_LVTMA/UNIPHY/TMDS1/LVTM1 digital encoders; radeon_encoders.c:423-443 is_digital returns true for them) and the corruption PRIMITIVE is proven live on the guest kernel: the audio IH work (r600.c:4341/evergreen.c:4900 -> r600_audio_update_hdmi walk at r600_hdmi.c:132) calls r600_hdmi_buffer_status_changed (r600_hdmi.c:283-291) and r600_hdmi_update_audio_settings (:425-435) on those encoders; dig->afmt at +288 reads the ADJACENT 288-byte slab chunk (kmalloc(280) has zero slack per kern_slaballoc.c zoneindex), a KLD reproduced the exact accesses - the OOB read returned a groomed non-NULL pointer, the guard at r600_hdmi.c:286 fell through, and last_buffer_filled_status was written THROUGH the OOB-read pointer (observed 0->1->0 in a controlled fake afmt), plus dig->afmt->offset (0x1234) would feed WREG32(HDMI0_*+offset) MMIO. New sinks + interrupt-driven trigger vs DF-4635 (which covered radeon_audio.c detect/enable via connector probe); not the DF-1911 walk race - no race, no modeset, no user gesture needed beyond audio activity on r600/evergreen-era LVDS/eDP hardware.", "exploit_chain": "(hardware-gated, not runnable here) groom the shared 288-byte slab size class so a chosen victim object follows the lvds enc_priv allocation -> audio interrupt status change schedules r600_audio_update_hdmi -> walk hits the LCD-carrying digital encoder -> OOB read dig->afmt = victim[0..8) -> if non-NULL: wild read (->enabled), wild write (last_buffer_filled_status at +8 of whatever victim[0..8) points to), and offset-driven wild MMIO (RREG32/WREG32 HDMI0_*+offset) -> NULL neighbor silently returns (hides the bug), garbage neighbor pointer = panic, groomed victim = controlled deref+write primitive -> crash-to-UAF escalation path; deterministic per audio status flip on affected laptops.", "evidence": "run.log (KLD: usable chunk=288, adjacent pair B-A=288, OOB READ dig->afmt(+288)==groomed ptr non-NULL, last_buffer_filled_status written 0->1->0 through it (r600_hdmi.c:291), NULL case silent, update_audio_settings replica dig->afmt->offset==0x1234); build.log (offsets: lvds=280 atom_dig=312 afmt@+288 + KLD build); fix_build.log (whole radeon.ko rebuilds -Werror with fix.diff, r600_hdmi.o linked); VERDICT.md full path citations", "kernel_refs": [ "sys/dev/drm/radeon/r600_hdmi.c:283-291", "sys/dev/drm/radeon/r600_hdmi.c:425-435", "sys/dev/drm/radeon/r600_hdmi.c:271-274", "sys/dev/drm/radeon/r600_hdmi.c:132-137", "sys/dev/drm/radeon/atombios_encoders.c:2860-2872", "sys/dev/drm/radeon/radeon_encoders.c:423-443", "sys/dev/drm/radeon/radeon_mode.h:427-445", "sys/dev/drm/radeon/r600.c:4341", "sys/dev/drm/radeon/evergreen.c:4900", "sys/kern/kern_slaballoc.c:638" ], "poc_changes": "wrote afmt_walk.c KLD (verbatim replica of r600_hdmi_buffer_status_changed on the live allocator with groomable adjacent victim) + r600_hdmi_offsets.c (verbatim struct defs); no seed PoC. fix.diff generated with real diff -u and compile-validated by a full radeon.ko rebuild (-Werror).", "attempts": 1, "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly v6.5.0.1712.g89e6a-DEVELOPMENT #1 x86_64 (kernel build DragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC)", "runtime_sec": 1200, "guest_dirty": 0, "build_cmd": "guest: cd /tmp/df4828 && cc -O -o r600_hdmi_offsets r600_hdmi_offsets.c && make (KLD: afmt_walk.c)", "run_cmd": "guest: kldload -v /tmp/df4828/afmt_walk.ko && kldunload afmt_walk (fix: cd /usr/src/sys/dev/drm/radeon && patch -p1 < fix.diff && make)", "code_hash": "384c9260f55487c88f67e76fcabcfc78fb6096fb91c6f2312b6af4805eb7316d", "notes": "Guest reset (with-src) performed after the run. r600_hdmi_audio_workaround (r600_hdmi.c:299-316) has the same unguarded dig->afmt->offset but is dead code (declared radeon_asic.h:407, never called). r600_hdmi_enable (:487) also loads dig->afmt OOB but is only reachable via radeon_audio_dpms which is encoder_mode-gated to HDMI/DP (atombios_encoders.c:1847-1850) - the mis-typed LVDS-mode encoders never take it. Upstream Linux code is byte-identical at the sinks (latent upstream; cross-object effect is DragonFly-specific via 32-byte slab granularity).", "recommended_fix": "In r600_hdmi_buffer_status_changed() and r600_hdmi_update_audio_settings(), early-return unless (dig && encoder->encoder_type == DRM_MODE_ENCODER_TMDS) - LCD-carrying digital encoders are LVDS-registered with a lvds-typed enc_priv; see fix.diff", "fix_status": "not_testable", "fix_baseline_reproduced": 0, "fix_patched_reproduced": 0, "fix_verdict": "fix.diff applies clean (patch -p1, hunks at 283/439) and the ENTIRE radeon.ko module rebuilds with -Werror on the guest toolchain including the patched r600_hdmi.o (fix_build.log, exit 0). Behavioral validation impossible on this guest: no radeon GPU, so r600_audio_update_hdmi (the only caller of both patched functions) never executes here; the KLD primitive harness is driver-path-independent by construction (it validates the geometry, not the gate).", "fix_evidence": "fix.diff; fix_build.log (patched r600_hdmi.c compiled -Werror, radeon.ko linked exit 0)" } |