Stack smashing detected

574,065

Solution 1

Stack Smashing here is actually caused due to a protection mechanism used by gcc to detect buffer overflow errors. For example in the following snippet:

#include <stdio.h>
void func()
{
    char array[10];
    gets(array);
}
int main(int argc, char **argv)
{
    func();
}

The compiler, (in this case gcc) adds protection variables (called canaries) which have known values. An input string of size greater than 10 causes corruption of this variable resulting in SIGABRT to terminate the program.

To get some insight, you can try disabling this protection of gcc using option -fno-stack-protector while compiling. In that case you will get a different error, most likely a segmentation fault as you are trying to access an illegal memory location. Note that -fstack-protector should always be turned on for release builds as it is a security feature.

You can get some information about the point of overflow by running the program with a debugger. Valgrind doesn't work well with stack-related errors, but like a debugger, it may help you pin-point the location and reason for the crash.

Solution 2

Minimal reproduction example with disassembly analysis

main.c

void myfunc(char *const src, int len) {
    int i;
    for (i = 0; i < len; ++i) {
        src[i] = 42;
    }
}
int main(void) {
    char arr[] = {'a', 'b', 'c', 'd'};
    int len = sizeof(arr);
    myfunc(arr, len + 1);
    return 0;
}

GitHub upstream.

Compile and run:

gcc -fstack-protector-all -g -O0 -std=c99 main.c
ulimit -c unlimited && rm -f core
./a.out

fails as desired:

*** stack smashing detected ***: terminated
Aborted (core dumped)

Tested on Ubuntu 20.04, GCC 10.2.0.

On Ubuntu 16.04, GCC 6.4.0, I could reproduce with -fstack-protector instead of -fstack-protector-all, but it stopped blowing up when I tested on GCC 10.2.0 as per Geng Jiawen's comment. man gcc clarifies that as suggested by the option name, the -all version adds checks more aggressively, and therefore presumably incurs a larger performance loss:

-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 or equal to 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. Only variables that are actually allocated on the stack are considered, optimized away variables or variables allocated in registers don't count.

-fstack-protector-all

Like -fstack-protector except that all functions are protected.

Disassembly

Now we look at the disassembly:

objdump -D a.out

which contains:

int main (void){
  400579:       55                      push   %rbp
  40057a:       48 89 e5                mov    %rsp,%rbp
  # Allocate 0x10 of stack space.
  40057d:       48 83 ec 10             sub    $0x10,%rsp
  # Put the 8 byte canary from %fs:0x28 to -0x8(%rbp),
  # which is right at the bottom of the stack.
  400581:       64 48 8b 04 25 28 00    mov    %fs:0x28,%rax
  400588:       00 00 
  40058a:       48 89 45 f8             mov    %rax,-0x8(%rbp)
  40058e:       31 c0                   xor    %eax,%eax
    char arr[] = {'a', 'b', 'c', 'd'};
  400590:       c6 45 f4 61             movb   $0x61,-0xc(%rbp)
  400594:       c6 45 f5 62             movb   $0x62,-0xb(%rbp)
  400598:       c6 45 f6 63             movb   $0x63,-0xa(%rbp)
  40059c:       c6 45 f7 64             movb   $0x64,-0x9(%rbp)
    int len = sizeof(arr);
  4005a0:       c7 45 f0 04 00 00 00    movl   $0x4,-0x10(%rbp)
    myfunc(arr, len + 1);
  4005a7:       8b 45 f0                mov    -0x10(%rbp),%eax
  4005aa:       8d 50 01                lea    0x1(%rax),%edx
  4005ad:       48 8d 45 f4             lea    -0xc(%rbp),%rax
  4005b1:       89 d6                   mov    %edx,%esi
  4005b3:       48 89 c7                mov    %rax,%rdi
  4005b6:       e8 8b ff ff ff          callq  400546 <myfunc>
    return 0;
  4005bb:       b8 00 00 00 00          mov    $0x0,%eax
}
  # Check that the canary at -0x8(%rbp) hasn't changed after calling myfunc.
  # If it has, jump to the failure point __stack_chk_fail.
  4005c0:       48 8b 4d f8             mov    -0x8(%rbp),%rcx
  4005c4:       64 48 33 0c 25 28 00    xor    %fs:0x28,%rcx
  4005cb:       00 00 
  4005cd:       74 05                   je     4005d4 <main+0x5b>
  4005cf:       e8 4c fe ff ff          callq  400420 <[email protected]>
  # Otherwise, exit normally.
  4005d4:       c9                      leaveq 
  4005d5:       c3                      retq   
  4005d6:       66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
  4005dd:       00 00 00 

