{"id":111236,"date":"2025-06-02T07:00:00","date_gmt":"2025-06-02T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=111236"},"modified":"2025-07-11T21:45:19","modified_gmt":"2025-07-12T04:45:19","slug":"20250602-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20250602-00\/?p=111236","title":{"rendered":"Stating the obvious about debugging an invalid parameter error when freeing memory"},"content":{"rendered":"<p>For historical reasons, Windows has many functions for allocating and freeing memory. This is particularly common when the memory is allocated by one component but freed by another. In that case, the allocating component typically provides a function to the consumer for freeing the memory, and the implementation is to forward the call to whatever memory freeing function matches the function that the component had used to allocate it. For example, if the producer used <code>HeapAlloc<\/code> to allocate from a custom heap, then the custom memory-freeing function will use <code>HeapFree<\/code> with that same custom heap.<\/p>\n<p>What does it mean when you get an invalid parameter (or a crash) when you try to free the memory using the custom free function?<\/p>\n<p>One possible reason is that you are not freeing the same pointer that was returned by the allocator. This commonly happens when the allocator produced an array of elements, and you walked through the array by incrementing the pointer.<\/p>\n<pre>MPR_TRANSPORT_0* result;\r\nDWORD actual;\r\nDWORD total;\r\nif (MprConfigTransportEnum(mprConfig, 0, (BYTE*)&amp;result,\r\n        (DWORD)-1, &amp;actual, &amp;total, nullptr) != NO_ERROR)\r\n{\r\n    \u27e6 ... error handling ... \u27e7\r\n}\r\n\/\/ Process each of the transports\r\nfor (DWORD index = 0; index &lt; actual; ++index, <span style=\"border: solid 1px currentcolor;\">++result<\/span>) {\r\n    switch (result-&gt;dwTransportId) {\r\n    \u27e6 ... \u27e7\r\n    }\r\n}\r\n\r\n\/\/ Free the buffer\r\nMprConfigBufferFree(result); \/\/ oops\r\n<\/pre>\n<p>The problem here is that we received a pointer to the results in <code>result<\/code>, but then as we processed each result, we also modified the <code>result<\/code> variable. This means that the value passed to <code>Mpr\u00adConfig\u00adBuffer\u00adFree<\/code> is not the same value that was originally received, and it is therefore an invalid parameter.<\/p>\n<p>To diagnose this type of bug, set a breakpoint at the return from <code>Mpr\u00adConfig\u00adTransport\u00adEnum<\/code> and inspect the returned pointer. Write it down somewhere safe, and then set another breakpoint at the <code>Mpr\u00adConfig\u00adBuffer\u00adFree<\/code>. Check the value you are passing to <code>Mpr\u00adConfig\u00adBuffer\u00adFree<\/code> and confirm that it matches the value you wrote down. If not, then you are freeing the wrong pointer.<\/p>\n<p>One solution is simply never to modify the received pointer at all.<\/p>\n<pre>\/\/ Process each of the transports\r\nfor (DWORD index = 0; index &lt; actual; ++index) {\r\n    <span style=\"border: solid 1px currentcolor;\">auto transport = result + index;<\/span>\r\n    switch (<span style=\"border: solid 1px currentcolor;\">transport<\/span>-&gt;dwTransportId) {\r\n    \u27e6 ... \u27e7\r\n    }\r\n}\r\n\r\n\/\/ Free the buffer\r\nMprConfigBufferFree(result);\r\n<\/pre>\n<p>Or you can use a ranged for loop.<\/p>\n<pre>\/\/ Process each of the transports\r\nfor (auto&amp;&amp; transport : <span style=\"border: solid 1px currentcolor;\">wil::make_range(result, actual)<\/span>) {\r\n    switch (<span style=\"border: solid 1px currentcolor;\">transport<\/span>.dwTransportId) {\r\n    \u27e6 ... \u27e7\r\n    }\r\n}\r\n\r\n\/\/ Free the buffer\r\nMprConfigBufferFree(result);\r\n<\/pre>\n<p>If you really like to use pointers, you just have to make a copy and modify the copy.<\/p>\n<pre><span style=\"border: solid 1px currentcolor;\">MPR_TRANSPORT_0* transport = result;<\/span>\r\n\/\/ Process each of the transports\r\nfor (DWORD index = 0; index &lt; actual; ++index, <span style=\"border: solid 1px currentcolor;\">++transport<\/span>) {\r\n    switch (<span style=\"border: solid 1px currentcolor;\">transport<\/span>-&gt;dwTransportId) {\r\n    \u27e6 ... \u27e7\r\n    }\r\n}\r\n\r\n\/\/ Free the buffer\r\nMprConfigBufferFree(result);\r\n<\/pre>\n<p>If you decide to go this way, you may as well tweak the declarations to avoid a somewhat disturbing cast when calling <code>Mpr\u00adConfig\u00adTransport\u00adEnum<\/code>.<\/p>\n<pre><span style=\"border: solid 1px currentcolor;\">BYTE* result;<\/span>\r\nDWORD actual;\r\nDWORD total;\r\nif (MprConfigTransportEnum(mprConfig, 0, <span style=\"border: solid 1px currentcolor;\">&amp;result<\/span>,\r\n        (DWORD)-1, &amp;actual, &amp;total, nullptr) != NO_ERROR)\r\n{\r\n    \u27e6 ... error handling ... \u27e7\r\n}\r\n\r\n<span style=\"border: solid 1px currentcolor;\">auto transport = (MPR_TRANSPORT_0*)result;<\/span>\r\n\/\/ Process each of the transports\r\nfor (DWORD index = 0; index &lt; actual; ++index, ++transport) {\r\n    switch (transport-&gt;dwTransportId) {\r\n    \u27e6 ... \u27e7\r\n    }\r\n}\r\n\r\n\/\/ Free the buffer\r\nMprConfigBufferFree(result);\r\n<\/pre>\n<p>You might decide to try to protect yourself from accidentally messing up the saved pointer by making a <code>const<\/code> copy.<\/p>\n<pre>MPR_TRANSPORT_0* result;\r\nDWORD actual;\r\nDWORD total;\r\nif (MprConfigTransportEnum(mprConfig, 0, (BYTE*)&amp;result,\r\n        (DWORD)-1, &amp;actual, &amp;total, nullptr) != NO_ERROR)\r\n{\r\n    \u27e6 ... error handling ... \u27e7\r\n}\r\n\r\n<span style=\"border: solid 1px currentcolor;\">const auto resultToFree = result;<\/span>\r\n\r\n\/\/ Process each of the transports\r\nfor (DWORD index = 0; index &lt; actual; ++index, ++result) {\r\n    switch (result-&gt;dwTransportId) {\r\n    \u27e6 ... \u27e7\r\n    }\r\n}\r\n\r\n\/\/ Free the buffer\r\nMprConfigBufferFree(<span style=\"border: solid 1px currentcolor;\">resultToFree<\/span>);\r\n<\/pre>\n<p>Another case where the &#8220;free&#8221; operation fails or crashes is if you somehow managed to corrupt memory, say by indexing off the end of the returned array, or indexing off the end of some other array that results in corruption of the heap or the variable that holds the pointer to free.<\/p>\n<p>This is harder to diagnose because C++ memory corruption is hard to diagnose in general. If you have access to tools like valgrind, BoundsChecker, or Application Verifier, you can use those to help chase down memory corruption bugs.<\/p>\n<p>The point of today&#8217;s article was to just state the obvious about where this problem comes from and provide some ideas for diagnosing them.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Writing it down for posterity.<\/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-111236","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Writing it down for posterity.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/111236","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=111236"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/111236\/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=111236"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=111236"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=111236"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}