我是靠谱客的博主 健康画板,最近开发中收集的这篇文章主要介绍数组越界引起的异常一、引言二、问题追溯三、最后,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

一、引言

      在提供给客户的bin档突然在最新版就不能用了,而客户提供的出错log 跟bin 档本身的功能却一点也没有关系,最后只能一点点回退修改的代码来排查,最终定位的问题确是数组越界导致的异常。

 

二、问题追溯

    在当前的案例中,我们会提供一个bin档给客户,客户使用我们提供的API 接口通过UART 加载外置的DSP。同时为了追溯bin档的版本号,定义了一个接口来获得加载成功之后的版本号。如下面的示例代码:

static int dsp_get_algorithm_version(void)
{
#define MAX_LABLE_SIZE 20
int rc = 0;
uint32 cmd,rsp;
char algo_version[MAX_LABLE_SIZE]={0};
uint8 index = 0;
/*
*
iteratively to retrieve successive Build string characters, which
*
terminates when a 0 binary value is retrieved.
*/
while((char)rsp){
cmd = DSP_GET_NEXT_BUILDLABEL;
rc = dsp_uart_cmd(cmd, &rsp);
if (rc){
LOD_D(("%s(): Set chip cmd fail %dn",
__func__, rc));
goto read_algo_version_error;
}
algo_version[index++] = (char)rsp;
}
LOG_D(("algorithm_version:%sn",algo_version));
read_algo_version_error:
return rc;
}

  该函数预定义了一个20bytes 的数组用来存放通过uart接口获得的版本号,每次读取获得一个字节。在最新的dsp 版本中,版本号改成了超过20字节的字符串,连续的读取导致字符数组越界了,程序死在了莫名奇妙的地方,导致花了很长的时间才找到了问题。

  复盘产生这种问题的原因,一是版本号没有按照事先约定的最大长度来定义;二是上层没有对可能产生的这种错误进行检查和保护,从而事后花很长的时间进行调试,这也体现了《代码大全》中所提到的防御性编程方法。

  回到这个例子,对代码做如下的变动,检测读取的长度是否超过最大长度,如果超过最大长度,做截断处理:

static int dsp_get_algorithm_version(void)
{
#define MAX_LABLE_SIZE 20
int rc = 0;
uint32 cmd,rsp;
char algo_version[MAX_LABLE_SIZE]={0};
uint8 index = 0;
/*
*
iteratively to retrieve successive Build string characters, which
*
terminates when a 0 binary value is retrieved.
*/
while((char)rsp && (index < MAX_LABLE_SIZE)){
cmd = DSP_GET_NEXT_BUILDLABEL;
rc = dsp_uart_cmd(cmd, &rsp);
if (rc){
LOD_D(("%s(): Set chip cmd fail %dn",
__func__, rc));
goto read_algo_version_error;
}
algo_version[index++] = (char)rsp;
}
if(index == MAX_LABLE_SIZE && algo_version[index - 1] != ''){
LOG_W(("exceed algo_version array length : %d"), MAX_LABLE_SIZE);
algo_version[index - 1] = '';
}
LOG_D(("algorithm_version:%sn",algo_version));
read_algo_version_error:
return rc;
}

三、最后

   引用 https://www.cnblogs.com/bakari/archive/2012/08/27/2658215.html提到的防御性编程技巧作为收尾:

1、通过采用良好的编程风格,来防范大多数编码错误
如选用有意义的变量名,或者审慎地使用括号,都可以使编码变得更加清晰了,并减少缺陷出现的可能性。在投入到编码工作之前,先考虑大体的设计方案,这也很关键。
2、不要仓促地写代码
在写每一行时都要三思而后行。可能会出现什么样的错误?你是否已经考虑了所有可能出现的逻辑分支?放慢速度,有条不紊的编程虽然看上去很平凡,但这的确是减少缺陷的好办法。
3、不要相信任何人
不要相信任何人毫无疑问,任何人(包括你自己)都可能把缺陷引到你的逻辑程序当中,用怀疑的眼光审视所有的输入和所有的结果,直到你能证明它们是正确的为止。
下面这些情况可能是给你带来麻烦的原因:
真正的用户意外地提供了假的输入,或者错误地操作了程序;恶意的用户,故意造成不好的程序行为;客户端代码使用的参数调用了你的函数,或者提供了不一致的输入;运行环境没有为程序提供足够的服务;外部程序库运行失误,不遵从你所依赖的接口协议;
4、编码的目标是清晰,不只是简洁
简单就是一种美,不要让你的代码过于复杂。
5、不要让任何人做让他们不该做的修补工作
将所有变量保持在尽可能小的范围内。不到万不得已,不要声明全局变量。如果变量可以声明为函数内的局部变量,就不要再文件范围上声明。如果变量可以声明为循环体内的局部变量,就不要再函数范围上声明。
6、检查所有的返回值
如果一个函数返回一个值,它这样做肯定是由理由的。大多数难以察觉的错误都是因为程序员没有检查返回值而出现的。无论如何,都要在适当的级别上捕获和处理相应的异常。
7、审慎地处理内存(和其他宝贵的资源)
8 、使用安全的数据结构
如果你做不到,那么就安全地使用危险的数据结构。
最常见的安全隐患大概是由缓冲溢出引起的。缓冲溢出是由于不正确地使用固定大小的数据结构而造成的。如
果你的代码在没有检查一个缓冲的大小之前就写入这个缓冲,那么写入的内容总是有可能会超过缓冲的末尾的。
这种情况很容易出现,如下面这一小段C语言代码所示:
1 char *unsafe_copy(const char *source)
2
3 {
4
char *buffer = new char[10];
5
6
strcpy(buffer, source);
7
8
return buffer;
9 }
如果source中数据的长度超过10个字符,它的副本就会超出buffer所保留内存的末尾。随后,任何事都可能会发生。数据出错是最好情况下的结果——一些其他数据结构的内容会被覆盖。而在最坏的情况下,恶意用户会利用这个简单的错误,把可执行代码加入到程序堆栈中,并使用它来任意运行他自己的程序,从而劫持了计算机。这类缺陷常常被系统黑客所利用,后果极其严重。
避免由于这些隐患而受到攻击其实很简单:不要编写这样的糟糕代码!使用更安全的、不允许破坏程序的数据结构——使用类似C++的string类的托管缓冲。
或者对不安全的数据类型系统地使用安全的操作。通过把strcpy更换为有大小限制的字符串复制操作strncpy,就可以使上面的C代码段得到保护。
1 char *safer_copy(const char *source)
2
3 {
4
char *buffer = new char[10];
5
6
strncpy(buffer, source, 10);
7
8
return buffer;
9 }
9、在声明的位置上初始化所有的变量
10、尽可能推迟一些声明变量
使变量的声明位置与使用它的位置尽量接近,从而防止它干扰代码的其他部分。不要再多个地方重用同一个临时变量,变量重用会使以后对代码重新完善的工作变得异常复杂。
11、审慎地进行强制转换
如果你真的想使用强制转换,就必须对之深思熟虑。你所告诉编译器的是:“忘记类型检查吧,我知道这个变量是什么,而你不知道。”你在类型系统中撕开了一个大洞,并直接穿越过去。这样做很不可靠。
12、其他
提供默认的行为
遵从语言的习惯
检查数值的上下限
正确设置常量

 

最后

以上就是健康画板为你收集整理的数组越界引起的异常一、引言二、问题追溯三、最后的全部内容,希望文章能够帮你解决数组越界引起的异常一、引言二、问题追溯三、最后所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(47)

评论列表共有 0 条评论

立即
投稿
返回
顶部