Connector/encoder object-table walks dereference unvalidated VBIOS offsets, accumulate attacker sizes, and over-index ucMuxState[2] β OOB heap reads at connector init
Summary
radeon_get_atom_connector_info_from_object_table dereferences seven unvalidated u16 table offsets; the display-path walk accumulates attacker u16 usSize values (two 0x8000 sizes put the record pointer 64 KiB out); per-object usRecordOffset/usSrcDstTableOffset are dereferenced raw; router enum_id runs to a BIOS u8 count (<=255) then indexes ucMuxState[enum_id] though the array is UCHAR[2] (up to 253 B OOB); record chains step by ucRecordSize with no image-end bound. HARNESS: path walk OOB, raw deref of record offset 0xFFF0, enum_id=255 into ucMuxState[2], record chain crossing the image end. Crafted Object_Header: OOB reads of adjacent kernel heap at radeon_modeset_init -> crash; garbage propagates into connector/encoder configuration. Distinct surface from DF-1200's power-table indices.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3745 Β· 13 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.4 KB | β raw | |
| VERDICT.md | β | 2.9 KB | β raw | |
| build.log | β | 0 B | β download | |
| build.sh | β | 65 B | view raw | |
| env.txt | β | 375 B | view raw | |
| harness_objtable.c | β | 4.5 KB | view raw | |
| run.log | β | 739 B | view raw | |
| run.sh | β | 108 B | view raw | |
| README.md | β | 2.4 KB | β raw | |
| VERDICT.md | β | 2.9 KB | β raw | |
| manifest.json | β | 1.1 KB | view raw | |
| verdict.json | β | 3.4 KB | view raw | |
| fix.diff | β | 1004 B | view raw |
DF-3745 β Connector/encoder object-table walks dereference unvalidated VBIOS offsets and indices β OOB heap reads at connector init
Severity: Medium Β· CWE: CWE-125 Out-of-bounds Read Β· File: sys/dev/drm/radeon/radeon_atombios.c:537-548, 551-557, 654-656, 682-698, 732-740, 660-672, 772-803
What this is
radeon_get_atom_connector_info_from_object_table() (the DCE4+ connector path) dereferences a web of unvalidated BIOS values: (1) table bases from u16 offsets usStateArrayOffset/usClockInfoArrayOffset/usNonClockInfoArrayOffset/usDisplayPathTableOffset/usConnectorObjectTableOffset/usEncoderObjectTableOffset/usRouterObjectTableOffset (:537-548); (2) the display-path walk accumulates attacker u16 usSize values (:551-557) β two 0x8000 sizes put the record pointer 64 KiB out; (3) per-object usRecordOffset/usSrcDstTableOffset u16 offsets dereferenced immediately (:654-656, :685-698); (4) router src/dst: enum_id runs to *num_dst_objs (u8<=255) then indexes ucMuxState[enum_id] though ucMuxState is UCHAR[2] (atombios.h:4380-4395) β up to 253 B OOB (:732/:740); (5) record chains stepped by ucRecordSize with no image-end bound (:660-672, :772-803). All of this reads past the rdev->bios heap image at boot/attach (values feed connector config; distinct surface from the power-table indices of DF-1200). Harness on guest demonstrates each sub-case. Kernel path unreachable on this guest.
Reproduce
./build.sh # cc -O1 -g -Wall -o <bin> harness_objtable.c (DragonFly guest, cc 8.3) ./run.sh # expected: path walk goes OOB at path 1 (offset 0x6666 > 4096-byte image); record offset 0xFFF0 deref; enum_id=255 vs ucMuxState[2]; chain walks past image end; 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
validate every BIOS offset/size against the image length before use (needs the bios_size plumbing of DF-3744) β see fix.diff (authored after verification; never applied to sys/).
DF-3745 β Connector/encoder object-table walks dereference unvalidated VBIOS offsets and indices β OOB heap reads at connector init
Severity: Medium Β· CWE: CWE-125 Out-of-bounds Read Β· File: sys/dev/drm/radeon/radeon_atombios.c:537-548, 551-557, 654-656, 682-698, 732-740, 660-672, 772-803
What this is
radeon_get_atom_connector_info_from_object_table() (the DCE4+ connector path) dereferences a web of unvalidated BIOS values: (1) table bases from u16 offsets usStateArrayOffset/usClockInfoArrayOffset/usNonClockInfoArrayOffset/usDisplayPathTableOffset/usConnectorObjectTableOffset/usEncoderObjectTableOffset/usRouterObjectTableOffset (:537-548); (2) the display-path walk accumulates attacker u16 usSize values (:551-557) β two 0x8000 sizes put the record pointer 64 KiB out; (3) per-object usRecordOffset/usSrcDstTableOffset u16 offsets dereferenced immediately (:654-656, :685-698); (4) router src/dst: enum_id runs to *num_dst_objs (u8<=255) then indexes ucMuxState[enum_id] though ucMuxState is UCHAR[2] (atombios.h:4380-4395) β up to 253 B OOB (:732/:740); (5) record chains stepped by ucRecordSize with no image-end bound (:660-672, :772-803). All of this reads past the rdev->bios heap image at boot/attach (values feed connector config; distinct surface from the power-table indices of DF-1200). Harness on guest demonstrates each sub-case. Kernel path unreachable on this guest.
Reproduce
./build.sh # cc -O1 -g -Wall -o <bin> harness_objtable.c (DragonFly guest, cc 8.3) ./run.sh # expected: path walk goes OOB at path 1 (offset 0x6666 > 4096-byte image); record offset 0xFFF0 deref; enum_id=255 vs ucMuxState[2]; chain walks past image end; 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
validate every BIOS offset/size against the image length before use (needs the bios_size plumbing of DF-3744) β see fix.diff (authored after verification; never applied to sys/).
VERDICT β DF-3745
Status: inconclusive (source-certain; harness-reproduced arithmetic; kernel path unreachable on this guest) reproduced: 0 (in-kernel) β harness demonstration: YES Impact on real hardware: Medium β CWE-125 Out-of-bounds Read
Finding
radeon_get_atom_connector_info_from_object_table() (the DCE4+ connector path) dereferences a web of unvalidated BIOS values: (1) table bases from u16 offsets usStateArrayOffset/usClockInfoArrayOffset/usNonClockInfoArrayOffset/usDisplayPathTableOffset/usConnectorObjectTableOffset/usEncoderObjectTableOffset/usRouterObjectTableOffset (:537-548); (2) the display-path walk accumulates attacker u16 usSize values (:551-557) β two 0x8000 sizes put the record pointer 64 KiB out; (3) per-object usRecordOffset/usSrcDstTableOffset u16 offsets dereferenced immediately (:654-656, :685-698); (4) router src/dst: enum_id runs to *num_dst_objs (u8<=255) then indexes ucMuxState[enum_id] though ucMuxState is UCHAR[2] (atombios.h:4380-4395) β up to 253 B OOB (:732/:740); (5) record chains stepped by ucRecordSize with no image-end bound (:660-672, :772-803). All of this reads past the rdev->bios heap image at boot/attach (values feed connector config; distinct surface from the power-table indices of DF-1200). Harness on guest demonstrates each sub-case. Kernel path unreachable on this guest.
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_objtable.c).
Harness result
Command: ./run.sh (after ./build.sh) Expected/observed: path walk goes OOB at path 1 (offset 0x6666 > 4096-byte image); record offset 0xFFF0 deref; enum_id=255 vs ucMuxState[2]; chain walks past image end; exit_code=0 Full logs: build.log, run.log.
Impact / exploit chain
crafted Object_Header table -> OOB pointer arithmetic at radeon_modeset_init -> reads of adjacent kernel heap -> crash (values also propagate into connector/encoder setup)
Fix
validate every BIOS offset/size against the image length before use (needs the bios_size plumbing of DF-3744) β fix.diff.
Honest notes
- DF-3745 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 β DF-3745
Status: inconclusive (source-certain; harness-reproduced arithmetic; kernel path unreachable on this guest) reproduced: 0 (in-kernel) β harness demonstration: YES Impact on real hardware: Medium β CWE-125 Out-of-bounds Read
Finding
radeon_get_atom_connector_info_from_object_table() (the DCE4+ connector path) dereferences a web of unvalidated BIOS values: (1) table bases from u16 offsets usStateArrayOffset/usClockInfoArrayOffset/usNonClockInfoArrayOffset/usDisplayPathTableOffset/usConnectorObjectTableOffset/usEncoderObjectTableOffset/usRouterObjectTableOffset (:537-548); (2) the display-path walk accumulates attacker u16 usSize values (:551-557) β two 0x8000 sizes put the record pointer 64 KiB out; (3) per-object usRecordOffset/usSrcDstTableOffset u16 offsets dereferenced immediately (:654-656, :685-698); (4) router src/dst: enum_id runs to *num_dst_objs (u8<=255) then indexes ucMuxState[enum_id] though ucMuxState is UCHAR[2] (atombios.h:4380-4395) β up to 253 B OOB (:732/:740); (5) record chains stepped by ucRecordSize with no image-end bound (:660-672, :772-803). All of this reads past the rdev->bios heap image at boot/attach (values feed connector config; distinct surface from the power-table indices of DF-1200). Harness on guest demonstrates each sub-case. Kernel path unreachable on this guest.
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_objtable.c).
Harness result
Command: ./run.sh (after ./build.sh) Expected/observed: path walk goes OOB at path 1 (offset 0x6666 > 4096-byte image); record offset 0xFFF0 deref; enum_id=255 vs ucMuxState[2]; chain walks past image end; exit_code=0 Full logs: build.log, run.log.
Impact / exploit chain
crafted Object_Header table -> OOB pointer arithmetic at radeon_modeset_init -> reads of adjacent kernel heap -> crash (values also propagate into connector/encoder setup)
Fix
validate every BIOS offset/size against the image length before use (needs the bios_size plumbing of DF-3744) β fix.diff.
Honest notes
- DF-3745 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_testablefix.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)
Confirmed kernel references
- sys/dev/drm/radeon/radeon_atombios.c:536-548
- sys/dev/drm/radeon/radeon_atombios.c:551-557
- sys/dev/drm/radeon/radeon_atombios.c:654-656
- sys/dev/drm/radeon/radeon_atombios.c:682-698
- sys/dev/drm/radeon/radeon_atombios.c:703-740
- sys/dev/drm/radeon/radeon_atombios.c:772-803
- sys/dev/drm/radeon/atombios.h:4380-4395
Detail
Exploit chain
crafted Object_Header table -> OOB pointer arithmetic at radeon_modeset_init -> reads of adjacent kernel heap -> crash (values also propagate into connector/encoder setup)
Evidence (decisive lines)
harness_objtable.c + build.log + run.log; VERDICT.md; kernel refs sys/dev/drm/radeon/radeon_atombios.c:536-548
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
validate every BIOS offset/size against the image length before use (needs the bios_size plumbing of DF-3744)
Verdict
radeon_get_atom_connector_info_from_object_table() (the DCE4+ connector path) dereferences a web of unvalidated BIOS values: (1) table bases from u16 offsets usStateArrayOffset/usClockInfoArrayOffset/usNonClockInfoArrayOffset/usDisplayPathTableOffset/usConnectorObjectTableOffset/usEncoderObjectTableOffset/usRouterObjectTableOffset (:537-548); (2) the display-path walk accumulates attacker u16 usSize values (:551-557) β two 0x8000 sizes put the record pointer 64 KiB out; (3) per-object usRecordOffset/usSrcDstTableOffset u16 offsets dereferenced immediately (:654-656, :685-698); (4) router src/dst: enum_id runs to *num_dst_objs (u8<=255) then indexes ucMuxState[enum_id] though ucMuxState is UCHAR[2] (atombios.h:4380-4395) β up to 253 B OOB (:732/:740); (5) record chains stepped by ucRecordSize with no image-end bound (:660-672, :772-803). All of this reads past the rdev->bios heap image at boot/attach (values feed connector config; distinct surface from the power-table indices of DF-1200). Harness on guest demonstrates each sub-case. Kernel path unreachable on this guest.
No comments yet.