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

Unbounded flicker-filter table index: a non-matching flicker_removal exits the SLOPE_limit walk at i==5 and reads SLOPE_value[5]/YCOEF_value[5]/YCOEF_EN_value[5] one element past the [5]-element arrays (latent; unreachable with current constants)

Summary

The match loop has no out-of-loop bound; on no-match the index is 5 and the subsequent table uses fold garbage longs into three MMIO register values. flicker_removal always yields 2 or 3 from the static mode rows, so the OOB is unreachable today - it becomes live the moment anyone adds a mode row or tweaks the constants. Defense-in-depth. Clamp-the-index fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4565 Β· 3 files
FileTypeDescriptionSize
README.md β€” 1.6 KB ↓ raw
trigger.c β€” 1.3 KB view raw
verdict.json β€” 1.9 KB view raw

PoC DF-4565 β€” latent one-past-the-end array index in the TV flicker filter

sys/dev/drm/radeon/radeon_legacy_tv.c:647-660 (radeon_legacy_tv_mode_set()): the match loop over SLOPE_limit[5] has no fallback when flicker_removal matches none of the five entries; the loop then exits with i == ARRAY_SIZE(SLOPE_limit) and the subsequent SLOPE_value[i] / YCOEF_value[i] / YCOEF_EN_value[i] reads (lines 652-660) run one element past the [5]-element arrays declared at lines 269-272. The garbage longs are folded into tv_y_saw_tooth_cntl / tv_y_fall_cntl / tv_y_rise_cntl MMIO values.

Unreachable with the current constants (and therefore filed as Info / hardening): flicker_removal is derived only from the fixed available_tv_modes[] rows (ver_total ∈ {740, 706, 727, 742}) and the NTSC/PAL line counts (525/625); the result is always 2 or 3, both of which are present in SLOPE_limit = {6,5,4,3,2}. Every other input to the function (tv_std from the DRM "tv standard" enum property, validated 0..8 by the DRM property machinery; h_pos/v_pos/h_size are zero-initialized kzalloc state with no user-settable property in this tree) is static.

Build / run

Not runnable: the audit guest has no AMD GPU (radeon never attaches), and the path is unreachable with the compiled-in tables anyway. This pack records the code defect and the recommended bound.

Fix

Clamp the index after the loop:

    for (i = 0; i < ARRAY_SIZE(SLOPE_limit); ++i) {
        if (flicker_removal == SLOPE_limit[i])
            break;
    }
+   if (i >= ARRAY_SIZE(SLOPE_limit))
+       i = ARRAY_SIZE(SLOPE_limit) - 1;

Fix verification

not_testable
per-fix-DF-4565

Confirmed kernel references

Detail

Evidence (decisive lines)

findings/poc/DF-4565/README.md (code shape + unreachability arithmetic); sys/dev/drm/radeon/radeon_legacy_tv.c:269-272 (arrays), :643-650 (match loop), :652-660 (indexed uses)

PoC changes

no PoC attempted: device absent from guest and defect unreachable with current tables (Info finding)

Verified recommended fix

Clamp the SLOPE table index after the match loop (i = min(i, ARRAY_SIZE(SLOPE_limit)-1)).

Verdict

Latent one-past-the-end index in radeon_legacy_tv_mode_set()'s flicker-filter table walk (sys/dev/drm/radeon/radeon_legacy_tv.c:647-660): the SLOPE_limit[] match loop has no out-of-loop bound, so a non-matching flicker_removal would index SLOPE_value[5]/YCOEF_value[5]/YCOEF_EN_value[5] one past the [5]-element arrays. Proven unreachable with the compiled-in constants (flicker_removal is always 2 or 3, both present in SLOPE_limit={6,5,4,3,2}), and the audit guest has no radeon hardware, so no PoC is possible or needed; filed as Info hardening.