DF-5646 / verdict.json
{ "finding_id": "DF-5646", "status": "reproduced", "verdict": "One loader.conf hint line (hint.lm.0.irq=\"0x10000\") panics the kernel at boot: isahint.c:73-75 passes the unvalidated kenv irq straight into machintr_legacy_intr_cpuid(), and ioapic_abi_legacy_intr_cpuid() (ioapic_abi.c:1126-1127) KKASSERTs irq < ioapic_abi_legacy_irq_max during isa_probe_children. Deterministic 1/1 boots, machine dead in ddb before userland. On kernels without INVARIANTS the same input is an OOB read of ioapic_irqmaps[cpuid][irq] (ioapic_abi.c:1129-1131) with a hint-chosen 31-bit index. Root/loader-gated, hence Low severity, but a hard boot-time kernel panic from documented configuration syntax.", "reproduced": true, "impact": "panic", "confidence": "certain", "exploit_chain": "loader.conf.local hint.lm.0.irq=0x10000 -> loader static kenv -> resource_kenv (subr_bus.c:2315, consulted before compiled devtab) -> isahint_identify walks devtab (lm0 at=isa, ioconf.c) -> isahint_add_device: resource_int_value(irq) returns 65536, only check start>0 -> machintr_legacy_intr_cpuid(65536) -> ioapic_abi KKASSERT panic at boot; no unprivileged path (identify cannot be re-triggered at runtime).", "evidence": "panic.txt (full panic + backtrace: isa_probe_children -> bus_generic_probe_hack -> isahint_identify -> isahint_add_device -> ioapic_abi_legacy_intr_cpuid); run.fixed.log (patched kernel boots clean with the same hint armed); env.txt; VERDICT.md", "kernel_refs": [ "sys/bus/isa/isahint.c:73", "sys/bus/isa/isahint.c:74-75", "sys/kern/subr_bus.c:2315-2323", "sys/platform/pc64/apic/ioapic_abi.c:1122-1131", "sys/platform/pc64/apic/ioapic_abi.c:469" ], "poc_changes": "None needed \u2014 the seeded one-line hint trigger works verbatim (value 0x10000 chosen to exceed ioapic_abi_legacy_irq_max while parsing cleanly via strtol).", "attempts": 1, "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": 95, "guest_dirty": 0, "build_cmd": "none (loader.conf kenv hint trigger; see trigger.hint / run.sh)", "run_cmd": "printf hint.lm.0.irq=\"0x10000\"\\n > /boot/loader.conf.local && shutdown -r now # -> boot panic, recover with vm.sh reset with-src", "code_hash": "46468bdd448470a43471a4112af1f9f8553af03b0aba282b4fef004bf3b3559e", "notes": "Guest kernel is X86_64_GENERIC with options INVARIANTS (line 56) so KKASSERT fires; release kernels take the OOB-read branch instead \u2014 same fix. Guest was reset (vm.sh reset with-src) after verification; guest_dirty=0.", "recommended_fix": "Validate the hint irq against the legacy ISA IRQ space before calling the interrupt ABI: require start > 0 && start < 16 at isahint.c:73.", "fix_status": "fixed", "fix_baseline_reproduced": true, "fix_patched_reproduced": false, "fix_verdict": "fix.diff applied to guest /usr/src, kernel rebuilt (nativekernel, BUILD_OK, uname -v #1: Fri Oct 9 05:53:50 UTC 2026), rebooted with the exact same hint.lm.0.irq=0x10000 armed alongside the DF-5647 poison set: no panic, guest reached ssh, dmesg clean. Baseline panic / patched boot both captured.", "fix_evidence": "run.fixed.log (current-boot dmesg segment of the patched kernel: no panic lines, devices probe normally); build.log (kernel build + installkernel + BUILD_OK tail)" } |