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)
PoC verification
Evidence pack
findings/poc/DF-1407 Β· 12 files| File | Type | Description | Size | |
|---|---|---|---|---|
| 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 |
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
fixedVALIDATED 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
Confirmed kernel references
- s
- y
- s
- /
- d
- e
- v
- /
- d
- r
- m
- /
- a
- m
- d
- /
- a
- m
- d
- g
- p
- u
- /
- a
- m
- d
- g
- p
- u
- _
- a
- t
- o
- m
- b
- i
- o
- s
- .
- c
- :
- 1
- 4
- 0
- s
- y
- s
- /
- d
- e
- v
- /
- d
- r
- m
- /
- a
- m
- d
- /
- a
- m
- d
- g
- p
- u
- /
- a
- m
- d
- g
- p
- u
- _
- a
- t
- o
- m
- b
- i
- o
- s
- .
- c
- :
- 1
- 4
- 4
- s
- y
- s
- /
- d
- e
- v
- /
- d
- r
- m
- /
- a
- m
- d
- /
- a
- m
- d
- g
- p
- u
- /
- a
- m
- d
- g
- p
- u
- _
- a
- t
- o
- m
- b
- i
- o
- s
- .
- c
- :
- 1
- 5
- 1
- s
- y
- s
- /
- d
- e
- v
- /
- d
- r
- m
- /
- a
- m
- d
- /
- a
- m
- d
- g
- p
- u
- /
- a
- m
- d
- g
- p
- u
- .
- h
- :
- 8
- 4
- 1
- s
- y
- s
- /
- d
- e
- v
- /
- d
- r
- m
- /
- a
- m
- d
- /
- a
- m
- d
- g
- p
- u
- /
- a
- m
- d
- g
- p
- u
- _
- m
- o
- d
- e
- .
- h
- :
- 1
- 2
- 7
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.
No comments yet.