sq_edc_source_names[] indexed by unbounded 3-bit SQ_EDC_INFO.SOURCE field: OOB rodata pointer handed to %s
Summary
SQ_EDC_INFO.SOURCE is a 3-bit field (0..7) but sq_edc_source_names[] has 7 entries; the DRM_INFO uses sq_edc_source_names[sq_edc_source] guarded only by != -1. Reserved encoding 7 reads the rodata pointer after the array and %s walks it: fault inside the printk formatter during SQ interrupt handling (panic in an error-reporting path) or printing of adjacent static rodata to dmesg. Hardware EDC event latching the reserved source encoding; not directly commandable by unprivileged GPU users - robustness of the error path with panic side effect and minor static-content disclosure to dmesg.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3600 Β· 7 files| File | Type | Description | Size | |
|---|---|---|---|---|
| run.sh | β | 89 B | view raw | |
| run.log | β | 1003 B | view raw | |
| README.md | β | 2.0 KB | β raw | |
| VERDICT.md | β | 2.3 KB | β raw | |
| fix.diff | β | 575 B | view raw | |
| manifest.json | β | 863 B | view raw | |
| verdict.json | β | 2.0 KB | view raw |
DF-3600 β sq_edc_source_names[] indexed by unbounded 3-bit HW field (OOB rodata read -> %s)
What
gfx_v8_0_parse_sq_irq() reads the EDC source from hardware:
gfx_v8_0.c:6811
sq_edc_source = REG_GET_FIELD(RREG32(mmSQ_EDC_INFO), SQ_EDC_INFO, SOURCE);
SQ_EDC_INFO__SOURCE_MASK = 0x1c0 (3-bit field,
sys/dev/drm/amd/include/asic_reg/gca/gfx_8_0_sh_mask.h:14083) β values 0..7,
but the lookup table has only 7 entries:
gfx_v8_0.c:710-718
static const char * const sq_edc_source_names[] = {
"SQ_EDC_INFO_SOURCE_INVALID", ... "SQ_EDC_INFO_SOURCE_TA", /* 7 entries */
};
and the printf at :6831 does
sq_edc_source_names[sq_edc_source] with no bound check. When the field
reads 7 (reserved encoding β real on parts where the EDC source latches
garbage, e.g. uncorrected-but-unmapped EDC events), index 7 reads one
pointer past the array in kernel rodata and %s walks it:
- if the adjacent qword is not a valid string pointer β kernel page fault from the log formatter while handling an SQ interrupt (panic from what is supposed to be an error-reporting path), or
- if it happens to be a valid pointer, it prints unrelated kernel rodata to dmesg (minor info disclosure of static kernel content).
Path: gfx_v8_0_sq_irq :6848-6867 β gfx_v8_0_parse_sq_irq :6769 (both
ISR and work-queue contexts; in_task() only gates whether the SQ_EDC_INFO
register is read β in ISR context sq_edc_source stays -1 and is safe; the
unbounded index is the work-queue/task path).
Trigger / severity
Requires the GPU to raise an SQ interrupt with EDC/ECC encoding whose SQ_EDC_INFO.SOURCE latches 7 β a hardware error-event path (not directly commandable by unprivileged GPU users), so: Low, defense-in-depth with a panic side effect.
Reproduce
Source assertion (guest is virtio-only, no AMD HW):
./run.sh
prints the 7-entry array, the 3-bit mask, and the unbounded index site.
Fix
fix.diff: bound the index with ARRAY_SIZE(sq_edc_source_names).
DF-3600 β VERDICT
status: inconclusive (kernel run) / source certain reproduced: 0
Proof
sq_edc_source_names[](gfx_v8_0.c:710-718) has exactly 7 elements (INVALID, INST, SGPR, VGPR, LDS, GDS, TA).SQ_EDC_INFO__SOURCE_MASK = 0x1c0,__SHIFT = 0x6(gfx_8_0_sh_mask.h:14083-14084) β a 3-bit field, representable range 0..7. Encoding 7 is reserved/invalid on the hardware side.gfx_v8_0_parse_sq_irq:6811 stores the field via REG_GET_FIELD, and the DRM_INFO at :6831 evaluates(sq_edc_source != -1) ? sq_edc_source_names[sq_edc_source] : "unavailable". The only guard is!= -1(the ISR-context sentinel); nothing bounds the value to the array. Source == 7 βsq_edc_source_names[7]reads the rodata qword after the array and hands it to%s:- invalid pointer β fault inside the printk/%s formatter while servicing an SQ interrupt (kernel panic in the error handler), or
-
valid pointer β adjacent kernel rodata printed to dmesg (minor static- content disclosure).
-
Trigger: SQ interrupt with enc 1/2 processed in task context (
gfx_v8_0_sq_irq:6848-6867 β work func :6839-6846) while SQ_EDC_INFO.SOURCE latches the reserved encoding 7 β a hardware EDC event path, not directly controllable by unprivileged GPU users. Severity Low (robustness of an error-reporting path).
Why not a kernel run
Audit guest is virtio-only (no AMD GPU, no drm/amdgpu module, /boot/modules
absent). inconclusive at runtime; index/mask mismatch proven from source
(run.log).
Fix validation
fix.diff bounds the index with ARRAY_SIZE(sq_edc_source_names). Not
buildable on this guest β fix_status: not_testable; diff line-matched to
gfx_v8_0.c:6831.
kernel_refs
- sys/dev/drm/amd/amdgpu/gfx_v8_0.c:710-718 (7-entry array)
- sys/dev/drm/amd/amdgpu/gfx_v8_0.c:6811 (3-bit field read)
- sys/dev/drm/amd/amdgpu/gfx_v8_0.c:6831 (unbounded index + %s)
- sys/dev/drm/amd/include/asic_reg/gca/gfx_8_0_sh_mask.h:14083 (mask 0x1c0)
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 adds the ARRAY_SIZE bound at :6831; not buildable/testable on the virtio-only guest (amdgpu module absent).
findings/poc/DF-3600/fix.diff
Confirmed kernel references
Detail
Evidence (decisive lines)
run.log (7 entries vs 3-bit mask vs unguarded index); VERDICT.md; fix.diff
PoC changes
authored fresh (pass 2)
Verified recommended fix
Bound the index: use sq_edc_source only when 0 <= sq_edc_source < ARRAY_SIZE(sq_edc_source_names); see fix.diff.
Verdict
gfx_v8_0_parse_sq_irq reads the 3-bit SQ_EDC_INFO.SOURCE field (mask 0x1c0, gfx_8_0_sh_mask.h:14083 => 0..7) at gfx_v8_0.c:6811 and prints sq_edc_source_names[sq_edc_source] at :6831 with only a != -1 guard; the array (gfx_v8_0.c:710-718) has 7 entries, so reserved encoding 7 indexes one pointer past the array in kernel rodata and hands it to %s - fault inside the printk formatter during SQ interrupt handling (panic in an error-reporting path) or printing of adjacent static rodata to dmesg. HW-error-event triggered, not directly attacker-commandable -> Low. Not executable on the virtio-only audit guest -> inconclusive at runtime; mismatch proven from source.
No comments yet.