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

dce110_timing_generator_validate_timing: h_blank/h_back_porch u32 underflow bypasses the minimum checks; no vertical checks -> program_blanking programs wrapped (masked) blanking registers

Summary

validate_timing computes h_blank and h_back_porch with u32 subtractions that wrap on inconsistent timings: h_total < h_addressable+borders makes h_blank ~4G (passes the min check), h_sync_width > h_front_porch makes h_back_porch wrap (passes its check). No vertical checks at all. The accepted timing is later programmed by program_blanking whose identical math wraps and is masked into the H/V BLANK registers. All sinks are masked MMIO to constant addresses - not kernel memory corruption; impact ceiling is an inconsistent CRTC timing on real ASIC (display corruption; speculative pipe hang). drm helpers normally pre-validate userspace modes; this is the defense-in-depth layer being bypassed. Reject-underflow fix (compile-validated with -Werror amdgpu.ko build).

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4091 Β· 7 files
FileTypeDescriptionSize
README.md β€” 3.2 KB ↓ raw
VERDICT.md β€” 2.9 KB ↓ raw
env.txt β€” 1.4 KB view raw
fix.diff β€” 1.1 KB view raw
fix_build.log β€” 1.2 MB ↓ download
manifest.json β€” 709 B view raw
verdict.json β€” 2.8 KB view raw

DF-4091 β€” dce110_timing_generator_validate_timing underflow lets inconsistent timings pass; program_blanking programs wrapped blanking values

File: sys/dev/drm/amd/display/dc/dce110/dce110_timing_generator.c (validate :1109-1163; sink program_blanking:596-697) Class: CWE-191 integer underflow in the display timing gatekeeper (checklist item (d): bounds rejects) Severity: Low β€” pass 2 (GLM 5.3), bucket base:dev Confidence: certain (arithmetic); hardware-gated impact

The bug

dce110_timing_generator_validate_timing() is the CRTC-level gatekeeper β€” called for every stream via dc_validate_stream() (core/dc_resource.c:2873). Two of its computations wrap on u32:

h_blank = h_total - h_addressable - h_border_right - h_border_left;  /* :1144 */
if (h_blank < tg110->min_h_blank) return false;                      /* passes if wrapped */
...
h_back_porch = h_blank - (h_front_porch - h_sync_width);             /* :1154-1157, wraps */
  • If h_total < h_addressable + borders, h_blank underflows to ~4 G and sails through the < 56 check.
  • If h_sync_width > h_front_porch, h_back_porch underflows and passes < 4.
  • There are no vertical checks at all (no v_addressable fit, no v-porch minimums).

The accepted timing is later programmed by dce110_timing_generator_program_blanking() / program_timing_generator():

tmp = timing->h_total - (h_sync_start + timing->h_border_left);  /* :656-657 */
...
tmp = timing->v_total - (v_sync_start + timing->v_border_top);    /* :679 */

both of which wrap the same way; set_reg_field_value_ex() (dm_services.h:133-141) masks the wrapped value into the field, so the CRTC receives garbage (truncated-to-13/16-bit) H/V blank start/end values.

Impact ceiling (honest)

  • Not kernel memory corruption: every value is field-masked into fixed MMIO registers computed from constant register offsets (CRTC_REG/DCP_REG); no pointer or length is derived from timing data anywhere in this file (no division exists either β€” the DF-3828 watermark div0 class is absent here).
  • Worst realistic effect: an inconsistent CRTC timing (blanking window that does not exist inside the total) is programmed β€” display corruption, loss of sync, or a wedged display pipe; a hang would be a system-level DoS (speculative β€” no ASIC available to test).
  • Userspace normally cannot smuggle such a mode past DRM-core mode validation (hdisplay ≀ htotal etc.), but dc_validate_stream exists as defense-in-depth and is bypassable by any internal producer that adjusts timings (e.g. DRR/FreeSync v_total adjustments in set_drr:366-468, which itself only masks values).

Hardware-gated: the audit guest has no AMD ASIC (env.txt), so no runtime verification was possible; source-level certainty only, matching the dce110 family checklist instruction.

Fix

fix.diff β€” reject timings whose active+borders do not fit inside the totals (h and v), reject zero addressable, and guard the back-porch computation against its underflow before it happens. Compile-validated: the whole amdgpu.ko module (which compiles this file) rebuilt with -Werror after applying the diff (BUILD_EXIT=0, fix_build.log).

VERDICT.md
↓ download raw

DF-4091 VERDICT

