/*
   american fuzzy lop++ - custom mutators related routines
   -------------------------------------------------------

   Originally written by Shengtuo Hu

   Now maintained by  Marc Heuse <mh@mh-sec.de>,
                        Heiko Eissfeldt <heiko.eissfeldt@hexco.de> and
                        Andrea Fioraldi <andreafioraldi@gmail.com>
                        Dominik Maier <mail@dmnk.co>

   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("Deinitializing 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;

  /* 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, afl->perm);

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

    if (afl->chown_needed) {

      if (fchown(fd, -1, afl->fsrv.gid) == -1) { PFATAL("fchown() failed"); }

    }

    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, true);

  }

  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);
  return fault;

}

