Skip to content

Fix cached Windows TLS credential lifetime race - #135337

Merged
rzikm merged 1 commit into
dotnet:mainfrom
rzikm:rzikm/fix-windows-tls-credential-lifetime
Oct 8, 2026
Merged

rzikm merged 1 commit into
dotnet:mainfrom
rzikm:rzikm/fix-windows-tls-credential-lifetime

Conversation

@rzikm

@rzikm rzikm commented Oct 7, 2026

Copy link
Copy Markdown
Member

Windows SslStream handshakes can throw ObjectDisposedException when credential-cache scavenging releases the last reference after cache lookup but before ASC/ISC acquires its reference. The legacy retry loop does not handle that exception, and the race affects both handshake implementations.

Acquire a credential reference before returning a cache hit and retain it through the PAL call. Release it on handshake success/failure, credential replacement, and stream disposal. Recover an already-closed handle only during cache-reference acquisition, treating it as a cache miss rather than catching arbitrary handshake exceptions. Cache keys and public authentication behavior are unchanged.

Regression coverage

Add 28 process-isolated eviction cases covering client/server roles, TLS 1.2/1.3, both handshake implementations, client-certificate reselection, disabled resumption, and certificate rejection. Eviction is triggered synchronously by the cache-hit diagnostic event, before SSPI uses the credential. The tests also verify that evicted handles close after stream disposal.

All 28 final cases fail with the reported ObjectDisposedException against the preserved unchanged x64 Release product and pass against the fixed x64/x86 Debug and Release products.

Validation

Builds used a Q: subst mapping to avoid long paths. Windows x64/x86 runtime and library builds passed, as did the managed product builds for all platform targets.

Suite Architectures Result per run
Debug functional x64, x86 5,372 total, 36 skipped, 0 failed
Release functional x64, x86 5,340 total, 36 skipped, 0 failed
Debug and Release unit x64, x86 121 total, 4 skipped, 0 failed

Release omits 32 existing DEBUG-only resumption cases; both Debug suites exercise them. One earlier x64 Debug run failed a TLS-resumption flag assertion. Focused baseline/fixed checks and subsequent full suites passed; that failure's cause was not established. Runtime testing was Windows-only; Linux/macOS and older Windows versions were not exercised.

A BenchmarkDotNet ShortRun of cache lookup plus caller reference release measured 50.76 ns before and 59.30 ns after, with zero allocations in both. These are isolated cache-operation measurements, not end-to-end handshake timings.

Fixes: #133904

Resolves #133904

Note

The code changes and this description were generated with GitHub Copilot assistance.

Acquire a reference before returning cached credentials and balance it across both handshake paths, replacement, failure, and disposal. Recover closed-handle races only during cache reference acquisition. Add deterministic eviction and cleanup coverage for client/server authentication, TLS 1.2/1.3, client certificates, disabled resumption, and rejection.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: fa96f0b5-bd4d-400d-baf1-9268a7f37ed0
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 4 pipeline(s).
12 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: @dotnet/ncl, @bartonjs, @vcsjones
See info in area-owners.md if you want to be subscribed.

@rzikm

rzikm commented Oct 8, 2026

Copy link
Copy Markdown
Member Author

/ba-g SmtpClient failures are unrelated (and reverted already), no SslStream-related tests are failing

@rzikm
rzikm merged commit 5b5586c into dotnet:main Oct 8, 2026
85 of 87 checks passed
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.

System.Net.Security tests fail on Windows with ObjectDisposedException on SafeFreeCredential_SECURITY during handshake

2 participants