DF-5778 / verdict.json
{ "finding_id": "DF-5778", "file": "sys/dev/drm/amd/display/dc/irq/irq_service.c", "title": "Unguarded svc->funcs->to_dal_irq_source double-deref in dal_irq_service_to_irq_source + swallowed dal_irq_service_construct failure \u2014 latent NULL-deref hardening gap (Info)", "verdict_summary": "Latent CWE-476: irq_service.c:165 double-derefs the service object with no guard (asymmetric with every sibling entry point and with dc_interrupt_set), and construct-failure (:53-63) is swallowed by family constructors. Reachability traced dead via all 8 in-tree create-sites and dc.c ctx ordering; executed as a structural KLD-harness proof.", "status": "untested", "reproduced": 0, "impact": "none", "confidence": "certain", "verdict": "Structural/latent finding verified statically and by production-code KLD harness on the DragonFly guest (no AMD GPU; synthetic-table family standard). The harness compiles the verbatim dispatcher (irq_service.c:77-169, irq_service.h structs, irq_types.h enum+macro) and proves: T1 the exact irq_service.c:165 deref chain on a zero-initialised irq_service hits svc->funcs (off+16)==NULL with NO guard on the path (fault-free probe); B1 find_irq_source_info accepts exactly 0..89 and rejects one-past-end 90 and all larger/(enum)-1 values with canaries intact (table walk in-bounds \u2014 executable negative result for the pass-2 bounds question); B3 the DF-4731 DAL_VALID_IRQ_SRC_NUM off-by-one is defused at this files set/ack paths by the `>=` bound; B4 upstream Linux v4.17 used `>` (accepts 90 \u2192 &info[90] one-past-end) while the DFly trees `>=` (irq_service.c:82) is stricter/correct \u2014 no port-introduced defect, byte-identical otherwise; T2-T4 verbatim happy path, dummy-slot log-and-fail with zero register traffic, and generic RMW confined to in-table regs. The in-kernel NULL-deref manifestation cannot be exercised: no AMD hardware on the guest and no in-tree caller can produce the NULL object (all 8 resource create-sites NULL-check pool->base.irqs; dc->ctx is kzalloc-checked at core/dc.c:602-607 before pool creation at :671). Severity Info, defense-in-depth.", "exploit_chain": "", "evidence": "run.log: T1 zeroed irq_service=... svc->funcs (off+16) = 0 + T1 VERDICT: ... NO guard; run.log B1 RESULT: PASS (bounds) and B2 ... PASS (intact); run.log B3/B4 lines; VERDICT.md section 2 reachability trace with path:line for all 8 create-sites; env.txt (no AMD GPU); fix.diff", "kernel_refs": [ "sys/dev/drm/amd/display/dc/irq/irq_service.c:53", "sys/dev/drm/amd/display/dc/irq/irq_service.c:160", "sys/dev/drm/amd/display/dc/irq/irq_service.c:165", "sys/dev/drm/amd/display/dc/core/dc.c:1673", "sys/dev/drm/amd/display/dc/core/dc.c:1690", "sys/dev/drm/amd/display/dc/irq/dce110/irq_service_dce110.c:398", "sys/dev/drm/amd/display/dc/dm_services.h:68" ], "poc_changes": "no seed PoC; harness authored fresh. Build iterations on guest: (1) <kmod.mk> not on out-of-tree include path -> absolute /usr/src/sys/conf/kmod.mk; (2) no sys/stddef.h on DFly -> address arithmetic for the member offset; (3)(4) -Werror=missing-prototypes -> added verbatim prototypes from irq_service_interface.h:28-47 and irq_service.h:72-83; (5) guest root shell is csh -> run.sh pipes commands through /bin/sh (as vm.sh does).", "attempts": 5, "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": 210, "guest_dirty": 0, "build_cmd": "cd /root/df5778 && make SYSDIR=/usr/src/sys KERNBUILDDIR=/usr/obj/usr/src/sys/X86_64_GENERIC", "run_cmd": "kldload /root/df5778/df5778_harness.ko; sleep 1; dmesg | grep DF5778; kldunload df5778_harness", "code_hash": "f73d899c322abd1a372c59e125f0c57422aaac434668fa2997f1c2ec774c3ad0", "notes": "Guest /root/df5778 removed after capture; module cleanly unloaded (kldstat back to kernel+ehci+xhci). Upstream comparison fetched live from torvalds/linux v4.17 during this audit (find_irq_source_info used `>` there; DFly `>=` is stricter \u2014 positive divergence, documented in VERDICT.md B4). Pass-2 knowns not re-reported: DF-2168 + DF-4932/4995/5054/5078 (zero-filled VLINE0 slots, funcs==NULL), DF-4933/5055/5079 (dce110_vblank_set pipe_offset underflow), DF-4731 (DAL_VALID_IRQ_SRC_NUM off-by-one, adjacent file), DF-4732 (DFly lockmgr IRQ-path divergence, adjacent file).", "recommended_fix": "Guard dal_irq_service_to_irq_source (reject !irq_service/!funcs/!member with DC_IRQ_SOURCE_INVALID) and give dc_interrupt_to_irq_source/dc_interrupt_ack the same dc/res_pool->irqs NULL check dc_interrupt_set already has (fix.diff)", "fix_status": "not_testable", "fix_baseline_reproduced": 0, "fix_patched_reproduced": 0, "fix_verdict": "Pure-hardening early-return guards on paths provably non-NULL in-tree; no AMD GPU on the guest to build an amdgpu kernel and no way to drive the objects to NULL, so a fix-build validation is not possible (house precedent DF-4731). Guard logic desk-checked against every cited call site; callers already tolerate DC_IRQ_SOURCE_INVALID (dummy slot).", "fix_evidence": "fix.diff (guards at irq_service.c:165 and dc.c:1673/1690); VERDICT.md section 5" } |