录制失败的模糊测试以便稍后重新执行

时间:2014-12-10 15:49:24

标签: c++ c fuzzing

我正在寻找一种更好的方法来保存模糊测试中的有趣结果,以便稍后重复。目前的计划是将失败的输入序列化并将其作为测试用例写出来。假设我们要测试:

int function_under_test(struct arbitrary *);

假设此函数在任意结构中使用半随机数据运行几千次并且失败两次。我希望能够重现失败的案例,以确定错误修复是否成功。

我可以看到实现这一目标的两种策略:

  1. 强制确定性 - 存储随机种子,记下哪个测试编号失败
  2. 将导致失败的结构存储在某处持久
  3. 选项1需要充分注意如何生成模糊测试用例以允许重现性,并且需要对随机数生成器进行重新设置。选项2需要一些繁琐的代码生成,可能会被序列化库减少。我希望听到有一个我看不到的第三种选择。

    我对选项2的计划主要是将结构序列化为一个字符数组,将所述数组写入文本文件,然后将其读回并按需转换为结构。以下是POD的概念验证,更复杂的类型需要更复杂的序列化。例子是C,但是基于C ++的答案也是受欢迎的。

    #include <stdio.h> /* for printf in main() */
    #include <stddef.h> /* size_t */
    
    #define SIZEFOO 5
    struct arbitrary 
    {
      int foo[SIZEFOO];
      double bar;
    };
    
    int equal_arbitrary(struct arbitrary *lhs, struct arbitrary *rhs)
    {
      /* return 1 if structs are equal, 0 if not */
      for (int i=0; i < SIZEFOO; i++)
      {
        if (lhs->foo[i] != rhs->foo[i]) {return 0;}
      }
      if (lhs->bar != rhs->bar) {return 0;}
      return 1;
    }
    
    void arbitrary_to_bytes(struct arbitrary *input, char *output)
    {
      union
      {
        struct arbitrary data;
        unsigned char view[sizeof(struct arbitrary)];
      } local;
      size_t i;
    
      local.data = *input;
      for (i = 0; i < sizeof(struct arbitrary); i++)
        {
          output[i] = local.view[i];
        }
      return;
    }
    
    void bytes_to_arbitrary(char *input, struct arbitrary *output)
    {
      union
      {
        struct arbitrary data;
        unsigned char view[sizeof(struct arbitrary)];
      } local;
      size_t i;
    
      for (i = 0; i < sizeof(struct arbitrary); i++)
        {
          local.view[i] = input[i];
        }
    
      *output = local.data;
      return;
    }
    
    int main(void)
    {
      struct arbitrary original;
      struct arbitrary copied;
      unsigned char working[sizeof (struct arbitrary)];
    
      for (int i=0; i < SIZEFOO; i++) { original.foo[i] = 3 + i*i; }
      original.bar = 3.14;
    
      arbitrary_to_bytes(&original,working);
      bytes_to_arbitrary(working,&copied);
    
      if (equal_arbitrary(&original,&copied))
        {
          printf("PASS\n");
        }
      else
        {
          printf("FAIL\n");
        }
    
      return 0;
    }
    

    在执行期间,当(模糊)测试用例失败时,其中一个副作用是将输入结构转换为字节数组(如上所述),并将类似下面的内容写入随后可以成为其中的文件确定性测试套件:

    result function_under_test_fuzz_123(void) /* 123 a unique number */
    {
      int rc;
      struct arbitrary;
      unsigned char test_data[] = "byte array stored as ascii string";
    
      bytes_to_arbitrary(test_data, &arbitrary);
    
      rc = function_under_test(&arbitrary);
    
      /* Do whatever is needed to determine if the function failed */
      if (rc) {return PASS;} else {return FAIL;}
    }
    

    我相信通过union的类型惩罚是有效的,前提是另一个值是char数组。当函数输入不是普通旧数据时,(反)序列化步骤变得相当复杂,但一般策略仍然可用。将测试用例存储为ascii可能会产生一些相当大的文本文件。

    在花时间设置基础设施来执行上述操作之前(主要是代码生成器,对测试框架进行一些修改),似乎值得向社区询问是否有更好的方法。我怀疑我是第一个认为可重复的模糊测试是个好主意的人!

    更改为C ++意味着模板而不是外部代码生成器,并在BOOST中找到一个友好的序列化库,但是没有改变&#34;最好的方法来保存失败的模糊测试结果?&# 34。

    谢谢

1 个答案:

答案 0 :(得分:1)

如果使用C ++ 11(及以上)<random>标头(或Boost.Random),只要使用相同的生成器,存储种子就足以保证确定性。 在重新播种随机数生成器时会有些麻烦。在实践中非常简单,只要你阅读了这些手册。

否则,问题基本上是&#34;如何序列化数据&#34;,它在互联网上有如此多的答案以多种形式存在,甚至对它来说都不是很敏感在这里概述它们。它们是模糊测试的结果还是谷物品牌在美国的分销这一事实在这里并不重要。