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)
PoC verification
Evidence pack
findings/poc/DF-4565 Β· 3 files| File | Type | Description | Size | |
|---|---|---|---|---|
| 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_testableConfirmed 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.
No comments yet.