Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More reliably clean up more SafeHandle instances #71991

Merged
merged 5 commits into from
Jul 13, 2022
Merged
Show file tree
Hide file tree
Changes from 3 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
1 change: 1 addition & 0 deletions src/coreclr/vm/corelib.h
Original file line number Diff line number Diff line change
Expand Up @@ -792,6 +792,7 @@ DEFINE_CLASS(CALLCONV_SUPPRESSGCTRANSITION, CompilerServices, CallConvSup
DEFINE_CLASS(CALLCONV_MEMBERFUNCTION, CompilerServices, CallConvMemberFunction)

DEFINE_CLASS_U(Interop, SafeHandle, SafeHandle)
DEFINE_FIELD_U(_ctorStackTrace, SafeHandle, m_ctorStackTrace)
DEFINE_FIELD_U(handle, SafeHandle, m_handle)
DEFINE_FIELD_U(_state, SafeHandle, m_state)
DEFINE_FIELD_U(_ownsHandle, SafeHandle, m_ownsHandle)
Expand Down
3 changes: 3 additions & 0 deletions src/coreclr/vm/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -1913,6 +1913,9 @@ class SafeHandle : public Object
// Modifying the order or fields of this object may require
// other changes to the classlib class definition of this
// object or special handling when loading this system class.
#if DEBUG
STRINGREF m_ctorStackTrace; // Debug-only stack trace captured when the SafeHandle was constructed
#endif
Volatile<LPVOID> m_handle;
Volatile<INT32> m_state; // Combined ref count and closed/disposed state (for atomicity)
Volatile<CLR_BOOL> m_ownsHandle;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,9 @@ internal static void CheckValidOpenSslHandle(SafeHandle handle)
{
if (handle == null || handle.IsInvalid)
{
throw CreateOpenSslCryptographicException();
Exception e = CreateOpenSslCryptographicException();
handle?.Dispose();
throw e;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,13 @@ internal static SafeSecKeyRefHandle ImportEphemeralKey(ReadOnlySpan<byte> keyBlo

if (ret == 0)
{
throw CreateExceptionForOSStatus(osStatus);
Exception e = CreateExceptionForOSStatus(osStatus);
keyHandle.Dispose();
throw e;
}

Debug.Fail($"SecKeyImportEphemeral returned {ret}");
keyHandle.Dispose();
throw new CryptographicException();
}

Expand Down
4 changes: 3 additions & 1 deletion src/libraries/Common/src/Interop/Unix/Interop.IOErrors.cs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,9 @@ internal static TSafeHandle CheckIo<TSafeHandle>(TSafeHandle handle, string? pat
{
if (handle.IsInvalid)
{
ThrowExceptionForIoErrno(Sys.GetLastErrorInfo(), path, isDirectory);
Exception e = Interop.GetExceptionForIoErrno(Sys.GetLastErrorInfo(), path, isDirectory);
handle.Dispose();
throw e;
}

return handle;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,9 @@ internal static void CheckValidOpenSslHandle(SafeHandle handle)
{
if (handle == null || handle.IsInvalid)
{
throw CreateOpenSslCryptographicException();
Exception e = CreateOpenSslCryptographicException();
handle?.Dispose();
throw e;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,9 @@ internal static SafeEcKeyHandle EcKeyCreateByExplicitCurve(ECCurve curve)

if (key == null || key.IsInvalid)
{
Exception e = Interop.Crypto.CreateOpenSslCryptographicException();
stephentoub marked this conversation as resolved.
Show resolved Hide resolved
key?.Dispose();
throw Interop.Crypto.CreateOpenSslCryptographicException();
throw e;
}

// EcKeyCreateByExplicitParameters may have polluted the error queue, but key was good in the end.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,9 @@ internal static SafeX509StoreHandle X509ChainNew(SafeX509StackHandle systemTrust

if (store.IsInvalid)
{
throw CreateOpenSslCryptographicException();
Exception e = CreateOpenSslCryptographicException();
store.Dispose();
throw e;
}

return store;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ internal static bool OpenThreadToken(TokenAccessLevels desiredAccess, WinSecurit
if (openAs == WinSecurityContext.Both)
{
openAsSelf = false;
tokenHandle.Dispose();
if (OpenThreadToken(Kernel32.GetCurrentThread(), desiredAccess, openAsSelf, out tokenHandle))
return true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Security.Cryptography;

using Microsoft.Win32.SafeHandles;
Expand All @@ -14,79 +14,54 @@ internal static partial class BCrypt
{
internal static class BCryptAlgorithmCache
{
private static readonly ConcurrentDictionary<(string HashAlgorithmId, BCryptOpenAlgorithmProviderFlags Flags), (SafeBCryptAlgorithmHandle Handle, int HashSizeInBytes)> s_handles = new();

/// <summary>
/// Return a SafeBCryptAlgorithmHandle of the desired algorithm and flags. This is a shared handle so do not dispose it!
/// Returns a SafeBCryptAlgorithmHandle of the desired algorithm and flags. This is a shared handle so do not dispose it!
/// </summary>
public static SafeBCryptAlgorithmHandle GetCachedBCryptAlgorithmHandle(string hashAlgorithmId, BCryptOpenAlgorithmProviderFlags flags, out int hashSizeInBytes)
public static unsafe SafeBCryptAlgorithmHandle GetCachedBCryptAlgorithmHandle(string hashAlgorithmId, BCryptOpenAlgorithmProviderFlags flags, out int hashSizeInBytes)
{
// There aren't that many hash algorithms around so rather than use a LowLevelDictionary and guard it with a lock,
// we'll use a simple list. To avoid locking, we'll recreate the entire list each time an entry is added and replace it atomically.
//
// This does mean that on occasion, racing threads may create two handles of the same type, but this is ok.
var key = (hashAlgorithmId, flags);

// Latch the _cache value into a local so we aren't disrupted by concurrent changes to it.
Entry[] cache = _cache;
foreach (Entry entry in cache)
while (true)
{
if (entry.HashAlgorithmId == hashAlgorithmId && entry.Flags == flags)
if (s_handles.TryGetValue(key, out (SafeBCryptAlgorithmHandle Handle, int HashSizeInBytes) result))
{
hashSizeInBytes = entry.HashSizeInBytes;
return entry.Handle;
hashSizeInBytes = result.HashSizeInBytes;
return result.Handle;
}
}

SafeBCryptAlgorithmHandle safeBCryptAlgorithmHandle;
NTSTATUS ntStatus = Interop.BCrypt.BCryptOpenAlgorithmProvider(out safeBCryptAlgorithmHandle, hashAlgorithmId, null, flags);
if (ntStatus != NTSTATUS.STATUS_SUCCESS)
throw Interop.BCrypt.CreateCryptographicException(ntStatus);

Array.Resize(ref cache, cache.Length + 1);
Entry newEntry = new Entry(hashAlgorithmId, flags, safeBCryptAlgorithmHandle);
cache[^1] = new Entry(hashAlgorithmId, flags, safeBCryptAlgorithmHandle);

// Atomically overwrite the cache with our new cache. It's possible some other thread raced to add a new entry with us - if so, one of the new entries
// will be lost and the next request will have to allocate it again. That's considered acceptable collateral damage.
_cache = cache;

hashSizeInBytes = newEntry.HashSizeInBytes;
return newEntry.Handle;
}

private static volatile Entry[] _cache = Array.Empty<Entry>();

private struct Entry
{
public unsafe Entry(string hashAlgorithmId, BCryptOpenAlgorithmProviderFlags flags, SafeBCryptAlgorithmHandle handle)
: this()
{
HashAlgorithmId = hashAlgorithmId;
Flags = flags;
Handle = handle;
NTSTATUS ntStatus = Interop.BCrypt.BCryptOpenAlgorithmProvider(out SafeBCryptAlgorithmHandle handle, key.hashAlgorithmId, null, key.flags);
if (ntStatus != NTSTATUS.STATUS_SUCCESS)
{
Exception e = Interop.BCrypt.CreateCryptographicException(ntStatus);
handle.Dispose();
throw e;
}

int hashSize;
NTSTATUS ntStatus = Interop.BCrypt.BCryptGetProperty(
ntStatus = Interop.BCrypt.BCryptGetProperty(
handle,
Interop.BCrypt.BCryptPropertyStrings.BCRYPT_HASH_LENGTH,
&hashSize,
sizeof(int),
out int cbHashSize,
0);

if (ntStatus != NTSTATUS.STATUS_SUCCESS)
{
throw Interop.BCrypt.CreateCryptographicException(ntStatus);
Exception e = Interop.BCrypt.CreateCryptographicException(ntStatus);
handle.Dispose();
throw e;
}

Debug.Assert(cbHashSize == sizeof(int));
Debug.Assert(hashSize > 0);

HashSizeInBytes = hashSize;
if (!s_handles.TryAdd(key, (handle, hashSize)))
{
handle.Dispose();
}
}

public string HashAlgorithmId { get; private set; }
public BCryptOpenAlgorithmProviderFlags Flags { get; private set; }
public SafeBCryptAlgorithmHandle Handle { get; private set; }
public int HashSizeInBytes { get; private set; }
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,17 @@ internal static unsafe SafeHandle CryptDecodeObjectToMemory(CryptDecodeObjectStr
int cbRequired = 0;

if (!CryptDecodeObject(MsgEncodingType.All, (IntPtr)lpszStructType, pbEncoded, cbEncoded, 0, null, ref cbRequired))
{
throw Marshal.GetLastWin32Error().ToCryptographicException();
}

SafeHandle sh = SafeHeapAllocHandle.Alloc(cbRequired);
if (!CryptDecodeObject(MsgEncodingType.All, (IntPtr)lpszStructType, pbEncoded, cbEncoded, 0, (void*)sh.DangerousGetHandle(), ref cbRequired))
throw Marshal.GetLastWin32Error().ToCryptographicException();
{
Exception e = Marshal.GetLastWin32Error().ToCryptographicException();
sh.Dispose();
throw e;
}

return sh;
}
Expand All @@ -42,11 +48,15 @@ internal static unsafe byte[] CryptEncodeObjectToByteArray(CryptDecodeObjectStru
{
int cb = 0;
if (!CryptEncodeObject(MsgEncodingType.All, lpszStructType, decoded, null, ref cb))
{
throw Marshal.GetLastWin32Error().ToCryptographicException();
}

byte[] encoded = new byte[cb];
if (!CryptEncodeObject(MsgEncodingType.All, lpszStructType, decoded, encoded, ref cb))
{
throw Marshal.GetLastWin32Error().ToCryptographicException();
}

return encoded.Resize(cb);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ internal static partial bool DuplicateHandle(
IntPtr hSourceProcessHandle,
IntPtr hSourceHandle,
IntPtr hTargetProcessHandle,
ref SafeAccessTokenHandle lpTargetHandle,
out SafeAccessTokenHandle lpTargetHandle,
uint dwDesiredAccess,
[MarshalAs(UnmanagedType.Bool)] bool bInheritHandle,
uint dwOptions);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ internal static SafeNCryptSecretHandle DeriveSecretAgreement(

if (error != ErrorCode.ERROR_SUCCESS)
{
secretAgreement.Dispose();
throw error.ToCryptographicException();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ internal sealed partial class ContextAwareResult
{
private WindowsIdentity? _windowsIdentity;

// Security: We need an assert for a call into WindowsIdentity.GetCurrent.
bartonjs marked this conversation as resolved.
Show resolved Hide resolved
private void SafeCaptureIdentity()
{
Debug.Assert(OperatingSystem.IsWindows());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,9 @@ public override void ImportParameters(DSAParameters parameters)
parameters.Y, parameters.Y.Length,
parameters.X, parameters.X != null ? parameters.X.Length : 0))
{
throw Interop.Crypto.CreateOpenSslCryptographicException();
Exception e = Interop.Crypto.CreateOpenSslCryptographicException();
key.Dispose();
throw e;
}

SetKey(key);
Expand Down Expand Up @@ -183,7 +185,9 @@ private SafeDsaHandle GenerateKey()

if (!Interop.Crypto.DsaGenerateKey(out key, KeySize))
{
throw Interop.Crypto.CreateOpenSslCryptographicException();
Exception e = Interop.Crypto.CreateOpenSslCryptographicException();
key.Dispose();
throw e;
}

return key;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ public int ImportParameters(ECParameters parameters)

if (key == null || key.IsInvalid)
{
key?.Dispose();
throw new CryptographicException();
}

Expand Down Expand Up @@ -175,7 +176,10 @@ public static SafeEcKeyHandle GenerateKeyByKeySize(int keySize)
SafeEcKeyHandle? key = Interop.AndroidCrypto.EcKeyCreateByOid(oid);

if (key == null || key.IsInvalid)
{
key?.Dispose();
throw new PlatformNotSupportedException(SR.Format(SR.Cryptography_CurveNotSupported, oid));
}

return key;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ internal int GenerateKey(ECCurve curve)

if (key == null || key.IsInvalid)
{
key?.Dispose();
throw new PlatformNotSupportedException(SR.Format(SR.Cryptography_CurveNotSupported, oid));
}

Expand All @@ -80,6 +81,7 @@ internal int GenerateKey(ECCurve curve)

if (key == null || key.IsInvalid)
{
key?.Dispose();
throw new CryptographicException();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,7 @@ ref MemoryMarshal.GetReference(keyBlob),
if (errorCode != ErrorCode.ERROR_SUCCESS)
{
Exception e = errorCode.ToCryptographicException();
keyHandle.Dispose();
if (errorCode == ErrorCode.NTE_INVALID_PARAMETER)
{
throw new PlatformNotSupportedException(SR.Format(SR.Cryptography_CurveNotSupported, curveName), e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,16 @@ public ECDiffieHellmanCng(int keySize)
[SupportedOSPlatform("windows")]
public ECDiffieHellmanCng(ECCurve curve)
{
// GenerateKey will already do all of the validation we need.
GenerateKey(curve);
try
{
// GenerateKey will already do all of the validation we need.
GenerateKey(curve);
}
catch
{
Dispose();
throw;
}
}

public override int KeySize
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@ internal ECDiffieHellmanOpenSslPublicKey(SafeEvpPKeyHandle pkeyHandle)

if (key.IsInvalid)
{
Exception e = Interop.Crypto.CreateOpenSslCryptographicException();
key.Dispose();
throw Interop.Crypto.CreateOpenSslCryptographicException();
throw e;
}

_key = new ECOpenSsl(key);
Expand Down
12 changes: 10 additions & 2 deletions src/libraries/Common/src/System/Security/Cryptography/ECDsaCng.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,16 @@ public sealed partial class ECDsaCng : ECDsa, IRuntimeAlgorithm
[SupportedOSPlatform("windows")]
public ECDsaCng(ECCurve curve)
{
// Specified curves generate the key immediately
GenerateKey(curve);
try
{
// Specified curves generate the key immediately
GenerateKey(curve);
}
catch
{
Dispose();
throw;
}
}

/// <summary>
Expand Down
Loading