Notice the handy comments automatically added by objdump's artificial intelligence module.

If you run this program multiple times through GDB, you will see that:

  • the canary gets a different random value every time
  • the last loop of myfunc is exactly what modifies the address of the canary

The canary randomized by setting it with %fs:0x28, which contains a random value as explained at:

Debug attempts

From now on, we modify the code:

    myfunc(arr, len + 1);

to be instead:

    myfunc(arr, len);
    myfunc(arr, len + 1); /* line 12 */
    myfunc(arr, len);

to be more interesting.

We will then try to see if we can pinpoint the culprit + 1 call with a method more automated than just reading and understanding the entire source code.

gcc -fsanitize=address to enable Google's Address Sanitizer (ASan)

If you recompile with this flag and run the program, it outputs:

#0 0x4008bf in myfunc /home/ciro/test/main.c:4
#1 0x40099b in main /home/ciro/test/main.c:12
#2 0x7fcd2e13d82f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
#3 0x400798 in _start (/home/ciro/test/a.out+0x40079

followed by some more colored output.

This clearly pinpoints the problematic line 12.

The source code for this is at: https://github.com/google/sanitizers but as we saw from the example it is already upstreamed into GCC.

ASan can also detect other memory problems such as memory leaks: How to find memory leak in a C++ code/project?

Valgrind SGCheck

As mentioned by others, Valgrind is not good at solving this kind of problem.

It does have an experimental tool called SGCheck:

SGCheck is a tool for finding overruns of stack and global arrays. It works by using a heuristic approach derived from an observation about the likely forms of stack and global array accesses.

So I was not very surprised when it did not find the error:

valgrind --tool=exp-sgcheck ./a.out

The error message should look like this apparently: Valgrind missing error

GDB

An important observation is that if you run the program through GDB, or examine the core file after the fact:

gdb -nh -q a.out core

then, as we saw on the assembly, GDB should point you to the end of the function that did the canary check:

(gdb) bt
#0  0x00007f0f66e20428 in __GI_raise ([email protected]=6) at ../sysdeps/unix/sysv/linux/raise.c:54
#1  0x00007f0f66e2202a in __GI_abort () at abort.c:89
#2  0x00007f0f66e627ea in __libc_message ([email protected]=1, [email protected]=0x7f0f66f7a49f "*** %s ***: %s terminated\n") at ../sysdeps/posix/libc_fatal.c:175
#3  0x00007f0f66f0415c in __GI___fortify_fail (msg=<optimized out>, [email protected]=0x7f0f66f7a481 "stack smashing detected") at fortify_fail.c:37
#4  0x00007f0f66f04100 in __stack_chk_fail () at stack_chk_fail.c:28
#5  0x00000000004005f6 in main () at main.c:15
(gdb) f 5
#5  0x00000000004005f6 in main () at main.c:15
15      }
(gdb)

And therefore the problem is likely in one of the calls that this function made.

Next we try to pinpoint the exact failing call by first single stepping up just after the canary is set:

  400581:       64 48 8b 04 25 28 00    mov    %fs:0x28,%rax
  400588:       00 00 
  40058a:       48 89 45 f8             mov    %rax,-0x8(%rbp)

and watching the address:

(gdb) p $rbp - 0x8
$1 = (void *) 0x7fffffffcf18
(gdb) watch 0x7fffffffcf18
Hardware watchpoint 2: *0x7fffffffcf18
(gdb) c
Continuing.
Hardware watchpoint 2: *0x7fffffffcf18
Old value = 1800814336
New value = 1800814378
myfunc (src=0x7fffffffcf14 "*****?Vk\266", <incomplete sequence \355\216>, len=5) at main.c:3
3           for (i = 0; i < len; ++i) {
(gdb) p len
$2 = 5
(gdb) p i
$3 = 4
(gdb) bt
#0  myfunc (src=0x7fffffffcf14 "*****?Vk\266", <incomplete sequence \355\216>, len=5) at main.c:3
#1  0x00000000004005cc in main () at main.c:12

Now, this does leaves us at the right offending instruction: len = 5 and i = 4, and in this particular case, did point us to the culprit line 12.

However, the backtrace is corrupted, and contains some trash. A correct backtrace would look like:

#0  myfunc (src=0x7fffffffcf14 "abcd", len=4) at main.c:3
#1  0x00000000004005b8 in main () at main.c:11

so maybe this could corrupt the stack and prevent you from seeing the trace.

Also, this method requires knowing what is the last call of the canary checking function otherwise you will have false positives, which will not always be feasible, unless you use reverse debugging.

Solution 3

Please look at the following situation:

[email protected]:$ cat test_overflow.c 
#include <stdio.h>
#include <string.h>
int check_password(char *password){
    int flag = 0;
    char buffer[20];
    strcpy(buffer, password);
    if(strcmp(buffer, "mypass") == 0){
        flag = 1;
    }
    if(strcmp(buffer, "yourpass") == 0){
        flag = 1;
    }
    return flag;
}
int main(int argc, char *argv[]){
    if(argc >= 2){
        if(check_password(argv[1])){
            printf("%s", "Access granted\n");
        }else{
            printf("%s", "Access denied\n");
        }
    }else{
        printf("%s", "Please enter password!\n");
    }
}
[email protected]:$ gcc -g -fno-stack-protector test_overflow.c 
[email protected]:$ ./a.out mypass
Access granted
[email protected]:$ ./a.out yourpass
Access granted
[email protected]:$ ./a.out wepass
Access denied
[email protected]:$ ./a.out wepassssssssssssssssss
Access granted
[email protected]:$ gcc -g -fstack-protector test_overflow.c 
[email protected]:$ ./a.out wepass
Access denied
[email protected]:$ ./a.out mypass
Access granted
[email protected]:$ ./a.out yourpass
Access granted
[email protected]:$ ./a.out wepassssssssssssssssss
*** stack smashing detected ***: ./a.out terminated
======= Backtrace: =========
/lib/tls/i686/cmov/libc.so.6(__fortify_fail+0x48)[0xce0ed8]
/lib/tls/i686/cmov/libc.so.6(__fortify_fail+0x0)[0xce0e90]
./a.out[0x8048524]
./a.out[0x8048545]
/lib/tls/i686/cmov/libc.so.6(__libc_start_main+0xe6)[0xc16b56]
./a.out[0x8048411]
======= Memory map: ========
007d9000-007f5000 r-xp 00000000 08:06 5776       /lib/libgcc_s.so.1
007f5000-007f6000 r--p 0001b000 08:06 5776       /lib/libgcc_s.so.1
007f6000-007f7000 rw-p 0001c000 08:06 5776       /lib/libgcc_s.so.1
0090a000-0090b000 r-xp 00000000 00:00 0          [vdso]
00c00000-00d3e000 r-xp 00000000 08:06 1183       /lib/tls/i686/cmov/libc-2.10.1.so
00d3e000-00d3f000 ---p 0013e000 08:06 1183       /lib/tls/i686/cmov/libc-2.10.1.so
00d3f000-00d41000 r--p 0013e000 08:06 1183       /lib/tls/i686/cmov/libc-2.10.1.so
00d41000-00d42000 rw-p 00140000 08:06 1183       /lib/tls/i686/cmov/libc-2.10.1.so
00d42000-00d45000 rw-p 00000000 00:00 0 
00e0c000-00e27000 r-xp 00000000 08:06 4213       /lib/ld-2.10.1.so
00e27000-00e28000 r--p 0001a000 08:06 4213       /lib/ld-2.10.1.so
00e28000-00e29000 rw-p 0001b000 08:06 4213       /lib/ld-2.10.1.so
08048000-08049000 r-xp 00000000 08:05 1056811    /dos/hacking/test/a.out
08049000-0804a000 r--p 00000000 08:05 1056811    /dos/hacking/test/a.out
0804a000-0804b000 rw-p 00001000 08:05 1056811    /dos/hacking/test/a.out
08675000-08696000 rw-p 00000000 00:00 0          [heap]
b76fe000-b76ff000 rw-p 00000000 00:00 0 
b7717000-b7719000 rw-p 00000000 00:00 0 
bfc1c000-bfc31000 rw-p 00000000 00:00 0          [stack]
Aborted
[email protected]:$ 

When I disabled the stack smashing protector no errors were detected, which should have happened when I used "./a.out wepassssssssssssssssss"

So to answer your question above, the message "** stack smashing detected : xxx" was displayed because your stack smashing protector was active and found that there is stack overflow in your program.

Just find out where that occurs, and fix it.

Solution 4

You could try to debug the problem using valgrind:

The Valgrind distribution currently includes six production-quality tools: a memory error detector, two thread error detectors, a cache and branch-prediction profiler, a call-graph generating cache profiler, and a heap profiler. It also includes two experimental tools: a heap/stack/global array overrun detector, and a SimPoint basic block vector generator. It runs on the following platforms: X86/Linux, AMD64/Linux, PPC32/Linux, PPC64/Linux, and X86/Darwin (Mac OS X).

Solution 5

It means that you wrote to some variables on the stack in an illegal way, most likely as the result of a Buffer overflow.

Share:
574,065
Biswajyoti Das
Author by

Biswajyoti Das

I am doing my bachelors in Computer science and engineering .

Updated on September 29, 2021

Comments

  • Biswajyoti Das
    Biswajyoti Das about 1 year

    I am executing my a.out file. After execution the program runs for some time then exits with the message:

    **** stack smashing detected ***: ./a.out terminated*
    *======= Backtrace: =========*
    */lib/tls/i686/cmov/libc.so.6(__fortify_fail+0x48)Aborted*
    

    What could be the possible reasons for this and how do I rectify it?

  • Peter Mortensen
    Peter Mortensen over 13 years
    Stack overflow is the stack smashing into something else. Here it is the other way around: something has smashed into the stack.
  • Ted Pennings
    Ted Pennings over 10 years
    thanks for this answer! I found that in my case I had not initialized the variable I was trying to write to
  • Bas Wijnen
    Bas Wijnen about 10 years
    Not really. It's one part of the stack smashing into another part. So it really is a buffer overflow, just not over the top of stack, but "only" into another part of the stack.
  • toasted_flakes
    toasted_flakes almost 9 years
    Valgrind doesn't work well for stack-related errors, since it can't add red zones there
  • D.W.
    D.W. almost 9 years
    This answer is incorrect, and provides dangerous advice. First of all, removing stack protector is not the right solution -- if you are getting a stack smashing error, you probably have a serious security vulnerability in your code. The correct response is to fix the buggy code. Second, as grasGendarme points out, the recommendation to try Valgrind is not going to be effective. Valgrind typically doesn't work for detecting illegal memory accesses to stack-allocated data.
  • D.W.
    D.W. almost 9 years
    Yeah, but Valgrind doesn't work well for overflows of stack-allocated buffers, which is the situation that this error message indicates.
  • sud03r
    sud03r almost 9 years
    The OP asks for possible reasons for this behaviour, my answer provides an example and how it relates to a reasonably known error. Besides, removing the stack-protector is not a solution, it is sort of an experiment one could do to get more insights to the problem. The advice actually is to fix the error somehow, thanks for pointing about valgrind, i will edit my answer to reflect this.
  • Exectron
    Exectron almost 9 years
    How could we use that stack array overrun detector? Can you elaborate?
  • Hi-Angel
    Hi-Angel over 8 years
    @D.W. the stack protection should be turned off in a release version, because at first -- the stack smashing detected message is a help only for a developers; at second -- an application could have yet chances to survive; and at third -- this is a tiny optimization.
  • Ricky Mutschlechner
    Ricky Mutschlechner over 8 years
    Stack Smashing is not a protection mechanism. It's a buffer overflow attack in which you corrupt the return address in your stack to do something of YOUR choosing, rather than the program writer's choosing. Why does this have 95 upvotes...?
  • CygnusX1
    CygnusX1 about 5 years
    The author meant probably that the message about stack smashing appears, and the program is terminated due to a protection mechanism.
  • Ciro Santilli OurBigBook.com
    Ciro Santilli OurBigBook.com over 4 years
    I like this example because it does not rely on standard library functions like gets and scrcpy. I wonder if we could minimize if further. I would at least get rid of string.h with size_t len = sizeof( arr );. Tested on gcc 6.4, Ubuntu 16.04. I would also give the failing example with the arr + 2 to minimize copy pasting.
  • Ciro Santilli OurBigBook.com
    Ciro Santilli OurBigBook.com over 4 years
    @CraigMcQueen I've tried to use Valgrind's experimental heuristic SGCheck stack smashing detector on a minimal example: stackoverflow.com/a/51897264/895245 but it failed.
  • Shiva Acharjee
    Shiva Acharjee about 4 years
    So assuming I have an array char str[1]; and I try to insert value str[5]='1'; so this should gives me an error 'stack smashing'?
  • Martin Ueding
    Martin Ueding about 4 years
    Using -fsanitize=address with GCC or Clang on Linux should give you very detailed information about the overflow.
  • m4l490n
    m4l490n almost 3 years
    why would I be getting the stack smashing error on a return? I'm detecting something in my program and I end it with return 1 and as soon as the return is executed I'm getting this. The code runs just fine the whole time.
  • mangusta
    mangusta almost 3 years
    @m4l490n you better open a new question and post your code so that we could have an insight about what's going on with your execution flow
  • Rick Mac Gillis
    Rick Mac Gillis over 2 years
    Why not just have the error be "stack overflow detected"? :)
  • Geng  Jiawen
    Geng Jiawen almost 2 years
    on on higher gcc like 10, you may need to use -fstack-protector-all. Demo: godbolt.org/z/n1cqPn
  • Nate Eldredge
    Nate Eldredge almost 2 years
    @Hi-Angel: If a potentially malicious user has found a way to get your program to overwrite part of its stack, you very likely don't want it to survive. Better a DOS than an actual intrusion.
  • Sangeerththan Balachandran
    Sangeerththan Balachandran about 1 year
    This does not really answer the question. If you have a different question, you can ask it by clicking Ask Question. To get notified when this question gets new answers, you can follow this question. Once you have enough reputation, you can also add a bounty to draw more attention to this question. - From Review
  • ygroeg
    ygroeg about 1 year
    @SangeerththanBalachandran I think it does answer the question, which is What could be the possible reasons for this and how do I rectify it?. I showed a reason that I did not see in the list of answers and added the solution that solved the problem for me.
  • Sangeerththan Balachandran
    Sangeerththan Balachandran about 1 year
    this is not the problem OP was facing and your problem is with the makefile specific for a project you have worked.
  • ygroeg
    ygroeg about 1 year
    @SangeerththanBalachandran I believe that, if the same problem has different reasons, why shouldn't I post the path to different solution and thought process? The solution that is marked as correct, won't be able to solve the makefile problem. The fact that, OP wasn't facing this problem, doesn't mean that all people that encounter this error later will solve it like OP did. A lot of people use makefiles for their projects and a lot of those can make mistakes in them.
  • Sangeerththan Balachandran
    Sangeerththan Balachandran about 1 year
    in such a case it will be useful to further provide what kind of mistakes happened specifically.
  • ygroeg
    ygroeg about 1 year
    @SangeerththanBalachandran Feel free to post specific examples below, if you want to provide something useful to this answer. I'll add those to answer with reference to you. I pointed the error and the root cause of such case without geting into specifications of my project. The solution is quiet simple, but examples can be different, i.e. make, cmake, qmake and what not.
  • valiano
    valiano about 1 year
    Adding to above comment - -fstack-protector-strong works as well