“溢出”这个词很生动,水满则溢,前面说过栈就象一个容器,容器装满了,还要往里装东西,当然就会溢出了。
有两种不同情景都被称为栈溢出,一种是栈中的数据被越界覆盖,wiki中称这种情况为stack buffer overflow。一种常用的黑客攻击手段--栈溢出攻击,就是通过栈越界访问,用事先设计好的数据覆盖正常栈里的内容。比如把保存函数返回地址的栈内存用某段黑客代码的地址覆盖,函数结束时不能正确返回,而是顺着被篡改的地址跳到黑客代码处,攻击者从而获得系统控制权,执行一些非法操作。这属于一种有意识的越界访问,但并没有真正溢出栈的存储空间的边界,只是破坏了栈内的数据。
而嵌入式软件需要注意另一种真正的栈溢出:所有栈空间都被填满,即真正的stack overflow。因为栈空间说到底就是一块固定大小的内存,当程序不断压栈,以至超出了系统预留的栈空间,压栈操作就可能越界覆盖其它内存功能模块,这就是真的栈溢出。
栈溢出或者说占用过多主要有几种原因:
a. 递归函数调用
函数调用过程需要保存当前地址,传入的参数等,递归函数不断调用自身,直到某条件满足,是非常消耗栈的一种方式,因此嵌入式系统中一般不使用递归函数。
b. 过大的局部变量
嵌入式平台下栈的大小有限,所以大的缓存应尽量分配到堆内存,避免大尺寸的局部数组。可以对照Wiki的解释(简化):
Stack overflow mean:
Stack overflow, when too much memory is used on the call stack
Stack buffer overflow, when a program writes to a memory address on the program's call stack outside of the intended data structure, usually a fixed length buffer.
A stack overflow occurs when too muchmemory is used on the call stack. The call stack contains a limited amount of memory. The size of the call stack depends on many factors, including the programming language, machine architecture, multi-threading, and amount of available memory. When too much memory is used on the call stack the stack is said to overflow。This is usually caused by one of two types of programming errors.
a. Infinite recursion
The most common cause of stack overflows is excessively deep or infinite recursion.
An example of infinite recursion inC language: main() { main(); } .The main function calls itself until the stack overflows resulting in a segmentation fault.
b. Very large stack variables
The other major cause of a stack overflow results from an attempt to allocate more memory on the stack than will fit. This is usually the result of creating local array variables that are far too large. For this reason arrays larger than a few kilobytes should be allocated dynamically instead of as a local variable.
Stack overflows are made worse by anything that reduces the effective stack size of a given program. For example, the same program being run without multiple threads might work fine, but as soon as multi-threading is enabled the program will crash. This is because most programs with threads have less stack space per thread than a program with no threading support.
程序移植过程中两种情况容易发生真正的栈溢出:1)windows上运行正常的程序,移植到嵌入式平台后发生奇怪错误:如跟踪某函数体内的return结果明明为0,但返回上一层函数后就突然变成另外的值,这多数就是由于大数组导致了栈溢出。2)程序从单线程移植到多线程环境,容易栈溢出,因为多线程往往对栈空间限制得更严格,这里涉及另一对概念,系统栈与任务栈,后文再续。