负责任且有效地查找软件缺陷
注意:本文并非关于安全问题的负责任披露。
我们致力于发掘寻找软件缺陷的方法的历史几乎和人们写代码的历史一样长。近期以来,将“外部缺陷查找”(即寻找他人软件中的缺陷)视为一项本身就值得追求的活动,已经变得非常普遍。有几点因素起到了推动作用:
- 投入使用的软件数量已经大幅增加,而且可以推测,潜在的软件缺陷数量也随之增加了。
- 由于软件越来越多地被用来监控和控制我们生活的方方面面,而且系统往往是联网的,因此软件缺陷的影响可能比以往更大。
- 用于暴力挖掘软件缺陷的廉价算力随处可见。
OSS-Fuzz 项目是外部缺陷查找工作取得成功的典范。项目网页提到,该项目“已经在 300 个开源项目中找出超过 20,000 个缺陷。”
本文探讨了如何开展外部缺陷查找工作,以最大化项目的整体效益。简而言之,这类工作必须小心、周全地完成,并且需要和软件开发人员合作完成。相反,仅仅是简单地把一大堆问题甩给某些项目,然后就撒手走人,恐怕不太好。我还没有见过太多关于这方面的文章。不过,“数十亿行代码之后:使用静态分析在现实世界中查找软件缺陷” 这篇文章写得很好,也涉及了一些相同的问题。
问题在于,缺陷查找者与项目开发者之间通常在成本和动机方面不匹配。外部缺陷查找者希望尽可能多地发现并报告缺陷,如果他们有合适的技术(比如一个崭新的模糊测试(一种自动化的软件测试技术。其核心原理是向程序大量输入随机、无效或非预期的数据,通过观察程序是否出现崩溃、内存泄漏等异常,来发现潜在的缺陷和安全漏洞)工具),那么在大型、防御薄弱的目标系统里发现数百甚至数千个缺陷的成本最终可能会非常低。缺陷查找者的动机既有利他的一面(他们希望通过修复大量缺陷,使得目标软件变得更加可靠),也有自私的一面(如果一种缺陷查找工具或技术能够发现大量此前未知的软件缺陷,就足以证明其威力强大)。另一方面,项目的开发人员通常会感谢缺陷查找者为发现项目中的缺陷而付出的努力(当然,他们也希望系统能够更加可靠),但对他们来说,过多的缺陷并不是他们所希望看到的。首先,每份缺陷报告需要投入大量关注和精力,通常所需要的工作量远远大于单单只是发现该缺陷所耗费的精力。其次,大量的缺陷可能会给项目带来负面影响,进而影响项目的开发人员。
在本文的剩余部分,我将会列举一些从事外部缺陷查找工作的人员在发现每个软件缺陷时应该扪心自问的问题。问题的答案以及回答问题的过程,将有助于他们更负责任、更有效地完成工作。
正在测试的系统是否适合作为外部缺陷查找的目标?
这一点显而易见,但还是需要提一下:并不是每个代码库都希望或需要收到缺陷报告。有很多一次性项目、研究项目以及其它代码库,它们的用户数量极少,开发人员最多也就一两个人。将这类代码作为外部缺陷查找的目标通常不太合适。相反,一个系统的用户越多、用途越重要,它就越适合作为外部缺陷查找的目标。对于是否值得查找外部缺陷,一个合理的评判标准或许是:你使用的操作系统的包管理器默认情况下是否提供了你要测试的软件的安装方式?如果不是这样,那么也许它还没有达到成为外部缺陷查找的合适目标的程度。
这个缺陷是一个安全漏洞吗?
这完全是另一回事了;如果你发现了一个潜在的漏洞,请向他人寻求建议 。
这是个已知的软件缺陷吗?
模糊测试工具的一个缺点是,它们往往会不断重新发现已知的缺陷。虽然存在可以自动将缺陷分类的技术,但它们远非完美,特别是对于那些不会导致程序崩溃的缺陷而言。此外,有充分的理由相信,完美的自动化缺陷分类是不可能实现的,因为“不同的缺陷”这个概念本质上是人为定义的。当你发现的某个缺陷有可能已经被发现并被报告过的时候,最好先别急着提交你发现的缺陷,先等一会。一旦缺陷跟踪系统里那些疑似重复的缺陷被修好之后,就很容易判断你的测试用例是否还会触发故障。如果是,那么你可以上报这个缺陷。否则,请放弃这个缺陷。
有时,你用你的缺陷查找方法会发现一个已知缺陷的触发方式,比缺陷追踪系统中现有的触发方式还要更简单。例如,如果某个浏览器存在一个竞争条件,人们认为这个条件只能通过五个线程才能触发,而你的测试用例仅使用两个线程就使其发生,那么这便是一个有趣的新发现。在这种情况下,你应该考虑将你的测试用例追加到现有的问题中。
你能写一份有说服力的缺陷报告吗?
撰写优质的软件缺陷报告是一项需要通过学习掌握的技能,需要相当大的细心和耐心。这里 有 一些 不错的资源。
我只想补充几点:
- 一个软件缺陷报告不只是对一系列事实的罗列,它还构成了一个理由,说明为什么一位开发者(他当天肯定已经安排好了其它计划)应该转而去修复这个缺陷。
- 设身处地为开发者着想非常重要。如果你正在阅读这份报告,你手头的信息是否足以让你据此采取行动?你会愿意这样做吗?
- 无论如何都要避免表现得粗鲁或自以为是。
- 与来自实际使用场景的测试用例相比,由模糊测试工具发现的测试用例对开发者来说,先天上更缺乏说服力。首先,这些测试用例看起来往往很奇怪。其次,开发人员经常认为(有时无疑是正确的),模糊测试工具发现的边界情况不会被人类用户触发。如果你提交的软件缺陷报告中包含由模糊测试工具生成的输入,则必须仔细地精简测试用例,不仅要追求最小化,还要兼顾可读性和可理解性。
外部缺陷查找的一大优势在于,由于负责测试的人员通常会提交大量缺陷报告,因此他们能够非常出色地完成这项工作。相比之下,普通用户可能不会经常提交缺陷报告,因此我们不指望他们具备同样的专业能力。将撰写高质量软件缺陷报告的能力视为一种超能力,它能让你与系统开发者合作,以高效且愉快的方式共同提升代码质量。
这个缺陷重要吗?
开发人员几乎总是愿意修复那些可能对用户造成严重后果的重要缺陷。另一方面,他们往往乐于推迟修复那些他们认为不重要的缺陷,以便先处理更紧迫的事。作为一名外部缺陷查找者,很难判断自己发现的是哪种缺陷(而且绝大多数缺陷其实并不重要)。解决这个问题的一个方法是查看缺陷跟踪系统中已有的缺陷(包括已修复的和未修复的):你能找出哪些共同特征,迫使开发人员迅速解决了这些缺陷?你能找出哪些共同特征,导致开发人员忽略了这些缺陷或将其标记为 WONTFIX(即不予修复)。你还可以与开发者沟通交流:他们可能会告诉你,他们对哪些类型的缺陷感兴趣,对哪些类型的不感兴趣。CVC4 SMT 求解器的开发者最近发布了一份 相关公开文档 。我希望其它许多项目也能效仿他们的做法。
常识也很有用:如果该错误需要通过一种冷门或不太可能出现的命令行选项组合才能触发,那么这个缺陷或许也就没那么重要了。例如,在使用 Csmith 对编译器进行模糊测试时,我们很少使用除 -O0、-O1、-O2、-Os 和 -O3 以外的参数来调用 GCC 和 LLVM。
你尝试过修复这个缺陷吗?
在某些情况下,修复你不熟悉的软件中的缺陷是不切实际的,因为这可能涉及操作系统内核中复杂而深层的竞态条件,或是编译器生成错误目标代码的细微问题。要找到修复该缺陷的正确位置,可能需要对该系统非常熟悉。另一方面,许多缺陷最终都很容易修复,包括拼写错误、复制粘贴错误,或者库调用中参数顺序错误等。如果你能花几分钟时间确认一下你报告的这个缺陷是否容易修复,并且如果确实容易修复的话,还能提供一个补丁方案,那么你就有可能为项目开发人员节省大量的时间和精力。他们会很欣赏这一点的。
你在缺陷跟踪系统中有多少个未解决的缺陷?
如果你已经报告了几个软件缺陷,但它们尚未得到修复(或者更糟糕的是,甚至连确认都没有),那说明哪里出问题了。也许开发人员正忙于其它事务,又或许他们认为这些缺陷并不值得处理或并不重要。无论如何,目前继续报告缺陷已经没什么用了。如果你还没这么做的话,不妨与开发人员沟通一下,了解具体情况;或者干脆另寻他处,在其它代码库中寻找缺陷。
开发人员信任你吗?
如果你所报告缺陷的系统开发者从未见过你或听说过你,他们很可能至少会看一眼你提交的第一个问题。如果他们信任你(因为你之前提交过许多高质量的缺陷报告),他们就会仔细审查这份报告,并且很可能会认真对待。不过,如果他们已经不信任你(举个例子,如果你向他们的缺陷跟踪系统提交了大量边缘情况问题),那么他们很可能再也不会理你了,你可能需要另寻他处以继续寻找软件缺陷。千万别让这种情况发生,这会让开发者很不爽,从而使我们所有人查找缺陷的工作变得更加困难。
你的工具是开源的吗?
内部缺陷查找(即由项目开发人员自行寻找缺陷)可能优于外部缺陷查找,因为开发人员更了解自己项目的实际需求,而且他们可以安排在最有效的时机进行缺陷查找工作,例如在合入一项重要的新功能之后。因此,除了开展外部缺陷查找工作之外,另一种方法是开发优质的缺陷查找工具,并将其提供给开发人员,最好是以开源软件的形式提供。该方案的弊端包括:增加了缺陷查找领域研究人员的软件工程负担(他们现在必须开发可供他人使用的工具,而非仅供自己使用的工具);同时,由于其工作成果的部分价值如今被隐藏起来,而非以可量化的形式公开展示,导致他们获得的社会和学术认可度降低。
总结
尽管软件的正确性在某种程度上是一个技术问题(即代码要么实现了其规范,要么没有实现),但提高软件质量却是一项高度依赖人的活动。在这里,开发人员和外部缺陷查找者属于同一团队,但由于双方的成本和动机不同,彼此之间很容易产生摩擦。如果发现缺陷的人在提交缺陷报告之前先问自己一遍本文中列举的一系列问题,就可以减少这种摩擦。
August 17, 2020 John Regehr, Professor of Computer Science, University of Utah, USA
译自: https://blog.regehr.org/archives/2037
本文由 HCTT 翻译团队 原创翻译,华中科技大学开放原子开源俱乐部荣誉推出。