DF-4812 / verdict.json
{ "finding_id": "DF-4812", "status": "reproduced", "reproduced": 1, "impact": "leak", "confidence": "certain", "verdict": "REPRODUCED dynamically on the audit guest with the stock in-tree driver: a crafted SSDT (QEMU -acpitable) supplies the TOS6200 device whose GHCI answers Package(3) instead of the required HCI_WORDS=6; acpi_toshiba.c hci_call() (sys/dev/acpica/acpi_toshiba/acpi_toshiba.c:433-441) evaluates GHCI with ACPI_ALLOCATE_BUFFER, ACPI_PKG_VALID fails, and the function returns ENXIO at line 440 WITHOUT AcpiOsFree(results.Pointer) \u2014 every other exit frees at the end: label (460-462). Unprivileged sysctl READS of hw.acpi.toshiba.* then leak the ACPICA result buffer (M_ACPICA, Osd/OsdMemory.c:62): measured 469 objs/46.0K -> 39.5K/3.71M after 20k reads and -> 118K/11.0M after 40k more (~190B and ~1 object per hci_call; sysctl(8) makes 2 handler calls per invocation and silently swallows the error, exit 0). Unbounded -> kernel heap exhaustion DoS on any unit whose GHCI firmware returns a short package. Fix validated on the same guest: one-line AcpiOsFree before the return, module rebuilt and swapped; identical 40k-read load leaves M_ACPICA exactly flat (118K/11.0M before and after) while the invalid package! marker and allocation counters prove the same path still executes.", "exploit_chain": "unprivileged user loop: while :; sysctl hw.acpi.toshiba.force_fan (silent, exit 0) -> 2x hci_call -> 2x leaked ACPI_OBJECT buffers per iteration -> linear M_ACPICA exhaustion -> kernel memory starvation/DoS (no info disclosure; contents are AML-built package objects)", "evidence": "findings/poc/DF-4812/run.log (before/after vmstat -m rows for baseline runs 1-2 and patched run 3, dmesg marker count 40283); VERDICT.md (full path trace path:line + fix validation); ssdt_tosh.dsl/.aml (crafted table); fix_build.log (clean -Werror module build); env.txt (uname, kldstat, SSDT present, final pool state)", "kernel_refs": [ "sys/dev/acpica/acpi_toshiba/acpi_toshiba.c:433", "sys/dev/acpica/acpi_toshiba/acpi_toshiba.c:437", "sys/dev/acpica/acpi_toshiba/acpi_toshiba.c:440", "sys/dev/acpica/acpi_toshiba/acpi_toshiba.c:460", "sys/dev/acpica/Osd/OsdMemory.c:62", "sys/dev/acpica/acpivar.h:491" ], "poc_changes": "No seed PoC (pass-2 static finding). Verification built from scratch: crafted SSDT compiled with the guests /usr/sbin/iasl, injected via QEMU_EXTRA=-acpitable (vm.sh supports the env var); unprivileged read loop via vm.sh run_user.", "attempts": 3, "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64", "runtime_sec": 2100, "guest_dirty": 0, "fix_status": "fixed", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_verdict": "One-line AcpiOsFree(results.Pointer) before the line-440 return; applied to guest /usr/src, module rebuilt clean (-Werror), swapped live. Baseline module: +7.3MB M_ACPICA under 40k reads; patched module under the identical load: exactly 0 growth with path markers still firing. Bad behavior GONE.", "fix_evidence": "findings/poc/DF-4812/fix.diff; findings/poc/DF-4812/fix_build.log; findings/poc/DF-4812/run.log (RUN 3)", "build_cmd": "iasl ssdt_tosh.dsl (guest) ; cd /usr/src/sys/dev/acpica/acpi_toshiba && make obj && make", "run_cmd": "kldload acpi_toshiba; vmstat -m|grep acpica; as unpriv user: loop 20000x sysctl hw.acpi.toshiba.force_fan; vmstat -m|grep acpica", "code_hash": "0bc7a849410df8fe6ac665ef4f0f81fc1dbe5780de3eaddcc6bdc623435c9743", "notes": "Trigger precondition is a Toshiba firmware variant returning a GHCI package with <6 elements \u2014 exactly the divergence the defensive branch was written for; supplied on the guest via a crafted SSDT so the stock driver+ACPICA executed the real kernel path. DF sysctl(8) silently ignores failed leaf reads, making the user-side trigger silent (no output, exit 0). Guest was reset to the clean-source (with-src) snapshot after the run. Related known findings not re-reported: DF-1892/1893/2393/2394.", "recommended_fix": "Add AcpiOsFree(results.Pointer) before the return (ENXIO) in hci_call()s invalid-package branch (acpi_toshiba.c:440)" } |