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

Missing serial lock in acpi_panasonic_shutdown triggers KKASSERT panic on INVARIANTS kernels

Summary

acpi_panasonic_shutdown calls hkey_sound_mute without ACPI_SERIAL_BEGIN(panasonic). hkey_sound_mute asserts lock via ACPI_SERIAL_ASSERT(:374) expands KKASSERT(lockstatus==LK_EXCLUSIVE). INVARIANTS kernels panic on every shutdown. Production no-op but SSET evaluation races with concurrent sysctl/notify. All other callers properly acquire lock. Root reboot/halt triggers. Recursive panic prevents crash dumps. [VERIFIED pass2: reproduced both triggers - device_shutdown dispatch and REAL reboot panic at hkey_sound_mute+0xb9 assertion, reboot chain backtrace captured; serial-lock fix-validated - shutdown clean, guest cycles back unattended]

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2591 Β· 14 files
FileTypeDescriptionSize
README.md β€” 1.5 KB ↓ raw
VERDICT.md β€” 4.5 KB ↓ raw
run.sh β€” 197 B view raw
run_fixed.log β€” 244 B view raw
panic_reboot.txt β€” 1.6 KB view raw
triggerA.txt β€” 525 B view raw
serial.txt β€” 19.3 KB view raw
panasim.c β€” 19.4 KB view raw
panasim.h β€” 1.1 KB view raw
pana_shim.c β€” 2.6 KB view raw
build.sh β€” 88 B view raw
fix.diff β€” 1.4 KB view raw
env.txt β€” 1.9 KB view raw
verdict.json β€” 4.4 KB view raw

DF-2591 β€” PoC evidence pack

Finding: acpi_panasonic_shutdown() calls hkey_sound_mute() without ACPI_SERIAL_BEGIN(panasonic) (acpi_panasonic.c:231); hkey_sound_mute() opens with ACPI_SERIAL_ASSERT(panasonic) (:374) which expands to KKASSERT(lockstatus(&panasonic_serial, curthread) == LK_EXCLUSIVE) (sys/dev/acpica/acpivar.h:172) β€” guaranteed panic on INVARIANTS kernels at every shutdown/reboot of a system with this device.

Rig

Same verbatim-interposition kernel harness as DF-2386 (see that README). Two real triggers:

  • A (run.sh baseline cmd): hw.panasim.cmd=9 makes the rig worker call device_shutdown(dev) β€” the same subr_bus dispatch (sys/kern/subr_bus.c:2154-2162) the reboot path uses for every device (MOD_SHUTDOWN β†’ device_shutdown(root_bus) β†’ bus_generic_shutdown, subr_bus.c:3549-3551, :2883-2890).
  • B (run.sh baseline reboot): root reboot end-to-end.

Build

sh build.sh            # shared with DF-2386

Run

sh run.sh baseline cmd      # trigger A: device_shutdown -> assert panic
sh run.sh baseline reboot   # trigger B: real reboot -> assert panic
sh run.sh fixed   cmd|reboot  # fixed: no assert; clean shutdown

Expected result (baseline)

KKASSERT panic naming the panasonic serial (lockstatus ≠ LK_EXCLUSIVE) inside acpi_panasonic_shutdown→hkey_sound_mute, on the INVARIANTS stock-style kernel. Fixed build: no assert, device_shutdown returns, reboot completes cleanly.

VERDICT.md
↓ download raw

DF-2591 β€” PoC verdict

File: sys/dev/acpica/acpi_panasonic/acpi_panasonic.c (:231, assert at :374)

Verdict: REPRODUCED β€” acpi_panasonic_shutdown() calls hkey_sound_mute() without ACPI_SERIAL_BEGIN(panasonic); the callee opens with ACPI_SERIAL_ASSERT(panasonic) (:374) which expands to KKASSERT(lockstatus(&panasonic_serial, curthread) == LK_EXCLUSIVE) (sys/dev/acpica/acpivar.h:172) β€” a guaranteed panic on INVARIANTS kernels at every shutdown/reboot of a system with this device, plus an unsynchronized SSET evaluation racing concurrent sysctl/notify on production kernels. Both triggers verified through the real code path; fixed build shuts down cleanly (A/B validated).

