⬢ DragonFlyBSD Kernel Audit
DF-4197 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-4197",
  "status": "inconclusive",
  "reproduced": 0,
  "impact": "none",
  "confidence": "certain",
  "verdict": "Kernel manifestation is hardware-gated on this guest (no DRM device), but the defect itself is the shim, and the shims exact algorithm is reproduced deterministically and under 2-thread stress on the DragonFly guest: sys/dev/drm/include/linux/seqlock.h write_sequnlock() decrements the sequence count back to its pre-write value (0->1->0) instead of advancing it monotonically (Linux: 0->1->2) and read_seqbegin() does not wait for an even count, so drm_vblank_count_and_time (drm_vblank.c:784-788) accepts a torn {count,time} pair whenever exactly one store_vblank (drm_vblank.c:92-106) completes inside its two field loads. Demo: TORN PAIR ACCEPTED {count=100(old), time=200000(new)} with read_seqretry==0 under DF semantics vs retry==1 under corrected semantics; stress: torn_accepted=4057/4189 across two runs under DF semantics vs 0 under fixed semantics (~8M reads each). Torn values are legitimate vblank counters (no kernel-memory disclosure) but corrupt the vblank UAPI: wrong reply.sequence/tval (drm_wait_vblank_reply:1518), wrong DRM_IOCTL_CRTC_GET_SEQUENCE output (:1801), wrong event target sequence in DRM_IOCTL_CRTC_QUEUE_SEQUENCE (:1861), mismatched flip-completion timestamps. i915 engine stats (intel_engine_cs.c:1623) share the broken shim.",
  "exploit_chain": "",
  "evidence": "run.log: demo TORN PAIR ACCEPTED rc=1 / demo_fixed REJECTED rc=0; stress torn_accepted=4057 and 4189 (two runs) vs stress_fixed torn_accepted=0; VERDICT.md verbatim shim-vs-Linux semantics comparison",
  "kernel_refs": [
    "sys/dev/drm/include/linux/seqlock.h:77",
    "sys/dev/drm/include/linux/seqlock.h:90",
    "sys/dev/drm/drm_vblank.c:784",
    "sys/dev/drm/drm_vblank.c:102",
    "sys/dev/drm/drm_vblank.c:1518",
    "sys/dev/drm/drm_vblank.c:1801",
    "sys/dev/drm/drm_vblank.c:1861",
    "sys/dev/drm/i915/intel_engine_cs.c:1623"
  ],
  "poc_changes": "harness written fresh (no seed): verbatim shim algorithm from seqlock.h:69-99 (pthread_mutex stands in for struct spinlock; sfence/lfence kept), verbatim store_vblank/drm_vblank_count_and_time bodies, plus a fixed-semantics variant compiled into the same binary for A/B contrast",
  "attempts": 3,
  "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": 30,
  "guest_dirty": 0,
  "build_cmd": "cc -O2 -Wall -pthread -o seqlock_aba seqlock_aba.c",
  "run_cmd": "./seqlock_aba demo; ./seqlock_aba demo_fixed; ./seqlock_aba stress; ./seqlock_aba stress_fixed",
  "code_hash": "81f9e02ad6d83ebb5b5cabd341b3eb86cb1fc4e1cd880f34a95059a69544aada",
  "notes": "Both torn values are legitimate vblank data -> no info leak; impact is corruption of the vblank counter/timestamp UAPI contract (early/never-satisfied waits, mismatched flip timestamps), hence impact=none at kernel-memory level and Low severity. On i386 builds the u64 count can tear mid-field (unlocked drm_vblank_count:277-285 aggravator). Sequence count wraps at 2^32 are irrelevant vs the protocol hole.",
  "recommended_fix": "Restore Linux seqcount semantics in the shim: write_sequnlock -> sl->sequence++ and read_seqbegin -> spin until even (see fix.diff)",
  "fix_status": "inconclusive",
  "fix_baseline_reproduced": 1,
  "fix_patched_reproduced": 0,
  "fix_verdict": "fix.diff restores monotonic seqcount semantics (write_sequnlock increments; read_seqbegin waits for even \u2014 identical to the *_fixed harness variant). Harness A/B: demo_fixed rejects the torn pair (retry=1) and stress_fixed accepts 0 torn pairs over ~7M reads vs 4057/4189 baseline. Kernel-level validation impossible on this guest (no DRM device); harness-level validation passed cleanly.",
  "fix_evidence": "run.log stress lines DF-shim ... torn_accepted=4057/4189 vs FIXED(Linux) ... torn_accepted=0; fix.diff"
}