summaryrefslogtreecommitdiff
path: root/fuzz/fuzz.rs
blob: 610e08b187d765ca54659dcceef86ff8388be32c (plain)
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
use core::slice;
use gentoo_utils::{Parseable, atom::Atom};
use mon::{Parser, ParserFinishedError, input::InputIter};
use std::{
    io::{BufRead, BufReader, Write},
    process::{ChildStdin, ChildStdout, Command, Stdio},
    sync::{LazyLock, Mutex},
};

struct PyProcess {
    stdin: Mutex<ChildStdin>,
    stdout: Mutex<BufReader<ChildStdout>>,
    buffer: Mutex<String>,
}

#[allow(clippy::missing_safety_doc, clippy::needless_return)]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn LLVMFuzzerTestOneInput(input: *const u8, len: usize) -> i32 {
    static PY_PROCESS: LazyLock<PyProcess> = LazyLock::new(|| {
        #[allow(clippy::zombie_processes)]
        let mut proc = Command::new("atom.py")
            .stdin(Stdio::piped())
            .stdout(Stdio::piped())
            .stderr(Stdio::inherit())
            .spawn()
            .expect("failed to spawn atom.py");

        let stdin = Mutex::new(proc.stdin.take().unwrap());
        let stdout = Mutex::new(BufReader::new(proc.stdout.take().unwrap()));

        PyProcess {
            stdin,
            stdout,
            buffer: Mutex::new(String::new()),
        }
    });

    let slice = unsafe { slice::from_raw_parts(input, len) };

    if slice.iter().any(|b| !b.is_ascii_graphic()) {
        return -1;
    }

    let str = match str::from_utf8(slice) {
        Ok(str) => str,
        Err(_) => return -1,
    };

    let atom = str.trim();

    let mut stdin = PY_PROCESS.stdin.lock().expect("failed to get stdin lock");

    writeln!(&mut stdin, "{atom}").expect("failed to write to python stdin");

    let mut stdout = PY_PROCESS.stdout.lock().expect("failed to get stdout lock");

    let mut buffer = PY_PROCESS.buffer.lock().expect("failed to get buffer lock");

    buffer.clear();

    stdout
        .read_line(&mut buffer)
        .expect("failed to readline from python");

    let portage_result = match buffer.as_str().trim() {
        "0" => true,
        "1" => false,
        result => panic!("got unexpected result from python: {result}"),
    };

    let gentoo_utils_result = Atom::parser().parse_finished(InputIter::new(atom));

    match (portage_result, gentoo_utils_result) {
        (true, Ok(_)) => {
            eprintln!("agreement that {atom} is valid");
        }
        (false, Err(_)) => {
            eprintln!("agreement that {atom} is invalid");
        }
        (true, Err(_)) => {
            panic!("rejected valid atom: {atom}");
        }
        (false, Ok(atom))
            if atom.usedeps().iter().any(|usedep| {
                atom.usedeps()
                    .iter()
                    .filter(|u| usedep.flag() == u.flag())
                    .count()
                    > 1
            }) =>
        {
            eprintln!("disagreement due to duplicates in usedeps");
        }
        (false, Ok(_)) => {
            panic!("accpeted invalid atom: {atom}")
        }
    }

    return 0;
}