Linux Applications Debugging Techniques/Stack corruption

Stack corruption is rather hard to diagnose. Luckily, gcc 4.x can instrument the code to check for stack corruption:

  • -fstack-protector Add stack protection to functions that have “alloca” or have a (signed or unsigned) char array with size > 8 (SSP_BUFFER_SIZE)
  • -fstack-protector-strong To more functions, see below
  • -fstack-protector-all To ALL functions

gcc will add guard variables and code to check for buffer overflows upon exiting a function. A quick example:

/* Compile with: gcc -ggdb -fstack-protector-all stacktest.c */

#include <stdio.h>
#include <string.h>
 
void bar(char* str) 
{
    char buf[4];
    strcpy(buf, str);
}
	 
void foo() 
{
    printf("It survived!");
}
	 
int main(void) 
{
    bar("Longer than 4.");
    foo();
    return 0;
}

When run, the program will dump core:

$ ./a.out 
*** stack smashing detected ***: ./a.out terminated
Aborted (core dumped)
Core was generated by `./a.out'.
Program terminated with signal 6, Aborted.
#0  0x0000003684030265 in raise () from /lib64/libc.so.6
(gdb) bt full
#0  0x0000003684030265 in raise () from /lib64/libc.so.6
No symbol table info available.
#1  0x0000003684031d10 in abort () from /lib64/libc.so.6
No symbol table info available.
#2  0x000000368406a84b in __libc_message () from /lib64/libc.so.6
No symbol table info available.
#3  0x00000036840e8ebf in __stack_chk_fail () from /lib64/libc.so.6
No symbol table info available.
#4  0x0000000000400584 in bar (str=0x400715 "Longer than 4.") at stacktest.c:10
        buf =           "Long"
#5  0x00000000004005e3 in main () at stacktest.c:19
No locals.

-fstack-protector-strong edit

Added by google to gcc.

Benefit - gain big performance while sacrificing little security (for scenarios using -fstack-protector-all)

Background - some times stack-protector is too-simple while stack-protector-all over-kills, for example, to build one of our core systems, we forcibly add "-fstack-protector-all" to all compile commands, which brings big performance penalty (due to extra stack guard/check insns on function prologue and epilogue) on both atom and arm. To use "-fstack-protector" is just regarded as not secure enough (only "protects" <2% functions) by the system secure team. "-fstack-protector-strong" hits the balance between "-fstack-protector" and "-fstack-protector-all".

Adds the check to a function:

  • if any of its local variable’s address is taken, as part of the RHS of an assignment
  • or if any of its local variable’s address is taken as part of a function argument.
  • or if it has an array, regardless of array type or length
  • or if it has a struct/union which contains an array, regardless of array type or length.
  • or if function has register local variables

See http://gcc.gnu.org/ml/gcc-patches/2012-06/msg00974.html

Further reading edit


Starting with gcc 4.9 and later, you can use the ubsan sanitizer for bounds checking.