Line-accurate confirmation

  1. acpi_panasonic.c:222-233 β€” acpi_panasonic_shutdown: sc = device_get_softc(dev); mute = 1; hkey_sound_mute(sc->handle, HKEY_SET, &mute); β€” no ACPI_SERIAL_BEGIN anywhere in the function; every other caller path takes the serial (acpi_panasonic_sysctl :248/:262, acpi_panasonic_notify :487/ :492, acpi_panasonic_power_profile :509/:512).
  2. acpi_panasonic.c:370-374 β€” hkey_sound_mute opens with ACPI_SERIAL_ASSERT(panasonic).
  3. sys/dev/acpica/acpivar.h:172 β€” the assert is a plain KKASSERT (unconditional in the header), active on INVARIANTS kernels.
  4. Reboot chain: root reboot β†’ boot() β†’ module MOD_SHUTDOWN β†’ device_shutdown(root_bus) (sys/kern/subr_bus.c:3549-3551) β†’ bus_generic_shutdown (:2883-2890) β†’ DEVICE_SHUTDOWN dispatch (device_shutdown, :2154-2162) β†’ verbatim acpi_panasonic_shutdown.

Mechanical verification

Same rig as DF-2386 (see that VERDICT). Two real triggers:

  • A β€” device_shutdown(dev) (the same subr_bus dispatch the reboot path uses; hw.panasim.cmd=9 runs it in the rig worker):
panic: assertion "lockstatus(&panasonic_serial, curthread) == LK_EXCLUSIVE" failed in hkey_sound_mute at /usr/src/sys/dev/acpica/panasim/acpi_panasonic_verbatim.c:374
Debugger("panic")
Stopped at      Debugger+0x7c:  movb    $0,0xbdd659(%rip)

The KKASSERT names the lock, the function, and line 374 of the verbatim file (path proves the assertion fired inside the verbatim driver code).

  • B β€” real reboot end-to-end (root), full backtrace captured (panic_reboot.txt):
boot() called on cpu#2
Switching to cpu #0 for shutdown
...
syncing disks... done
panic: assertion "lockstatus(&panasonic_serial, curthread) == LK_EXCLUSIVE" failed in hkey_sound_mute at ...acpi_panasonic_verbatim.c:374
Trace beginning at frame ...
hkey_sound_mute() at hkey_sound_mute+0xb9
acpi_panasonic_shutdown() at acpi_panasonic_shutdown+0x23
bus_generic_shutdown() at bus_generic_shutdown+0x53
acpi_shutdown() at acpi_shutdown+0x9
bus_generic_shutdown() at bus_generic_shutdown+0x53
Debugger("panic")

Exactly the claimed path: reboot β†’ acpi bus shutdown β†’ device shutdown β†’ mute β†’ assert. The panic fires mid-shutdown after disks synced (a recursive-panic-prone moment, as the finding notes).

Reachability / privesc assessment (honest)

  • Hardware precondition: Panasonic MAT0019 firmware. Trigger: root reboot/halt (PR:H in the CVSS) β€” on an INVARIANTS kernel with this driver attached, the assert fires on EVERY shutdown (deterministic, no race needed).
  • Production (non-INVARIANTS) kernels: the assert compiles out; the residual defect is acpi_panasonic_sset (SSET evaluation) running unlocked against concurrent sysctl/notify holders of the serial β€” a real but narrow protocol violation (CWE-667).
  • Impact: panic/DoS at shutdown; no memory corruption; not a privesc. Low severity upheld.

Fix validation (A/B)

