From 9d01bb0847a2e724295d3b8a49df493d4a60d598 Mon Sep 17 00:00:00 2001 From: Rui Marinho Date: Fri, 29 May 2026 16:13:22 +0100 Subject: [PATCH] Fix native resource leaks in Keychain - GetAllSigningIdentities: CFRelease the CFDataRef returned by SecCertificateCopyData (Copy semantics = +1 retain count). - GetAllSigningCertificates: Same CFDataRef leak fix. - FindInternetUserNameAndPassword: Free passwordData with SecKeychainItemFreeContent and release the item ref. - FindInternetPassword(string,...): Same leak fix. - FindInternetPassword(Uri): Free passwordData and align cleanup. All three FindInternet* methods now use try/finally to guarantee native resources are freed even if GetUsernameFromKeychainItemRef or marshaling throws. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- Xamarin.MacDev/Keychain.cs | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/Xamarin.MacDev/Keychain.cs b/Xamarin.MacDev/Keychain.cs index d099f6d..38e339c 100644 --- a/Xamarin.MacDev/Keychain.cs +++ b/Xamarin.MacDev/Keychain.cs @@ -459,6 +459,7 @@ public IList GetAllSigningIdentities () if (SecIdentityCopyCertificate (itemRef, out certRef) == OSStatus.Ok) { var data = SecCertificateCopyData (certRef); var rawData = CFDataGetBytes (data); + CFRelease (data); if (rawData != null) { try { @@ -520,6 +521,7 @@ public IList GetAllSigningCertificates () if (SecIdentityCopyCertificate (itemRef, out certRef) == OSStatus.Ok) { var data = SecCertificateCopyData (certRef); var rawData = CFDataGetBytes (data); + CFRelease (data); if (rawData != null) { try { @@ -935,9 +937,14 @@ public unsafe Tuple FindInternetUserNameAndPassword (Uri uri) if (result != OSStatus.Ok) return null; - var username = GetUsernameFromKeychainItemRef (item); - - return Tuple.Create (username, Marshal.PtrToStringAuto (passwordData, (int) passwordLength)); + try { + var username = GetUsernameFromKeychainItemRef (item); + var password = Marshal.PtrToStringAuto (passwordData, (int) passwordLength); + return Tuple.Create (username, password); + } finally { + SecKeychainItemFreeContent (IntPtr.Zero, passwordData); + CFRelease (item); + } } public unsafe Tuple FindInternetPassword (string serverName = "", string securityDomain = "", string accountName = "", string path = "", ushort port = 0) @@ -961,9 +968,14 @@ public unsafe Tuple FindInternetPassword (string serverName = "" if (result != OSStatus.Ok) return null; - var username = GetUsernameFromKeychainItemRef (item); - - return Tuple.Create (username, Marshal.PtrToStringAuto (passwordData, (int) passwordLength)); + try { + var username = GetUsernameFromKeychainItemRef (item); + var password = Marshal.PtrToStringAuto (passwordData, (int) passwordLength); + return Tuple.Create (username, password); + } finally { + SecKeychainItemFreeContent (IntPtr.Zero, passwordData); + CFRelease (item); + } } public string FindInternetPassword (Uri uri) @@ -988,12 +1000,15 @@ public string FindInternetPassword (Uri uri) (uint) user.Length, user, (uint) path.Length, path, (ushort) uri.Port, 0, auth, out passwordLength, out passwordData, ref item); - CFRelease (item); - - if (result != OSStatus.Ok) - return null; + try { + if (result != OSStatus.Ok) + return null; - return Marshal.PtrToStringAuto (passwordData, (int) passwordLength); + return Marshal.PtrToStringAuto (passwordData, (int) passwordLength); + } finally { + SecKeychainItemFreeContent (IntPtr.Zero, passwordData); + CFRelease (item); + } } }