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

BIOS-count-driven OOB pointer write past rdev->i2c_bus[16] in radeon_atombios_i2c_init

Summary

num_indices = (BIOS u16 size - 4)/28 (up to 2340 with size=0xFFFF, unvalidated by atom_parse_data_header) drives 'rdev->i2c_bus[i] = radeon_i2c_create(...)' into the fixed array rdev->i2c_bus[RADEON_MAX_I2C_BUS=16]. i2c.valid is BIOS-gated (usClkMaskRegisterIndex != 0), so every OOB store is attacker-controlled: heap pointers overwrite the following radeon_device fields (debugfs[32], vm_manager, gpu_clock_mutex, atif/atcs). HARNESS on the guest: GPIO_I2C_Info size 4092 -> 146 indices -> 130 OOB pointer writes (1040 B) past i2c_bus[16]; worst case 2324 writes (18.5 KB). Crafted VBIOS on any ATOM radeon card (radeon.ko ships in default installs): kernel-heap corruption inside struct radeon_device at attach -> crash/DoS minimum, adjacent-structure corruption exploitable further. One-line cap fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3739 Β· 13 files
FileTypeDescriptionSize
README.md β€” 2.0 KB ↓ raw
VERDICT.md β€” 2.8 KB ↓ raw
build.log β€” 0 B ↓ download
build.sh β€” 65 B view raw
env.txt β€” 375 B view raw
harness_i2c.c β€” 4.8 KB view raw
run.log β€” 479 B view raw
run.sh β€” 131 B view raw
README.md β€” 2.0 KB ↓ raw
VERDICT.md β€” 2.8 KB ↓ raw
manifest.json β€” 970 B view raw
verdict.json β€” 3.2 KB view raw
fix.diff β€” 563 B view raw

DF-3739 β€” BIOS-count-driven OOB pointer write past rdev->i2c_bus[16] in radeon_atombios_i2c_init

Severity: High Β· CWE: CWE-787 Out-of-bounds Write Β· File: sys/dev/drm/radeon/radeon_atombios.c:172-187

What this is

Source-certain heap-corruption write in the radeon ATOM I2C init path. atom_parse_data_header() (atom.c:1366-1385) returns the GPIO_I2C_Info table's self-declared u16 size with NO validation; radeon_atombios_i2c_init() derives num_indices=(size-4)/28 (up to 2340 with size=0xFFFF) and stores radeon_i2c_create() heap pointers into rdev->i2c_bus[i] (radeon.h:2440) whose capacity is RADEON_MAX_I2C_BUS=16 (radeon_mode.h:96). i2c.valid is BIOS-controlled (usClkMaskRegisterIndex!=0, :113-116), so every OOB store is attacker-gated. Entries 16..num_indices-1 overwrite the following radeon_device fields (debugfs[32], debugfs_count, vm_manager, gpu_clock_mutex, atif/atcs, ... radeon.h:2441+). Harness on guest: size=4092 -> 146 entries -> 130 OOB pointer writes (1040 B); worst case 0xFFFF -> 2324 OOB writes (18.5 KB). Not executable in-kernel on this guest (no AMD GPU); module present in /boot/kernel/radeon.ko.

Reproduce

./build.sh    # cc -O1 -g -Wall -o <bin> harness_i2c.c   (DragonFly guest, cc 8.3)
./run.sh      # expected: num_indices=146 vs i2c_bus[16]; 130 OOB pointer writes (1040 bytes); worst case 2324 (18592 bytes); exit_code=0

The harness replicates the kernel parse arithmetic VERBATIM (cited path:line) against a synthetic crafted-VBIOS buffer with canary-guarded destinations.

Guest / environment

Guest has NO AMD/radeon GPU (virtio-only); /boot/kernel/radeon.ko exists (code shipped in the default install) but cannot attach, so the kernel code path is unreachable at runtime on this guest. Evidence is source-level certainty (path:line traced) plus a verbatim-arithmetic userland harness compiled and executed on the guest (build.log/run.log). See env.txt (uname, cc, kldstat, /boot/kernel/radeon.ko presence).

Fix

cap the loop at RADEON_MAX_I2C_BUS β€” see fix.diff (authored after verification; never applied to sys/).

DF-3739 β€” BIOS-count-driven OOB pointer write past rdev->i2c_bus[16] in radeon_atombios_i2c_init

Severity: High Β· CWE: CWE-787 Out-of-bounds Write Β· File: sys/dev/drm/radeon/radeon_atombios.c:172-187

What this is

