G.O.S.S.I.P 阅读推荐 2024-04-17 检测 Sanitizer 本身的 Bug

admin 2024年4月18日00:03:58评论5 views字数 3065阅读10分13秒阅读模式

今天要为大家推荐的论文来自ASPLOS 2024上的UBfuzz: Finding Bugs in Sanitizer Implementations,由于苏黎世联邦理工学院(ETH Zurich)Zhendong Su研究组完成并投稿。

G.O.S.S.I.P 阅读推荐 2024-04-17 检测 Sanitizer 本身的 Bug

背景介绍:Sanitizers

使用 C/C++ 等程序语言编写的代码可能包含未定义行为(Undefined Behavior),比如 Buffer Overflow、Use After Free 等。实际上这些未定义行为,尤其是跟内存访问相关的行为,可能导致很多的安全漏洞。比如,Google 的公开报告[1] 显示 Chromium 中超过70%的安全漏洞都源自于内存安全问题。为了更好的检测程序中的未定义行为,学术界及工业界广泛使用 Sanitizer 作为一种动态检测手段。常用的 Sanitizer 包括 AddressSanitizer (ASan,用于检测内存错误,比如栈溢出),UndefinedBehaviorSanitizer (UBSan,用于检测多种未定义行为,比如整数溢出),以及 MemorySanitizer (MSan,用于检测未初始化内存的使用)。Sanitizer 目前已经集成在主流编译器中,比如 GCC 和 LLVM。给定源代码,我们可以在编译的过程中开启 Sanitizer 选项来得到插桩后的可执行文件。例如,下图左边的代码包含一个栈溢出漏洞 (第8行),下图右上部分显示开启 ASan (-fsanitize=address) 之后编译并运行代码可以正确的检测出栈溢出。但是,如果我们同时使用优化选项 -O2,ASan就检测不出来了 。这其实是由于 GCC 中 ASan 的一个实现 bug 导致的(注意:并不是由于 -O2 的优化导致的,后文会讨论),而这篇文章的目的就是自动地检测出这种 Sanitizer 实现中的 bug。

G.O.S.S.I.P 阅读推荐 2024-04-17 检测 Sanitizer 本身的 Bug

这种 Sanitizer bug 的直接结果就是可能导致程序中的未定义行为不能被检测出来,从而严重影响 Sanitizer 的有效性。例如,即使现在大家使用 Fuzzing 技术结合 Sanitizer 来大规模检测内存安全漏洞,由于 Sanitizer 的实现 bug,程序中的安全漏洞即使被 Fuzzer 触发也有可能不被报出来,从而导致 Fuzzer 无法有效地发现漏洞。

方法及实现

自动化地检测 Sanitizer 实现中的 bug 看起来好像很直接:首先生成大量的包含各种未定义行为的程序,然后测试 Sanitizer 是否能够检测出来。如果不能的话,那就证明我们“发现”了一个 Sanitizer 的 bug。理论上这种方法的确可行,但是最关键的问题是目前并没有有效地方法来自动地生成这种包含未定义行为的程序。这篇文章的第一个贡献就是设计并实现了一种通用的生成这种程序的方法:Shadow Statement Insertion。通俗地来说,该方法需要使用者先提供一个正确的程序 P 和想要得到的未定义行为类型 T,然后通过各种静态/动态分析来决定程序 P 中可以引入未定义行为 T 的具体位置,最后通过精确地变异 P 来得到新的包含未定义行为 T 的程序 P’。例如,假设想要在下图左边的正确程序 P 中引入一个栈溢出。该方法首先分析出其可能的引入位置是 a[x],然后分析出数组 a 的大小是5,且索引值 x 为1。为了引入一个栈溢出,该方法随之插入对应的Shadow Statement (下图右中的 x=5 )。当然,实际的插入方法和过程更为复杂,大家可以通过阅读原文来了解具体的实现过程。

G.O.S.S.I.P 阅读推荐 2024-04-17 检测 Sanitizer 本身的 Bug

论文中分析了 9 种常见的未定义行为的触发条件并分别定义了他们的 Shadow Statement。

