The -fstack-check compiler option emits code which can make any program segfault on entering its mainline. The placement of the stack relative to page boundaries needs to be right to demonstrate the problem. This placement varies with environment, length of pathname used to invoke the program, whether it is invoked directly or via GDB, and quite possibly other factors also. This is an old problem, I first saw it at gcc 2.95.3. Shell/expect scripts in How-To-Repeat demonstrate the problem reliably. This is a dissasem of main() :- 0x8048354 <main>: push %ebp 0x8048355 <main+1>: mov %esp,%ebp 0x8048357 <main+3>: sub $0x18,%esp 0x804835a <main+6>: and $0xfffffff0,%esp 0x804835d <main+9>: movl $0x0,0xfffffffc(%ebp) 0x8048364 <main+16>: lea 0xffffded8(%esp,1),%eax 0x804836b <main+23>: mov %eax,0xfffffff8(%ebp) 0x804836e <main+26>: mov 0xfffffffc(%ebp),%eax 0x8048371 <main+29>: add $0x1128,%eax 0x8048376 <main+34>: mov %esp,%edx 0x8048378 <main+36>: sub %eax,%edx 0x804837a <main+38>: mov %edx,0xfffffff4(%ebp) 0x804837d <main+41>: jmp 0x804838f <main+59> 0x804837f <main+43>: mov 0xfffffff8(%ebp),%eax 0x8048382 <main+46>: movl $0x0,(%eax) 0x8048388 <main+52>: subl $0x1000,0xfffffff8(%ebp) 0x804838f <main+59>: mov 0xfffffff4(%ebp),%edx 0x8048392 <main+62>: cmp %edx,0xfffffff8(%ebp) 0x8048395 <main+65>: ja 0x804837f <main+43> 0x8048397 <main+67>: mov 0xfffffff4(%ebp),%eax 0x804839a <main+70>: movl $0x0,(%eax) 0x80483a0 <main+76>: sub 0xfffffffc(%ebp),%esp 0x80483a3 <main+79>: lea 0xffffeed8(%esp,1),%eax 0x80483aa <main+86>: movl $0x0,(%eax) 0x80483b0 <main+92>: movl $0x8048424,(%esp,1) 0x80483b7 <main+99>: call 0x8048278 <printf> 0x80483bc <main+104>: mov $0x0,%eax 0x80483c1 <main+109>: leave 0x80483c2 <main+110>: ret The segfault occurs at 0x804839a <main+70>: movl $0x0,(%eax) Environment: System: Linux dimstar 2.4.24 #1 Sat Jan 10 17:03:52 EST 2004 i686 unknown Architecture: i686 host: i686-pc-linux-gnu build: i686-pc-linux-gnu target: i686-pc-linux-gnu configured with: /usr/src/gcc-3.3.2/configure --prefix=/usr/local/gcc3.2 How-To-Repeat: The preprocessed source:- ---------------------------- START hello.i ---------------------------- extern int printf (__const char *__restrict __format, ...) ; int main(int argc,char**argv) { printf("\nHello World!\n\n"); return 0 ; } ---------------------------- END hello.i ---------------------------- This test script will verify that we get the crash. It works by increasing the length of the pathname of hello in 16 byte increnents until it crashes (copious output) :- ---------------------------- START sh.sh ---------------------------- #!/bin/sh gcc -g -Wall -fstack-check -o hello hello.i i=aaaaaaaaaaaaaaa j=`/bin/pwd` while(true);do $j/hello || break j=$j/$i mkdir -p $j cp hello $j done echo "Failing path is $j" ---------------------------- END sh.sh ---------------------------- This script discovers the required pathname to see the problem under gdb. It saves this in the command file "cmd" for future use :- ---------------------------- START demo.sh ---------------------------- #!/bin/sh # expect treats all the shell lines as comment comtinuations \ gcc -g -Wall -fstack-check -o hello hello.i; \ i=aaaaaaaaaaaaaaa; \ exec expect -f "$0" -- $i `/bin/pwd` # We need to find a path that shows the problem when run under gdb set i [lindex $argv 0] set j [lindex $argv 1] puts "Finding a pathname that displays the problem." puts "This may take a while..." log_user 0 spawn sh expect "$ " set k 0 set l 0 while {1} \ { puts -nonewline "$l\r" flush stdout set l [incr l] exp_send "gdb $j/hello\r" expect "(gdb) " exp_send "run\r" expect \ { signal \ { set k 1 expect "(gdb) " exp_send "q\r" expect "(y or n) " exp_send "y\r" } normally \ { expect "(gdb) " exp_send "q\r" } } expect "$ " if {$k} break set j $j/$i exec mkdir -p $j exec cp hello $j } puts "\nFailing path is $j" exec echo "gdb $j/hello" > cmd exec chmod a+x cmd puts "gdb invocation command stored in ./cmd" ---------------------------- END demo.sh ---------------------------- After running demo.sh, you can invoke gdb by running cmd directly. Alternatively, you can run the following script to get directly to the first machine instruction in main() :- ---------------------------- START run_gdb.sh ---------------------------- #!/bin/sh # expect treats all the shell lines as comment comtinuations \ exec expect -f "$0" spawn ./cmd expect "(gdb) " exp_send "b __libc_start_main\r" expect "(gdb) " exp_send "run\r" expect "(gdb) " exp_send "n 11\r" expect "(gdb) " exp_send "si 10\r" expect "(gdb) " puts "\n\nYou can now interact with gdb." puts "If it won't exit for you (lost LWP or whatever), type \"////\" to kill it" exp_send " \r" interact //// exit ---------------------------- END run_gdb.sh ---------------------------- At gcc3.2.2, you can use "si 17" to get to the actual failing instruction. At that point if you note the contents of eax and use "x" to examine that location, the program will continue without failing. Otherwise, 'movl $0x0,(%eax)' will fail. I do not understand why this should be.
Fix: Workaround: don't use -fstack-check
I really think -fstack-check should be removed from the compiler, its use is no longer needed.
Confirmed with mainline. You have to run the testcase a few times, but on one, it'll inevitably crash. Only options needed to reproduce are "-fstack-check"
I would not like the "-fstack-check" to be removed from the compiler - it is needed to debug multi-threaded applications. Found the bug on Mandrake 9.1 with gcc 3.2.2, too. (In reply to comment #0) > The -fstack-check compiler option emits code which can make any program > segfault on entering its mainline. > The placement of the stack relative to page boundaries needs to be right > to demonstrate the problem. > This placement varies with environment, length of pathname used to invoke the > program, whether it is invoked directly or via GDB, > and quite possibly other factors also. > This is an old problem, I first saw it at gcc 2.95.3. > Shell/expect scripts in How-To-Repeat demonstrate the problem reliably. > > This is a dissasem of main() :- > > 0x8048354 <main>: push %ebp > 0x8048355 <main+1>: mov %esp,%ebp > 0x8048357 <main+3>: sub $0x18,%esp > 0x804835a <main+6>: and $0xfffffff0,%esp > 0x804835d <main+9>: movl $0x0,0xfffffffc(%ebp) > 0x8048364 <main+16>: lea 0xffffded8(%esp,1),%eax > 0x804836b <main+23>: mov %eax,0xfffffff8(%ebp) > 0x804836e <main+26>: mov 0xfffffffc(%ebp),%eax > 0x8048371 <main+29>: add $0x1128,%eax > 0x8048376 <main+34>: mov %esp,%edx > 0x8048378 <main+36>: sub %eax,%edx > 0x804837a <main+38>: mov %edx,0xfffffff4(%ebp) > 0x804837d <main+41>: jmp 0x804838f <main+59> > 0x804837f <main+43>: mov 0xfffffff8(%ebp),%eax > 0x8048382 <main+46>: movl $0x0,(%eax) > 0x8048388 <main+52>: subl $0x1000,0xfffffff8(%ebp) > 0x804838f <main+59>: mov 0xfffffff4(%ebp),%edx > 0x8048392 <main+62>: cmp %edx,0xfffffff8(%ebp) > 0x8048395 <main+65>: ja 0x804837f <main+43> > 0x8048397 <main+67>: mov 0xfffffff4(%ebp),%eax > 0x804839a <main+70>: movl $0x0,(%eax) > 0x80483a0 <main+76>: sub 0xfffffffc(%ebp),%esp > 0x80483a3 <main+79>: lea 0xffffeed8(%esp,1),%eax > 0x80483aa <main+86>: movl $0x0,(%eax) > 0x80483b0 <main+92>: movl $0x8048424,(%esp,1) > 0x80483b7 <main+99>: call 0x8048278 <printf> > 0x80483bc <main+104>: mov $0x0,%eax > 0x80483c1 <main+109>: leave > 0x80483c2 <main+110>: ret > > The segfault occurs at 0x804839a <main+70>: movl $0x0,(%eax) > > Environment: > System: Linux dimstar 2.4.24 #1 Sat Jan 10 17:03:52 EST 2004 i686 unknown > Architecture: i686 > > > host: i686-pc-linux-gnu > build: i686-pc-linux-gnu > target: i686-pc-linux-gnu > configured with: /usr/src/gcc-3.3.2/configure --prefix=/usr/local/gcc3.2 > > How-To-Repeat: > The preprocessed source:- > > ---------------------------- START hello.i ---------------------------- > extern int printf (__const char *__restrict __format, ...) ; > int main(int argc,char**argv) > { > printf("\nHello World!\n\n"); > return 0 ; > } > ---------------------------- END hello.i ---------------------------- > > This test script will verify that we get the crash. > It works by increasing the length of the pathname of hello > in 16 byte increnents until it crashes (copious output) :- > > ---------------------------- START sh.sh ---------------------------- > #!/bin/sh > gcc -g -Wall -fstack-check -o hello hello.i > i=aaaaaaaaaaaaaaa > j=`/bin/pwd` > while(true);do > $j/hello || break > j=$j/$i > mkdir -p $j > cp hello $j > done > echo "Failing path is $j" > ---------------------------- END sh.sh ---------------------------- > > This script discovers the required pathname to see the problem under gdb. > It saves this in the command file "cmd" for future use :- > > ---------------------------- START demo.sh ---------------------------- > #!/bin/sh > # expect treats all the shell lines as comment comtinuations \ > gcc -g -Wall -fstack-check -o hello hello.i; \ > i=aaaaaaaaaaaaaaa; \ > exec expect -f "$0" -- $i `/bin/pwd` > > # We need to find a path that shows the problem when run under gdb > > set i [lindex $argv 0] > set j [lindex $argv 1] > > puts "Finding a pathname that displays the problem." > puts "This may take a while..." > log_user 0 > > spawn sh > expect "$ " > set k 0 > set l 0 > while {1} \ > { > puts -nonewline "$l\r" > flush stdout > set l [incr l] > exp_send "gdb $j/hello\r" > expect "(gdb) " > exp_send "run\r" > expect \ > { > signal \ > { > set k 1 > expect "(gdb) " > exp_send "q\r" > expect "(y or n) " > exp_send "y\r" > } > normally \ > { > expect "(gdb) " > exp_send "q\r" > } > } > expect "$ " > if {$k} break > set j $j/$i > exec mkdir -p $j > exec cp hello $j > } > puts "\nFailing path is $j" > exec echo "gdb $j/hello" > cmd > exec chmod a+x cmd > puts "gdb invocation command stored in ./cmd" > ---------------------------- END demo.sh ---------------------------- > > After running demo.sh, you can invoke gdb by running cmd directly. > Alternatively, you can run the following script to get directly to > the first machine instruction in main() :- > > ---------------------------- START run_gdb.sh ---------------------------- > #!/bin/sh > # expect treats all the shell lines as comment comtinuations \ > exec expect -f "$0" > spawn ./cmd > expect "(gdb) " > exp_send "b __libc_start_main\r" > expect "(gdb) " > exp_send "run\r" > expect "(gdb) " > exp_send "n 11\r" > expect "(gdb) " > exp_send "si 10\r" > expect "(gdb) " > puts "\n\nYou can now interact with gdb." > puts "If it won't exit for you (lost LWP or whatever), type \"////\" to kill it" > exp_send " \r" > interact //// exit > ---------------------------- END run_gdb.sh ---------------------------- > > At gcc3.2.2, you can use "si 17" to get to the actual failing instruction. > At that point if you note the contents of eax and use "x" to examine that > location, the program will continue without failing. > > Otherwise, 'movl $0x0,(%eax)' will fail. > I do not understand why this should be.
I am experiencing the same bug with GCC 4.0.1 & 4.0.2 on SLES 9 with: gcc -v Using built-in specs. Target: i686-pc-linux-gnu Configured with: ../gcc-4.0.2/configure --enable-threads=posix --prefix=/usr/local/gcc4.0.2/ Thread model: posix gcc version 4.0.2
*** Bug 39306 has been marked as a duplicate of this bug. ***
*** This bug has been marked as a duplicate of 10127 ***