Skip to content

Fix ConnectAsync sending stale ReceiveAsync buffer via ConnectEx#124381

Merged
rzikm merged 1 commit intodotnet:mainfrom
rzikm:fix/socket-connectex-stale-buffer
Feb 13, 2026
Merged

Fix ConnectAsync sending stale ReceiveAsync buffer via ConnectEx#124381
rzikm merged 1 commit intodotnet:mainfrom
rzikm:fix/socket-connectex-stale-buffer

Conversation

@rzikm
Copy link
Member

@rzikm rzikm commented Feb 13, 2026

Summary

Socket.ConnectAsync(EndPoint) reuses the cached _singleBufferReceiveEventArgs (AwaitableSocketAsyncEventArgs), which may still hold _buffer/_count from a prior ReceiveAsync call. StartOperationConnect does not clear these fields, so DoOperationConnectEx passes the stale buffer to ConnectEx's lpSendBuffer/dwSendDataLength — causing ConnectEx to send stale receive data as "send-on-connect" data with the new TCP connection.

This is particularly problematic after DisconnectAsync(reuseSocket: true), where the reused socket's second connection silently sends data received during the first connection, corrupting any protocol handshake (e.g. TLS ClientHello is preceded by the previous connection's data).

Fix

Clear the buffer via SetBuffer(default) before calling ConnectAsync on the cached SAEA, ensuring ConnectEx is called with a NULL send buffer (no send-on-connect data).

Reproduction

  1. Create a TCP socket, connect, receive data, then DisconnectAsync(reuseSocket: true)
  2. ConnectAsync to the same or different endpoint
  3. The server receives stale data from the first connection before any data the client explicitly sends

This was verified with:

  • A .NET loopback test (server receives 65536 leaked bytes = ReceiveBufferSize)
  • A pure C reproduction proving the Windows kernel is correct — ConnectEx(NULL, 0) sends nothing; the bug is .NET passing stale buffer/count
  • Wireshark captures showing stale TLS records from connection 1 being sent on connection 2

Fix #124353

ConnectAsync(EndPoint) reuses the cached _singleBufferReceiveEventArgs
(AwaitableSocketAsyncEventArgs), which may still hold _buffer/_count from
a prior ReceiveAsync call. StartOperationConnect does not clear these
fields, so DoOperationConnectEx passes the stale buffer to ConnectEx's
lpSendBuffer/dwSendDataLength — causing ConnectEx to send stale receive
data with the new TCP connection.

This is particularly problematic after DisconnectAsync(reuseSocket: true),
where the reused socket's second connection silently sends the data
received during the first connection, corrupting any protocol handshake
(e.g. TLS ClientHello is preceded by the previous connection's data).

The fix clears the buffer via SetBuffer(default) before ConnectAsync,
ensuring ConnectEx is called with a NULL send buffer (no send-on-connect).

Fix dotnet#124353
@dotnet-policy-service
Copy link
Contributor

Tagging subscribers to this area: @karelz, @dotnet/ncl
See info in area-owners.md if you want to be subscribed.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request fixes a critical bug where Socket.ConnectAsync(EndPoint) would inadvertently send stale data from a previous ReceiveAsync operation as "send-on-connect" data with the new TCP connection. This occurred because the cached AwaitableSocketAsyncEventArgs retained buffer state (_buffer, _offset, _count) from prior operations, which ConnectEx interprets as data to send with the connection.

The issue was particularly problematic after DisconnectAsync(reuseSocket: true), where the reused socket's second connection would silently transmit data received during the first connection, potentially corrupting protocol handshakes like TLS ClientHello.

Changes:

  • Added SetBuffer(default) call in ConnectAsync(EndPoint, CancellationToken) to clear stale buffer state before connecting
  • Added comprehensive regression test DisconnectAndReuse_SameHost_Succeeds that validates no stale data leaks across connections

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Tasks.cs Clears cached SAEA buffer state with SetBuffer(default) before ConnectAsync to prevent stale data from being passed to ConnectEx
src/libraries/System.Net.Sockets/tests/FunctionalTests/DisconnectTest.cs Adds regression test that verifies no stale receive data is sent after disconnect+reconnect

@rzikm rzikm enabled auto-merge (squash) February 13, 2026 14:25
@rzikm rzikm merged commit 6081019 into dotnet:main Feb 13, 2026
94 of 98 checks passed
richlander pushed a commit to richlander/runtime that referenced this pull request Feb 14, 2026
…net#124381)

## Summary

`Socket.ConnectAsync(EndPoint)` reuses the cached
`_singleBufferReceiveEventArgs` (`AwaitableSocketAsyncEventArgs`), which
may still hold `_buffer`/`_count` from a prior `ReceiveAsync` call.
`StartOperationConnect` does not clear these fields, so
`DoOperationConnectEx` passes the stale buffer to `ConnectEx`'s
`lpSendBuffer`/`dwSendDataLength` — causing `ConnectEx` to send stale
receive data as "send-on-connect" data with the new TCP connection.

This is particularly problematic after `DisconnectAsync(reuseSocket:
true)`, where the reused socket's second connection silently sends data
received during the first connection, corrupting any protocol handshake
(e.g. TLS `ClientHello` is preceded by the previous connection's data).

## Fix

Clear the buffer via `SetBuffer(default)` before calling `ConnectAsync`
on the cached SAEA, ensuring `ConnectEx` is called with a `NULL` send
buffer (no send-on-connect data).

## Reproduction

1. Create a TCP socket, connect, receive data, then
`DisconnectAsync(reuseSocket: true)`
2. `ConnectAsync` to the same or different endpoint
3. The server receives stale data from the first connection before any
data the client explicitly sends

This was verified with:
- A .NET loopback test (server receives 65536 leaked bytes =
`ReceiveBufferSize`)
- A pure C reproduction proving the Windows kernel is correct —
`ConnectEx(NULL, 0)` sends nothing; the bug is .NET passing stale
buffer/count
- Wireshark captures showing stale TLS records from connection 1 being
sent on connection 2

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Using a SslStream with a socket that was disconnected with reuseSocket: true causes AuthenticationException

2 participants

Comments