DF-5180 / verdict.json
{ "finding_id": "DF-5180", "status": "untested", "reproduced": false, "impact": "dos", "confidence": "certain", "verdict": "Source-confirmed heap OOB read: acpi_PkgStr() (acpi_package.c:86-98) accepts ACPI_TYPE_BUFFER elements and copies them with strlcpy() using siz = Buffer.Length + 1, but ACPICA externalizes Buffers without a NUL terminator (utcopy.c:288-290 memcpy of exactly Buffer.Length bytes; only Strings get Length+1 with terminator at :276-278). DragonFlys strlcpy() ends with an unbounded while (*s++) ; source scan when no NUL is found within siz-1 bytes (sys/libkern/strlcpy.c:56-61), so a zero-free Buffer makes the scan read ptr[Buffer.Length] and beyond. Size accounting (utobject.c:512-514, utcopy.c:285-286) shows a trailing 8-aligned buffer ends exactly at the end of the zeroed result allocation (utalloc.c:527), putting the first OOB byte past the kmallocd block. Containment: the allocation is zeroed up front, so Length%8!=0 buffers stop on zero padding inside the same block; the strlcpy return value is discarded and the dst write is bounded by the checked size, so nothing is disclosed - impact ceiling is a kernel panic at an unlucky slab/page boundary. Reachable via acpi_cmbat.c:374-380 (_BIF strings) with crafted ACPI tables or hostile EC/battery AML. Distinct from known DF-2177 (NULL ptr), DF-2385 (unbounded idx), DF-2178 (dead checks). No guest run: the guests QEMU DSDT is benign and no post-boot entry point reaches acpi_PkgStr; static proof chain is complete.", "exploit_chain": "", "evidence": "findings/poc/DF-5180/VERDICT.md (full chain incl. containment analysis); trigger.dsl (crafted _BIF reaching the scan); fix.diff (applies cleanly, patch -p1 --dry-run OK)", "kernel_refs": [ "sys/dev/acpica/acpi_package.c:86", "sys/dev/acpica/acpi_package.c:95", "sys/dev/acpica/acpi_package.c:98", "sys/libkern/strlcpy.c:56", "sys/contrib/dev/acpica/source/components/utilities/utcopy.c:288", "sys/contrib/dev/acpica/source/components/utilities/utobject.c:512", "sys/contrib/dev/acpica/source/components/utilities/utalloc.c:527", "sys/dev/acpica/acpi_cmbat.c:374" ], "poc_changes": "n/a (no seed PoC; trigger documented as crafted-DSDT snippet in trigger.dsl)", "attempts": 0, "guest_uname": "", "runtime_sec": 0, "guest_dirty": 0, "build_cmd": "./build.sh (source-confirmed; no build required)", "run_cmd": "./run.sh (source-confirmed; see trigger.dsl)", "code_hash": "8e1380bfeae2deb7ec3f458461d06cc66df9d784c11c4cc40645c9218b55dcf6", "notes": "QEMU guest not started: ACPI-firmware-gated, guest DSDT benign; a fabricated-object KLD would show a read contained inside the same allocation (no observable effect), so a run adds no evidence. Family precedent (DF-2177/2178/2385) is source-confirmation. Guest left untouched.", "recommended_fix": "For ACPI_TYPE_BUFFER, bound the copy to the buffer: scan for the first NUL strictly within Buffer.Length and memcpy() that prefix (dst is already zeroed); keep strlcpy() only for NUL-terminated Strings.", "fix_status": "not_testable", "fix_baseline_reproduced": 0, "fix_patched_reproduced": 0, "fix_verdict": "fix.diff authored after source verification (kernel-idiomatic bounded loop; no libkern memchr dependency); verified to apply cleanly to a pristine copy (patch -p1 --dry-run OK). Kernel build/boot validation skipped: Low-severity firmware-gated trigger, no runtime path on the audit guest.", "fix_evidence": "findings/poc/DF-5180/fix.diff (sha256 76e6a177410b6d51ade09ac3ca45649bd478643404cee8002ef026ab9675c7d4); dry-run apply check in VERDICT.md" } |