# CVE-2025-54105 — Windows Brokering File System `bfs.sys` Race Condition in `BfsCheckAndReleaseIdlePolicy` (Shared vs Exclusive Lock)

---

## Summary

| | |
|---|---|
| **Product** | Microsoft Windows — `bfs.sys` (Microsoft Brokering File System) |
| **CVE ID** | CVE-2025-54105 |
| **Impact** | Elevation of Privilege |
| **MSRC severity** | Important |
| **CWE** | CWE-362: Concurrent Execution using Shared Resource with Improper Synchronization (Race Condition) |
| **Patch Date** | September 9, 2025 |
| **Pre-patch binary** | `bfs.sys` 10.0.26100.4946 (Aug 2025) — SHA256 `34c6bec796e53af24c22c969fca04e363b289ffffb103fac1cd9fd49c40b567d` |
| **Post-patch binary** | `bfs.sys` 10.0.26100.6584 (Sep 9 2025 fix) — SHA256 `d3ce3d0314cc9552eec9617afc0930bb168cdbbfb1b925ade4d048bc90a089c6` |
| **Feature flag** | `Feature_3434922298` — **the fix is CFR-gated** |

---

## Product Description

The **Brokering File System** (`bfs.sys`) evaluates per-container storage **policy
entries** for AppContainer / packaged apps and is reachable from low-privileged
(including sandboxed) callers, so a synchronization bug in the policy path is a
local elevation-of-privilege primitive. Policy entries are reference-counted
objects released by `BfsDereferencePolicyEntryEx`; idle entries are reclaimed by
`BfsCheckAndReleaseIdlePolicy`, which walks the policy set under a push-lock and
drops references on entries that have gone idle.

---

## Vulnerability Summary

`BfsCheckAndReleaseIdlePolicy` walked and **released** idle policy entries while
holding only a **shared** push-lock:

```c
// BfsCheckAndReleaseIdlePolicy (bfs.sys 10.0.26100.4946) — PRE-PATCH, from our diff
KeEnterCriticalRegion();
ExAcquirePushLockSharedEx(param_1, 0);         // *** SHARED lock — allows concurrent walkers ***
...
// iterate the policy set; for idle entries:
BfsDereferencePolicyEntryEx((longlong)entry, '\x01');   // drop reference / release
...
ExReleasePushLockSharedEx(param_1, 0);
KeLeaveCriticalRegion();
```

A **shared** push-lock permits multiple threads to enter the release path
concurrently. Two threads can therefore observe the same entry as idle and both
call `BfsDereferencePolicyEntryEx` on it (or mutate the policy list
simultaneously) — a race that double-releases / frees a policy entry still in use
by the other thread (CWE-362 → use-after-free / list corruption).

---

## Prerequisites and Constraints

- Local authenticated session — reachable from low-privileged / AppContainer
  callers driving Brokering File System policy operations.
- **Requires a race** — the attacker drives concurrent policy activity so two
  threads reach the idle-release path on the same entry within the window.
- The corrupted object is a kernel-pool policy entry; winning the race yields a
  controllable use-after-free / EoP rather than a mere crash.

---

## Vulnerability Details

### Root Cause

The idle-policy reclamation path performs a **mutating** operation (dropping
references and unlinking entries) under a **shared** reader lock. Because shared
locks do not exclude other shared holders, concurrent invocations race on the
same policy entry / list, so the check ("is this entry idle?") and the action
("release it") are not atomic with respect to other threads.

### The patch (confirmed — diff, .4946 → .6584)

Gated behind `Feature_3434922298`, the lock is upgraded from **shared** to
**exclusive**, serializing the check-and-release:

```c
// BfsCheckAndReleaseIdlePolicy (10.0.26100.6584) — PATCHED
uVar7 = Feature_3434922298__private_IsEnabledDeviceUsageNoInline();
if ((int)uVar7 == 0) {
    KeEnterCriticalRegion();
    ExAcquirePushLockSharedEx(param_1, 0);        // ORIGINAL: shared
}
else {
    KeEnterCriticalRegion();
    ExAcquirePushLockExclusiveEx(param_1, 0);     // *** FIXED: exclusive ***
}
...
// release / iteration restructured so it runs under the exclusive lock when enabled
```

With the exclusive lock, only one thread walks and releases idle policy entries at
a time, so two threads can no longer race the same entry's release — closing the
CWE-362 window.

### Patch Completeness Assessment

**The fix is CFR-gated behind `Feature_3434922298`.** The exclusive-lock path runs
only when the flag is enabled; with it disabled, the patched binary still takes the
original **shared** lock and remains raceable. Patch state is not determined by
file version alone — the same runtime-gated pattern seen across this corpus,
including the sibling bug CVE-2025-53142 (`Feature_1122292024`).

---

## Detection Guidance

**Crash signature.** Pool-corruption / use-after-free bugchecks in
`bfs!BfsCheckAndReleaseIdlePolicy` / `BfsDereferencePolicyEntryEx`, intermittent
(race-dependent), on systems exercising Brokering File System policy from
non-administrative / AppContainer processes. Driver Verifier (Special Pool) on
`bfs.sys` increases observability.

**Behavioural.** Concurrent, repeated Brokering File System policy operations from
sandboxed processes aimed at racing the idle-policy release — the trigger is
sustained parallelism, not a single call.

**Config.** Because the fix is CFR-gated, confirming `Feature_3434922298` is enabled
is the only way to verify the corrected (exclusive-lock) path is live.

---

## References

- MSRC advisory — CVE-2025-54105 (Microsoft Brokering File System Elevation of Privilege)
- Full binary diff: `/data/patch_diffs/bfs_sys-cve-2025-54105-ghidriff.md`
- Sibling bug: CVE-2025-53142 (bfs.sys policy-entry use-after-free, Aug 2025)
