| /* |
| american fuzzy lop++ - custom mutators related routines |
| ------------------------------------------------------- |
| |
| Originally written by Shengtuo Hu |
| |
| Now maintained by Marc Heuse <[email protected]>, |
| Heiko Eißfeldt <[email protected]> and |
| Andrea Fioraldi <[email protected]> |
| Dominik Maier <[email protected]> |
| |
| Copyright 2016, 2017 Google Inc. All rights reserved. |
| Copyright 2019-2024 AFLplusplus Project. All rights reserved. |
| |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at: |
| |
| https://www.apache.org/licenses/LICENSE-2.0 |
| |
| This is the real deal: the program takes an instrumented binary and |
| attempts a variety of basic fuzzing tricks, paying close attention to |
| how they affect the execution path. |
| |
| */ |
| |
| #include "afl-fuzz.h" |
| |
| struct custom_mutator *load_custom_mutator(afl_state_t *, const char *); |
| #ifdef USE_PYTHON |
| struct custom_mutator *load_custom_mutator_py(afl_state_t *, char *); |
| #endif |
| |
| void run_afl_custom_queue_new_entry(afl_state_t *afl, struct queue_entry *q, |
| u8 *fname, u8 *mother_fname) { |
| |
| if (afl->custom_mutators_count) { |
| |
| u8 updated = 0; |
| |
| LIST_FOREACH(&afl->custom_mutator_list, struct custom_mutator, { |
| |
| if (el->afl_custom_queue_new_entry) { |
| |
| if (el->afl_custom_queue_new_entry(el->data, fname, mother_fname)) { |
| |
| updated = 1; |
| |
| } |
| |
| } |
| |
| }); |
| |
| if (updated) { |
| |
| struct stat st; |
| if (stat(fname, &st)) { PFATAL("File %s is gone!", fname); } |
| if (!st.st_size) { |
| |
| FATAL("File %s became empty in custom mutator!", fname); |
| |
| } |
| |
| q->len = st.st_size; |
| |
| } |
| |
| } |
| |
| } |
| |
| void setup_custom_mutators(afl_state_t *afl) { |
| |
| /* Try mutator library first */ |
| struct custom_mutator *mutator; |
| u8 *fn = afl->afl_env.afl_custom_mutator_library; |
| u32 prev_mutator_count = 0; |
| |
| if (fn) { |
| |
| if (afl->limit_time_sig && afl->limit_time_sig != -1) |
| FATAL( |
| "MOpt and custom mutator are mutually exclusive. We accept pull " |
| "requests that integrates MOpt with the optional mutators " |
| "(custom/redqueen/...)."); |
| |
| u8 *fn_token = (u8 *)strsep((char **)&fn, ";:,"); |
| |
| if (likely(!fn_token)) { |
| |
| mutator = load_custom_mutator(afl, fn); |
| list_append(&afl->custom_mutator_list, mutator); |
| afl->custom_mutators_count++; |
| |
| } else { |
| |
| while (fn_token) { |
| |
| if (*fn_token) { // strsep can be empty if ";;" |
| |
| if (afl->not_on_tty && afl->debug) |
| SAYF("[Custom] Processing: %s\n", fn_token); |
| prev_mutator_count = afl->custom_mutators_count; |
| mutator = load_custom_mutator(afl, fn_token); |
| list_append(&afl->custom_mutator_list, mutator); |
| afl->custom_mutators_count++; |
| if (prev_mutator_count > afl->custom_mutators_count) |
| FATAL("Maximum Custom Mutator count reached."); |
| fn_token = (u8 *)strsep((char **)&fn, ";:,"); |
| |
| } |
| |
| } |
| |
| } |
| |
| } |
| |
| /* Try Python module */ |
| #ifdef USE_PYTHON |
| u8 *module_name = afl->afl_env.afl_python_module; |
| |
| if (module_name) { |
| |
| if (afl->limit_time_sig) { |
| |
| FATAL( |
| "MOpt and Python mutator are mutually exclusive. We accept pull " |
| "requests that integrates MOpt with the optional mutators " |
| "(custom/redqueen/...)."); |
| |
| } |
| |
| struct custom_mutator *m = load_custom_mutator_py(afl, module_name); |
| afl->custom_mutators_count++; |
| list_append(&afl->custom_mutator_list, m); |
| |
| } |
| |
| #else |
| if (afl->afl_env.afl_python_module) { |
| |
| FATAL("Your AFL binary was built without Python support"); |
| |
| } |
| |
| #endif |
| |
| } |
| |
| void destroy_custom_mutators(afl_state_t *afl) { |
| |
| if (afl->custom_mutators_count) { |
| |
| LIST_FOREACH_CLEAR(&afl->custom_mutator_list, struct custom_mutator, { |
| |
| if (!el->data) { FATAL("Deintializing NULL mutator"); } |
| if (el->afl_custom_deinit) el->afl_custom_deinit(el->data); |
| if (el->dh) dlclose(el->dh); |
| |
| if (el->post_process_buf) { |
| |
| afl_free(el->post_process_buf); |
| el->post_process_buf = NULL; |
| |
| } |
| |
| ck_free(el); |
| |
| }); |
| |
| } |
| |
| } |
| |
| struct custom_mutator *load_custom_mutator(afl_state_t *afl, const char *fn) { |
| |
| void *dh; |
| struct custom_mutator *mutator = ck_alloc(sizeof(struct custom_mutator)); |
| |
| if (memchr(fn, '/', strlen(fn))) { |
| |
| mutator->name_short = strdup(strrchr(fn, '/') + 1); |
| |
| } else { |
| |
| mutator->name_short = strdup(fn); |
| |
| } |
| |
| if (strlen(mutator->name_short) > 22) { mutator->name_short[21] = 0; } |
| |
| mutator->name = fn; |
| ACTF("Loading custom mutator library from '%s'...", fn); |
| |
| dh = dlopen(fn, RTLD_NOW); |
| if (!dh) FATAL("%s", dlerror()); |
| mutator->dh = dh; |
| |
| /* Mutator */ |
| /* "afl_custom_init", optional for backward compatibility */ |
| mutator->afl_custom_init = dlsym(dh, "afl_custom_init"); |
| if (!mutator->afl_custom_init) { |
| |
| FATAL("Symbol 'afl_custom_init' not found."); |
| |
| } |
| |
| /* "afl_custom_fuzz" or "afl_custom_mutator", required */ |
| mutator->afl_custom_fuzz = dlsym(dh, "afl_custom_fuzz"); |
| if (!mutator->afl_custom_fuzz) { |
| |
| /* Try "afl_custom_mutator" for backward compatibility */ |
| WARNF("Symbol 'afl_custom_fuzz' not found. Try 'afl_custom_mutator'."); |
| |
| mutator->afl_custom_fuzz = dlsym(dh, "afl_custom_mutator"); |
| if (!mutator->afl_custom_fuzz) { |
| |
| WARNF("Symbol 'afl_custom_mutator' not found."); |
| |
| } else { |
| |
| OKF("Found 'afl_custom_mutator'."); |
| |
| } |
| |
| } else { |
| |
| OKF("Found 'afl_custom_mutator'."); |
| |
| } |
| |
| /* "afl_custom_introspection", optional */ |
| #ifdef INTROSPECTION |
| mutator->afl_custom_introspection = dlsym(dh, "afl_custom_introspection"); |
| if (!mutator->afl_custom_introspection) { |
| |
| ACTF("optional symbol 'afl_custom_introspection' not found."); |
| |
| } else { |
| |
| OKF("Found 'afl_custom_introspection'."); |
| |
| } |
| |
| #endif |
| |
| /* "afl_custom_fuzz_count", optional */ |
| mutator->afl_custom_fuzz_count = dlsym(dh, "afl_custom_fuzz_count"); |
| if (!mutator->afl_custom_fuzz_count) { |
| |
| ACTF("optional symbol 'afl_custom_fuzz_count' not found."); |
| |
| } else { |
| |
| OKF("Found 'afl_custom_fuzz_count'."); |
| |
| } |
| |
| /* "afl_custom_deinit", optional for backward compatibility */ |
| mutator->afl_custom_deinit = dlsym(dh, "afl_custom_deinit"); |
| if (!mutator->afl_custom_deinit) { |
| |
| FATAL("Symbol 'afl_custom_deinit' not found."); |
| |
| } |
| |
| /* "afl_custom_post_process", optional */ |
| mutator->afl_custom_post_process = dlsym(dh, "afl_custom_post_process"); |
| if (!mutator->afl_custom_post_process) { |
| |
| ACTF("optional symbol 'afl_custom_post_process' not found."); |
| |
| } else { |
| |
| OKF("Found 'afl_custom_post_process'."); |
| |
| } |
| |
| u8 notrim = 0; |
| /* "afl_custom_init_trim", optional */ |
| mutator->afl_custom_init_trim = dlsym(dh, "afl_custom_init_trim"); |
| if (!mutator->afl_custom_init_trim) { |
| |
| notrim = 1; |
| ACTF("optional symbol 'afl_custom_init_trim' not found."); |
| |
| } else { |
| |
| OKF("Found 'afl_custom_init_trim'."); |
| |
| } |
| |
| /* "afl_custom_trim", optional */ |
| mutator->afl_custom_trim = dlsym(dh, "afl_custom_trim"); |
| if (!mutator->afl_custom_trim) { |
| |
| notrim = 1; |
| ACTF("optional symbol 'afl_custom_trim' not found."); |
| |
| } else { |
| |
| OKF("Found 'afl_custom_trim'."); |
| |
| } |
| |
| /* "afl_custom_post_trim", optional */ |
| mutator->afl_custom_post_trim = dlsym(dh, "afl_custom_post_trim"); |
| if (!mutator->afl_custom_post_trim) { |
| |
| notrim = 1; |
| ACTF("optional symbol 'afl_custom_post_trim' not found."); |
| |
| } else { |
| |
| OKF("Found 'afl_custom_post_trim'."); |
| |
| } |
| |
| if (notrim) { |
| |
| if (mutator->afl_custom_init_trim || mutator->afl_custom_trim || |
| mutator->afl_custom_post_trim) { |
| |
| WARNF( |
| "Custom mutator does not implement all three trim APIs, standard " |
| "trimming will be used."); |
| |
| } |
| |
| mutator->afl_custom_init_trim = NULL; |
| mutator->afl_custom_trim = NULL; |
| mutator->afl_custom_post_trim = NULL; |
| |
| } |
| |
| /* "afl_custom_havoc_mutation", optional */ |
| mutator->afl_custom_havoc_mutation = dlsym(dh, "afl_custom_havoc_mutation"); |
| if (!mutator->afl_custom_havoc_mutation) { |
| |
| ACTF("optional symbol 'afl_custom_havoc_mutation' not found."); |
| |
| } else { |
| |
| OKF("Found 'afl_custom_havoc_mutation'."); |
| |
| } |
| |
| /* "afl_custom_havoc_mutation", optional */ |
| mutator->afl_custom_havoc_mutation_probability = |
| dlsym(dh, "afl_custom_havoc_mutation_probability"); |
| if (!mutator->afl_custom_havoc_mutation_probability) { |
| |
| ACTF("optional symbol 'afl_custom_havoc_mutation_probability' not found."); |
| |
| } else { |
| |
| OKF("Found 'afl_custom_havoc_mutation_probability'."); |
| |
| } |
| |
| /* "afl_custom_queue_get", optional */ |
| mutator->afl_custom_queue_get = dlsym(dh, "afl_custom_queue_get"); |
| if (!mutator->afl_custom_queue_get) { |
| |
| ACTF("optional symbol 'afl_custom_queue_get' not found."); |
| |
| } else { |
| |
| OKF("Found 'afl_custom_queue_get'."); |
| |
| } |
| |
| /* "afl_custom_splice_optout", optional, never called */ |
| mutator->afl_custom_splice_optout = dlsym(dh, "afl_custom_splice_optout"); |
| if (!mutator->afl_custom_splice_optout) { |
| |
| ACTF("optional symbol 'afl_custom_splice_optout' not found."); |
| |
| } else { |
| |
| OKF("Found 'afl_custom_splice_optout'."); |
| afl->custom_splice_optout = 1; |
| |
| } |
| |
| /* "afl_custom_fuzz_send", optional */ |
| mutator->afl_custom_fuzz_send = dlsym(dh, "afl_custom_fuzz_send"); |
| if (!mutator->afl_custom_fuzz_send) { |
| |
| ACTF("optional symbol 'afl_custom_fuzz_send' not found."); |
| |
| } else { |
| |
| OKF("Found 'afl_custom_fuzz_send'."); |
| |
| } |
| |
| /* "afl_custom_post_run", optional */ |
| mutator->afl_custom_post_run = dlsym(dh, "afl_custom_post_run"); |
| if (!mutator->afl_custom_post_run) { |
| |
| ACTF("optional symbol 'afl_custom_post_run' not found."); |
| |
| } else { |
| |
| OKF("Found 'afl_custom_post_run'."); |
| |
| } |
| |
| /* "afl_custom_queue_new_entry", optional */ |
| mutator->afl_custom_queue_new_entry = dlsym(dh, "afl_custom_queue_new_entry"); |
| if (!mutator->afl_custom_queue_new_entry) { |
| |
| ACTF("optional symbol 'afl_custom_queue_new_entry' not found"); |
| |
| } else { |
| |
| OKF("Found 'afl_custom_queue_new_entry'."); |
| |
| } |
| |
| /* "afl_custom_describe", optional */ |
| mutator->afl_custom_describe = dlsym(dh, "afl_custom_describe"); |
| if (!mutator->afl_custom_describe) { |
| |
| ACTF("optional symbol 'afl_custom_describe' not found."); |
| |
| } else { |
| |
| OKF("Found 'afl_custom_describe'."); |
| |
| } |
| |
| OKF("Custom mutator '%s' installed successfully.", fn); |
| |
| /* Initialize the custom mutator */ |
| if (mutator->afl_custom_init) { |
| |
| mutator->data = mutator->afl_custom_init(afl, rand_below(afl, 0xFFFFFFFF)); |
| |
| } |
| |
| mutator->stacked_custom = (mutator && mutator->afl_custom_havoc_mutation); |
| mutator->stacked_custom_prob = |
| 6; // like one of the default mutations in havoc |
| |
| return mutator; |
| |
| } |
| |
| u8 trim_case_custom(afl_state_t *afl, struct queue_entry *q, u8 *in_buf, |
| struct custom_mutator *mutator) { |
| |
| u8 fault = 0; |
| u32 trim_exec = 0; |
| u32 orig_len = q->len; |
| u32 out_len = 0; |
| u8 *out_buf = NULL; |
| |
| u8 val_buf[STRINGIFY_VAL_SIZE_MAX]; |
| |
| afl->stage_name = afl->stage_name_buf; |
| afl->bytes_trim_in += q->len; |
| |
| /* Initialize trimming in the custom mutator */ |
| afl->stage_cur = 0; |
| s32 retval = mutator->afl_custom_init_trim(mutator->data, in_buf, q->len); |
| if (unlikely(retval) < 0) { |
| |
| FATAL("custom_init_trim error ret: %d", retval); |
| |
| } else { |
| |
| afl->stage_max = retval; |
| |
| } |
| |
| if (afl->not_on_tty && afl->debug) { |
| |
| SAYF("[Custom Trimming] START: Max %u iterations, %u bytes", afl->stage_max, |
| q->len); |
| |
| } |
| |
| while (afl->stage_cur < afl->stage_max) { |
| |
| u8 *retbuf = NULL; |
| |
| sprintf(afl->stage_name_buf, "ptrim %s", |
| u_stringify_int(val_buf, trim_exec)); |
| |
| u64 cksum; |
| |
| size_t retlen = mutator->afl_custom_trim(mutator->data, &retbuf); |
| |
| if (unlikely(!retbuf)) { |
| |
| FATAL("custom_trim failed (ret %zu)", retlen); |
| |
| } else if (unlikely(retlen > orig_len)) { |
| |
| /* Do not exit the fuzzer, even if the trimmed data returned by the custom |
| mutator is larger than the original data. For some use cases, like the |
| grammar mutator, the definition of "size" may have different meanings. |
| For example, the trimming function in a grammar mutator aims at |
| reducing the objects in a grammar structure, but does not guarantee to |
| generate a smaller binary buffer. |
| |
| Thus, we allow the custom mutator to generate the trimmed data that is |
| larger than the original data. */ |
| |
| if (afl->not_on_tty && afl->debug) { |
| |
| WARNF( |
| "Trimmed data returned by custom mutator is larger than original " |
| "data"); |
| |
| } |
| |
| } else if (unlikely(retlen == 0)) { |
| |
| /* Do not run the empty test case on the target. To keep the custom |
| trimming function running, we simply treat the empty test case as an |
| unsuccessful trimming and skip it, instead of aborting the trimming. */ |
| |
| ++afl->trim_execs; |
| |
| } |
| |
| if (likely(retlen)) { |
| |
| retlen = write_to_testcase(afl, (void **)&retbuf, retlen, 0); |
| |
| if (unlikely(!retlen)) { |
| |
| ++afl->trim_execs; |
| |
| } else { |
| |
| fault = fuzz_run_target(afl, &afl->fsrv, afl->fsrv.exec_tmout); |
| ++afl->trim_execs; |
| |
| if (afl->stop_soon || fault == FSRV_RUN_ERROR) { goto abort_trimming; } |
| |
| classify_counts(&afl->fsrv); |
| cksum = hash64(afl->fsrv.trace_bits, afl->fsrv.map_size, HASH_CONST); |
| |
| } |
| |
| } |
| |
| if (likely(retlen && cksum == q->exec_cksum)) { |
| |
| /* Let's save a clean trace, which will be needed by |
| update_bitmap_score once we're done with the trimming stuff. |
| Use out_buf NULL check to make this only happen once per trim. */ |
| |
| if (!out_buf) { |
| |
| memcpy(afl->clean_trace_custom, afl->fsrv.trace_bits, |
| afl->fsrv.map_size); |
| |
| } |
| |
| if (afl_realloc((void **)&out_buf, retlen) == NULL) { |
| |
| FATAL("can not allocate memory for trim"); |
| |
| } |
| |
| out_len = retlen; |
| // TODO are we sure that retbuf fits into out_buf if retbuf can actually |
| // increase in size? |
| memcpy(out_buf, retbuf, retlen); |
| |
| /* Tell the custom mutator that the trimming was successful */ |
| afl->stage_cur = mutator->afl_custom_post_trim(mutator->data, 1); |
| |
| if (afl->not_on_tty && afl->debug) { |
| |
| SAYF("[Custom Trimming] SUCCESS: %u/%u iterations (now at %u bytes)", |
| afl->stage_cur, afl->stage_max, out_len); |
| |
| } |
| |
| } else { |
| |
| /* Tell the custom mutator that the trimming was unsuccessful */ |
| s32 retval2 = mutator->afl_custom_post_trim(mutator->data, 0); |
| if (unlikely(retval2 < 0)) { |
| |
| FATAL("Error ret in custom_post_trim: %d", retval2); |
| |
| } else { |
| |
| afl->stage_cur = retval2; |
| |
| } |
| |
| if (afl->not_on_tty && afl->debug) { |
| |
| SAYF("[Custom Trimming] FAILURE: %u/%u iterations", afl->stage_cur, |
| afl->stage_max); |
| |
| } |
| |
| } |
| |
| /* Since this can be slow, update the screen every now and then. */ |
| |
| if (!(trim_exec++ % afl->stats_update_freq)) { show_stats(afl); } |
| |
| } |
| |
| /* If we have made changes, we also need to update the on-disk |
| version of the test case. */ |
| |
| if (out_buf) { |
| |
| s32 fd; |
| |
| unlink(q->fname); /* ignore errors */ |
| |
| fd = open(q->fname, O_WRONLY | O_CREAT | O_EXCL, DEFAULT_PERMISSION); |
| |
| if (fd < 0) { PFATAL("Unable to create '%s'", q->fname); } |
| |
| ck_write(fd, out_buf, out_len, q->fname); |
| close(fd); |
| |
| /* Update the queue's knowledge of length as soon as we write the file. |
| We do this here so that exit/error cases that *don't* update the file |
| also don't update q->len. */ |
| q->len = out_len; |
| |
| memcpy(afl->fsrv.trace_bits, afl->clean_trace_custom, afl->fsrv.map_size); |
| update_bitmap_score(afl, q); |
| |
| } |
| |
| if (afl->not_on_tty && afl->debug) { |
| |
| SAYF("[Custom Trimming] DONE: %u bytes -> %u bytes", orig_len, q->len); |
| |
| } |
| |
| abort_trimming: |
| |
| if (out_buf) afl_free(out_buf); |
| afl->bytes_trim_out += q->len; |
| return fault; |
| |
| } |
| |