From 5616c03222edc3778d22668197a25aff9eb9e102 Mon Sep 17 00:00:00 2001 From: Rui Marinho Date: Wed, 29 Apr 2026 19:06:22 +0100 Subject: [PATCH 1/2] Fix native memory leak in Keychain.GetKeychainPath error path Wrap SecKeychainGetPath call and result handling in try/finally to ensure Marshal.FreeHGlobal(pathBuffer) executes even when an exception is thrown on non-OK status. Previously, the 1 KB native buffer leaked on every failed call. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- Xamarin.MacDev/Keychain.cs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Xamarin.MacDev/Keychain.cs b/Xamarin.MacDev/Keychain.cs index 734e419..1ad1eee 100644 --- a/Xamarin.MacDev/Keychain.cs +++ b/Xamarin.MacDev/Keychain.cs @@ -430,15 +430,16 @@ public string GetKeychainPath () uint bufferLength = 1024; var pathBuffer = Marshal.AllocHGlobal ((int) bufferLength); - var status = SecKeychainGetPath (keychainPtr, out bufferLength, pathBuffer); - - if (status != OSStatus.Ok) - throw new Exception ($"Could not get keychain's path {GetError (status)}"); + try { + var status = SecKeychainGetPath (keychainPtr, out bufferLength, pathBuffer); - var path = Marshal.PtrToStringAuto (pathBuffer); - Marshal.FreeHGlobal (pathBuffer); + if (status != OSStatus.Ok) + throw new Exception ($"Could not get keychain's path {GetError (status)}"); - return path; + return Marshal.PtrToStringAuto (pathBuffer); + } finally { + Marshal.FreeHGlobal (pathBuffer); + } } public IList GetAllSigningIdentities () From 2dbfe550a66bb4d270ffc677137c79ddfb211985 Mon Sep 17 00:00:00 2001 From: Rui Marinho Date: Wed, 6 May 2026 15:17:27 +0100 Subject: [PATCH 2/2] Use managed byte array instead of Marshal.AllocHGlobal in GetKeychainPath Replace unmanaged allocation with a pinned managed byte array as suggested by rolfbjarne. This eliminates the need for try/finally cleanup since the GC handles the buffer lifetime. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- Xamarin.MacDev/Keychain.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Xamarin.MacDev/Keychain.cs b/Xamarin.MacDev/Keychain.cs index 1ad1eee..d099f6d 100644 --- a/Xamarin.MacDev/Keychain.cs +++ b/Xamarin.MacDev/Keychain.cs @@ -429,17 +429,17 @@ public string GetKeychainPath () } uint bufferLength = 1024; - var pathBuffer = Marshal.AllocHGlobal ((int) bufferLength); - try { - var status = SecKeychainGetPath (keychainPtr, out bufferLength, pathBuffer); + var pathBuffer = new byte [bufferLength]; + OSStatus status; + unsafe { + fixed (byte* pathPtr = pathBuffer) + status = SecKeychainGetPath (keychainPtr, out bufferLength, (IntPtr) pathPtr); + } - if (status != OSStatus.Ok) - throw new Exception ($"Could not get keychain's path {GetError (status)}"); + if (status != OSStatus.Ok) + throw new Exception ($"Could not get keychain's path {GetError (status)}"); - return Marshal.PtrToStringAuto (pathBuffer); - } finally { - Marshal.FreeHGlobal (pathBuffer); - } + return Encoding.UTF8.GetString (pathBuffer, 0, (int) bufferLength); } public IList GetAllSigningIdentities ()