# CVE-2026-20853 — Windows WalletService `WalletService.dll` Singleton Release Race: Unconditional Teardown in `TheWallet::Release`

---

## Summary

| | |
|---|---|
| **Product** | Windows — `WalletService.dll` (WalletService; `TheWallet` singleton) |
| **CVE ID** | CVE-2026-20853 |
| **Impact** | Elevation of Privilege |
| **MSRC severity** | Important |
| **CVSS** | 7.4 / 6.4 — `CVSS:3.1/AV:L/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H/E:U/RL:O/RC:C` |
| **CWE** | CWE-362 Race Condition (improper synchronization) — leads to UAF |
| **Delivery** | Local race — re-reference the singleton during its reference-count teardown window |
| **KB / Fixed build** | KB5074109 — `WalletService.dll` 10.0.26100.7623 (Win11 24H2 x64) |
| **Patch Date** | January 13, 2026 (2026-Jan) |
| **Pre-patch binary** | `WalletService.dll` 10.0.26100.7309 — SHA256 `ce29edfce447d9f20218d33c60e98a7f2f339512735d3bcfb73227888e47e218` |
| **Post-patch binary** | `WalletService.dll` 10.0.26100.7623 — SHA256 `0050a978e39b82cb28a554f9c1263f0cc90bf2fcf5b771c850832f50596c8d67` |
| **Feature flag** | `Feature_3403109688` — **the fix is CFR-gated** |
| **Exploitability** | Exploitation Less Likely; not publicly disclosed; not exploited (per MSRC) |

---

## Product Description

`WalletService.dll` implements the Windows WalletService. `TheWallet` is a
reference-counted **singleton**: a global instance pointer `s_pInstance` and a
reference count (at `this + 0x48`). `TheWallet::Release` decrements the count and,
when it reaches zero, clears the global pointer and destroys the object under a
critical section (`s_cs`). The service is reachable without special privileges
(`PR:N`), so a lifetime race in the singleton teardown is a local elevation
vector.

---

## Vulnerability Summary

Pre-patch, once the reference count reached zero `TheWallet::Release` entered the
critical section, re-checked the count, and then **unconditionally** nulled the
global pointer and destroyed the object:

```c
// TheWallet::Release (10.0.26100.7309) — PRE-PATCH, from our diff
result = _InterlockedDecrement(this + 0x48);   // refcount--
if (result == 0) {
    EnterCriticalSection(&s_cs);
    if (*(int *)(this + 0x48) == 0) {
        s_pInstance = 0;                        // *** plain assignment ***
        (**vtable[0x50])(this, 1);              // *** destroy object ***
    }
    LeaveCriticalSection(&s_cs);
    return 0;
}
return result;
```

The teardown overwrites `s_pInstance` and frees the object **without accounting
for another thread that may have re-referenced the object or created a new
instance** in the brief window. This is a TOCTOU race: a concurrent thread that
obtained/created the instance ends up with a pointer to memory this path then
frees — a **use-after-free** reachable through the singleton (CWE-362 → UAF).

---

## Prerequisites and Constraints

- Local, `PR:N` / `AV:L`; `AC:H` reflects the race timing.
- Race: acquire a new reference to `TheWallet` (or create a new instance) exactly
  while another thread runs `Release` past the count-zero check.
- Winning the race leaves a live reference pointing at a freed/destroyed object.

---

## Vulnerability Details

### Root Cause

After confirming the reference count is zero, `Release` destroys the object and
clears the global pointer without re-validating, under the lock, that (a) the
count is still zero and (b) `s_pInstance` still refers to `this`. A concurrent
re-reference or re-creation therefore desynchronizes ownership from the teardown.

### The patch (confirmed — diff, .7309 → .7623)

Gated behind `Feature_3403109688`, `TheWallet::Release` adds a **double check**
inside the critical section and a **compare-exchange** on the global pointer:

```c
// TheWallet::Release (10.0.26100.7623) — PATCHED, feature-enabled branch
if (iVar1 - 1 != 0) return iVar1 - 1;               // refcount not 0 -> done
EnterCriticalSection(&s_cs);                         // CComCritSecLock
if (Feature_3403109688::IsEnabled()) {
    if (*(u64 *)(this + 0x48) != 0) goto done;       // *** double-check: another thread re-referenced -> abort ***
    LOCK();
    pTVar3 = s_pInstance;
    if (this == s_pInstance) { s_pInstance = 0; pTVar3 = this; }  // *** CAS: null only if s_pInstance == this ***
    UNLOCK();                                          // = InterlockedCompareExchange64(&s_pInstance, 0, this)
    if (this != pTVar3) goto done;                    // CAS failed -> abort
}
else {                                                // feature-disabled: original behaviour
    if (*(int *)(this + 0x48) != 0) goto done;
    s_pInstance = 0;
}
if (this != 0) (**vtable[0x50])(this, 1);            // destroy only after both checks pass
done:
LeaveCriticalSection(&s_cs);
```

If the count became non-zero after the lock (another thread re-referenced), the
release is **aborted**; and the global pointer is cleared only when it still
equals `this` via the interlocked compare-exchange — so a thread that installed a
new instance is not clobbered and the object is destroyed only when it is truly
the last owner. This closes the teardown race.

### Patch Completeness Assessment

**CFR-gated behind `Feature_3403109688`.** The double-check + CAS path runs only
when the flag is enabled; the original unconditional teardown still ships in
.7623. Patch state is not determined by file version alone — the runtime-gated
pattern seen across this corpus. Verify `Feature_3403109688` is enabled to confirm
the race-free path is live.

---

## Detection Guidance

**Behavioural.** Concurrent acquisition/creation and release of the WalletService
`TheWallet` singleton; crashes shortly after the last `Release`.

**Crash signature.** UAF / invalid-pointer bugchecks in
`WalletService!TheWallet::Release` or on the `TheWallet` vtable after teardown, on
unpatched or flag-disabled builds.

**Config.** The fix is CFR-gated — confirm `Feature_3403109688` is enabled so the
double-check and compare-exchange run.

---

## References

- MSRC advisory — CVE-2026-20853 (Windows WalletService Elevation of Privilege), released 2026-01-13, KB5074109.
- Full binary diff: `/data/patch_diffs/walletservice_dll-cve-2026-20853-ghidriff.md`
- Related same-KB Clipboard/singleton race: CVE-2026-20844 (clipboardserver.dll).
