4

I am using WinVerifyTrust to verify the validity of some Windows executables with the following function, called in a loop from _tmain:

int signature_is_valid(const wchar_t *filepath) {
    GUID guid = WINTRUST_ACTION_GENERIC_VERIFY_V2;
    WINTRUST_FILE_INFO file_info = { 0 };
    WINTRUST_DATA wd;

    file_info.cbStruct = sizeof(file_info);
    file_info.pcwszFilePath = filepath;
    file_info.hFile = NULL;
    file_info.pgKnownSubject = NULL;

    ZeroMemory(&wd, sizeof(wd));
    wd.cbStruct = sizeof(wd);
    wd.dwUIChoice = WTD_UI_NONE;
    wd.fdwRevocationChecks = WTD_REVOCATION_CHECK_NONE;
    wd.dwUnionChoice = WTD_CHOICE_FILE;
    wd.dwStateAction = 0;
    wd.pFile = &file_info;
    wd.dwProvFlags = WTD_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT | WTD_CACHE_ONLY_URL_RETRIEVAL;

    return 0 == WinVerifyTrust(NULL, &guid, &wd);
}

However, with every loop the memory keeps growing, a sure sign of a memory leak.

Is there a problem with my understanding of the API or is the WinVerifyTrust function actually leaking? I am testing this on a Windows XP Professional SP3 system.

EDIT:

Here is some output from umdh:

+   16812 (  16992 -    180)    472 allocs BackTraceAD1
+     467 (    472 -      5) BackTraceAD1 allocations

 ntdll!RtlDebugAllocateHeap+000000E1
 ntdll!RtlAllocateHeapSlowly+00000044
 ntdll!RtlAllocateHeap+00000E64
 kernel32!LocalAlloc+00000058
 CRYPT32!operator new+00000011
 CRYPT32!I_CryptCreateLruEntry+00000011
 CRYPT32!CreateAuthRootAutoUpdateMatchCaches+00000107
 CRYPT32!CCertChainEngine::FindAuthRootAutoUpdateMatchingCtlEntries+0000004D
 CRYPT32!CChainPathObject::GetAuthRootAutoUpdateUrlStore+000000C9
 CRYPT32!CChainPathObject::CChainPathObject+0000030E
 CRYPT32!ChainCreatePathObject+00000050
 CRYPT32!CCertIssuerList::AddIssuer+0000006A
 CRYPT32!CChainPathObject::FindAndAddIssuersFromStoreByMatchType+00000182
 CRYPT32!CChainPathObject::FindAndAddIssuersByMatchType+00000096
 CRYPT32!CChainPathObject::FindAndAddIssuers+00000023
 CRYPT32!CChainPathObject::CChainPathObject+000001F9
 CRYPT32!ChainCreatePathObject+00000050
 CRYPT32!CCertIssuerList::AddIssuer+0000006A
 CRYPT32!CChainPathObject::FindAndAddIssuersFromCacheByMatchType+00000084
 CRYPT32!CChainPathObject::FindAndAddIssuersByMatchType+00000023
 CRYPT32!CChainPathObject::FindAndAddIssuers+00000063
 CRYPT32!CChainPathObject::CChainPathObject+000001F9
 CRYPT32!ChainCreatePathObject+00000050
 CRYPT32!CCertChainEngine::CreateChainContextFromPathGraph+0000019E
 CRYPT32!CCertChainEngine::GetChainContext+00000044
 CRYPT32!CertGetCertificateChain+00000060
 WINTRUST!_WalkChain+0000019C
 WINTRUST!WintrustCertificateTrust+000000B7
 WINTRUST!_VerifyTrust+00000144
 WINTRUST!WinVerifyTrust+0000004E
 SigTest!signature_is_valid+000000DD 

