diff --git a/.docker/Dockerfile b/.docker/Dockerfile index 5b057d8..89109cb 100644 --- a/.docker/Dockerfile +++ b/.docker/Dockerfile @@ -14,7 +14,6 @@ RUN emerge \ llvm-core/lld \ dev-vcs/git \ sys-process/parallel \ - dev-util/cbindgen \ net-libs/nodejs diff --git a/fuzz/atom/parser/fuzz.rs b/fuzz/atom/parser/fuzz.rs index 9af41f2..db41eff 100644 --- a/fuzz/atom/parser/fuzz.rs +++ b/fuzz/atom/parser/fuzz.rs @@ -1,100 +1,64 @@ +#![allow(clippy::missing_safety_doc)] + 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}, + io::{self, Write}, sync::{LazyLock, Mutex}, }; -struct PyProcess { - stdin: Mutex, - stdout: Mutex>, - buffer: Mutex, +#[derive(Debug)] +struct State { + input: io::Stdin, + output: io::Stdout, } -#[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 = 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()), - } + static PIPES: LazyLock> = LazyLock::new(|| { + Mutex::new(State { + input: io::stdin(), + output: io::stdout(), + }) }); let slice = unsafe { slice::from_raw_parts(input, len) }; + let str = str::from_utf8(slice).expect("expected ascii input"); - if slice.iter().any(|b| !b.is_ascii_graphic()) { + if str.chars().any(|c| !c.is_ascii_graphic()) { return -1; } - let str = match str::from_utf8(slice) { - Ok(str) => str, - Err(_) => return -1, + let mut state = PIPES.lock().unwrap(); + + writeln!(&mut state.output, "{str}").unwrap(); + + let mut buffer = String::new(); + + state.input.read_line(&mut buffer).unwrap(); + + let control = match buffer.as_str().trim() { + "0" => Ok(()), + "1" => Err(()), + other => panic!("unexpected input from pipes: {other}"), }; - let atom = str.trim(); + let gentoo_utils = Atom::parse(str); - 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"); + match (control, gentoo_utils) { + (Ok(_), Ok(_)) => { + eprintln!("agreement that {str} is valid"); } - (false, Err(_)) => { - eprintln!("agreement that {atom} is invalid"); + (Err(_), Err(_)) => { + eprintln!("agreement that {str} is invalid"); } - (true, Err(ParserFinishedError::Err(it) | ParserFinishedError::Unfinished(it))) => { - panic!("rejected valid atom: {atom}: {}", it.rest()); + (Ok(_), Err(rest)) => { + panic!("disagreement on {str}\ncontrol:Ok\ngentoo-utils:Err({rest})"); } - (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}") + (Err(_), Ok(_)) => { + panic!("disagreement on {str}\ncontrol:Err\ngentoo-utils:Ok") } } - return 0; + 0 } diff --git a/fuzz/atom/parser/meson.build b/fuzz/atom/parser/meson.build index 88452f5..24ee1b7 100644 --- a/fuzz/atom/parser/meson.build +++ b/fuzz/atom/parser/meson.build @@ -1,7 +1,30 @@ -fuzzers += { - 'atom_parser': { - 'gencorpus': meson.current_source_dir() / 'gencorpus.rs', - 'fuzzer': meson.current_source_dir() / 'fuzz.rs', - 'deps': [mon], - }, -} +gencorpus = executable( + 'gencorpus', + 'gencorpus.rs', + dependencies: [mon], + link_with: [gentoo_utils], +) + +corpus = custom_target( + 'corpus', + output: 'corpus', + command: [gencorpus, 'corpus'], +) + +fuzz_rs = static_library( + 'parser_fuzzer', + 'fuzz.rs', + rust_abi: 'c', + rust_args: [ + '-Cpasses=sancov-module', + '-Cllvm-args=-sanitizer-coverage-level=3', + '-Cllvm-args=-sanitizer-coverage-inline-8bit-counters', + ], + link_with: [gentoo_utils], +) + +fuzz = executable( + 'fuzzer', + link_args: ['-fsanitize=fuzzer'], + link_with: [fuzz_rs], +) diff --git a/fuzz/atom/vercmp/meson.build b/fuzz/atom/vercmp/meson.build index 5634061..4f451d9 100644 --- a/fuzz/atom/vercmp/meson.build +++ b/fuzz/atom/vercmp/meson.build @@ -1,7 +1,31 @@ -fuzzers += { - 'atom_vercmp': { - 'gencorpus': meson.current_source_dir() / 'gencorpus.rs', - 'fuzzer': meson.current_source_dir() / 'fuzz.rs', - 'deps': [mon], - }, -} +gencorpus = executable( + 'gencorpus', + 'gencorpus.rs', + dependencies: [mon], + link_with: [gentoo_utils], +) + +corpus = custom_target( + 'corpus', + output: 'corpus', + command: [gencorpus, 'corpus'], +) + +fuzz_rs = static_library( + 'vercmp_fuzzer', + 'fuzz.rs', + rust_abi: 'c', + rust_args: [ + '-Cpasses=sancov-module', + '-Cllvm-args=-sanitizer-coverage-level=3', + '-Cllvm-args=-sanitizer-coverage-inline-8bit-counters', + ], + dependencies: [mon], + link_with: [gentoo_utils], +) + +fuzz = executable( + 'fuzzer', + link_args: ['-fsanitize=fuzzer'], + link_with: [fuzz_rs], +) diff --git a/fuzz/meson.build b/fuzz/meson.build index eb485db..e64f43d 100644 --- a/fuzz/meson.build +++ b/fuzz/meson.build @@ -1,60 +1 @@ -cbindgen = find_program('cbindgen') - -fuzzers = {} - subdir('atom') - -foreach fuzzer, meta : fuzzers - gencorpus_rs = meta['gencorpus'] - fuzz_rs = meta['fuzzer'] - deps = meta['deps'] - - gencorpus = executable( - fuzzer + '_' + 'gencorpus', - gencorpus_rs, - dependencies: [mon], - link_with: [gentoo_utils], - ) - - corpus_directory = fuzzer + '_' + 'corpus' - - corpus = custom_target( - f'@fuzzer@_corpus', - output: f'@fuzzer@_corpus', - command: [gencorpus, corpus_directory], - ) - - fuzz_h = custom_target( - f'@fuzzer@_fuzz.h', - input: fuzz_rs, - output: f'@fuzzer@_fuzz.h', - command: [cbindgen, '@INPUT@', '-o', '@OUTPUT'], - ) - - fuzz_rs = static_library( - f'@fuzzer@.rs', - fuzz_rs, - rust_abi: 'c', - rust_args: [ - '-Cpasses=sancov-module', - '-Cllvm-args=-sanitizer-coverage-level=3', - '-Cllvm-args=-sanitizer-coverage-inline-8bit-counters', - ], - dependencies: deps, - link_with: [gentoo_utils], - ) - - fuzz = executable( - f'@fuzzer@_fuzzer', - link_args: ['-fsanitize=fuzzer'], - link_with: [fuzz_rs], - ) - - test( - fuzzer + '_' + 'fuzz', - fuzz, - args: [corpus_directory], - depends: [corpus], - timeout: 0, - ) -endforeach