{"id":41523,"date":"2003-12-12T10:00:00","date_gmt":"2003-12-12T10:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2003\/12\/12\/why-are-structure-sizes-checked-strictly\/"},"modified":"2003-12-12T10:00:00","modified_gmt":"2003-12-12T10:00:00","slug":"why-are-structure-sizes-checked-strictly","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20031212-00\/?p=41523","title":{"rendered":"Why are structure sizes checked strictly?"},"content":{"rendered":"<p>\nYou may have noticed that Windows as a general rule\nchecks structure sizes strictly.\nFor example, consider the MENUITEMINFO structure:\n<\/p>\n<pre>\ntypedef struct tagMENUITEMINFO {\n  UINT    cbSize;\n  UINT    fMask;\n  UINT    fType;\n  UINT    fState;\n  UINT    wID;\n  HMENU   hSubMenu;\n  HBITMAP hbmpChecked;\n  HBITMAP hbmpUnchecked;\n  ULONG_PTR dwItemData;\n  LPTSTR  dwTypeData;\n  UINT    cch;\n#if(WINVER &gt;= 0x0500)\n  HBITMAP hbmpItem; \/\/ available only on Windows 2000 and higher\n#endif\n} MENUITEMINFO, *LPMENUITEMINFO;\n<\/pre>\n<p>\nNotice that the size of this structure changes\ndepending on whether WINVER &gt;= 0x0500\n(<i>i.e.<\/i>, whether you are targetting Windows 2000 or higher).\nIf you take the Windows 2000 version of this structure\nand pass it to Windows NT 4,\nthe call will fail since the sizes don&#8217;t match.\n<\/p>\n<p>\n&#8220;But the old version of the operating system\nshould accept any size that is greater than or equal to\nthe size it expects.\nA larger value means that the structure\ncame from a newer version of the program,\nand it should just ignore the parts it doesn&#8217;t understand.&#8221;\n<\/p>\n<p>\nWe tried that. It didn&#8217;t work.\n<\/p>\n<p>\nConsider the following imaginary sized structure\nand a function that consumes it.\nThis will be used as the guinea pig for the discussion to follow:\n<\/p>\n<pre>\ntypedef struct tagIMAGINARY {\n  UINT cbSize;\n  BOOL fDance;\n  BOOL fSing;\n#if IMAGINARY_VERSION &gt;= 2\n  \/\/ v2 added new features\n  IServiceProvider *psp; \/\/ where to get more info\n#endif\n} IMAGINARY;\n\/\/ perform the actions you specify\nSTDAPI DoImaginaryThing(const IMAGINARY *pimg);\n\/\/ query what things are currently happening\nSTDAPI GetImaginaryThing(IMAGINARY *pimg);\n<\/pre>\n<p>\nFirst, we found lots of programs which simply\nforgot to initialize the <code>cbSize<\/code> member altogether.<\/p>\n<pre>\nIMAGINARY img;\nimg.fDance = TRUE;\nimg.fSing = FALSE;\nDoImaginaryThing(&amp;img);\n<\/pre>\n<p>\nSo they got stack garbage as their size.\nThe stack garbage happened to be a large number,\nso it passed the\n&#8220;greater than or equal to the expected <code>cbSize<\/code>&#8221; test\nand the code worked.\nThen the next version of the header file expanded the structure,\nusing the <code>cbSize<\/code> to detect\nwhether the caller is using the old or new style.\nNow, the stack garbage is still greater than or equal to\nthe new <code>cbSize<\/code>,\nso version 2 of <code>DoImaginaryThing<\/code> says,\n&#8220;Oh cool, this is somebody who wants to provide additional information\nvia the <code>IServiceProvider<\/code> field.&#8221;\nExcept of course that it&#8217;s stack garbage,\nso calling the <code>IServiceProvider::QueryService<\/code> method crashes.\n<\/p>\n<p>\nNow consider this related scenario:\n<\/p>\n<pre>\nIMAGINARY img;\nGetImaginaryThing(&amp;img);\n<\/pre>\n<p>\nThe next version of the header file expanded the structure,\nand the stack garbage happened to be a large number,\nso it passed the\n&#8220;greater than or equal to the expected <code>cbSize<\/code>&#8221; test,\nso it returned not just the <code>fDance<\/code> and\n<code>fSing<\/code> flags, but also returned an\n<code>psp<\/code>.\nOops, but the caller was compiled with v1, so its structure\ndoesn&#8217;t have a <code>psp<\/code> member.\nThe <code>psp<\/code> gets written past the end of the structure,\ncorrupting whatever came after it in memory.\nAh, so now we have one of those dreaded <b>buffer overflow<\/b> bugs.\n<\/p>\n<p>\nEven if you were lucky and the memory that came afterwards was\nsafe to corrupt, you still have a bug:  By the rules of COM\nreference counts, when a function returns an interface pointer,\nit is the caller&#8217;s responsibility to release the pointer when\nno longer needed.\nBut the v1 caller doesn&#8217;t know about this <code>psp<\/code> member,\nso it certainly doesn&#8217;t know that it needs to be\n<code>psp-&gt;Release()<\/code>d.  So now, in addition to memory\ncorruption (as if that wasn&#8217;t bad enough), you also have a memory\nleak.\n<\/p>\n<p>\nWait, I&#8217;m not done yet.  Now let&#8217;s see what happens when a program\nwritten in the future runs on an older system.\n<\/p>\n<p>\nSuppose somebody is writing their program intending it to be run on v2.\nThey set the <code>cbSize<\/code> to the larger v2 structure size\nand set the <code>psp<\/code> member to a service provider\nthat performs security checks before allowing any\nsinging or dancing to take place.\n(<i>E.g.<\/i>, makes sure everybody paid the entrance fee.)\nNow somebody takes this program and runs it on v1.\nThe new v2 structure size passes the\n&#8220;greater than or equal to the v1 structure size&#8221; test,\nso v1 will accept the structure and Do the ImaginaryThing.\nExcept that v1 didn&#8217;t support the <code>psp<\/code> field,\nso your service provider never gets called\nand your security module is bypassed.\nNow everybody is coming into your club without paying.\n<\/p>\n<p>Now, you might say, &#8220;Well those are just buggy programs.\nThey deserve to lose.&#8221;\nIf you stand by that logic, then prepare to take the heat\nwhen you read magazine articles like\n&#8220;Microsoft intentionally designed &lt;Product X&gt;\nto be incompatible with &lt;software from a major competitor&gt;.\nWhere is the Justice Department when you need them?&#8221;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>You may have noticed that Windows as a general rule checks structure sizes strictly. For example, consider the MENUITEMINFO structure: typedef struct tagMENUITEMINFO { UINT cbSize; UINT fMask; UINT fType; UINT fState; UINT wID; HMENU hSubMenu; HBITMAP hbmpChecked; HBITMAP hbmpUnchecked; ULONG_PTR dwItemData; LPTSTR dwTypeData; UINT cch; #if(WINVER &gt;= 0x0500) HBITMAP hbmpItem; \/\/ available only on [&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":[2],"class_list":["post-41523","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-history"],"acf":[],"blog_post_summary":"<p>You may have noticed that Windows as a general rule checks structure sizes strictly. For example, consider the MENUITEMINFO structure: typedef struct tagMENUITEMINFO { UINT cbSize; UINT fMask; UINT fType; UINT fState; UINT wID; HMENU hSubMenu; HBITMAP hbmpChecked; HBITMAP hbmpUnchecked; ULONG_PTR dwItemData; LPTSTR dwTypeData; UINT cch; #if(WINVER &gt;= 0x0500) HBITMAP hbmpItem; \/\/ available only on [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/41523","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=41523"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/41523\/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=41523"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=41523"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=41523"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}