{"id":7263,"date":"2012-06-28T07:00:00","date_gmt":"2012-06-28T07:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2012\/06\/28\/you-still-need-the-safe-functions-even-if-you-check-string-lengths-ahead-of-time\/"},"modified":"2012-06-28T07:00:00","modified_gmt":"2012-06-28T07:00:00","slug":"you-still-need-the-safe-functions-even-if-you-check-string-lengths-ahead-of-time","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20120628-00\/?p=7263","title":{"rendered":"You still need the &#034;safe&#034; functions even if you check string lengths ahead of time"},"content":{"rendered":"<p>\nCommenter\nPOKE53280,0\nclaims,\n&#8220;If one validates parameters before using string functions\n(which quality programmers should do),\n<a HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2010\/12\/08\/10101773.aspx#10102139\">\nthe &#8216;safe&#8217; functions have no reason to exist<\/a>.&#8221;\n<\/p>\n<p>\nConsider the following function:\n<\/p>\n<pre>\nint SomeFunction(const char *s)\n{\n  char buffer[256];\n  if (strlen(s) &ge; 256) return ERR;\n  strcpy(buffer, s);\n  ...\n}\n<\/pre>\n<p>\nWhat could possibly go wrong?\nYou check the length of the string, and if it doesn&#8217;t fit\nin the buffer, then you reject it.\nTherefore, you claim, the <code>strcpy<\/code> is safe.\n<\/p>\n<p>\nWhat could possibly go wrong is that the length of the string\ncan change between the time you check it and the time you use it.\n<\/p>\n<pre>\nchar attack[512] = \"special string designed to trigger a \"\n                   \"buffer overflow and attack your machine. [...]\";\nvoid Thread1()\n{\n char c = attack[256];\n while (true) {\n  attack[256] ^= c;\n }\n}\nvoid Thread2()\n{\n while (true) {\n  SomeFunction(attack);\n }\n}\n<\/pre>\n<p>\nThe first thread changes the length of the string rapidly between\n255 and 511, between a string that passes validation and a string that\ndoesn&#8217;t, and more specifically between a string that passes validation\nand a string that, if it snuck through validation, would pwn the machine.\n<\/p>\n<p>\nThe second thread keeps handing this string to\n<code>Some&shy;Function<\/code>.\nEventually, the following race condition will be hit:\n<\/p>\n<ul>\n<li>Thread&nbsp;1 changes the length to 255.\n<li>Thread&nbsp;2 checks the length and when it reaches attack[256],\n    it reads zero and concludes that the string length is\n    less than 256.<\/p>\n<li>Thread&nbsp;1 changes the length to 511.\n<li>Thread&nbsp;2 copies the string and when it reaches attack[256],\n    it reads nonzero and keeps copying, thereby overflowing its buffer.\n<\/ul>\n<p>\nOops, you just fell victim to the\nTime-of-check-to-time-of-use attack\n(commonly abbreviated TOCTTOU).\n<\/p>\n<p>\nNow, the code above as-written is not technically a vulnerability\nbecause\n<a HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2010\/02\/16\/10101773.aspx\">\nyou haven&#8217;t crossed a security boundary<\/a>.\nThe attack code and the vulnerable code are running under the same\nsecurity context.\nTo make this a true vulnerability, you need to have the attack code\nrunning in a lower security context from the vulnerable code.\nFor example, if the threads were running user-mode code and\n<code>Some&shy;Function<\/code> is a kernel-mode function,\nthen you have a real vulnerability.\nOf course, if <code>Some&shy;Function<\/code> were at the boundary\nbetween user-mode and kernel-mode, then it has other things\nit needs to do, like verify that the memory is in fact readable\nby the process.\n<\/p>\n<p>\nA more interesting case where you cross a security boundary\nis if the two threads are running code driven from an untrusted\nsource; for example, they might be threads in a script interpreter,\nand the toggling of <code>attack[256]<\/code> is being done by\na function on a Web page.\n<\/p>\n<pre>\n\/\/ this code is in some imaginary scripting language\nvar attack = new string(\"...\");\nprocedure Thread1()\n{\n var c = attack[256];\n while (true) attack[256] ^= c;\n}\nhandler OnClick()\n{\n new backgroundTask(Thread1);\n while (true) foo(attack);\n}\n<\/pre>\n<p>\nWhen the user clicks on the button, the script interpret\ncreates a background thread and starts toggling the\nlength of the string under the instructions of the script.\nMeanwhile, the main thread calls <code>foo<\/code>\nrepeatedly.\nAnd suppose the interpreter&#8217;s implementation of <code>foo<\/code>\ngoes like this:\n<\/p>\n<pre>\nvoid interpret_foo(const function_args&amp; args)\n{\n if (args.GetLength() != 1) wna(\"foo\");\n if (args.GetArgType(0) != V_STRING) wta(\"foo\", 0, V_STRING);\n char *s = args.PinArgString(0);\n SomeFunction(s);\n args.ReleasePin(0);\n}\n<\/pre>\n<p>\nThe script interpreter has kindly converted the script\ncode into the equivalent native code, and now you have a problem.\nAssuming the user doesn&#8217;t get impatient and click &#8220;Stop script&#8221;,\nthe script will eventually hit the race condition and cause\na buffer overflow in <code>Some&shy;Function<\/code>.\n<\/p>\n<p>\nAnd then you get to scramble a security hotfix.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Commenter POKE53280,0 claims, &#8220;If one validates parameters before using string functions (which quality programmers should do), the &#8216;safe&#8217; functions have no reason to exist.&#8221; Consider the following function: int SomeFunction(const char *s) { char buffer[256]; if (strlen(s) &ge; 256) return ERR; strcpy(buffer, s); &#8230; } What could possibly go wrong? You check the length of [&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-7263","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Commenter POKE53280,0 claims, &#8220;If one validates parameters before using string functions (which quality programmers should do), the &#8216;safe&#8217; functions have no reason to exist.&#8221; Consider the following function: int SomeFunction(const char *s) { char buffer[256]; if (strlen(s) &ge; 256) return ERR; strcpy(buffer, s); &#8230; } What could possibly go wrong? You check the length of [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/7263","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=7263"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/7263\/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=7263"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=7263"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=7263"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}