在Trust-in-Soft工作期间,我一直在寻找开源软件中的缺陷。花费时间最多的程序是SQLite——一款部署极其广泛的轻量级数据库。它约有113KSLOC的指针密集型代码,规模过大,难以进行轻松的静态验证。不过,它的测试套件令人印象深刻,已经用于驱动Valgrind、ASan和UBSan等动态工具。我则用这些测试配合tis-interpreter开展工作。 本文重点以蓝色区域展示各类未定义行为(UB)的分布情况。需要说明的是,这张图遗漏了许多UB(总共约有数百种),并且不按比例绘制。坦白讲,使用tis-interpreter并非一帆风顺:它并非从零设计的C语言解释器,而是对一种可靠形式化方法工具的改编。它比Valgrind还慢,并且在处理单独编译的库以及与系统交互的代码(如mmap调用)时存在困难。正如上图所示,当运行在ASan和UBSan都判定为干净的代码上时,它往往会发现许多让人不乐意听到的琐碎问题。无论如何,我用tis-interpreter推动SQLite测试的原因之一,就是帮助我们发现并修复这些痛点。 接下来看看一些实际bug。我边测试边提交问题,其中不少已经在新版SQLite中修复。我还会讨论这些bug与“友好C方言”这一概念的关系。 **悬垂指针的值** SQLite喜欢使用(但不 dereference)已经被释放的堆块指针,这种情况在很多地方出现。例如,当zHdr处于悬垂状态时,代码会执行这样的比较: ```c if( (zHdr>=zEndHdr && (zHdr>zEndHdr || offset64!=pC->payloadSize)) || (offset64 > pC->payloadSize) ){ rc = SQLITE_CORRUPT_BKPT; goto abort_due_to_error; } ``` 这些对悬垂指针的使用属于未定义行为,但它们编译后是否有害呢?这正是tis-interpreter帮助我们深挖的问题。

打开网易新闻 查看精彩图片