whexy1999

年度爽文:Fuzzing 顶会论文打假报告

Whexy /
February 04, 2024
17 min read
✍️

作者声明

本文主要是对 fuzz-evaluator 的报告内容的总结,不代表本人观点。如需阅读原文,请访问 SoK: Prudent Evaluation Practices for Fuzzing。本人对文章以外可能产生的任何解读误差不负责任。文中的数据和观点仅供参考,不构成任何建议与意见。

Fuzzing 指的是软件安全领域的 “模糊测试”,是一种暴力找洞的测试方法。模糊测试随机产生一些输入,然后观察程序的行为。如果程序在某个输入上表现出异常,那么这个输入就可能发现了一个 “漏洞”。虽然业内提出了 “白盒” 和 “灰盒” 的测试方法以增加可控性,但随机性仍然是模糊测试的核心特点。

在顶会上,关于模糊测试的论文数量也在逐年增长。然而,由于 Fuzzing 随机性强、测试资源消耗大,以及运行时间长,评审很难做到短时间内确认论文方法的有效性。因此常有滥竽充数的文章入围(甚至获奖)。有不少团队试图通过对论文的实验数据进行复现,来验证论文的有效性。

当然,这些复现也可能引发争论。例如,2023 年 12 月,香港科技大学的助理教授佘东冬在推特平台炮轰德国 CISPA 团队 Andreas Zeller 教授对他 2019 年博士期间发表在 Oakland 会议上的论文 NEUZZ 的复现结果。

佘东冬质疑复现结果的推特截图

佘东冬质疑复现结果的推特截图

两人在推特上来回拉扯了几页推文。其中,还有其他人站在 Andreas Zeller 教授一边,一起质疑佘东冬教授的这篇论文是 “完全的诈骗”。

Cornelius 的推特截图

Cornelius 的推特截图

最终 Andreas Zeller 教授以阴阳怪气的祝福结束了争论。

Andreas Zeller 教授回应佘东冬的推特截图

Andreas Zeller 教授回应佘东冬的推特截图

神仙打架,凡人吃瓜。作为刚入门的 Fuzzing 小白,我等也不配参与这场争论。但是,我还是想知道,到底有多少 Fuzzing 论文是 “诈骗” 的呢?这些论文的 “诈骗” 程度又有多高呢?

背景介绍

🇩🇪

CISPA

上文中提到的德国 CISPA 团队在 Fuzzing 领域有比较高的声誉和影响力。他们和 Google Fuzzing 团队也保持着良好关系,使得他们能获得大量真实可靠的测试数据。

🔍

Google & Fuzzing

谷歌在 Fuzzing 领域有着丰富的经验和资源。他们的 OSS-Fuzz 项目是业界公认的 Fuzzing 平台,也是 Fuzzing 领域的标杆。他们的研究人员也参与开发或维护了 AFL、honggfuzz、libAFL、syzkaller 等著名的 Fuzzing 工具,以及 Fuzzing Test Suite(FTS)、FuzzBench 等测试套件。

最近,我们关注到在 GitHub 有一个名为 fuzz-evaluator 的账号。它一直在更新一些 Fuzzing 论文的复现结果。这些复现结果都是以 GitHub 仓库的形式发布的,包括了复现的代码、数据、实验环境等。我们也并不惊讶的发现,这些复现结果往往和原论文的实验结果有出入。私下里,我们都叫它 “Fuzzing 警察”。

直到前几天,我们发现 CISPA 团队在 S&P 2024 上发表了一篇名为 SoK: Prudent Evaluation Practices for Fuzzing 的文章。这篇文章总结了他们对近六年 150 多篇顶会 Fuzzing 论文的调研结果,并对这些论文的实验数据进行了分析。原来,fuzz-evaluator 的账号就是他们的团队注册的。

fuzz-evaluator 的调研结果

可复现性

在文章里,首先对于可复现性进行了调研。整整 150 篇顶会文章的复现工作量是非常大的,所以他们选择针对 ①是否提交了 artifact、②是否开源代码、③是否公开数据,以及 ④是否获得复现徽标这四个指标进行调研。其中,

  • 有 74% 的论文选择开源自己提出的 Fuzzing 工具;
  • 有 11% 的论文选择公开测试数据。在这些选择公开数据的论文中,64% 是由于没有进行开源而决定公开数据的;
  • 有 36% 的论文没有提交 artifact。另外有 37% 的论文提交了 artifact,但没有得到任何徽章。只剩下 26% 的文章提交了 artifact 并且得到了至少一个徽章。

