4

I'm using .NET 3.5.

I'm experiencing a memory leak when creating a reference to a X509Certificate. I'm using ANTS profiler to analyse the results and the private bytes are increasing while the bytes in the heap remain static (indicating it's a memory leak due to unmanaged code).

I'm using the CRYPT32 dll to manage the certificates. I open a reference to cert store to get a store handle which is an internal pointer (intptr). I then use this store handler to find the cert in store (saved locally). Once I have the cert I close the cert store and return the cert to the calling program. I'm using the flag 0 in the certclosestore which keeps open resources open after the close store is called. I believe this is what is causing the memory leak as it's mentioned here: http://msdn.microsoft.com/en-us/library/ms937035.aspx

However, when I change the close flag to:

CertCloseStore(storeHandle, 2)

This is supposed to free up the allocated resources. However it just causes the service to bomb out.

The application works on the sense that it is verify the certs etc. The only problem is that the memory use is slowly creeping up and the service needs to be restarted every week or so. Any ideas or thoughts would be greatly appreciated.

public static X509Certificate CreateFromRegistry(string certificateIdent)
{
  X509Certificate certificate = null;
  IntPtr storeHandle = CertificateStore.CertOpenStore(CERT_STORE_PROV_SYSTEM, 
      0, 0,CERT_SYSTEM_STORE_LOCAL_MACHINE, "MY");;

  certificate = new X509Certificate(CertificateStore.
  FindCertInStore(certificateIdent, storeHandle));
  CertificateStore.CertCloseStore(storeHandle, 0);
  return certificate;
}



public class CertificateStore
{
    const int CERT_STORE_PROV_SYSTEM = 10;
    private static int CERT_SYSTEM_STORE_LOCAL_MACHINE = (2 << 16);
    const uint PKCS_7_ASN_ENCODING = 0x00010000;
    const uint X509_ASN_ENCODING = 0x00000001;
    const uint CERT_FIND_SUBJECT_STR = 0x00080007;
    const uint CERT_FIND_ISSUER_STR = 0x00080004;
    static uint MY_ENCODING_TYPE = PKCS_7_ASN_ENCODING | X509_ASN_ENCODING;

    [DllImport("CRYPT32", EntryPoint = "CertOpenStore",
        CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern IntPtr CertOpenStore(
        int storeProvider, int encodingType,
        int hcryptProv, int flags, string pvPara);

    [DllImport("CRYPT32", EntryPoint = "CertCloseStore",
        CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern bool CertCloseStore(
        IntPtr storeProvider,
        int flags);
}

public static X509Certificate FindCertInStore
        (string trustedRootIssuerName, IntPtr storeHandle)
    {
        IntPtr hCertCntxt;
        X509Certificate theActualCertificate = null;

        if (storeHandle != IntPtr.Zero)
        {
            hCertCntxt = CertFindCertificateInStore(
               storeHandle,
               MY_ENCODING_TYPE,
               0,
               CERT_FIND_ISSUER_STR,
               trustedRootIssuerName,
               IntPtr.Zero);

            if (hCertCntxt != IntPtr.Zero)
            {
                theActualCertificate = new X509Certificate(hCertCntxt);
            }
        }
        return theActualCertificate;
    }
CorribView
  • 711
  • 1
  • 19
  • 44

1 Answers1

4

Well, you are leaking CRYPT32 resources of course. One immediate candidate I see in your snippet is the return value of CertFindCertificateInStore(). It must be released by an explicit call to CertFreeCertificateContext(), I don't see one.

The X509Certification(IntPtr) constructor is not documented well, it doesn't describe how long the context needs to be valid. I see it calling an internal method named X509Utils._DuplicateCertContext() so very high odds that you immediately can call the release function after creating the object.

Do check the rest of your code with a fine-toothed comb and triple check that all handles and pointers you get from CRYPT32 are released.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • Thank you for your informative reply. You are correct, I don't call CertFreeCertificateContext anywhere in my code to release this resource. I'll give that a go just after the X509Certificate returns the cert and hopefully you're right about being able to release it after it's been created. – CorribView Aug 16 '12 at 13:04
  • It worked :-) Thanks for your solution! I was working on this for a while, think I'd still be staring at the code without your input. – CorribView Aug 16 '12 at 14:18