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
/// Wrapper around the afl-fuzz binary

use libc;
use std::env;
use std::ffi::CString;
use std::path::PathBuf;
use std::ptr;

extern "C" {
    // `main` function with afl-fuzz.c
    fn afl_fuzz_main(argc: libc::c_int,
                     argv: *const *const libc::c_char) -> libc::c_int;
}

fn call_afl_fuzz_main(args: Vec<CString>) -> Result<(), libc::c_int> {
    assert!(!args.is_empty());
    // `main` functions in C expect an array of pointers to the arguments
    let mut args_ptrs = args.iter()
                        .map(|arg| arg.as_ptr())
                        .collect::<Vec<_>>();
    let argc = args_ptrs.len() as libc::c_int;
    // argv is a null-terminated array
    // http://stackoverflow.com/a/11020198
    args_ptrs.push(ptr::null());
    let ret = unsafe {
        afl_fuzz_main(argc, args_ptrs.as_ptr())
    };

    match ret {
        0 => Ok(()),
        n => Err(n),
    }
}

// TODO: use builder pattern
pub struct AflFuzzConfig {
    pub in_dir: PathBuf,
    pub out_dir: PathBuf,
    pub target: PathBuf,
}

impl AflFuzzConfig {
    fn into_args(self) -> Vec<CString> {
        let mut args = vec![];

        // -i
        args.push("-i");
        args.push(self.in_dir.to_str().unwrap());

        // -o
        args.push("-o");
        args.push(self.out_dir.to_str().unwrap());

        // target
        args.push(self.target.to_str().unwrap());

        // Convert args from `str`s to `CString`s
        args.into_iter()
            .map(|s| CString::new(s).unwrap())
            .collect()
    }
}

pub fn afl_fuzz_env() -> Result<(), libc::c_int> {
    let args = env::args();

    // don't include anything before 'afl-fuzz' or 'cargo-afl-fuzz'
    let mut args = args.skip_while(|a| !a.ends_with("afl-fuzz"))
                       .map(|arg| CString::new(arg).unwrap())
                       .collect::<Vec<_>>();
    assert!(!args.is_empty(), "Error generating afl-fuzz arguments");

    args[0] = CString::new("afl-fuzz").unwrap();

    call_afl_fuzz_main(args)
}


pub fn afl_fuzz(config: AflFuzzConfig) -> Result<(), libc::c_int> {
    let mut args = vec![];

    // Fake the first argument
    let arg0 = CString::new("afl-fuzz").unwrap();
    args.push(arg0);

    args.extend(config.into_args());

    call_afl_fuzz_main(args)
}

/// FIXME: figure out a way to make it not run indefinitely
#[test]
fn testsomething() {
    let config = AflFuzzConfig {
        in_dir: ".".into(),
        out_dir: "testout".into(),
        target: "../target/debug/examples/hello".into(),
    };
    afl_fuzz(config);
}