diff options
Diffstat (limited to 'fuzz/atom/parser')
| -rw-r--r-- | fuzz/atom/parser/fuzz.rs | 100 | ||||
| -rw-r--r-- | fuzz/atom/parser/gencorpus.rs | 64 | ||||
| -rw-r--r-- | fuzz/atom/parser/meson.build | 6 |
3 files changed, 170 insertions, 0 deletions
diff --git a/fuzz/atom/parser/fuzz.rs b/fuzz/atom/parser/fuzz.rs new file mode 100644 index 0000000..610e08b --- /dev/null +++ b/fuzz/atom/parser/fuzz.rs @@ -0,0 +1,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; +} diff --git a/fuzz/atom/parser/gencorpus.rs b/fuzz/atom/parser/gencorpus.rs new file mode 100644 index 0000000..67ba1e3 --- /dev/null +++ b/fuzz/atom/parser/gencorpus.rs @@ -0,0 +1,64 @@ +use std::{ + env, + error::Error, + fs::{self, OpenOptions}, + io::Write, + path::PathBuf, +}; + +use gentoo_utils::{ + atom::Atom, + ebuild::{Depend, repo::Repo}, +}; + +fn main() -> Result<(), Box<dyn Error>> { + let corpus_dir = PathBuf::from( + env::args() + .nth(1) + .expect("expected corpus directory as first argument"), + ); + + fs::create_dir_all(&corpus_dir)?; + + let repo = Repo::new("/var/db/repos/gentoo"); + let mut atoms = Vec::new(); + + for category in repo.categories()? { + for ebuild in category?.ebuilds()? { + let depend = ebuild?.depend().to_vec(); + + for expr in depend { + walk_expr(&mut atoms, &expr); + } + } + } + + for (i, atom) in atoms.iter().enumerate() { + let path = corpus_dir.as_path().join(i.to_string()); + let mut file = OpenOptions::new() + .write(true) + .truncate(true) + .create(true) + .open(path)?; + + write!(file, "{atom}")?; + } + + Ok(()) +} + +fn walk_expr(atoms: &mut Vec<Atom>, depend: &Depend<Atom>) { + match depend { + Depend::Element(atom) => { + atoms.push(atom.clone()); + } + Depend::AllOf(exprs) + | Depend::OneOf(exprs) + | Depend::AnyOf(exprs) + | Depend::ConditionalGroup(_, exprs) => { + for expr in exprs { + walk_expr(atoms, expr); + } + } + } +} diff --git a/fuzz/atom/parser/meson.build b/fuzz/atom/parser/meson.build new file mode 100644 index 0000000..efc84c6 --- /dev/null +++ b/fuzz/atom/parser/meson.build @@ -0,0 +1,6 @@ +fuzzers += { + 'atom_parser': [ + meson.current_source_dir() / 'gencorpus.rs', + meson.current_source_dir() / 'fuzz.rs', + ], +} |
