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

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)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-5159 Β· 5 files
FileTypeDescriptionSize
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.
VERDICT.md
↓ download raw

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 << 24 with |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): -arg overflows 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_testable
baseline no→ patch + rebuild →patched clean

No 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.
per-fix-DF-5159

Confirmed kernel references

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).