{"id":134,"date":"2007-09-15T20:57:05","date_gmt":"2007-09-16T04:57:05","guid":{"rendered":"http:\/\/outflux.net\/blog\/archives\/2007\/09\/15\/catching-stack-overflows-in-gdb-as-they-happen\/"},"modified":"2007-09-27T10:33:48","modified_gmt":"2007-09-27T18:33:48","slug":"catching-stack-overflows-in-gdb-as-they-happen","status":"publish","type":"post","link":"https:\/\/outflux.net\/blog\/archives\/2007\/09\/15\/catching-stack-overflows-in-gdb-as-they-happen\/","title":{"rendered":"catching stack overflows in gdb as they happen"},"content":{"rendered":"<p>Recently I was trying to help debug a stack overflow <a href=\"https:\/\/bugs.launchpad.net\/ubuntu\/+source\/wpasupplicant\/+bug\/138873\">crash in wpa_supplicant<\/a>.  The trouble with a stack crash is that you end up without a useful call history since the stack is left partially wrecked.  The compiler code for detecting stack overflows (SSP), sets up a canary value between the local variables of the function and the stack frame.  When the function exits, it tests this canary value and aborts if it doesn&#8217;t match what it is expecting.  So, logically, to catch the stack overflow, gdb needs to be set up in a way to watch the canary location too.  Since the canary is only valid while in the function, gdb must be set up to have a memory watch only when the function is called.<\/p>\n<p>Here is the function preamble:<\/p>\n<blockquote>\n<pre>\r\n0x08081940 &lt;wpa_driver_wext_get_scan_results+0&gt;:        push   %ebp\r\n0x08081941 &lt;wpa_driver_wext_get_scan_results+1&gt;:        mov    %esp,%ebp\r\n0x08081943 &lt;wpa_driver_wext_get_scan_results+3&gt;:        push   %edi\r\n0x08081944 &lt;wpa_driver_wext_get_scan_results+4&gt;:        push   %esi\r\n0x08081945 &lt;wpa_driver_wext_get_scan_results+5&gt;:        push   %ebx\r\n<\/pre>\n<\/blockquote>\n<p>Save registers, prepare %ebp.<\/p>\n<blockquote>\n<pre>\r\n0x08081946 &lt;wpa_driver_wext_get_scan_results+6&gt;:        mov    $0x1000,%ebx\r\n0x08081951 &lt;wpa_driver_wext_get_scan_results+17&gt;:       mov    0x8(%ebp),%eax\r\n0x08081954 &lt;wpa_driver_wext_get_scan_results+20&gt;:       mov    0xc(%ebp),%edx\r\n0x08081957 &lt;wpa_driver_wext_get_scan_results+23&gt;:       lea    0xffffffb0(%ebp),%esi\r\n<\/pre>\n<\/blockquote>\n<p>Make room for local variables, copy some function arguments and local variables into registers.<\/p>\n<blockquote>\n<pre>\r\n0x0808195a &lt;wpa_driver_wext_get_scan_results+26&gt;:       mov    %gs:0x14,%ecx\r\n0x08081961 &lt;wpa_driver_wext_get_scan_results+33&gt;:       mov    %ecx,0xffffffec(%ebp)\r\n<b>0x08081964<\/b> &lt;wpa_driver_wext_get_scan_results+36&gt;:       xor    %ecx,%ecx\r\n<\/pre>\n<\/blockquote>\n<p>Here&#8217;s the stack canary getting set, and the register cleared.  It&#8217;s saved at %ebp minus 0x14 (0xffffffec signed is -0x14):<\/p>\n<blockquote>\n<pre>\r\n(gdb) printf \"0x%x\\n\", 0-0xffffffec\r\n0x14\r\n<\/pre>\n<\/blockquote>\n<p>Now for the function play-out:<\/p>\n<blockquote>\n<pre>\r\n0x08081a37 &lt;wpa_driver_wext_get_scan_results+247&gt;:      mov    0xffffffec(%ebp),%edx\r\n<b>0x08081a3a<\/b> &lt;wpa_driver_wext_get_scan_results+250&gt;:      xor    %gs:0x14,%edx\r\n0x08081a41 &lt;wpa_driver_wext_get_scan_results+257&gt;:      jne    0x8081eae &lt;wpa_driver_wext_get_scan_results+1390&gt;\r\n<\/pre>\n<\/blockquote>\n<p>There is the canary check.<\/p>\n<blockquote>\n<pre>\r\n0x08081a47 &lt;wpa_driver_wext_get_scan_results+263&gt;:      add    $0xec,%esp\r\n0x08081a4d &lt;wpa_driver_wext_get_scan_results+269&gt;:      pop    %ebx\r\n0x08081a4e &lt;wpa_driver_wext_get_scan_results+270&gt;:      pop    %esi\r\n0x08081a4f &lt;wpa_driver_wext_get_scan_results+271&gt;:      pop    %edi\r\n0x08081a50 &lt;wpa_driver_wext_get_scan_results+272&gt;:      pop    %ebp\r\n0x08081a51 &lt;wpa_driver_wext_get_scan_results+273&gt;:      ret    \r\n...\r\n0x08081eae &lt;wpa_driver_wext_get_scan_results+1390&gt;:     call   0x804bdc8 &lt;__stack_chk_fail@plt&gt;\r\n<\/pre>\n<\/blockquote>\n<p>Release local stack, pop saved registers and return.  Nearer the end is the call to __stack_chk_fail when the canary doesn&#8217;t match.<\/p>\n<p>So, to watch the canary, we need to set up a memory watch after it as been set, and tear it down before we leave the function.  Respectively, we can use addresses 0x08081964 and 0x08081a3a (in bold above):<\/p>\n<blockquote>\n<pre>\r\n(gdb) br *0x08081964\r\nBreakpoint 1 at 0x8081964\r\n(gdb) br *0x08081a3a\r\nBreakpoint 2 at 0x8081a3a\r\n<\/pre>\n<\/blockquote>\n<p>At the first breakpoint, we set a memory watch using a gdb-local variable, based on %ebp (we can&#8217;t use %ebp directly since it will change in lower function calls):<\/p>\n<blockquote>\n<pre>\r\n(gdb) commands 1\r\nType commands for when breakpoint 1 is hit, one per line.\r\nEnd with a line saying just \"end\".\r\n&gt;silent\r\n&gt;set variable $cow = (unsigned long*)($ebp - 0x14)\r\n&gt;watch *$cow\r\n&gt;cont\r\n&gt;end\r\n<\/pre>\n<\/blockquote>\n<p>Since I couldn&#8217;t find an easy way to track the memory watch number that was created during the first breakpoint, I just built a gdb counter, and deleted the memory watch when leaving, since I could predict gdb&#8217;s numbering (first watch will be &#8220;3&#8221;, following our breakpoints 1 and 2):<\/p>\n<blockquote>\n<pre>\r\n(gdb) set variable $count = 3\r\n(gdb) commands 2\r\nType commands for when breakpoint 2 is hit, one per line.\r\nEnd with a line saying just \"end\".\r\n&gt;silent\r\n&gt;delete $count\r\n&gt;set variable $count = $count + 1\r\n&gt;cont\r\n&gt;end\r\n<\/pre>\n<\/blockquote>\n<p>Now we can run, and wait for the canary to get overwritten:<\/p>\n<blockquote>\n<pre>\r\n(gdb) cont\r\nContinuing.\r\nHardware watchpoint 3: *$cow\r\nHardware watchpoint 4: *$cow\r\n...\r\nHardware watchpoint 12: *$cow\r\nHardware watchpoint 13: *$cow\r\nHardware watchpoint 13: *$cow\r\n\r\nOld value = 4278845440\r\nNew value = 4278845546\r\n0x0804eae6 in ?? ()\r\n<\/pre>\n<\/blockquote>\n<p>We see the canary value is 0xFF0A0000 getting it&#8217;s little-endian first byte overwritten to FF0A006A.  We catch it before it has wrecked the stack, and we can see very clearly where we are:<\/p>\n<blockquote>\n<pre>\r\n(gdb) bt\r\n#0  hexstr2bin (hex=0x080a239d \"6151663870517a74\", buf=0x080a2395 \"aQf8pQzt00000000j\", len=8)\r\n    at ..\/src\/utils\/common.c:88\r\n#1  0x08082297 in wpa_driver_wext_get_scan_results (priv=0xb7dd816c, \r\n    results=0x080a239d, max_size=0x79)\r\n    at ..\/src\/drivers\/driver_wext.c:1383\r\n...\r\n(gdb) x\/1i $eip      \r\n0x804eae6 &lt;hexstr2bin +54&gt;:      addl   $0x1,0xfffffff0(%ebp)\r\n<\/pre>\n<\/blockquote>\n<p>On a closer look at the source, we realize wext_get_scan_custom got inlined into the function (it was static and only called from one place, so the compiler optimized it).  Further tracking in the source shows that the &#8220;16&#8221; value passed in should actually be &#8220;8&#8221; (the limit of the destination, not the source, buffer size).<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Recently I was trying to help debug a stack overflow crash in wpa_supplicant. The trouble with a stack crash is that you end up without a useful call history since the stack is left partially wrecked. The compiler code for detecting stack overflows (SSP), sets up a canary value between the local variables of the [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[10,6,14],"tags":[],"_links":{"self":[{"href":"https:\/\/outflux.net\/blog\/wp-json\/wp\/v2\/posts\/134"}],"collection":[{"href":"https:\/\/outflux.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/outflux.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/outflux.net\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/outflux.net\/blog\/wp-json\/wp\/v2\/comments?post=134"}],"version-history":[{"count":0,"href":"https:\/\/outflux.net\/blog\/wp-json\/wp\/v2\/posts\/134\/revisions"}],"wp:attachment":[{"href":"https:\/\/outflux.net\/blog\/wp-json\/wp\/v2\/media?parent=134"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/outflux.net\/blog\/wp-json\/wp\/v2\/categories?post=134"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/outflux.net\/blog\/wp-json\/wp\/v2\/tags?post=134"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}