0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

数组越界的问题解析

鱼鹰谈单片机 来源:鱼鹰谈单片机 2023-04-17 09:15 次阅读

数组越界问题大家在软件开发过程中应该都司空见惯了。如果你没见过,大概率是一个新手,工作经验不足,倒不是说你自己会生产这种 BUG,但有些同事却可能是 BUG 搬运工。

在鱼鹰五年的工作开发过程中,除了在北京刚毕业那会没遇到这种隐藏问题(碰到的都是自己生产的 BUG,不过自产自销,也还行),在深圳的这几家公司都遇到了数组越界的问题。

问题一

第一个问题是关于串口驱动导致的越界(最终结果是 hardfault),这个鱼鹰在以前的笔记中也反复强调了,因为这个问题差点导致自己熬了一个通宵,也是醉了(老代码的一个 bug)。

当然这个问题的解决和当时没有在线调试环境(当时的 PCB 板子通过串口烧录代码,没有调试接口,大坑)有很大关系,否则解决起来会快不少。

当然当时鱼鹰也没掌握这个方法《BUG 终结者,现场抓获!|颠覆认知》,否则出现问题时,这种小问题分分钟定位它。

所以当时解决这个问题,全靠玄学:运气。

否则这个问题不知道要蹂躏鱼鹰多少天。

问题二

这个问题在前东家遇到。当时的环境是 boot + app 形式。boot 代码也是跑了多年的老代码,从来没有出现过问题。

直到有一次版本升级,发现程序不能跳转到 app 正常运行(具体细节不记得了)。

当时有同事怀疑是我当时更新的 printf 打印函数有关系,因为当时的版本更新有这个改动。但鱼鹰对自己写的代码还是比较有自信的,并且我的 printf 改动和 app 跳转能有什么关系。

但怀疑到你头上了,同时鱼鹰也经常负责定位这类疑难杂症,刚好空闲,那就去瞧瞧看了,证明一下这不是你的问题。

因为问题 100% 复现,又掌握了那个现场抓获的技巧,很快就定位到是 boot 的一段代码申请的栈数组空间不足,导致被调用的函数使用这块空间时越界了。

类似下面这种:

func2(uint8_t*buff)
{
i=5;
buff[i]= 0;
}
fun1()
{
uint8_t buff[4];
func2(buff);
}

当然实际代码肯定不可能这么简单,i 的值是变化的,不可能一眼看出。

这个问题也是导致 hardfault(退出 func2 时,破坏了返回地址)。

看到没有,有时候二分法(二分查找有问题的代码提交)查找问题也不是那么可靠,因为问题可能根本不在提交的的代码中。

而下面的问题三也证明了这一点(当然不是说二分法没用,只是不能全靠它作为你的结果判断)。

问题三

这个问题是现东家遇到的问题。

自己开发的一个新模块,当合并到主分支时,发现开机必定 hardfault,这让我百思不得其解。自己新加入的代码,都没用到数组,怎么会hardfault。

我的第一反应就是,不是我的锅。

但问题出现在我合并的过程,也只能由我定位了。还好经验丰富,一天时间+加班几个小时,总算是定位到了。

这个问题定位有几个难点:

1、使用 C++

2、使用O2 优化,而使用 O0 的方式问题不复现了(最蛋疼)

3、使用了 map 库函数

因此在复现率很高的情况下,还是花了这么多时间。

但好在顺利解决了(这么高的复现率,定位root case只是时间问题,信心也是 100%)。

简单来说,是以前的一段代码在使用 sprintf 时(这里强烈建议用 snprintf),导致栈缓存空间越界,然后导致上一层函数的局部变量被篡改,而这个局部变量会导致 map 传入的参数有问题,最终导致了 hardfault 。

可以看到,虽然根因在一个函数中,但最终出现问题却可能在另一个函数中。

就像犯罪现场,作案现场只有一个(root case),但可能案发现场并不是作案现场。

因此解决 bug 过程其实就是警察破案,通过蛛丝马迹找到第一作案现场,如此才能正确破案。

而这种代码在工程里面有好几处.....并且在合入我的代码之前,运行良好。所以,数组越界也不一定会 hardfault,就看你破坏的是啥了。

为什么?

大家很奇怪,为毛数据越界大部分情况下会 hardfault,有时却不会产生问题。只有思考到更深层的原因,你才能在 BUG 环绕中有所成长。

这个时候,就看你的基础扎实不扎实了。

这里来个简单示意函数(优化O0)

void func2()
{
inti= 0;
intbuff[4];
 
 buff[4] = 0;
}
voidfunc1()
{
intj=0;//假设该局部变量使用r4
func2();
}

栈空间如下(因为只有 4 个字,编译器可能 buff[4] 直接使用寄存器了,但为了简单说明,这里假设 buff 都使用了栈):

552128a6-dcbc-11ed-bfe3-dac502259ad0.png

从上图我们可以知道,进入 func2 函数时,先 push,离开时 pop。

局部变量 i 使用 r4 寄存器,但是栈空间 r4 保存的是 func1 使用的j的值。

因此,当我们数组越界时(一般越界是往高地址,因为数组索引一般是自加),很容易破坏上一个函数的栈空间,在这里破坏的是 j 的值。如果 j 很重要,那么很可能会导致 hardfault 或者其它问题(能引起 hardfault 反而是好事)。

并且这里面还有重要的返回地址 lr,如果这个值被越界破坏,那么大概率都是hardfault,因为你企图跳转到一个不存在的地址执行。

数组越界是一个很危险的 BUG,能观察到现象还好,万一是默默破坏而不能很快被察觉,成为一个隐藏 BUG,那才是最危险的。

那为啥问题三增加别的代码会触发这个 BUG ,修改优化等级又会消失呢?

