美文网首页
第15章 编写零漏洞代码所需编码准则

第15章 编写零漏洞代码所需编码准则

作者: FelixDai | 来源:发表于2019-08-10 16:36 被阅读0次

15.1 数组下标应从0开始

指定数组下标时出现的失误可能打乱整个数据集合。特别是编写循环语句时,更要多加留意。

15.2 置换字符串时必须使用括号

宏函数中的括号

15.3 文件必须有开就有关

程序打开相应文件后没有关闭,会造成“无法打开文件”错误。

15.4 不要无视编译器的警告错误

如果程序本身有语法错误,编译器会在编译过程种发现并提示其存在。

  1. 致命错误(fatel error)
    通常指如果不修复错误,程序就无法运行
  2. 警告错误(warning error)
    这种错误可能在程序运行过程种并不会引起什么大问题,或者根本不会产生问题。修复这种问题意味着修复全部程序漏洞。

15.5 掌握并在编码时防止运行时错误

程序运行过程种出现的错误称为运行时错误,这种错误不同于编译错误和逻辑错误。

编译错误主要是语法问题引发的,逻辑错误主要是程序逻辑或算法的设计缺陷引发的,而运行时错误则与运行时环境紧密相关。

下面详细降解其中两个最具代表性的运行时错误。这两种错误非常常见,只要能够避免二者的发生,就可以编写相当稳定的程序。

15.5.1 栈溢出

计算机用栈这种数据结构管理临时存储空间。程序中使用的自动变量在被声明的同时就被保存到栈,而一旦脱离自动变量使用范围,就会被栈释放。

变量在栈中随时保存或释放,所以没有必要将栈设为无限大。各操作系统都对栈的大小进行了限制,虽然这种限制程度略有放宽,甚至一部分操作系统允许用户自主控制并调整栈大小,但处理大容量数据时仍可能发生栈溢出。

栈溢出常见于使用大数组或调用递归函数的情况。因此,使用递归调用时,一定要细致检查出现栈溢出的可能性。特别是并未准确限制递归调用的次数,而递归只有在满足某个条件时才会终止的情况下,这种检查更为必要。

15.5.2 除以0

没有程序员会故意用0除某值,但在非常复杂的代码中,可能出现除数偶然为0的情况。

另一种情况常见于控制语句。试想,在for或while语句中,用计算循环次数的变量(计数器)作为除数,与其他变量值做除法运算。难道没有计数器为0的情况吗?倒序计数时又会怎样呢?这种情况大多潜藏在复杂逻辑中,很难把握。这就要求程序员清醒认识到,除以0的情况随时可能出现,并为防范这种情况的出现而细致检查程序所有可能的运行情况。

15.6 用静态变量声明大数组

C语言根据变量的生存周期、影响范围和存储位置的不同,将变量分为几类,如下表所示:

变量修饰符 变量名 存储位置
extern 外部变量 存储于堆
static 静态变量 存储于堆
auto 自动变量 存储于栈
register 寄存器变量 存储于CPU的寄存器

自动变量存储于以栈形态管理数据的内存。嵌入式系统等部分常用操作系统中,规定的栈比较小。因此,如果一个程序用到的所有自动变量的总和超出栈的大小,就会触发栈溢出,进而导致程序异常终止。

只要数组本身可鞥占据大量栈空间,就最好将数组声明为静态变量。这样可以保证有足够的空间,将数组之外的其他变量声明为自动变量并正常使用。

但管理静态变量与模块化原则相冲突,所以应该慎重对待是否声明静态变量的问题。

15.7 预留足够大的存储空间

15.8 注意信息交换引发的涌现效果

程序单元之间进行信息交换的过程中,可能出现信息丢失,也可能新增冗余信息,这一点多少可以预见,并成为广为人知的安全问题。这种问题可以通过在各程序单元中检查信息有效性得到解决,也就是用检查输入数据长度的方式校验。

但即使如此,系统层面仍可能出现问题。因为程序单元之间进行信息交换的过程中,可能引发涌现性。涌现性指的是能够引起意想不到的效果的性质,是复杂系统相关研究领域非常常见的词汇。程序单元之间通过交换包括数据、文字消息在内的信息形式联系在一起,之后,其组成的系统单元或上层系统都具有复杂系统相似的特性。

那么如何预防这种涌现性现象引起的问题呢?目前还没有可行的对策,但存在一种沿用至今的解决方式,即系统层面的综合测试方法。例如,将包含10各相互联系的程序单元称为综合系统,首先在这一层级进行综合测试;然后将这种系统聚集为整个软件系统,在该层级再次进行综合测试;之后将其应用于实际业务,在人工交互阶段再次进行严格的综合测试。以这样严格的综合测试为基础,可以在一定程度上发现并预防涌现性现象,但这要求在测试过程中投入与软件开发过程同样多的资源。

相关文章

网友评论

      本文标题:第15章 编写零漏洞代码所需编码准则

      本文链接:https://www.haomeiwen.com/subject/kxlpdctx.html