当然,不开源或者不提交 artifact 并不代表论文实际上有什么问题。毕竟,开源和提交 artifact 都是需要额外的工作量的。但是,这些指标可以作为一个参考。

测试目标(被 fuzz 的程序)

在对 Fuzzing 工具进行评估时,我们需要找一些被 fuzz 的程序作为目标。例如,最好选择一些真实程序作为测试目标,而不是一些人为构造的程序。以及,最好选择被广泛使用的程序作为测试目标,而不是一些过时的程序。在这 150 篇论文中,他们发现:

  • 平均每篇论文只测试了 9 个目标;
  • 所有的测试目标中,76% 的目标只被一篇论文测试过。这意味着大部分测试目标的结果在其他论文中没有得到验证;
  • 61% 的论文不使用公开的测试套件,而是自己选择了测试目标。

测试套件是一些公开的测试用例集合,例如 FuzzBench、FTS 等。这些测试套件是由业界的专家维护的,可以保证测试用例的质量。而自己选择测试目标的话,可能会选择一些过时的目标,或者选择一些不具有代表性的目标。

即便如此,在选择测试套件时,也需要考虑时效性。在使用了公开测试套件的论文中:

  • 有 17% 的论文使用了 LAVA-M,5% 的论文使用了 CGC。这是两个过时的测试套件;
  • 10% 使用 FuzzBench,8% 使用 FTS,4% 使用 Magma,1% 使用 UniBench。这些测试套件是比较新的,也是业界公认的测试套件。

比较对象

一般在测试新的 Fuzzing 工具时,我们会选择一些已有的 Fuzzing 工具作为比较对象。这些比较对象一般是一些业界公认的 Fuzzing 工具,例如 AFL、AFL++、libFuzzer 等。在这 150 篇论文中,他们发现:

  • 35% 的论文和 AFL 进行了对比;
  • 6% 的论文和 AFL++ 进行了对比;
  • 5% 的论文和 libFuzzer 进行了对比;
  • 23% 的论文没有和 SOTA 比较。

有趣的是,以上的流行的比较对象全都不是学术界提出的 Fuzzing 工具。这是否意味着学术界的 Fuzzing 工具和业界的 Fuzzing 工具之间的差距还是比较大的?

当然,他们也总结了被比较次数中排名前三的学术界 Fuzzing 工具。它们依次是: FairFuzz(特别烂) 、Qsym(特别慢)、MOpt(效果不错)。

实验环境

保持实验环境的一致性和公平性是非常重要的。在这 150 篇论文中,他们发现:

  • 15% 的论文没有描述实验环境;
  • 5% 的论文使用了偏心的资源配置;
  • 5% 的论文使用了偏心的初始种子。

我们读到这里感到很惊讶。因为使用了偏心的资源配置是很容易从论文文本中发现的。这是否意味着审稿人并没有仔细审阅论文呢?如果实验环境本身就是偏心的,那么论文的实验结果就是不可靠的。这些论文加起来也占了顶会文章的四分之一。

数据测试

最后,对实验数据的归纳和分析是非常重要的。在这 150 篇论文中,他们发现:

  • 63% 的论文没有使用统计学工具分析数据;
  • 15% 的论文重复实验次数少于 5 次;
  • 73% 的论文没有提供置信区间或标准差。

正如我们在最开始说的,Fuzzing 是一种随机性很强的测试方法。因此,对实验数据进行统计学分析是非常重要的。这样才能保证实验结果的可靠性。我们可以理解部分作者可能并不熟悉统计学工具,但是重复实验次数少于 5 次是不可接受的。

fuzz-evaluator 总结的常见实验错误

