1、聊一聊
分享一首非常洒脱自在的歌曲!
本文主要是跟大家分享三种堆栈溢出检测的方法,也算是接着之前分享的堆栈溢出分析的续篇。
2、正文部分
在阅读本文前建议大伙阅读往期文章 :
☞ 【进阶】" 堆栈溢出 ",也就这么回事!
1
堆栈溢出检测
堆栈溢出并不可怕,因为有部分堆栈溢出并不会造成什么影响,比如说局部数组导致堆栈溢出,却又没有篡改溢出部分内存,又或者篡改了相关数据却不会立马让程序奔溃,问题就悄无声息的隐藏着。
所以堆栈溢出有时候是非常让人抓狂的,经常会导致一些奇奇怪怪的问题,对于一般的程序员感觉把任务的堆栈加大了似乎问题就不再复现了,从而暗自欣喜,其实仅仅是bug移动了一个位置罢了,然而拿到客户现场没跑个几天就宕机了。
2
堆栈溢出与数组越界
大家用更加抽象的思维去看待这个问题,堆栈溢出和数据越界或许就区别不大了,其本质上都是超出了数据原本的定义范围,造成程序中重要数据被篡改而引发的问题。
前一篇文章跟大家介绍了堆栈用来干什么、一些什么情况会造成堆栈溢出、以及为了避免堆栈溢出我们如何确定合适的堆栈大小。然而这些还远远不够,仍然有非常多种情况堆栈的实际使用大小是未知的,比如使用可变参数函数,递归算法的处理等等,所以为了能够让所编写的嵌入式软件更加的强大和健壮,那么程序里面有相应机制进行堆栈的溢出检测就变得非常有必要了!
数据越界我们可以通过assert断言来提前检测其数组下标是否超出范围,而对于堆栈的分配和使用却没有这样一个清晰可控的过程,特别是在C环境下进行编码,一般都由C编译器帮我们处理好了,程序员无法直接触及到该过程。
既然正面比较难捕获该异常,那就只能通过间接手段检测,无论做什么事情,必然会留下线索,堆栈溢出也一样。
3
堆栈溢出检测三大法宝
1
软件预设检测法
堆栈说到底就是用来重复使用的内存,并且其规律是固定的,要么向下或者向上增长,使用完以后也不会对其进行清除等等处理,那么这里就为我们分析堆栈的使用情况带来了一丝线索。
软件预设检测法就是在堆栈的末端规定一段内存区域,然后在程序运行前先采用特殊的数值对该部分内存区域进行填充,系统软件定时或者定期的比对这一部分规定的内存区域与所设置的特殊值是否一致,一旦检测不一致大概率说明堆栈已经使用到了末尾(这里为什么说大概率呢?因为你程序中起飞的指针也有可能篡改这一部分数据!),所以也就说明你之前分配的堆栈空间不够。
该方法也是目前前后台程序或者是多线程程序经常使用的软件检测堆栈溢出的办法,特别是目前的RTOS基本上都是采用此办法。
该方法的缺点也非常明显,首先检测需要耗费CPU不少开销,有时候由于较严重的堆栈溢出直接会导致程序卡死而来不及检测,甚至大数组局部变量直接跳过预设值区域,导致堆栈检测失效等问题。
所以对于软件上检测堆栈溢出的不可靠性,各路大佬又想出了非常多改进版本的检测方案,这里暂时不展开,后续再跟大家介绍!
2
堆栈限制寄存器
前面我们说了数组越界可直接检测下标,而堆栈分配前软件上不好实时检测,然而有一部分芯片硬件上为该检测量身定做了一套策略。
原理很简单,通过设置堆栈限制寄存器,每次使用堆栈都会拿实际的堆栈指针与堆栈限制寄存器进行比对,看是否超过了所限制的范围,一旦超过了便会触发相应的异常中断,最终交给用户做最后的处理,用户甚至可以根据当前的寄存器情况追溯到对应的溢出位置从而修护问题。
对于单任务的前后台程序运行或许直接设置堆栈限制寄存器的值可以实时的检测堆栈的使用情况,然而对于多任务系统,每个任务都有其自身的内存堆栈,所以需要在任务切换过程中动态的更新保存堆栈限制寄存器的值,从而实现对每个任务的堆栈监控。
所以拥有了这样的硬件神器或许根本就不需要程序员太过担心堆栈溢出问题导致的后果,不幸的是目前支持堆栈限制功能的CPU太少了,对于MCU而言这样的功能更加是稀缺。
那么bug菌下面便介绍另外一种硬件实施办法!
3
MPU替代法
相对堆栈限制寄存器或许MPU就比较廉价了,目前比较高性能的MCU都会自带MPU功能,然而bug菌可以说80%的嵌入式程序员几乎都不怎么用MPU功能,或者说根本不会用该功能。
什么是MPU呢?英文名字Memory Protection Unit,内存保护单元,当然MPU的缩写还可以表示微处理器的意思,这里就暂时不讨论。
对于内存保护单元简单一点说就是可以通过设置MPU控制器来指定哪些内存在什么情况下可以访问,哪些内存在什么情况下不可以访问。
那再来看看我们堆栈溢出,不正好就是CPU访问了不该访问的地方吗?所以MPU该上阵了。
如上图我们把堆栈的末端一部分内存设置为MPU单元保护区,一旦堆栈分配的内存访问到这部分区域便会触发MPU访问异常中断,从而通知用户进行溢出后的处理工作。
很明显这种MPU方案与第一种软件检测方案几乎是类似的,只是前一种去主动检查该区域是否被修改,而另外一种是通过MPU被动触发异常。但是后者硬件MPU检测是实时的更加降低了漏检的风险。
他们两个都没有第二种方案完美,比如对于大数组的漏检测风险,堆栈区域的浪费等等,整体来说只能说" 够用 ",软件上可以更好的降低漏检风险,毕竟你的堆栈溢出也不会那么凑巧!
3、结束语
好了,本文到此结束!全是bug菌一个字一个字敲的,原创不易!
最近大家应该有发现bug菌更新有点慢,其实手上已经积累了20多个文章主题待写,可是一直迟迟没有动手,原因就不多解释了吧!
我是bug菌,一个走路都带风的男子!
推荐专辑 点击蓝色字体即可跳转
☞ MCU进阶专辑
☞ 嵌入式C语言进阶专辑
☞ “bug说”专辑