summaryrefslogtreecommitdiff
path: root/fuzz/atom/parser/fuzz.rs
diff options
context:
space:
mode:
Diffstat (limited to 'fuzz/atom/parser/fuzz.rs')
-rw-r--r--fuzz/atom/parser/fuzz.rs100
1 files changed, 100 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;
+}