Source-certain heap-corruption write in the radeon ATOM I2C init path. atom_parse_data_header() (atom.c:1366-1385) returns the GPIO_I2C_Info table's self-declared u16 size with NO validation; radeon_atombios_i2c_init() derives num_indices=(size-4)/28 (up to 2340 with size=0xFFFF) and stores radeon_i2c_create() heap pointers into rdev->i2c_bus[i] (radeon.h:2440) whose capacity is RADEON_MAX_I2C_BUS=16 (radeon_mode.h:96). i2c.valid is BIOS-controlled (usClkMaskRegisterIndex!=0, :113-116), so every OOB store is attacker-gated. Entries 16..num_indices-1 overwrite the following radeon_device fields (debugfs[32], debugfs_count, vm_manager, gpu_clock_mutex, atif/atcs, ... radeon.h:2441+). Harness on guest: size=4092 -> 146 entries -> 130 OOB pointer writes (1040 B); worst case 0xFFFF -> 2324 OOB writes (18.5 KB). Not executable in-kernel on this guest (no AMD GPU); module present in /boot/kernel/radeon.ko.

Reproduce

./build.sh    # cc -O1 -g -Wall -o <bin> harness_i2c.c   (DragonFly guest, cc 8.3)
./run.sh      # expected: num_indices=146 vs i2c_bus[16]; 130 OOB pointer writes (1040 bytes); worst case 2324 (18592 bytes); exit_code=0

The harness replicates the kernel parse arithmetic VERBATIM (cited path:line) against a synthetic crafted-VBIOS buffer with canary-guarded destinations.

Guest / environment

Guest has NO AMD/radeon GPU (virtio-only); /boot/kernel/radeon.ko exists (code shipped in the default install) but cannot attach, so the kernel code path is unreachable at runtime on this guest. Evidence is source-level certainty (path:line traced) plus a verbatim-arithmetic userland harness compiled and executed on the guest (build.log/run.log). See env.txt (uname, cc, kldstat, /boot/kernel/radeon.ko presence).

Fix

cap the loop at RADEON_MAX_I2C_BUS β€” see fix.diff (authored after verification; never applied to sys/).

VERDICT.md
↓ download raw

VERDICT β€” DF-3739

Status: inconclusive (source-certain; harness-reproduced arithmetic; kernel path unreachable on this guest) reproduced: 0 (in-kernel) β€” harness demonstration: YES Impact on real hardware: High β€” CWE-787 Out-of-bounds Write

Finding

Source-certain heap-corruption write in the radeon ATOM I2C init path. atom_parse_data_header() (atom.c:1366-1385) returns the GPIO_I2C_Info table's self-declared u16 size with NO validation; radeon_atombios_i2c_init() derives num_indices=(size-4)/28 (up to 2340 with size=0xFFFF) and stores radeon_i2c_create() heap pointers into rdev->i2c_bus[i] (radeon.h:2440) whose capacity is RADEON_MAX_I2C_BUS=16 (radeon_mode.h:96). i2c.valid is BIOS-controlled (usClkMaskRegisterIndex!=0, :113-116), so every OOB store is attacker-gated. Entries 16..num_indices-1 overwrite the following radeon_device fields (debugfs[32], debugfs_count, vm_manager, gpu_clock_mutex, atif/atcs, ... radeon.h:2441+). Harness on guest: size=4092 -> 146 entries -> 130 OOB pointer writes (1040 B); worst case 0xFFFF -> 2324 OOB writes (18.5 KB). Not executable in-kernel on this guest (no AMD GPU); module present in /boot/kernel/radeon.ko.

Why not reproduced in-kernel

The QEMU guest is virtio-only (no AMD GPU): pciconf -l shows no 1002 device; kldstat shows radeon not loaded. /boot/kernel/radeon.ko exists (the vulnerable code ships in the default install) but cannot attach, so radeon_atombios.c parse paths never execute here. All conclusions are source-level (path:line above) plus the on-guest harness (harness_i2c.c).

Harness result

Command: ./run.sh (after ./build.sh) on the DragonFly guest Expected/observed: num_indices=146 vs i2c_bus[16]; 130 OOB pointer writes (1040 bytes); worst case 2324 (18592 bytes); exit_code=0 Full logs: build.log, run.log.

Impact / exploit chain

crafted VBIOS GPIO_I2C_Info table with size>=4+17*28=480 -> i2c valid entries -> heap pointer corruption inside struct radeon_device at driver attach (boot or kldload radeon) -> crash/DoS, potential further exploitation via corrupted adjacent structures (locks, vm_manager)

Fix

cap the loop at RADEON_MAX_I2C_BUS β€” fix.diff.

Honest notes

  • DF-3739 does NOT re-report pass-1 findings DF-1198/1199/1200 (power-table state_index mismatch, asic_ss_info underflow, power-table indices) β€” different functions/tables/fields.
  • fix validation not possible on this guest (no hardware); fix.diff is source-authored only.

Fix status (batch validation)

Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.

VERDICT.md
↓ download raw

VERDICT β€” DF-3739

Status: inconclusive (source-certain; harness-reproduced arithmetic; kernel path unreachable on this guest) reproduced: 0 (in-kernel) β€” harness demonstration: YES Impact on real hardware: High β€” CWE-787 Out-of-bounds Write

