解决递归调用栈溢出的方法是通过尾递归优化,事实上尾递归和循环的效果是一样的,所以,把循环看成是一种特殊的尾递归函数也是可以的。
尾递归,在函数返回的时候,调用自身本身,并且,return语句不能包含表达式。这样,编译器或者解释器就可以把尾递归做优化,使递归本身无论调用多少次,都只占用一个栈帧,不会出现栈溢出的情况。
扩展资料
针对堆栈溢出可能造成的计算机安全问题,通常有以下这些防范措施:
1、强制按照正确的规则写代码。
2、通过 *** 作系统使得缓冲区不可执行,从而阻止攻击者植入攻击代码。但由于攻击者并不一定要通过植入代码来实现攻击,同时linux在信号传递和GCC的在线重用都使用了可执行堆栈的属性,因此该方法依然有一定弱点。
3、利用编译器的边界检查来实现缓冲区的保护。该方法使得缓冲区溢出不可能出现,完全消除了缓冲区溢出的威胁,但代价较大,如性能速度变慢。
4、程序指针完整性检查,该方法能阻止绝大多数缓冲区溢出攻击。该方法就是说在程序使用指针之前,检查指针的内容是否发生了变化。
参考资料来源:百度百科-堆栈溢出
参考资料来源:百度百科-栈溢出
* 1. 学习FreeRTOS的任务栈溢出检测方法一(模拟栈溢出)。 * 2. FreeRTOS的任务栈溢出检测方法一说明: * a. FreeRTOSConfig.h文件中配置宏定义: * #define configCHECK_FOR_STACK_OVERFLOW 1 * b. 在任务切换时检测任务栈指针是否过界了,如果过界了,在任务切换的时候会触发栈溢出钩子函数。 * void vApplicationStackOverflowHook( TaskHandle_t xTask, * signed char *pcTaskName ) * 用户可以在钩子函数里面做一些处理。本实验是在钩子函数中打印出现栈溢出的任务。 * c. 这种方法不能保证所有的栈溢出都能检测到。比如任务在执行的过程中发送过栈溢出。任务切换前 * 栈指针又恢复到了正常水平,这种情况在任务切换的时候是检测不到的。又比如任务栈溢出后,把 * 这部分栈区的数据修改了,这部分栈区的数据不重要或者暂时没有用到还好,如果是重要数据被修 * 改将直接导致系统进入硬件异常。这种情况下,栈溢出检测功能也是检测不到的。 * d. 本实验就是简单的在任务vTaskUserIF中申请过大的栈空间,模拟出一种栈溢出的情况,溢出后触 * 发钩子函数,因为我们将溢出部分的数据修改了,进而造成进入硬件异常。 #define configCHECK_FOR_STACK_OVERFLOW 1 /* ********************************************************************************************************* * 函 数 名: StackOverflowTest * 功能说明: 任务栈溢出测试 * 形 参: 无 * 返 回 值: 无 ********************************************************************************************************* */ static void StackOverflowTest(void) {int16_t i uint8_t buf[2048] (void)buf/* 防止警告 *//* 1. 为了能够模拟任务栈溢出,并触发任务栈溢出函数,这里强烈建议使用数组的时候逆着赋值。 因为对于M3和M4内核的MCU,堆栈生长方向是向下生长的满栈。即高地址是buf[2047], 低地址 是buf[0]。如果任务栈溢出了,也是从高地址buf[2047]到buf[0]的某个地址开始溢出。 因此,如果用户直接修改的是buf[0]开始的数据且这些溢出部分的数据比较重要,会直接导致 进入到硬件异常。 2. 栈溢出检测是在任务切换的时候执行的,我们这里加个延迟函数,防止修改了重要的数据导致直接 进入硬件异常。 3. 任务vTaskTaskUserIF的栈空间大小是2048字节,在此任务的入口已经申请了栈空间大小 ------uint8_t ucKeyCode ------uint8_t pcWriteBuffer[500] 这里再申请如下这么大的栈空间 -------int16_t i -------uint8_t buf[2048] 必定溢出。*/for(i = 2047i >= 0i--){ buf[i] = 0x55vTaskDelay(1) } }/* ********************************************************************************************************* * 函 数 名: vApplicationStackOverflowHook * 功能说明: 栈溢出的钩子函数 * 形 参: xTask 任务句柄 * pcTaskName 任务名 * 返 回 值: 无 ********************************************************************************************************* */ void vApplicationStackOverflowHook( TaskHandle_t xTask, signed char *pcTaskName ) {printf("任务:%s 发现栈溢出\r\n", pcTaskName) } 实验目的: * 1. 学习FreeRTOS的任务栈溢出检测方法二(模拟栈溢出)。 * 2. FreeRTOS的任务栈溢出检测方法二说明: * a. FreeRTOSConfig.h文件中配置宏定义: * #define configCHECK_FOR_STACK_OVERFLOW 2 * b. 在任务切换时检测任务栈指针是否过界了,如果过界了,在任务切换的时候会触发栈溢出钩子函数。 * void vApplicationStackOverflowHook( TaskHandle_t xTask, * signed char *pcTaskName ) * 用户可以在钩子函数里面做一些处理。本实验是在钩子函数中打印出现栈溢出的任务。 * c. 任务创建的时候将任务栈所有数据初始化为0xa5,任务切换时进行任务栈检测的时候检测末尾 * 的16个字节是否都是0xa5,通过这种方式来检测任务栈是否溢出了。相比方法一,这种方法的速度 * 稍慢些,但是这样就有效的避免了方法一里面的部分情况。不过依然不能保证所有的栈溢出都能检测 * 到,比如任务栈末尾的16个字节没有用到,即没有被修改,但是任务栈已经溢出了,这种情况是检 * 测不到的。另外任务栈溢出后,任务栈末尾的16个字节没有修改,但是溢出部分的栈区的数据修改 * 了,这部分栈区的数据不重要或者暂时没有用到还好,如果是重要数据被修改将直接导致系统进入硬 * 件异常。这种情况下,栈溢出检测功能也是检测不到的。 * d. 本实验就是简单的在任务vTaskUserIF中申请过大的栈空间,模拟出一种栈溢出的情况,溢出后触 * 发钩子函数,因为我们将溢出部分的数据修改了,进而造成进入硬件异常。 #define configCHECK_FOR_STACK_OVERFLOW 2 函数内容和上面一样: static void StackOverflowTest(void) void vApplicationStackOverflowHook( TaskHandle_t xTask, signed char *pcTaskName )gcc的一个编译选项:-fstack-protector,以下是关于这个选项的描述:-fstack-protector
启用该选项后编译器会产生额外的代码来检测缓冲区溢出,例如栈溢出攻击。这是通过在有缺陷的函数中添加一个保护变量来实现的。这包括会调用到alloca的函数,以及具有超过8个字节缓冲区的函数。当执行到这样的函数时,保护变量会得到初始化,而函数退出时会检测保护变量。如果检测失败,会输出一个错误信息并退出程序。
!注意:在Ubuntu 6.10以及之后的版本中,如果编译时没有指定-fno-fstack-protector, -nostdlib或者-ffreestanding选项的话,那么这个选项对于C,C++,ObjC,ObjC++语言默认是启用的。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)