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

Integer underflow in radeon_atombios_get_asic_ss_info causes 1.4-billion-iteration OOB heap read

Summary

radeon_atombios_get_asic_ss_info at :1555/:1577: num_indices=(size-sizeof(HEADER))/sizeof(ATOM_ASIC_SS_ASSIGNMENT_V2=12). size is u16 from BIOS. size<4 -> underflow to 0xFFFFFFFFFFFFFFFC/12=0x5555555555555555, truncates to int=1431655765. Loop iterates 1.4B times reading 12B per iteration past BIOS buffer into kernel heap -> panic on unmapped page or info leak. frev==2 and frev==3 (sizeof=12) affected. frev==1 (sizeof=10) and other tables (sizeof 4/8) produce negative int (loop skipped). Fix: validate size>=sizeof(HEADER) before subtraction.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-1199 Β· 11 files
FileTypeDescriptionSize
harness.c trigger-source replica of (size-sizeof(HEADER))/sizeof(V2) underflow with real struct sizes 3.0 KB view raw
VERDICT.md verdict full narrative + fix (guard size>=sizeof(HEADER)) 3.7 KB ↓ raw
build.sh build-script cc -O2 -Wall -o harness harness.c 172 B view raw
run.sh run-script ./harness 66 B view raw
run.log run-log underflow to 1431655765 for size 0..3 931 B view raw
env.txt environment uname, cc version 418 B view raw
fix.diff suggested-fix guard size>=sizeof(ATOM_COMMON_TABLE_HEADER) before the switch 704 B view raw
fix_build.log build-log radeon.ko rebuilt with both radeon fixes, -Werror, rc=0 143.5 KB view raw
README.md readme human reproduce doc 1.4 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 human reproduce doc
↓ download raw

DF-1199 β€” radeon asic_ss_info size underflow -> OOB read loop

File: sys/dev/drm/radeon/radeon_atombios.c:1512-1578 Class: memory corruption (hardware/firmware-attacker; malicious CISS PCI device or malicious VBIOS at driver attach). No local-unprivileged syscall trigger on the audit guest (no HP Smart Array / no AMD GPU present).

Reproduce

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

What the harness does

harness.c is a faithful userspace replica of the kernel parsing routine cited above, fed crafted controller/VBIOS data that the real malicious device would supply. It demonstrates the out-of-bounds access / overflow / underflow using the real kernel macros and struct sizes, with a canary or computed-index check to make the OOB observable without needing the hardware.

Expected output

A [BUG REPRODUCED] (or UNDERFLOW for DF-1199) marker plus the computed out-of-range index / overflow byte count / underflowed loop count. See run.log for the captured decisive run.

Fix

See fix.diff (git-apply-able) and VERDICT.md. The fix was validated to compile (module rebuilt with -Werror) β€” see fix_build.log. No live-kernel trigger exists on the guest, so the fix is validated at the applies + compiles + closes-the-code-path level.

Artifacts

VERDICT.md (full narrative), harness.c, build.sh, run.sh, run.log, env.txt, fix.diff, fix_build.log, manifest.json.

VERDICT.md verdict full narrative + fix (guard size>=sizeof(HEADER))
↓ download raw

DF-1199 β€” radeon_atombios_get_asic_ss_info integer underflow (OOB read loop)

Verdict

REPRODUCED (harness) β€” real bug confirmed by source trace + userspace replica. Impact class: integer underflow β†’ ~12 GB out-of-bounds read loop over kernel memory (panic on first unmapped page, or large info-leak of the BIOS/heap region until it faults). No local-unprivileged trigger on the audit guest (no AMD GPU); trigger requires a malicious VBIOS. uid=0 chain N/A β€” hardware/firmware-attacker class; this is a read/DoS primitive (no write), so no escalation chain regardless.

Mechanism (confirmed path:line)

radeon_atombios_get_asic_ss_info() (sys/dev/drm/radeon/radeon_atombios.c) computes a loop count from the BIOS-supplied table size:

  • size is declared uint16_t (radeon_atombios.c:1512), populated by atom_parse_data_header(... &size ...) (radeon_atombios.c:1528) from the BIOS table header's usStructureSize.
  • num_indices is declared int (radeon_atombios.c:1516).
  • radeon_atombios.c:1555-1556 (frev==2), 1577-1578 (frev==3): num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) / sizeof(ATOM_ASIC_SS_ASSIGNMENT_V2);

sizeof(ATOM_COMMON_TABLE_HEADER) = 4 (atombios.h:200-206: USHORT+UCHAR+ UCHAR); sizeof(ATOM_ASIC_SS_ASSIGNMENT_V2) = 12. The subtraction is performed in size_t (unsigned, 64-bit) because sizeof yields size_t, so when size < 4 the uint16_t size is promoted and the subtraction wraps to 0xFFFFFFFF...FFC. Dividing by 12 gives 0x5555555555555555, which truncates to int = 1431655765 (positive). The loop for (i = 0; i < num_indices; i++) then iterates ~1.4 billion times, reading 12 bytes per iteration (ss_assign pointer arithmetic, radeon_atombios.c:1572-1573) past the BIOS table into kernel memory β€” a DoS (panic on the first unmapped page) or a large read of kernel/BIOS memory.

(For frev==1 the element size is 10 and the count would be negative as int, so the loop is skipped β€” frev 2 and 3 are the affected paths.)

Harness proof (run.log)

harness.c reproduces the exact arithmetic with the real struct sizes:

size=    0 -> num_indices=1431655765   <-- UNDERFLOW (huge loop -> OOB read)
size=    1 -> num_indices=1431655765   <-- UNDERFLOW (huge loop -> OOB read)
size=    2 -> num_indices=1431655765   <-- UNDERFLOW (huge loop -> OOB read)
size=    3 -> num_indices=1431655765   <-- UNDERFLOW (huge loop -> OOB read)
size=    4 -> num_indices=0
size=   16 -> num_indices=1
size=   40 -> num_indices=3
Finding's cited value: size=2 -> num_indices=1431655765 (== 1431655765 ? YES)
size_t trace: (2 - 4) = 0xfffffffffffffffe (18446744073709551614);  /12 = 0x1555555555555555; trunc->int = 1431655765

Why not a live-kernel trigger / no uid0 chain

radeon attaches only to AMD/ATI Radeon PCIe GPUs (none on the guest). size comes from the GPU VBIOS table header parsed at probe β€” not reachable from any unprivileged syscall. Hardware/firmware-attacker class. Additionally this is a read-only primitive (no write), so there is no memory-corruption escalation chain to pursue even with hardware present β€” the impact ceiling is DoS / info leak of the BIOS-adjacent kernel memory region.

Fix (fix.diff)

Validate size >= sizeof(ATOM_COMMON_TABLE_HEADER) once, before the switch (frev), returning false if not (radeon_atombios.c:1533). This closes the underflow for all three frev cases (1/2/3) with one guard. Matches the finding's proposed fix (validate size >= sizeof(HEADER) before subtraction).

Fix validation

radeon.ko rebuilt from patched source (DF-1198/1199 applied) compiled with -Werror and linked; radeon_atombios.o rebuilt. fix_status: not_testable (no AMD GPU for a live trigger; validated applies + compiles + closes the path).

Fix verification

not_testable

compile+harness validated

module build rc=0

Confirmed kernel references

β€”

Detail

Exploit chain

none

Evidence (decisive lines)

β€”

Verdict

REPRODUCED (harness). radeon_atombios_get_asic_ss_info size<4 -> size_t underflow -> 1.4B iterations OOB read. radeon not in GENERIC.