{"id":9053,"date":"2011-11-23T07:00:00","date_gmt":"2011-11-23T07:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2011\/11\/23\/it-is-not-unreasonable-to-expect-uninitialized-garbage-to-change-at-any-time-you-dont-need-to-ask-for-an-explanation\/"},"modified":"2011-11-23T07:00:00","modified_gmt":"2011-11-23T07:00:00","slug":"it-is-not-unreasonable-to-expect-uninitialized-garbage-to-change-at-any-time-you-dont-need-to-ask-for-an-explanation","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20111123-00\/?p=9053","title":{"rendered":"It is not unreasonable to expect uninitialized garbage to change at any time, you don&#039;t need to ask for an explanation"},"content":{"rendered":"<p>\nA customer admitted that they had a bug in their code:\n<\/p>\n<pre>\n<i><a HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2004\/02\/12\/71851.aspx\">#define UNICODE\n#define _UNICODE<\/a>\n#include &lt;windows.h&gt;\n\/\/ error checking removed for expository purposes\n\/\/ code that writes out the data\nRegSetValueEx(hkey, pszValue, 0, REG_SZ, (const BYTE *)pszData,\n              _tcslen(pszData) * sizeof(TCHAR) + 1);\n\/\/ code that reads the data\nDWORD dwType, cbData;\nRegQueryValueEx(hkey, pszValue, NULL, &amp;dwType, NULL, &amp;cbData);\nTCHAR *pszData = new TCHAR[cbData \/ sizeof(TCHAR)];\nRegQueryValueEx(hkey, pszValue, NULL, &amp;dwType, pszData, &amp;cbData);\n<\/i><\/pre>\n<p>\nOne bug in the above code is in the final parameter passed to\n<code>Reg&shy;Set&shy;Value&shy;Ex<\/code>:\nIt&#8217;s supposed to be the count in bytes,\nbut the calculation appends only one byte for the terminating null\ninstead of a full <code>TCHAR<\/code>.\nIn other words, it should be<\/p>\n<pre>\nRegSetValueEx(hkey, pszValue, 0, REG_SZ, (const BYTE *)pszData,\n              _tcslen(pszData) * sizeof(TCHAR) + <font COLOR=\"blue\">sizeof(TCHAR)<\/font>);\n<\/pre>\n<p>\nFor concreteness, let&#8217;s say the original string was five <code>TCHAR<\/code>s\nin length, not counting the terminating null.\nTherefore, the correct buffer size is 12 bytes, but they passed only 11\nto <code>Reg&shy;Set&shy;Value&shy;Ex<\/code>.\n<\/p>\n<p>\nThis error is compounded in the code that reads the value back:\nThe code happily divides <code>cbData \/ sizeof(TCHAR)<\/code>\nwithout checking that the division is even.\nIn our example, the call returns a length of 11 bytes.\nThey divide by <code>sizeof(TCHAR)<\/code> (which is 2, since the\ncode is compiled as Unicode), leaving 5 (remainder discarded),\ncausing them to allocate a 5-<code>TCHAR<\/code> buffer.\n<\/p>\n<p>\nThat error would have been okay by itself except for another error,\nwhich is calling <code>Reg&shy;Query&shy;Value&shy;Ex<\/code>\na second time with\nan invalid buffer size:\nThe <code>cbData<\/code> variable remains the original value of 11\neven though they allocated only 10 bytes.\nThe subsequent <code>Reg&shy;Query&shy;Value&shy;Ex<\/code> call reads 11 bytes\ninto a 10-byte buffer.\n<\/p>\n<p>\nThe customer conceded that the code that writes the value is buggy,\nbut points out that the code &#8220;worked&#8221; on Windows&nbsp;XP,\nin the sense that the string read back from the registry was\ncorrect.\nBut Windows Vista &#8220;broke&#8221; their program,\nbecause the string read back now contained garbage at the end.\nInstead of returning <code>\"Hello\"<\/code>,\nit returned\n<code>\"Hello&#x400;&#x2545;&#x6DE;\"<\/code>.\nThe customer wanted to know what change to Windows Vista broke\ntheir program.\n<\/p>\n<p>\nThe change to Windows Vista that broke their program\nis known as &#8220;luck running out.&#8221;\nThe program contained three bugs, which combined to form a heap\nbuffer write overflow.\nThe uninitialized garbage at the end of the heap block they\nallocated happened to be zero on Windows&nbsp;XP\ndue to a coincidence in the way their program allocated and freed\nmemory.\nConsequently, when the data was read from the registry, the\n&#8220;string&#8221; ended in a single null byte instead of two.\nThe extra null byte that &#8220;happened to be there already&#8221;\ncombined with the single null byte read from the registry to form\na proper null terminator.\n<\/p>\n<p>\nWhen run on Windows Vista, that happy coincidence no longer\ntook place, and the uninitialized garbage was nonzero,\nresulting in the subsequent attempt to use the string to read\npast the end of the buffer and pick up heap garbage.\n(Yay, bug number four: read overflow.)\nWhy was the uninitialized garbage different?\n<\/p>\n<p>\nIt&#8217;s different because there was nothing forcing it to be the same.\nThe internals of the heap manager change all the time.\n(Look-aside lists, low fragmentation heap, and fault-tolerant heap\nare just a few recent examples.)\nAny of these changes will result in heap memory being used and reused\ndifferently.\nPlus, changes in other parts of Windows may have allocated and freed\nmemory differently between Windows&nbsp;XP and Windows Vista.\nHeck, the program itself may have allocated and freed memory differently\ndue to the change in operating system.\n(For one thing, the length of the string <code>\"Windows Vista\"<\/code>\nis different from the length of the string <code>\"Windows XP\"<\/code>.)\n<\/p>\n<p>\nUninitialized garbage will contain unpredictable values.\nThere&#8217;s no point asking why you got a different unpredictable value\nthis time.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>A customer admitted that they had a bug in their code: #define UNICODE #define _UNICODE #include &lt;windows.h&gt; \/\/ error checking removed for expository purposes \/\/ code that writes out the data RegSetValueEx(hkey, pszValue, 0, REG_SZ, (const BYTE *)pszData, _tcslen(pszData) * sizeof(TCHAR) + 1); \/\/ code that reads the data DWORD dwType, cbData; RegQueryValueEx(hkey, pszValue, NULL, [&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-9053","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>A customer admitted that they had a bug in their code: #define UNICODE #define _UNICODE #include &lt;windows.h&gt; \/\/ error checking removed for expository purposes \/\/ code that writes out the data RegSetValueEx(hkey, pszValue, 0, REG_SZ, (const BYTE *)pszData, _tcslen(pszData) * sizeof(TCHAR) + 1); \/\/ code that reads the data DWORD dwType, cbData; RegQueryValueEx(hkey, pszValue, NULL, [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/9053","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=9053"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/9053\/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=9053"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=9053"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=9053"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}