IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 开发测试 -> fuzz对测试用例作用的一些函数 -> 正文阅读

[开发测试]fuzz对测试用例作用的一些函数


1、sync_fuzzers(char **argv)该函数是读取其他fuzz的queue中的case文件,然后保存到自己的queue里


/* Grab interesting test cases from other fuzzers. */

static void sync_fuzzers(char** argv) {

  DIR* sd;
  struct dirent* sd_ent;
  u32 sync_cnt = 0;

  sd = opendir(sync_dir);打开sync_dir文件夹
  if (!sd) PFATAL("Unable to open '%s'", sync_dir);

  stage_max = stage_cur = 0;
  cur_depth = 0;

  /* Look at the entries created for every other fuzzer in the sync directory. */

  while ((sd_ent = readdir(sd))) {while循环遍历sync_dir下面的所有文件

    static u8 stage_tmp[128];

    DIR* qd;
    struct dirent* qd_ent;
    u8 *qd_path, *qd_synced_path;
    u32 min_accept = 0, next_min_accept;

    s32 id_fd;

    /*跳过点文件和我们自己的输出目录*/


    if (sd_ent->d_name[0] == '.' || !strcmp(sync_id, sd_ent->d_name)) continue;
    跳过.开头的文件和sync_id即我们自己的输出文件夹

   
    /*跳过没有队列/子目录的任何内容*/

    qd_path = alloc_printf("%s/%s/queue", sync_dir, sd_ent->d_name);

    if (!(qd = opendir(qd_path))) {
      ck_free(qd_path);
      continue;
    }

    /*检索上次看到的测试用例的ID*/

    qd_synced_path = alloc_printf("%s/.synced/%s", out_dir, sd_ent->d_name);

    id_fd = open(qd_synced_path, O_RDWR | O_CREAT, 0600);打开out_dir/.synced/sd_ent->d_name,返回到id_fd

    if (id_fd < 0) PFATAL("Unable to create '%s'", qd_synced_path);

    if (read(id_fd, &min_accept, sizeof(u32)) > 0) 
    读取out_dir/.synced/sd_ent->d_name文件即id_fd里的前(sizeof(u32)4个字节到min_accept里
      lseek(id_fd, 0, SEEK_SET);然后lseek从新调整文件内指针到开头。

    next_min_accept = min_accept;
    设置next_min_accept为min_accept,这个值代表之前从这个文件夹里读取到的最后一个queue的id。

     /* 显示数据统计 */     

    sprintf(stage_tmp, "sync %u", ++sync_cnt);sync_cnt计数加一,由"sync %u"格式化到stage_tmp中。
    stage_name = stage_tmp;
    stage_cur  = 0;
    stage_max  = 0;

    /*接下来利用一个while循环对于此fuzzer排队的每个文件,解析ID并查看我们之前是否曾看过它;
     如果没有,执行此个测试用例。 */

    while ((qd_ent = readdir(qd))) {利用readdir(qd)进一步取出目录中的文件

      u8* path;
      s32 fd;
      struct stat st;

      if (qd_ent->d_name[0] == '.' ||
          sscanf(qd_ent->d_name, CASE_PREFIX "%06u", &syncing_case) != 1 || 
          syncing_case < min_accept) continue;
          跳过.开头的文件和标识小于min_accept的文件,因为这些文件应该已经被sync过了。

      /*下面就是新的了*/

      if (syncing_case >= next_min_accept)
        next_min_accept = syncing_case + 1;

      path = alloc_printf("%s/%s", qd_path, qd_ent->d_name);

      /* 如果其他模糊器正在恢复,请允许此操作失败。。。 */

      fd = open(path, O_RDONLY);只读的方式打开qd_path/qd_ent->d_name返回为fd

      if (fd < 0) {
         ck_free(path);
         continue;
      }

      if (fstat(fd, &st)) PFATAL("fstat() failed");

      /* 忽略大小为零或过大的文件。*/
	开始同步这个
      if (st.st_size && st.st_size <= MAX_FILE) {

        u8  fault;
        u8* mem = mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);将fd对应的文件映射到进程空间中,返回u8 *mem

        if (mem == MAP_FAILED) PFATAL("Unable to mmap '%s'", path);
        
/*看看会发生什么。我们依赖save_if_interest()来捕获主要错误并保存测试用例*/

        write_to_testcase(mem, st.st_size);调用write_to_testcase(mem, st.st_size)将其写到outfile中。

        fault = run_target(argv, exec_tmout);接着run_target运行对应文件,返回fault。

        if (stop_soon) return;

        syncing_party = sd_ent->d_name;
        queued_imported += save_if_interesting(argv, mem, st.st_size, fault);
        调用save_if_interesting(argv, mem, st.st_size, fault)将感兴趣的样本保存。
        syncing_party = 0;

        munmap(mem, st.st_size);调用 munmap(mem, st.st_size) 接触映射

        if (!(stage_cur++ % stats_update_freq)) show_stats();
        然后 stage_cur++ % stats_update_freq 如果是0即循环到一个周期,如果是1则输出对应的fuzz信息

      }

      ck_free(path);
      close(fd);

    }

    ck_write(id_fd, &next_min_accept, sizeof(u32), qd_synced_path);&next_min_accept对应文件中的内容写到id_fd对应的文件中。

	关闭对应的文件/目录描述等。
    close(id_fd);
    closedir(qd);
    ck_free(qd_path);
    ck_free(qd_synced_path);
    
  }  

  closedir(sd);

}

