added andrea's splicing, added cycle_schedules

This commit is contained in:
van Hauser
2020-07-21 20:53:51 +02:00
parent 9a33a29b4a
commit ce9b4698fe
8 changed files with 834 additions and 122 deletions

View File

@ -24,6 +24,11 @@
*/
#include "afl-fuzz.h"
#include <string.h>
static u8 *strnstr(const u8 *s, const u8 *find, size_t slen);
static u32 string_replace(u8 **out_buf, s32 *temp_len, u32 pos, u8 *from,
u8 *to);
/* MOpt */
@ -362,6 +367,450 @@ static void locate_diffs(u8 *ptr1, u8 *ptr2, u32 len, s32 *first, s32 *last) {
#endif /* !IGNORE_FINDS */
#define BUF_PARAMS(name) (void **)&afl->name##_buf, &afl->name##_size
/* search a string */
static u8 *strnstr(const u8 *s, const u8 *find, size_t slen) {
char c, sc;
size_t len;
if ((c = *find++) != '\0') {
len = strlen(find);
do {
do {
if (slen-- < 1 || (sc = *s++) == '\0') return (NULL);
} while (sc != c);
if (len > slen) return (NULL);
} while (strncmp(s, find, len) != 0);
s--;
}
return ((u8 *)s);
}
/* replace between deliminators, if rep == NULL, then we will duplicate the
* target */
static u32 delim_replace(u8 **out_buf, s32 *temp_len, size_t pos,
const u8 *ldelim, const u8 *rdelim, u8 *rep) {
u8 *end_buf = *out_buf + *temp_len;
u8 *ldelim_start = strnstr(*out_buf + pos, ldelim, *temp_len - pos);
if (ldelim_start != NULL) {
u32 max = (end_buf - ldelim_start - 1 > AFL_TXT_STRING_MAX_LEN
? AFL_TXT_STRING_MAX_LEN
: end_buf - ldelim_start - 1);
if (max > 0) {
u8 *rdelim_end = strnstr(ldelim_start + 1, rdelim, max);
if (rdelim_end != NULL) {
u32 rep_len, delim_space_len = rdelim_end - ldelim_start - 1, xtra = 0;
if (rep != NULL) {
rep_len = (u32)strlen(rep);
} else { // NULL? then we copy the value in between the delimiters
rep_len = delim_space_len;
delim_space_len = 0;
rep = ldelim_start + 1;
xtra = rep_len;
}
if (rep_len != delim_space_len) {
memmove(ldelim_start + rep_len + xtra + 1, rdelim_end,
*temp_len - (rdelim_end - *out_buf));
}
memcpy(ldelim_start + 1, rep, rep_len);
*temp_len = (*temp_len - delim_space_len + rep_len);
return 1;
}
}
}
return 0;
}
static u32 delim_swap(u8 **out_buf, s32 *temp_len, size_t pos, const u8 *ldelim,
const u8 *mdelim, const u8 *rdelim) {
u8 *out_buf_end = *out_buf + *temp_len;
u32 max = (*temp_len - pos > AFL_TXT_STRING_MAX_LEN ? AFL_TXT_STRING_MAX_LEN
: *temp_len - pos);
u8 *ldelim_start = strnstr(*out_buf + pos, ldelim, max);
if (ldelim_start != NULL) {
max = (out_buf_end - ldelim_start - 1 > AFL_TXT_STRING_MAX_LEN
? AFL_TXT_STRING_MAX_LEN
: out_buf_end - ldelim_start - 1);
if (max > 1) {
u8 *mdelim_pos = strnstr(ldelim_start + 1, mdelim, max);
if (mdelim_pos != NULL) {
max = (out_buf_end - mdelim_pos - 1 > AFL_TXT_STRING_MAX_LEN
? AFL_TXT_STRING_MAX_LEN
: out_buf_end - mdelim_pos - 1);
if (max > 0) {
u8 *rdelim_end = strnstr(mdelim + 1, rdelim, max);
if (rdelim_end != NULL) {
u32 first_len = mdelim_pos - ldelim_start - 1;
u32 second_len = rdelim_end - mdelim_pos - 1;
u8 scratch[AFL_TXT_STRING_MAX_LEN];
memcpy(scratch, ldelim_start + 1, first_len);
if (first_len != second_len) {
memmove(ldelim_start + second_len + 1, mdelim_pos,
out_buf_end - mdelim_pos);
}
memcpy(ldelim_start + 1, mdelim_pos + 1, second_len);
if (first_len != second_len) {
memmove(mdelim_pos + first_len + 1, rdelim_end,
out_buf_end - rdelim_end);
}
memcpy(mdelim_pos + 1, scratch, first_len);
return 1;
}
}
}
}
}
return 0;
}
/* replace a string */
static u32 string_replace(u8 **out_buf, s32 *temp_len, u32 pos, u8 *from,
u8 *to) {
u8 *start = strnstr(*out_buf + pos, from, *temp_len - pos);
if (start) {
u32 from_len = strlen(from);
u32 to_len = strlen(to);
if (from_len != to_len) {
memmove(start + to_len, start + from_len,
*temp_len - from_len - (start - *out_buf));
}
memcpy(start, to, to_len);
*temp_len = (*temp_len - from_len + to_len);
return 1;
}
return 0;
}
/* Returns 1 if a mutant was generated and placed in out_buf, 0 if none
* generated. */
static int text_mutation(afl_state_t *afl, u8 **out_buf, s32 *orig_temp_len) {
s32 temp_len;
u32 pos, yes = 0,
mutations = rand_below(afl, AFL_TXT_STRING_MAX_MUTATIONS) + 1;
u8 *new_buf = ck_maybe_grow(BUF_PARAMS(out_scratch),
*orig_temp_len + AFL_TXT_STRING_MAX_MUTATIONS);
temp_len = *orig_temp_len;
memcpy(new_buf, *out_buf, temp_len);
for (u32 i = 0; i < mutations; i++) {
if (temp_len < AFL_TXT_MIN_LEN) { return 0; }
pos = rand_below(afl, temp_len - 1);
int choice = rand_below(afl, 72);
switch (choice) {
case 0: /* Semantic statement deletion */
yes += string_replace(out_buf, &temp_len, pos, "\n", "#");
break;
case 1:
yes += string_replace(out_buf, &temp_len, pos, "(", "(!");
break;
case 2:
yes += string_replace(out_buf, &temp_len, pos, "==", "!=");
break;
case 3:
yes += string_replace(out_buf, &temp_len, pos, "!=", "==");
break;
case 4:
yes += string_replace(out_buf, &temp_len, pos, "==", "<");
break;
case 5:
yes += string_replace(out_buf, &temp_len, pos, "<", "==");
break;
case 6:
yes += string_replace(out_buf, &temp_len, pos, "==", ">");
break;
case 7:
yes += string_replace(out_buf, &temp_len, pos, ">", "==");
break;
case 8:
yes += string_replace(out_buf, &temp_len, pos, "=", "<");
break;
case 9:
yes += string_replace(out_buf, &temp_len, pos, "=", ">");
break;
case 10:
yes += string_replace(out_buf, &temp_len, pos, "<", ">");
break;
case 11:
yes += string_replace(out_buf, &temp_len, pos, ">", "<");
break;
case 12:
yes += string_replace(out_buf, &temp_len, pos, "++", "--");
break;
case 13:
yes += string_replace(out_buf, &temp_len, pos, "--", "++");
break;
case 14:
yes += string_replace(out_buf, &temp_len, pos, "+", "-");
break;
case 15:
yes += string_replace(out_buf, &temp_len, pos, "+", "*");
break;
case 16:
yes += string_replace(out_buf, &temp_len, pos, "+", "/");
break;
case 17:
yes += string_replace(out_buf, &temp_len, pos, "+", "%");
break;
case 18:
yes += string_replace(out_buf, &temp_len, pos, "*", "-");
break;
case 19:
yes += string_replace(out_buf, &temp_len, pos, "*", "+");
break;
case 20:
yes += string_replace(out_buf, &temp_len, pos, "*", "/");
break;
case 21:
yes += string_replace(out_buf, &temp_len, pos, "*", "%");
break;
case 22:
yes += string_replace(out_buf, &temp_len, pos, "-", "+");
break;
case 23:
yes += string_replace(out_buf, &temp_len, pos, "-", "*");
break;
case 24:
yes += string_replace(out_buf, &temp_len, pos, "-", "/");
break;
case 25:
yes += string_replace(out_buf, &temp_len, pos, "-", "%");
break;
case 26:
yes += string_replace(out_buf, &temp_len, pos, "/", "-");
break;
case 27:
yes += string_replace(out_buf, &temp_len, pos, "/", "*");
break;
case 28:
yes += string_replace(out_buf, &temp_len, pos, "/", "+");
break;
case 29:
yes += string_replace(out_buf, &temp_len, pos, "/", "%");
break;
case 30:
yes += string_replace(out_buf, &temp_len, pos, "%", "-");
break;
case 31:
yes += string_replace(out_buf, &temp_len, pos, "%", "*");
break;
case 32:
yes += string_replace(out_buf, &temp_len, pos, "%", "/");
break;
case 33:
yes += string_replace(out_buf, &temp_len, pos, "%", "+");
break;
case 34:
yes += string_replace(out_buf, &temp_len, pos, " ", "|");
break;
case 35:
yes += string_replace(out_buf, &temp_len, pos, " ", "$");
break;
case 36:
yes += string_replace(out_buf, &temp_len, pos, "0", "1");
break;
case 37:
yes += string_replace(out_buf, &temp_len, pos, "1", "0");
break;
case 38:
yes += string_replace(out_buf, &temp_len, pos, " ", "`");
break;
case 39:
yes += string_replace(out_buf, &temp_len, pos, " ", "\"");
break;
case 40:
yes += string_replace(out_buf, &temp_len, pos, ";", " ");
break;
case 41:
yes += string_replace(out_buf, &temp_len, pos, "&&", "||");
break;
case 42:
yes += string_replace(out_buf, &temp_len, pos, "||", "&&");
break;
case 43:
yes += string_replace(out_buf, &temp_len, pos, "!", "");
break;
case 44:
yes += string_replace(out_buf, &temp_len, pos, "==", "=");
break;
case 45:
yes += string_replace(out_buf, &temp_len, pos, "--", "");
break;
case 46:
yes += string_replace(out_buf, &temp_len, pos, "<<", "<");
break;
case 47:
yes += string_replace(out_buf, &temp_len, pos, ">>", ">");
break;
case 48:
yes += string_replace(out_buf, &temp_len, pos, "<", "<<");
break;
case 49:
yes += string_replace(out_buf, &temp_len, pos, ">", ">>");
break;
case 50:
yes += string_replace(out_buf, &temp_len, pos, "\"", "'");
break;
case 51:
yes += string_replace(out_buf, &temp_len, pos, "'", "\"");
break;
case 52:
yes += string_replace(out_buf, &temp_len, pos, "(", "\"");
break;
case 53: /* Remove a semicolon delimited statement after a semicolon */
yes += delim_replace(out_buf, &temp_len, pos, ";", ";", ";");
break;
case 54: /* Remove a semicolon delimited statement after a left curly
brace */
yes += delim_replace(out_buf, &temp_len, pos, "}", ";", "}");
break;
case 55: /* Remove a curly brace construct */
yes += delim_replace(out_buf, &temp_len, pos, "{", "}", "");
break;
case 56: /* Replace a curly brace construct with an empty one */
yes += delim_replace(out_buf, &temp_len, pos, "{", "}", "{}");
break;
case 57:
yes += delim_swap(out_buf, &temp_len, pos, ";", ";", ";");
break;
case 58:
yes += delim_swap(out_buf, &temp_len, pos, "}", ";", ";");
break;
case 59: /* Swap comma delimited things case 1 */
yes += delim_swap(out_buf, &temp_len, pos, "(", ",", ")");
break;
case 60: /* Swap comma delimited things case 2 */
yes += delim_swap(out_buf, &temp_len, pos, "(", ",", ",");
break;
case 61: /* Swap comma delimited things case 3 */
yes += delim_swap(out_buf, &temp_len, pos, ",", ",", ",");
break;
case 62: /* Swap comma delimited things case 4 */
yes += delim_swap(out_buf, &temp_len, pos, ",", ",", ")");
break;
case 63: /* Just delete a line */
yes += delim_replace(out_buf, &temp_len, pos, "\n", "\n", "");
break;
case 64: /* Delete something like "const" case 1 */
yes += delim_replace(out_buf, &temp_len, pos, " ", " ", "");
break;
case 65: /* Delete something like "const" case 2 */
yes += delim_replace(out_buf, &temp_len, pos, "\n", " ", "");
break;
case 66: /* Delete something like "const" case 3 */
yes += delim_replace(out_buf, &temp_len, pos, "(", " ", "");
break;
case 67: /* Swap space delimited things case 1 */
yes += delim_swap(out_buf, &temp_len, pos, " ", " ", " ");
break;
case 68: /* Swap space delimited things case 2 */
yes += delim_swap(out_buf, &temp_len, pos, " ", " ", ")");
break;
case 69: /* Swap space delimited things case 3 */
yes += delim_swap(out_buf, &temp_len, pos, "(", " ", " ");
break;
case 70: /* Swap space delimited things case 4 */
yes += delim_swap(out_buf, &temp_len, pos, "(", " ", ")");
break;
case 71: /* Duplicate a single line of code */
yes += delim_replace(out_buf, &temp_len, pos, "\n", "\n", NULL);
break;
case 72: /* Duplicate a construct (most often, a non-nested for loop */
yes += delim_replace(out_buf, &temp_len, pos, "\n", "}", NULL);
break;
}
}
if (yes == 0 || temp_len <= 0) { return 0; }
swap_bufs(BUF_PARAMS(out), BUF_PARAMS(out_scratch));
*out_buf = new_buf;
*orig_temp_len = temp_len;
return 1;
}
/* Take the current entry from the queue, fuzz it for a while. This
function is a tad too long... returns 0 if fuzzed successfully, 1 if
skipped or bailed out. */
@ -1854,6 +2303,22 @@ havoc_stage:
/* We essentially just do several thousand runs (depending on perf_score)
where we take the input file and make random stacked tweaks. */
u32 r_max, r;
if (unlikely(afl->expand_havoc)) {
/* add expensive havoc cases here, they are activated after a full
cycle without finds happened */
r_max = 16 + ((afl->extras_cnt + afl->a_extras_cnt) ? 2 : 0) +
(afl->queue_cur->is_ascii ? AFL_TXT_BIAS : 0);
} else {
r_max = 15 + ((afl->extras_cnt + afl->a_extras_cnt) ? 2 : 0);
}
for (afl->stage_cur = 0; afl->stage_cur < afl->stage_max; ++afl->stage_cur) {
u32 use_stacking = 1 << (1 + rand_below(afl, HAVOC_STACK_POW2));
@ -1896,8 +2361,9 @@ havoc_stage:
}
switch (rand_below(
afl, 16 + ((afl->extras_cnt + afl->a_extras_cnt) ? 2 : 0))) {
retry_havoc:
switch ((r = rand_below(afl, r_max))) {
case 0:
@ -2191,176 +2657,203 @@ havoc_stage:
break;
}
case 15: {
/* Overwrite bytes with a randomly selected chunk from another
testcase or insert that chunk. */
default:
if (afl->queued_paths < 2) break;
if (likely(r <= 16 && (afl->extras_cnt || afl->a_extras_cnt))) {
/* Pick a random queue entry and seek to it. */
/* Values 15 and 16 can be selected only if there are any extras
present in the dictionaries. */
u32 tid;
do
tid = rand_below(afl, afl->queued_paths);
while (tid == afl->current_entry);
if (r == 15) {
struct queue_entry* target = afl->queue_buf[tid];
/* Overwrite bytes with an extra. */
/* Make sure that the target has a reasonable length. */
if (!afl->extras_cnt ||
(afl->a_extras_cnt && rand_below(afl, 2))) {
while (target && (target->len < 2 || target == afl->queue_cur))
target = target->next;
/* No user-specified extras or odds in our favor. Let's use an
auto-detected one. */
if (!target) break;
u32 use_extra = rand_below(afl, afl->a_extras_cnt);
u32 extra_len = afl->a_extras[use_extra].len;
u32 insert_at;
/* Read the testcase into a new buffer. */
if (extra_len > temp_len) { break; }
fd = open(target->fname, O_RDONLY);
insert_at = rand_below(afl, temp_len - extra_len + 1);
memcpy(out_buf + insert_at, afl->a_extras[use_extra].data,
extra_len);
if (unlikely(fd < 0)) { PFATAL("Unable to open '%s'", target->fname); }
} else {
u32 new_len = target->len;
u8 * new_buf = ck_maybe_grow(BUF_PARAMS(in_scratch), new_len);
/* No auto extras or odds in our favor. Use the dictionary. */
ck_read(fd, new_buf, new_len, target->fname);
u32 use_extra = rand_below(afl, afl->extras_cnt);
u32 extra_len = afl->extras[use_extra].len;
u32 insert_at;
close(fd);
if (extra_len > temp_len) { break; }
u8 overwrite = 0;
if (temp_len >= 2 && rand_below(afl, 2))
overwrite = 1;
else if (temp_len + HAVOC_BLK_XL >= MAX_FILE) {
if (temp_len >= 2) overwrite = 1;
else break;
}
insert_at = rand_below(afl, temp_len - extra_len + 1);
memcpy(out_buf + insert_at, afl->extras[use_extra].data,
extra_len);
if (overwrite) {
}
u32 copy_from, copy_to, copy_len;
break;
copy_len = choose_block_len(afl, new_len - 1);
if (copy_len > temp_len) copy_len = temp_len;
} else { // case 16
copy_from = rand_below(afl, new_len - copy_len + 1);
copy_to = rand_below(afl, temp_len - copy_len + 1);
u32 use_extra, extra_len,
insert_at = rand_below(afl, temp_len + 1);
u8 *ptr;
memmove(out_buf + copy_to, new_buf + copy_from, copy_len);
/* Insert an extra. Do the same dice-rolling stuff as for the
previous case. */
} else {
u32 clone_from, clone_to, clone_len;
clone_len = choose_block_len(afl, new_len);
clone_from = rand_below(afl, new_len - clone_len + 1);
clone_to = rand_below(afl, temp_len);
if (!afl->extras_cnt ||
(afl->a_extras_cnt && rand_below(afl, 2))) {
u8 * temp_buf =
ck_maybe_grow(BUF_PARAMS(out_scratch), temp_len + clone_len);
use_extra = rand_below(afl, afl->a_extras_cnt);
extra_len = afl->a_extras[use_extra].len;
ptr = afl->a_extras[use_extra].data;
/* Head */
} else {
memcpy(temp_buf, out_buf, clone_to);
use_extra = rand_below(afl, afl->extras_cnt);
extra_len = afl->extras[use_extra].len;
ptr = afl->extras[use_extra].data;
/* Inserted part */
}
memcpy(temp_buf + clone_to, new_buf + clone_from, clone_len);
if (temp_len + extra_len >= MAX_FILE) { break; }
/* Tail */
memcpy(temp_buf + clone_to + clone_len, out_buf + clone_to,
temp_len - clone_to);
out_buf = ck_maybe_grow(BUF_PARAMS(out), temp_len + extra_len);
swap_bufs(BUF_PARAMS(out), BUF_PARAMS(out_scratch));
out_buf = temp_buf;
temp_len += clone_len;
}
/* Tail */
memmove(out_buf + insert_at + extra_len, out_buf + insert_at,
temp_len - insert_at);
break;
/* Inserted part */
memcpy(out_buf + insert_at, ptr, extra_len);
}
temp_len += extra_len;
/* Values 15 and 16 can be selected only if there are any extras
present in the dictionaries. */
break;
case 16: {
}
/* Overwrite bytes with an extra. */
} else
if (!afl->extras_cnt || (afl->a_extras_cnt && rand_below(afl, 2))) {
switch (r) {
/* No user-specified extras or odds in our favor. Let's use an
auto-detected one. */
case 15: // fall through
case 17: {
u32 use_extra = rand_below(afl, afl->a_extras_cnt);
u32 extra_len = afl->a_extras[use_extra].len;
u32 insert_at;
/* Overwrite bytes with a randomly selected chunk from another
testcase or insert that chunk. */
if (extra_len > temp_len) { break; }
if (afl->queued_paths < 2) break;
insert_at = rand_below(afl, temp_len - extra_len + 1);
memcpy(out_buf + insert_at, afl->a_extras[use_extra].data,
extra_len);
/* Pick a random queue entry and seek to it. */
} else {
u32 tid;
do
tid = rand_below(afl, afl->queued_paths);
while (tid == afl->current_entry);
/* No auto extras or odds in our favor. Use the dictionary. */
struct queue_entry *target = afl->queue_buf[tid];
u32 use_extra = rand_below(afl, afl->extras_cnt);
u32 extra_len = afl->extras[use_extra].len;
u32 insert_at;
/* Make sure that the target has a reasonable length. */
if (extra_len > temp_len) { break; }
while (target && (target->len < 2 || target == afl->queue_cur))
target = target->next;
insert_at = rand_below(afl, temp_len - extra_len + 1);
memcpy(out_buf + insert_at, afl->extras[use_extra].data, extra_len);
if (!target) break;
}
/* Read the testcase into a new buffer. */
break;
fd = open(target->fname, O_RDONLY);
}
if (unlikely(fd < 0)) {
case 17: {
PFATAL("Unable to open '%s'", target->fname);
u32 use_extra, extra_len, insert_at = rand_below(afl, temp_len + 1);
u8 *ptr;
}
/* Insert an extra. Do the same dice-rolling stuff as for the
previous case. */
u32 new_len = target->len;
u8 *new_buf = ck_maybe_grow(BUF_PARAMS(in_scratch), new_len);
if (!afl->extras_cnt || (afl->a_extras_cnt && rand_below(afl, 2))) {
ck_read(fd, new_buf, new_len, target->fname);
use_extra = rand_below(afl, afl->a_extras_cnt);
extra_len = afl->a_extras[use_extra].len;
ptr = afl->a_extras[use_extra].data;
close(fd);
} else {
u8 overwrite = 0;
if (temp_len >= 2 && rand_below(afl, 2))
overwrite = 1;
else if (temp_len + HAVOC_BLK_XL >= MAX_FILE) {
use_extra = rand_below(afl, afl->extras_cnt);
extra_len = afl->extras[use_extra].len;
ptr = afl->extras[use_extra].data;
if (temp_len >= 2)
overwrite = 1;
else
break;
}
}
if (temp_len + extra_len >= MAX_FILE) { break; }
if (overwrite) {
out_buf = ck_maybe_grow(BUF_PARAMS(out), temp_len + extra_len);
u32 copy_from, copy_to, copy_len;
/* Tail */
memmove(out_buf + insert_at + extra_len, out_buf + insert_at,
temp_len - insert_at);
copy_len = choose_block_len(afl, new_len - 1);
if (copy_len > temp_len) copy_len = temp_len;
/* Inserted part */
memcpy(out_buf + insert_at, ptr, extra_len);
copy_from = rand_below(afl, new_len - copy_len + 1);
copy_to = rand_below(afl, temp_len - copy_len + 1);
temp_len += extra_len;
memmove(out_buf + copy_to, new_buf + copy_from, copy_len);
break;
} else {
}
u32 clone_from, clone_to, clone_len;
clone_len = choose_block_len(afl, new_len);
clone_from = rand_below(afl, new_len - clone_len + 1);
clone_to = rand_below(afl, temp_len);
u8 *temp_buf = ck_maybe_grow(BUF_PARAMS(out_scratch),
temp_len + clone_len);
/* Head */
memcpy(temp_buf, out_buf, clone_to);
/* Inserted part */
memcpy(temp_buf + clone_to, new_buf + clone_from, clone_len);
/* Tail */
memcpy(temp_buf + clone_to + clone_len, out_buf + clone_to,
temp_len - clone_to);
swap_bufs(BUF_PARAMS(out), BUF_PARAMS(out_scratch));
out_buf = temp_buf;
temp_len += clone_len;
}
break;
}
default:
// perform ascii mutations
if (text_mutation(afl, &out_buf, &temp_len) == 0)
goto retry_havoc;
} // end default: switch(r)
}
@ -4827,7 +5320,7 @@ u8 fuzz_one(afl_state_t *afl) {
return (key_val_lv_1 | key_val_lv_2);
#undef BUF_PARAMS
}
#undef BUF_PARAMS