(Owed by: 春夜喜雨 http://blog.csdn.net/chunyexiyu)
前言:
stack:
栈,也常常称作堆栈;
stack vs heap:
栈(堆栈)与堆,从stack翻译来看一叠,一摞,一堆,感觉和heap翻译差不多,不过heap表达的是凌乱的一堆,stack表达的也是一堆,但是一层层叠放的、一摞一摞的,有规律的一堆:-)
使用时也确实和他们的翻译很新近,stack是一层层的,heap呢?具体分配随不同程序各不相同,随逻辑分支不同也会各不相同,确实有点零乱呢。
stack overflow
堆栈溢出,这个可是一个非常常见的错误。大名鼎鼎的stackoverflow网站,可是一个查找程序问题的好去处。也侧面说明了stack overflow错误真的很有代表性啊。
触发stack overflow
什么方法可以触发overflow呢?对于overflow来说,溢出了;如果整体是杯子的话,满溢的情况可以算作溢出了;从局部角度看,一层层函数杯子的杯子山,一个杯子漏到另一个杯子也可以算作溢出了。基于这两个方向可以触发溢出。
对于stack而言,通常程序预留的空间是有限的,常见缺省2M/8M/…,不同系统编译程序缺省可能不同吧,想从满了的方式溢出的话,可以在栈里多放些东西让它溢出。想从局部漏了/损坏的角度让它异常溢出,那就是局部用超了,或是把程序的堆栈破坏了,从而让它溢出。
空间满溢
从满的方式让它溢出,就是把占用stack的东西多放一些,已知的话
- 全局变量是不占stack的,编译程序时初始化的全局变量放在.data段,未初始化的全局变量方法.bss段。
- 编译出的代码也是不占stack的,放入在.code段里。
- new/malloc分配空间,是从heap上分配的。(new repalcement方式除外)
- 局部变量是初始化放在stack里面的,占用stack空间。
- 函数参数是放在stack里面的,占用stack空间。
- 函数调用时上层代码指针eip是必须要放入stack里面的。
- 函数调用时堆栈检查/gs校验信息是要放入stack里面的。
- 等等
总体来看,主要两块编码可控制部分占用堆栈:一块是局部变量初始化占用空间,一块是函数调用占用空间。
例子1:局部变量占用空间过大导致溢出
#include <stdio.h>
int main()
{
int a[2*1024*1024] = {0};
printf("length of a: %zd\n", sizeof(a));
return 0;
}
上面程序在mac-shell上执行报错:
Segmentation fault: 11
例子2:程序调用过多导致溢出
#include <stdio.h>
void subfunc(int n)
{
if (n > 0)
{
subfunc(n-1);
}
}
int main()
{
int n = 300000;
printf("call subfunc levels\n", n);
subfunc(n);
return 0;
}
上面程序在mac-shell上执行报错:
call subfunc levels: 300000
Segmentation fault: 11
stack异常溢出
stack异常溢出算是一种局部异常溢出,它代表的是某个函数使用栈超出了编译器预先分配的范围,影响到了上层函数的分配空间内容,也或者把堆栈给破坏掉了,上层堆栈例如代码指针eip信息给改了。
在当前编译器里,通常已经不在保存上层函数的栈指针了,而是使用编译时预先计算函数要使用的栈空间大小,进入函数后,调整偏移量来预留出函数使用的栈空间。
这样就意味着函数预留栈空间是编译时计算好了的,不能用超啊,超了就算溢出了。
使用vs时,编译参数里的/gs检查就是来查“超员”的,在分配给函数的空间地址靠近栈低一侧最近的地方记个标记,一会执行完函数时检查这个标记是否被改掉了,改掉了说明函数肯定就用超空间了。这个标记主要是检查不要向栈低用超,毕竟栈低是已分配的部分,已分配给别人的用超,肯定要管的;栈顶用超是不被检查的,毕竟还没分配,一会函数退出时就丢掉了。
堆栈指向溢出的话,通常就是堆栈被破坏导致的溢出,常见的错误例如有/gs检查堆栈溢出,也有代码指针指向错误类的。
栈的空间分配:从高地址向低地址分配,意味着最开始的main函数在高地址,之后函数依次向低地址进行分配使用;函数向高地址用的超出函数分配地址范围,就会形成踩踏,踩踏了上层函数的堆栈空间,形成堆栈踩踏。
低地址… … … 高地址
栈顶 … … … … 栈低
也写一个简单的局部溢出例子吧:
#include <stdio.h>
void testFunc()
{
int* p1 = NULL;
int** pp1 = &p1;
for (int i=3; i<8; i++){
*(pp1 + i) = 0;
}
}
int main()
{
testFunc();
}
执行结果:
Segmentation fault: 11
(Owed by: 春夜喜雨 http://blog.csdn.net/chunyexiyu)