Status: untested β€” hardware-gated (no AMD ASIC on the audit guest; vgapci0 ... chip=0x11111234 is QEMU std VGA, env.txt). Source-level certainty, per the dce110-family checklist instruction. Confidence: certain (arithmetic defects); impact ceiling speculative (no hardware to observe a misprogrammed CRTC).

What was checked (whole file, 2261 lines)

  • (a) program_timing / program_blanking arithmetic (:281-353, :596-697): all h/v sync-start and blank-start/end computations can wrap (u32) on inconsistent timings, but every sink is set_reg_field_value_ex (dm_services.h:133-141) which masks to the register field width before dm_write_reg to a constant address (CRTC_REG/DCP_REG + fixed offsets). No kernel-memory effect is reachable from timing values. No division exists in the file (DF-3828 watermark-div0 class absent).
  • (b) set_drr / vtotal bounds (:366-468): vertical_total_max/min zero-checked (:387-389); -1 values masked into fields. Benign.
  • (c) test pattern generators (:699-1102): fixed arrays src_color[6]/dst_color[6], loops bounded by 6; shifts src_bpc - dst_bpc ∈ {6,8,10}, 16 - dst_bpc ∈ {6,8,10}; no UB.
  • (d) validate_timing (:1109-1163): the finding β€” h_blank (:1144-1146) and h_back_porch (:1154-1157) underflow past their minimum checks; zero vertical checks. Gatekeeper called at core/dc_resource.c:2873 (dc_validate_stream).
  • (e) DCCG/clock source: not touched by this file (no clock-source interaction code present) β€” n/a.
  • (f) globalsync / crc (:1214-1382, :2094-2197): check_point - FLIP_READY_BACK_LOOKUP (:1300) can underflow only if the CRTC V_TOTAL register reads < 3 (hardware state, not user timing) β€” masked anyway. CRC window values are pure masked field writes (:2116-2157). disable_vga (:1797-1836) leaves addr = 0 on an unknown controller_id, but dce110_timing_generator_construct:2245 derives the id as CONTROLLER_ID_D0 + instance with instance bounded by the resource pool (≀ 6 pipes) β€” default case unreachable in practice.

Verification performed

  • No amdgpu device in the guest β‡’ no runtime run; the module builds and the file's logic is fully source-traceable (all sinks masked register writes).
  • fix.diff applied to the guest /usr/src; the complete amdgpu module (which compiles dce110_timing_generator.c) rebuilt with -Werror and linked amdgpu.ko (BUILD_EXIT=0; fix_build.log β€” full untrimmed build log). /usr/src restored pristine afterwards; guest reset with the clean-source snapshot.

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_testable
baseline no→ patch + rebuild →patched clean

Applied to the guest /usr/src; the complete amdgpu module (which compiles dce110_timing_generator.c) rebuilt with -Werror and linked amdgpu.ko (BUILD_EXIT=0, full log in fix_build.log). Runtime fix validation not testable: no AMD ASIC on this guest.

findings/poc/DF-4091/fix.diff, findings/poc/DF-4091/fix_build.log
↓ fix.diffper-fix-DF-4091

Confirmed kernel references

Detail

Evidence (decisive lines)

VERDICT.md (full 6-item checklist), fix.diff, fix_build.log (full untrimmed amdgpu -Werror build log, amdgpu.ko linked)

PoC changes

n/a (untested)

Verified recommended fix

Reject timings whose active+borders exceed h_total/v_total and guard the back-porch subtraction in dce110_timing_generator_validate_timing.

Verdict

Low - hardware-gated, source-certain (per the dce110-family checklist instruction). dce110_timing_generator_validate_timing (:1109-1163, the CRTC gatekeeper called from dc_validate_stream at core/dc_resource.c:2873) computes h_blank (:1144-1146) and h_back_porch (:1154-1157) with u32 subtractions that wrap on inconsistent timings, so a timing with h_total < h_addressable+borders or h_sync_width > h_front_porch passes the minimums; there are no vertical checks at all. program_blanking (:656-657, :679) wraps identically and set_reg_field_value_ex (dm_services.h:133-141) masks the wrapped values into the H/V blank registers. All sinks in the file are masked MMIO writes to constant addresses; no kernel memory corruption and no division exist in the file. Impact ceiling: garbage CRTC timing on a real ASIC (display corruption / speculative pipe hang). Full-file checklist results in VERDICT.md (set_drr, test patterns, GSL/CRC, disable_vga all bounded/masked). Fix authored; the whole amdgpu.ko module rebuilt -Werror clean with it applied.