但是,这种方法并不能有效地检测 Sanizier 实现中的 bug。这是为什么呢🤔️ 原因是由于编译器的优化带来的影响。在现在的编译器中,Sanitizer 实际上是众多编译环节(Pass) 中的其中一个,如下图所示。

G.O.S.S.I.P 阅读推荐 2024-04-17 检测 Sanitizer 本身的 Bug

所以 Sanitizer 接收到的程序实际上是经过优化处理的,而这种情况下,编译器的优化可能导致包含未定义行为的代码被转化成正确代码(可以参考作者在 ASPLOS 2023的文章[2]或者 VUSec 组的 PLDI 2023 [3])。例如,下图左包含栈溢出的程序会被 GCC -O2 优化为下图右,从而导致ASan无法将其正确检测出来。

G.O.S.S.I.P 阅读推荐 2024-04-17 检测 Sanitizer 本身的 Bug

这就导致一个很严重的问题:对于一个生成的包含未定义行为的程序,当 Sanitizer 没有报告时,是无法知道这是因为 Sanitizer 的实现 bug 还是由于编译器的过度优化。为了解决这个问题,本文的第二个贡献是提出了一个针对 Sanitizer 测试的Test Oracle:Crash-Site Mapping。其核心思想是通过校验最终生成的Assembly Code 中是否仍然存在源程序中未定义行为对应的代码片段来判断编译器是否过度优化。当最终的 Assembly Code 中仍然存在未定义行为对应的代码片段且 Sanitizer 并没有报告时,就知道这是一个 Sanitizer 的实现 Bug。具体的技术细节可以参考论文原文。

实验结果

作者把该测试框架(程序生成器+Test Oracle)称作 UBfuzz,具体的复现代码可以在文章对应的 Artifact 中找到。作者测试了 GCC 和 LLVM 中的 ASan、UBSan、以及 MSan,最终结果是作者一共找到了 31 个 Sanitizer 的实现 bug。值得说一下的是,所有 Sanitizer 都有 bug。

G.O.S.S.I.P 阅读推荐 2024-04-17 检测 Sanitizer 本身的 Bug

一点思考

这个工作最开始的出发点其实是作者在分析 Fuzzing 找到的一个栈溢出时,发现 LLVM 编译的版本能快速找到但是 GCC 编译的版本无法找到。所以作者猜想可能是 GCC 的Sanitizer 有问题,从而启发了这个工作。在这个工作里,作者设计了一个程序生成器,能够大规模地生成想要的包含各种未定义行为的程序。所以理论上,这个生成器也可以用来测试其他的程序分析工具,比如各种静态分析工具。

论文PDF:https://arxiv.org/pdf/2401.04538.pdf

欢迎感兴趣的同学联系作者呀~作者开源并维护其中的未定义代码程序生成器 UBGen,代码在这儿 https://github.com/shao-hua-li/UBGen 欢迎大家点点小星星~

参考链接:
[1] Memory Safety
https://www.chromium.org/Home/chromium-security/memory-safety/

[2]  Shaohua Li, and Zhendong Su. “Finding Unstable Code via Compiler-Driven Differential Testing.” ASPLOS 2023.

https://dl.acm.org/doi/abs/10.1145/3582016.3582053
[3] Isemann, Raphael, et al. “Don’t look UB: Exposing sanitizer-eliding compiler optimizations.” PLDI 2023.

https://dl.acm.org/doi/abs/10.1145/3591257

投稿作者介绍:
李少华,目前为苏黎世联邦理工学院博士生,即将加入香港中文大学成为助理教授(招生ing),导师为 Zhendong Su。研究方向包括但不限于程序语言、编译器测、软件工程、系统/安全、MLsys。个人主页:https://shao-hua-li.github.io/

原文始发于微信公众号(安全研究GoSSIP):G.O.S.S.I.P 阅读推荐 2024-04-17 检测 Sanitizer 本身的 Bug

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年4月18日00:03:58
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   G.O.S.S.I.P 阅读推荐 2024-04-17 检测 Sanitizer 本身的 Bughttp://cn-sec.com/archives/2668073.html

发表评论

匿名网友 填写信息