YARPGen——挖掘编译器Bug的另一个工具

        在上一篇文章中,我介绍了一种编译器验证工具——Csmith,它随机产生有效的C代码,通过模糊测试的方式来帮助发现编译器中的Bug。但是Csmith也有其局限性,例如对C++的支持不完善等。为了克服Csmith存在的问题,并加强随机程序生成器的Bug挖掘能力,Dmitry Babokin,John Regehr和Vsevolod Livinskiy三人开发了另一个随机程序生成器——YARPGen(Yet Another Random Program Generator)。本文首先对YARPGen进行基本介绍,然后将其与Csmith进行对比,最后介绍使用YARPGen对编译器进行验证的方法。

一、YARYPGen基本介绍

        YARPGen论文原文:Random testing for C and C++ compilers with YARPGen | Proceedings of the ACM on Programming Languages

        使用YARPGen验证编译器的思想与Csmith一样,都是通过差分测试进行的。关于差分测试的讲解可以参考我的上一篇文章:使用Csmith自动挖掘编译器的Bug_ClarkC.的博客-CSDN博客

        作者开发YARPGen是为了探究以下三个问题:

  • 以前的编译器测试方法(包括随机测试)遗漏了哪些Bug?
  • 是否有可能生成不包含未定义行为的随机程序,并且涵盖尽可能多的C/C++特性,而无需求助于Csmith使用的那种繁重的动态安全检查?
  • 是否可以在生成器中建立某些机制,来针对优化器的不同部分进行测试?

        YARPGen提出了“生成策略”(generation policies)的机制,即系统地改变所生成代码的特性的机制,其目的是提高所生成程序的多样性,使YARPGen能够避免或至少延迟饱和情况的出现。该策略将LLVM在给定的随机测试时间内执行目标优化的次数增加了20%,GCC增加了40%,从而提高了触发编译器优化过程中潜在Bug的可能性。同时YARPGen还提出了一种新的避免未定义行为的方法,在下一节会进行介绍。

        到YARPGen论文完成时,该工具已在GCC中发现了83个新Bug,在LLVM中发现了62个,在英特尔的C++编译器中发现了76个。

二、与Csmith的对比

        YARPGen与Csmith的主要区别表现在它们在避免生成未定义行为时所采取的策略。首先,两者都使用了静态分析的方式,但是Csmith使用了封装函数(wrapper functions)来防止不安全的算术运算。YARPGen则不同,它将分析过程与代码生成过程结合在一起,以便可靠且高效地避免未定义行为的生成,而不会牺牲程序的表现力(expressiveness,指程序所包含的语言特征)。YARPGen的策略是在生成表达式时执行非常有限的回溯形式,将触发未定义行为的操作转化为安全的类似操作。

        虽然这种策略的效果显著,但是在某些方面Csmith的表现力比YARPGen更强,比如Csmith支持合理表达的指针算术运算。

        另外,Csmith虽然支持C++03和C++11标准,但实际支持并不完善。YARPGen V1.1就已经支持到C++17标准,最新的V2.0应该不会弱于先前版本。因此YARPGen对C++的支持强于Csmith。

三、YARPGen的使用

       对YARPGen有了初步了解之后,本节介绍其源码的编译及其使用方法。

        YARPGen源码地址:GitHub - intel/yarpgen: Yet Another Random Program Generator

        本文中使用的源码为YARPGen的最新版本V2.0。首先将下载的源码包解压,进入解压后的文件夹:

cd yarpgen-main

        然后建立编译文件夹并执行编译:

mkdir build
cd build
cmake ..
make

        编译完成后,build文件夹中会有YARPGen的同名可执行文件,执行该文件即可随机生成测试程序。下面进行一次生成实验:

mkdir test
cd test
../yarpgen -o .

        此时test文件夹中就能找到由YARPGen所生成的测试程序:

        可以看到生成了3个文件:init.h声明了测试用例中的全局变量和数据结构;func.cpp中包含了真正的测试代码,并在注释中标注了生成该用例时的YARPGen版本、生成时间、种子和生成命令;driver.cpp中对全局变量进行初始化,main函数调用测试函数并计算测试结果的校验和。

        YARPGen的命令行选项可通过-h命令查看:

    

        除了yarpgen这个可执行文件之外,Github的官方文档中还推荐使用run_gen.py脚本来自动进行编译器验证,该脚本的路径为yarpgen_main/scripts/run_gen.py。同在该文件夹中的test_sets.txt文件规定了使用run_gen.py脚本测试时所用到的编译器及编译选项,使用者可以编辑test_sets.txt以测试各种不同的编译器和编译选项的组合。使用如下命令运行该脚本进行测试:

./run_gen.py

        run_gen.py脚本运行后的输出如下图所示:

 

         运行结束后打印的结果概要中,我们可以看到该次脚本运行共生成了77个不同的测试程序,全部生成成功。在gcc与clang使用各个不同编译选项的情况下,虽然存在编译失败以及运行失败的情况,但是都没有出现不同的运行结果,也就是说这组测试用例没有发现编译器Bug。

         该脚本运行的详细结果可在yarpgen_main/scripts/testing/result文件夹中查看,其中对编译器以及各种不同的运行结果进行了分类,如下图所示:

         在yarpgen-main/scripts/testing/result/gcc/compfail文件夹下,我们可以看到gcc出现编译失败的所有测试的详细信息都被保存为文件夹的形式,这些文件夹以生成该用例时所使用的种子命名。每次测试的文件夹包含测试程序及其Makefile,以及本次测试情况的详细记录log.txt。 

        我们可以通过人工分析yarpgen-main/scripts/testing/result文件夹中的这些内容来进一步确认编译器Bug的位置。

猜你喜欢

转载自blog.csdn.net/ClarkCC/article/details/128526091