{"id":10273,"date":"2011-07-01T07:00:00","date_gmt":"2011-07-01T07:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2011\/07\/01\/the-list-of-heaps-returned-by-getprocessheaps-is-valid-when-it-returns-but-who-knows-what-happens-later\/"},"modified":"2011-07-01T07:00:00","modified_gmt":"2011-07-01T07:00:00","slug":"the-list-of-heaps-returned-by-getprocessheaps-is-valid-when-it-returns-but-who-knows-what-happens-later","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20110701-00\/?p=10273","title":{"rendered":"The list of heaps returned by GetProcessHeaps is valid when it returns, but who knows what happens later"},"content":{"rendered":"<p>\nA customer had a problem involving heap corruption.\n<\/p>\n<blockquote CLASS=\"q\">\n<p>\nIn our code, we call\n<code>Get&shy;Process&shy;Heaps<\/code>\nand then for each heap, we call <code>Heap&shy;Set&shy;Information<\/code>\nto enable the low fragmentation heap.\nHowever, the application crashes due to an invalid heap handle.\n<\/p>\n<pre>\nHANDLE heaps[1025];\nDWORD nHeaps = GetProcessHeaps(heaps, 1024);\nfor (DWORD i = 0; i &lt; nHeaps; i++) {\n ULONG HeapFragValue = HEAP_LFH;\n HeapSetInformation(heaps[i], HeapCompatibilityInformation,\n                    &amp;HeapFragValue, sizeof(HeapFragValue));\n}\n<\/pre>\n<p>\nMy question is, why do we need to allocate an array of size 1025\neven though we pass 1024 to <code>Get&shy;Process&shy;Heaps<\/code>?\n<\/p>\n<\/blockquote>\n<p>\nHa, faked you out with that question, eh?\n(It sure faked <i>me<\/i> out.)\n<\/p>\n<p>\nIt&#8217;s not clear why the code under-reports the buffer size\nto <code>Get&shy;Process&shy;Heaps<\/code>.\nSo let&#8217;s ignore the customer&#8217;s stated question and move on to\nthe more obvious question:\nWhy does this code crash due to an invalid heap handle?\n<\/p>\n<p>\nWell, for one thing,\nthe code mishandles the case where there are more than 1024\nheaps in the process.\nBut as it happens,\nthe value returned by <code>Get&shy;Process&shy;Heaps<\/code>\nwas well below 1024,\nso that wasn&#8217;t the reason for the crash.\n<\/p>\n<p>\nUnlike kernel objects, heaps are just chunks of user-mode-managed\nmemory.\nA heap handle is not reference-counted.\n(Think about it: If it were, how would you release the reference count?\nThere is no <code>Heap&shy;Close&shy;Handle<\/code> function.)\nWhen you destroy a heap, all existing handles to that heap\nbecome invalid.\n<\/p>\n<p>\nThe consequence of this is that there is a race condition inherent\nin the use of the <code>Get&shy;Process&shy;Heaps<\/code> function:\nEven though the list of heaps is correct when it is returned to you,\nanother thread might sneak in and destroy one of those heaps out\nfrom under you.\n<\/p>\n<p>\nThis didn&#8217;t explain the reported crash, however.\n&#8220;We execute this code during application startup,\nbefore we create any worker threads,\nso there should be no race condition.&#8221;\n<\/p>\n<p>\nWhile it may be true that the code is executed before the program\ncalls <code>Create&shy;Thread<\/code>,\na study of the crash dump reveals that some sneaky DLLs had paid\na visit to the process and had even unloaded themselves!\n<\/p>\n<pre>\n0:001&gt; lm\nstart    end        module name\n75b10000 75be8000   kernel32   (deferred)\n77040000 7715e000   ntdll      (deferred)\n...\nUnloaded modules:\n775e0000 775e6000   NSI.dll\n76080000 760ad000   WS2_32.dll\n71380000 713a2000   COEC23~1.DLL\n<\/pre>\n<p>\n&#8220;Well, that explains how a heap could have been destroyed from\nbehind our back.\nThat <code>COEC23~1.DLL<\/code> probably created a private heap\nand destroyed it when it was unloaded.\nBut how did that DLL get injected into our process in the first place?&#8221;\n<\/p>\n<p>\nGiven the presence of some networking DLLs,\nthe customer guessed that <code>COEC23~1.DLL<\/code>\nwas injected by network firewall security software,\nbut given that these were Windows Error Reporting crash dumps,\nthere was no way to get information from the user&#8217;s machine\nabout how that <code>COEC23~1.DLL<\/code> ended up loaded in\nthe process, and then spontaneously unloaded.\n<\/p>\n<p>\nEven though we weren&#8217;t able to find the root cause, we were still\nable to make some suggestions to avoid the crash.\n<\/p>\n<p>\nInstead of trying to convert every heap to a low fragmentation heap,\njust convert the process heap.\nThe process heap remains valid for the lifetime of the process,\nso you won&#8217;t see it destroyed out from under you.\n(Or if you do, then you have bigger problems than a crash in\n<code>Heap&shy;Set&shy;Information<\/code>.)\n<\/p>\n<p>\nIn fact, you can remove the code entirely when running on Windows&nbsp;Vista\nor higher,\nbecause all heaps default to the low fragmentation heap\nstarting in Windows&nbsp;Vista.\n<\/p>\n<p>\n<a HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2010\/04\/30\/10004931.aspx\">\nRunning around and changing settings on heaps you didn&#8217;t create\nis not a good idea<\/a>.\nSomebody else owns that heap; who knows what they&#8217;re going to do with it?\n<\/p>\n<p>\nOkay, so if <code>Get&shy;Process&shy;Heaps<\/code> is so fragile, why does\nit even exist?\n<\/p>\n<p>\nWell, it&#8217;s not really intended for general use.\nIt exists primarily for diagnostics.\nYou might be chasing down a memory corruption bug, so you sprinkle\ninto your code some calls to a helper function that calls\n<code>Get&shy;Process&shy;Heaps<\/code>\nto get all the heaps and then calls\n<code>Heap&shy;Validate<\/code> on each one\nto check for corruption.\nOr maybe you&#8217;re chasing down a memory leak in a particular scenario,\nso you have a function which calls\n<code>Get&shy;Process&shy;Heaps<\/code> and\n<code>Heap&shy;Walk<\/code> once before\nentering the scenario, and then again after the scenario completes,\nand then compares the results looking for leaks.\nIn both cases, you&#8217;re using the facility for debugging and diagnostic\npurposes.\nIf there&#8217;s a race condition that destroys a heap while you&#8217;re studying\nit, you&#8217;ll just throw away the results of that run and try again.\n<\/p>\n<p>\n<b>Bonus chatter<\/b>:\nWhile writing up this story,\nI went back and did some more Web searching for that mysterious\n<code>COEC23~1.DLL<\/code>.\n(Tracking it down is hard because all you really know about the file\nname is that it begins with &#8220;CO&#8221;; the rest is a hashed short file name.)\nAnd I found it:\nIt&#8217;s not an antivirus program.\nIt&#8217;s one of those &#8220;desktop enhancement&#8221; programs that injects itself\ninto every process with the assistance of\n<code>App&shy;Init_DLLs<\/code>, or as I prefer to call it\n<a HREF=\"http:\/\/blogs.msdn.com\/oldnewthing\/archive\/2007\/12\/13\/6648400.aspx\">\n<code>Deadlock_Or_Crash_Randomly_DLLs<\/code><\/a>.\n(You may have noticed that I anonymized the program as &#8220;CO&#8221;,\nshort for <i>Contoso<\/i>, a fictional company used throughout\nMicrosoft literature.)<\/p>\n","protected":false},"excerpt":{"rendered":"<p>A customer had a problem involving heap corruption. In our code, we call Get&shy;Process&shy;Heaps and then for each heap, we call Heap&shy;Set&shy;Information to enable the low fragmentation heap. However, the application crashes due to an invalid heap handle. HANDLE heaps[1025]; DWORD nHeaps = GetProcessHeaps(heaps, 1024); for (DWORD i = 0; i &lt; nHeaps; i++) { [&hellip;]<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-10273","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>A customer had a problem involving heap corruption. In our code, we call Get&shy;Process&shy;Heaps and then for each heap, we call Heap&shy;Set&shy;Information to enable the low fragmentation heap. However, the application crashes due to an invalid heap handle. HANDLE heaps[1025]; DWORD nHeaps = GetProcessHeaps(heaps, 1024); for (DWORD i = 0; i &lt; nHeaps; i++) { [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/10273","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=10273"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/10273\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=10273"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=10273"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=10273"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}