Finding

Source-certain heap-corruption write in the radeon ATOM I2C init path. atom_parse_data_header() (atom.c:1366-1385) returns the GPIO_I2C_Info table's self-declared u16 size with NO validation; radeon_atombios_i2c_init() derives num_indices=(size-4)/28 (up to 2340 with size=0xFFFF) and stores radeon_i2c_create() heap pointers into rdev->i2c_bus[i] (radeon.h:2440) whose capacity is RADEON_MAX_I2C_BUS=16 (radeon_mode.h:96). i2c.valid is BIOS-controlled (usClkMaskRegisterIndex!=0, :113-116), so every OOB store is attacker-gated. Entries 16..num_indices-1 overwrite the following radeon_device fields (debugfs[32], debugfs_count, vm_manager, gpu_clock_mutex, atif/atcs, ... radeon.h:2441+). Harness on guest: size=4092 -> 146 entries -> 130 OOB pointer writes (1040 B); worst case 0xFFFF -> 2324 OOB writes (18.5 KB). Not executable in-kernel on this guest (no AMD GPU); module present in /boot/kernel/radeon.ko.

Why not reproduced in-kernel

The QEMU guest is virtio-only (no AMD GPU): pciconf -l shows no 1002 device; kldstat shows radeon not loaded. /boot/kernel/radeon.ko exists (the vulnerable code ships in the default install) but cannot attach, so radeon_atombios.c parse paths never execute here. All conclusions are source-level (path:line above) plus the on-guest harness (harness_i2c.c).

Harness result

Command: ./run.sh (after ./build.sh) on the DragonFly guest Expected/observed: num_indices=146 vs i2c_bus[16]; 130 OOB pointer writes (1040 bytes); worst case 2324 (18592 bytes); exit_code=0 Full logs: build.log, run.log.

Impact / exploit chain

crafted VBIOS GPIO_I2C_Info table with size>=4+17*28=480 -> i2c valid entries -> heap pointer corruption inside struct radeon_device at driver attach (boot or kldload radeon) -> crash/DoS, potential further exploitation via corrupted adjacent structures (locks, vm_manager)

Fix

cap the loop at RADEON_MAX_I2C_BUS β€” fix.diff.

Honest notes

  • DF-3739 does NOT re-report pass-1 findings DF-1198/1199/1200 (power-table state_index mismatch, asic_ss_info underflow, power-table indices) β€” different functions/tables/fields.
  • fix validation not possible on this guest (no hardware); fix.diff is source-authored only.

Fix status (batch validation)

Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.

Fix verification

not_testable
baseline no→ patch + rebuild →patched clean

fix.diff authored after source-level verification; cannot be validated on this guest (no AMD GPU; radeon.ko cannot attach). Compiling the module or booting a patched kernel would not exercise the path without hardware.

fix.diff (unified, against read-only sys/ tree; never applied)
↓ fix.diffper-fix-DF-3739

Confirmed kernel references

Detail

Exploit chain

crafted VBIOS GPIO_I2C_Info table with size>=4+17*28=480 -> i2c valid entries -> heap pointer corruption inside struct radeon_device at driver attach (boot or kldload radeon) -> crash/DoS, potential further exploitation via corrupted adjacent structures (locks, vm_manager)

Evidence (decisive lines)

harness_i2c.c + build.log + run.log; VERDICT.md; kernel refs sys/dev/drm/radeon/radeon_atombios.c:172-187

PoC changes

harness written from scratch (no seed PoC): verbatim kernel arithmetic replica with canary-guarded destination model, built and executed on the DragonFly guest (cc 8.3); fixed nested-comment, stddef/stdint includes and volatile divisors during bring-up

Verified recommended fix

cap the loop at RADEON_MAX_I2C_BUS

Verdict

Source-certain heap-corruption write in the radeon ATOM I2C init path. atom_parse_data_header() (atom.c:1366-1385) returns the GPIO_I2C_Info table's self-declared u16 size with NO validation; radeon_atombios_i2c_init() derives num_indices=(size-4)/28 (up to 2340 with size=0xFFFF) and stores radeon_i2c_create() heap pointers into rdev->i2c_bus[i] (radeon.h:2440) whose capacity is RADEON_MAX_I2C_BUS=16 (radeon_mode.h:96). i2c.valid is BIOS-controlled (usClkMaskRegisterIndex!=0, :113-116), so every OOB store is attacker-gated. Entries 16..num_indices-1 overwrite the following radeon_device fields (debugfs[32], debugfs_count, vm_manager, gpu_clock_mutex, atif/atcs, ... radeon.h:2441+). Harness on guest: size=4092 -> 146 entries -> 130 OOB pointer writes (1040 B); worst case 0xFFFF -> 2324 OOB writes (18.5 KB). Not executable in-kernel on this guest (no AMD GPU); module present in /boot/kernel/radeon.ko.