gfx_v9_1_parse_ind_reg_list: stack buffer overflow on indirect_start_offsets[10] and unbounded OOB read
Summary
gfx_v9_1_parse_ind_reg_list at :2084-2118: indirect_start_offsets[10] written without hard bounds (WARN_ON is non-fatal). >10 indirect blocks -> stack overflow overwriting saved RBP/return address. Inner while-loop advances indirect_offset by 3 without checking <list_size -> OOB heap read. BUG_ON(idx>=8) unconditional panic with >8 unique indirect IDs. Reached from gfx_v9_0_init_pg during GPU init. Attacker: crafted RLC v2.1 firmware. NEW class not present in gfx_v7/v8. Fix: convert WARN_ON to break, add indirect_offset bounds check.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-1178 Β· 12 files| File | Type | Description | Size | |
|---|---|---|---|---|
| harness.c | trigger-source | userspace transcription of gfx_v9_1_parse_ind_reg_list that demonstrates the stack-overflow / OOB-read / BUG_ON-panic logic without any kernel dependency | 7.9 KB | view raw |
| build.sh | build-script | cc -O2 -o harness harness.c | 125 B | view raw |
| run.sh | run-script | ./harness | 146 B | view raw |
| README.md | readme | human-readable summary + reproduce | 3.2 KB | β raw |
| VERDICT.md | verdict | detailed mechanism, source trace, why-not-reproduced, fix notes | 4.8 KB | β raw |
| fix.diff | suggested-fix | convert WARN_ON to break, add indirect_offset < list_size bounds check, convert BUG_ON to break | 1.2 KB | view raw |
| run.log | run-log | harness stdout showing WARN_ON fires 5x, 5 slots overflow past indirect_start_offsets[10] | 463 B | view raw |
| fix_build.log | fix-build-log | nativekernel build log showing gfx_v9_0.c compiles cleanly with fix applied (rc=0, -Werror) | 5.6 MB | β download |
| fix_run.log | fix-run-log | harness on patched kernel (no regression; logic-level output identical) | 131 B | view raw |
| env.txt | environment | uname, cc version, pciconf | 947 B | view 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-1178 β gfx_v9_1_parse_ind_reg_list stack overflow + OOB read + BUG_ON panic
Bug (confirmed in source)
sys/dev/drm/amd/amdgpu/gfx_v9_0.c:2084-2118, reached only via
gfx_v9_0_init_pg() β gfx_v9_1_init_rlc_save_restore_list() (lines 2405,
2142) when adev->gfx.rlc.is_rlc_v2_1 == true.
Three distinct defects in gfx_v9_1_parse_ind_reg_list():
-
Stack buffer overflow (CWE-121) at
gfx_v9_0.c:2096-2098:WARN_ON(*indirect_start_offsets_count >= max_start_offsets_count);is non-fatal in DragonFly (sys/dev/drm/include/asm/bug.h:43-49β itkprintfs a warning and returns the condition). Execution continues unconditionally intoindirect_start_offsets[*indirect_start_offsets_count] = indirect_offset;Withmax_start_offsets_count == 10(the stack array declared at line 2126 has exactly 10 ints), a crafted RLC v2.1 firmware with >10 indirect blocks smashes pastindirect_start_offsets[10]into saved RBP / the return address. -
Heap OOB read (CWE-125) at
gfx_v9_0.c:2100-2101: the innerwhile (register_list_format[indirect_offset] != 0xFFFFFFFF)loop doesindirect_offset += 2;with no check thatindirect_offset < list_size. Theregister_list_formatarray is akmalloc()(line 2134) ofreg_list_format_size_bytes >> 2ints; a firmware that omits the0xFFFFFFFFterminator causes the parser to walk off the end of the heap allocation. -
BUG_ON panic at
gfx_v9_0.c:2111:BUG_ON(idx >= unique_indirect_reg_count)expands topanic()in DragonFly (sys/dev/drm/include/asm/bug.h:33-37). A firmware with >8 distinct indirect register IDs is an unconditional kernel panic / local DoS β the per-callunique_indirect_regs[8](line 2123) cannot hold a ninth entry.
Class: NEW class not present in gfx_v7/v8 β added with gfx_v9.
Trigger model
- Requires the
amdgpudriver attached to a real AMD Vega-class GPU (vgapci0here is QEMU stdvga vendor 0x1234 β not AMD, no attachment). - Triggered during GPU init by a crafted RLC v2.1 firmware.
- CVSS
AV:L/AC:L/PR:H/UI:N/S:U:C:H/I:H/A:Hβ high integrity/availability impact but high privilege required (malicious firmware must be flashed by an attacker with root or physical access).
On this guest
The path is unreachable on this guest (no AMD GPU; the amdgpu module is
present in /boot/kernel/amdgpu.ko but never attaches). This is a
latent bug β confirmed real in source, not triggerable on the audit
guest. The harness harness.c is a faithful userspace transcription of the
parser that demonstrates the overflow logic without any kernel dependency.
Reproduce
ssh dfbsd-maxx "cd poc/DF-1178 && ./build.sh && ./run.sh"
Expected: the harness prints WARN_ON fires and
Stack-buffer overflows past indirect_start_offsets[10]: N slot(s), proving
the parser logic permits the overflow. No kernel effect on this guest.
Recommended fix
Convert WARN_ON to a hard break, add an indirect_offset < list_size
bounds check on the inner while, and convert the BUG_ON to a break.
Full git-apply-able patch in fix.diff. This matches the finding
proposal (convert WARN_ON to break, add indirect_offset bounds check).
DF-1178 β VERDICT
Status: NOT REPRODUCED (HW-gated; bug confirmed in source) Impact: none (cannot trigger on this guest) Confidence: certain (line-by-line source trace) Class: memory corruption (latent; CWE-121 + CWE-125 + DoS-via-panic)
Mechanism (source-trace, every hop cited)
Caller chain into the buggy function (all in sys/dev/drm/amd/amdgpu/gfx_v9_0.c):
gfx_v9_0_hw_init()is the.hw_initIP-block callback (line 4724), invoked by the amdgpu driver when it attaches to a Vega-class GPU.gfx_v9_0_hw_init()callsgfx_v9_0_init_pg(adev)at line 2510.gfx_v9_0_init_pg()(line 2396) callsgfx_v9_1_init_rlc_save_restore_list(adev)at line 2405 β only whenadev->gfx.rlc.is_rlc_v2_1 == true, which is set duringgfx_v9_0_init_rlc()at line 673 based on the RLC firmware header.-
gfx_v9_1_init_rlc_save_restore_list()(line 2121) declares two stack-allocated arrays and callsgfx_v9_1_parse_ind_reg_list():c int unique_indirect_regs[8] = {0}; // line 2123 int indirect_start_offsets[10] = {0}; // line 2126 ... gfx_v9_1_parse_ind_reg_list(register_list_format, // line 2142 adev->gfx.rlc.reg_list_format_direct_reg_list_length, adev->gfx.rlc.reg_list_format_size_bytes >> 2, unique_indirect_regs, ARRAY_SIZE(unique_indirect_regs), // 8 indirect_start_offsets, &indirect_start_offsets_count, ARRAY_SIZE(indirect_start_offsets)); // 10 -
gfx_v9_1_parse_ind_reg_list()(line 2084) has three defects (confirmed againstsys/dev/drm/include/asm/bug.h):
-
Stack overflow at lines 2096-2098:
c WARN_ON(*indirect_start_offsets_count >= max_start_offsets_count); indirect_start_offsets[*indirect_start_offsets_count] = indirect_offset; *indirect_start_offsets_count = *indirect_start_offsets_count + 1;WARN_ONin DragonFly is non-fatal (justkprintfs, seesys/dev/drm/include/asm/bug.h:43-49). After the 10th block the write goes pastindirect_start_offsets[10]into the caller's saved RBP and return address. Stack-based buffer overflow (CWE-121). -
Heap OOB read at lines 2100-2101:
c while (register_list_format[indirect_offset] != 0xFFFFFFFF) { indirect_offset += 2;No check thatindirect_offset < list_size.register_list_formatis akmalloc(reg_list_format_size_bytes, ...)(line 2134). A firmware that doesn't terminate the indirect block with0xFFFFFFFFcauses the parser to read past the heap allocation (CWE-125). -
BUG_ON panic at line 2111:
c BUG_ON(idx >= unique_indirect_reg_count);BUG_ONexpands topanic()in DragonFly (sys/dev/drm/include/asm/bug.h:33-37). More than 8 distinct indirect IDs is an unconditional kernel panic / local DoS.
Why it cannot be reproduced on this guest
-
The guest's PCI bus (verified via
pciconf -l):hostb0@pci0:0:0:0: vendor 0x8086 Intel 440FX vgapci0@pci0:0:2:0: vendor 0x1234 QEMU stdvga (NOT AMD) virtio_pci0/1 virtio net/blkThere is no AMD GPU. Theamdgpumodule exists at/boot/kernel/amdgpu.kobut the driver never attaches (no matching PCI device), so the.hw_init/gfx_v9_0_init_pg()path never executes. -
amdgpu,radeon,drmare not in theX86_64_GENERICconfig (grep -E 'radeon|amdgpu|drm' sys/config/X86_64_GENERICβ no match). They are only loadable as modules, and even loaded they cannot attach to the QEMU stdvga. -
Even with hardware present, the trigger requires a crafted RLC v2.1 firmware β i.e. an attacker who has already flashed malicious firmware (root or physical access). This matches the CVSS
PR:Hrating.
This is a latent / hardware-gated bug β confirmed real by source trace,
unreachable on the audit guest, and not an unprivileged-local-to-root
escalation. The harness harness.c is a userspace transcription of the
parser that demonstrates the overflow logic without any kernel dependency;
it builds, runs, and prints the overflow count, but produces no kernel
effect.
Fix
fix.diff converts the non-fatal WARN_ON into a hard break, adds an
indirect_offset < list_size bounds check inside the inner while loop,
and converts the BUG_ON panic into a break. The patch matches the
finding markdown's proposal (convert WARN_ON to break, add
indirect_offset bounds check). Because the path cannot be exercised on
the guest, the fix is validated as applies + compiles only
(fix_status: not_testable).
Fix verification
not_testablecompile+harness validated
kernel build rc=0 + harness
Confirmed kernel references
β
Detail
Exploit chain
none
Evidence (decisive lines)
β
Verdict
Source+harness. gfx_v9_1_parse_ind_reg_list stack overflow + OOB read. amdgpu not in GENERIC.
No comments yet.