linux下关于栈的一些有意思的地方「stack」

linux C/C++ 开发过程中避免不了对程序栈的使用,函数参数传递、局部自动变量都会在用户空间的栈上占有一席之地。我们都知道linux 我们是可以设置stack大小一般情况下默认是8M,那么我们来验证下,请看下面的代码:(测试的docker实例中ulimit -s 是8192)

#include <stdio.h>
#include <unistd.h>
void test(){
static int i = 0;
char t[1024*256] = { 0 };
printf("t=%p index=%d\\n",&t,++i);
test();
}
int main() {
test();
return 0;
}
/<unistd.h>/<stdio.h>

输出结果:

linux下关于栈的一些有意思的地方「stack」

这段代码主要是为了演示在linux平台下配置了线程栈8M的情况下大概有多少栈内存可以使用,可以看到这里没有分配到8M,因为进程进入到main过程中还有一些调用、递归调用的返回地址等的开销。

有时候可能开发过程中由于bug导致栈溢出,那么我们如何来判断是否是栈溢出导致的core

dump?其实很简单,通过gdb获取到访问的内存地址,然后查看进程对应的栈地址空间映射范围就知道是否是因为栈溢出导致的:

方法1:通过采用pmap pid 这里以pid=35为例

pmap 35
Address Kbytes RSS Dirty Mode Mapping
0000000000400000 4 4 0 r-x-- a.out
0000000000600000 4 4 4 r---- a.out
0000000000601000 4 4 4 rw--- a.out
00007f0869b26000 1804 984 0 r-x-- libc-2.17.so
......
00007f086a935000 4 4 4 rw--- [ anon ]
00007ffe390d7000 8192 16 16 rw--- [ stack ] 00007ffe39170000 8 0 0 r---- [ anon ]
00007ffe39172000 8 4 0 r-x-- [ anon ]
ffffffffff600000 4 0 0 r-x-- [ anon ]

看到mapping是stack一行,通过address + size 得到栈的地址空间范围,然后跟gdb中获取到的地址比对,如果超过了该范围,那就表示栈溢出了。

方法2:通过 proc文件系统看

cat /proc/35/smaps

......
7ffe390d7000-7ffe398d7000 rw-p 00000000 00:00 0 [stack]
Size: 132 kB
........

对应的stack一行就有对应的栈起始、结束地址映射范围。

另外关于栈溢出攻击的案例也很多,那么我们是否有一些方案来避免这些问题的发生呢?方法总比困难多:

1)代码设计时,时刻提醒自己栈、栈、栈合理利用,做好写入字符串与目标内存长度的校验,避免异常溢出

2)是人总会有犯错的时候,不翻车只是时机不到,那么我们可以利用编译器层面在编译时植入一些检查代码来避免这些问题:gcc/g++ 编译时带上参数-fstack-protector(只对局部变量进行检查)、-fstack-protector-all(为所有函数插入检查代码),下面我们来比对下有这个参数和没有这个参数的汇编级代码区别:

linux下关于栈的一些有意思的地方「stack」

Fig 未开启-fstack-protector

linux下关于栈的一些有意思的地方「stack」

Fig 开启-fstack-protector

大家可以对比下可以发现开启了之后多了一些检验的代码,由于开启该选项会多一些指令,性能会有写损耗,所以大家使用时需要评估一下,是否值得引入,利大于弊是建议还是使用。这里摘录 GCC的一个解释:

-fstack-protector
Emit extra code to check for buffer overflows, such as stack smashing attacks. This is done by adding a guard variable to functions with vulnerable objects. This includes functions that call alloca, and functions with buffers larger than 8 bytes. The guards are initialized when a function is entered and then checked when the function exits. If a guard check fails, an error message is printed and the program exits.

-fstack-protector-all
Like -fstack-protector except that all functions are protected.


人从事多年linux后台开发,曾在几家大型互联网做过研发工作,欢迎新手、老鸟在评论区留言交流、探讨学习~


分享到:


相關文章: