UINT8 loop-variable wraparound in CMI enumeration causes infinite loop while holding the global hp lock (local DoS; also amplifies cmi_order[] overflow)
Summary
The CMI enumeration uses UINT8 instance with the error-break idiom 'instance = maxInstance'. With max_instance==254 and the SHOW_MAX_INSTANCE detail bit, maxInstance==255; on get_cmi_block failure instance=255 then ++instance wraps to 0, restarting the loop forever inside dev_read while holding the global ACPI_SERIAL hp lockmgr. Failure at 0 = pure unkillable hang wedging all hp sysctls and /dev/hpcmi; failure at k>=1 = infinite re-append driving cmi_order_size unboundedly past cmi_order[128] (wraparound-restart mechanism, distinct from DF-2382's >128-instances). Unpriv /dev/hpcmi read (0644); data precondition BIOS-controlled. Loop logic LIVE-harness-verified compiled on the guest. stop-flag fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4426 Β· 9 files| File | Type | Description | Size | |
|---|---|---|---|---|
| wrap_harness.c | β | 3.9 KB | view raw | |
| build.sh | β | 98 B | view raw | |
| run.sh | β | 141 B | view raw | |
| build.log | β | 86 B | view raw | |
| run.log | β | 874 B | view raw | |
| env.txt | β | 488 B | view raw | |
| fix.diff | β | 943 B | view raw | |
| VERDICT.md | β | 3.6 KB | β raw | |
| verdict.json | β | 3.5 KB | view raw |
DF-4426 VERDICT β UINT8 instance wraparound β infinite loop under hp lock
Reproduced?
On-guest kernel reproduction: no β the audit guest (QEMU/SeaBIOS, no HP
BIOS) has no PNP0C14 ACPI node, so acpi_wmi never attaches
(env.txt: no /dev/wmistat*, no hw.acpi.wmi sysctl), and acpi_hp
consequently never creates /dev/hpcmi (env.txt: no /dev/hpcmi, no
hw.acpi.hp sysctl oid). The defect requires a physical HP WMI BIOS whose
CMI GUID advertises max_instance == 254.
Logic-level reproduction: YES, on the DragonFly guest itself β the exact
loop control flow from acpi_hp.c:1164-1204 was extracted verbatim into
wrap_harness.c (only the WMI layer stubbed; UINT8 = unsigned char
identical on DFly x86-64), compiled with DragonFly cc 8.3 on the guest,
and run (run.log):
- error at instance 0 β 300,000 body executions, 0 inserts β a pure
infinite loop with zero memory writes. In-kernel this pins the calling
thread inside
dev_readforever while it holdslockmgr(&hp_serial, LK_EXCLUSIVE|LK_RETRY)(acpi_hp.c:1157, acpivar.h:170) β every laterhw.acpi.hp.*sysctl op and every/dev/hpcmiopen blocks forever. Unprivileged local DoS (device is 0644, acpi_hp.c:523-524). - error at instance 5 β unbounded restart: cmi_order_size reached
250,000 in the capped demo β in-kernel this drives
cmi_order[128]OOB writes far beyond what DF-2382 describes (which covers >128 instances; here a single broken instance at k causes infinite re-appending of k entries per wrap). - no error β normal 255-iteration termination (control run).
Root cause (path:line)
sys/dev/acpica/acpi_hp/acpi_hp.c:1172βfor (instance = 0; instance < maxInstance; ++instance)withUINT8 instance(declared :1145).sys/dev/acpica/acpi_hp/acpi_hp.c:1178βinstance = maxInstance;as the error-break idiom; withmaxInstance == 255,++instancewraps to 0 and the loop restarts (UINT8arithmetic).maxInstance == 255requiressc->has_cmi == 255(WMI GUIDmax_instance == 254, returned asmax_instance+1by acpi_wmi.c:344) andACPI_HP_CMI_DETAIL_SHOW_MAX_INSTANCEset inhw.acpi.hp.cmi_detail(sysctl root-writable only β kern_sysctl.c:1446SYSCAP_NOSYSCTL_WRcheck), which skips themaxInstance--at acpi_hp.c:1168.
Why the guest can't show the kernel manifestation
No WMI firmware. Injecting a crafted PNP0C14 SSDT into the QEMU firmware is
out of scope for a Low-severity finding; the loop logic itself is fully
characterized and the lock-held-forever consequence is structural (the whole
enumeration runs between ACPI_SERIAL_BEGIN(hp)/:1157 and
ACPI_SERIAL_END(hp)/:1229).
Exploit chain
(none β denial of service only: stuck kernel thread + permanently held
global hp sx/lockmgr lock; repeatable by any user once the sysctl
precondition is set, e.g. sysctl hw.acpi.hp.cmi_detail=8 then
cat /dev/hpcmi on a matching BIOS.)
Fix
fix.diff (dry-run verified): replace the instance = maxInstance idiom
with a stop flag + break. One-line semantic fix; keeps the loop variable
wrap-proof.
Fix validation
not_testable β no HP WMI hardware on the guest; the fix is a control-flow change whose logic effect is directly visible in the harness (with the fix applied to the harness loop, all three runs terminate).
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 not possible: kernel path needs HP WMI firmware absent from the guest. fix.diff is control-flow-only and its effect is directly visible when applied to the harness loop (all runs terminate).
fix.diff (patch -p1 dry-run verified against the read-only sys/ tree copy)
Confirmed kernel references
Detail
Evidence (decisive lines)
run.log: 'RUN err@0' 300000 executions/0 inserts and 'RUN err@5' 250000 inserts with VERDICT infinite-loop lines; VERDICT.md root-cause walk (acpi_hp.c:1145,1157,1165,1168,1172,1178; acpi_wmi.c:344); env.txt guest hardware absence; fix.diff dry-run applyable stop-flag fix
PoC changes
No PoC seed existed (new pass-2 finding). Wrote wrap_harness.c replicating the loop verbatim with stubbed WMI layer; compiled and executed on the DragonFly guest itself (cc 8.3 [DragonFly]) rather than the Linux host (no host compiler).
Verified recommended fix
Replace the 'instance = maxInstance' error-break idiom with a stop flag tested at loop top (fix.diff), or use an int loop variable and break.
Verdict
UINT8 loop-variable wraparound in acpi_hp_hpcmi_read's CMI enumeration (acpi_hp.c:1172,1178): when the WMI CMI GUID reports max_instance==254 and the SHOW_MAX_INSTANCE detail bit is set, maxInstance stays 255 and the error-break idiom 'instance = maxInstance' wraps 255->0 on ++instance, restarting the loop forever while the global hp lockmgr lock (acpi_hp.c:1157) is held - unkillable kernel thread plus permanent wedge of hw.acpi.hp.* sysctls and /dev/hpcmi. Kernel-level reproduction is impossible on the audit guest (no PNP0C14/WMI firmware: no /dev/hpcmi, no hw.acpi.wmi sysctl - see env.txt), but the exact loop logic was extracted verbatim, compiled with DragonFly cc 8.3 ON the guest, and demonstrated: error-at-0 gives a pure 0-write infinite loop; error-at-5 re-appends cmi_order entries unboundedly (cmi_order_size hit 250000 in a capped run), which in-kernel also amplifies the DF-2382 cmi_order[128] overflow far past the >128-instances scenario. Distinct defect from DF-2382/DF-1606: different mechanism (wraparound restart vs missing bound), different minimal manifestation (no-write pure hang).
No comments yet.