和core说再见之:栈溢出

本文介绍栈的概念及其在程序中的作用,详细解析栈溢出的原因及现象,并通过具体实例展示栈溢出的过程,最后提出了解决栈溢出的有效方法。

1. 概述
栈,
就是那些由编译器在需要的时候分配,在不需要的时候自动清除的变量的存储区。里面的变量通常是局部变量、函数参数等;和堆相比,栈通常很小,在linux下,通过ulimit -s可以查看栈的大小。
所谓栈溢出,是缓冲区溢出的一种,本质上是写入栈的数据超过栈的大小,使得数据写入其他单元,往往造成不可预期的后果,最常见的就是程序崩溃。

2. 实例

一个栈溢出的程序:

#include <iostream> using namespace std; //递归函数 void stack_test(int group) { //占用栈内存,1M char a[1024*1024]; //递归调用 group--; if(group > 0) { cout << "stack " << group << endl; stack_test(group); } } int main() { //函数层级 int layer; cin >> layer; //调用递归函数 stack_test(layer); return 0; }
测试

linux 2.6.9xenu_5-0-0-0下输入命令:
$ ulimit -s
10240
表示栈内存限制10M

编译运行程序:
$ ./bin/test
15
stack 14
stack 13
stack 12
stack 11
stack 10
stack 9
stack 8
stack 7
stack 6
stack 5
stack 4
Segmentation fault (core dumped)

core调试:
$ gdb /home/coresave/core.test.1
core.test.17380.1314809919 core.test.19889.1314811895
[work@bb-hm-test09.vm.baidu.com test]$ gdb ./bin/test /home/coresave/core.test.19889.1314811895
GNU gdb Red Hat Linux (6.3.0.0-1.96rh)
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu"...Using host libthread_db library "/lib64/tls/libthread_db.so.1".

Core was generated by `./bin/test'.
Program terminated with signal 11, Segmentation fault.

Reading symbols from /usr/lib64/libstdc++.so.6...done.
Loaded symbols for /usr/lib64/libstdc++.so.6
Reading symbols from /lib64/tls/libm.so.6...done.
Loaded symbols for /lib64/tls/libm.so.6
Reading symbols from /lib64/libgcc_s.so.1...done.
Loaded symbols for /lib64/libgcc_s.so.1
Reading symbols from /lib64/tls/libc.so.6...done.
Loaded symbols for /lib64/tls/libc.so.6
Reading symbols from /lib64/ld-linux-x86-64.so.2...done.
Loaded symbols for /lib64/ld-linux-x86-64.so.2
#0 0x0000000000400abc in stack_test (group=3) at /home/work/zhouxm/test/src/main.cpp:18
18 cout << "stack " << group << endl;

3. 分析
原因:
1)栈中大数组char a[1024*1024];
2)递归函数

两个信息:
1)Program terminated with signal 11, Segmentation fault.
Signal 11, or officially know as "segmentation fault", means that theprogram accessed a memory location that was not assigned. That'susually a bug in the program.

2) core在 cout << "stack " << group << endl;
在栈溢出的时候,并不一定会马上core,这也是为啥你core的地方常常离出问题的代码很远。

4. 解决

1)在需要占用大内存的时候(例如,大数组),别偷懒,乖乖的用堆!
2)尽量避免层次多的递归函数


欢迎拍砖!补充!

### 分析函数栈顶地址变化导致的栈溢出coredump的原因 #### 1. 栈溢出的基本原理 在程序运行过程中,函数调用栈会随着函数的调用返回动态变化。每次函数调用时,系统会在栈上分配一定的空间用于存储局部变量、函数参数、返回地址等信息。当函数栈顶地址(`esp`/`rsp`)的变化导致分配的栈空间超出栈的边界时,就会发生栈溢出。这种溢出可能会覆盖其他函数的返回地址或关键数据,最终导致程序异常终止并生成coredump文件[^3]。 #### 2. 导致栈溢出的主要原因 - **无限递归**:函数无终止条件地递归调用自身,导致栈空间被持续消耗,最终耗尽栈空间。例如,示例代码中的`test()`函数递归调用自身,没有终止条件,会导致栈溢出[^1]。 - **局部变量过大**:在函数内部声明了过大的局部数组,例如`char tmp[512]`,如果函数调用层级较深,可能导致单次调用栈空间不足。 - **缓冲区溢出**:向固定大小的缓冲区写入超出其容量的数据,多余的数据会覆盖栈上的其他信息,如函数的返回地址或其他局部变量。 #### 3. 栈溢出导致coredump的表现 当发生栈溢出时,最直接的表现是程序崩溃并生成coredump文件。此时,栈指针(`esp`/`rsp`)可能指向了非法地址,或者栈帧结构被破坏,导致程序无法正常返回。在coredump文件中,通过分析栈指针的变化函数调用上下文,可以判断是否是栈溢出导致的问题[^4]。 #### 4. 使用GDB分析栈溢出问题 - **加载coredump文件**:使用`gdb`加载可执行文件coredump文件,例如: ```bash gdb -ex run --args ./your_program core ``` - **查看栈帧信息**:使用`bt`命令查看函数调用栈,检查是否有异常的栈帧或递归调用。 - **检查栈指针变化**:通过`info registers`查看`esp`/`rsp`的值,并结合栈帧的布局分析栈指针的变化是否异常。 - **分析局部变量布局**:例如,在引用中提到的`wrapper2`函数,通过分析局部变量的空间分配返回地址的位置,可以判断栈溢出是否发生在特定函数调用时[^5]。 #### 5. 解决栈溢出问题的方法 - **避免无限递归**:确保递归调用有明确的终止条件,或者改用迭代方式实现。 - **减少局部变量大小**:避免在栈上分配过大的数组,可以考虑使用动态内存分配(如`malloc`/`new`)。 - **使用编译器保护机制**:启用栈保护机制,如`-fstack-protector`选项,可以在栈溢出发生时检测到并终止程序,避免进一步的破坏。 - **调整栈大小限制**:对于多线程程序,可以通过`ulimit -s`调整栈大小,或者在创建线程时指定更大的栈空间。 - **静态代码分析**:使用静态分析工具(如`Coverity`、`Clang Static Analyzer`)检测潜在的栈溢出风险。 #### 6. 示例:递归导致栈溢出的修复 针对示例代码中的递归问题,可以通过引入递归深度限制来修复: ```cpp #include <iostream> void test(int depth) { if (depth <= 0) return; char tmp[512]; // 申请512字节的栈空间 test(depth - 1); // 递归调用 } int main() { test(1000); // 设置递归深度限制 return 0; } ``` 通过引入深度限制,可以有效避免栈溢出问题[^1]。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值