Skip to content

DokanEventRelease re-entrancy race: double ExDeleteLookasideListEx / freed VCB access causes BSOD (0xA / 0x50) #1335

Description

@strvert

Check List

  • I checked my issue doesn't exist yet (searched for lookaside / ExDeleteLookasideListEx / DokanEventRelease / AV_dokan2; no matching report)
  • My issue is valid with mirror default sample and not specific to my user-mode driver implementation (the race is in the driver's unmount path; it can be triggered from any FS implementation, including mirror, by concurrent unmount requests)
  • I can always reproduce the issue with the provided description below (timing-dependent race — reproduction is probabilistic; Driver Verifier increases the chance)
  • I have updated Dokany to the latest version and have reboot my computer after (2.3.1.1000 is the latest release)
  • I tested one of the last snapshot from appveyor CI (not run; the faulty code is unchanged on master — see below)

Describe the bug

DokanEventRelease (sys/notification.c) has a non-atomic re-entry guard, leaving a race window between passing the guard and setting the unmount flags. If two threads enter concurrently for the same VCB, this leads to either a double ExDeleteLookasideListEx on the same list (0xA IRQL_NOT_LESS_OR_EQUAL), or a store into a VCB already freed by the thread that got ahead (0x50 PAGE_FAULT_IN_NONPAGED_AREA).

Observed under a workload where a user-mode FS process repeatedly mounts/unmounts (up to 8 concurrent mounts, 60 s request timeout): multiple BSODs, three times 0xA at dokan2+0x13cbc and once 0x50 at dokan2+0x144da, all in the same process on the same code path. Present in both v2.3.1.1000 and master (as of 2026-07-22).

To Reproduce

  1. Mount a volume (mirror is fine) with a short request timeout.
  2. Issue DokanRemoveMountPoint concurrently from two threads against the same mount point — or race one DokanRemoveMountPoint against the timeout-triggered unmount (timeout.c DokanUnmount).
  3. Repeat in a loop. Enabling Driver Verifier (special pool, force IRQL checking) on dokan2.sys makes the race easier to catch.

Expected behavior

Concurrent unmount/release requests for the same VCB are serialized: one performs the teardown, the others return cleanly. No BSOD.

Logs

BSOD minidump analysis. The 0xA dumps are identical (FAILURE_BUCKET_ID: AV_dokan2!unknown_function):

nt!KeBugCheckEx
nt!KiBugCheckDispatch
nt!KiPageFault
nt!ExpRemoveGeneralLookaside+0x21
nt!ExDeleteLookasideListEx+0x28
dokan2+0x13cbc

Symbol resolution with dokan2.pdb (private PDB) from the release asset dokan.zip — the installed dokan2.sys is SHA256-identical to x64/Release/Driver/dokan2.sys:

  • dokan2+0x13cbc = dokan2!DokanEventRelease+0x198 (notification.c:380) — return address right after the ExDeleteLookasideListEx(&vcb->FCBAvlNodeLookasideList) call at line 378
  • dokan2+0x144da = dokan2!DokanStopFcbGarbageCollectorThread+0x36 (notification.c:309) — the Vcb->FcbGarbageCollectorThread = NULL; store, i.e. the 0x50 is a write to an already-freed VCB

DokanStopFcbGarbageCollectorThread is called from DokanEventRelease (line 374), so both crashes are inside the unmount path. Minidumps can be provided if needed.

Environment:

  • Windows version: Windows 11 Pro build 26200
  • Processor architecture: x64
  • Dokany version: 2.3.1.1000 (dokan2.sys built 2025-09-28)
  • Library type (Dokany/FUSE): Dokany

Additional context

Root cause in v2.3.1.1000's DokanEventRelease (sys/notification.c):

  1. The re-entry guards (line 336 IsDeletePending, line 341 IsUnmountPendingVcb) are plain flag reads with no locking; check and set are not atomic.
  2. The flags are only set at lines 358–359 (SetLongFlag: VCB_DISMOUNT_PENDING / DCB_DELETE_PENDING), after DokanDeleteMountPoint (line 355, which involves mount manager I/O). Two threads entering at about the same time both pass the guard and execute the whole teardown twice.
  3. FCBAvlNodeLookasideListInit is never cleared after ExDeleteLookasideListEx (lines 377–379; same at 385–387 on master), so nothing prevents re-entering the deleted list.

The 0xA is two threads running ExDeleteLookasideListEx on the same list concurrently — ExpRemoveGeneralLookaside then walks a corrupted list (ExDeleteLookasideListEx does not tolerate concurrent calls on the same list). The 0x50 is the trailing thread executing the line-309 store after the leading thread had already freed the VCB.

Entry points that can race for the same VCB:

  • DokanRemoveMountPointSendGlobalReleaseIRP → FSCTL_EVENT_RELEASE → DokanGlobalEventRelease (fscontrol.c:430). Every call reaches the driver; no idempotency on the user-mode side.
  • Volume FSCTL (fscontrol.c:575)
  • Timeout watcher thread → DokanUnmount (timeout.c:32). The driver unmounts on its own when the request timeout fires.
  • MountManager link deletion (device.c:469/480)
  • EventStart failure (event.c:1162)

For example, while a DokanRemoveMountPoint unmount is inside DokanDeleteMountPoint, the timeout thread (or a second DokanRemoveMountPoint) can pass the guard.

Suggested fix:

  1. Make the re-entry guard an interlocked test-and-set, and set the flags at the top of the function (before IoAcquireRemoveLock)
  2. Set FCBAvlNodeLookasideListInit = FALSE before ExDeleteLookasideListEx
  3. Make teardown steps such as DokanStopFcbGarbageCollectorThread idempotent and order them against VCB freeing

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions