Search code examples
c++cglibcstack-smash

Stack smashing not fires. Why?


I'm trying to make glibc detect stack smashing, and I use the following code:

 #include <stdio.h>
 #include <string.h>

 static const int n = 5;

 int main(int argc, char *argv[])
 {
  if (argc != 2)
  {
     printf("usage: %s string\n", argv[0]);
     return -1;
  }
  printf("%s, len = %d\n", argv[1], strlen(argv[1]));
  unsigned char a[n][n];
  unsigned char * b = a[n - 1];
  memcpy(b, argv[1], (strlen(argv[1]) + 1) * sizeof(unsigned char));
  return 0;
 }

If argv[1] length greater than 5, I expect detection of stack smashing error, however, I do not, and valgrind detects no errors. What I should change to get this error? (array a must be two-dimensional)


Solution

  • It seems like the logic in gcc which decides when to enable stack protection is slightly tricky. First note from the docs:

    -fstack-protector

    Emit extra code to check for buffer overflows, such as stack smashing attacks. This is done by adding a guard variable to functions with vulnerable objects. This includes functions that call alloca, and functions with buffers larger than 8 bytes. The guards are initialized when a function is entered and then checked when the function exits. If a guard check fails, an error message is printed and the program exits

    So, we should expect a function with local buffers smaller than 8 bytes to be unprotected. For example, this:

    int unprotected() {
        char a[5];
        strcpy(a, "this is much too long");
        return a[0];
    }
    

    compiled with gcc -fstack-protector -Wstack-protector, gives a warning like

    warning: stack protector not protecting function: all local arrays are less than 8 bytes long [-Wstack-protector]

    So, you might think your char[5][5] will be protected, since it is more than 8 bytes long. However, when I compile that to assembler, I get no warning or stack protection (you can find the assembler to look for in this Dr. Dobbs article). It seems that gcc treats it as 5 buffers of 5 bytes each instead of a single buffer of 25 bytes.

    You can persuade gcc to enable stack protection either by showing it a single buffer larger than 8 bytes:

    void protected(char *arg) {
        union {
            char dummy[5 * 5];
            char a[5][5];
        } u;
        memcpy(u.a[4], arg, (strlen(arg) + 1));
    }
    

    or by simply using -fstack-protector-all.