# CVE-2026-69516 — Connected Devices Platform Service `cdpsvc.dll` Sessions-AppId Global-Static Use-After-Free (Local EoP to Local Service)

---

## Summary

| | |
|---|---|
| **Product** | Windows — `cdpsvc.dll` (Connected Devices Platform Service, runs as Local Service) |
| **CVE ID** | CVE-2026-69516 |
| **Impact** | Elevation of Privilege (Medium Integrity → Local Service) |
| **MSRC severity** | Important |
| **CVSS** | 7.0 / 6.1 — `CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H/E:U/RL:O/RC:C` |
| **CWE** | CWE-416: Use After Free (race condition) |
| **Delivery** | Local — a Medium-IL process races the CDP session path |
| **KB / Fixed build** | KB5124008 — `cdpsvc.dll` 10.0.26100.9444 (Win11 24H2/25H2 x64) |
| **Patch Date** | September 8, 2026 (2026-Sep) |
| **Pre-patch binary** | `cdpsvc.dll` 10.0.26100.9278 — SHA256 `60ab3737d120b52aa7fcc39622093a6fc56afef3140f7847f3f634e9b99a9af0` |
| **Post-patch binary** | `cdpsvc.dll` 10.0.26100.9444 — SHA256 `e70f62d825e77a9a43272397dc2ec73b975f3ea3a4fe602b72c045acb43dfdab` |
| **Feature flag** | none — unconditional code change (not CFR-gated) |
| **Exploitability** | Exploitation Unlikely; not publicly disclosed; not exploited (per MSRC) |

> Provenance: this is the corrected diff of the 24H2 `.9278 → .9444` pair. An earlier attempt on the same
> binaries aborted with a ghidriff symbol-count assertion because one build was analyzed without its PDB.
> This build also carries heavy unrelated **WIL feature-reporting churn** (`wil::details::EnabledStateManager`,
> `FeatureImpl<__WilFeatureTraits_Feature_4001340728>`, `WilApi_RecordFeatureUsage`, `ReportUsageToService`);
> that is library re-emission, **not** this fix. The security-relevant change is the sessions-AppId
> lifetime refactor described below.

---

## Product Description

`cdpsvc.dll` implements the Connected Devices Platform Service (`CDPSvc`), running as **Local Service**.
Its `SharingPlatform` component creates and manages cross-device session streams. Several session paths
need the process-wide **"sessions AppId"** COM object, which was held in a global static
(`_sm_spSessionsAppId`).

---

## Vulnerability Summary

The session functions — `SessionClient::CreateAndStartSessionStream`,
`SessionHost::CreateAndStartSessionStream`, `Internal::SessionDiscoveryAsyncOperation::OnConnectedInternal`,
`SendInvitationHelper::SendInvitationToDevice`, and the `CDPListener` creation path — read the sessions
AppId **directly from the global static**: they null-checked it, `AddRef`'d it (via `vtable+8`) and used
the raw pointer, with **no synchronization** against concurrent release/re-initialization of that global.
A second thread that releases or replaces `_sm_spSessionsAppId` between the check and the use frees the
object while the session path still references it — a **use-after-free** in the Local Service process. A
Medium-IL caller that wins the race can elevate to **Local Service**. Microsoft rates it **Important, 7.0**,
`AC:H` (race required).

---

## Prerequisites and Constraints

- Local Medium-IL code execution able to drive CDP session creation (`AV:L`, `PR:L`, `UI:N`).
- A race must be won (`AC:H`): the global AppId must be released/replaced in the window where a session
  path holds the raw pointer.

---

## Vulnerability Details

### Root Cause

The sessions-AppId object was shared through a global static and consumed as a raw pointer without a
lifetime-safe accessor, so its release on one thread could invalidate a pointer in use on another.

### The patch (confirmed — diff, .9278 → .9444)

Every direct read of `_sm_spSessionsAppId` is replaced by a call to a new accessor
`SharingPlatform::SharingPlatformStatics::GetSessionsAppId`, which returns a properly reference-counted
local `ComPtr`:

```c
// SessionClient::CreateAndStartSessionStream — PRE (.9278)
if (_sm_spSessionsAppId != NULL) {
    (**(code **)(*_sm_spSessionsAppId + 8))();   // AddRef on the raw global static
}
local = _sm_spSessionsAppId;                     // use the shared pointer directly (racy)

// SessionClient::CreateAndStartSessionStream — POST (.9444)
lVar2 = SharingPlatform::SharingPlatformStatics::GetSessionsAppId(&local_78);   // *** NEW accessor ***
if (lVar2 < 0) {
    wil::details::in1diag3::Return_Hr(..., 0x148, ".\\sessionclient.cpp", lVar2);
} else {
    ... use local_78 (a ref-counted local copy) ...
}
```

`GetSessionsAppId` returns an `AddRef`'d local reference under the statics' own synchronization, so the
AppId object cannot be freed out from under the session path. The same substitution is applied to the
session-host, async-connect, and invitation paths (the accessor appears as a new callee of each in the
diff). The change is **unconditional** — no feature gate.

### Patch Completeness Assessment

Not CFR-gated; the safe accessor ships directly in `.9444`. The fix addresses the lifetime of the shared
AppId object across the session paths; it does not otherwise change the session logic.

---

## Detection Guidance

**Behavioural.** Crashes / heap-corruption in `cdpsvc!SharingPlatform::SessionClient::CreateAndStartSessionStream`
or the CDP session/discovery/invitation paths under concurrent session activity; Medium-IL processes
driving rapid CDP session creation/teardown. There is no simple single-call trigger — the signal is
race-induced instability.

**Config.** No feature flag — ensure the Sep-2026 update (`cdpsvc.dll` 10.0.26100.9444, KB5124008) is
installed.

---

## References

- MSRC advisory — CVE-2026-69516 (Connected Devices Platform Service (Cdpsvc) Elevation of Privilege), 2026-09-08, KB5124008.
- Full binary diff: `/data/patch_diffs/cdpsvc_dll-cve-2026-69516-ghidriff.md`
