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

Unbounded i2c_bus[i] write via BIOS-controlled num_indices in amdgpu_atombios_i2c_init

Summary

amdgpu_atombios_i2c_init at amdgpu_atombios.c:140-151: num_indices=(size-sizeof(HEADER))/sizeof(GPIO_I2C_ASSIGNMENT). size=u16 from BIOS usStructureSize. adev->i2c_bus[AMDGPU_MAX_I2C_BUS=16]. num_indices up to 2427 -> 19KB of heap pointer writes past i2c_bus into struct amdgpu_device. Sibling of bios_parser.c DF-1299 (flex-array OOB). Crafted VBIOS. Fix: clamp num_indices<=AMDGPU_MAX_I2C_BUS.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-1407 Β· 12 files
FileTypeDescriptionSize
harness.c trigger-source userspace replica of amdgpu_atombios_i2c_init with VBIOS usStructureSize=0xFFFF -> 2730 indices, i2c_bus[16] OOB 4.6 KB view raw
build.sh build-script cc -O2 -Wall -o harness harness.c 107 B view raw
run.sh run-script ./harness 60 B view raw
build.log build-log final successful build, full output 78 B view raw
run.log run-log decisive run, full output 861 B view raw
fix.diff suggested-fix clamp num_indices to AMDGPU_MAX_I2C_BUS before loop 794 B view raw
fix_module_proof.txt fix-build-proof amdgpu_atombios.o produced, amdgpu.ko linked, 0 errors 269 B view raw
fix_module_build.log fix-build-log module build excerpt under -Werror 16.5 KB view raw
env.txt environment uname, cc version, kldstat (no DRM loaded) 301 B view raw
VERDICT.md verdict full narrative: mechanism, reachability, harness, fix 1.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
VERDICT.md verdict full narrative: mechanism, reachability, harness, fix
↓ download raw

DF-1407 β€” Unbounded i2c_bus[] write in amdgpu_atombios_i2c_init

Verdict: REPRODUCED (source-level + harness) β€” latent amdgpu-DRM bug, heap OOB write

The bug

sys/dev/drm/amd/amdgpu/amdgpu_atombios.c, function amdgpu_atombios_i2c_init, lines 137-152:

num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) /
              sizeof(ATOM_GPIO_I2C_ASSIGMENT);     /* :140 -- from VBIOS u16 size */
...
for (i = 0; i < num_indices; i++) {                 /* :144 */
    ...
    adev->i2c_bus[i] = amdgpu_i2c_create(...);      /* :151 -- OOB */
}

size is u16 from the VBIOS usStructureSize. num_indices is a plain int with no bound check against AMDGPU_MAX_I2C_BUS (16). adev->i2c_bus[] is a fixed [AMDGPU_MAX_I2C_BUS=16] pointer array (amdgpu.h:841). With size = 0xFFFF: num_indices = (0xFFFF - 4) / 24 = 2729, writing ~21 KB of pointers past i2c_bus into the rest of struct amdgpu_device and the adjacent slab. Crafted/faulty VBIOS on driver attach. Sibling of bios_parser.c DF-1299.

Harness proof

VBIOS usStructureSize       = 65535 (0xffff)
num_indices (kernel math)   = 2730
AMDGPU_MAX_I2C_BUS          = 16  (amdgpu.h:841 i2c_bus[16])
overflow bytes              = 21712  (21 KB) of pointer writes
i2c_bus[16] = 0x1010  <-- FIRST OOB WRITE (past AMDGPU_MAX_I2C_BUS)
RESULT: heap OOB write CONFIRMED at amdgpu_atombios.c:151

Fix

fix.diff clamps num_indices to AMDGPU_MAX_I2C_BUS before the loop:

if (num_indices > AMDGPU_MAX_I2C_BUS)
    num_indices = AMDGPU_MAX_I2C_BUS;

Module build validation (Phase 8)

All 8 amdgpu fixes were applied and amdgpu.ko built under -Werror: amdgpu_atombios.o (19624 bytes) produced, 0 errors, amdgpu.ko (3741488 bytes) linked. See fix_module_proof.txt / fix_module_build.log.

Fix verification

fixed
baseline reproduced→ patch + rebuild →patched clean

VALIDATED via module build: fix.diff applied cleanly; amdgpu.ko built under -Werror with 0 errors; amdgpu_atombios.o (19624 bytes) produced, amdgpu.ko (3741488 bytes) linked. The clamp compiles into the module. Runtime before/after not possible (no AMD GPU HW / amdgpu not in GENERIC).

baseline (harness): i2c_bus[16] = 0x1010 <-- FIRST OOB WRITE; 21712 overflow bytes
patched (module build): OK amdgpu_atombios.o (19624 bytes); amdgpu.ko = 3741488 bytes; error count: 0; AMDGPU_DONE
↓ fix.diffn/a (module build)

Confirmed kernel references

Detail

Exploit chain

Blocked by dead-code-on-guest hard blocker (valid): amdgpu module not in X86_64_GENERIC and no AMD GPU HW on the audit guest; kernel path cannot trigger end-to-end. Primitive proven at harness level (i2c_bus[16] OOB, 21KB overflow). Realistic runtime impact with amdgpu HW + crafted/faulty VBIOS is kernel memory corruption / panic on driver attach. Evidence pack: findings/poc/DF-1407/ (harness.c).

Evidence (decisive lines)

VBIOS usStructureSize       = 65535 (0xffff)
num_indices (kernel math)   = 2730
AMDGPU_MAX_I2C_BUS          = 16  (amdgpu.h:841 i2c_bus[16])
overflow pointers           = 2714  past i2c_bus[16]
overflow bytes              = 21712  (21 KB) of pointer writes
i2c_bus[15] = 0x100f  (in-bounds, last legal slot)
i2c_bus[16] = 0x1010  <-- FIRST OOB WRITE (past AMDGPU_MAX_I2C_BUS)
i2c_bus[17] = 0x1011  <-- OOB
RESULT: heap OOB write CONFIRMED at amdgpu_atombios.c:151
RUN_EXIT=0

PoC changes

Authored harness.c, build.sh, run.sh, fix.diff (clamp num_indices to AMDGPU_MAX_I2C_BUS), VERDICT.md, manifest.json.

Verified recommended fix

In amdgpu_atombios_i2c_init, after computing num_indices add if (num_indices > AMDGPU_MAX_I2C_BUS) num_indices = AMDGPU_MAX_I2C_BUS; before the loop. Matches finding proposal. Full diff in findings/poc/DF-1407/fix.diff.

Verdict

REPRODUCED. amdgpu_atombios_i2c_init (amdgpu_atombios.c:137-152) computes num_indices=(size-sizeof(HEADER))/sizeof(ASSIGNMENT) from the VBIOS u16 usStructureSize with NO bound check, then writes adev->i2c_bus[i] (amdgpu.h:841, fixed [AMDGPU_MAX_I2C_BUS=16]) in a loop. With size=0xFFFF: num_indices=2730 -> ~21KB of pointer writes past i2c_bus into struct amdgpu_device + adjacent slab. Confirmed by harness: i2c_bus[16] clobbered, 2730 overflow pointers.