静态分析工具PointsTo:百万行代码中的释放后使用漏洞检测

本文探讨动态程序分析的局限性,并介绍Trail of Bits开发的静态分析工具PointsTo。该工具通过LLVM位码转换、数据流图构建和路径敏感分析,有效检测大规模代码库中的释放后使用漏洞,显著减少漏报。

动态程序分析的问题 - Trail of Bits博客

开发者可以使用诸如AddressSanitizer和Valgrind等工具,这些工具会在运行的代码访问未初始化内存、泄漏内存或释放后使用内存时发出警告。尽管这些优秀工具可用,内存错误仍然存在,仍然会发布给用户,并在实际中被利用。

当今大多数错误查找工具都是动态的:它们在程序运行时识别错误。这很好,因为所有程序都有庞大的测试套件来执行每一行代码……对吗?不对。大型测试套件是例外,而不是规则。测试套件确实有助于发现和减少错误,但错误仍然会漏网。

也许解决方案是付费让专业人士审计代码。更多眼睛查看代码是好事™,但根本问题仍然存在。专家头脑中的分析仍然是“动态的”:思考每一条代码路径是不可行的。

因此,动态分析可能会错过错误,因为它们无法检查所有可能的程序路径。那么,什么可以检查所有可能的程序路径?

在数百万行代码中查找释放后使用

我们使用静态分析来分析数百万行代码,而无需运行代码。这种称为数据流跟踪的分析技术使我们能够分析和总结关于所有可能程序路径的属性。这解决了前述问题,即当某些程序路径未执行时遗漏错误。

一个能看到一切的分析实际上是如何工作的?下面我们描述一个实际的全程序静态分析工具PointsTo的1-2-3步骤,这是我们开发并经常使用的工具。该工具查找并报告大型代码库中潜在的释放后使用错误。

步骤1:转换为LLVM位码

PointsTo在程序的LLVM位码表示上操作。我们选择LLVM位码,因为它是执行程序分析的便捷中间表示。不出所料,我们分析流水线的第一阶段将程序的源代码转换为LLVM位码数据库。我们使用名为CompInfo的内部工具来生成这些数据库。一个类似的替代开源工具是whole-program-llvm。

步骤2:创建数据流图

PointsTo背后的关键思想是分析指向分配对象的指针如何在程序中流动。我们关心的是指针的赋值和复制、指针解引用以及指针的释放。这些指针操作使用数据流图表示。

过程中最有趣的步骤是将分配和释放转换为特殊赋值的原因和方法。“为什么”是因为这种转换让我们可以重新利用现有的程序分析来查找从FREE定义到指针解引用的路径。“如何”更微妙:PointsTo如何知道应该将“new A”更改为ALLOC,将“delete a”更改为FREE?

想象一个假设的嵌入式系统,程序内存匮乏,因此自然选择是使用名为ration_memory的自定义内存分配器。我们创建了一种Python建模语言,向PointsTo提供关于高级函数行为的信息。我们的建模脚本告诉PointsTo“new A”返回一个新对象,因此我们可以用它来对ration_memory说同样的事情。

插曲:隐藏的数据流

从源代码到数据流图的转换看起来很简单,但那是因为我们开始的源代码很简单。它没有函数调用,更重要的是,没有函数指针或方法调用!如果下面的callback是函数指针会发生什么?如果callback释放了x会发生什么?

1
2
3
int *x = malloc(4);
callback(x);
*x += 1;

这是PointsTo的秘方和同名来源:我们执行上下文和路径敏感的指针分析,告诉我们哪些函数指针指向哪些函数以及何时指向。总之,我们可以生成一个错误报告,跟踪x通过callback并返回。

步骤3:结局

是时候报告潜在错误供专家分析了。PointsTo搜索数据流图,查找从赋值到FREE再到解引用的流。这些流被转换为源代码行的程序切片,显示执行需要遵循的路径以产生释放后使用。以下是一个真实错误的程序切片示例:

(示例程序切片内容)

当向编译器人员描述这个系统时,通常的第一个问题是:但是误报呢?如果我们收到关于释放后使用的报告,但它不是呢?这就是编译器分析和漏洞分析的优先级分歧的地方。

编译器分析中的误报可能会引入错误,因此编译器通常是保守的。也就是说,它们用误报换取漏报。它们可能会错过一些优化机会,因为它们无法证明某些事情,但至少程序会被正确编译咳嗽

对于漏洞分析,这是一个糟糕的交易。漏洞分析中的误报是不方便的,但当需要查看数百万行代码时,它们只是沧海一粟。然而,漏报是不可接受的。漏报是一个被遗漏的错误,可能会进入生产环境。一个总能找到错误并有时警告你可疑但正确代码的工具是一项投资,可以在代码审计期间节省时间和金钱。

总结

分析程序中的错误是困难的。应该遵循行业最佳实践,如拥有广泛的测试套件。开发者应该定期通过动态分析工具运行他们的程序,以摘取低垂的果实。但更重要的是,开发者应该理解测试套件和动态分析不是万能药。错误有一个讨厌的习惯,隐藏在很少执行的代码路径后面。这就是为什么需要查看所有路径。这就是我们制作PointsTo的原因。

PointsTo是最近Empire Hacking(纽约双月聚会)的讨论话题。我在那里的演讲包括更多关于PointsTo设计和实现的信息,对于好奇的读者,幻灯片和视频在下面重现。我们希望未来发布更多来自Empire Hacking的视频。

PointsTo最初是为Cyber Fast Track制作的,我们要感谢DARPA资助我们的工作。Trail of Bits的顾问使用PointsTo和其他内部工具进行应用程序安全审查。如果您有兴趣对代码进行详细审计,并得到PointsTo和我们的CRS等工具的支持,请联系我们。

如果您喜欢这篇文章,请分享: Twitter LinkedIn GitHub Mastodon Hacker News

页面内容

  • 在数百万行代码中查找释放后使用
  • 步骤1:转换为LLVM位码
  • 步骤2:创建数据流图
  • 步骤3:结局
  • 总结

最近文章

  • Trail of Bits的Buttercup在AIxCC挑战赛中获得第二名
  • Buttercup现已开源!
  • AIxCC决赛:记录表
  • 攻击者的提示注入工程:利用GitHub Copilot
  • 作为新员工发现NVIDIA Triton中的内存损坏

© 2025 Trail of Bits. 使用Hugo和Mainroad主题生成。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计