你所不知道的C語言:函數調用篇(續)

从递归学习 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

你所不知道的C语言:函数调用篇(续)

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

你所不知道的C语言:函数调用篇(续)


分享到:


相關文章: