{"id":102316,"date":"2019-03-15T07:00:00","date_gmt":"2019-03-15T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=102316"},"modified":"2019-06-06T17:34:46","modified_gmt":"2019-06-07T00:34:46","slug":"20190315-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20190315-00\/?p=102316","title":{"rendered":"How can we use IsBadWritePtr to fix a buffer overflow, if IsBadWritePtr is itself bad?"},"content":{"rendered":"<p>A customer asked for assistance in investigating an access violation caused by a buffer overflow. They figured that they could probe whether the buffer is large enough to receive the data by using <code>Is&shy;Bad&shy;Write&shy;Ptr<\/code>, but then they saw that <a HREF=\"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/20060927-07\/?p=29563\"><code>Is&shy;Bad&shy;Xxx&shy;Ptr<\/code> should really be called <code>Crash&shy;Program&shy;Randomly<\/code><\/a>. They were wondering what alternatives existed to <code>Is&shy;Bad&shy;Xxx&shy;Ptr<\/code>. <\/p>\n<p>The alternative to <code>Is&shy;Bad&shy;Xxx&shy;Ptr<\/code> is <i>not passing bad pointers in the first place<\/i>. <\/p>\n<p>If you are getting an access violation from a buffer overflow, the fix for the problem is not to try to detect the overflow as it happens. the fix is to stop the overflow <i>before<\/i> it happens. <\/p>\n<p>The customer shared their code and the stack trace at which the access violation occurred: <\/p>\n<pre>\nmsvcrt!memcpy+0xb4\ncontoso!CBuffer::CopyFromRange+0x92\ncontoso!CBuffer::ReadAt+0x861\ncontoso!CLockBytes::ReadAt+0xfd\ncontoso!CStream::Read+0xe3\ncontoso!CData::ParseFile+0x606\n<\/pre>\n<p>The buffer overflow occurred because the <code>memcpy<\/code> was writing past the end of the buffer passed to <code>CStream::Read<\/code>. The thing to do is not try to detect the maximum writable buffer size, but to stop passing invalid buffer sizes. <\/p>\n<p>Because there&#8217;s probably writable memory after the buffer that is not part of the buffer. If the invalid buffer size were only slightly larger than the buffer (rather than ridiculously larger than the buffer), you wouldn&#8217;t have gotten an access violation, but you still had a buffer overflow. <\/p>\n<p>The offending <code>Read<\/code> call came from here: <\/p>\n<pre>\n<i>\/\/ Code in italics is wrong\n    uint32_t numBlocks;\n    uint32_t actualBytesRead;\n\n    \/\/ First, read the number of blocks.\n    HRESULT hr = stream.Read(&amp;numBlocks, sizeof(uint32_t), &amp;actualBytesRead);\n    if (FAILED(hr) || actualBytesRead != sizeof(uint32_t)) {\n        goto Reject;\n    }\n\n    \/\/ Next, read the size of each block.\n    uint32_t blockSize;\n    hr = stream.Read(&amp;blockSize, sizeof(uint32_t), &amp;actualBytesRead);\n    if (FAILED(hr) || actualBytesRead != sizeof(uint32_t)) {\n        goto Reject;\n    }\n\n    \/\/ Now read the blocks.\n    DWORD i;\n    for (i = 0; i &lt; numBlocks; i++)\n    {\n        \/\/ Read each block.\n        BLOCK block = { 0 };\n        hr = stream.Read(&amp;block, blockSize, &amp;actualBytesRead);<\/i>\n        \/\/               ^^^^^^^^^^^^^^^^^ invalid buffer here\n        if (FAILED(hr) || actualBytesRead != sizeof(uint32_t)) {\n            goto Reject;\n        }\n<\/pre>\n<p>The stack trace implicates the highlighted line of code. <\/p>\n<p>So how do we prevent the invalid buffer from being passed to the <code>Read<\/code> method? <\/p>\n<p>From code inspection, we see that we read <code>blockSize<\/code> bytes into a <code>BLOCK<\/code> structure, but we didn&#8217;t take any steps to ensure that <code>blockSize<\/code> is no larger than at <code>BLOCK<\/code>. In other words, we have a buffer of size <code>sizeof(BLOCK)<\/code>, and we ask to read <code>blockSize<\/code> bytes into it, so it is our responsibility to ensure that <code>blockSize &lt;= sizeof(BLOCK)<\/code>. <\/p>\n<p>However, no such buffer size validation was present. <\/p>\n<p>How to fix this depends on how you want to deal with unexpected block sizes. <\/p>\n<p>If your intent is to allow large block sizes and just ignore the fields that are &#8220;from the future&#8221;, then you would read <code>min(blockSize, sizeof(block))<\/code> bytes, and then <code>Seek<\/code> over the extra bytes (if any). <\/p>\n<p>If your intent is to reject large block sizes, then go ahead and reject them. <\/p>\n","protected":false},"excerpt":{"rendered":"<p>Don&#8217;t catch the overflow as it happens. Stop the overflow before it happens.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-102316","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Don&#8217;t catch the overflow as it happens. Stop the overflow before it happens.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/102316","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=102316"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/102316\/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=102316"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=102316"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=102316"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}