{"id":2663,"date":"2013-11-14T07:00:00","date_gmt":"2013-11-14T07:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2013\/11\/14\/why-is-my-formatmessage-call-crashing-trying-to-read-my-insertion-parameter\/"},"modified":"2013-11-14T07:00:00","modified_gmt":"2013-11-14T07:00:00","slug":"why-is-my-formatmessage-call-crashing-trying-to-read-my-insertion-parameter","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20131114-00\/?p=2663","title":{"rendered":"Why is my FormatMessage call crashing trying to read my insertion parameter?"},"content":{"rendered":"<p>A customer was looking for assistance in debugging a crash\nin their product.\nThe stack trace looked like this:\n<\/p>\n<pre>\nntdll!_woutput_l+0x356\nntdll!_vsnwprintf_l+0x81\nntdll!_vsnwprintf+0x11\nntdll!RtlStringVPrintfWorkerW+0x3c\nntdll!RtlStringCchPrintfExW+0xa8\nntdll!RtlFormatMessageEx+0x324\nKERNELBASE!BaseDllFormatMessage+0x18e\nKERNELBASE!FormatMessageW+0x37\ncontoso!FormatWithFallbackLanguage+0x8a\ncontoso!RecordFailure+0x56\n<\/pre>\n<p>\nThe string being formatted is\n<code>There was an error processing the object '%1'.<\/code>,\nand the insertion is a long (but valid) string.\nA unit test which passes a similarly long object name to\n<code>Record&shy;Failure<\/code> does not crash.\n<\/p>\n<p>\nWhat is the problem?\nThere are clues in the stack trace.\n<\/p>\n<p>\nThe natural place to start is the function that calls\n<code>Format&shy;Message<\/code> to see what parameters are\nbeing passed in.\nAnd that&#8217;s where you see something strange:\n<\/p>\n<pre>\n<i>\/\/ Code in italics is wrong\nBOOL FormatWithFallbackLanguage(\n    DWORD dwMessageId, PCTSTR pszBuffer, SIZE_T cchBuffer, ...)\n{\n va_list ap;\n va_start(ap, cchBuffer);\n \/\/ Format from the user's preferred language.\n DWORD cchResult = FormatMessage(\n               FORMAT_MESSAGE_FROM_HMODULE,\n               g_hinst, dwMessageId, g_preferredLangId,\n               pszBuffer, cchBuffer, &amp;ap);\n \/\/ If that didn't work, then use the fallback language.\n if (cchResult == 0) {\n  cchResult = FormatMessage(\n               FORMAT_MESSAGE_FROM_HMODULE,\n               g_hinst, dwMessageId, g_fallbackLangId,\n               pszBuffer, cchBuffer, &amp;ap);\n }\n va_end(ap);\n return cchResult != 0;\n}<\/i>\n<\/pre>\n<p>\n(The clue in the stack trace was the word\n<i>fallback<\/i> in the function name,\nwhich suggests that if the formatting attempt fails,\nit&#8217;ll try again some other way.)\n<\/p>\n<p>\nThe purpose of this function is to format the message\nusing the specified message ID,\nfirst looking in the preferred language message table,\nand if that fails, by looking in the fallback language\nmessage table.\n<\/p>\n<p>\nThe crash occurred on the second call to\n<code>Format&shy;Message<\/code>.\nThe customer said,\n&#8220;I&#8217;m guessing that the parameters being passed to\n<code>Format&shy;Message<\/code>\nmay be causing this,\nbut I&#8217;m not sure how to proceed.\nCan you provide pointers for further investigation?&#8221;\n<\/p>\n<p>\nThe bug is that code is reusing the\n<code>ap<\/code> parameter without resetting it.\nThe documentation for\n<code>Format&shy;Message<\/code> says\nabout the <code>Arguments<\/code> parameter:\n<\/p>\n<blockquote CLASS=\"q\"><p>\nThe state of the <b>va_list<\/b> argument is undefined\nupon return from the function.\nTo use the <b>va_list<\/b> again,\ndestroy the variable argument list pointer using <b>va_end<\/b>\nand reinitialize it with <b>va_start<\/b>.\n<\/p><\/blockquote>\n<p>\nThe function\n<code>Format&shy;With&shy;Fallback&shy;Language<\/code>\ncalls <code>Format&shy;Message<\/code>, which consumes\nand renders unusable the <code>ap<\/code> variable.\nIf the first format attempt fails,\nthe code passes the same (now invalid)\n<code>va_list<\/code> to a second\n<code>Format&shy;Message<\/code>,\nwhich is now operating on undefined data and is\ntherefore within its rights to crash.\n<\/p>\n<p>\nIn practice, what happens is that the\n<code>Format&shy;Message<\/code> function calls\n<code>va_arg<\/code> on the\n<code>va_list<\/code> to extract the insertions,\nand since <code>va_list<\/code>s are single-use,\nthat pretty much renders it useless for anything else.\nIf you want to walk the parameters a second time,\nyou need to clean up the <code>va_list<\/code>\nand then reinitialize it.\n<\/p>\n<pre>\nBOOL FormatWithFallbackLanguage(\n    DWORD dwMessageId, PCTSTR pszBuffer, SIZE_T cchBuffer, ...)\n{\n va_list ap;\n va_start(ap, cchBuffer);\n \/\/ Format from the user's preferred language.\n DWORD cchResult = FormatMessage(\n               FORMAT_MESSAGE_FROM_HMODULE,\n               g_hinst, dwMessageId, g_preferredLangId,\n               pszBuffer, cchBuffer, &amp;ap);\n \/\/ If that didn't work, then use the fallback language.\n if (cchResult == 0) {\n  <font COLOR=\"blue\">va_end(ap);\n  va_start(ap, cchBuffer);<\/font>\n  cchResult = FormatMessage(\n               FORMAT_MESSAGE_FROM_HMODULE,\n               g_hinst, dwMessageId, g_fallbackLangId,\n               pszBuffer, cchBuffer, &amp;ap);\n }\n va_end(ap);\n return cchResult != 0;\n}\n<\/pre>\n<p>\nBy ending the old argument list and restarting it,\nthe second call to\n<code>Format&shy;Message<\/code> has the correct starting point\nfor extracting the variadic parameters.\nAn alternate (and in my opinion better) way to fix the bug\nwould be\n<\/p>\n<pre>\nBOOL FormatWithFallbackLanguage(\n    DWORD dwMessageId, PCTSTR pszBuffer, SIZE_T cchBuffer, ...)\n{\n va_list ap;\n \/\/ Format from the user's preferred language.\n <font COLOR=\"blue\">va_start(ap, cchBuffer);<\/font>\n DWORD cchResult = FormatMessage(\n               FORMAT_MESSAGE_FROM_HMODULE,\n               g_hinst, dwMessageId, g_preferredLangId,\n               pszBuffer, cchBuffer, &amp;ap);\n <font COLOR=\"blue\">va_end(ap);<\/font>\n \/\/ If that didn't work, then use the fallback language.\n if (cchResult == 0) {\n  <font COLOR=\"blue\">va_start(ap, cchBuffer);<\/font>\n  cchResult = FormatMessage(\n               FORMAT_MESSAGE_FROM_HMODULE,\n               g_hinst, dwMessageId, g_fallbackLangId,\n               pszBuffer, cchBuffer, &amp;ap);\n  <font COLOR=\"blue\">va_end(ap);<\/font>\n }\n return cchResult != 0;\n}\n<\/pre>\n<p>\nThis avoids the &#8220;magic switcheroo&#8221; and more clearly\nscopes the region of validity of the <code>ap<\/code>\nvariable to &#8220;solely for the purpose of the\n<code>Format&shy;Message<\/code> function.&#8221;\n<\/p>\n<p>\n<b>Bonus chatter<\/b>:\nSuppose the <code>Format&shy;With&shy;Fallback&shy;Language<\/code>\naccepted a <code>va_list<\/code> parameter directly.\nYou might be tempted to implement it like this:\n<\/p>\n<pre>\n<i>\/\/ code in italics is wrong\nBOOL FormatWithFallbackLanguage(\n    DWORD dwMessageId, PCTSTR pszBuffer, SIZE_T cchBuffer, va_list ap)\n{\n va_list apOriginal = ap;\n \/\/ Format from the user's preferred language.\n DWORD cchResult = FormatMessage(\n               FORMAT_MESSAGE_FROM_HMODULE,\n               g_hinst, dwMessageId, g_preferredLangId,\n               pszBuffer, cchBuffer, &amp;ap);\n \/\/ If that didn't work, then use the fallback language.\n if (cchResult == 0) {\n  cchResult = FormatMessage(\n               FORMAT_MESSAGE_FROM_HMODULE,\n               g_hinst, dwMessageId, g_fallbackLangId,\n               pszBuffer, cchBuffer, &amp;apOriginal);\n }\n return cchResult != 0;\n}<\/i>\n<\/pre>\n<p>\nThis is not legal because a <code>va_list<\/code> is not\ndirectly copyable.\nSome architectures have rather complicated calling conventions\nthat entail memory allocation in order to enumerate the parameters\npassed to variadic functions,\nand a bitwise copy will not respect those complexities.\nYou have to use the <code>va_copy<\/code> macro to make a copy\nof a <code>va_list<\/code>.\n<\/p>\n<p>\n<b>Exercise<\/b>:\nHow did this error elude unit testing?\n<\/p>\n<p>\n<b>Exercise<\/b>:\nWhat else can go wrong in this function?<\/p>\n","protected":false},"excerpt":{"rendered":"<p>A customer was looking for assistance in debugging a crash in their product. The stack trace looked like this: ntdll!_woutput_l+0x356 ntdll!_vsnwprintf_l+0x81 ntdll!_vsnwprintf+0x11 ntdll!RtlStringVPrintfWorkerW+0x3c ntdll!RtlStringCchPrintfExW+0xa8 ntdll!RtlFormatMessageEx+0x324 KERNELBASE!BaseDllFormatMessage+0x18e KERNELBASE!FormatMessageW+0x37 contoso!FormatWithFallbackLanguage+0x8a contoso!RecordFailure+0x56 The string being formatted is There was an error processing the object &#8216;%1&#8217;., and the insertion is a long (but valid) string. A unit test which [&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-2663","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>A customer was looking for assistance in debugging a crash in their product. The stack trace looked like this: ntdll!_woutput_l+0x356 ntdll!_vsnwprintf_l+0x81 ntdll!_vsnwprintf+0x11 ntdll!RtlStringVPrintfWorkerW+0x3c ntdll!RtlStringCchPrintfExW+0xa8 ntdll!RtlFormatMessageEx+0x324 KERNELBASE!BaseDllFormatMessage+0x18e KERNELBASE!FormatMessageW+0x37 contoso!FormatWithFallbackLanguage+0x8a contoso!RecordFailure+0x56 The string being formatted is There was an error processing the object &#8216;%1&#8217;., and the insertion is a long (but valid) string. A unit test which [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/2663","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=2663"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/2663\/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=2663"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=2663"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=2663"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}