在这篇 SoK 的最后,fuzz-evaluator 团队举例子、树典型,批判了一些论文,总结了一些常见的实验错误。他们提到的论文里,有一些出自是我比较熟悉的作者,甚至还有不菲的交情。但是我们要始终坚持维护正义,不徇私情,大义灭亲

  • MemLock (ICSE'20, artifacts reusable) 使用唯一崩溃作为指标,与实际 bug 发现数量不符。“唯一崩溃” 是用于保存导致程序崩溃的种子的去重策略,但并不代表种子之间完全无重复;
  • SoFi (CCS'21) 发现 “漏洞” 并提交给代码原作者,但被原作者悉数拒绝。也就是说,这些 “漏洞” 并不是真正的漏洞;

其实,用发现漏洞的数量,尤其是用新 CVE 的数量作为评价指标本身就是有问题的。谁知道这些漏洞是不是你用 Fuzzing 发现的?毕竟,Fuzzing 是一种随机性很强的测试方法。这些漏洞可能是其他测试方法发现的,或者是其他人发现的。可能这一次能发现,下一次就不能发现。所以,我们应该用代码覆盖率等更稠密、更客观的指标来评价 Fuzzing 工具。

尽管使用了代码覆盖率,也有一些论文使用了不公平或无法复现的比较方式。例如:

  • DARWIN (NDSS'23 distinguished paper) 测试时没有提供 baseline,其实只比 AFL 高出 1.73%。它声称在 15 个 target 上领先,实际上只有 4 个;
  • FUZZJIT (USENIX'23) 声称比 SOTA 高 33% 代码覆盖,实际低了 12%;
  • EcoFuzz (USENIX'20) 使用了 “路径数” 作为指标,实际代码覆盖(分支覆盖)比所有文中的比较对象都低;
  • PolyFuzz (USENIX'23) 使用了不同的初始种子进行比较,使自己获得了 273% 的初始优势;
  • FishFuzz (USENIX'23) 测量覆盖率时使用了不公平的插桩,它声称的 8.44% 提升实际只有 1.69%。

结语

对于 fuzz-evaluator 的报告,我们有很多感慨。首先,对于他们的结论,我们一点儿也不感到惊讶。毕竟只要是 Fuzzing 领域的研究者,都或多或少地尝试过复现别人的论文。如此高的不可复现率,也是我们的亲身体会。这几年不乏把这一结论广而告之的研究者,但 Fuzzing 文章仍然源源不断地被接收。动辄 10%, 20% 的提升。这些提升的真实性,我们也是心知肚明。

放卫星、吹大牛,这些似乎已经成为了 Fuzzing 领域的常态。现在,一些真正有价值的研究被埋没在了这些噱头之下。我们需要更多的人站出来,对不负责任的研究进行批判。我们更需要更多的人站出来,对那些有价值的研究进行肯定。故事谁都会讲,道理谁都能说得高大上,公式谁都能列一大堆,但你能把 Benchmark 里的所有目标测完吗?你能不挑 case 吗?你能在均值上提升 3% 的分支覆盖率吗?

最后,比起关起门来自己做实验,我们更需要的是公开透明的公证机制。例如,Google 的 FuzzBench 项目就是一个很好的例子。除了一个非常不错的测试集,它还提供了一个公开公平的测试环境。免费的谷歌云机器,写几行 Dockerfile 就可以在上面跑实验(不需要开源,在 Docker 里提供二进制就行)。我建议所有已发表的 Fuzzing 论文都应该在 FuzzBench 上公开测试,免得工业界瞧不起咱们。我们起码可以避免一些不负责任的研究者通过一些小把戏来忽悠审稿人。

再比如,去参加公开的 Fuzzing 比赛,也是一个很好的方法。例如近两年一直在举办的 SBFT Fuzzing Competition。2023 年的获奖者是 Hastefuzz,只是关了 sanitizer 就把所有人甩在后面,这不得让那些动不动提升 20% 的文章汗颜?2024 年的获奖者是 BandFuzz,没开源,暂时不知道是谁写的,也不知道他们是不是也会在文章里吹大牛。但是,至少他们的实验结果是公开的,是可复现的。

搞 Fuzz,说简单也简单,说难也难。Fuzzing 不需要多少深入的数学理论,也不需要对系统细致入微的工程理解。这使得整个领域的门槛变低,谁都能来说道一番。然而,如何在这个领域做出有价值的研究,同时还能在一群滥竽充数的人中脱颖而出,这是一个值得思考的问题。

© LICENSED UNDER CC BY-NC-SA 4.0