amdgpu atom_index_iio indexes the 10-entry static atom_iio_len[] with an unvalidated VBIOS opcode byte (OOB read of kernel .data) and the garbage stride drives an unbounded walk past the BIOS image at attach (DF-4338's amdgpu twin)
Summary
Byte-identical to the radeon twin: the IIO table walk indexes the 10-entry static table with a 0..255 opcode byte (up to ~990B past it in kernel .data) and the garbage stride walks past the bios allocation at attach. Guest mirror: opcode 200 -> SIGSEGV; fixed logic stops clean. Opcode-bound fix (full walk bounded by the DF-4345 port).
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4346 Β· 14 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 1.1 KB | β raw | |
| VERDICT.md | β | 2.1 KB | β raw | |
| atom_harness.c | β | 14.3 KB | view raw | |
| atom_harness_fixed.c | β | 11.4 KB | view raw | |
| build.sh | β | 95 B | view raw | |
| run.sh | β | 99 B | view raw | |
| run_fixed.sh | β | 233 B | view raw | |
| build.log | β | 26 B | view raw | |
| run.log | β | 815 B | view raw | |
| fix_run.log | β | 1.1 KB | view raw | |
| env.txt | β | 382 B | view raw | |
| fix.diff | β | 845 B | view raw | |
| manifest.json | β | 684 B | view raw | |
| verdict.json | β | 2.3 KB | view raw |
DF-4346 β amdgpu atom_index_iio: unvalidated VBIOS opcode indexes 10-entry atom_iio_len[] (kernel .data OOB) and drives an unbounded walk past the BIOS image
sys/dev/drm/amd/amdgpu/atom.c:1291 β base += atom_iio_len[CU8(base)]
with an opcode byte straight from the VBIOS (0..255) into the 10-entry
static atom_iio_len[] (atom.c:1280). Out-of-range opcodes read past
the array in kernel .data; the resulting stride walks the IIO table
past the bios allocation at driver attach. Amdgpu twin of DF-4338
(radeon), previously unfiled for this file.
Reproduce (logic level β guest has no AMD GPU)
cc -O1 -g -o atom_harness atom_harness.c ./atom_harness iio # opcode 200 -> atom_iio_len[200] -> SIGSEGV/139 ./atom_harness_fixed iio # op > ATOM_IIO_END -> clean stop, exit 0
See run.log case iio and fix_run.log. Kernel-runtime validation
requires an AMD card with crafted VBIOS (not emulatable in QEMU).
Fix
fix.diff β bound the opcode before indexing (return on
op > ATOM_IIO_END); full bios_size-bounded walk in the DF-4345 port.
DF-4346 VERDICT β atom_index_iio OOB opcode index + unbounded walk (amdgpu twin of DF-4338)
Status: inconclusive (kernel runtime hardware-gated; logic-level OOB reproduced on the audit guest). Defect: certain.
Root cause
atom_index_iio (atom.c:1280-1294), byte-identical to the radeon twin
DF-4338 fixed:
1287: while (CU8(base) == ATOM_IIO_START) {
1288: ctx->iio[CU8(base + 1)] = base + 2;
1289: base += 2;
1290: while (CU8(base) != ATOM_IIO_END)
1291: base += atom_iio_len[CU8(base)];
1292: base += 3;
1293: }
atom_iio_len(atom.c:1280) has 10 entries (ATOM_IIO_NOP..ATOM_IIO_END = 0..9). The opcode byteCU8(base)is unvalidated VBIOS data (0..255):atom_iio_len[CU8(base)]reads up to 990 bytes past the static array in kernel .data (info leak into the walk, garbage stride).- The garbage (or a crafted in-range) stride then drives
baseforward with no bound β the walk reads past the bios allocation at attach until it happens to hit ATOM_IIO_END or faults.
Runs at driver attach via amdgpu_atom_parse (atom.c:1334) on the
IIO pointer taken from the master data table.
Reproduction
Guest (no AMD GPU): ./atom_harness iio β verbatim loop, atom_iio_len
placed at a guard page. Opcode byte 200 β atom_iio_len[200] β
SIGSEGV exit=139 (run.log, case iio). Fixed logic
(./atom_harness_fixed iio) rejects op > ATOM_IIO_END and bounds the
walk β exit 0 (fix_run.log).
Fix
fix.diff β the standalone opcode-bound fix (same shape as DF-4338's):
validate the opcode against ATOM_IIO_END before indexing, and stop the
walk. The complete bound (walk also limited by bios_size) is included
in the central DF-4345 port.
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
inconclusivefix.diff (standalone opcode bound, same shape as DF-4338's) passes git apply --check; fixed-logic mirror rejects opcode 200 without trapping. No kernel runtime possible on this guest.
fix.diff; fix_run.log case iio
Confirmed kernel references
Detail
Evidence (decisive lines)
run.log case iio (exit=139 at atom_iio_len[200]); fix_run.log case iio (clean stop, exit 0); fix.diff (git apply --check OK)
PoC changes
iio case carried in the shared amdgpu harness (guarded atom_iio_len, opcode 200)
Verified recommended fix
Validate the IIO opcode against ATOM_IIO_END before indexing atom_iio_len[] and bound the walk by the bios image length
Verdict
Code defect certain: atom_index_iio (amd/amdgpu/atom.c:1291) indexes the 10-entry static atom_iio_len[] with an unvalidated VBIOS opcode byte (0..255) -- an OOB read of kernel .data whose garbage stride then drives an unbounded forward walk past the bios allocation at driver attach (atom.c:1287-1293, called from amdgpu_atom_parse at atom.c:1334). Byte-identical to the radeon twin DF-4338 fixed; previously unfiled for amdgpu. Kernel runtime hardware-gated (no AMD GPU on the guest); the verbatim loop mirrored on the guest with atom_iio_len at a guard page: opcode 200 -> SIGSEGV exit 139 (run.log case iio); fixed logic (op > ATOM_IIO_END -> return) stops cleanly, exit 0 (fix_run.log).
No comments yet.