Off-by-one in btc_copy_vbios_mc_reg_table + post-write bound check in btc_set_mc_special_registers allows VBIOS OOB write
Summary
btc_copy_vbios_mc_reg_table at btc_dpm.c:1992: check table->last>SMC_EVERGREEN_MC_REGISTER_ARRAY_SIZE(16) uses > not >=, admits last==16. btc_set_mc_special_registers at :1920-1933: j=table->last=16, first write at mc_reg_address[16]/mc_data[16] BEFORE guard if(j>=SIZE) at :1933 (post-write check). Off-by-one + post-write = OOB write past arrays into vddc_voltage_table. Sibling of ni_dpm.c:2721 which checks BEFORE write. Crafted VBIOS with last==16. Fix: change > to >=, move bound check before write.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-1314 Β· 9 files| File | Type | Description | Size | |
|---|---|---|---|---|
| VERDICT.md | verdict | source-trace verdict: off-by-one (> vs >=) admits last==16 -> OOB write | 2.4 KB | β raw |
| README.md | readme | build/run/expected for this hardware-gated finding | 2.0 KB | β raw |
| trigger_path.c | trigger-source | documents the unreachable trigger path + ni_dpm.c sibling | 1.3 KB | view raw |
| fix.diff | suggested-fix | change `>` to `>=` so last==16 is rejected | 369 B | view raw |
| build.sh | build-log | no userspace PoC; documents hardware-gated nature | 389 B | view raw |
| run.sh | run-log | reachability check (module/PCI/CPU) | 677 B | view raw |
| env.txt | environment | guest uname, cc, PCI, CPU, module presence | 2.2 KB | view raw |
| ../fix_build_combined.log | build-log | Combined 41-finding kernel build (rc=0, -Werror clean) | 5.6 MB | β download |
| ../fix_build_summary.txt | build-summary | Summary of the combined 41-finding kernel build | 826 B | view raw |
DF-1298 / DF-1305 / DF-1314 / DF-1315 β hardware-gated driver findings
These four findings are kernel driver/virtualization bugs that are confirmed real by source trace but that cannot run on the QEMU audit guest because the required hardware is absent (see env.txt):
| Finding | Subsystem | Required hardware | Present on guest? |
|---|---|---|---|
| DF-1298 | amdgpu display VBIOS parser | AMD GPU (amdgpu) | NO (QEMU std-VGA only) |
| DF-1305 | radeon kv DPM | AMD radeon KV APU | NO |
| DF-1314 | radeon btc/evergreen DPM | AMD radeon NI GPU | NO |
| DF-1315 | nvmm AMD-SVM backend | AMD-SVM CPU | NO (guest CPU has no SVM bit) |
Build
./build.sh
There is no userspace PoC binary to compile for these β the vulnerable code
is inside a kernel driver/VMM module that only executes when the relevant device
attaches or the SVM backend runs. build.sh documents this.
Run
./run.sh
run.sh performs a reachability check (kldstat, pciconf, dmesg) and
reports that the path is unreachable on this guest. No panic/leak is expected
here because the trigger hardware is absent.
Expected (bug present, on a host WITH the hardware)
Each VERDICT.md describes the exact trigger and the kernel-level effect (heap overflow / OOB write / host DR7 persistence). On this guest those are latent.
Reproduce the FIX validation
The fix for each is fix.diff. To compile-validate a module fix:
1. scp fix.diff to the guest; cd /usr/src && patch -p1 < fix.diff
2. Build the relevant module, e.g.
cd /usr/src && make -j6 MODULES_OVERRIDE=... or a full
make -j6 nativekernel KERNCONF=X86_64_GENERIC (which also builds modules).
3. Confirm the module/object compiles with no errors (see fix_build.log).
Runtime fix-behaviour cannot be compared on this guest because the PoC paths
are hardware-gated; the fix is therefore classified not_testable with a
compile + source-trace validation.
DF-1314 β btc_copy_vbios_mc_reg_table off-by-one (radeon BTC DPM)
Verdict
NOT TESTABLE AT RUNTIME on this guest β confirmed real latent vulnerability in source.
Mechanism (confirmed by source trace)
sys/dev/drm/radeon/btc_dpm.c initialises the MC register table for
Barts/Turks/Caicos (evergreen-family) radeon GPUs.
-
btc_copy_vbios_mc_reg_table()(line 1987) copies the VBIOS table into the driver struct. Line 1992:c if (table->last > SMC_EVERGREEN_MC_REGISTER_ARRAY_SIZE) /* SIZE = 16 */ return -EINVAL;uses>instead of>=, so it admitslast == 16. It then setseg_table->last = table->last(= 16) at line 2000. -
btc_initialize_mc_reg_table()(line 2014) then callsbtc_set_mc_special_registers()(line 2050). -
btc_set_mc_special_registers()(line 1913) startsj = table->last= 16 (line 1920) and, in theMC_SEQ_MISC1 >> 2case, writestable->mc_reg_address[j]andtable->mc_reg_table_entry[k].mc_data[j]at index 16 (lines 1924, 1927) β which is one past themc_reg_address[16]/mc_data[16]arrays (indices 0β15) β BEFORE the guardif (j >= SMC_EVERGREEN_MC_REGISTER_ARRAY_SIZE) return -EINVAL;at line 1933 (a post-write check).
Result: last == 16 β an OOB write at array index 16, overflowing
struct evergreen_mc_reg_table (cypress_dpm.h) into adjacent fields
(e.g. vddc_voltage_table).
- Sibling bug:
ni_dpm.c:2850has the identical>pattern withSMC_NISLANDS_MC_REGISTER_ARRAY_SIZE. Same off-by-one; same fix. (Noted for maintainers; the included fix.diff targets the cited btc_dpm.c.)
Why it does not reproduce on this guest
- radeon is not in GENERIC (loadable
radeon.ko, not loaded); no AMD GPU present. The MC-reg-table init runs only at radeon evergreen/BTC bring-up, which never happens on this GPU-less guest. (Case d.)
Severity / realistic ceiling
On a host with a Northern-Islands/evergreen radeon GPU whose VBIOS reports
last == 16, this is a kernel heap OOB write during DPM init β heap
corruption / panic; firmware-data-driven (malformed/malicious VBIOS).
Fix (see fix.diff)
Change > to >= at btc_dpm.c:1992 so last == 16 is rejected before the
arrays are populated and before btc_set_mc_special_registers writes past
them. Compile-validated (radeon module build). Runtime not exercisable here.
Fix verification
not_testablecompile+boot validated -Werror
nativekernel rc=0, boots #1 clean
Confirmed kernel references
β
Detail
Exploit chain
none
Evidence (decisive lines)
β
Verdict
Source-confirmed. btc_copy_vbios_mc_reg_table off-by-one > should be >= -> last==16 -> OOB write. radeon not in GENERIC. Sibling ni_dpm.c:2850.
No comments yet.