先看下面的一个程序:
cat test21.c
void main(void)
{
int *pointer;
int i;
pointer = malloc(sizeof(int));
for(i = 0; 1; i++)
{
pointer[i] = i;
printf("pointer[%d] = %d\n", i, pointer[i]);
}
}
执行时,最终会crash掉:
pointer[33786] = 33786
pointer[33787] = 33787
pointer[33788] = 33788
pointer[33789] = 33789
Segmentation fault (core dumped)
我们的任务是分析 产生 Segfault的原因
先用gdb调入可执行程序,设置好断点:
$ gdb -q
(gdb) file test21
Reading symbols from /home/charles/test21...done.
(gdb) break 8
Breakpoint 1 at 0x8048435: file test21.c, line 8.
(gdb) condition 1 i == 33790
(gdb) info breakpoints
Num Type Disp Enb Address What
1 breakpoint keep y 0x08048435 in main at test21.c:8
stop only if i == 33790
然后,运行 run命令:
pointer[33788] = 33788
pointer[33789] = 33789
Breakpoint 1, main () at test21.c:8
8 pointer[i] = i;
然后,运行 next:
(gdb) next
Program received signal SIGSEGV, Segmentation fault.
0x08048444 in main () at test21.c:8
8 pointer[i] = i;
(gdb)
反汇编:
(gdb) disassemble
Dump of assembler code for function main:
0x08048414 <+0>: push %ebp
0x08048415 <+1>: mov %esp,%ebp
0x08048417 <+3>: and $0xfffffff0,%esp
0x0804841a <+6>: sub $0x20,%esp
0x0804841d <+9>: movl $0x4,(%esp)
0x08048424 <+16>: call 0x8048330 <malloc@plt>
0x08048429 <+21>: mov %eax,0x1c(%esp)
0x0804842d <+25>: movl $0x0,0x18(%esp)
0x08048435 <+33>: mov 0x18(%esp),%eax
0x08048439 <+37>: shl $0x2,%eax
0x0804843c <+40>: add 0x1c(%esp),%eax
0x08048440 <+44>: mov 0x18(%esp),%edx
=> 0x08048444 <+48>: mov %edx,(%eax)
0x08048446 <+50>: mov 0x18(%esp),%eax
0x0804844a <+54>: shl $0x2,%eax
0x0804844d <+57>: add 0x1c(%esp),%eax
0x08048451 <+61>: mov (%eax),%eax
0x08048453 <+63>: mov %eax,0x8(%esp)
0x08048457 <+67>: mov 0x18(%esp),%eax
0x0804845b <+71>: mov %eax,0x4(%esp)
0x0804845f <+75>: movl $0x8048550,(%esp)
0x08048466 <+82>: call 0x8048320 <printf@plt>
0x0804846b <+87>: addl $0x1,0x18(%esp)
0x08048470 <+92>: jmp 0x8048435 <main+33>
End of assembler dump.
检查下 eax寄存器:
(gdb) print /x $eax
$2 = 0x806c000
(gdb) x $eax
0x806c000: Cannot access memory at address 0x806c000
(gdb)
EAX指向的地址不允许访问
看一下此时虚拟内存的布局:
(gdb) info proc mappings
process 7323
Mapped address spaces:
Start Addr End Addr Size Offset objfile
0x8048000 0x8049000 0x1000 0x0 /home/charles/test21
0x8049000 0x804a000 0x1000 0x0 /home/charles/test21
0x804a000 0x804b000 0x1000 0x1000 /home/charles/test21
0x804b000 0x806c000 0x21000 0x0 [heap]
0xb7e1a000 0xb7e1b000 0x1000 0x0
0xb7e1b000 0xb7fbf000 0x1a4000 0x0 /lib/i386-linux-gnu/libc-2.15.so
0xb7fbf000 0xb7fc1000 0x2000 0x1a4000 /lib/i386-linux-gnu/libc-2.15.so
0xb7fc1000 0xb7fc2000 0x1000 0x1a6000 /lib/i386-linux-gnu/libc-2.15.so
0xb7fc2000 0xb7fc5000 0x3000 0x0
0xb7fda000 0xb7fdd000 0x3000 0x0
0xb7fdd000 0xb7fde000 0x1000 0x0 [vdso]
0xb7fde000 0xb7ffe000 0x20000 0x0 /lib/i386-linux-gnu/ld-2.15.so
0xb7ffe000 0xb7fff000 0x1000 0x1f000 /lib/i386-linux-gnu/ld-2.15.so
0xb7fff000 0xb8000000 0x1000 0x20000 /lib/i386-linux-gnu/ld-2.15.so
0xbffdf000 0xc0000000 0x21000 0x0 [stack]
可以看到,此时 EAX指向的地址是heap区的结束地址,这个虚拟地址是没有被映射到地址空间的,所以访问会出错。
再看一下 pointer的地址:
(gdb) print /x pointer
$3 = 0x804b008
(gdb) print pointer + i
$4 = (int *) 0x806c000
可见, 分给程序用的heap区域比总的少了8个字节。
可以看一下这个起始的8个字节的内容:
(gdb) x /8xw 0x804b000
0x804b000: 0x00000000 0x00000011 0x00000000 0x00000001
0x804b010: 0x00000002 0x00000003 0x00000004 0x00000005
前8个字节保存的是 heap header的信息。
参考:
1. http://www.dedoimedo.com/computers/gdb.html