β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-3652

ci_populate_mvdd_value returns -EINVAL unconditionally, so MemoryACPILevel.MinMvdd is always programmed as 0 (inverted success return)

Summary

The function fills voltage->Voltage on a match then unconditionally 'return -EINVAL'; the only caller therefore always sets MemoryACPILevel.MinMvdd = 0 - the ACPI memory DPM level is shipped to the SMC without an MVDD floor. Verified byte-identical in upstream Linux master (upstream-inherited correctness bug, no memory-safety impact).

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3652 Β· 6 files
FileTypeDescriptionSize
README.md doc 655 B ↓ raw
VERDICT.md doc 2.4 KB ↓ raw
fix.diff doc 277 B view raw
env.txt doc 799 B view raw
verdict.json doc 2.6 KB view raw
manifest.json doc 903 B view raw
README.md doc
↓ download raw

DF-3652 β€” ci_populate_mvdd_value returns -EINVAL unconditionally, so MemoryACPILevel.MinMvdd is always programmed as 0 (upstream-inverted success return)

File: sys/dev/drm/radeon/ci_dpm.c (pass 2, GLM 5.3 re-audit) Severity: Info | CWE-670 Always-Incorrect Control Flow Implementation | confidence: certain

Reproduce

No harness: the defect is a NULL/UAF control-flow property (source-level certainty); the kernel path needs CI-class hardware absent from the guest β€” see env.txt.

(source review only; see VERDICT.md)

Full narrative: VERDICT.md. Proposed fix: fix.diff (authored against the read-only sys/ tree; never applied there).

VERDICT.md doc
↓ download raw

DF-3652 β€” verdict narrative

Finding

ci_populate_mvdd_value returns -EINVAL unconditionally, so MemoryACPILevel.MinMvdd is always programmed as 0 (upstream-inverted success return)

Kernel references

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.

Root cause and reasoning

Functional/logic defect (no memory-safety impact). ci_populate_mvdd_value walks the mvdd dependency table, fills voltage->Voltage on a match (2350), then falls through to 'return -EINVAL' at 2359 even on success - the success return was inverted at some point (the 'if (i >= count) return -EINVAL' guard at 2355-2356 already covers the failure case). The only caller (ci_populate_smc_acpi_level, 3095-3099) therefore always takes the error branch and programs table->MemoryACPILevel.MinMvdd = 0: the ACPI memory level is shipped to the SMC without an MVDD floor. Verified byte-for-byte identical in upstream Linux master (torvalds/linux ci_dpm.c) - an upstream-inherited bug, filed as hardening/correctness for the DFly tree. Note the filled value also skips the VOLTAGE_SCALE multiplication and byte order conversion the caller applies on its own path (3099), consistent with the caller's use, so only the return is wrong.

Exploitability

No direct security impact: with a genuine MVDD control object the ACPI DPM level requests MinMvdd=0 from the SMC - a functional power-management regression (potential memory instability on downclock), not a kernel memory-safety issue.

Return 0 from ci_populate_mvdd_value when a matching MVDD entry was found (only -EINVAL when the clock is out of range). 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_testable
baseline no→ patch + rebuild →patched clean

Fix 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).
↓ fix.diffper-fix-DF-3652

Confirmed kernel references

Detail

Exploit chain

No direct security impact: with a genuine MVDD control object the ACPI DPM level requests MinMvdd=0 from the SMC - a functional power-management regression (potential memory instability on downclock), not a kernel memory-safety issue.

Evidence (decisive lines)

VERDICT.md (full source trace); 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; source-trace only

Verified recommended fix

Return 0 from ci_populate_mvdd_value when a matching MVDD entry was found (only -EINVAL when the clock is out of range).

Verdict

Functional/logic defect (no memory-safety impact). ci_populate_mvdd_value walks the mvdd dependency table, fills voltage->Voltage on a match (2350), then falls through to 'return -EINVAL' at 2359 even on success - the success return was inverted at some point (the 'if (i >= count) return -EINVAL' guard at 2355-2356 already covers the failure case). The only caller (ci_populate_smc_acpi_level, 3095-3099) therefore always takes the error branch and programs table->MemoryACPILevel.MinMvdd = 0: the ACPI memory level is shipped to the SMC without an MVDD floor. Verified byte-for-byte identical in upstream Linux master (torvalds/linux ci_dpm.c) - an upstream-inherited bug, filed as hardening/correctness for the DFly tree. Note the filled value also skips the VOLTAGE_SCALE multiplication and byte order conversion the caller applies on its own path (3099), consistent with the caller's use, so only the return is wrong.