{
  "finding_id": "DF-2857",
  "status": "reproduced",
  "reproduced": 1,
  "impact": "panic",
  "confidence": "certain",
  "verdict": "shm_deallocate_segment() (sys/kern/sysv_shm.c:180-194) only clears SHMSEG_ALLOCATED at line 193, AFTER the blocking vm_object_deallocate()/kfree teardown; vm_object_terminate() yields (lwkt_user_yield every 64 pages, vm_object.c:932) which releases shm_token, and with the default shm_allow_removed=1 shm_find_segment_by_shmid() keeps returning the mid-teardown segment. An unprivileged owner racing a second shmctl(IPC_RMID) re-enters shm_deallocate_segment and double-deallocates the vm_object (ref_count already 0) -> unconditional panic 'vm_object_deallocate: object deallocated too many times' (vm_object.c:690). Reproduced on the FIRST run, stock INVARIANTS kernel, default sysctls (no use_phys>=2 needed, unlike DF-2677), as user nobody; stack shows sys_shmctl -> shm_deallocate_segment -> vm_object_deallocate -> double vm_object_deallocate_locked frame. A racing shmat through the same window references the dying object (the DF-2677 panic sink via a separate, unfixed door), and OBJ_DEAD interleavings reach a literal kfree() double-free of shm_handle plus shm_committed/shm_nused double-decrement. fix.diff (hide segment before blocking teardown) validated: patched kernel #1 completes 400/400 race iterations cleanly where baseline panicked immediately.",
  "exploit_chain": "unpriv nobody: (1) shmget(IPC_PRIVATE, 192MB, 0600); (2) shmat + memset (fault ~48k pages) + shmdt -> nattch==0, segment alive; (3) two forked racers spam shmctl(shmid, IPC_RMID): first starts the multi-yield teardown, the next RMID landing before sysv_shm.c:193 re-enters shm_deallocate_segment -> vm_object_deallocate on ref-0 object -> panic. Corruption ceiling (char.): shmat racer re-raising ref_count under OBJ_DEAD turns the second pass into kfree(shm_handle) double-free + committed/nused double-decrement; uid0 chain not developed because the ref-0 panic deterministically pre-empts the double-free interleaving on this kernel.",
  "evidence": [
    "panic.txt: 'panic: vm_object_deallocate: object deallocated too many times: 5' with sys_shmctl -> shm_deallocate_segment -> vm_object_deallocate -> stacked vm_object_deallocate_locked frames (re-entrant teardown proof)",
    "serial_full.log: full baseline serial console ending in DDB",
    "run.fixed.log: patched kernel #1, 400/400 iterations 'completed 400 iterations without panic', exit 0",
    "fix_build.log.gz: full patched-kernel build (rc=0), includes patched sysv_shm.c compile",
    "fix.diff: one-hunk reorder clearing shm_perm.mode/shm_internal before vm_object_deallocate"
  ],
  "kernel_refs": [
    "sys/kern/sysv_shm.c:180",
    "sys/kern/sysv_shm.c:187",
    "sys/kern/sysv_shm.c:193",
    "sys/kern/sysv_shm.c:161",
    "sys/kern/sysv_shm.c:171",
    "sys/kern/sysv_shm.c:439",
    "sys/kern/sysv_shm.c:444",
    "sys/vm/vm_object.c:690",
    "sys/vm/vm_object.c:884",
    "sys/vm/vm_object.c:932"
  ],
  "poc_changes": "n/a (written fresh for this finding; no seed)",
  "attempts": 2,
  "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": 60,
  "guest_dirty": 0,
  "build_cmd": "cc -O2 -Wall -o shm_teardown_race shm_teardown_race.c",
  "run_cmd": "su -m nobody -c /tmp/shm_teardown_race rmid 400   (defaults: kern.ipc.shm_use_phys=1, kern.ipc.shm_allow_removed=1)",
  "code_hash": "4bc42fda338bbd9b14573bde923669f026e275c88d3cdd678fb36d6217be5d97",
  "notes": "Default-config reproduction (no root-set tunable, unlike DF-2677 which needed use_phys>=2). Window is on the deallocation side: ~768 forced token-release points for a 192MB segment. Distinct root cause from DF-2677 (find-during-teardown vs late-nattch++); DF-2677's early-attach-accounting fix would NOT close this window - only hiding the segment before the blocking teardown does. Guest reset to clean with-src snapshot after validation.",
  "recommended_fix": "In shm_deallocate_segment(), set shmseg->shm_perm.mode = SHMSEG_FREE and shmseg->shm_internal = NULL (and adjust shm_committed/shm_nused) BEFORE calling vm_object_deallocate()/kfree, so the segment is unfindable for the whole blocking teardown.",
  "fix_status": "fixed",
  "fix_kernel_uname": "DragonFly 6.5-DEVELOPMENT #1: Wed Sep  2 12:38:41 UTC 2026     root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC  x86_64 (patched sys/kern/sysv_shm.c hunk at :184)",
  "fix_baseline_reproduced": 1,
  "fix_patched_reproduced": 0,
  "fix_verdict": "FIXED: baseline (stock #0) panicked on the first race run; identical PoC on the fix.diff kernel (#1) completed 400/400 iterations with exit 0 and the guest stayed healthy. Second RMIDs now fail with EINVAL because the segment is hidden before vm_object_deallocate can yield.",
  "fix_evidence": [
    "run.fixed.log",
    "fix_build.log.gz",
    "panic.txt (baseline A vs patched B)"
  ]
}