这和编译器有关系,有可能你的代码导致有问题的代码使用了不同的内存布局,从而越界篡改的位置变成了重要的内存,因此出现了现象,而优化等级对栈内存布局更是有很大影响。

另外本篇笔记介绍的局部缓存数组的越界,实际上还有全局数组的越界,那种问题相对简单许多,看 map 文件即可。

因此,操作数组时,一定要时时刻刻检测数组的索引的大小,以防越界。





审核编辑:刘清

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 寄存器
    +关注

    关注

    31

    文章

    5258

    浏览量

    119398
  • C++语言
    +关注

    关注

    0

    文章

    147

    浏览量

    6937
  • 数组越界
    +关注

    关注

    0

    文章

    2

    浏览量

    5518
  • printf函数
    +关注

    关注

    0

    文章

    31

    浏览量

    5868

原文标题:数组越界是一颗隐形炸弹

文章出处:【微信号:emOsprey,微信公众号:鱼鹰谈单片机】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    越界智能监测摄像机

    随着科技的不断发展,越界智能监测摄像机在安防领域得到了广泛应用。这种智能监测设备结合了图像识别技术和人工智能算法,旨在实现对区域内物体的越界行为进行监测和警示。通过高清晰度的摄像头捕捉到场景中的物体
    的头像 发表于 09-05 11:06 128次阅读
    <b class='flag-5'>越界</b>智能监测摄像机

    labview字符串数组转化为数值数组

    在LabVIEW中,将字符串数组转换为数值数组是一项常见的任务,尤其是在处理数据采集、信号处理或用户输入时。 1. 理解LabVIEW的数据类型 在开始之前,了解LabVIEW中的数据类型是非
    的头像 发表于 09-04 17:47 608次阅读

    深入探索KUKA KRL中的数组应用

    如果 CHAR 类型数组的所有数组元素都拥有相同的字符串,则不必单独初始化每个数组元素。忽略右侧的数组下标。(对于一维数组下标,不写下标。)
    的头像 发表于 04-18 10:37 934次阅读
    深入探索KUKA KRL中的<b class='flag-5'>数组</b>应用

    PHP中数组的使用方法!

    PHP中数组的使用方法! PHP是一种广泛使用的网络编程语言,它的数组功能非常强大且灵活。数组是一种数据结构,它允许我们在单个变量中存储多个值。 在本篇文章中,我将详细解释PHP数组
    的头像 发表于 01-12 15:11 424次阅读

    光耦失效的几种常见问题解析

    光耦失效的几种常见问题解析  光耦失效是一个常见的问题,特别是在电子设备中经常使用光耦进行隔离和信号传输的情况下。下面将详细介绍一些光耦失效的常见问题以及解析。 1. 输出信号弱或无输出 有时,光耦
    的头像 发表于 12-25 14:30 5025次阅读

    数组和指针不相同吗?数组和指针有哪些区别

    数组就是指针,指针就是数组,这样的言论在评论区看到不下于10次。
    的头像 发表于 12-13 16:34 1005次阅读
    <b class='flag-5'>数组</b>和指针不相同吗?<b class='flag-5'>数组</b>和指针有哪些区别

    数组与指针不能混用的情况

    数组与指针不能混用的情况  数组与指针是 C/C++ 中非常常见的特性和概念。然而,在某些情况下,数组与指针是不能混用的。这种情况通常涉及到数组的内存分配和
    的头像 发表于 12-07 13:46 505次阅读

    C语言求数组长度的5种方法

    [0]);printf("Arraylength:%d\n",length);return0;}解析:sizeof(array)返回整个数组的字节大小,sizeo
    的头像 发表于 11-19 08:00 2474次阅读
    C语言求<b class='flag-5'>数组</b>长度的5种方法

    Linux程序内存越界定位分析

    。经分析发现是库源代码里一全局数组内存地址大面积越界到其他全局数组了。 问题现象:现象为触发某个业务条件,将导致程序逻辑运行不正常,异常log如下图,可看出“g_s32MaxFd”变量的值(文件句柄)被置0,正常情况应该是大于0
    的头像 发表于 11-13 11:30 377次阅读
    Linux程序内存<b class='flag-5'>越界</b>定位分析

    什么是数组数组有什么用?数组的使用方法

    数组(Array)是有序的元素序列。
    的头像 发表于 11-08 14:58 1380次阅读
    什么是<b class='flag-5'>数组</b>?<b class='flag-5'>数组</b>有什么用?<b class='flag-5'>数组</b>的使用方法

    燃爆!煤矿安全,人员越界识别的必要性!

    人员越界识别AI算法作为智慧矿山AI算法的一大创新,应用于煤矿安全管理中,通过实时监测和预警,有效避免人员越界引发的事故,提升煤矿的安全性和生产效率。
    的头像 发表于 10-30 15:03 294次阅读

    声明数组语法及应用案例

    数组是相同数据类型的元素的集合。支持固定长度或可变的一维和多维数组。简单来说:数组是指将相同数据类型的标签的连续集合体用一个名称表示。
    的头像 发表于 10-23 09:41 474次阅读
    声明<b class='flag-5'>数组</b>语法及应用案例

    对于一个数组为什么越界使用会导致MCU的程序跑飞呢?

    对于一个数组为什么越界使用会导致MCU的程序跑飞呢
    发表于 10-13 06:01

    数组的定义 什么是数组

    数组 数组是内置类型,是一组同类型数据的集合,它是值类型,通过从0开始的下标索引访问元素值。 在初始化后长度是固定的,无法修改其长度。当作为方法的参数传入时将复制一份数组而不是引用同一指针。
    的头像 发表于 10-09 09:39 1749次阅读

    数组越界访问可能会导致什么情况?

    数组越界访问可能会导致什么情况
    发表于 10-09 07:21