google / AFL

american fuzzy lop - a security-oriented fuzzer
https://lcamtuf.coredump.cx/afl/
Apache License 2.0
3.56k stars 625 forks source link

How does __afl_persistent_loop actually work? #134

Closed vivin closed 3 years ago

vivin commented 3 years ago

I see that the macro is defined in afl-clang-fast.c and I can see that it is basically an alias for __afl_persistent_loop in afl-llvm-rt.o.c. But I don't understand how the function would ever return 0:

int __afl_persistent_loop(unsigned int max_cnt) {

  static u8  first_pass = 1;
  static u32 cycle_cnt;

  if (first_pass) {

    /* Make sure that every iteration of __AFL_LOOP() starts with a clean slate.
       On subsequent calls, the parent will take care of that, but on the first
       iteration, it's our job to erase any trace of whatever happened
       before the loop. */

    if (is_persistent) {

      memset(__afl_area_ptr, 0, MAP_SIZE);
      __afl_area_ptr[0] = 1;
      __afl_prev_loc = 0;
    }

    cycle_cnt  = max_cnt;
    first_pass = 0;
    return 1;

  }

  if (is_persistent) {

    if (--cycle_cnt) {

      raise(SIGSTOP);

      __afl_area_ptr[0] = 1;
      __afl_prev_loc = 0;

      return 1;

    } else {

      /* When exiting __AFL_LOOP(), make sure that the subsequent code that
         follows the loop is not traced. We do that by pivoting back to the
         dummy output region. */

      __afl_area_ptr = __afl_area_initial;

    }

  }

  return 0;

}

I can see that in the second if it will stop the process if it is able to decrement the cycle count, and so I get that this is where it basically signals that an iteration has ended. What I don't understand is how it would ever get to this part, because first_pass is always set to 1 at the beginning of the function.

vivin commented 3 years ago

Well, I'm dumb 😆 I didn't notice that the variables are static and because I don't regularly write C, I forgot that they persist outside the lifetime of the function. The code makes sense now.