Skip to content

Guard against empty remote address on macOS accept - #131869

Merged
wfurt merged 6 commits into
dotnet:mainfrom
wfurt:fix-macos-empty-accept-address
Sep 9, 2026
Merged

wfurt merged 6 commits into
dotnet:mainfrom
wfurt:fix-macos-empty-accept-address

Conversation

@wfurt

@wfurt wfurt commented Aug 5, 2026

Copy link
Copy Markdown
Member

Fixes #121848

Problem

On macOS, when a peer connects to an AF_INET6 listening socket and immediately resets the connection (SO_LINGER=0), accept(2) returns a valid file descriptor but leaves the remote sockaddr buffer untouched and reports addrlen=0. This kernel behavior was measured with a plain C repro in the issue comment and does not depend on dual-mode or IPV6_V6ONLY.

The 2024 fix (#108616) guarded the EndPoint.Create call inside FinishOperationAccept on the Unix path, but the shared FinishOperationSyncSuccess in SocketAsyncEventArgs.cs calls Create a second time on the same zero-sized SocketAddress and throws ArgumentException. For Kestrel this kills the accept loop permanently while the process stays alive and the port stays bound.

When a peer connects and immediately resets a socket (SO_LINGER=0),
macOS accept() can return successfully with an empty remote sockaddr
(addrlen=0). The 2024 fix (dotnet#108616) guarded the Create call inside
FinishOperationAccept, but the shared FinishOperationSyncSuccess path
calls EndPoint.Create a second time on the same zero-sized address
and throws ArgumentException.

For Kestrel this crashes the accept loop and stops accepting new
connections while leaving the process alive and the port bound.

Guard the second Create call with the same Size > 0 check, and swallow
SocketException from the diagnostic NetEventSource.Accepted call whose
access of RemoteEndPoint can trigger getpeername returning ENOTCONN.

Fixes dotnet#121848
Copilot AI lite review requested due to automatic review settings August 5, 2026 12:38
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).
13 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

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

@wfurt
wfurt requested a review from a team August 5, 2026 12:41
@wfurt wfurt added the os-mac-os-x macOS aka OSX label Aug 5, 2026
@wfurt wfurt added this to the 11.0.0 milestone Aug 5, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR addresses an accept-path robustness issue where an empty remote sockaddr can lead to EndPoint.Create(SocketAddress) throwing during accept completion, potentially breaking server accept loops on macOS. It adds a guard in SocketAsyncEventArgs.FinishOperationSyncSuccess and introduces a functional regression test covering the dual-stack + immediate RST scenario.

Changes:

  • Guard EndPoint.Create in SocketAsyncEventArgs.FinishOperationSyncSuccess when the remote SocketAddress is empty.
  • Harden NetEventSource accept logging against remote endpoint lookup failures.
  • Add a new functional test (AcceptDualStackResetTests) to exercise the reset-after-connect accept scenario on a dual-stack listener.

Reviewed changes

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

File Description
src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.cs Avoids calling EndPoint.Create on zero-sized SocketAddress during accept completion; adjusts accept logging behavior.
src/libraries/System.Net.Sockets/tests/FunctionalTests/Accept.cs Adds a regression test that repeatedly connects/resets against a dual-stack IPv6 listener and verifies the listener remains able to accept new connections.

Comment thread src/libraries/System.Net.Sockets/tests/FunctionalTests/Accept.cs Outdated
Comment thread src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.cs Outdated
@akirayamamoto

Copy link
Copy Markdown

Confirming this fixes it on 10.0. I applied the patch to the v10.0.10 tag, where the line it changes is identical, and rebuilt System.Net.Sockets. Before, our repro killed the listener on the first iteration, both on a raw dual-stack accept loop and on Kestrel with a wildcard URL. After, 200 iterations clean and still serving. macOS 26.5, arm64.

Any chance of a backport to release/10.0?

@wfurt

wfurt commented Aug 6, 2026

Copy link
Copy Markdown
Member Author

thanks @akirayamamoto for the confirmation. backport is my goal but we need approval and your verification is useful.

Addresses Copilot review feedback: passing the local remoteEndPoint
variable to NetEventSource.Accepted avoids the extra getpeername
syscall and removes the need for the broad SocketException catch.
Copilot AI review requested due to automatic review settings August 6, 2026 09:28
Marks the test as skipped rather than silently passing on hosts without
IPv6 support, matching the [ConditionalFact(typeof(Socket), nameof(Socket.OSSupportsUnixDomainSockets))]
pattern used elsewhere in this test project.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

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

Suppressed comments (1)

src/libraries/System.Net.Sockets/tests/FunctionalTests/Accept.cs:508

  • The regression being exercised is macOS-specific (per the comment), but this test currently runs on all platforms. That increases runtime/flake risk without adding coverage elsewhere. Prefer gating it to macOS with standard test attributes, and use ConditionalFact rather than an early return so the test is reported as skipped when prerequisites aren’t met.
        [ConditionalFact(typeof(Socket), nameof(Socket.OSSupportsIPv6))]
        public async Task AcceptAsync_DualStackListener_PeerImmediatelyResets_ListenerStaysHealthy()
        {
            using Socket listener = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp);
            listener.DualMode = true;

Copilot AI review requested due to automatic review settings August 6, 2026 09:35

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

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

Suppressed comments (1)

src/libraries/System.Net.Sockets/tests/FunctionalTests/Accept.cs:505

  • This test targets a macOS-specific accept(2) behavior (empty remote sockaddr on immediate RST), but it will currently run on any platform that supports IPv6. That adds significant extra work to all CI legs and risks platform-specific differences in SO_LINGER/RST semantics causing flakiness. Consider restricting the test to macOS (the affected platform).
        [ConditionalFact(typeof(Socket), nameof(Socket.OSSupportsIPv6))]
        public async Task AcceptAsync_DualStackListener_PeerImmediatelyResets_ListenerStaysHealthy()

Copilot AI review requested due to automatic review settings September 8, 2026 15:21
Copilot AI review requested due to automatic review settings September 8, 2026 22:07

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

The new functional test can often accept the healthy connection first and exit early, which may fail to reliably exercise the reset/empty-sockaddr regression scenario as written.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 3/3 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment thread src/libraries/System.Net.Sockets/tests/FunctionalTests/Accept.cs
Connect and reset the IPv4 peer before establishing the healthy IPv6 connection so the regression path is exercised more reliably.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings September 8, 2026 22:31

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟢 Approval recommended

The changes are narrowly scoped, align with existing accept-socket initialization behavior (supporting remoteEP == null), and add a targeted regression test.

Review details

Suppressed comments (1)

Previously missed (1) — in code that hasn't changed since the last review.

src/libraries/System.Net.Sockets/tests/FunctionalTests/Accept.cs:505

  • The hard-coded iteration count 200 is a magic number in a stress-style regression test. Giving it a named constant makes it clearer that the repetition is intentional (and makes it easier to adjust later without hunting through the loop body).
  • Files reviewed: 3/3 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

@wfurt
wfurt merged commit 7c35b7d into dotnet:main Sep 9, 2026
78 of 81 checks passed
@wfurt

wfurt commented Sep 10, 2026

Copy link
Copy Markdown
Member Author

/backport to release/10.0

Note

This backport command was posted with assistance from GitHub Copilot.

@wfurt

wfurt commented Sep 10, 2026

Copy link
Copy Markdown
Member Author

/backport to release/11.0

Note

This backport command was posted with assistance from GitHub Copilot.

@github-actions

Copy link
Copy Markdown
Contributor

Started backporting to release/10.0 (link to workflow run)

@github-actions

Copy link
Copy Markdown
Contributor

Started backporting to release/11.0 (link to workflow run)

wfurt pushed a commit that referenced this pull request Sep 16, 2026
…indows/Linux (refs #133778) (#133925)

Workflow artifact: ci-fix
Artifact kind: help
Linked KBE: #133778

> [!NOTE]
> This is an AI/Copilot-generated **best-effort** fix attempt that I
could not fully validate. It is a starting point for a maintainer, not a
finished change. Please review the analysis below before merging.

## Root cause (best analysis)


`System.Net.Sockets.Tests.AcceptDualStackResetTests.Accept_DualStackListener_PeerImmediatelyResets_ListenerStaysHealthy(useAsync:
True)` fails on `windows-x64`/`linux-x64` (`TestReadyToRun_Libraries`
and the runtime pipeline):

```
System.Net.Sockets.SocketException : An existing connection was forcibly closed by the remote host.
   at System.Net.Sockets.Tests.AcceptDualStackResetTests.Accept_DualStackListener_PeerImmediatelyResets_ListenerStaysHealthy(Boolean useAsync) in /_/src/libraries/System.Net.Sockets/tests/FunctionalTests/Accept.cs:line 527
```

Line 527 is the `AcceptAsync`/`Accept` call, **not** the receive. The
test (added by #131869) closes an IPv4 peer with
`SO_LINGER=0` so the kernel sends an immediate RST, then expects the
listener to still deliver the healthy IPv6 peer's byte. The `try { ... }
catch (SocketException)` block only wraps the `ReceiveAsync`; the
`accept` that precedes it sits **outside** the `try`. On Windows and
Linux the reset is surfaced by `accept` itself (`WSAECONNRESET` /
"connection forcibly closed"), which escapes the catch and fails the
test, even though the very next accept iteration would return the
healthy peer. macOS discards the reset at accept (which
#131869 hardened `EndPoint.Create` for), so it doesn't hit
this path — which is why the test passed there but not on Windows/Linux.

The test comment already anticipates this ("Some platforms surface the
reset connection from accept()"), but the code doesn't guard the accept
accordingly.

## Attempted fix

Move the `accept` inside the existing `try` block so a `SocketException`
from either the accept or the following receive is tolerated, and the
loop retries to accept the healthy peer on the next iteration (the loop
already runs up to 2 accepts). No production code changes; the
listener-stays-healthy assertion is preserved. This is **not** a
test-disable — the test still runs and still asserts the healthy peer's
message is received.

## What is unverified / where I need help

- I could not build and run `System.Net.Sockets.Tests` in this
environment, so I have not confirmed the test now passes on
windows-x64/linux-x64 while still catching a genuine listener
regression.
- Please confirm the intended contract: on Windows/Linux, should the RST
be observable at `accept` (making tolerating it here correct), or should
the runtime itself suppress the reset connection from the accept loop
the way macOS does? If the latter, the real fix belongs in the socket
accept path rather than the test.

## Validation
- Command: `not run because the sockets functional test suite could not
be built/executed within the run budget`
- Result: not run

## Evidence
- Failing build:
https://dev.azure.com/dnceng-public/cbb18261-c48f-4abb-8651-8cdcb5474649/_build/results?buildId=1593787
- First build it occurred: after #131869 (merged
2026-09-09, commit 7c35b7d), which introduced this test (computed
within the scanned window; may not be the true origin)
- Suspected regressing change: #131869 (added the test
with the accept outside the catch)

## Help wanted
- Likely author: `@wfurt` (authored #131869 that added the
test)
- Area owners (`area-System.Net.Sockets`): `@dotnet/ncl`

---
Filed by
[`ci-failure-fix`](https://github.com/dotnet/runtime/blob/main/.github/workflows/ci-failure-fix.md).
Comment here or on the workflow file to suggest changes;
[`ci-failure-scan-feedback`](https://github.com/dotnet/runtime/blob/main/.github/workflows/ci-failure-scan-feedback.md)
reads in-scope feedback daily and opens (or updates) a PR with prompt
edits.

Structured data:
```json
{
  "artifact_kind": "help",
  "linked_kbe": 133778,
  "workflow_artifact": "ci-fix"
}
```

> Generated by [CI Outer-Loop Failure
Fixer](https://github.com/dotnet/runtime/actions/runs/34916254170) ·
opus48 · 764.8 AIC · ⌖ 20.8 AIC · ⊞ 19.6K ·
[◷](https://github.com/search?q=repo%3Adotnet%2Fruntime+%22gh-aw-workflow-id%3A+ci-failure-fix%22&type=pullrequests)

<!-- gh-aw-agentic-workflow: CI Outer-Loop Failure Fixer, engine:
copilot, model: claude-opus-4.8, id: 34916254170, workflow_id:
ci-failure-fix, run:
https://github.com/dotnet/runtime/actions/runs/34916254170 -->

<!-- gh-aw-workflow-id: ci-failure-fix -->
<!-- gh-aw-workflow-call-id: dotnet/runtime/ci-failure-fix -->

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
wfurt added a commit that referenced this pull request Sep 17, 2026
Fixes #121848

## Problem

On macOS, when a peer connects to an `AF_INET6` listening socket and
immediately resets the connection (`SO_LINGER=0`), `accept(2)` returns a
valid file descriptor but leaves the remote sockaddr buffer untouched
and reports `addrlen=0`. This kernel behavior was measured with a plain
C repro in the [issue
comment](#121848 (comment))
and does not depend on dual-mode or `IPV6_V6ONLY`.

The 2024 fix (#108616) guarded the `EndPoint.Create` call inside
`FinishOperationAccept` on the Unix path, but the shared
`FinishOperationSyncSuccess` in
[`SocketAsyncEventArgs.cs`](https://github.com/dotnet/runtime/blob/main/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.cs#L1016)
calls `Create` a second time on the same zero-sized `SocketAddress` and
throws `ArgumentException`. For Kestrel this kills the accept loop
permanently while the process stays alive and the port stays bound.

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
wfurt pushed a commit that referenced this pull request Sep 17, 2026
…indows/Linux (refs #133778) (#133925)

Workflow artifact: ci-fix
Artifact kind: help
Linked KBE: #133778

> [!NOTE]
> This is an AI/Copilot-generated **best-effort** fix attempt that I
could not fully validate. It is a starting point for a maintainer, not a
finished change. Please review the analysis below before merging.

## Root cause (best analysis)


`System.Net.Sockets.Tests.AcceptDualStackResetTests.Accept_DualStackListener_PeerImmediatelyResets_ListenerStaysHealthy(useAsync:
True)` fails on `windows-x64`/`linux-x64` (`TestReadyToRun_Libraries`
and the runtime pipeline):

```
System.Net.Sockets.SocketException : An existing connection was forcibly closed by the remote host.
   at System.Net.Sockets.Tests.AcceptDualStackResetTests.Accept_DualStackListener_PeerImmediatelyResets_ListenerStaysHealthy(Boolean useAsync) in /_/src/libraries/System.Net.Sockets/tests/FunctionalTests/Accept.cs:line 527
```

Line 527 is the `AcceptAsync`/`Accept` call, **not** the receive. The
test (added by #131869) closes an IPv4 peer with
`SO_LINGER=0` so the kernel sends an immediate RST, then expects the
listener to still deliver the healthy IPv6 peer's byte. The `try { ... }
catch (SocketException)` block only wraps the `ReceiveAsync`; the
`accept` that precedes it sits **outside** the `try`. On Windows and
Linux the reset is surfaced by `accept` itself (`WSAECONNRESET` /
"connection forcibly closed"), which escapes the catch and fails the
test, even though the very next accept iteration would return the
healthy peer. macOS discards the reset at accept (which
#131869 hardened `EndPoint.Create` for), so it doesn't hit
this path — which is why the test passed there but not on Windows/Linux.

The test comment already anticipates this ("Some platforms surface the
reset connection from accept()"), but the code doesn't guard the accept
accordingly.

## Attempted fix

Move the `accept` inside the existing `try` block so a `SocketException`
from either the accept or the following receive is tolerated, and the
loop retries to accept the healthy peer on the next iteration (the loop
already runs up to 2 accepts). No production code changes; the
listener-stays-healthy assertion is preserved. This is **not** a
test-disable — the test still runs and still asserts the healthy peer's
message is received.

## What is unverified / where I need help

- I could not build and run `System.Net.Sockets.Tests` in this
environment, so I have not confirmed the test now passes on
windows-x64/linux-x64 while still catching a genuine listener
regression.
- Please confirm the intended contract: on Windows/Linux, should the RST
be observable at `accept` (making tolerating it here correct), or should
the runtime itself suppress the reset connection from the accept loop
the way macOS does? If the latter, the real fix belongs in the socket
accept path rather than the test.

## Validation
- Command: `not run because the sockets functional test suite could not
be built/executed within the run budget`
- Result: not run

## Evidence
- Failing build:
https://dev.azure.com/dnceng-public/cbb18261-c48f-4abb-8651-8cdcb5474649/_build/results?buildId=1593787
- First build it occurred: after #131869 (merged
2026-09-09, commit 7c35b7d), which introduced this test (computed
within the scanned window; may not be the true origin)
- Suspected regressing change: #131869 (added the test
with the accept outside the catch)

## Help wanted
- Likely author: `@wfurt` (authored #131869 that added the
test)
- Area owners (`area-System.Net.Sockets`): `@dotnet/ncl`

---
Filed by
[`ci-failure-fix`](https://github.com/dotnet/runtime/blob/main/.github/workflows/ci-failure-fix.md).
Comment here or on the workflow file to suggest changes;
[`ci-failure-scan-feedback`](https://github.com/dotnet/runtime/blob/main/.github/workflows/ci-failure-scan-feedback.md)
reads in-scope feedback daily and opens (or updates) a PR with prompt
edits.

Structured data:
```json
{
  "artifact_kind": "help",
  "linked_kbe": 133778,
  "workflow_artifact": "ci-fix"
}
```

> Generated by [CI Outer-Loop Failure
Fixer](https://github.com/dotnet/runtime/actions/runs/34916254170) ·
opus48 · 764.8 AIC · ⌖ 20.8 AIC · ⊞ 19.6K ·
[◷](https://github.com/search?q=repo%3Adotnet%2Fruntime+%22gh-aw-workflow-id%3A+ci-failure-fix%22&type=pullrequests)

<!-- gh-aw-agentic-workflow: CI Outer-Loop Failure Fixer, engine:
copilot, model: claude-opus-4.8, id: 34916254170, workflow_id:
ci-failure-fix, run:
https://github.com/dotnet/runtime/actions/runs/34916254170 -->

<!-- gh-aw-workflow-id: ci-failure-fix -->
<!-- gh-aw-workflow-call-id: dotnet/runtime/ci-failure-fix -->

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
wfurt added a commit that referenced this pull request Sep 17, 2026
Fixes #121848

## Problem

On macOS, when a peer connects to an `AF_INET6` listening socket and
immediately resets the connection (`SO_LINGER=0`), `accept(2)` returns a
valid file descriptor but leaves the remote sockaddr buffer untouched
and reports `addrlen=0`. This kernel behavior was measured with a plain
C repro in the [issue
comment](#121848 (comment))
and does not depend on dual-mode or `IPV6_V6ONLY`.

The 2024 fix (#108616) guarded the `EndPoint.Create` call inside
`FinishOperationAccept` on the Unix path, but the shared
`FinishOperationSyncSuccess` in
[`SocketAsyncEventArgs.cs`](https://github.com/dotnet/runtime/blob/main/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.cs#L1016)
calls `Create` a second time on the same zero-sized `SocketAddress` and
throws `ArgumentException`. For Kestrel this kills the accept loop
permanently while the process stays alive and the port stays bound.

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
wfurt pushed a commit that referenced this pull request Sep 17, 2026
…indows/Linux (refs #133778) (#133925)

Workflow artifact: ci-fix
Artifact kind: help
Linked KBE: #133778

> [!NOTE]
> This is an AI/Copilot-generated **best-effort** fix attempt that I
could not fully validate. It is a starting point for a maintainer, not a
finished change. Please review the analysis below before merging.

## Root cause (best analysis)


`System.Net.Sockets.Tests.AcceptDualStackResetTests.Accept_DualStackListener_PeerImmediatelyResets_ListenerStaysHealthy(useAsync:
True)` fails on `windows-x64`/`linux-x64` (`TestReadyToRun_Libraries`
and the runtime pipeline):

```
System.Net.Sockets.SocketException : An existing connection was forcibly closed by the remote host.
   at System.Net.Sockets.Tests.AcceptDualStackResetTests.Accept_DualStackListener_PeerImmediatelyResets_ListenerStaysHealthy(Boolean useAsync) in /_/src/libraries/System.Net.Sockets/tests/FunctionalTests/Accept.cs:line 527
```

Line 527 is the `AcceptAsync`/`Accept` call, **not** the receive. The
test (added by #131869) closes an IPv4 peer with
`SO_LINGER=0` so the kernel sends an immediate RST, then expects the
listener to still deliver the healthy IPv6 peer's byte. The `try { ... }
catch (SocketException)` block only wraps the `ReceiveAsync`; the
`accept` that precedes it sits **outside** the `try`. On Windows and
Linux the reset is surfaced by `accept` itself (`WSAECONNRESET` /
"connection forcibly closed"), which escapes the catch and fails the
test, even though the very next accept iteration would return the
healthy peer. macOS discards the reset at accept (which
#131869 hardened `EndPoint.Create` for), so it doesn't hit
this path — which is why the test passed there but not on Windows/Linux.

The test comment already anticipates this ("Some platforms surface the
reset connection from accept()"), but the code doesn't guard the accept
accordingly.

## Attempted fix

Move the `accept` inside the existing `try` block so a `SocketException`
from either the accept or the following receive is tolerated, and the
loop retries to accept the healthy peer on the next iteration (the loop
already runs up to 2 accepts). No production code changes; the
listener-stays-healthy assertion is preserved. This is **not** a
test-disable — the test still runs and still asserts the healthy peer's
message is received.

## What is unverified / where I need help

- I could not build and run `System.Net.Sockets.Tests` in this
environment, so I have not confirmed the test now passes on
windows-x64/linux-x64 while still catching a genuine listener
regression.
- Please confirm the intended contract: on Windows/Linux, should the RST
be observable at `accept` (making tolerating it here correct), or should
the runtime itself suppress the reset connection from the accept loop
the way macOS does? If the latter, the real fix belongs in the socket
accept path rather than the test.

## Validation
- Command: `not run because the sockets functional test suite could not
be built/executed within the run budget`
- Result: not run

## Evidence
- Failing build:
https://dev.azure.com/dnceng-public/cbb18261-c48f-4abb-8651-8cdcb5474649/_build/results?buildId=1593787
- First build it occurred: after #131869 (merged
2026-09-09, commit 7c35b7d), which introduced this test (computed
within the scanned window; may not be the true origin)
- Suspected regressing change: #131869 (added the test
with the accept outside the catch)

## Help wanted
- Likely author: `@wfurt` (authored #131869 that added the
test)
- Area owners (`area-System.Net.Sockets`): `@dotnet/ncl`

---
Filed by
[`ci-failure-fix`](https://github.com/dotnet/runtime/blob/main/.github/workflows/ci-failure-fix.md).
Comment here or on the workflow file to suggest changes;
[`ci-failure-scan-feedback`](https://github.com/dotnet/runtime/blob/main/.github/workflows/ci-failure-scan-feedback.md)
reads in-scope feedback daily and opens (or updates) a PR with prompt
edits.

Structured data:
```json
{
  "artifact_kind": "help",
  "linked_kbe": 133778,
  "workflow_artifact": "ci-fix"
}
```

> Generated by [CI Outer-Loop Failure
Fixer](https://github.com/dotnet/runtime/actions/runs/34916254170) ·
opus48 · 764.8 AIC · ⌖ 20.8 AIC · ⊞ 19.6K ·
[◷](https://github.com/search?q=repo%3Adotnet%2Fruntime+%22gh-aw-workflow-id%3A+ci-failure-fix%22&type=pullrequests)

<!-- gh-aw-agentic-workflow: CI Outer-Loop Failure Fixer, engine:
copilot, model: claude-opus-4.8, id: 34916254170, workflow_id:
ci-failure-fix, run:
https://github.com/dotnet/runtime/actions/runs/34916254170 -->

<!-- gh-aw-workflow-id: ci-failure-fix -->
<!-- gh-aw-workflow-call-id: dotnet/runtime/ci-failure-fix -->

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
jtschuster pushed a commit to jtschuster/runtime that referenced this pull request Sep 18, 2026
…indows/Linux (refs dotnet#133778) (dotnet#133925)

Workflow artifact: ci-fix
Artifact kind: help
Linked KBE: dotnet#133778

> [!NOTE]
> This is an AI/Copilot-generated **best-effort** fix attempt that I
could not fully validate. It is a starting point for a maintainer, not a
finished change. Please review the analysis below before merging.

## Root cause (best analysis)


`System.Net.Sockets.Tests.AcceptDualStackResetTests.Accept_DualStackListener_PeerImmediatelyResets_ListenerStaysHealthy(useAsync:
True)` fails on `windows-x64`/`linux-x64` (`TestReadyToRun_Libraries`
and the runtime pipeline):

```
System.Net.Sockets.SocketException : An existing connection was forcibly closed by the remote host.
   at System.Net.Sockets.Tests.AcceptDualStackResetTests.Accept_DualStackListener_PeerImmediatelyResets_ListenerStaysHealthy(Boolean useAsync) in /_/src/libraries/System.Net.Sockets/tests/FunctionalTests/Accept.cs:line 527
```

Line 527 is the `AcceptAsync`/`Accept` call, **not** the receive. The
test (added by dotnet#131869) closes an IPv4 peer with
`SO_LINGER=0` so the kernel sends an immediate RST, then expects the
listener to still deliver the healthy IPv6 peer's byte. The `try { ... }
catch (SocketException)` block only wraps the `ReceiveAsync`; the
`accept` that precedes it sits **outside** the `try`. On Windows and
Linux the reset is surfaced by `accept` itself (`WSAECONNRESET` /
"connection forcibly closed"), which escapes the catch and fails the
test, even though the very next accept iteration would return the
healthy peer. macOS discards the reset at accept (which
dotnet#131869 hardened `EndPoint.Create` for), so it doesn't hit
this path — which is why the test passed there but not on Windows/Linux.

The test comment already anticipates this ("Some platforms surface the
reset connection from accept()"), but the code doesn't guard the accept
accordingly.

## Attempted fix

Move the `accept` inside the existing `try` block so a `SocketException`
from either the accept or the following receive is tolerated, and the
loop retries to accept the healthy peer on the next iteration (the loop
already runs up to 2 accepts). No production code changes; the
listener-stays-healthy assertion is preserved. This is **not** a
test-disable — the test still runs and still asserts the healthy peer's
message is received.

## What is unverified / where I need help

- I could not build and run `System.Net.Sockets.Tests` in this
environment, so I have not confirmed the test now passes on
windows-x64/linux-x64 while still catching a genuine listener
regression.
- Please confirm the intended contract: on Windows/Linux, should the RST
be observable at `accept` (making tolerating it here correct), or should
the runtime itself suppress the reset connection from the accept loop
the way macOS does? If the latter, the real fix belongs in the socket
accept path rather than the test.

## Validation
- Command: `not run because the sockets functional test suite could not
be built/executed within the run budget`
- Result: not run

## Evidence
- Failing build:
https://dev.azure.com/dnceng-public/cbb18261-c48f-4abb-8651-8cdcb5474649/_build/results?buildId=1593787
- First build it occurred: after dotnet#131869 (merged
2026-09-09, commit 7c35b7d), which introduced this test (computed
within the scanned window; may not be the true origin)
- Suspected regressing change: dotnet#131869 (added the test
with the accept outside the catch)

## Help wanted
- Likely author: `@wfurt` (authored dotnet#131869 that added the
test)
- Area owners (`area-System.Net.Sockets`): `@dotnet/ncl`

---
Filed by
[`ci-failure-fix`](https://github.com/dotnet/runtime/blob/main/.github/workflows/ci-failure-fix.md).
Comment here or on the workflow file to suggest changes;
[`ci-failure-scan-feedback`](https://github.com/dotnet/runtime/blob/main/.github/workflows/ci-failure-scan-feedback.md)
reads in-scope feedback daily and opens (or updates) a PR with prompt
edits.

Structured data:
```json
{
  "artifact_kind": "help",
  "linked_kbe": 133778,
  "workflow_artifact": "ci-fix"
}
```

> Generated by [CI Outer-Loop Failure
Fixer](https://github.com/dotnet/runtime/actions/runs/34916254170) ·
opus48 · 764.8 AIC · ⌖ 20.8 AIC · ⊞ 19.6K ·
[◷](https://github.com/search?q=repo%3Adotnet%2Fruntime+%22gh-aw-workflow-id%3A+ci-failure-fix%22&type=pullrequests)

<!-- gh-aw-agentic-workflow: CI Outer-Loop Failure Fixer, engine:
copilot, model: claude-opus-4.8, id: 34916254170, workflow_id:
ci-failure-fix, run:
https://github.com/dotnet/runtime/actions/runs/34916254170 -->

<!-- gh-aw-workflow-id: ci-failure-fix -->
<!-- gh-aw-workflow-call-id: dotnet/runtime/ci-failure-fix -->

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
wfurt added a commit that referenced this pull request Sep 21, 2026
…33567)

Backport of #131869 to release/10.0

/cc @wfurt

## Customer Impact

In some cases on Apple platforms, an unexpected error can leave us in a
permanently broken state — the listening socket remains bound to its
port, but the server stops accepting new connections.

This is a small reliability fix for a regression introduced in .NET 10.
It was reported by 7 users (GitHub and on the internet).
The fix handles this condition gracefully.

## Regression

- [x] Yes - introduced in .NET 10

## Testing

A new test was also added to cover this scenario.
The fix was verified by a customer using a build from the main branch

## Risk

Low. This just checks for error and swallows the exception.

---------

Co-authored-by: Tomas Weinfurt <tweinfurt@yahoo.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Copilot-Session: 7ec5412f-9a88-4e9f-a1c4-fcff00d61248
wfurt added a commit that referenced this pull request Sep 21, 2026
…33566)

Backport of #131869 to release/11.0

/cc @wfurt

## Customer Impact

In some cases on Apple platforms, an unexpected error can leave us in a
permanently broken state — the listening socket remains bound to its
port, but the server stops accepting new connections.

This is a small reliability fix for a regression introduced in .NET 10.
It was reported by 7 users (GitHub and on the internet).
The fix handles this condition gracefully.

## Regression

- [x] Yes - introduced in .NET 10

## Testing

A new test was also added to cover this scenario.
The fix was verified by a customer using a build from the main branch

## Risk

Low. This just checks for error and swallows the exception

---------

Co-authored-by: Tomas Weinfurt <tweinfurt@yahoo.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Copilot-Session: 7ec5412f-9a88-4e9f-a1c4-fcff00d61248
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.

SocketAddress is an invalid size for the System.Net.IPEndPoint end point

4 participants