从递归学习 function call
- infinite.c
<code>int func() { static int count = 0; return ++count && func(); } int main() { return func(); }/<code>
用 GDB 执行和测试,记得加上 -g:
<code>$ gcc -o infinite infinite.c -g $ gdb -q infinite Reading symbols from infinite...done. (gdb) r Starting program: /tmp/infinite Program received signal SIGSEGV, Segmentation fault. 0x00000000004004f8 in func () at infinite.c:3 3 return ++count && func(); (gdb) p count $1 = 524032/<code>
如果将infinite.c 改为以下,重复上述动作:
<code>int func(int x) { static int count = 0; return ++count && func(x++); } int main() { return func(0); }/<code>
将得到:
<code>Program received signal SIGSEGV, Segmentation fault. 0x0000000000400505 in func (x=1) at infinite.c:3 3 return ++count && func(x++); (gdb) p count $1 = 262016/<code>
继续修改 infinite.c 為以下,重复上述动作:
<code>int func(int x) { static int count = 0; int y = x; // local var return ++count && func(x++); } int main() { return func(0); }/<code>
将得到以下:
<code>Program received signal SIGSEGV, Segmentation fault. 0x00000000004004de in func (x=) at infinite.c:1 1 int func(int x) { (gdb) p count $1 = 174677/<code>
stack 里面有 x (parameter), y (local variable), return address
- stack frame
观察UNIX Process 中的 stack 空间:
<code>$ sudo cat /proc/1/maps | grep stack 7fff7e13f000-7fff7e160000 rw-p 00000000 00:00 0 [stack]/<code>
60000Hex - 3f000Hex = 21000Hex = 135168Dec
135168 * 4 = 540672
这跟前面的数字很接近!
- return address 很重要,不管有没有选上,都要有
退路
sp = stack pointer; stack register
(AMD x86_64) A stack frame’s best friends are the two registers rsp and rbp, called the “stack pointer” and the “frame pointer”.
function prologue
function epilogue
(gdb) x /16gx $rsp
5.10. Borrow an
stack-based buffer overflow
- CVE-2015-7547
vulnerability in glibc’s DNS client-side resolver that is used to translate human-readable domain names, like google.com, into a network IP address. [读者可以网上自行搜索了解]
- Buffer Overflow : Example of Using GDB to Check Stack Memory(非常好的PPT文档)
准备 gdb-example.c,其內容为
<code>#include void foo(char *input) { int a1 = 11; int a2 = 22; char buf[7]; strcpy(buf, input); } void main(int argc, char **argv) { foo(argv[1]); }/<code>
编译,记得加上 -g 和 -m32
用 i686 架构执行 GDB:
<code>$ setarch i686 -R gdb -q ./gdb-example (gdb) break 6 Breakpoint 1 at 0x804847a: file gdb-example.c, line 6. (gdb) run “whatever” Starting program: /tmp/gdb-example "whatever" Breakpoint 1, foo (input=0xffffd406 "whatever") at gdb-example.c:6 6 strcpy(buf, input);/<code>
观察 stack 內容:
<code>(gdb) info frame Stack level 0, frame at 0xffffd180: eip = 0x8048490 in foo (gdb-example.c:6); saved eip = 0x80484da called by frame at 0xffffd1b0 source language c. Arglist at 0xffffd178, args: input=0xffffd40c "whatever" Locals at 0xffffd178, Previous frame’s sp is 0xffffd180 Saved registers: ebp at 0xffffd178, eip at 0xffffd17c (gdb) x &a1 0xffffd15c: 0x0000000b (gdb) x &a2 0xffffd160: 0x00000016 (gdb) x buf 0xffffd165: 0x44f7f9c0 (gdb) x 0xffffd160 0xffffd160: 0x00000016/<code>
藏在 Heap 里细节
free() 释放的是 pointer 指向位于 heap 的连续内存,而非 pointer 本身占有的内存(*ptr)。
举例來说:
<code>#include int main() { int *p = (int *) malloc(1024); free(p); free(p); return 0; }/<code>
编译不會有错误,但运行时会失败:
<code>*** Error in './free': double free or corruption (top): 0x000000000067a010 ***/<code>
倘若改为以下:
<code>#include int main() { int *p = (int *) malloc(1024); free(p); p = NULL; free(p); return 0; }/<code>
则会编译和执行都成功。
因此,为了防止对同一个 pointer 作 free() 两次的操作,而导致程序失败,free() 后应该设定为 NULL。
[ 思考] 为什么 glibc 可以侦测出上述程序的 “double free or corruption” 呢?
malloc / free
在 GNU/Linux 里观察 malloc
事先安装 gdb套件
<code>$ sudo apt install gdb gdb-dbg/<code>
GDB 操作:
<code>$ gdb -q `which gdb` Reading symbols from /usr/bin/gdb... (gdb) start ... Temporary breakpoint 1, main (argc=1, argv=0x7fffffffe4c8) at ./gdb/gdb.c:25 ... (gdb) p ((double(*)())pow)(2.,3.) $1 = 8 (gdb) call malloc_stats() Arena 0: system bytes = 135168 in use bytes = 28000 Total (incl. mmap): system bytes = 135168 in use bytes = 28000 max mmap regions = 0 max mmap bytes = 0 $2 = -168929728 (gdb) call malloc_info(0, stdout) /<code>
glibc 提供了 malloc_stats() 和 malloc_info() 这两个函数,可显示 process 的 heap 信息
延伸阅读
如何实现一个 malloc(
https://kb.cnblogs.com/page/512454/)
- c malloc/free 初探 (https://descent-incoming.blogspot.com/2015/06/c-mallocfree.html)
- 用 C 语言编写一个简单的垃圾回收器(https://blog.csdn.net/lxw907304340/article/details/44420119)
原文: Writing a Simple Garbage Collector in C
https://blog.csdn.net/lxw907304340/article/details/44420119
https://sourceware.org/gdb/onlinedocs/gdb/Target-Description-Format.html
malloc: first-fit => https://github.com/jserv/mini-arm-os/blob/master/07-Threads/malloc.c