Divide-by-zero (kernel panic) in drm_mode_vrefresh: user-supplied umode with htotal*vtotal == 0 (mod 2^32) passes drm_mode_validate_basic and wraps den to zero
Summary
drm_mode_vrefresh computes den = htotal*vtotal in unsigned int; htotal=vtotal=65536 wraps to exactly 0 and the DIV_ROUND_CLOSEST is #DE. validate_basic only checks monotonicity and clock!=0 - no magnitude cap - so the crafted umode is accepted from DRM_IOCTL_MODE_SETCRTC. The trap fires in driver DPM refresh consumers (radeon r600_dpm/rs780_dpm, amdgpu_dpm) running on the user-programmed mode; the same division runs for every probed mode. PROVEN on the guest: verbatim validate_basic ACCEPTS the umode (status=0) and verbatim vrefresh dies SIGFPE at the exact line, -O2 and -O0. Local kernel-panic DoS for a DRM-master holder. u64-widen + USHRT_MAX-reject fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4225 Β· 10 files| File | Type | Description | Size | |
|---|---|---|---|---|
| harness.c | β | 15.3 KB | view raw | |
| dbg.c | β | 931 B | view raw | |
| build.sh | β | 143 B | view raw | |
| run.sh | β | 111 B | view raw | |
| build.log | β | 1022 B | view raw | |
| run.log | β | 1.2 KB | view raw | |
| env.txt | β | 320 B | view raw | |
| VERDICT.md | β | 2.7 KB | β raw | |
| fix.diff | β | 1.7 KB | view raw | |
| verdict.json | β | 3.4 KB | view raw |
DF-4225 β VERDICT
Status: untested (kernel path) / arithmetic PROVEN on guest. Impact ceiling: kernel panic (DoS) on GPU-equipped systems. No memory-corruption primitive (clean #DE).
What was proven, on the guest
- Validation acceptance β the verbatim
drm_mode_validate_basic()(drm_modes.c:1102-1130) returnsMODE_OKfor the crafted umode {clock=108000, h: 1,2,3,65536 / v: 1,2,3,65536, flags=0, vscan=0} βrun.loglineumode validate_basic status=0. The kernel's only guards are monotonicity +clock != 0; no magnitude cap exists betweendrm_mode_convert_umode()(drm_modes.c:1691-1751) and the mode being programmed. - The trap β verbatim
drm_mode_vrefresh()then computesden = 65536*65536which wraps to 0 inunsigned int, andDIV_ROUND_CLOSEST(num, 0)raises SIGFPE (exit=136) immediately after thedrm_modes.c:796marker, at -O2 and -O0 (guestcc 8.3 [DragonFly], same integer ABI as the guest kernel). In-kernel this idiv is the same #DE β panic.
Why the kernel path itself is untested here
The QEMU guest has no DRM device (vgapci0 chip=0x11111234 Bochs VGA; no
/dev/dri), so DRM_IOCTL_MODE_SETCRTC cannot be issued and no
radeon/amdgpu DPM consumer exists. The cited consumers are:
sys/dev/drm/drm_crtc.c:655 (legacy setcrtc umode intake) β
sys/dev/drm/radeon/r600_dpm.c:195, rs780_dpm.c:66,
sys/dev/drm/amd/amdgpu/amdgpu_dpm.c:178 (drm_mode_vrefresh(&hw_mode) on
the user-programmed mode), plus drm_probe_helper.c:526 (vrefresh fill for
every probed mode). All are source-verified; none instantiable on this VM.
Chain (on real hardware)
open /dev/dri/card0 β DRM_IOCTL_SET_MASTER β DRM_IOCTL_MODE_SETCRTC
{mode_valid=1, modeinfo{htotal=65536, vtotal=65536, monotonic 1/2/3 chain,
clock=108000}} β drm_crtc.c:655 drm_mode_convert_umode β MODE_OK β
modeset commits β radeon DPM work β r600_dpm.c:195
drm_mode_vrefresh(&hw_mode) β drm_modes.c:796 num/0 β #DE β panic.
Fix validation
fix.diff widens num/den to u64 (kills the wrap) and adds a USHRT_MAX
magnitude cap in drm_mode_validate_basic() (matches the boundary the WARN at
drm_modes.c:1634-1639 already anticipates). Kernel-level fix boot test is
not possible on this guest (no DRM); the u64 math eliminates the wrap by
construction ((u64)65536*65536 = 2^32 β 0).
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.
Fix verification
not_testablePatched-kernel test impossible on this guest (no DRM hardware). fix.diff removes the wrap by construction ((u64)65536*65536 = 2^32 != 0) and the validate_basic cap rejects the umode before it can be programmed; harness-level equivalence: with den computed in u64 the harness case-5 division returns 0 instead of trapping.
fix.diff; run.log validate_basic acceptance demonstrates the exact acceptance path the cap closes
Confirmed kernel references
Detail
Exploit chain
open /dev/dri/card0 -> SET_MASTER -> DRM_IOCTL_MODE_SETCRTC{mode_valid=1, modeinfo htotal=65536 vtotal=65536, monotonic 1/2/3 chains, clock=108000} -> drm_crtc.c:655 drm_mode_convert_umode -> MODE_OK (no magnitude cap) -> modeset commit -> radeon DPM: r600_dpm.c:195 drm_mode_vrefresh(&hw_mode) -> drm_modes.c:796 num/0 -> #DE -> panic.
Evidence (decisive lines)
run.log: 'umode validate_basic status=0' then '[marker] VREFRESH: drm_modes.c:796' then 'Floating point exception (core dumped)' exit=136 (both -O2 and -O0); harness.c case 5; env.txt documents absent DRM device.
PoC changes
self-authored (case 5 of the DF-4224 harness, shared verbatim extracts); htotal/vtotal read from argv so GCC cannot fold the wrap
Verified recommended fix
Widen num/den to u64 in drm_mode_vrefresh (drm_modes.c:784-787) and add a USHRT_MAX magnitude cap for h/v timings in drm_mode_validate_basic(), matching the boundary drm_mode_convert_to_umode already WARNs about.
Verdict
Kernel path untestable on this guest (no DRM device; DRM_IOCTL_MODE_SETCRTC cannot be issued), but both halves of the bug are proven on the guest toolchain: (1) verbatim drm_mode_validate_basic() ACCEPTS the crafted umode {clock=108000, h 1/2/3/65536, v 1/2/3/65536} with MODE_OK β the function only checks monotonicity, never magnitude; (2) verbatim drm_mode_vrefresh() then computes den = 65536*65536 which wraps to 0 in unsigned int and DIV_ROUND_CLOSEST(num,0) raises SIGFPE (exit=136) at drm_modes.c:796, at -O2 and -O0. On real hardware the mode enters via drm_crtc.c:655 (setcrtc umode) and the trap fires in the radeon/amdgpu DPM vrefresh consumers (r600_dpm.c:195, rs780_dpm.c:66, amdgpu_dpm.c:178) β local kernel-panic DoS for a DRM master. No memory corruption.
No comments yet.