Wild OOB kernel read in btc_find_voltage when a VBIOS voltage object has zero LUT entries: entries[0xFFFFFFFF] β +32 GiB past eg_pi (btc twin of DF-3649/3956)
Summary
radeon_atom_get_voltage_table returns SUCCESS with count==0 for a zero-entry V2/V3 voltage object; cypress_construct_voltage_tables only trims counts>32; btc_find_voltage then evaluates entries[count-1] with u32 count==0 -> index 0xFFFFFFFF x 8-byte entries -> wild read -> panic during the first power-state change. Sibling guards exist in cypress but btc forgot them. Guard fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3974 Β· 5 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.3 KB | β raw | |
| VERDICT.md | β | 2.4 KB | β raw | |
| verdict.json | β | 2.2 KB | view raw | |
| manifest.json | β | 747 B | view raw | |
| fix.diff | β | 355 B | view raw |
DF-3974 β Wild OOB kernel read in btc_find_voltage when a VBIOS voltage object has zero LUT entries (count==0 β entries[0xFFFFFFFF], +32 GiB offset)
- File:
sys/dev/drm/radeon/btc_dpm.c:1293-1303(sink), reached viabtc_apply_voltage_delta_rules(btc_dpm.c:1305-1328) frombtc_apply_state_adjust_rules(2233-2238) - Class: CWE-125/CWE-191 (Medium) β family: DF-3649 (ci CAC count-0), DF-3956 (kv vid_mapping count-0). This is the btc empty-voltage-table twin.
Build / Run / Expected
Hardware-gated (Barts/Turks/Caicos + VBIOS whose VDDCI or VDDC voltage object carries a zero entry count). Panic (page fault on unmapped read) or silent garbage voltage pick.
Root cause
radeon_atom_get_voltage_table()βsys/dev/drm/radeon/radeon_atombios.c:3712-3805β returns 0 (success) withvoltage_table->count == 0when the V2 object'sucNumOfVoltageEntries == 0(loop skipped, count assigned at 3758) or the V3 GPIO object'sucGpioEntryNum == 0(3789).cypress_construct_voltage_tables()βcypress_dpm.c:1487-1513β only trims whencount > MAX_NO_VREG_STEPS; count==0 passes through as success.btc_find_voltage()βbtc_dpm.c:1293:c for (i = 0; i < table->count; i++) { ... } return table->entries[table->count - 1].value; /* count==0 β entries[0xFFFFFFFF] */countisu32(radeon_mode.h:681);entriesisentries[32]of 8-byte entries (radeon_mode.h:671-685). Index 0xFFFFFFFF zero-extends β byte offset 0x7_FFFF_FFF8 (~32 GiB) pasteg_pi->vddci_voltage_table(embedded in the kmalloc'd evergreen_power_info) β wild read β page fault β panic; if something is somehow mapped there, an unrelated 16-bit value becomes the programmed VDDCI (hardware misregulation).- Reachability:
btc_apply_voltage_delta_rules(1305) is called on everybtc_dpm_pre_set_power_stateβbtc_apply_state_adjust_rules(2233-2238) with the parsed non-zero vddc/vddci values; the guard at 1312 only rejects zero voltages, not zero table counts. The vddci branch (1315-1320) fires when*vddc - *vddci > vddc_vddci_deltawith an empty vddci table; the vddc branch (1321-1326) symmetrically.
Note cypress_populate_smc_voltage_tables (cypress_dpm.c:1534/1552) does guard with
if (...count) β the btc path simply forgot the same guard.
VERDICT β DF-3974
status: inconclusive (hardware-gated; source-level certainty: certain)
Why
Same reasoning as DF-3972: the guest has no radeon GPU (env.txt), and the zero-entry voltage object must be present in the card's VBIOS VoltageObjectInfo table. The code path, however, is fully deterministic once such a VBIOS is parsed:
radeon_dpm_enable (btc_dpm.c:2373)
ββ 2392-2399 cypress_construct_voltage_tables
ββ radeon_atom_get_voltage_table(VDDCI) β ret 0, count = 0 [radeon_atombios.c:3789]
(radeon_atom_get_voltage_table succeeds with zero entries; only entry-count > 32 rejected)
...first power-state change:
radeon_pm_compute_clocks β radeon_dpm_change_power_state_locked (radeon_pm.c:1137)
ββ btc_dpm_pre_set_power_state (btc_dpm.c:2287)
ββ btc_apply_state_adjust_rules (btc_dpm.c:2092)
ββ 2233 btc_apply_voltage_delta_rules
ββ 1315-1319 *vddc - *vddci > vddc_vddci_delta (200, hardcoded at 2696)
ββ btc_find_voltage(&eg_pi->vddci_voltage_table /* count==0 */, ...) [1317]
ββ 1302 return table->entries[0xFFFFFFFF].value
β load at eg_pi + offsetof(vddci_voltage_table) + 0x7FFFFFFF8 β #PF
The u32 count type (radeon_mode.h:681) makes count - 1 = 0xFFFFFFFF; the 8-byte entry stride
(radeon_mode.h:673-677) puts the faulting address ~32 GiB past the allocation β a wild read that
cannot land on anything mapped in the kernel's direct map on this guest's 4 GiB RAM, i.e. a
guaranteed panic rather than a lucky garbage pick. Exactly the DF-3649/DF-3956 family shape.
Preconditions beyond hardware: pi->voltage_control true (a VDDC GPIO voltage object must exist β
2392 gates the whole construct on it) with β₯1 entries, and eg_pi->vddci_control true
(btc_dpm.c:2636) with a VDDCI object whose entry count field is 0 β all plausible in a crafted ROM.
Fix validation
not_testable (no hardware). fix.diff: early-out on count==0 in btc_find_voltage (mirror of the
if (count) guards used in cypress_populate_smc_voltage_tables).
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_testablefix.diff authored (count==0 early-out); not build-testable without a Barts-class GPU.
findings/poc/DF-3974/fix.diff
Confirmed kernel references
- sys/dev/drm/radeon/btc_dpm.c:1293
- sys/dev/drm/radeon/btc_dpm.c:1302
- sys/dev/drm/radeon/btc_dpm.c:1317
- sys/dev/drm/radeon/btc_dpm.c:1323
- sys/dev/drm/radeon/btc_dpm.c:2233
- sys/dev/drm/radeon/radeon_atombios.c:3758
- sys/dev/drm/radeon/radeon_atombios.c:3789
- sys/dev/drm/radeon/cypress_dpm.c:1497
- sys/dev/drm/radeon/radeon_mode.h:681
Detail
Evidence (decisive lines)
VERDICT.md deterministic trace; radeon_mode.h:671-685 (u32 count, entries[32], 8-byte entries); the sibling guards at cypress_dpm.c:1534/1552 prove the intended pattern
PoC changes
No PoC: hardware-gated (crafted VBIOS voltage object on Barts-class hardware).
Verified recommended fix
Guard table->count == 0 in btc_find_voltage (return voltage unchanged), mirroring the count guards in cypress_populate_smc_voltage_tables.
Verdict
btc_find_voltage (btc_dpm.c:1293-1303) indexes entries[count-1] with a u32 count; radeon_atom_get_voltage_table returns success with count==0 for a VBIOS voltage object whose LUT entry count is zero (radeon_atombios.c:3758/3789), and cypress_construct_voltage_tables only trims counts > 32 β so a crafted VDDCI/VDDC object yields entries[0xFFFFFFFF] β +32 GiB wild read β kernel page fault during the first power-state change (btc_apply_state_adjust_rules β btc_apply_voltage_delta_rules, btc_dpm.c:2233/1317). Hardware-gated: no radeon GPU in the guest, so inconclusive-by-hardware with source-level certainty; exact twin of DF-3649/DF-3956.
No comments yet.