acpi_parse_prw() copies _PRW elements 0..N-3 instead of 2..N as wake power resources (off-by-two)
Summary
The code's own comment (and the ACPI spec) define _PRW elements 2..N as power-resource references and power_count = Count-2, but the copy loop indexes Elements[i] for i in [0, power_count) instead of Elements[i+2]. The mis-copied elements 0/1 (GPE indicator and lowest-wake INTEGER) are dropped by acpi_GetReference()'s type check, so with Count==3 the sole genuine power resource is never referenced and larger packages get a shifted prefix - wake power rails are never raised at sleep nor cleaned on resume. Inherited verbatim from FreeBSD main. Type-safe (no memory corruption): availability/functional defect on firmware with _PRW power resources (PCIe WOL, lid/USB wake on laptops). Not runtime-triggerable on the audit guest (QEMU DSDT contains zero _PRW objects - verified); consumer trace documented; fix compiled into the patched validation kernel.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3795 Β· 4 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.4 KB | β raw | |
| VERDICT.md | β | 2.4 KB | β raw | |
| fix.diff | β | 310 B | view raw | |
| verdict.json | β | 2.7 KB | view raw |
DF-3795 β acpi_parse_prw() copies the wrong _PRW elements as power resources
- File:
sys/dev/acpica/acpi.c(pass 2) - Lines: 2962β2970 (off-by-two at 2969β2970)
- Class: CWE-682 (incorrect computation) / incorrect index
- Severity: Low (functional/availability, firmware-dependent) Β· Confidence: certain
What
Per ACPI spec (and the code's own comment), _PRW packages are:
Package { <gpe>, <lowest_wake_sstate>, <power-resource ref>, ... }
elem0 elem1 elem2 .. elemN
acpi_parse_prw() computes power_count = Package.Count - 2 β the number
of power-resource references β but then copies elements 0 .. count-1
instead of 2 .. N:
/* Elements 2 to N of the _PRW object are power resources. */
power_count = res->Package.Count - 2;
...
for (i = 0; i < power_count; i++)
prw->power_res[i] = res->Package.Elements[i]; /* should be [i + 2] */
Consequences in the consumer (acpi_powerres.c:552-556
acpi_pwr_wake_enable() β acpi_pwr_reference_resource() β
acpi_GetReference(), acpi_package.c:132):
- Elements 0 and 1 are an INTEGER (GPE bit) or a PACKAGE (GPE handle+bit)
and an INTEGER (lowest wake state).
acpi_GetReference()type-checks and returns NULL for both β dropped (no memory-safety impact). - With exactly one power resource (Count == 3) the real reference at
element 2 is never copied; with more, a shifted prefix (2..Count-3)
is used. Net effect: wake power resources are not powered on at sleep
(
acpi_wake_sleep_prep, acpi.c:2752-2754) nor cleaned up on wake (acpi.c:2789-2790), so devices whose wake depends on those rails cannot wake the machine (availability), and stale/extra references may keep rails on after resume.
This bug is inherited from FreeBSD (verified identical in freebsd-src/main sys/dev/acpica/acpi.c ~line 4117), so it is a long-standing MI defect, not a DFly-only divergence β but it is still a defect in this tree.
Guest testability
The QEMU guest's DSDT contains no _PRW objects at all
(acpidump -b -n DSDT; iasl -d; grep -c _PRW β 0), so the mis-parse
cannot be triggered on this guest; the verdict is code-certain but
runtime-not_reproduced. Hardware with wake-capable devices whose _PRW
lists power resources (Count > 2 β common for PCIe WOL, wake-on-lid,
Suns/USB on laptops) is affected.
Fix
See fix.diff: prw->power_res[i] = res->Package.Elements[i + 2];
DF-3795 VERDICT
Status: not_reproduced on this guest β code-certain by trace; no _PRW in guest firmware
Guest: QEMU-emulated DragonFly 6.5-DEVELOPMENT (X86_64_GENERIC).
Why not reproduced at runtime
acpidump -b -n DSDT && iasl -d on the guest: its DSDT contains zero
_PRW objects (grep -c _PRW dsdt.dsl β 0). The mis-parse only fires
for _PRW packages with Count > 2 (i.e. wake-capable devices listing
power resources). Exercising it requires different firmware, not different
userspace β a guest-runtime proof is impossible on this DSDT, so the
honest status is not_reproduced rather than a forced demonstration.
Why the code defect is certain
- The function's own comment (acpi.c:2962) states elements 2 to N are
the power resources; the loop at acpi.c:2969-2970 copies
Elements[i]fori in [0, power_count), i.e. elements 0 to Count-3. - ACPI spec semantics of
_PRW: elem0 = GPE indicator (INTEGER or PACKAGE), elem1 = lowest wake sleep state, elem2.. = power-resource references. - Consumer path is type-safe β
acpi_pwr_reference_resource()(acpi_powerres.c:582) βacpi_GetReference()(acpi_package.c:132-160) returns NULL for INTEGER/PACKAGE elements β so elements 0/1 are dropped, and with Count==3 the single genuine power resource at element 2 is never referenced. Effect:acpi_wake_sleep_prep(acpi.c:2752-2754) never turns on the device's wake power rails;acpi_wake_run_prep(acpi.c:2789-2790) never cleans them. Availability/functional impact on wake-capable hardware; no memory-safety impact. - Cross-check: FreeBSD main has the identical line
(
freebsd-src/sys/dev/acpica/acpi.c~4117:Elements[i]) β inherited MI bug, not a DFly divergence.
Fix validation
fix.diff (Elements[i + 2]) was compiled into the patched test kernel
together with the DF-3793/DF-3794 fixes (one-line change; the affected
branch is unreachable on this guest's firmware), so runtime behavioral
validation of this specific change is honestly not_testable here; the
index arithmetic is self-evident against the comment and the consumer.
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 is a one-line index correction compiled successfully into the patched test kernel; no _PRW-bearing firmware on this guest so runtime before/after comparison is impossible.
guest build.log shows the patched acpi.c (all three fixes) compiling and linking into X86_64_GENERIC.
Confirmed kernel references
Detail
Evidence (decisive lines)
VERDICT.md: DSDT survey (grep -c _PRW -> 0); code trace acpi.c:2962-2970 vs consumer acpi_powerres.c:582 + acpi_package.c:132-160; FreeBSD cross-check.
PoC changes
no executable PoC possible on this guest (firmware lacks _PRW); dsdt-survey commands documented in VERDICT.md
Verified recommended fix
prw->power_res[i] = res->Package.Elements[i + 2];
Verdict
acpi_parse_prw() computes power_count = Package.Count - 2 (elements 2..N per its own comment and the ACPI spec) but copies res->Package.Elements[i] for i in [0, power_count) instead of [i+2] (acpi.c:2969-2970). The mis-copied elements 0/1 (GPE indicator / lowest wake state) are dropped by acpi_GetReference()'s type check (acpi_package.c:132-160), so with Count==3 the genuine power resource at element 2 is never referenced and larger packages get a shifted prefix: wake power rails are not raised at sleep nor cleaned on resume (acpi_powerres.c:552-556, acpi.c:2752-2754/2789-2790). Functional/availability defect on firmware with _PRW power resources; no memory-safety impact. Not reproducible on the audit guest: its QEMU DSDT contains zero _PRW objects (verified via acpidump+iasl), so runtime status is honestly not_reproduced; the defect is certain from the code/comment/consumer trace and is inherited verbatim from FreeBSD main (~line 4117).
No comments yet.