DF-5016 / verdict.json
{ "finding_id": "DF-5016", "status": "reproduced", "reproduced": 1, "impact": "panic", "confidence": "likely", "verdict": "The locking hole is real and the race was executed at scale (274,432 CONS_HISTORY reallocs x continuous scrolling tty writes, both uid 65534): history-ring appends (sc_term_gen_scroll -> sc_hist_save_one_line, sctermvar.h:437) run under syscons_lock only, while every ring realloc/free (sc_hist_ioctl/sc_set_text_mode -> sc_alloc_history_buffer, schistory.c:94-159) runs under vga_token only - no common lock, so kfree() of the ring can interleave with a concurrent appends reads of vtb_buffer/vtb_tail (UAF read + wild write) or with the NULL window. Kernel manifestation: a supervisor WRITE fault inside sc_vtb_bcopy at 0xffffffff7ffffffe during the unpriv churn campaign (panic #1) - the UAF branch signature; that run had NO keyboard input, excluding DF-5015s negative-tail path, and the competing generic ioctl-token assertion (identified via a TIOCGWINSZ-only control, which panics with a DIFFERENT signature: lwkt_reltokref count assertion, never a wild write) cannot produce a wild write. Confidence likely rather than certain because panic #1 occurred once and no backtrace was captured (trace_on_panic not armed on that boot).", "exploit_chain": "proc A (unpriv): loop ioctl(CONS_HISTORY) realloc/free of the ring; proc B (unpriv): write scrolling lines to the same vty (append fires per scrolled line, under syscons_lock); interleave kfree under vga_token with a reader mid-append -> UAF write into a freed 4KB-class kmem object with attacker-controlled line content, or wild write via garbage vtb_tail. No uid=0 chain developed.", "evidence": "race5016.c; panic-lwkt-token-count-race.log (campaign panic, later identified as the generic ioctl issue via the TIOCGWINSZ control - kept for the record); DF-5015/panic-scvtb_bcopy-unpriv-churn.log (the wild-write panic attributed here); VERDICT.md", "kernel_refs": [ "sys/dev/misc/syscons/sctermvar.h:425-450", "sys/dev/misc/syscons/syscons.h:547-548", "sys/dev/misc/syscons/syscons.c:970-1006", "sys/dev/misc/syscons/syscons.c:1885-1923", "sys/dev/misc/syscons/schistory.c:93-159", "sys/dev/misc/syscons/scvidctl.c:163" ], "poc_changes": "racer written from scratch (two-process, uid 65534); discovery chain documented in VERDICT.md including the confound isolation (CONS_CLRHIST-only, TIOCGWINSZ-only controls)", "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": 3600, "guest_dirty": 0, "build_cmd": "cc -O1 -o /tmp/race5016 /tmp/race5016.c (guest)", "run_cmd": "/tmp/race5016 scroll & /tmp/race5016 realloc & (uid 65534)", "code_hash": "19e5e987a36402c82342fa9c14db9a27a3de6cb11b9cd989881f104ed7abf338", "notes": "the lwkt_reltokref assertion panic observed in several race runs is a GENERIC DragonFly ioctl-path issue (reproduced with TIOCGWINSZ alone on ttyv0, stock kernel, unpriv, ~60-90s, 4/4) and is NOT this finding; it deserves its own audit ticket outside syscons.", "recommended_fix": "perform the install/destroy (and free) of history rings under syscons_lock, the lock every append reader already holds (fix.diff exports syscons_lock and wraps the swap/destroy sections); NULL remains installed only while reallocating, which the scroll path already NULL-checks", "fix_status": "fixed", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_verdict": "Patched kernel (fix.diff, kernel #2 Sat Sep 26 02:44) compiled, installed and booted; race soak on it (353,280 CONS_HISTORY reallocs x continuous scroll appends in 20s, both uid 65534) produced no vtb-class fault and normal syscons operation remained functional (arm/flip/SCRSHOT/fill verified). The lwkt_reltokref assertion that eventually appears on longer ioctl loops is the generic DragonFly ioctl-path issue (TIOCGWINSZ-only control) and is orthogonal to this fix.", "fix_evidence": "/tmp/kbuild.log KBUILD_DONE; race soak transcript in DF-5015 pack run logs" } |