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

Hardcoded EDID parser over-reads rdev->bios by up to 32KB, disclosing kernel heap via EDID ioctl

Summary

radeon_combios_check_hardcoded_edid at radeon_combios.c:373-379: raw=rdev->bios+edid_info, size=EDID_LENGTH*(raw[0x7e]+1) = 128*(0-255) up to 32768. memcpy(edid,raw,size) reads from BIOS allocation which can be as small as 512 bytes (bios[2]*512 from radeon_read_platform_bios). raw[0x7e]=0xff -> 32768 byte over-read from 512-byte slab -> ~32KB adjacent kernel heap leaked. If drm_edid_is_valid passes: stored as bios_hardcoded_edid, returned via connector EDID ioctl to unprivileged user. Crafted VBIOS (PCI swap/hotplug/VFCT). Fix: clamp size to bios_size, reject unreasonable block count.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-1250 Β· 11 files
FileTypeDescriptionSize
harness.c trigger-source userspace replication of radeon_combios_check_hardcoded_edid over-read 2.9 KB view raw
build.sh build-script cc -O2 -Wall -o harness harness.c 93 B view raw
run.sh run-script ./harness 48 B view raw
run.log run-log harness output: 32768-byte memcpy over-reads 512-byte BIOS by 32320 456 B view raw
fix.diff suggested-fix add bios_length field + bound EDID read against BIOS allocation (radeon.h, radeon_bios.c, radeon_combios.c) 2.4 KB view raw
fix_build.log build-log radeon.ko builds clean with -Werror (fix compiles) 24.5 KB view raw
env.txt environment uname, cc, module list 346 B view raw
README.md readme how to reproduce 624 B ↓ raw
VERDICT.md verdict full analysis 2.8 KB ↓ 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
README.md readme how to reproduce
↓ download raw

DF-1250 β€” reproduction

Userspace harness replicating radeon_combios_check_hardcoded_edid()'s size = 128*(raw[0x7e]+1) computation and the resulting BIOS over-read.

The live kernel trigger requires an AMD/ATI radeon GPU with a malicious Video BIOS (absent from the QEMU guest); the harness proves the over-read math against a 512-byte BIOS slab.

./build.sh && ./run.sh

Expected: PRIMITIVE CONFIRMED: memcpy(edid, raw, 32768) reads 32320 bytes past a 512-byte BIOS slab.

Fix: fix.diff adds a bios_length field + bounds the EDID read. Builds cleanly into radeon.ko (-Werror). See VERDICT.md.

VERDICT.md verdict full analysis
↓ download raw

DF-1250 β€” radeon hardcoded-EDID parser over-reads rdev->bios

Verdict (one line)

CONFIRMED REAL (source trace + harness primitive), NOT reproduced on audit guest (no AMD/ATI radeon GPU).

Finding

sys/dev/drm/radeon/radeon_combios.c:radeon_combios_check_hardcoded_edid() computes the EDID copy size from an attacker-controlled BIOS byte raw[0x7e] and memcpys that many bytes out of the BIOS allocation before any validity check, so a malicious Video BIOS whose image is small (as little as 512 bytes via radeon_read_platform_bios) but whose raw[0x7e] = 0xff causes a ~32 KB over-read of adjacent kernel heap.

Mechanism (path:line)

  1. radeon_combios.c:373 β€” raw = rdev->bios + edid_info;
  2. radeon_combios.c:374 β€” size = EDID_LENGTH * (raw[0x7e] + 1); raw[0x7e] ∈ [0,255] β‡’ size ∈ [128, 32768].
  3. radeon_combios.c:375 β€” edid = kmalloc(size, ...);
  4. radeon_combios.c:379 β€” memcpy(edid, raw, size); ← over-read here.
  5. radeon_combios.c:381 β€” if (!drm_edid_is_valid(edid)) is checked only after the memcpy, so the leak is unconditional.
  6. The BIOS allocation can be as small as 512 bytes: sys/dev/drm/radeon/radeon_bios.c:radeon_read_platform_bios does size = bios[2] * 512 (radeon_bios.c:146) and kmemdup(bios, size,...) (radeon_bios.c:153). raw[0x7e]=0xff β‡’ size=32768 β‡’ ~32 KB of adjacent heap leaked.

Why not reproduced on the audit guest

radeon is a loadable DRM module, not in GENERIC, and the guest has no AMD/ATI GPU. The hardcoded-EDID table is parsed during GPU attach from the card's Video BIOS, which is not present. The realistic trigger is a malicious GPU Video BIOS (e.g. a hotplug PCIe card with a crafted ROM) β€” a legitimate "malicious peripheral firmware" threat, not exercisable here.

Primitive proof (harness)

harness.c models a 512-byte BIOS slab, sets raw[0x7e]=0xff, computes size exactly as the kernel does, and reports the over-read:

bios allocation = 512 bytes
raw[0x7e] = 0xff
computed size (radeon_combios.c:374) = 32768 bytes
over-read past BIOS allocation: 32320 bytes of adjacent kernel heap
PRIMITIVE CONFIRMED: memcpy(edid, raw, 32768) reads 32320 bytes past a 512-byte BIOS slab.

Fix

fix.diff (3 files): - radeon.h: add uint32_t bios_length; (allocation size of *bios). - radeon_bios.c: set rdev->bios_length = size; at every BIOS alloc site (5 sites). - radeon_combios.c: before dereferencing raw[0x7e] and before the memcpy, verify edid_info + 0x7f and edid_info + size are within rdev->bios_length; otherwise return false. Validated: builds cleanly into radeon.ko with -Werror. (DF-1251's fix shares the same bios_length infrastructure.)

Reproduce

ssh dfbsd-maxx; cd poc/DF-1250 && cc -O2 -Wall -o harness harness.c && ./harness

Fix verification

not_testable

compile+harness validated

module build rc=0

Confirmed kernel references

β€”

Detail

Exploit chain

none

Evidence (decisive lines)

β€”

Verdict

Source+harness. radeon_combios hardcoded EDID raw[0x7e] unbounded -> 32KB BIOS over-read. radeon not in GENERIC.