⬢ DragonFlyBSD Kernel Audit
DF-4636 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-4636",
  "status": "reproduced",
  "reproduced": 1,
  "impact": "dos",
  "confidence": "certain",
  "verdict": "CONFIRMED by verbatim-source control-flow reproduction on the DragonFly guest (4/4 deterministic rounds): all four ATIF/ATCS return parsers in sys/dev/drm/radeon/radeon_acpi.c (radeon_atif_verify_interface:212/220, radeon_atif_get_notification_params:259/267, radeon_atif_get_sbios_requests:318/326, radeon_atcs_verify_interface:494/502) dereference info->Buffer.Pointer and memcpy from it without checking info->Type==ACPI_TYPE_BUFFER, Pointer!=NULL, Buffer.Length>=2 before the u16 size read, or Buffer.Length>=size before the memcpy. ACPICA externalizes ANY return object type on AE_OK (nsxfeval.c:536-599), and in union acpi_object Buffer.Pointer aliases Integer.Value at offset 8 (verified at runtime), so an SBIOS ATIF/ATCS returning an INTEGER makes the kernel dereference the firmware-chosen 64-bit value verbatim (observed SIGSEGV si_addr=0x414141414100 == the returned integer, 16/16 runs); short/lying buffers fault exactly 1..8 bytes past the ACPI allocation on a guard page (32/32 runs); zero-length buffer faults at NULL+0 (16/16); well-formed control parses to err=0 in both variants. In-kernel impact is a kernel-mode wild/OOB read in ACPICA notify-dispatch/driver-init context -> panic; parsed values feed notification config and radeon_set_backlight_level() which is arithmetic-only (atombios_encoders.c:87-112), so no escalation sink \u2014 firmware-trust DoS, Medium. This is the radeon twin of pass-1 amdgpu finding DF-1750 (same defect, different file; this file was clean in pass 1). In-kernel end-to-end not runnable on this guest (no AMD GPU, radeon not built) \u2014 reproduction is at the verbatim-extraction level against byte-identical source (sha256-gated), same accepted methodology as DF-4605. fix.diff adds Type/NULL/Length validation to all four parsers; FIXED variant rejects all malformed fixtures with -EINVAL, no fault, 4/4 rounds, control still parses.",
  "exploit_chain": "trigger chain (no privilege escalation \u2014 firmware-controlled DoS): platform firmware implements ATIF/ATCS on the radeon devices ACPI node with a malformed return (buggy or malicious SBIOS, or crafted ACPI tables injected with root/loader access) -> radeon_acpi_init (radeon_acpi.c:707/713, runs after successful GPU init on Avivo+ ASICs with a bios) evaluates ATCS/ATIF -> AcpiEvaluateObject succeeds and externalizes the object (any type) -> parser reads *(u16*)info->Buffer.Pointer at radeon_acpi.c:212/259/318/494 -> with Type=INTEGER this is a load at the firmware-chosen 64-bit address; with a short/lying buffer it is a 1..13-byte heap OOB read past the M_ACPICA allocation -> unmapped/guarded address -> kernel fault in ACPICA context -> panic. The notify path (radeon_atif_handler:346 -> radeon_atif_get_sbios_requests:307) re-parses on every matching ACPI notify, so a persistent malicious SBIOS re-triggers at will.",
  "evidence": "run.log (decisive: PROOF-RESULT: BASELINE-CORRUPTS (5/5 fixtures SIGSEGV 4/4 iterations, control parses) x4 rounds; si_addr histogram 16x 0x414141414100, 16x 0x424242424200, 16x 0x0, 32x 0x80047d000 guard page; offsetof(Integer.Value)=8 offsetof(Buffer.Pointer)=8 (union aliasing: YES); FIX-APPLIED-CLEAN; PROOF-RESULT: FIXED-CLEAN (no faults, control still parses) x4); extract_and_test.sh (whole proof, sha256-gated to the audited tree, runs on the guest); fix.diff (git apply --check clean vs sys/); VERDICT.md (full narrative incl. 3 harness iterations fixed); env.txt (guest uname, cc 8.3, radeon absent from guest kernel); build.log.",
  "kernel_refs": [
    "sys/dev/drm/radeon/radeon_acpi.c:212",
    "sys/dev/drm/radeon/radeon_acpi.c:220",
    "sys/dev/drm/radeon/radeon_acpi.c:259",
    "sys/dev/drm/radeon/radeon_acpi.c:267",
    "sys/dev/drm/radeon/radeon_acpi.c:318",
    "sys/dev/drm/radeon/radeon_acpi.c:326",
    "sys/dev/drm/radeon/radeon_acpi.c:494",
    "sys/dev/drm/radeon/radeon_acpi.c:502",
    "sys/dev/drm/radeon/radeon_acpi.c:103",
    "sys/dev/drm/radeon/radeon_acpi.c:414",
    "sys/contrib/dev/acpica/source/components/namespace/nsxfeval.c:536",
    "sys/contrib/dev/acpica/source/include/actypes.h:1077"
  ],
  "poc_changes": "No seeded PoC existed (pass-2 finding); the proof was authored during verification. Three authoring iterations were fixed: a sha256 typo in the source-fidelity gate; two extraction bugs (indented #  define capability-mask lines in radeon_acpi.h missed by grep; line-range sed of radeon.h swallowed a trailing /* \u2014 both replaced with pattern-bounded extraction); non-canonical wild-address values (0x4141414100000000 -> #GP reports si_addr=0x0) switched to canonical 0x414141414100/0x424242424200 so the fault address prints the dereferenced firmware value verbatim.",
  "attempts": 4,
  "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": 1500,
  "guest_dirty": 0,
  "build_cmd": "scp -F dfbsd-qemu/config findings/poc/DF-4636/extract_and_test.sh findings/poc/DF-4636/fix.diff dfbsd:/tmp/df4636_dir/ && vm.sh run_root sh /tmp/df4636_dir/extract_and_test.sh (script compiles on guest: cc -O2 -Wall -o h_base h_base.c / cc -O2 -Wall -DDF4636_FIXED -o h_fixed h_fixed.c)",
  "run_cmd": "dfbsd-qemu/vm.sh run_root sh /tmp/df4636_dir/extract_and_test.sh",
  "code_hash": "f4f4d5035238f54c6559d9600099cb4dc5b3fbce5454f42c0f9152f63c5148b9",
  "notes": "Guest stayed up throughout (userland harness faults only); no reset needed. Pass-2 negative results recorded in the files task_log: (a) no ATPX code exists in this port at all (radeon_acpi.h constants only; radeon_register_atpx_handler stubbed inline-empty in radeon_drv.c:163-166) \u2014 the prompts atpx_call/atpx_handler/power_control/mux/detect hunt items are dead here; (b) the DF-4605 empty-if guard shape does NOT exist \u2014 radeon_atif_handler keeps a real early-return guard (radeon_acpi.c:357-360); (c) the DF-1749 handler-never-removed UAF shape does NOT exist \u2014 radeon_acpi_fini runs before kfree(rdev) (radeon_kms.c:73 vs :78) and ACPICA removes by exact function pointer (evxface.c:475-499), NULL handle -> AE_BAD_PARAMETER (evxface.c:399); (d) the DF-1751 wrong-allocator shape does NOT exist \u2014 AcpiOsFree is only ever applied to ACPICA-allocated objects; (e) unchecked AcpiInstallNotifyHandler status (radeon_acpi.c:771) is benign: ACPICA allows multiple distinct device-notify handlers and remove matches the exact pointer; (f) the #ifdef MN_TODO pcie-perf functions are dead code compiled out. brgphy.c audited clean in the same run (separate file row).",
  "recommended_fix": "In all four ATIF/ATCS parsers, validate info->Type==ACPI_TYPE_BUFFER, info->Buffer.Pointer!=NULL, info->Buffer.Length>=2 right after the !info check, and require info->Buffer.Length>=size before the memcpy/parse \u2014 see fix.diff (mirrors the DF-1750 amdgpu fix).",
  "fix_status": "inconclusive",
  "fix_baseline_reproduced": 0,
  "fix_patched_reproduced": 0,
  "fix_kernel_uname": null,
  "fix_verdict": "Kernel-build fix validation not possible on this guest: no AMD GPU (virtio-vga) and radeon is absent from the guest kernel config and module set, so no in-kernel baseline or patched run exists there. Validated instead at the verbatim-extraction level inside the same proof: the identical harness compiled from the fix.diff-patched extraction returns -EINVAL for all five malformed fixtures with no fault in 4/4 rounds (PROOF-RESULT: FIXED-CLEAN x4 in run.log) while the unpatched extraction faults deterministically (BASELINE-CORRUPTS x4), and the well-formed control still parses after the fix. fix.diff additionally passes git apply --check against the unmodified sys/ tree. fix_baseline_reproduced/fix_patched_reproduced are 0 because they describe the kernel-level validation run, which did not occur (same posture as DF-4605).",
  "fix_evidence": "run.log: FIX-APPLIED-CLEAN, PROOF-RESULT: FIXED-CLEAN (no faults, control still parses) x4 rounds vs PROOF-RESULT: BASELINE-CORRUPTS (5/5 fixtures SIGSEGV 4/4 iterations, control parses) x4; host: git apply --check findings/poc/DF-4636/fix.diff -> clean."
}