summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Turner <jturner.usa@gmail.com>2025-11-16 20:17:35 +0000
committerJohn Turner <jturner.usa@gmail.com>2025-11-16 20:17:35 +0000
commit7c404ca8c5d0037a17797ace2961d5424cf3a4a5 (patch)
tree7fbeee2ee1d507342ffbbf6d3c2ef76bf359bfde
parentfdf1f09d6f43e7aa80a43fa117c26d535106f936 (diff)
downloadgentoo-utils-7c404ca8c5d0037a17797ace2961d5424cf3a4a5.tar.gz
add fuzzer
-rw-r--r--meson.build6
-rw-r--r--meson.options1
-rwxr-xr-xscripts/atom.py13
-rw-r--r--tests/fuzz.rs50
-rw-r--r--tests/meson.build27
5 files changed, 95 insertions, 2 deletions
diff --git a/meson.build b/meson.build
index 3bf81f0..ac6d6af 100644
--- a/meson.build
+++ b/meson.build
@@ -1,6 +1,6 @@
project(
'gentoo-utils',
- 'rust',
+ ['rust', 'cpp'],
meson_version: '>=1.9.1',
default_options: {'rust_std': '2024', 'rust_nightly': 'enabled'},
)
@@ -16,5 +16,7 @@ gentoo_utils = static_library(
'gentoo_utils',
'src/lib.rs',
dependencies: [mon, get, itertools],
- link_with: [thiserror]
+ link_with: [thiserror],
)
+
+subdir('tests')
diff --git a/meson.options b/meson.options
new file mode 100644
index 0000000..4d0ae09
--- /dev/null
+++ b/meson.options
@@ -0,0 +1 @@
+option('fuzz', type: 'feature', value: 'disabled')
diff --git a/scripts/atom.py b/scripts/atom.py
new file mode 100755
index 0000000..a1a59bd
--- /dev/null
+++ b/scripts/atom.py
@@ -0,0 +1,13 @@
+#!/usr/bin/env python3
+
+import sys
+import portage.dep as dep
+
+input = sys.stdin.read().strip()
+
+try:
+ dep.Atom(input)
+except Exception as e:
+ sys.exit(1)
+
+sys.exit(0)
diff --git a/tests/fuzz.rs b/tests/fuzz.rs
new file mode 100644
index 0000000..69804e8
--- /dev/null
+++ b/tests/fuzz.rs
@@ -0,0 +1,50 @@
+use core::slice;
+use gentoo_utils::{Parseable, atom::Atom};
+use mon::{Parser, ParserFinishedError, input::InputIter};
+use std::{
+ io::Write,
+ process::{Command, Stdio},
+};
+
+#[allow(clippy::missing_safety_doc, clippy::needless_return)]
+#[unsafe(no_mangle)]
+pub unsafe extern "C" fn LLVMFuzzerTestOneInput(input: *const u8, len: usize) -> i32 {
+ let slice = unsafe { slice::from_raw_parts(input, len) };
+
+ let atom = match str::from_utf8(slice) {
+ Ok(str) => str.trim(),
+ _ => return -1,
+ };
+
+ let mut proc = Command::new("atom.py")
+ .stdin(Stdio::piped())
+ .spawn()
+ .unwrap();
+
+ proc.stdin
+ .as_mut()
+ .unwrap()
+ .write_all(atom.as_bytes())
+ .unwrap();
+
+ let status = proc.wait().unwrap();
+
+ let result = Atom::parser().check_finished(InputIter::new(atom));
+
+ match (status.success(), result) {
+ (true, Ok(_)) => {
+ eprintln!("agreement that {atom} is valid");
+ return 0;
+ }
+ (true, Err(ParserFinishedError::Err(it) | ParserFinishedError::Unfinished(it))) => {
+ panic!("gentoo-utils rejected valid atom: {atom}: {}", it.rest());
+ }
+ (false, Err(_)) => {
+ eprintln!("agreement that {atom} is invalid");
+ return -1;
+ }
+ (false, Ok(_)) => {
+ panic!("gentoo-utils accepted invalid atom: {atom}");
+ }
+ }
+}
diff --git a/tests/meson.build b/tests/meson.build
new file mode 100644
index 0000000..c4b5e8d
--- /dev/null
+++ b/tests/meson.build
@@ -0,0 +1,27 @@
+cbindgen = find_program('cbindgen')
+
+fuzz_h = custom_target(
+ 'fuzz_h',
+ input: 'fuzz.rs',
+ output: 'fuzz.h',
+ command: [cbindgen, '@INPUT@', '-o', '@OUTPUT'],
+)
+
+fuzz_rs = static_library(
+ 'fuzz_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: [mon],
+ link_with: [gentoo_utils],
+)
+
+fuzz_cpp = executable(
+ 'fuzz',
+ link_args: ['-fsanitize=fuzzer'],
+ link_with: [fuzz_rs],
+)