if(connection == nullptr)
goto fail;
goto fail;
安全编码与未定义行为
未定义行为
缓冲区溢出
查看下面的代码,其输出结果是什么?
1 | int main(){ |
产生了越界访问,导致了未定义行为。建议:
- 不要使用超出边界的指针或数组下标(这不是废话吗)
- 缓冲区溢出问题在CWE的数据库中排名第二
访问已经销毁的局部空间(悬空指针)
这种最常见的就是在一个函数中定义一段空间,然后返回这段空间的首地址。由于这段空间在函数返回后就被销毁,因此这个地址也成为了悬空指针。
超出类型能容纳的范围
查看下面的代码:
1 | int32_t Average(int32_t x, int32_t y){ |
两个整型相加,可能会溢出。建议:
- 确保有符号整数运算不溢出,避免无符号整数运算产生的回绕
有符号和无符号混用
建议:
- 用来表示数值时,不要用char
- 避免混用有符号和无符号数,如果不得不一起用,显式指定转换类型
- 类型转换导致的溢出问题在CWE中排11位,有超过800条漏洞
未定义行为后果
未定义行为的一个典型后果是,由于访问了非法内存,所以可以通过精心设计构造的输入,修改栈中保存的返回地址,从而跳转到黑客想要跳转的区域。
表达式与变量
全局变量初始化
假设我们在a.cpp
中有一个全局变量
1 | int g_x = 1; |
而在b.cpp
中,也有一个全局变量,且使用g_x
进行定义
1 | extern int g_x; |
这里就有了问题,两个不同文件定义的全局变量,其初始化顺序是不确定的。建议:
- 避免全局变量出现初始化顺序依赖的情况
代码度量
代码的组织
- 程序必须为阅读者编写,只是顺便用于机器执行
- 短小的函数总是更简洁、更容易阅读
- 功能单一的函数更容易复用,简洁明了的代码更容易维护
业界指南、规范与工具
业界指南
- 谷歌C++风格指南1,以风格约定与习惯引导为主,不涉及消除安全问题
- SEI CERT C++ Coding Standard2,以消除安全隐患为目的,不涉及风格约定,内容太多,建议作为字典
- C++ Core Guideline3,重点为如何合理应用现代C++特性,这个非常值得一读
工具
编译器
gcc等编译器本身就能检查很多问题,且几乎不会误报,我们应当做到保持编译器警告清零
辅助检查工具
- PC-link Plus:以CERT、MISRA等业界规范作为准则,能检查上千条规则,但是有误报和漏报
- CPPCheck:开源静态检查工具,主要检查未定义行为,以减少误报为设计目标
- 华为云代码检查:代码检查CodeCheck精准定位代码缺陷安全检查华为云 (huaweicloud.com)