Unvalidated 16-bit BIOS offsets dereferenced in LVDS mode-table and GPIO voltage-table parsing (OOB read up to 64KB)
Summary
radeon_combios_get_lvds_info at :1246-1272: tmp=RBIOS16(lcd_info+64+i*2) is 16-bit offset into BIOS, then RBIOS16(tmp)/RBIOS16(tmp+2)/RBIOS8(tmp+23)/RBIOS16(tmp+28) etc with NO bounds check vs BIOS allocation. Attacker controls hdisplay/vdisplay (from BIOS) to match, then leaked u32s land in lvds->native_mode -> DRM mode-info ioctl to unprivileged user. radeon_combios_get_power_modes at :2754-2760: voltage_table_offset=RBIOS16 unchecked, dereferenced for gpio.reg/gpio.mask. Crafted VBIOS with tmp=0xF000 against 512-byte allocation. Fix: check tmp+29<=bios_size before deref.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-1252 Β· 8 files| File | Type | Description | Size | |
|---|---|---|---|---|
| VERDICT.md | verdict | source trace, both OOB-read paths, fix rationale | 3.4 KB | β raw |
| fix.diff | suggested-fix | add bios_size field + radeon_bios_in() bounds checks at LVDS and voltage-table paths | 4.2 KB | view raw |
| fix_build_radeon.log | build-log | radeon_bios.o, radeon_combios.o compiled with fix, RC=0, no warnings | 1.3 KB | view raw |
| build.sh | build-log | repro: apply-check + note | 406 B | view raw |
| run.sh | run-log | no runtime trigger (no HW) | 396 B | view raw |
| env.txt | environment | uname, cc version, module state | 359 B | 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-1252 β Unvalidated 16-bit BIOS offsets dereferenced in LVDS mode-table and GPIO voltage-table parsing
Verdict
SOURCE-CONFIRMED (real bug), INCONCLUSIVE at runtime β the radeon.ko module is not loaded (no AMD GPU with legacy COMBIOS in the QEMU guest) and is not in the GENERIC kernel, so the unvalidated-offset paths are dormant. Fix authored and compile-validated.
Mechanism (source trace)
RBIOS16(i) / RBIOS8(i) are raw byte-indexing macros into the cached BIOS blob with no bounds check (sys/dev/drm/radeon/radeon.h:2686-2688):
#define RBIOS8(i) (rdev->bios[i]) #define RBIOS16(i) (RBIOS8(i) | (RBIOS8((i)+1) << 8)) #define RBIOS32(i) ((RBIOS16(i)) | (RBIOS16((i)+2) << 16))
rdev->bios is a kmalloc(size, β¦) blob (radeon_bios.c:68,98,269,717) whose size is the ROM image length (variable; never stored on the struct). Two parser paths read a 16-bit offset from the BIOS and then dereference it without validating it lies within the blob:
Path 1 β LVDS native-mode table (radeon_combios.c:1246-1272):
for (i = 0; i < 32; i++) {
tmp = RBIOS16(lcd_info + 64 + i * 2); /* tmp = attacker/BIOS-controlled 16-bit offset */
if (tmp == 0) break;
/* NO bounds check that tmp < bios_size */
if ((RBIOS16(tmp) == hdisplay) && /* derefs bios[tmp], bios[tmp+1] */
(RBIOS16(tmp + 2) == vdisplay)) { /* derefs bios[tmp+2], bios[tmp+3] */
... RBIOS16(tmp + 17), RBIOS16(tmp+19), RBIOS16(tmp+21), RBIOS8(tmp+23),
RBIOS16(tmp+24), RBIOS16(tmp+26), RBIOS16(tmp+28), RBIOS16(tmp+9) ...
With tmp up to 0xFFFF and the BIOS blob often < 64KB, every RBIOS*(tmp+N) reads up to ~64KB past the allocation β kernel heap OOB read. The leaked u32s land in lvds->native_mode (htotal/vtotal/clock/etc.) which are exposed to userspace via the DRM mode-info ioctl.
Path 2 β GPIO voltage table (radeon_combios.c:2754-2760):
u16 voltage_table_offset = RBIOS16(offset + 0x5 + 0xc); /* unchecked 16-bit offset */
if (entries && voltage_table_offset) {
... RBIOS16(voltage_table_offset) * 4; /* derefs bios[vto], bios[vto+1] */
RBIOS8(voltage_table_offset + 0x2); /* derefs bios[vto+2] */
Same OOB-read pattern.
Why not reproduced at runtime
radeon.kois not loaded on the guest and is not in X86_64_GENERIC.- Requires an AMD Radeon GPU with a legacy COMBIOS (pre-AtomBIOS ATI chips) attached; QEMU guest has no AMD GPU.
- The BIOS image is read from the card's ROM at probe time; a malicious PCI card (VFIO passthrough) could supply a crafted ROM. Not unprivileged-user-triggered.
Fix (fix.diff, compile-validated)
- Add
size_t bios_size;tostruct radeon_device(radeon.h) and aradeon_bios_in(rdev, off, len)bounds-check helper. - Set
rdev->bios_sizeat every allocation site inradeon_bios.c(and clear it on the NULL error paths). - Guard both cited paths in
radeon_combios.c: skip the entry if!radeon_bios_in(rdev, tmp, 30)(LVDS) /!radeon_bios_in(rdev, voltage_table_offset, 3)(voltage table).
The patched radeon.h, radeon_bios.c, and radeon_combios.c all compile cleanly with gcc 8.3, -Werror, no warnings.
Realistic impact ceiling
Kernel heap OOB read (up to ~64KB) of attacker/BIOS-influenced data, with partial leak to userspace via DRM mode ioctls, triggered by a malicious PCI card's crafted ROM at GPU probe. On this guest: not reachable (no AMD GPU). Driver/hardware-trust hardening fix.
Fix verification
not_testablecompile validated
module/object build rc=0
Confirmed kernel references
β
Detail
Exploit chain
none
Evidence (decisive lines)
β
Verdict
Source-confirmed. RBIOS16/8 raw offset no bounds -> OOB read. radeon not in GENERIC, no AMD GPU.
No comments yet.