about summary refs log tree commit diff
path: root/include/persistent_replay.h
blob: 58b22fb4b3027d7c8525dd141d2705e4fff40608 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
#ifndef _HAVE_PERSISTENT_REPLAY_H
#define _HAVE_PERSISTENT_REPLAY_H

#include <dirent.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>

static unsigned short int is_replay_record;
static unsigned int       replay_record;
static unsigned int       replay_record_cnt;
static char               replay_record_path[PATH_MAX];
static char              *replay_record_dir;
static struct dirent    **record_list;

#ifdef AFL_PERSISTENT_REPLAY_ARGPARSE
static char **record_arg = NULL;
#endif  // AFL_PERSISTENT_REPLAY_ARGPARSE

static int select_files(const struct dirent *dirbuf) {

  char fn[4096];

  if (dirbuf->d_name[0] == '.') {

    return 0;

  } else {

    snprintf(fn, sizeof(fn), "RECORD:%06u", replay_record);
    return !!strstr(dirbuf->d_name, fn);

  }

}

static int compare_files(const struct dirent **da, const struct dirent **db) {

  unsigned int c1 = 0, c2 = 0;

  sscanf((*da)->d_name, "RECORD:%*u,cnt:%06u", &c1);
  sscanf((*db)->d_name, "RECORD:%*u,cnt:%06u", &c2);

  return c1 - c2;

}

__attribute__((destructor)) static void __afl_record_replay_destroy(void) {

  for (int i = 0; i < replay_record_cnt; i++) {

    free(record_list[i]);

  }

  free(record_list);

}

__attribute__((constructor)) static void __afl_record_replay_init(
#ifdef AFL_PERSISTENT_REPLAY_ARGPARSE
    int argc, char **argv
#endif  // AFL_PERSISTENT_REPLAY_ARGPARSE
) {

#ifdef AFL_PERSISTENT_REPLAY_ARGPARSE
  char **argp;
#endif  // AFL_PERSISTENT_REPLAY_ARGPARSE

  struct stat sb;

  /* caveat: if harness uses @@ and we don't pass it, it will regardless loop
   * the number of iterations defined for AFL_LOOP (on the same file)*/
  if (!(is_replay_record = !!getenv("AFL_PERSISTENT_REPLAY"))) {

    // printf("[warning] AFL_PERSISTENT_REPLAY not set.\n");
    return;

  }

  replay_record = atoi(getenv("AFL_PERSISTENT_REPLAY"));
  replay_record_dir = getenv("AFL_PERSISTENT_DIR");

  if (!(stat(replay_record_dir, &sb) == 0 && S_ISDIR(sb.st_mode))) {

    fprintf(stderr, "[error] Can't find the requested record directory!\n");
    is_replay_record = 0;
    return;

  }

  replay_record_cnt = scandir(replay_record_dir ? replay_record_dir : "./",
                              &record_list, select_files, compare_files);

  if (!replay_record_cnt) {

    fprintf(stderr, "[error] Can't find the requested record!\n");
    is_replay_record = 0;

  }

#ifdef AFL_PERSISTENT_REPLAY_ARGPARSE
  argp = argv;
  while (*argp) {

    if (!strcmp(*argp, "@@")) {

      record_arg = argp;
      *record_arg = replay_record_path;
      break;

    }

    ++argp;

  }

#endif  // AFL_PERSISTENT_REPLAY_ARGPARSE

}

/* only used if explictly included for compatibility
   compiling without afl-cc */

#ifdef AFL_COMPAT

  #ifndef PATH_MAX
    #define PATH_MAX 4096
  #endif

  #define FUZZ_BUF_SIZE 1024000

  // extern ssize_t read(int fildes, void *buf, size_t nbyte);

  // extern int __afl_persistent_loop(unsigned int max_cnt);
  // extern unsigned char fuzz_buf[];

  #ifndef __AFL_HAVE_MANUAL_CONTROL
    #define __AFL_HAVE_MANUAL_CONTROL
  #endif

  #define __AFL_FUZZ_TESTCASE_LEN (read(0, fuzz_buf, FUZZ_BUF_SIZE))
  #define __AFL_FUZZ_TESTCASE_BUF fuzz_buf
  #define __AFL_FUZZ_INIT() void sync(void);
  #define __AFL_INIT() sync()
  #define __AFL_LOOP(x) __afl_persistent_loop(x)

unsigned char fuzz_buf[FUZZ_BUF_SIZE];

int __afl_persistent_loop(unsigned int max_cnt) {

  static unsigned int       cycle_cnt = 1;
  static unsigned short int inited = 0;
  char                      tcase[PATH_MAX];

  if (is_replay_record) {

    if (!inited) {

      cycle_cnt = replay_record_cnt;
      inited = 1;

    }

    snprintf(tcase, PATH_MAX, "%s/%s",
             replay_record_dir ? replay_record_dir : "./",
             record_list[replay_record_cnt - cycle_cnt]->d_name);

  #ifdef AFL_PERSISTENT_REPLAY_ARGPARSE
    if (record_arg) {

      *record_arg = tcase;

    } else

  #endif  // AFL_PERSISTENT_REPLAY_ARGPARSE
    {

      int fd = open(tcase, O_RDONLY);
      dup2(fd, 0);
      close(fd);

    }

  } else {

    if (!inited) {

      cycle_cnt = max_cnt;
      inited = 1;

    }

  }

  return cycle_cnt--;

}

#endif  // AFL_COMPAT

#endif  // _HAVE_PERSISTENT_REPLAY_H