Skip to content

Commit 74afd1b

Browse files
authored
Harden UnixPkcs12Reader native memory usage
* Do not do the allocation and parsing in the constructor, since throwing there prevents Dispose from running * If something goes wrong between AllocHGlobal and moving the pointer into a PointerMemoryManager, call FreeHGlobal. * Past that point it will be correctly freed by Dispose. * Switch from AllocHGlobal/FreeHGlobal to NativeMemory.Alloc/Free * Harden the Dispose method against mid-execution failure.
1 parent ec1a6b9 commit 74afd1b

11 files changed

+48
-33
lines changed

src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/AndroidCertificatePal.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,9 @@ ref MemoryMarshal.GetReference(rawData),
118118

119119
private static AndroidCertificatePal ReadPkcs12(ReadOnlySpan<byte> rawData, SafePasswordHandle password, bool ephemeralSpecified)
120120
{
121-
using (var reader = new AndroidPkcs12Reader(rawData))
121+
using (var reader = new AndroidPkcs12Reader())
122122
{
123+
reader.ParsePkcs12(rawData);
123124
reader.Decrypt(password, ephemeralSpecified);
124125

125126
UnixPkcs12Reader.CertAndKey certAndKey = reader.GetSingleCert();

src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/AndroidPkcs12Reader.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,17 @@ namespace System.Security.Cryptography.X509Certificates
1111
{
1212
internal sealed class AndroidPkcs12Reader : UnixPkcs12Reader
1313
{
14-
internal AndroidPkcs12Reader(ReadOnlySpan<byte> data)
14+
internal AndroidPkcs12Reader()
1515
{
16-
ParsePkcs12(data);
1716
}
1817

1918
public static bool IsPkcs12(ReadOnlySpan<byte> data)
2019
{
2120
try
2221
{
23-
using (var reader = new AndroidPkcs12Reader(data))
22+
using (var reader = new AndroidPkcs12Reader())
2423
{
24+
reader.ParsePkcs12(data);
2525
return true;
2626
}
2727
}

src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/AppleCertificatePal.Pkcs12.iOS.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@ private static AppleCertificatePal ImportPkcs12(
1414
SafePasswordHandle password,
1515
bool ephemeralSpecified)
1616
{
17-
using (ApplePkcs12Reader reader = new ApplePkcs12Reader(rawData))
17+
using (ApplePkcs12Reader reader = new ApplePkcs12Reader())
1818
{
19+
reader.ParsePkcs12(rawData);
1920
reader.Decrypt(password, ephemeralSpecified);
2021
return ImportPkcs12(reader.GetSingleCert());
2122
}

src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/AppleCertificatePal.Pkcs12.macOS.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,9 @@ private static AppleCertificatePal ImportPkcs12(
1515
bool exportable,
1616
SafeKeychainHandle keychain)
1717
{
18-
using (ApplePkcs12Reader reader = new ApplePkcs12Reader(rawData))
18+
using (ApplePkcs12Reader reader = new ApplePkcs12Reader())
1919
{
20+
reader.ParsePkcs12(rawData);
2021
reader.Decrypt(password, ephemeralSpecified: false);
2122

2223
UnixPkcs12Reader.CertAndKey certAndKey = reader.GetSingleCert();

src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/ApplePkcs12Reader.iOS.cs

+1-2
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,8 @@ namespace System.Security.Cryptography.X509Certificates
1010
{
1111
internal sealed class ApplePkcs12Reader : UnixPkcs12Reader
1212
{
13-
internal ApplePkcs12Reader(ReadOnlySpan<byte> data)
13+
internal ApplePkcs12Reader()
1414
{
15-
ParsePkcs12(data);
1615
}
1716

1817
protected override ICertificatePalCore ReadX509Der(ReadOnlyMemory<byte> data)

src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/ApplePkcs12Reader.macOS.cs

+1-2
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,8 @@ namespace System.Security.Cryptography.X509Certificates
1111
{
1212
internal sealed class ApplePkcs12Reader : UnixPkcs12Reader
1313
{
14-
internal ApplePkcs12Reader(ReadOnlySpan<byte> data)
14+
internal ApplePkcs12Reader()
1515
{
16-
ParsePkcs12(data);
1716
}
1817

1918
protected override ICertificatePalCore ReadX509Der(ReadOnlyMemory<byte> data)

src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/OpenSslPkcs12Reader.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,8 @@ namespace System.Security.Cryptography.X509Certificates
99
{
1010
internal sealed class OpenSslPkcs12Reader : UnixPkcs12Reader
1111
{
12-
private OpenSslPkcs12Reader(ReadOnlySpan<byte> data)
12+
private OpenSslPkcs12Reader()
1313
{
14-
ParsePkcs12(data);
1514
}
1615

1716
protected override ICertificatePalCore ReadX509Der(ReadOnlyMemory<byte> data)
@@ -89,7 +88,8 @@ private static bool TryRead(
8988

9089
try
9190
{
92-
pkcs12Reader = new OpenSslPkcs12Reader(data);
91+
pkcs12Reader = new OpenSslPkcs12Reader();
92+
pkcs12Reader.ParsePkcs12(data);
9393
return true;
9494
}
9595
catch (CryptographicException e)

src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/StorePal.Android.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,9 @@ private static ICertificatePal[] ReadPkcs12Collection(
118118
SafePasswordHandle password,
119119
bool ephemeralSpecified)
120120
{
121-
using (var reader = new AndroidPkcs12Reader(rawData))
121+
using (var reader = new AndroidPkcs12Reader())
122122
{
123+
reader.ParsePkcs12(rawData);
123124
reader.Decrypt(password, ephemeralSpecified);
124125

125126
ICertificatePal[] certs = new ICertificatePal[reader.GetCertCount()];

src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/StorePal.iOS.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,11 @@ private static ILoaderPal FromBlob(ReadOnlySpan<byte> rawData, SafePasswordHandl
4646
if (contentType == X509ContentType.Pkcs12)
4747
{
4848
X509Certificate.EnforceIterationCountLimit(ref rawData, readingFromFile, password.PasswordProvided);
49-
ApplePkcs12Reader reader = new ApplePkcs12Reader(rawData);
49+
ApplePkcs12Reader reader = new ApplePkcs12Reader();
5050

5151
try
5252
{
53+
reader.ParsePkcs12(rawData);
5354
reader.Decrypt(password, ephemeralSpecified);
5455
return new ApplePkcs12CertLoader(reader, password);
5556
}

src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/StorePal.macOS.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -72,10 +72,11 @@ private static ApplePkcs12CertLoader ImportPkcs12(
7272
bool ephemeralSpecified,
7373
SafeKeychainHandle keychain)
7474
{
75-
ApplePkcs12Reader reader = new ApplePkcs12Reader(rawData);
75+
ApplePkcs12Reader reader = new ApplePkcs12Reader();
7676

7777
try
7878
{
79+
reader.ParsePkcs12(rawData);
7980
reader.Decrypt(password, ephemeralSpecified);
8081
return new ApplePkcs12CertLoader(reader, keychain, password, exportable);
8182
}

src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/UnixPkcs12Reader.cs

+28-17
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Collections.Generic;
66
using System.Diagnostics;
77
using System.Formats.Asn1;
8+
using System.Runtime.CompilerServices;
89
using System.Runtime.InteropServices;
910
using System.Security.Cryptography.Asn1;
1011
using System.Security.Cryptography.Asn1.Pkcs12;
@@ -30,7 +31,7 @@ internal abstract class UnixPkcs12Reader : IDisposable
3031
protected abstract ICertificatePalCore ReadX509Der(ReadOnlyMemory<byte> data);
3132
protected abstract AsymmetricAlgorithm LoadKey(ReadOnlyMemory<byte> safeBagBagValue);
3233

33-
protected void ParsePkcs12(ReadOnlySpan<byte> data)
34+
internal void ParsePkcs12(ReadOnlySpan<byte> data)
3435
{
3536
try
3637
{
@@ -42,10 +43,19 @@ protected void ParsePkcs12(ReadOnlySpan<byte> data)
4243

4344
unsafe
4445
{
45-
IntPtr tmpPtr = Marshal.AllocHGlobal(encodedData.Length);
46-
Span<byte> tmpSpan = new Span<byte>((byte*)tmpPtr, encodedData.Length);
47-
encodedData.CopyTo(tmpSpan);
48-
_tmpManager = new PointerMemoryManager<byte>((void*)tmpPtr, encodedData.Length);
46+
void* tmpPtr = NativeMemory.Alloc((uint)encodedData.Length);
47+
48+
try
49+
{
50+
Span<byte> tmpSpan = new Span<byte>((byte*)tmpPtr, encodedData.Length);
51+
encodedData.CopyTo(tmpSpan);
52+
_tmpManager = new PointerMemoryManager<byte>(tmpPtr, encodedData.Length);
53+
}
54+
catch
55+
{
56+
NativeMemory.Free(tmpPtr);
57+
throw;
58+
}
4959
}
5060

5161
ReadOnlyMemory<byte> tmpMemory = _tmpManager.Memory;
@@ -115,26 +125,27 @@ public void Dispose()
115125
// Generally, having a MemoryManager cleaned up in a Dispose is a bad practice.
116126
// In this case, the UnixPkcs12Reader is only ever created in a using statement,
117127
// never accessed by a second thread, and there isn't a manual call to Dispose
118-
// mixed in anywhere.
119-
if (_tmpManager != null)
128+
// mixed in anywhere outside of an aborted allocation path.
129+
130+
PointerMemoryManager<byte>? manager = _tmpManager;
131+
_tmpManager = null;
132+
133+
if (manager != null)
120134
{
121135
unsafe
122136
{
123-
Span<byte> tmp = _tmpManager.GetSpan();
137+
Span<byte> tmp = manager.GetSpan();
124138
CryptographicOperations.ZeroMemory(tmp);
125-
126-
fixed (byte* ptr = tmp)
127-
{
128-
Marshal.FreeHGlobal((IntPtr)ptr);
129-
}
139+
NativeMemory.Free(Unsafe.AsPointer(ref MemoryMarshal.GetReference(tmp)));
130140
}
131141

132-
((IDisposable)_tmpManager).Dispose();
133-
_tmpManager = null;
142+
((IDisposable)manager).Dispose();
134143
}
135144

136-
ContentInfoAsn[]? rentedContents = Interlocked.Exchange(ref _safeContentsValues, null);
137-
CertAndKey[]? rentedCerts = Interlocked.Exchange(ref _certs, null);
145+
ContentInfoAsn[]? rentedContents = _safeContentsValues;
146+
CertAndKey[]? rentedCerts = _certs;
147+
_safeContentsValues = null;
148+
_certs = null;
138149

139150
if (rentedContents != null)
140151
{

0 commit comments

Comments
 (0)