use core::slice; use gentoo_utils::{ Parseable, atom::{Atom, Version}, }; use mon::{Parser, ParserFinishedError, input::InputIter}; use std::{ cmp::Ordering, io::{BufRead, BufReader, Write}, process::{ChildStdin, ChildStdout, Command, Stdio}, sync::{LazyLock, Mutex}, }; struct PyProcess { stdin: Mutex, stdout: Mutex>, buffer: Mutex, } #[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("vercmp.py") .stdin(Stdio::piped()) .stdout(Stdio::piped()) .stderr(Stdio::inherit()) .spawn() .expect("failed to spawn vercmp.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 control = Version::parser() .parse_finished(InputIter::new("1.2.0a_alpha1_beta2-r1-8")) .unwrap(); 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 version_str = match str.split_ascii_whitespace().next() { Some(lhs) => lhs, None => return -1, }; let version = match Version::parser().parse_finished(InputIter::new(version_str)) { Ok(a) => a, Err(_) => return -1, }; let gentoo_utils = control.cmp(&version); let portage_result = portage_vercmp(&PY_PROCESS, &control, &version); match portage_result { Ok(portage) if portage == gentoo_utils => { eprintln!("agreement on {control} cmp {version} == {portage:?}"); } Ok(portage) => { panic!( "disagreement on {control} == {version}:\nportage:{portage:?} gentoo_utils:{gentoo_utils:?}" ) } Err(_) => { panic!("parsed invalid versions: {control} | {version}") } } return 0; } fn portage_vercmp(pyproc: &PyProcess, a: &Version, b: &Version) -> Result { let mut stdin = pyproc.stdin.lock().expect("failed to get stdin lock"); let mut stdout = pyproc.stdout.lock().expect("failed to get stdout lock"); let mut buffer = pyproc.buffer.lock().expect("failed to get buffer lock"); writeln!(&mut stdin, "{a} {b}").expect("failed to write line to python process"); stdin.flush().unwrap(); buffer.clear(); stdout .read_line(&mut buffer) .expect("failed to read line from python process"); match buffer.as_str().trim() { "0" => Ok(Ordering::Equal), "1" => Ok(Ordering::Greater), "-1" => Ok(Ordering::Less), "err" => Err(()), other => panic!("unexpected result from python: {other}"), } }