Skip to content

[release/11.0] Guard against empty remote address on macOS accept - #133566

Merged
wfurt merged 3 commits into
release/11.0from
backport/pr-131869-to-release/11.0
Sep 21, 2026
Merged

wfurt merged 3 commits into
release/11.0from
backport/pr-131869-to-release/11.0

Conversation

@github-actions

@github-actions github-actions Bot commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

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

  • 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

@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.

@wfurt wfurt added this to the 11.0.0 milestone Sep 10, 2026
@wfurt wfurt self-assigned this Sep 10, 2026
@wfurt wfurt added the Servicing-consider Issue for next servicing release review label Sep 10, 2026
@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.

@karelz

karelz commented Sep 14, 2026

Copy link
Copy Markdown
Member

Team approval - .NET 10 regression - permanently broken state.

Note: This goes hand in hand with 10.0.x servicing change: #133567 ... therefore I am leaving the final decision to Tactics as it goes beyond just 11 RC2 (which I could approve in theory).

wfurt and others added 3 commits September 17, 2026 11:24
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>
…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>
Use a release-compatible conditional property because the servicing branch's xUnit extensions do not define TestPlatforms.OpenBSD.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 7ec5412f-9a88-4e9f-a1c4-fcff00d61248
@wfurt
wfurt force-pushed the backport/pr-131869-to-release/11.0 branch from fdd9762 to a56e8cd Compare September 17, 2026 21:30
@ViveliDuCh

Copy link
Copy Markdown
Member

Hi,

the code complete date for RC2 is Monday 21st September. Make sure to merge this PR on that date at the latest, or it won't make it into that release.

As a reminder, if this is a product change, you also need Tactics approval before merging this PR (test-only or infra-only changes don't require Tactics approval).

@wfurt wfurt added Servicing-approved Approved for servicing release and removed Servicing-consider Issue for next servicing release review labels Sep 18, 2026
@wfurt

wfurt commented Sep 18, 2026

Copy link
Copy Markdown
Member

approved via email

@wfurt

wfurt commented Sep 21, 2026

Copy link
Copy Markdown
Member

/ba-g test failures unrelated

@wfurt
wfurt merged commit b2c69ed into release/11.0 Sep 21, 2026
89 of 93 checks passed
@wfurt
wfurt deleted the backport/pr-131869-to-release/11.0 branch September 21, 2026 17:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-System.Net.Sockets Servicing-approved Approved for servicing release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants