从历史上最昂贵的空指针崩溃,论指针判空的重要性
从历史上最昂贵的空指针崩溃,论指针判空的重要性
2024年7月18日,美国信息安全巨头CrowdStrike推送的软件更新存在严重错误,导致全球范围内大量使用其软件的Windows电脑集体陷入“蓝屏”状态,无法正常使用。这场波及多个行业的“蓝屏”事件,不仅使多国航空、铁路、银行等基础设施服务中断,例如,特斯拉工厂生产因此暂停,马斯克称其为“史上最大IT失败”(Biggest IT fail ever),以此为导火索,特斯拉市值下跌了319.87亿美元。
这次蓝屏的罪魁祸首,就是指针未判空导致的。从蓝屏的Dump信息中可以看到:
尝试读取000000000000009c导致访问无效内存,9c是结构体或类的偏移地址,实际上在访问结构体/类的时候,没有对结构体/类进行判空导致的。
对于C++来说,空指针异常是最常见的程序错误。一些现代的安全的编程语言,例如Rust,已经可以彻底规避空指针异常问题;而一些支持空安全的语言,例如Kotlin、Swift、Dart等,也能很好地引导开发者规避空指针异常问题。
而且在最新的C++标准草案中,也没有看见对空安全的支持,由此可见C++语言的安全性受到了新的挑战。
为了提高C++的安全性,推荐采用微软的 SAL 注释系统。
SAL
Microsoft 源代码注释语言(SAL)是一个注释系统,用于在 C 和 C++ 程序中提供有关函数参数和返回值的额外信息。它旨在提供给静态代码分析工具如 Visual Studio 代码分析等足够的信息,以便更准确地检测代码中的错误,例如缓冲区溢出、类型不匹配和内存泄漏。
SAL 通过在源代码中添加特定的注释使得开发者可以明确函数如何使用参数、参数和返回值的有效性、以及执行完函数时的状态。这些注释不会改变程序的实际行为,但它们可以大大提高代码的可靠性和安全性。
SAL 注释在 <sal.h>
头文件中定义,并且在编译时不会产生任何影响,因为它们是为了静态代码分析工具设计的。SAL 注释有多个版本,其中 SAL 2.0 是专门为 Windows 驱动程序开发提供的更丰富的注释集。
为什么使用 SAL 注释?
- 提高代码质量:通过使用 SAL 注释,开发者可以减少因误解参数用法而导致的错误。
- 增强代码分析:SAL 注释可以帮助代码分析工具更准确地识别潜在的代码问题。
- 增强文档:SAL 注释也是一种文档形式,它可以帮助其他开发者理解代码的预期用法和行为。
- 减少安全漏洞:许多安全漏洞源于对输入数据的错误处理。SAL 注释有助于确保数据处理函数的正确使用。
SAL 注释的例子
SAL 注释使用特殊的前缀 _In_
、_Out_
、_Inout_
等来表明函数参数的预期用法。例如:
_In_
:表示一个输入参数。调用者应该在调用之前提供一个初始化的值。_Out_
:表示一个输出参数。函数将提供一个值,调用者应该在调用后检查这个值。_Inout_
:表示一个既是输入又是输出的参数。调用者提供一个初始值,函数可能会修改它。
#include <sal.h>
void MyFunction(
_In_ int inputValue, // 一个输入参数
_Out_ int* outputValue // 一个输出参数
);
在这个例子中,MyFunction
有两个参数:一个输入参数 inputValue
,和一个输出参数 outputValue
。调用者应该提供 inputValue
的值,并准备好在函数返回后检查 outputValue
指向的值。
想要详细了解SAL更多信息,请参考官方文档:Understanding SAL
SAL对空指针的安全优化
当指针参数批注包含 opt
时,它指示该参数可能为 null。 否则,批注行为与不包含 opt
的版本的行为相同。 下面是指针参数批注的 opt
变体的列表:
In_opt
Out_opt
Inout_opt
In_opt_z
Inout_opt_z
In_reads_opt
In_reads_bytes_opt
In_reads_opt_z
...
访问可选参数时,如果没有判空,则在静态分析时可以发现。
SAL 注释是一个强大的工具,开发者不仅可以减少错误,还可以帮助代码分析工具找到更多的潜在问题。虽然它需要在编码过程中额外的努力来添加这些注释,但长远来看,这些努力可以通过减少调试和维护成本来得到补偿。