栈溢出

栈溢出

 

做一个手机项目,在手机上运行死机,trace出来报错 “栈溢出”,首先修改了函数中较大的数组,超过128的基本上都去掉了,还是挂机, 找了半天,感觉无处下手, 最后测试出一种情况,不挂机,就是 不加载所有的图片,正常了,最后的解决办法是:在一开始进入程序时,不加载所有的图片 ,等程序初始化好了,再开始加载图片,不挂机了 。

 

 

下面是转载别人的一篇文章

 

最近在做一个程序(VC6.0),功能大概有网络通信、数据库、绘图等。测试的时候程序一运行到某个函数就出现此错误,查了很多地方,试了很多解决办法,终于把问题解决了,写个日志提醒一下自己,也希望作为一个普遍解决办法让大家少费工夫(其他编译器也会出现同样的问题)。
    大家都知道,Windows程序的内存机制大概是这样的,全局变量(局部的静态变量本质也属于此范围)存储于堆内存,该段内存较大,一般不会溢出;函数地址、函数参数、局部变量等信息存储于栈内存,VC6中栈内存默认大小为1M,对于当前日益扩大的程序规模而言,稍有不慎就可能出问题。
(动态申请的内存即new出来的内存不在栈中)
    即如果函数这样写:
    void test_stack_overflow()
    {
       char* chdata = new[2*1024*1024];
       delete []chdata;
    }
    是不会出现这个错误的,而这样写则不行:
    void test_stack_overflow()
    {
       char chdata[2*1024*1024];
    }
    大多数情况下都会出现内存溢出的错误,不信在vc6中随便做个程序,调用一下这个函数试式。


    出现栈内存溢出的常见原因有2个:
    1> 函数调用层次过深,每调用一次,函数的参数、局部变量等信息就压一次栈。
    2> 局部静态变量体积太大
    第一种情况不太常见,因为很多情况下我们都用其他方法来代替递归调用(反正我是这么做的),所以只要不出现无限制的调用都应该是没有问题的,起码深度几十层我想是没问题的,这个我没试过但我想没有谁会把调用深度作那么多。检查是否是此原因的方法为,在引起溢出的那个函数处设一个断点,然后执行程序使其停在断点处, 然后按下快捷键Alt+7调出call stack窗口,在窗口中可以看到函数调用的层次关系。

    第二种情况比较常见了,我就是犯了这个错误,我在函数里定义了一个局部变量,是一个类对象,该类中有一个大数组,大概是1.5M。
    
    解决办法大致说来也有两种:
    1> 增加栈内存的数目
    2> 使用堆内存
    增加栈内存方法如下,在vc6种依次选择Project->Setting->Link,在Category中选择output,在Reserve中输入16进制的栈内存大小如:0x10000000,然后点ok就可以了。
  
    其他编译器也有类似的设置,个人认为这不是一个好办法,有一个致命原因,不知道有没有人遇到过,我把栈内存改大后,与数据库建立不了连接了(ADO方式,Acess数据库),把栈内存还原,问题立刻消失。不知道究竟是什么原因,有知道的可以告诉我。
     email: la_ariza@sina.com

     第二种解决办法是比较可行的,具体实现由很多种方法可以直接把数组定义改成指针,然后动态申请内存;也可以把局部变量变成全局变量,一个偷懒的办法是直接在定义前边加个static,呵呵,直接变成静态变量(实质就是全局变量)。即可以把上例中的函数这么写:

    void test_stack_overflow()
    {
       static char chdata[2*1024*1024];
    }


    当然,除非万不得已,尽量不要使用这么大的数组,出现这种情况多半说明程序结构有问题。

### 栈溢出漏洞的原理 栈溢出漏洞是由于程序在运行过程中,向栈中写入的数据超出了栈缓冲区的容量限制所导致的一种安全问题。栈是一种后进先出(LIFO)的数据结构,用于存储局部变量、函数调用的参数和返回地址等信息。当程序错误地修改了栈指针,或者在局部缓冲区中存储了过多的数据时,就会发生栈溢出[^1]。 攻击者可以通过覆盖栈上的返回地址,使程序跳转到他们指定的代码段执行,从而获取对系统的控制权。这种攻击通常利用恶意构造的输入数据,例如通过`gets()`函数读取用户输入时,如果未对输入长度进行限制,就可能导致缓冲区溢出[^2]。 以下是一个简单的栈溢出示例代码: ```c #include <stdio.h> #include <string.h> void input(char *s) { gets(s); // 不安全的函数,容易引发栈溢出 } int main() { char s[10]; input(s); return 0; } ``` 在此代码中,`char s[10]`定义了一个大小为10字节的栈缓冲区。如果输入的字符串长度超过10字节,则会导致栈溢出,覆盖相邻的栈内存区域,包括返回地址[^3]。 --- ### 栈溢出漏洞的修复方法 为了避免栈溢出漏洞,可以采取以下措施: 1. **代码审查**:通过代码审查发现可能导致栈溢出的代码段,并进行修复。例如,避免在栈上分配过大的数组或结构体,确保递归调用有正确的终止条件等[^4]。 2. **使用栈保护技术**:现代编译器和操作系统提供了栈保护技术,如Canary机制。通过在栈上保存一个随机值(Canary值),并在函数返回前检查该值是否被修改,可以检测并防止栈溢出攻击[^4]。 3. **限制递归深度**:对于递归函数,可以设置递归深度的限制,以避免无限递归导致的栈溢出。 4. **优化数据结构**:使用更合适的数据结构来存储数据,可以减少栈空间的使用。例如,可以使用链表代替数组来存储大量数据。 5. **增加栈空间**:在某些情况下,可以通过增加栈空间的大小来避免栈溢出。然而,这并不是一个长期的解决方案,因为过大的栈空间可能会导致其他问题,如内存浪费和性能下降。 6. **使用动态内存分配**:对于需要大量内存的数据结构或算法,可以考虑使用动态内存分配(如堆空间)来存储数据,而不是在栈上分配大量内存。 7. **遵循安全编程实践**:遵循安全编程实践,如输入验证、错误处理和异常处理,可以减少栈溢出的风险。例如,使用`fgets()`替代`gets()`函数,以限制输入长度。 --- ### 示例修复代码 以下是上述栈溢出示例代码的修复版本: ```c #include <stdio.h> #include <string.h> #define MAX_LEN 10 void input(char *s) { fgets(s, MAX_LEN, stdin); // 使用fgets替代gets,限制输入长度 s[strcspn(s, "\n")] = '\0'; // 去除可能存在的换行符 } int main() { char s[MAX_LEN]; input(s); printf("Input: %s\n", s); return 0; } ``` 在修复后的代码中,使用了`fgets()`函数替代`gets()`,并设置了输入的最大长度,从而有效避免了栈溢出问题。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值