DF-4197 / verdict.json
{ "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" } |