# DF-3093 — VERDICT

**Status: NOT REPRODUCED** (extensive adversarial campaign, kernel-instrumented;
the missing-lock premise is verified true, the race window opens in practice,
but the claimed double-allocation never manifested in bounded time)

## What the finding claims

`MSDOSFS_LOCK_MP()`/`MSDOSFS_UNLOCK_MP()` expand to nothing
(sys/vfs/msdosfs/msdosfsmount.h:216-218) and `struct msdosfsmount` has no
lock member (verified: full struct read, no lock/token), so
`clusteralloc()`/`chainalloc()`/`usemap_alloc()` and the `pm_inusemap` scan
run unsynchronized on SMP.  Two concurrent `extendfile()` calls (any users
writing different files on the same FAT mount) can observe the same free
run and both `chainalloc()` it: INVARIANTS kernels panic in
`usemap_alloc()`'s `KASSERT` "Allocating used sector" (msdosfs_fat.c:381);
release kernels silently chain two FAT chains over the same clusters —
cross-linked files.

## What was verified TRUE (premise)

* msdosfsmount.h:216-218 — the three macros are literally empty; no lock
  member exists in `struct msdosfsmount`; nothing else in the allocation
  path takes any lock (fatentry/fatchchain also rely on the same empty
  macros).  The allocator is unsynchronized by construction.  CONFIRMED.
* Concurrent execution DOES occur: a diagnostic kernel (stock code plus
  an in-flight counter and a print-only double-allocation detector —
  `instr.patch`, the INVARIANTS KASSERT made harmless) recorded **>200
  `clusteralloc` critical-section overlap events, up to 3 CPUs deep**,
  under 6 CPU-pinned, barrier-synchronized writers on a 6-vCPU guest
  (run4: 66 events; run6: +137; minimal test: 25, live-polled depth 3).
  See trace-excerpts.txt.

## What was NOT reproduced (the claim itself)

Across ~4 hours, 3 instrumented kernels and ~500 barrier-synchronized
allocation bursts in 6 adversarial geometries, **zero** double-allocations:
`debug.df3093_double` never moved from 0; the offline FAT in-degree
scanner (`fat_scan.c`, deterministic cross-link detector: every cluster
must have in-degree ≤ 1) found every unmounted image clean; the
per-writer foreign-byte check never fired; no "Freeing unused sector"
kernel messages.

Campaign geometries (each mounted via vnconfig + mount_msdos as root —
mount requires root or `vfs.usermount=1`; the trigger itself is plain
`write(2)` and would work unprivileged once mounted):

1. run2.sh: 20 cycles, 6 staggered writers × 150 × 32 KiB (the original
   seed shape) — clean.
2. race2 v3: 6 CPU-pinned (usched_set) writers, per-cache-line spin
   barrier, files pre-created and pre-opened, single large `write(2)` per
   round — 64 MiB (16384-cluster) counts on 2 GiB FAT32 — clean.
3. run3.sh: fragmented 64 × 8 MiB runs (fallback path forced) — clean.
4. run4.sh: whole-volume fragmentation (500 × 1 MiB, delete alternating;
   no contiguous run ≥ count anywhere) — 66 overlaps, 0 doubles — clean.
5. run5.sh: half-word 4-cluster free runs (concurrent commits share
   bitmap words; targets the non-atomic `|=` lost-update angle) —
   clean (setup tail-fill bug noted in run5.log).
6. run6.sh: 132-cluster word-straddling runs + 16 MiB writes — 137 new
   overlaps, 0 doubles — clean.

Diagnostic kernels (see trace-excerpts.txt):
* kernel #2: 100 μs busy-delay between the fallback decision and the
  commit — widens the decide window 1000× — still 0 doubles.
* kernel #4: + 200 μs delay after the `pm_nxtfree` read + per-call
  entry/exit TSC trace — on this kernel the extend phases of concurrent
  writes ran strictly sequentially (never observed on the timing-unmodified
  kernel #1, which recorded the 66/137 overlaps).  Anomaly treated as a
  diagnostic-kernel artifact.

## Why the race window does not convert into a double (analysis)

The unsynchronized section is real, but the allocator's structure
self-defends: every caller reads `newst = pm_nxtfree` at scan entry and
every commit advances it; the fallback's tie-break `if (l > foundl)`
(strictly-greater, msdosfs_fat.c:768/786) makes every scanner pick the
first free run from its (already advanced) `newst`; and a late scanner's
`chainlength()` re-reads the bitmap words, sees the earlier committer's
bits, and skips past.  A double needs two CPUs to both complete their
bitmap reads before either sets its first bit — a sub-microsecond overlap
of the pure-memory scan phase, while the measured entry skew of even the
tightest userland barrier (per-line go words, pinned CPUs) is tens of
microseconds.  The INVARIANTS-panic claim therefore remains a real
theoretical possibility (the KASSERT would fire on any double), but it
did not manifest and would require extreme luck or a much wider scan
window (huge maps, slower memory) to hit on this class of hardware.

## Impact assessment

* INVARIANTS/debug kernels: KASSERT panic plausible but not demonstrated.
* Release kernels: cross-linked files (silent data corruption) plausible
  but not demonstrated in ~4 h of adversarial racing.
* The missing lock is nevertheless a genuine hardening defect: the code
  was designed to be serialized (`MSDOSFS_LOCK_MP` call sites exist
  throughout msdosfs_fat.c) and the primitive was never implemented.
  FreeBSD independently added `pm_fatmutex` for exactly this surface.
  Any future change that lengthens the scan/decision phase (bigger
  volumes, debug options, preemption inside the scan) re-opens the window
  that this campaign measured as open-but-narrow.

## Reproduce

    # guest: DragonFly 6.5-DEVELOPMENT X86_64_GENERIC (stock), 6 vCPU
    cd /root/df3093
    sh run2.sh 20 6 3 2097152        # base campaign
    sh run4.sh 2 6 2 16777216        # fallback-forced geometry
    sh run6.sh 2 6 2 16777216        # word-straddling geometry
    # detection: fat_scan after every cycle + race2 foreign-byte check;
    # on an instrumented kernel (instr.patch): sysctl debug.df3093_double

Expected on a healthy kernel: `fat_scan: clean`, foreign bytes 0.  A
cross-link shows as `max in-degree >= 2` + CORRUPTION FOUND, or as
foreign bytes in per-writer fill checks.

## Environment

DragonFly 6.5-DEVELOPMENT #0 X86_64_GENERIC (stock release kernel, no
INVARIANTS), 6-vCPU QEMU/KVM, guest /usr/src byte-identical to the
audited tree (md5 aecdffd0dbf7e0195421d73ccb959831 for msdosfs_fat.c);
diagnostic kernels #1-#6 built with make nativekernel from the same tree
(guest restored to the stock snapshot after verification).

## Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest `make -j6 nativekernel KERNCONF=X86_64_GENERIC` completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.

(No fix.diff for this item: documented recommendation only - see Fix above.)
