diff --git a/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.X509Chain.cs b/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.X509Chain.cs index 0e5a9a7e724266..e763f586d9de8a 100644 --- a/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.X509Chain.cs +++ b/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.X509Chain.cs @@ -42,15 +42,33 @@ internal static X509Certificate2[] X509ChainGetCertificates(SafeX509ChainContext var certPtrs = new IntPtr[count]; int res = Interop.AndroidCrypto.X509ChainGetCertificates(ctx, certPtrs, certPtrs.Length); - if (res == 0) - throw new CryptographicException(); - - Debug.Assert(res <= certPtrs.Length); var certs = new X509Certificate2[certPtrs.Length]; - for (int i = 0; i < res; i++) + try + { + if (res == 0) + throw new CryptographicException(); + + Debug.Assert(res <= certPtrs.Length); + + for (int i = 0; i < res; i++) + { + // X509Certificate2 duplicates these JNI global refs; the native-returned refs remain caller-owned. + certs[i] = new X509Certificate2(certPtrs[i]); + } + } + finally { - certs[i] = new X509Certificate2(certPtrs[i]); + // The native side can populate part of certPtrs and then fail (returning 0) if a JNI + // exception is thrown mid-loop, so release every non-null entry rather than only the + // first `res` entries. + for (int i = 0; i < certPtrs.Length; i++) + { + if (certPtrs[i] != IntPtr.Zero) + { + Interop.JObjectLifetime.DeleteGlobalReference(certPtrs[i]); + } + } } if (res == certPtrs.Length)