hvsocket.sys — One delivered security fix: an unconditional connection-establishment race hardening (CWE-362) in the AF_HYPERV connect() path (VmbusTlProviderConnect); the originally-claimed socket-address/authorization bypass remains a false positive (validation identical in both builds)
KB5094128
1. Overview
- Unpatched Binary:
hvsocket_unpatched.sys - Patched Binary:
hvsocket_patched.sys - Overall Similarity: 0.792 (79.2%)
- Diff Statistics: 272 matched functions, 177 changed functions, 95 identical functions, 0 unmatched functions in either direction.
- Verdict: One delivered security fix, plus the originally-framed bypass being a false positive. The socket-address validation gate that this report was originally framed around (
VmbusTlValidateSockAddress, including its rejection of wildcard destination GUIDs and its caller-authorization call) is present and functionally identical in BOTH builds, and is invoked as a mandatory gate fromVmbusTlProviderConnectin BOTH builds — so the originally-claimed authorization/wildcard bypass is not real. However,VmbusTlProviderConnectitself carries a genuine, unconditional (not feature-flag-gated) behavioral fix that this report's earlier triage missed: the patch reorders the connect path so thatVmbusTlAssociateConnectionToPartition(which publishes the new connection into the partition's connection tables and makes it reachable to peer-driven endpoint-action processing) now runs while the connection's "connect complete" notification event (endpoint+0x2C8) is held cleared, and only signals that event after the transport is established. The unpatched build publishes the connection via association while that event is still in its initial signaled state. This narrows a connection-establishment race window (CWE-362) on an attacker-reachable path and is delivered by default. See section 5(c). The bulk of the remaining 177 changed functions is a mechanical migration of the object-free path from interlocked singly-linked-list pools toExFreeToNPagedLookasideList, plus trace/ETW churn. Two further deltas are NOT delivered by default: (a) an Azure-configuration-gated denial of wildcard listeners inVmbusTlAuthenticateWildcardListen, with the original access-check path retained in the else branch, and (b) a WIL feature-flag-staged (Feature_MSRC97534_57540200) change to fast-mutex acquisition across several connection/service-association routines.
2. Vulnerability Summary
- Severity: Medium (delivered fix: connection-establishment race in
VmbusTlProviderConnect, CWE-362). The originally-claimed authorization bypass is None (Informational / false positive). - Vulnerability Class: Delivered fix — Race Condition / concurrent execution using shared state (CWE-362) on the AF_HYPERV connect path. The originally claimed Missing Authorization / Input Validation Bypass (CWE-862 / CWE-20) is NOT supported by the binaries.
- Affected Functions examined:
VmbusTlProviderConnect,VmbusTlValidateSockAddress,VmbusTlProcessNewConnection,VmbusTlAuthenticateWildcardListen, the newVmbusTlEndpointSetPendingConnectRequest/VmbusTlEndpointClearPendingConnectRequest/VmbusTlEstablishConnectionhelpers,VmbusTlSetupConnection,VmbusTlCreateConnection,VmbusTlEndpointActionWorkQueueRoutine, and theFeature_MSRC97534_57540200-gated locking routines.
Root Cause Analysis:
The premise of the original finding — that the unpatched connect path lacks socket-address validation and wildcard rejection — is false. VmbusTlValidateSockAddress exists in both builds (0x1C0019254 unpatched, 0x14001B1A0 patched). In both builds it performs exactly the same checks:
- address family must equal 0x22 (34, AF_HYPERV); otherwise it returns 0xC0000141 (STATUS_INVALID_ADDRESS) and traces "Address family mismatch.";
- the reserved field at offset +2 must be zero; otherwise 0xC0000141 and "Reserved field mismatch.";
- the destination GUID at offset +4 must not match the wildcard GUIDs (HV_GUID_ZERO, the all-zeros GUID, or HV_GUID_CHILDREN); if it matches, it returns 0xC0000141 and traces "Cannot connect to wildcard addresses.";
- it then resolves the partition (VmbusTlResolvePartitionId), references it (VmbusTlFindAndReferencePartition), and calls the caller-identity authorization routine VmbusTlAuthenticateConnectSockAddress(partition, sockaddr, PEPROCESS).
The only difference between the two VmbusTlValidateSockAddress bodies is the object-dereference cleanup path: unpatched pushes the freed object onto an interlocked S-List (ExpInterlockedPushEntrySList), patched frees it via ExFreeToNPagedLookasideList. That is a memory-management refactor, not a security change.
Attacker-Reachable Entry Point:
VmbusTlProviderConnect is reached from an AF_HYPERV WinSock connect()/WSAConnect(), but because the validation and wildcard-rejection it depends on are identical in both builds, there is no reachable behavioral difference on this path.
Call Chain (identical in both builds):
1. User-mode process calls connect() / WSAConnect() on an AF_HYPERV socket.
2. WSK dispatch routes to the hvsocket.sys NMR provider.
3. VmbusTlProviderConnect (0x1C001F9C0 unpatched, 0x140021AC0 patched) runs.
4. It calls HvSocketResolveUniversalAddress, then HvSocketIsPassthruRequest, then VmbusTlValidateSockAddress as a mandatory gate, and aborts if the return is negative before reaching VmbusTlCreateConnection. This gate is present in BOTH builds.
3. Code Comparison
Both VmbusTlProviderConnect bodies call the validation gate and reject on failure. Unpatched (VmbusTlProviderConnect @ 0x1C001F9C0, decompiled):
IsPassthruRequest = HvSocketIsPassthruRequest(a1, (__int64)&v52, (__int64)v13, &v51);
v15 = VmbusTlValidateSockAddress(a1, (__int64)&v52, (int)v13, CurrentProcess, IsPassthruRequest, &v56);
v47 = v15;
if ( v15 >= 0 )
{
v47 = VmbusTlCreateConnection(a1, CurrentProcess, &ListEntry);
// ... proceed ...
}
Patched (VmbusTlProviderConnect @ 0x140021AC0, decompiled):
IsPassthruRequest = HvSocketIsPassthruRequest(a1, (__int64)&v40, (__int64)v12, &v39);
v36 = VmbusTlValidateSockAddress(a1, &v40, (int)v12, CurrentProcess, IsPassthruRequest, (__int64)&v44);
v13 = v36;
if ( v36 < 0 )
{
// trace + abort
goto LABEL_63;
}
v36 = VmbusTlCreateConnection(a1, CurrentProcess, &Entry);
// ... proceed ...
The wildcard-rejection block inside VmbusTlValidateSockAddress is likewise present in both. Unpatched (0x1C0019254) expresses it as inline 8-byte comparisons; patched (0x14001B1A0) expresses it as memcmp against named GUID constants, but the logic is the same:
// patched VmbusTlValidateSockAddress
if ( memcmp(a2 + 2, &HV_GUID_ZERO, 0x10u) != 0 && memcmp(a2 + 2, &HV_GUID_CHILDREN, 0x10u) != 0 )
{
// not a wildcard -> resolve partition, reference it, authorize caller
v14 = VmbusTlResolvePartitionId(a1, a3, ...);
...
v6 = VmbusTlAuthenticateConnectSockAddress(v18, (__int64)a2, a4); // a4 = PEPROCESS
}
else
{
// wildcard -> "Cannot connect to wildcard addresses.", returns STATUS_INVALID_ADDRESS
}
4. Assembly Anchors
- Unpatched
VmbusTlProviderConnectcalls the validation gate at0x1C001FB98 call VmbusTlValidateSockAddress. - Patched
VmbusTlProviderConnectcalls the validation gate at0x140021C94 call VmbusTlValidateSockAddress. - Unpatched
VmbusTlValidateSockAddresscalls the caller-authorization routine at0x1C00193F2 call VmbusTlAuthenticateConnectSockAddress. - Patched
VmbusTlValidateSockAddresscalls it at0x14001B325 call VmbusTlAuthenticateConnectSockAddress.
Both the gate and the authorization call exist in both builds, so there is no missing-check delta on the connect path.
5. The two genuine deltas (neither delivered as a default-on security fix)
(a) VmbusTlAuthenticateWildcardListen — Azure-config-gated wildcard-listen denial.
Patched VmbusTlAuthenticateWildcardListen (0x1400078B8) adds a gate at the top of the function:
if ( (unsigned __int8)HvSocketGetAzureFeatureSetEnabled() != 0 )
{
// trace, then deny
return 3221225992LL; // 0xC0000208
}
else
{
// original logic: find wildcard descriptor, VmbusTlEndpointAccessCheck against its security descriptor
}
HvSocketGetAzureFeatureSetEnabled (0x14001E5C4) exists only in the patched build and reads a DWORD registry value under the virtualization key; it returns true only when that value is nonzero. When it is false — the default on an ordinary Hyper-V host — the function runs the exact same access-check path as the unpatched VmbusTlAuthenticateWildcardListen (0x1C00081AC), which already performs VmbusTlEndpointAccessCheck against the wildcard descriptor's / partition's security descriptor. In assembly, the denial is 0x1400078E5 call HvSocketGetAzureFeatureSetEnabled followed by 0x1400078F5 mov ebx, 0C0000208h. Because the original permissive path is retained in the else branch and the gate is off unless the registry value is set, this is a configuration-gated hardening for Azure-configured hosts, not a universally delivered fix. It also does not add any access check that was missing before; wildcard-listen access checking already exists in both builds.
(b) Feature_MSRC97534_57540200 — WIL feature-flag-staged locking change.
The patched build introduces the WIL staging flag Feature_MSRC97534_57540200 (the MSRC97534 token is a Microsoft Security Response Center case identifier). It gates a second fast-mutex acquisition in roughly eight connection/service-association routines, including VmbusTlAssociateConnectionToService, HvSocketBeginProcessingConnection, HvSocketPendConnection, VmbusTlConnectComplete, HvSocketAcceptIncomingConnection, VmbusTlAssociateListenerToPartition, VmbusTlCloseServiceEndpoints, and VmbusTlDissociateEndpointFromService. Concretely, in VmbusTlAssociateConnectionToService:
// unpatched (0x1C000A7A0): the (a2+16) fast mutex is taken UNCONDITIONALLY
KeEnterCriticalRegion();
ExAcquireFastMutexUnsafe((PFAST_MUTEX)(a2 + 16));
KeEnterCriticalRegion();
ExAcquireFastMutexUnsafe((PFAST_MUTEX)(a1 + 16));
// patched (0x1400096E0): the (a2+16) fast mutex is taken ONLY when the flag is enabled
if ( Feature_MSRC97534_57540200__private_IsEnabledNoReportingNoInline() != 0 )
{
KeEnterCriticalRegion();
ExAcquireFastMutexUnsafe((PFAST_MUTEX)(a2 + 16));
}
KeEnterCriticalRegion();
ExAcquireFastMutexUnsafe((PFAST_MUTEX)(a1 + 16));
This is a lock-discipline reorganization staged behind a runtime feature flag, with the pre-existing behavior reachable depending on the flag state. It carries an MSRC case tag, which indicates Microsoft is addressing a concurrency issue in these association paths, but the change is not delivered unconditionally in this build and its precise reachable impact (for example a specific race or lock-ordering defect) is not determinable from these two binaries alone. It is not the address-authorization issue the report was originally titled around.
5b. The delivered fix: connection-establishment race hardening in VmbusTlProviderConnect (CWE-362, delivered unconditionally)
Unlike the two feature/config-gated deltas above, VmbusTlProviderConnect (0x1C001F9C0 unpatched / 0x140021AC0 patched) carries an unconditional reordering of the connect path that closes a race window between publishing a new connection and finishing its transport setup. No Feature_MSRC97534_57540200 / IsEnabled gate guards this change; it is delivered by default.
The synchronization object. Each connection endpoint has a NotificationEvent at endpoint+0x2C8. It is created in VmbusTlCreateConnection and initialized signaled:
; unpatched VmbusTlCreateConnection @ 0x1C0018210
0x1C001832B lea rcx, [rbx+2C8h]; Event
0x1C0018332 mov r8b, 1; State ; State=1 -> event starts SIGNALED
0x1C0018335 xor edx, edx; Type ; Type=0 -> NotificationEvent
0x1C001833F call cs:__imp_KeInitializeEvent
This event is the "connect complete" gate. The peer-driven action processor waits on it before touching the connection's transport object at endpoint+0x2E0:
; unpatched VmbusTlEndpointActionWorkQueueRoutine @ 0x1C0005550
0x1C000580B cmp qword ptr [rbx+2E0h], 0
0x1C0005813 jz loc_1C000591E ; transport not published yet -> skip
0x1C000581F lea rcx, [rbx+2C8h]; Object
0x1C000582E call cs:__imp_KeWaitForSingleObject ; block until connect-complete signaled
0x1C000583A mov rax, [rbx+2E0h]
... call cs:__guard_dispatch_icall_fptr ; indirect call through the transport object
Unpatched order (racy). On the successful connect path, VmbusTlProviderConnect associates the connection FIRST, while the +0x2C8 event is still in its initial signaled state, and only resets that event afterwards, inside VmbusTlSetupConnection:
; unpatched VmbusTlProviderConnect @ 0x1C001F9C0 (success path)
0x1C001FE1E call VmbusTlAssociateConnectionToPartition ; publishes connection; event still SIGNALED
0x1C001FE79 mov [rbx+2A0h], rax ; store pending connect request inline
0x1C001FED5 call VmbusTlSetupConnection ; only now is the event reset
; unpatched VmbusTlSetupConnection @ 0x1C0018370
0x1C001839C call cs:__imp_KeResetEvent ; +0x2C8 cleared here (after association)
...
0x1C00184AE call cs:__imp_KeSetEvent ; re-signaled after establishment
VmbusTlProviderConnect in the unpatched build never touches the +0x2C8 event itself. So between VmbusTlAssociateConnectionToPartition returning (which registers the connection into the partition's connection tables and makes it discoverable to inbound, peer-driven VmbusTlEndpointActionWorkQueueRoutine processing) and VmbusTlSetupConnection reaching its KeResetEvent, the connection is published while its "connect complete" event is still signaled. A concurrently scheduled endpoint-action routine can pass the KeWaitForSingleObject gate and operate on a connection whose transport establishment has not yet run.
Patched order (barrier covers association). The patch clears the event FIRST, before setup and before association, and only re-signals it after the transport is established in the newly split-out VmbusTlEstablishConnection:
; patched VmbusTlProviderConnect @ 0x140021AC0 (success path)
0x140021E06 mov rcx, r14; Event ; r14 = [rbx+2C8h]
0x140021E29 call cs:__imp_KeClearEvent ; event cleared BEFORE setup/association
0x140021E41 call VmbusTlEndpointSetPendingConnectRequest ; new helper: records pending request at +0x2A0
0x140021E49 call VmbusTlSetupConnection
0x140021E5F call VmbusTlAssociateConnectionToPartition ; publishes connection while event is CLEARED
0x140021E6B jns loc_140021FE4 ; on associate success ->
0x140021FED call VmbusTlEstablishConnection ; new helper: dispatch transport, then KeSetEvent
; patched VmbusTlEstablishConnection @ 0x14001A658
0x14001A6B6 call _guard_dispatch_icall ; establish transport
0x14001A6CC lea rcx, [rbx+2C8h]; Event
0x14001A6D8 call cs:__imp_KeSetEvent ; connect-complete signaled only now
; on associate FAILURE the patch signals and unwinds the pending state
0x140021E79 call cs:__imp_KeSetEvent
0x140021E88 call VmbusTlEndpointClearPendingConnectRequest
Because the event is cleared before VmbusTlAssociateConnectionToPartition, any peer-driven VmbusTlEndpointActionWorkQueueRoutine that finds the newly published connection now blocks on the +0x2C8 wait until VmbusTlEstablishConnection has finished dispatching the transport and signaled the event. The race window between publication and transport establishment is closed.
New helpers, confirmed absent in the unpatched binary. VmbusTlEndpointSetPendingConnectRequest (0x14001A594), VmbusTlEndpointClearPendingConnectRequest (0x140002F7C), and VmbusTlEstablishConnection (0x14001A658) do not exist by content or by name in hvsocket_unpatched.asm; the unpatched build performs the equivalent pending-request store inline at 0x1C001FE79 and bundles the establishment inside VmbusTlSetupConnection.
Why this is a security fix, not a mechanical refactor. The reorder changes observable ordering on shared endpoint state: in the unpatched build a connection becomes reachable to concurrent, peer-driven action processing (indirect call through endpoint+0x2E0) before its transport is set up and while its completion gate is open; in the patched build the completion gate is held closed across the publish-and-establish window. This is the same MSRC-tagged concurrency theme as delta 5(b), but it is delivered unconditionally rather than behind a WIL flag.
Direction and gating: patched is stricter (barrier now covers association; unpatched leaves it open). Not a relaxation. Not WIL feature-staged — there is no Feature_MSRC*/IsEnabled check inside VmbusTlProviderConnect. Not telemetry, relocation, API modernization, or init-only: the connect path is reached directly from user-mode connect()/WSAConnect() on an AF_HYPERV socket.
Severity note. This is rated Medium / CWE-362. The reachable window and the narrowing are demonstrable from the binaries and the fix is MSRC-tagged and shipped by default. A concrete memory-corruption primitive (for example a reliable use-after-free or a controlled indirect call) is not demonstrable from these two binaries alone: VmbusTlEndpointActionWorkQueueRoutine null-checks endpoint+0x2E0 before use and dispatches through a CFG-guarded pointer, which bounds trivial exploitation. The finding is a genuine, attacker-reachable race that Microsoft closed unconditionally, without a proven corruption primitive from static comparison.
6. Exploit Primitive & Development Notes
None demonstrable. The originally claimed logical authorization/isolation bypass does not exist: the wildcard-address rejection and the caller-identity authorization it relied on are present in both builds. The two real deltas are a config-gated behavior and a feature-flag-staged locking change; neither yields a demonstrable, reachable primitive from these binaries. No memory-corruption, information-leak, or privilege-escalation primitive is present on the diffed paths.
7. Trigger / Reproduction
There is no differential trigger for the originally claimed bug: a connect() on an AF_HYPERV socket with a wildcard DestinationVmId is rejected with STATUS_INVALID_ADDRESS (0xC0000141) by VmbusTlValidateSockAddress in BOTH the unpatched and patched builds, because the wildcard-rejection block is identical in both. The VmbusTlAuthenticateWildcardListen denial only differs when the Azure feature-set registry value is set; the Feature_MSRC97534_57540200 locking behavior only differs by the WIL flag state.
8. Changed Functions — Full Triage
- VmbusTlProviderConnect (
0x1C001F9C0unpatched /0x140021AC0patched): Delivered security change (CWE-362), see section 5b. TheVmbusTlValidateSockAddressgate is identical in both builds, but the connect path was unconditionally reordered so thatVmbusTlAssociateConnectionToPartitionruns while theendpoint+0x2C8connect-complete event is held cleared (KeClearEventat0x140021E29, before the association at0x140021E5F), with the event only re-signaled after transport establishment in the newVmbusTlEstablishConnection. The unpatched build associates first (0x1C001FE1E) while that event is still signaled and only resets it later insideVmbusTlSetupConnection(KeResetEvent0x1C001839C). This narrows a connection-establishment race window. The remaining differences (variable renumbering, trace-level constant renames, the S-List-to-lookaside free-path swap, andExAllocatePoolWithTagtoExAllocatePool2modernization) are mechanical. - VmbusTlValidateSockAddress (
0x1C0019254unpatched /0x14001B1A0patched): Not a security change. Identical family / reserved / wildcard-GUID checks and identicalVmbusTlAuthenticateConnectSockAddresscaller-authorization call in both builds. Only the object-free cleanup path differs (S-List push vsExFreeToNPagedLookasideList). - VmbusTlProcessNewConnection (
0x1C001B824unpatched /0x14001CD30patched): Not a security change. No authorization call was added;VmbusTlAuthenticateConnectSockAddressis invoked only fromVmbusTlValidateSockAddressin both builds, never from this function. The difference is the S-List-to-lookaside refactor plus trace churn. - VmbusTlProcessNewConnectionForListener (
0x1C001ADA4unpatched /0x14001D2D4patched): Not a security change. S-List-to-lookaside refactor and refcount-cleanup restructuring. - VmbusTlAuthenticateWildcardListen (
0x1C00081ACunpatched /0x1400078B8patched): Behavioral, config-gated. Adds an Azure-feature-set-gated denial (0xC0000208) with the original access-check path retained; not a default-on fix. See section 5(a). Feature_MSRC97534_57540200-gated routines: Behavioral, feature-flag-staged. Conditional fast-mutex acquisition across the connection/service-association paths listed in section 5(b). Not delivered unconditionally.- Bulk (~170 functions): Mechanical migration of the object-free path from interlocked S-List pools to
ExFreeToNPagedLookasideList,ExAllocatePoolWithTagtoExAllocatePool2, and WPP/ETW trace argument and trace-level-constant churn. No security behavior change.
9. Unmatched Functions
0 unmatched functions in either direction. All modifications are alterations of existing functions. The Feature_MSRC97534_57540200__private_IsEnabled* and HvSocketGetAzureFeatureSetEnabled symbols are new in the patched build, but they are staging/config helpers rather than a delivered security gate.
10. Confidence & Caveats
- Confidence: High that the originally titled authentication/authorization-bypass finding is a false positive: the validation gate, the wildcard rejection, and the caller-identity authorization are all present and functionally identical in both builds, and are invoked from the connect path in both.
- Confidence on the delivered fix (section 5b): High that a real, unconditional behavioral change exists: the
endpoint+0x2C8event is initialized signaled inVmbusTlCreateConnection, the patchedVmbusTlProviderConnectclears it before association (0x140021E29) whereas the unpatched build associates while it is still signaled and resets it only later insideVmbusTlSetupConnection, and the three helper functions (VmbusTlEndpointSetPendingConnectRequest,VmbusTlEndpointClearPendingConnectRequest,VmbusTlEstablishConnection) are absent from the unpatched binary. Medium confidence on impact class (CWE-362 race) and honest that a concrete corruption primitive is not demonstrable from static comparison alone (the consumer null-checksendpoint+0x2E0and dispatches through a CFG-guarded pointer). - Caveat on the MSRC-tagged change: The
Feature_MSRC97534_57540200locking reorganization is real and MSRC-tagged, but it is staged behind a WIL feature flag with the prior path retained. Its exact security intent and reachable impact cannot be established from these two binaries alone, so it is not rated here beyond "concurrency-hardening, feature-staged, not delivered by default." - Caveat on the Azure gate: The
VmbusTlAuthenticateWildcardListendenial is real but conditioned on an Azure feature-set registry value and retains the original access-check path; it is environment-specific hardening, not a universally delivered fix.