DF-5668 / verdict.json
{ "finding_id": "DF-5668", "status": "reproduced", "verdict": "AcpiOsGetRootPointer (OsdEnvironment.c:72-73) accepts hint.acpi.0.rsdp with zero validation (no address bound, no RSDP signature/checksum/Revision check) and feeds it to ACPICA, while the same functions own AcpiFindRootPointer fallback path validates every candidate via AcpiTbValidateRsdp \u2014 a second, independent acceptance of the same loader hint that DF-5588s acpi_sdt fix does not cure. Reproduced on the stock kernel: a crafted RSDP->RSDT->MADT chain (FADT-less) planted at 0x40000000 boots through the native walk and reaches acpi0 attach, where AcpiTbLoadNamespace (tbxfload.c:275-285) indexes Tables[AcpiGbl_DsdtIndex = 0xFFFFFFFF] and faults: Fatal trap 12, Stopped at AcpiTbLoadNamespace+0x36 (cmpl $0x54445344 = DSDT), deterministic boot brick. Fully-signatured+checksummed chains fault identically (S2), proving the contrib-side DsdtIndex guard is also required. Trust boundary is boot-owner (loader.conf/loader prompt), consistent with the DF-5588/DF-1113 Medium precedent; no unprivileged in-guest path reaches the hint (cold-boot-only consumer, cached). fix.diff validates: RSDP validation + firmware fallback in AcpiOsGetRootPointer, plus the tbxfload.c ACPI_INVALID_TABLE_INDEX guard \u2014 F1 boots on firmware tables (acpi0 <BOCHS BXPC> attached), F2 degrades gracefully, F3 (with DF-5588s sdt fix) cures the zeroed-hint wedge end-to-end (6 CPUs, clean boot).", "reproduced": true, "impact": "panic", "confidence": "certain", "exploit_chain": "boot-owner (root/boot-disk write or loader console) sets hint.acpi.0.rsdp=0x40000000 in /boot/loader.conf; QEMU -device loader (or any boot-disk RAM planting) places a crafted RSDP->RSDT->MADT chain at that address; native sdt_probe consumes the control MADT so SMP bring-up succeeds; acpi_Startup -> AcpiInitializeTables -> AcpiOsGetRootPointer returns the unvalidated hint; AcpiTbParseRootTable walks the crafted chain (no RSDP validation on the host-supplied-pointer path); acpi_attach -> AcpiLoadTables -> AcpiTbLoadNamespace indexes Tables[0xFFFFFFFF] -> Fatal trap 12 (boot panic). Out-of-RAM hints fault earlier via AcpiOsMapMemory->pmap_mapdev reserved-bit PTEs; zeroed-RAM hints wedge the walk (sdt-side on stock, ACPICA-side once sdt is patched). Full ACPICA namespace control (arbitrary in-kernel AML) is available to the same boot-owner \u2014 equivalent privileges, no unprivileged escalation path exists.", "evidence": "run.S1_nosig.boot.log (stock, invalid-sig chain: lapic_count=3, acpi0: <DFBSD >, Fatal trap 12, Stopped at AcpiTbLoadNamespace+0x36); panic.txt (full trap block, fault VA 0xfffff8208d6e27f4, cmpl $0x54445344); run.S2_validfadtless.boot.log (fully-valid chain, identical trap); run.S3_zeroed_wedge.boot.log (zeroed hint: RSDT paddr == 0, pre-SMP wedge); fixrun.F1_nosig.boot.log (AcpiOsGetRootPointer: kenv RSDP failed validation, falling back to firmware scan, acpi0: <BOCHS BXPC> [attached!], machdep.acpi_root=1005984=0xF59A0); fixrun.F2_validfadtless.boot.log (graceful acpi0: Could not enable ACPI: AE_NO_ACPI_TABLES, boot completes); fixrun.F3_zeroedhint.boot.log (both acceptances reject, lapic_count=6, acpi0 attached, wedge gone); fix.diff; fix_build.log; env.txt; VERDICT.md sections 2-5.", "kernel_refs": [ "sys/platform/pc64/acpica/OsdEnvironment.c:59", "sys/platform/pc64/acpica/OsdEnvironment.c:72", "sys/contrib/dev/acpica/source/components/tables/tbxface.c:257", "sys/contrib/dev/acpica/source/components/tables/tbutils.c:376", "sys/contrib/dev/acpica/source/components/tables/tbutils.c:410", "sys/dev/acpica/acpi.c:462", "sys/dev/acpica/acpi.c:553", "sys/contrib/dev/acpica/source/components/tables/tbxfload.c:275", "sys/contrib/dev/acpica/source/components/tables/tbxfroot.c:211", "sys/contrib/dev/acpica/source/include/acglobal.h:171", "sys/contrib/dev/acpica/source/components/tables/tbfadt.c:490", "sys/dev/acpica/Osd/OsdMemory.c:85", "sys/platform/pc64/acpica/acpi_sdt.c:71" ], "poc_changes": "Adapted DF-5588s proven generator (gen_rsdp_inject.py) into gen_chain.py with two scenarios: nosig_fadtless (invalid signature + wrong checksum) and valid_fadtless (genuine RSD PTR + correct v1 checksum + correct RSDT/MADT checksums, still FADT-less \u2014 proves RSDP-level validation alone is insufficient). First S1 attempt failed due to a builder bug (5-byte value assigned into a 4-byte bytearray slice shifted every table field by one; kernel correctly complained madt_iterate_entries: invalid MADT entry len 0) \u2014 fixed to bP568. Fix v1 fell back to AcpiFindRootPointer only when the hint was absent; after observing AE_NOT_FOUND on QEMU (SeaBIOS RSDP verified present+valid at 0xF59A0 via the QEMU monitor), restructured so the firmware-scan fallback runs for both absent and invalid hints (v2).", "attempts": 6, "guest_uname": "DragonFly dfbsd 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": 2970, "guest_dirty": 0, "build_cmd": "python3 gen_chain.py nosig_fadtless nosig_fadtless.bin && python3 gen_chain.py valid_fadtless valid_fadtless.bin; guest: cd /usr/src && git apply -p0 fix.diff && make -j8 nativekernel KERNCONF=X86_64_GENERIC && make installkernel", "run_cmd": "vm.sh reset with-src; vm.sh run_root printf \"hint.acpi.0.rsdp=0x40000000\\nboot_verbose=YES\\n\" >> /boot/loader.conf; sync; vm.sh down; QEMU_EXTRA=\"-device loader,file=nosig_fadtless.bin,addr=0x40000000,force-raw=on\" vm.sh up 240 # expect Fatal trap 12 at AcpiTbLoadNamespace+0x36 (ssh never answers); serial log in dfbsd-qemu/boot.log", "code_hash": "d9be8620298d1d46585cda3f2dfb2809ca402a1222cae3afe079ab383808e2eb", "notes": "Reserved-ID block DF-5668..DF-5680; single finding filed (the file is 82 LOC; all other surfaces audited clean \u2014 see VERDICT.md section 6). The tbxfload.c Tables[ACPI_INVALID_TABLE_INDEX] OOB is an upstream ACPICA robustness bug (any FADT-less chain from any host that does not validate its root pointer; also reachable from genuinely broken firmware) \u2014 flagged for upstream in addition to the OsdEnvironment.c fix. DF-5588s fix.diff was applied only for the F3 combined-remediation run and was already independently validated by that finding. QEMU monitor used read-only to locate SeaBIOSs RSDP (0xF59A0, sig+checksum valid, 16-byte aligned) while diagnosing the fallback. Guest reset to clean-source snapshot after the run.", "recommended_fix": "Validate the kenv RSDP hint in AcpiOsGetRootPointer (bound against ptoa(Maxmem), require RSD PTR + Revision<=2 + zero v1/x checksums) and fall back to AcpiFindRootPointer on failure; guard AcpiTbLoadNamespace against AcpiGbl_DsdtIndex == ACPI_INVALID_TABLE_INDEX before indexing Tables[] (see fix.diff).", "fix_status": "fixed", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_verdict": "Baseline (stock): S1/S2 fatal trap at AcpiTbLoadNamespace+0x36, S3 pre-SMP wedge. Patched (fix.diff = OsdEnvironment.c validation+fallback + tbxfload.c DsdtIndex guard): F1 identical-to-S1 scenario boots clean on firmware tables (acpi0 <BOCHS BXPC> attached, machdep.acpi_root=0xF59A0, no panic); F2 fully-valid-chain scenario degrades gracefully (acpi0: Could not enable ACPI: AE_NO_ACPI_TABLES, boot completes, no panic); F3 with DF-5588s sdt fix additionally cures the zeroed-hint wedge end-to-end (lapic_count=6, hw.ncpu=6, acpi0 attached). The previously-observed bad behavior is gone in every scenario; kernel built clean with 0 errors.", "fix_evidence": "Baseline: run.S1_nosig.boot.log + panic.txt + run.S2_validfadtless.boot.log + run.S3_zeroed_wedge.boot.log. Patched: fixrun.F1_nosig.boot.log (reject+fallback lines), fixrun.F2_validfadtless.boot.log (graceful AE_NO_ACPI_TABLES), fixrun.F3_zeroedhint.boot.log (both acceptances reject, 6-CPU clean boot); fix_build.log (two clean nativekernel installs, 18:05 and 18:23 UTC 2026); fix.diff applies via git apply --check -p0 against the pristine tree." } |