Skip to content

Work around Schannel TLS resume disable race on older Windows#126693

Open
rzikm wants to merge 4 commits intodotnet:mainfrom
rzikm:fix/tls-resume-disable-schannel
Open

Work around Schannel TLS resume disable race on older Windows#126693
rzikm wants to merge 4 commits intodotnet:mainfrom
rzikm:fix/tls-resume-disable-schannel

Conversation

@rzikm
Copy link
Copy Markdown
Member

@rzikm rzikm commented Apr 9, 2026

Summary

On Windows Server 2022 (build 20348) and older, ApplyControlToken(SSL_SESSION_DISABLE_RECONNECTS) races with Schannel's internal session cache — InitializeSecurityContext's internal LookupCacheByName finds a fresh resumable entry and embeds the session ID in the ClientHello before ApplyControlToken can expire it.

Workaround

After ApplyControlToken, delete the security context and retry InitializeSecurityContext with a null context so the new ClientHello is generated without a stale session ID. This follows the same pattern used by Schannel's own webcli.c test and http.sys.

The workaround is conditioned on Environment.OSVersion.Version.Build < 22000 (pre-Windows 11), since newer Schannel builds correctly prevent cache population when ApplyControlToken is used.

Also re-enables the ClientDisableTlsResume_Succeeds test that was disabled due to this issue.

Fixes #103449

On Windows Server 2022 (build 20348) and older, ApplyControlToken with
SSL_SESSION_DISABLE_RECONNECTS races with Schannel's internal session
cache. ISC's LookupCacheByName finds a fresh resumable entry and embeds
the session ID in the ClientHello before ApplyControlToken can expire it.

Work around by following the pattern used by Schannel's own webcli.c
test and http.sys: after ApplyControlToken, delete the security context
and retry InitializeSecurityContext with a null context so the new
ClientHello is generated without a stale session ID.

The workaround is conditioned on build < 22000 (pre-Windows 11), since
newer Schannel builds correctly prevent cache population when
ApplyControlToken is used.

Also re-enables the ClientDisableTlsResume_Succeeds test that was
disabled due to this issue.

Fixes dotnet#103449

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings April 9, 2026 07:47
@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.

Copy link
Copy Markdown
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

Works around a Schannel race on older Windows builds where disabling TLS resumption via ApplyControlToken(SSL_SESSION_DISABLE_RECONNECTS) can still result in a resumable session ID being embedded into the initial ClientHello, causing unexpected resumption.

Changes:

  • Add a Windows build-gated workaround that disposes the security context after ApplyControlToken and retries InitializeSecurityContext to regenerate ClientHello without a stale session ID.
  • Re-enable the previously disabled ClientDisableTlsResume_Succeeds test on Windows by removing the ActiveIssue annotation.

Reviewed changes

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

File Description
src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Windows.cs Implements the context-delete + retry workaround for pre-22000 Windows builds after ApplyControlToken succeeds.
src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamAllowTlsResumeTests.cs Removes the Windows ActiveIssue guard to run the TLS resume disable test again.

rzikm and others added 2 commits April 9, 2026 09:58
Move the ApplyControlToken/workaround block before the consumed/
SECBUFFER_EXTRA check so both the normal and retry paths share it.
Reuse the original inputBuffers for the retry since this only runs
on the very first ISC call (newContext == true) where the input is
empty and the buffers are unmodified.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings April 9, 2026 08:01
Copy link
Copy Markdown
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

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

ApplyControlToken takes 'ref context' which makes the compiler lose
the null-state guarantee from the outer 'context != null' check.
Use context?.Dispose() to satisfy nullable analysis.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@rzikm rzikm marked this pull request as ready for review April 9, 2026 15:28
Copilot AI review requested due to automatic review settings April 9, 2026 15:28
Copy link
Copy Markdown
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

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

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.

SslStreamTlsResumeTests.ClientDisableTlsResume_Succeeds failed in CI

2 participants