2、trim_case(char **argv, struct queue_entry *q, u8 *in_buf)对于测试用例进行修剪

/*在进行确定性检查时,修剪所有新的测试用例以节省周期。修剪器使用文件大小的1/16和1/1024之间的两个增量的幂,以保持该阶段短而甜美*/
static u8 trim_case(char** argv, struct queue_entry* q, u8* in_buf) {

  static u8 tmp[64];
  static u8 clean_trace[MAP_SIZE];

  u8  needs_write = 0, fault = 0;
  u32 trim_exec = 0;
  u32 remove_len;
  u32 len_p2;

 /*虽然微调器在检测到变量行为时用处不大,但它在一定程度上仍然有效,因此我们不检查这一点*/

  if (q->len < 5) return 0;如果这个case的大小len小于5字节,就直接返回

  stage_name = tmp;令stage_name指向tmp数组首位
  bytes_trim_in += q->len;bytes_trim_in计数加上当前的q->len。
  bytes_trim_in代表被trim过的字节数



  len_p2 = next_p2(q->len);接着找出使得2^x > q->len的最小的x,作为len_p2

  remove_len = MAX(len_p2 / TRIM_START_STEPS, TRIM_MIN_BYTES);
  设置remove_len为MAX(len_p2 / TRIM_START_STEPS, TRIM_MIN_BYTES)
  即len_p2/16,与4中最大的那个,作为步长。

	/*继续,直到步数太高或跨步太小*/

  while (remove_len >= MAX(len_p2 / TRIM_END_STEPS, TRIM_MIN_BYTES)) {
  进入while循环,终止条件是remove_len小于终止步长len_p2的1/1024,每轮循环步长会除2
  
    u32 remove_pos = remove_len;设置remove_pos的值为remove_len

    sprintf(tmp, "trim %s/%s", DI(remove_len), DI(remove_len));
    读入"trim %s/%s", DI(remove_len), DI(remove_len)到tmp中, 即stage_name = “trim 512/512”

    stage_cur = 0;
    stage_max = q->len / remove_len;

    while (remove_pos < q->len) {
    进入while循环,remove_pos < q->len,即每次前进remove_len个步长,直到整个文件都被遍历完为止

      u32 trim_avail = MIN(remove_len, q->len - remove_pos);
      u32 cksum;

      write_with_gap(in_buf, q->len, remove_pos, trim_avail);

      fault = run_target(argv, exec_tmout);
      trim_execs++;计数器加一

      if (stop_soon || fault == FAULT_ERROR) goto abort_trimming;
      如果设置了stop_soon或者fault == FAULT_ERROR,直接跳转到abort_trimming

     /*请注意,我们在这里不跟踪崩溃或挂起;也许是TODO*/

      cksum = hash32(trace_bits, MAP_SIZE, HASH_CONST);计算当前trace_bits的hash32为cksum。

 

      if (cksum == q->exec_cksum) {如果相等

        u32 move_tail = q->len - remove_pos - trim_avail;
		从q->len中减去remove_len个字节,并由此重新计算出一个len_p2,这里注意一下while (remove_len >= MAX(len_p2 / TRIM_END_STEPS, TRIM_MIN_BYTES)
        q->len -= trim_avail;
        len_p2  = next_p2(q->len);

        memmove(in_buf + remove_pos, in_buf + remove_pos + trim_avail, 
                move_tail);
                从in_buf + remove_pos + trim_avail复制move_tail个字节到in_buf + remove_pos

       /*让我们保存一个干净的跟踪,它将由完成修剪后update_bitmap_score*/

        if (!needs_write) {

          needs_write = 1;
          memcpy(clean_trace, trace_bits, MAP_SIZE);拷贝trace_bits到clean_trace

        }

      } else remove_pos += remove_len;如果不等,remove_pos前移remove_len个字节。

     /*由于这可能会很慢,请不时更新屏幕*/

      if (!(trim_exec++ % stats_update_freq)) show_stats();
      stage_cur++;

    }

    remove_len >>= 1;

  }

 /*如果我们对in_buf进行了更改,我们还需要更新测试用例的磁盘版本*/

  if (needs_write) {如果needs_write为1

    s32 fd;
	删除原来的q->fname,创建一个新的q->fname,将in_buf里的内容写入,然后用clean_trace恢复trace_bits的值。
    unlink(q->fname); /* ignore errors */
	
    fd = open(q->fname, O_WRONLY | O_CREAT | O_EXCL, 0600);

    if (fd < 0) PFATAL("Unable to create '%s'", q->fname);

    ck_write(fd, in_buf, q->len, q->fname);
    close(fd);

    memcpy(trace_bits, clean_trace, MAP_SIZE);
    update_bitmap_score(q);进行一次update_bitmap_score

  }

abort_trimming:

  bytes_trim_out += q->len;
  return fault;

}

3、u32 calculate_score(struct queue_entry *q)根据queue entry的执行速度、覆盖到的path数和路径深度来评估出一个得分,这个得分perf_score在后面havoc的时候使用。


/*计算案例可取性得分,以调整浩劫模糊的长度。fuzz_one()的帮助函数。也许这些常量中的一些应该进入config.h*/


static u32 calculate_score(struct queue_entry* q) {

  u32 avg_exec_us = total_cal_us / total_cal_cycles;首先计算平均时间
  u32 avg_bitmap_size = total_bitmap_size / total_bitmap_entries;计算平均bitmap大小
  u32 perf_score = 100;定义初始的perf_score = 100

/*根据此路径的执行速度与全局平均值进行比较,调整分数。乘数范围从0.1x到3x。快速输入比模糊输入便宜,所以我们给它们更多的播放时间*/
	接下来通过给q->exec_us乘一个系数,判断他和avg_exec_us的大小来调整perf_score。
  if (q->exec_us * 0.1 > avg_exec_us) perf_score = 10;
  else if (q->exec_us * 0.25 > avg_exec_us) perf_score = 25;
  else if (q->exec_us * 0.5 > avg_exec_us) perf_score = 50;
  else if (q->exec_us * 0.75 > avg_exec_us) perf_score = 75;
  else if (q->exec_us * 4 < avg_exec_us) perf_score = 300;
  else if (q->exec_us * 3 < avg_exec_us) perf_score = 200;
  else if (q->exec_us * 2 < avg_exec_us) perf_score = 150;

  /* Adjust score based on bitmap size. The working theory is that better
     coverage translates to better targets. Multiplier from 0.25x to 3x. */
  然后通过给q->bitmap_size 乘一个系数,判断与avg_bitmap_size的大小关系来调整perf_score。
  if (q->bitmap_size * 0.3 > avg_bitmap_size) perf_score *= 3;
  else if (q->bitmap_size * 0.5 > avg_bitmap_size) perf_score *= 2;
  else if (q->bitmap_size * 0.75 > avg_bitmap_size) perf_score *= 1.5;
  else if (q->bitmap_size * 3 < avg_bitmap_size) perf_score *= 0.25;
  else if (q->bitmap_size * 2 < avg_bitmap_size) perf_score *= 0.5;
  else if (q->bitmap_size * 1.5 < avg_bitmap_size) perf_score *= 0.75;

  /* Adjust score based on handicap. Handicap is proportional to how late
     in the game we learned about this path. Latecomers are allowed to run
     for a bit longer until they catch up with the rest. */

  if (q->handicap >= 4) {如果q->handicap大于等于4

    perf_score *= 4;perf_score乘4.
    q->handicap -= 4;

  } else if (q->handicap) {

    perf_score *= 2;
    q->handicap--;

  }
/*基于输入深度的最终调整,假设模糊更深的测试用例更有可能揭示传统模糊器无法发现的东西*/

  switch (q->depth) {

    case 0 ... 3:   break;
    case 4 ... 7:   perf_score *= 2; break;
    case 8 ... 13:  perf_score *= 3; break;
    case 14 ... 25: perf_score *= 4; break;
    default:        perf_score *= 5;

  }

最后保证我们调整后的不会超出最大界限。

  if (perf_score > HAVOC_MAX_MULT * 100) perf_score = HAVOC_MAX_MULT * 100;

  return perf_score;

}

4、u8 common_fuzz_stuff(char **argv, u8 *out_buf, u32 len)写出修改后的测试用例,运行程序,处理result与错误等。

5、void write_to_testcase(void *mem, u32 len)将从mem中读取len个字节,写入到.cur_input中

6、u8 save_if_interesting(char **argv, void *mem, u32 len, u8 fault)检查这个case的执行结果是否是interesting的

/*检查例程模糊化过程中execve()的结果是否有趣,
如果是,保存或排队输入测试用例以供进一步分析。
如果条目已保存,则返回1,否则返回0*/

static u8 save_if_interesting(char** argv, void* mem, u32 len, u8 fault) {

  u8  *fn = "";
  u8  hnb;
  s32 fd;
  u8  keeping = 0, res;

  if (fault == crash_mode) {如果fault等于crash_mode。

    /* Keep only if there are new bits in the map, add to queue for
       future fuzzing, etc. */
	查看此时是否出现了newbits。
	如果没有的话若设置了crash_mode,则total_crashes计数加一。return 0
    if (!(hnb = has_new_bits(virgin_bits))) {
      if (crash_mode) total_crashes++;
      return 0;
    }    

#ifndef SIMPLE_FILES
若出现了newbits则调用 fn = alloc_printf("%s/queue/id:%06u,%s", out_dir, queued_paths,describe_op(hnb));拼接出路径fn
    fn = alloc_printf("%s/queue/id:%06u,%s", out_dir, queued_paths,
                      describe_op(hnb));

#else

    fn = alloc_printf("%s/queue/id_%06u", out_dir, queued_paths);

#endif /* ^!SIMPLE_FILES */

    add_to_queue(fn, len, 0);通过调用add_to_queue(fn, len, 0)将其插入队列。

    if (hnb == 2) {如果hnb==2成立。(有新路径发现)
      queue_top->has_new_cov = 1;设置queue_top->has_new_cov为1
      queued_with_cov++;计数器加一
    }

    queue_top->exec_cksum = hash32(trace_bits, MAP_SIZE, HASH_CONST);
    利用hash32从新计算trace_bits的哈希值,将其设置为queue_top->exec_cksum

    /*尝试在线校准;成功时,还调用update_bitmap_score()*/

    res = calibrate_case(argv, queue_top, mem, queue_cycle - 1, 0);调用calibrate_case进行用例校准,评估当前队列。

    if (res == FAULT_ERROR)
      FATAL("Unable to execute target application");

    fd = open(fn, O_WRONLY | O_CREAT | O_EXCL, 0600);
    if (fd < 0) PFATAL("Unable to create '%s'", fn);
    ck_write(fd, mem, len, fn);打开fn,将mem的内容写入文件fn。
    close(fd);

    keeping = 1;

  }

  switch (fault) {

    case FAULT_TMOUT:

    /*超时不是很有趣,但我们仍有义务保留少量样本。我们使用挂起特定位图中新位的存在作为唯一性的信号。在“dume”模式下,我们只保留一切*/

      total_tmouts++;计数器加一

      if (unique_hangs >= KEEP_UNIQUE_HANG) return keeping;

      if (!dumb_mode) {如果不是dume_mode

#ifdef WORD_SIZE_64
        simplify_trace((u64*)trace_bits);调用simplify_trace对trace_bits进行调整
#else
        simplify_trace((u32*)trace_bits);
#endif /* ^WORD_SIZE_64 */

        if (!has_new_bits(virgin_tmout)) return keeping;若没有新的超时路径,直接return keeping。

      }

      unique_tmouts++;计数器加一



      if (exec_tmout < hang_tmout) {

        u8 new_fault;
        write_to_testcase(mem, len);将mem的内容写到outfile
        new_fault = run_target(argv, hang_tmout);然后再次调用run_target运行一次,返回new_fault。

        
        if (!stop_soon && new_fault == FAULT_CRASH) goto keep_as_crash;
        如果未设置stop_soon,并且new_fault为FAULT_CRASH,那么跳转到keep_as_crash

        if (stop_soon || new_fault != FAULT_TMOUT) return keeping;

      }

#ifndef SIMPLE_FILES

      fn = alloc_printf("%s/hangs/id:%06llu,%s", out_dir,
                        unique_hangs, describe_op(0));

#else

      fn = alloc_printf("%s/hangs/id_%06llu", out_dir,
                        unique_hangs);

#endif /* ^!SIMPLE_FILES */

      unique_hangs++;

      last_hang_time = get_cur_time();

      break;

    case FAULT_CRASH:

keep_as_crash:

      /* This is handled in a manner roughly similar to timeouts,
         except for slightly different limits and no need to re-run test
         cases. */

      total_crashes++;

      if (unique_crashes >= KEEP_UNIQUE_CRASH) return keeping;

      if (!dumb_mode) {

#ifdef WORD_SIZE_64
        simplify_trace((u64*)trace_bits);
#else
        simplify_trace((u32*)trace_bits);
#endif /* ^WORD_SIZE_64 */

        if (!has_new_bits(virgin_crash)) return keeping;

      }

      if (!unique_crashes) write_crash_readme();

#ifndef SIMPLE_FILES

      fn = alloc_printf("%s/crashes/id:%06llu,sig:%02u,%s", out_dir,
                        unique_crashes, kill_signal, describe_op(0));

#else

      fn = alloc_printf("%s/crashes/id_%06llu_%02u", out_dir, unique_crashes,
                        kill_signal);

#endif /* ^!SIMPLE_FILES */

      unique_crashes++;

      last_crash_time = get_cur_time();
      last_crash_execs = total_execs;

      break;

    case FAULT_ERROR: FATAL("Unable to execute target application");

    default: return keeping;

  }

  /* If we're here, we apparently want to save the crash or hang
     test case, too. */

  fd = open(fn, O_WRONLY | O_CREAT | O_EXCL, 0600);
  if (fd < 0) PFATAL("Unable to create '%s'", fn);
  ck_write(fd, mem, len, fn);
  close(fd);

  ck_free(fn);

  return keeping;

}
  开发测试 最新文章
pytest系列——allure之生成测试报告(Wind
某大厂软件测试岗一面笔试题+二面问答题面试
iperf 学习笔记
关于Python中使用selenium八大定位方法
【软件测试】为什么提升不了?8年测试总结再
软件测试复习
PHP笔记-Smarty模板引擎的使用
C++Test使用入门
【Java】单元测试
Net core 3.x 获取客户端地址
上一篇文章      下一篇文章      查看所有文章
加:2022-09-04 01:41:58  更:2022-09-04 01:42:25 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/17 22:47:38-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码