+   10984 (  10984 -      0)      2 allocs BackTraceBB3
+       2 (      2 -      0) BackTraceBB3 allocations

 ntdll!RtlDebugAllocateHeap+000000E1
 ntdll!RtlAllocateHeapSlowly+00000044
 ntdll!RtlAllocateHeap+00000E64
 kernel32!LocalAlloc+00000058
 CRYPT32!PkiDefaultCryptAlloc+00000011
 CRYPT32!CertFindCertificateInCRL+00000051
 cryptnet!MicrosoftCertDllVerifyRevocation+00000250
 CRYPT32!I_CryptRemainingMilliseconds+0000021B
 CRYPT32!CertVerifyRevocation+000000B7
 CRYPT32!CChainPathObject::CalculateRevocationStatus+000001F2
 CRYPT32!CChainPathObject::CalculateAdditionalStatus+00000147
 CRYPT32!CCertChainEngine::CreateChainContextFromPathGraph+00000227
 CRYPT32!CCertChainEngine::GetChainContext+00000044
 CRYPT32!CertGetCertificateChain+00000060
 WINTRUST!_WalkChain+0000019C
 WINTRUST!WintrustCertificateTrust+000000B7
 WINTRUST!_VerifyTrust+00000144
 WINTRUST!WinVerifyTrust+0000004E
 SigTest!signature_is_valid+000000DD
 SigTest!wmain+00000073
 SigTest!__tmainCRTStartup+000001A8
 SigTest!wmainCRTStartup+0000000F
 kernel32!BaseProcessStart+00000023

It seems to me that the CRYPT32 functions are the ones leaking... or I'm missing something.

EDIT2

Here is the memory evolution for some thousand loops: alt text

the_void
  • 5,512
  • 2
  • 28
  • 34
  • Just because the memory grows with every iteration does not _necessarily_ mean there is a memory leak. The heap may decide to dole out fresh memory instead of previously freed memory. This is common on a system with lots of free memory. When there's memory pressure in the system, the working set will be trimmed, which will probably convince the heap not to be so greedy. – Adrian McCarthy May 23 '12 at 18:30
  • BTW you have a coding error. `wd.fdwRevocationChecks = WTD_REVOCATION_CHECK_NONE;` should be `wd.fdwRevocationChecks = WTD_REVOKE_NONE;` – klaus triendl Sep 17 '21 at 10:38

4 Answers4

0

According to MSDN documenation of WINTRUST_DATA, WTD_CACHE_ONLY_URL_RETRIEVAL is not supported on Windows XP or Windows 2000. I doubt that has anything to do with the apparent leakage, but thought it might be worth pointing out.

Dave Ruske
  • 401
  • 1
  • 4
  • 11
0

YES. If you have a low enough version of the crypt32.dll file there is a very annoying memory leak.

see http://social.technet.microsoft.com/Forums/en-US/itproxpsp/thread/c11530e8-56e2-4bb3-a887-f7809e644861

and install hotfix KB2641690 for fix

Dan
  • 1,981
  • 1
  • 14
  • 18
0

I don't see any info that this API leaks. perhaps it's just heap fragmentation in the process?

You can verify this for sure using umdh to take snapshots of your process at time X and X+delta, then analyze the comparative heap usage at those times. Make sure all symbols are available for this to be most helpful.

Steve Townsend
  • 53,498
  • 9
  • 91
  • 140
0

(Edit: Looking at the new graphs, I was wrong.)

Based on "I_CryptCreateLruEntry" in the callstack, my guess is that this is not a memory leak; it is just the API caching data in a bounded way. i.e. It will not grow indefinitely.

LRU in that name suggests it is storing the certificate it obtained in a Least Recently Used cache in order to speed up subsequent operations that may involve the same certificate.

If you run the code in a loop and find it is using several megabytes and still growing after many iterations then maybe there is a leak, or a very poorly configured caching algorithm, but otherwise I'd say you probably have nothing to worry about.

Leo Davidson
  • 6,093
  • 1
  • 27
  • 29
  • I know what LRU is supposed to do, but it doesn't seem to cache anything. For the same executable that is being checked in a loop, the memory keeps increasing linearly. – the_void Nov 26 '10 at 12:56
  • Looking at the new graph, I agree. That doesn't look like caching (unless it's caching gone horribly wrong :)). Sorry for the red herring. – Leo Davidson Nov 26 '10 at 13:02