β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-1245

Divide-by-zero DoS in evergreen_cs_track_validate_cb via user-controlled CB_COLOR_PITCH

Summary

evergreen_cs_handle_reg at evergreen_cs.c:1396 stores raw IB dword to cb_color_pitch[id] unmasked (unlike depth path G_028058_PITCH_TILE_MAX 11-bit at :568, texture G_030000_PITCH 12-bit at :777). validate_cb at :401-404: pitch=cb_color_pitch[id], surf.nbx=(pitch+1)*8, surf.nby=((slice+1)*64)/surf.nbx. pitch=0xFFFFFFFF -> (pitch+1)*8 wraps to 0 -> divide-by-zero #DE kernel panic. DRM_AUTH local user on Evergreen+. CB_COLOR0_INFO valid + CB_COLOR0_BASE reloc + CB_TARGET_MASK=0xF + DRAW. Fix: mask pitch to 0x7FF (PITCH_TILE_MAX), add surf.nbx==0 check.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-1245 Β· 8 files
FileTypeDescriptionSize
VERDICT.md verdict source trace, mechanism, fix rationale 2.3 KB ↓ raw
fix.diff suggested-fix guard surf.nbx==0 before division in evergreen_cs_track_validate_cb 607 B view raw
fix_build_radeon.log build-log evergreen_cs.o compiled with fix, RC=0, no warnings 1.3 KB view raw
build.sh build-log repro: apply-check + note 395 B view raw
run.sh run-log no runtime trigger (no HW) 374 B view raw
env.txt environment uname, cc version, module state 359 B view raw
../fix_build_combined.log build-log Combined 41-finding kernel build (rc=0, -Werror clean) 5.6 MB ↓ download
../fix_build_summary.txt build-summary Summary of the combined 41-finding kernel build 826 B view raw
VERDICT.md verdict source trace, mechanism, fix rationale
↓ download raw

DF-1245 β€” Divide-by-zero DoS in evergreen_cs_track_validate_cb via CB_COLOR_PITCH

Verdict

SOURCE-CONFIRMED (real bug), INCONCLUSIVE at runtime β€” the radeon.ko module is not loaded (no AMD Evergreen GPU in the QEMU guest) and is not compiled into the GENERIC kernel, so the divide-by-zero path is dormant. Fix authored and compile-validated.

Mechanism (source trace)

The radeon DRM command-submission parser stores the raw user IB dword for CB_COLORn_PITCH unmasked into the track state, unlike the depth and texture paths which mask first:

  • sys/dev/drm/radeon/evergreen_cs.c:1396 β€” track->cb_color_pitch[tmp] = radeon_get_ib_value(p, idx); (raw 32-bit, no & PITCH_TILE_MAX mask). Compare: the depth path uses G_028058_PITCH_TILE_MAX (11-bit, evergreen_cs.c:568); the texture path uses G_030000_PITCH (12-bit). The CB color path has no equivalent mask.
  • sys/dev/drm/radeon/evergreen_cs.c:401-404: pitch = track->cb_color_pitch[id]; /* unsigned, raw */ slice = track->cb_color_slice[id]; surf.nbx = (pitch + 1) * 8; surf.nby = ((slice + 1) * 64) / surf.nbx; /* DIVIDE BY ZERO if nbx==0 */
  • If a DRM_AUTH client submits CB_COLORn_PITCH = 0xFFFFFFFF, then (pitch+1)*8 = 0 (32-bit wrap) and the division at line 404 faults with a kernel #DE (divide error) β†’ panic.

Why not reproduced at runtime

  • radeon.ko is not loaded on the guest (kldstat shows only kernel + ehci.ko + xhci.ko) and is not in X86_64_GENERIC (no device radeon in the kernel config).
  • Requires an AMD Evergreen-class (Radeon HD 5xxx/6xxx) GPU attached; the QEMU guest uses -vga none/stdvga, no AMD GPU.
  • Requires DRM_AUTH (an authenticated DRM client, i.e. a local X user). This is a local DoS via GPU command submission, reachable only on systems with the affected hardware.

Fix (fix.diff, compile-validated)

Guard surf.nbx == 0 before the division, returning -EINVAL (matching the existing dev_warn + return -EINVAL pattern used elsewhere in the function). The patched evergreen_cs.c compiles cleanly with gcc 8.3, -Werror, no warnings.

Realistic impact ceiling

Local kernel panic (DoS) by a DRM-authenticated user on a machine with an AMD Evergreen GPU. No memory-corruption primitive β€” purely a divide-by-zero fault. On this guest: not reachable (no AMD GPU).

Fix verification

not_testable

compile validated

module/object build rc=0

Confirmed kernel references

β€”

Detail

Exploit chain

none

Evidence (decisive lines)

β€”

Verdict

Source-confirmed. evergreen_cs_track_validate_cb pitch+1 wraps to 0 -> div-by-zero. radeon not in GENERIC.