Wild OOB kernel read in ci_get_std_voltage_value_sidd when the CAC leakage table count is 0: idx=(u16)(count-1)=65535 past a ZERO_LENGTH_PTR that passes the entries!=NULL guard
Summary
With ucNumEntries==0, kzalloc(0) returns ZERO_LENGTH_PTR ((void*)-8 on DragonFly) which is non-NULL, so the guard passes; idx = (u16)(count-1) = 65535 and entries[65535].vddc/.leakage reads ~512KB past a 0-byte allocation -> unmapped kernel VA -> page fault/panic (or uncontrolled kernel-memory read). Twin of DF-3587/DF-3620.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3649 Β· 12 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | doc | 919 B | β raw | |
| VERDICT.md | doc | 3.3 KB | β raw | |
| fix.diff | doc | 638 B | view raw | |
| env.txt | doc | 799 B | view raw | |
| verdict.json | doc | 3.5 KB | view raw | |
| manifest.json | doc | 1.5 KB | view raw | |
| harness.c | doc | 5.7 KB | view raw | |
| build.sh | doc | 65 B | view raw | |
| run.sh | doc | 89 B | view raw | |
| build.log | log | 0 B | β download | |
| run.log | log | 556 B | view raw | |
| run.2.log | log | 556 B | view raw |
DF-3649 β Wild OOB kernel read in ci_get_std_voltage_value_sidd when the CAC leakage table count is 0: idx = (u16)(count-1) = 65535 past a ZERO_LENGTH_PTR that passes the entries!=NULL guard
File: sys/dev/drm/radeon/ci_dpm.c (pass 2, GLM 5.3 re-audit)
Severity: Medium | CWE-191 Integer Underflow / CWE-125 Out-of-bounds Read | confidence: certain
Reproduce
This pack carries a userland logic harness (harness.c) built and run ON THE
AUDIT GUEST with the exact kernel types; decisive section: [D]
./build.sh ./run.sh
Expected decisive line: count=0 -> idx=65535; wild read address 0x7fff0 (unmapped)
The kernel code path itself requires a Sea Islands radeon GPU + crafted VBIOS; the guest is virtio-only (no AMD PCI device, no /dev/dri, no drm kld) β see env.txt.
Full narrative: VERDICT.md. Proposed fix: fix.diff (authored against the read-only sys/ tree; never applied there).
DF-3649 β verdict narrative
Finding
Wild OOB kernel read in ci_get_std_voltage_value_sidd when the CAC leakage table count is 0: idx = (u16)(count-1) = 65535 past a ZERO_LENGTH_PTR that passes the entries!=NULL guard
Kernel references
sys/dev/drm/radeon/ci_dpm.c:2362sys/dev/drm/radeon/ci_dpm.c:2374sys/dev/drm/radeon/ci_dpm.c:2379sys/dev/drm/radeon/ci_dpm.c:2382sys/dev/drm/radeon/ci_dpm.c:2384sys/dev/drm/radeon/ci_dpm.c:2396sys/dev/drm/radeon/r600_dpm.c:1039sys/dev/drm/radeon/r600_dpm.c:1063sys/kern/kern_slaballoc.c:193sys/kern/kern_slaballoc.c:886
Was it reproduced?
No β not on the kernel path. The audit guest is virtio-only (0 AMD PCI devices, /dev/dri absent, no radeon/drm kld; env.txt), so sys/dev/drm/radeon/ci_dpm.c can never execute there. Per the pass-2 instruction for this hardware-gated file, the verdict is inconclusive (source-level certainty certain) rather than reproduced.
Logic-level proof (guest harness)
harness.c models the exact loop/index/division with the exact kernel types and was compiled and executed on the audit guest (cc -O2 -Wall, DragonFly 6.5-DEVELOPMENT). Decisive output, stable across 2 runs (run.log, run.2.log):
count=0 -> idx=65535; wild read address 0x7fff0 (unmapped)
Root cause and reasoning
Source-certain wild read, radeon/ci twin of DF-3587 (amdgpu) / DF-3620 (amdgpu ci). r600_parse_extended_power_table allocates cac_leakage_table.entries with kzalloc(ucNumEntries * entry_size) (r600_dpm.c:1039-1040) and sets count = ucNumEntries (1063). With a crafted usCACLeakageTableOffset whose table declares 0 entries, DragonFly kmalloc(0) returns ZERO_LENGTH_PTR == (void *)-8 (kern_slaballoc.c:193, 886-890) - NON-NULL - so the entries!=NULL guard at ci_dpm.c:2374 passes. The match loops then compute idx = rdev->pm.dpm.dyn_state.cac_leakage_table.count - 1 (ci_dpm.c:2382, 2399); with count==0 the u32 0-1 truncated into u16 idx yields 65535, and entries[65535].vddc / .leakage (2384-2386, 2401-2403) reads ~512KB past a 0-byte allocation - unmapped kernel VA -> page fault/panic (or uncontrolled kernel-memory read if the address happens to be mapped). Requires voltage_control != NONE and any vddc match in vddc_dependency_on_sclk (count>=1 enforced by ci_setup_default_dpm_tables). On-guest harness section [D] demonstrated idx=65535 and the resulting wild address. Kernel path unreachable on this GPU-less guest; inconclusive overall.
Exploitability
Crafted VBIOS: SVID2/GPIO VDDC voltage object + a CACLeakageTable with ucNumEntries=0 (kzalloc(0) -> ZERO_LENGTH_PTR passes the non-NULL guard) + any sclk dependency entry whose voltage matches/eclipses the SVI2 table value -> entries[65535] wild read. Primary impact panic; if the wild address were mapped the read leaks into StdVoltageHiSidd/LoSidd which are shipped to the SMC (not directly userspace-visible). DoS-grade.
Recommended fix
Require entries != NULL AND count > 0 before using the CAC leakage table in ci_get_std_voltage_value_sidd. See fix.diff.
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 validation would require a CI-class GPU with a crafted VBIOS (baseline panic/corruption) and a single-fix kernel build; the audit guest is virtio-only, so the fix is source-reviewed only (fix.diff).
fix.diff in this pack; not applied to sys/ (read-only audit target).
Confirmed kernel references
- sys/dev/drm/radeon/ci_dpm.c:2362
- sys/dev/drm/radeon/ci_dpm.c:2374
- sys/dev/drm/radeon/ci_dpm.c:2379
- sys/dev/drm/radeon/ci_dpm.c:2382
- sys/dev/drm/radeon/ci_dpm.c:2384
- sys/dev/drm/radeon/ci_dpm.c:2396
- sys/dev/drm/radeon/r600_dpm.c:1039
- sys/dev/drm/radeon/r600_dpm.c:1063
- sys/kern/kern_slaballoc.c:193
- sys/kern/kern_slaballoc.c:886
Detail
Exploit chain
Crafted VBIOS: SVID2/GPIO VDDC voltage object + a CACLeakageTable with ucNumEntries=0 (kzalloc(0) -> ZERO_LENGTH_PTR passes the non-NULL guard) + any sclk dependency entry whose voltage matches/eclipses the SVI2 table value -> entries[65535] wild read. Primary impact panic; if the wild address were mapped the read leaks into StdVoltageHiSidd/LoSidd which are shipped to the SMC (not directly userspace-visible). DoS-grade.
Evidence (decisive lines)
VERDICT.md (full source trace); harness.c + run.log/run.2.log section [D] (count=0 -> idx=65535; wild read address 0x7fff0 (unmapped)); env.txt (guest has no AMD GPU: pciconf 0x1002 count=0, /dev/dri absent, no drm kld); fix.diff
PoC changes
n/a β no seeded PoC for pass-2; harness written fresh (models the exact kernel types; divisors made volatile so -O2 cannot fold the #DE)
Verified recommended fix
Require entries != NULL AND count > 0 before using the CAC leakage table in ci_get_std_voltage_value_sidd.
Verdict
Source-certain wild read, radeon/ci twin of DF-3587 (amdgpu) / DF-3620 (amdgpu ci). r600_parse_extended_power_table allocates cac_leakage_table.entries with kzalloc(ucNumEntries * entry_size) (r600_dpm.c:1039-1040) and sets count = ucNumEntries (1063). With a crafted usCACLeakageTableOffset whose table declares 0 entries, DragonFly kmalloc(0) returns ZERO_LENGTH_PTR == (void *)-8 (kern_slaballoc.c:193, 886-890) - NON-NULL - so the entries!=NULL guard at ci_dpm.c:2374 passes. The match loops then compute idx = rdev->pm.dpm.dyn_state.cac_leakage_table.count - 1 (ci_dpm.c:2382, 2399); with count==0 the u32 0-1 truncated into u16 idx yields 65535, and entries[65535].vddc / .leakage (2384-2386, 2401-2403) reads ~512KB past a 0-byte allocation - unmapped kernel VA -> page fault/panic (or uncontrolled kernel-memory read if the address happens to be mapped). Requires voltage_control != NONE and any vddc match in vddc_dependency_on_sclk (count>=1 enforced by ci_setup_default_dpm_tables). On-guest harness section [D] demonstrated idx=65535 and the resulting wild address. Kernel path unreachable on this GPU-less guest; inconclusive overall.
No comments yet.