|VS2022 经过优化之后的空指针检测


|VS2022 经过优化之后的空指针检测
文章图片
|VS2022 经过优化之后的空指针检测
文章图片
|VS2022 经过优化之后的空指针检测
文章图片
|VS2022 经过优化之后的空指针检测
文章图片
|VS2022 经过优化之后的空指针检测
C++静态分析团队一直致力于提供各种工具来使开发者尽可能地编写安全的代码 。 感谢开发者的反馈和建议 , 我们添加了越来越多的代码安全检查规则并修复了一些对开发者影响比较大的Bug 。
在本文中 , 我将简要介绍最新的一项实验性功能:空指针引用错误 。 我还会将这项新功能和现有的类似检查做一个对比 , 看看新功能到底”新”在哪里 。
概述从底层角度来看 , 我们实现了多个代码分析引擎 。 当然 , 这些底层引擎对于开发者来说是透明的 , 不管我们使用哪一套分析引擎 , 它们所激发的警告信息都是一致的 。 在这些代码分析工具中 , 有一些是用来检查空指针引用错误的 , 它们包括:C6011 C6387和C28196 。 这些警告信息对于开发安全的代码来说 , 确实非常有用 , 也帮助开发者规避了很多代码错误 。
但是 , 它们针对现代C++新引入的一些特性还不能很好地支持 。 并且 , 它们所依赖的数据流框架也有着自身的限制 。 其中 , EspXEngine就是我们创建用来解决这些问题的一个引擎 。 基于这个引擎 , 我们提供了很多路径敏感的数据流分析 , 例如Concurrency Check和Use After Move Check 。 这些检查十分奏效 , 这也促使我们将空指针检查也加入到这个引擎中 。 我们很高兴地看到新的版本相较于旧版本而言 , 添加了很多改进措施 。
下面 , 我们来具体看看这些改进的细节信息 , 以及如何使用这些新的功能 。
路径敏感的分析两种引擎都可以针对路径敏感的代码进行分析 , 我们考察下面的代码来进行理解:
在上面的代码中有很多分支语句 。 其中的一些分支互相之前存在一定的关联性 , 但是基于代码流的分析无法判断出这些关联性 。 例如 , 基于代码流的分析可能认为代码是不安全的 , 因为它判断出一个潜在的空指针 , 因为变量p在分支2中被设置成了nullptr , 然后在分支3中又被使用了 。
但是 , 这个实际上不会发生 , 因为如果分支2被执行了 , 分支3就根本不会执行 。 而路径敏感的分析会判断这些代码可达性的条件并判断出上面的代码是安全的 。 但是要注意的是 , 这种精度的分析会花费更多的时间和内存 。 两种引擎针对这个代码片段 , 都会有着相同的行为模式 。
本地分析两种分析引擎都会做过程式分析 , 所以它们不会看到跨越函数的边界 , 并且依赖类型 , 类型扩展 , 模型等 。
上面的代码有一个Bug 。 因为对swap的调用 , 指针p会被设置为nullptr 。 在当前版本的代码检查中 , 这个Bug不能被检测到 。 而EspXEngine引擎就会发现这个错误并向开发者发出警告 。
但是有个问题 , 当我们调用我们自己的API时 , EspXEngine不会知道被调用函数的具体语义 。 在这种情况下 , 我们可以使用类型或者SAL标记来描述函数中的前置和后置条件 。
在上面的代码中 , 我们使用了_Notnull_和_Analysis_assume_这两个SAL标记来描述一些指针值上面所附加的限制 。 这种标记可以被两套引擎所支持 。 一个更加现代化的分案是使用更加丰富的类型来表达这些限制 , 而这个只能在EspXEngine中被支持 。 并且 , 当一个空指针被保存到一个gsl::not_null指针后 , 它会对代码做出标记 , 如下图所示:
而类型对于代码分析来说确实十分有用 , SAL可以用来表达一个大范围的代码契约 , 我们看看下面的代码:
这个函数有一个复杂的后置条件 。 只要第一个参数为真 , 当函数存在时 , 位置 *p 处的值必须不为空 。两个引擎都理解这些契约(尽管 EspXEngine 中的支持更复杂) , 并且许多 Windows API 都被注释以描述它们的行为 。 我们很想使用标准的语言工具 , 但是 C++20 不接受契约式编程 , 我们需要一个既适用于C又适用于C++ API的解决方案 。
现有空指针检查的问题下面我来展示一些代码例子 , 其中基于EspXEngine的空指针检查具有比当前更好的行为 。首先是一些当前检查规则中难以发现的空指针错误 , 如下图所示: