Skip to content

Commit 0d3a436

Browse files
rzikmvcsjones
andauthored
Link peer's X509 stack handle to parent SSL safe handle (#113124)
* Link peer's X509 stack handle to parent SSL safe handle * Update src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Ssl.cs Co-authored-by: Kevin Jones <vcsjones@github.com> * Add test for dispose parallel with handshake. --------- Co-authored-by: Kevin Jones <vcsjones@github.com>
1 parent a4dd239 commit 0d3a436

File tree

2 files changed

+60
-1
lines changed

2 files changed

+60
-1
lines changed

src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Ssl.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,14 @@ internal static unsafe ReadOnlySpan<byte> SslGetAlpnSelected(SafeSslHandle ssl)
123123
internal static partial IntPtr SslGetCertificate(IntPtr ssl);
124124

125125
[LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_SslGetPeerCertChain")]
126-
internal static partial SafeSharedX509StackHandle SslGetPeerCertChain(SafeSslHandle ssl);
126+
private static partial SafeSharedX509StackHandle SslGetPeerCertChain_private(SafeSslHandle ssl);
127+
128+
internal static SafeSharedX509StackHandle SslGetPeerCertChain(SafeSslHandle ssl)
129+
{
130+
return SafeInteriorHandle.OpenInteriorHandle(
131+
SslGetPeerCertChain_private,
132+
ssl);
133+
}
127134

128135
[LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_SslGetPeerFinished")]
129136
internal static partial int SslGetPeerFinished(SafeSslHandle ssl, IntPtr buf, int count);

src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamDisposeTest.cs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,5 +102,57 @@ await TestConfiguration.WhenAllOrAnyFailedWithTimeout(
102102
await Assert.ThrowsAnyAsync<ObjectDisposedException>(() => client.ReadAsync(readBuffer, cts.Token).AsTask());
103103
}
104104
}
105+
106+
[Fact]
107+
[OuterLoop("Computationally expensive")]
108+
public async Task Dispose_ParallelWithHandshake_ThrowsODE()
109+
{
110+
using CancellationTokenSource cts = new CancellationTokenSource();
111+
cts.CancelAfter(TestConfiguration.PassingTestTimeout);
112+
113+
await Parallel.ForEachAsync(System.Linq.Enumerable.Range(0, 10000), cts.Token, async (i, token) =>
114+
{
115+
(SslStream client, SslStream server) = TestHelper.GetConnectedSslStreams();
116+
using (client)
117+
using (server)
118+
using (X509Certificate2 serverCertificate = Configuration.Certificates.GetServerCertificate())
119+
using (X509Certificate2 clientCertificate = Configuration.Certificates.GetClientCertificate())
120+
{
121+
SslClientAuthenticationOptions clientOptions = new SslClientAuthenticationOptions()
122+
{
123+
TargetHost = Guid.NewGuid().ToString("N"),
124+
RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true,
125+
};
126+
127+
SslServerAuthenticationOptions serverOptions = new SslServerAuthenticationOptions()
128+
{
129+
ServerCertificate = serverCertificate,
130+
};
131+
132+
var clientTask = Task.Run(() => client.AuthenticateAsClientAsync(clientOptions, cts.Token));
133+
var serverTask = Task.Run(() => server.AuthenticateAsServerAsync(serverOptions, cts.Token));
134+
135+
// Dispose the instances while the handshake is in progress.
136+
client.Dispose();
137+
server.Dispose();
138+
139+
await ValidateExceptionAsync(clientTask);
140+
await ValidateExceptionAsync(serverTask);
141+
}
142+
});
143+
144+
static async Task ValidateExceptionAsync(Task task)
145+
{
146+
try
147+
{
148+
await task;
149+
}
150+
// either we disposed the stream, or the other side does and we get unexpected EOF
151+
catch (Exception ex) when (ex is ObjectDisposedException or IOException)
152+
{
153+
return;
154+
}
155+
}
156+
}
105157
}
106158
}

0 commit comments

Comments
 (0)