Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,12 +1,69 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Threading;
using Xunit;

namespace System.Security.Cryptography.OpenSsl.Tests
{
public static class SafeEvpPKeyHandleTests
{
[Fact]
public static void DuplicateHandle_ConcurrentWithDispose_DoesNotProduceInvalidHandle()
{
using ECDsaOpenSsl ecdsa = new ECDsaOpenSsl();

for (int i = 0; i < 1000; i++)
{
SafeEvpPKeyHandle keyHandle = ecdsa.DuplicateKeyHandle();
SafeEvpPKeyHandle? duplicated = null;

Thread disposeThread = new Thread(() =>
{
Thread.Sleep(Random.Shared.Next(0, 3));
keyHandle.Dispose();
});

Thread duplicateThread = new Thread(() =>
{
Thread.Sleep(Random.Shared.Next(0, 3));
try
{
duplicated = keyHandle.DuplicateHandle();
}
catch
{
// We are only interested in crashes, not managed exceptions.
}
});

disposeThread.Start();
duplicateThread.Start();
disposeThread.Join();
duplicateThread.Join();

if (duplicated is not null)
{
bool refAdded = false;

try
{
duplicated.DangerousAddRef(ref refAdded);
Assert.NotEqual(IntPtr.Zero, duplicated.DangerousGetHandle());
}
finally
{
if (refAdded)
{
duplicated.DangerousRelease();
}
}

duplicated.Dispose();
}
}
}

[Fact]
public static void TestOpenSslVersion()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,24 +53,41 @@ public SafeEvpPKeyHandle DuplicateHandle()
if (IsInvalid)
throw new InvalidOperationException(SR.Cryptography_OpenInvalidHandle);

// Reliability: Allocate the SafeHandle before calling UpRefEvpPkey so
// that we don't lose a tracked reference in low-memory situations.
SafeEvpPKeyHandle safeHandle = new SafeEvpPKeyHandle();
// Keep the source handle alive so that a concurrent Dispose on another
// thread does not zero the handle field between UpRef and the copy below.
bool addedRef = false;

int success = Interop.Crypto.UpRefEvpPkey(this);

if (success != 1)
try
{
Debug.Fail("Called UpRefEvpPkey on a key which was already marked for destruction");
Exception e = Interop.Crypto.CreateOpenSslCryptographicException();
safeHandle.Dispose();
throw e;
DangerousAddRef(ref addedRef);

// Reliability: Allocate the SafeHandle before calling UpRefEvpPkey so
// that we don't lose a tracked reference in low-memory situations.
SafeEvpPKeyHandle safeHandle = new SafeEvpPKeyHandle();

int success = Interop.Crypto.UpRefEvpPkey(this);

if (success != 1)
{
Debug.Fail("Called UpRefEvpPkey on a key which was already marked for destruction");
Exception e = Interop.Crypto.CreateOpenSslCryptographicException();
safeHandle.Dispose();
throw e;
}

// Since we didn't actually create a new handle, copy the handle
// to the new SafeHandle. DangerousAddRef prevents ReleaseHandle
// from being called, so handle is stable here.
safeHandle.SetHandle(handle);
return safeHandle;
}
finally
{
if (addedRef)
{
DangerousRelease();
}
}

// Since we didn't actually create a new handle, copy the handle
// to the new SafeHandle.
safeHandle.SetHandle(handle);
return safeHandle;
}

/// <summary>
Expand Down
Loading