WARN-only range guards: silent 64-bit wrap in bw_int_to_fixed_nonconst / bw_frc_to_fixed / bw_mul in release builds
Summary
All ASSERT guards are WARN_ON in release - |value|>=2^39 shifts bits out, the long-division loop wraps mod 2^64, and bw_mul computes ~2^78 products before any assert evaluates. Pure arithmetic (no memory access - OOB/UAF impossible by construction); ceiling is corrupted DML watermarks -> display underflow/GPU hang on AMD DCE hw. Upstream carries the same pattern; saturate/clamp fix needs an upstream decision.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-5159 Β· 5 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 934 B | β raw | |
| VERDICT.md | β | 3.2 KB | β raw | |
| env.txt | β | 987 B | view raw | |
| verdict.json | β | 3.7 KB | view raw | |
| manifest.json | β | 1006 B | view raw |
DF-5159 β build/run
No PoC program exists: the affected functions (bw_int_to_fixed_nonconst,
bw_frc_to_fixed, bw_mul) are internal helpers of the AMD DC display
bandwidth calculator compiled into the drm amdgpu module. The audit guest has
no AMD DC GPU (virtio/QXL VGA, chip=0x11111234) and no drm module loaded, so
there is no runtime entry point (see env.txt). The defect β WARN-only range
guards with silent u64/int64 wrap β is established by reading the code against
the ASSERT definition in os_types.h:76.
- Expected on matching hardware (AMD DCE GPU, mode set with pathological timings): dmesg WARN_ON splats from bw_fixed.c plus mis-programmed watermarks; no panic, no memory corruption.
- Artifacts: VERDICT.md (full narrative + path:line), env.txt, verdict.json.
- No fix.diff: behavior-changing DML arithmetic needs upstream validation (see VERDICT.md); recommended_fix carries the one-line direction.
DF-5159 β bw_fixed.c range guards are ASSERT()=WARN_ON only: silent 64-bit wrap in release builds
File: sys/dev/drm/amd/display/dc/calcs/bw_fixed.c (pass 2, GLM 5.3)
What
Every range/overflow guard in this translation unit is ASSERT(expr), which in
this tree expands to WARN_ON(!(expr)) (sys/dev/drm/amd/display/dc/os_types.h:76;
only CONFIG_DEBUG_KERNEL_DC builds upgrade it to ASSERT_CRITICAL). In a normal
release kernel, WARN_ON logs a splat and execution continues with the
wrapped value β the guards protect nothing:
bw_int_to_fixed_nonconst(bw_fixed.c:49-55):res.value = value << 24with|value| >= 2^39(BW_FIXED_MAX_I32, bw_fixed.h:40) shifts bits out of the int64 β silent wrap (and signed-shift UB for negatives).bw_frc_to_fixed(bw_fixed.c:57-110):abs_i64(INT64_MIN)(bw_fixed.c:41-47):-argoverflows int64 β UB / wraps back to 0x8000000000000000.- The 24-iteration long-division loop
res_value <<= 1(bw_fixed.c:86) wraps mod 2^64 once the accumulated quotient exceeds 64 bits (integer part assert at bw_fixed.c:76 is WARN-only). res.value = -res.value(bw_fixed.c:108) on an already-wrapped MIN.bw_mul(bw_fixed.c:143-191):res.value = arg1_int * arg2_int(bw_fixed.c:161) with integer parts up to 2^39-1 each produces up to ~2^78 β unconditional u64 wrap before the first assert (bw_fixed.c:163) even evaluates; the three subsequent summand asserts (169/175/184) are WARN-only.
Consequence: corrupted fixed-point values propagate into the DCE bandwidth calculations (watermarks, latency hiding) and are programmed into display hardware registers β wrong display configuration, WARN splats, potential underflow/GPU-hang behavior. No kernel memory unsafety is possible: the file performs pure arithmetic (no pointers, arrays, copies), so OOB/UAF/leak classes are structurally absent; the ceiling is wrong-value propagation.
This is distinct from the known DF-2155 (unguarded div0 panics in bw_frc_to_fixed/bw_floor2/bw_ceil2, lines 69-132), which is NOT re-reported.
Reachability
Inputs derive from validated display-mode timings, ATOM-BIOS tables and DML constants β not directly from unprivileged memory. No caller on the audit guest: the box has a virtio/QXL VGA device (0x1234:1111), no AMD DC GPU, and no drm modules loaded (env.txt). Severity Info (hardening), CWE-190.
Fix direction (not shipped as a diff)
Convert the asserts into saturating clamps (return Β±BW_FIXED_MAX_I32-shaped
values) or use full 64x64β128 multiplication and range-check before truncating;
upstream amdgpu carries the same WARN-only pattern, so semantics need an
upstream decision. fix.diff intentionally omitted: changing DML arithmetic
semantics with no hardware to validate against would be riskier than the defect.
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.
(No fix.diff for this item: documented recommendation only - see Fix above.)
Fix verification
not_testableNo fix.diff authored: changing DML arithmetic semantics (saturation behavior) has no validation target on this guest (no AMD DC hardware) and upstream amdgpu carries the same WARN-only pattern β an upstream decision is needed. Baseline was likewise not reproducible here.
VERDICT.md 'Fix direction' section documents why no diff is shipped for this Info hardening finding.
Confirmed kernel references
- sys/dev/drm/amd/display/dc/calcs/bw_fixed.c:53
- sys/dev/drm/amd/display/dc/calcs/bw_fixed.c:41
- sys/dev/drm/amd/display/dc/calcs/bw_fixed.c:76
- sys/dev/drm/amd/display/dc/calcs/bw_fixed.c:86
- sys/dev/drm/amd/display/dc/calcs/bw_fixed.c:161
- sys/dev/drm/amd/display/dc/calcs/bw_fixed.c:163
- sys/dev/drm/amd/display/dc/os_types.h:76
- sys/dev/drm/amd/display/dc/inc/bw_fixed.h:40
Detail
Evidence (decisive lines)
VERDICT.md (per-function wrap analysis with path:line); os_types.h:76 (#define ASSERT(expr) WARN_ON(!(expr))) is the decisive fact that turns the assert-annotated math into silently-wrapping math in release builds; env.txt documents guest has no AMD DC hardware / drm modules => no runtime entry.
PoC changes
No PoC exists: helpers are internal to the amdgpu DC module; the audit guest has no AMD DC GPU and no drm module loaded (env.txt), so the code has no reachable runtime entry on this machine.
Verified recommended fix
Replace WARN-only ASSERTs with saturating clamps (or 64x64->128 multiply + range check before truncation) in bw_int_to_fixed_nonconst / bw_frc_to_fixed / bw_mul.
Verdict
Code-level weakness established with certainty by inspection, but not demonstrable on the audit guest. Every range guard in bw_fixed.c is ASSERT(), which expands to WARN_ON (os_types.h:76) β advisory only in release kernels. Consequences: bw_int_to_fixed_nonconst (bw_fixed.c:53) silently shifts bits out for |value| >= 2^39; bw_frc_to_fixed's long-division loop (bw_fixed.c:84-93) wraps res_value mod 2^64 with only WARN at bw_fixed.c:76/100, and abs_i64(INT64_MIN) at bw_fixed.c:41-47 is signed-negation UB; bw_mul (bw_fixed.c:161) computes arg1_int*arg2_int with operands up to 2^39-1 (~2^78 product) β unconditional u64 wrap before the first assert even evaluates, and the later summand asserts (163/169/175/184) are WARN-only. The wrapped values feed DML watermark programming: wrong display config and WARN splats at worst β the file is pure arithmetic with no pointers, arrays or copies, so kernel memory unsafety is structurally impossible. The guest has no AMD DC GPU and no drm module loaded (env.txt), so there is no runtime entry point; rated Info hardening (CWE-190), distinct from known DF-2155 (div0 family, not re-reported).
No comments yet.