Fixed kernel (#6), fix.diff hunk wrapping the mute in ACPI_SERIAL_BEGIN/END:

  • trigger A: rig ring records worker: device_shutdown returned (no assert fired) (run_fixed.log);
  • trigger B: root reboot β†’ clean shutdown, grep -c panic over the full serial log = 0, guest cycles and returns on its own.

Artifacts

run_fixed.log, panic_reboot.txt (trigger B, full assert + backtrace), serial.txt (fixed-run serial), rig sources, build.sh/run.sh, build.log, fix.diff, env.txt.

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
↓ fix.diffper-fix-DF-2591

Confirmed kernel references

Detail

Exploit chain

root reboot/halt -> boot() -> rootbus MOD_SHUTDOWN -> device_shutdown(root_bus) (subr_bus.c:3549-3551) -> bus_generic_shutdown (:2883-2890) -> DEVICE_SHUTDOWN -> verbatim acpi_panasonic_shutdown -> hkey_sound_mute without the panasonic serial -> ACPI_SERIAL_ASSERT KKASSERT fails (lockstatus != LK_EXCLUSIVE) -> panic mid-shutdown after disks synced. No attacker-controlled data; PR:H as scored. Rig trigger A reaches the identical dispatch via device_shutdown on the fake child for a controlled capture.

Evidence (decisive lines)

panic_reboot.txt (trigger B: assertion message naming panasonic_serial/hkey_sound_mute/verbatim line 374 plus the full backtrace acpi_panasonic_shutdown+0x23 <- bus_generic_shutdown+0x53 <- acpi_shutdown+0x9 <- bus_generic_shutdown+0x53); triggerA.txt (device_shutdown capture); run_fixed.log (trigger A: worker: device_shutdown returned (no assert fired); trigger B: clean reboot, grep -c panic over the whole serial log = 0, guest cycles back unattended)

PoC changes

No seed PoC existed; reused the DF-2386 rig and added worker cmd 9 (device_shutdown on the fake child -- the same kobj DEVICE_SHUTDOWN dispatch the reboot path performs) so the assert can be captured without consuming a full reboot cycle; the real reboot was then run end-to-end as trigger B on a separate boot.

Verified recommended fix

acpi_panasonic_shutdown: wrap the hkey_sound_mute call in ACPI_SERIAL_BEGIN(panasonic) / ACPI_SERIAL_END(panasonic), matching every other caller

Verdict

Confirmed on the verbatim driver in the DF-2386 kernel rig, through both real trigger paths. acpi_panasonic_shutdown calls hkey_sound_mute(sc->handle, HKEY_SET, &mute) with no ACPI_SERIAL_BEGIN(panasonic) anywhere in the function (acpi_panasonic.c:228-232) while every other caller path takes the serial (:248/:262, :487/:492, :509/:512); hkey_sound_mute opens with ACPI_SERIAL_ASSERT(panasonic) (:374), which expands to KKASSERT(lockstatus(&panasonic_serial, curthread) == LK_EXCLUSIVE) (acpivar.h:172) and panics on INVARIANTS kernels. Trigger A (device_shutdown(dev), the same subr_bus.c:2154-2162 DEVICE_SHUTDOWN dispatch the reboot path uses) panicked with the assertion naming hkey_sound_mute and line 374 of the verbatim file. Trigger B (root reboot end-to-end) captured the full chain: boot() -> acpi_shutdown -> bus_generic_shutdown -> acpi_panasonic_shutdown+0x23 -> hkey_sound_mute+0xb9 -> assertion panic at :374 after disks synced (the recursive-panic-prone moment the finding describes). Deterministic on every shutdown of an INVARIANTS kernel with this driver attached; production non-INVARIANTS kernels compile the assert out and keep only the unsynchronized SSET evaluation racing concurrent sysctl/notify holders. Root-triggered DoS at reboot, no memory corruption, not a privesc. fix.diff (ACPI_SERIAL_BEGIN/END around the mute) A/B validated: device_shutdown returns with no assert and a real reboot completes with zero panics in the serial log.