Skip to content

Commit 104e707

Browse files
committed
fuzzing: provide a better error management
1 parent 8ab20c4 commit 104e707

File tree

3 files changed

+92
-91
lines changed

3 files changed

+92
-91
lines changed

fuzz/fuzz_targets/fuzz_common.rs

Lines changed: 59 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,6 @@ pub fn generate_and_run_uumain<F>(args: &[OsString], uumain_function: F) -> (Str
3838
where
3939
F: FnOnce(std::vec::IntoIter<OsString>) -> i32,
4040
{
41-
let uumain_exit_status;
42-
4341
// Duplicate the stdout and stderr file descriptors
4442
let original_stdout_fd = unsafe { dup(STDOUT_FILENO) };
4543
let original_stderr_fd = unsafe { dup(STDERR_FILENO) };
@@ -78,7 +76,8 @@ where
7876
);
7977
}
8078

81-
uumain_exit_status = uumain_function(args.to_owned().into_iter());
79+
let uumain_exit_status = uumain_function(args.to_owned().into_iter());
80+
8281
io::stdout().flush().unwrap();
8382
io::stderr().flush().unwrap();
8483

@@ -102,8 +101,8 @@ where
102101
let captured_stdout = read_from_fd(pipe_stdout_fds[0]).trim().to_string();
103102
let captured_stderr = read_from_fd(pipe_stderr_fds[0]).to_string();
104103
let captured_stderr = captured_stderr
105-
.splitn(2, ':')
106-
.nth(1)
104+
.split_once(':')
105+
.map(|x| x.1)
107106
.unwrap_or("")
108107
.trim()
109108
.to_string();
@@ -168,8 +167,8 @@ pub fn run_gnu_cmd(
168167
let stdout = String::from_utf8_lossy(&output.stdout).to_string();
169168
let stderr = String::from_utf8_lossy(&output.stderr).to_string();
170169
let stderr = stderr
171-
.splitn(2, ':')
172-
.nth(1)
170+
.split_once(':')
171+
.map(|x| x.1)
173172
.unwrap_or("")
174173
.trim()
175174
.to_string();
@@ -180,3 +179,56 @@ pub fn run_gnu_cmd(
180179
Err((stdout, stderr, exit_code))
181180
}
182181
}
182+
183+
pub fn compare_result(
184+
test_type: &str,
185+
input: &str,
186+
rust_stdout: &str,
187+
gnu_stdout: &str,
188+
rust_stderr: &str,
189+
gnu_stderr: &str,
190+
rust_exit_code: i32,
191+
gnu_exit_code: i32,
192+
fail_on_stderr_diff: bool,
193+
) {
194+
println!("Test Type: {}", test_type);
195+
println!("Input: {}", input);
196+
197+
let mut discrepancies = Vec::new();
198+
let mut should_panic = false;
199+
200+
if rust_stdout.trim() != gnu_stdout.trim() {
201+
discrepancies.push("stdout differs");
202+
println!("Rust stdout: {}", rust_stdout);
203+
println!("GNU stdout: {}", gnu_stdout);
204+
should_panic = true;
205+
}
206+
if rust_stderr.trim() != gnu_stderr.trim() {
207+
discrepancies.push("stderr differs");
208+
println!("Rust stderr: {}", rust_stderr);
209+
println!("GNU stderr: {}", gnu_stderr);
210+
if fail_on_stderr_diff {
211+
should_panic = true;
212+
}
213+
}
214+
if rust_exit_code != gnu_exit_code {
215+
discrepancies.push("exit code differs");
216+
println!("Rust exit code: {}", rust_exit_code);
217+
println!("GNU exit code: {}", gnu_exit_code);
218+
should_panic = true;
219+
}
220+
221+
if discrepancies.is_empty() {
222+
println!("All outputs and exit codes matched.");
223+
} else {
224+
println!("Discrepancy detected: {}", discrepancies.join(", "));
225+
if should_panic {
226+
panic!("Test failed for {}: {}", test_type, input);
227+
} else {
228+
println!(
229+
"Test completed with discrepancies for {}: {}",
230+
test_type, input
231+
);
232+
}
233+
}
234+
}

fuzz/fuzz_targets/fuzz_expr.rs

Lines changed: 16 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use rand::Rng;
1313
use std::{env, ffi::OsString};
1414

1515
mod fuzz_common;
16-
use crate::fuzz_common::{generate_and_run_uumain, run_gnu_cmd};
16+
use crate::fuzz_common::{compare_result, generate_and_run_uumain, run_gnu_cmd};
1717

1818
static CMD_PATH: &str = "expr";
1919

@@ -84,52 +84,25 @@ fuzz_target!(|_data: &[u8]| {
8484
let mut args = vec![OsString::from("expr")];
8585
args.extend(expr.split_whitespace().map(OsString::from));
8686

87-
let (rust_stdout, rust_stderr, uumain_exit_code) = generate_and_run_uumain(&args, uumain);
88-
8987
// Use C locale to avoid false positives, like in https://github.com/uutils/coreutils/issues/5378,
9088
// because uutils expr doesn't support localization yet
9189
// TODO remove once uutils expr supports localization
9290
env::set_var("LC_COLLATE", "C");
9391

94-
// Run GNU expr with the provided arguments and compare the output
95-
match run_gnu_cmd(CMD_PATH, &args[1..], true) {
96-
Ok((gnu_stdout, gnu_stderr, gnu_exit_code)) => {
97-
let gnu_stdout = gnu_stdout.trim().to_owned();
98-
if uumain_exit_code != gnu_exit_code {
99-
println!("Expression: {}", expr);
100-
101-
println!("GNU stderr: {}", gnu_stderr);
102-
println!("Rust stderr: {}", rust_stderr);
103-
104-
println!("Rust code: {}", uumain_exit_code);
105-
println!("GNU code: {}", gnu_exit_code);
106-
panic!("Different error codes");
107-
}
108-
if rust_stdout == gnu_stdout {
109-
println!(
110-
"Outputs matched for expression: {} => Result: {}",
111-
expr, rust_stdout
112-
);
113-
} else {
114-
println!("Expression: {}", expr);
115-
println!("Rust output: {}", rust_stdout);
116-
println!("GNU output: {}", gnu_stdout);
117-
panic!("Different output between Rust & GNU");
118-
}
119-
}
92+
let (rust_stdout, rust_stderr, uumain_exit_code) = generate_and_run_uumain(&args, uumain);
12093

121-
Err((_gnu_stdout, gnu_stderr, _gnu_exit_code)) => {
122-
if rust_stderr == gnu_stderr {
123-
println!(
124-
"GNU execution failed for input: {} stderr: {}",
125-
expr, rust_stderr
126-
);
127-
} else {
128-
println!("Input: {}", expr);
129-
println!("Rust stderr: {}", rust_stderr);
130-
println!("GNU stderr: {}", gnu_stderr);
131-
panic!("Different stderr between Rust & GNU");
132-
}
133-
}
134-
}
94+
let (gnu_stdout, gnu_stderr, gnu_exit_code) =
95+
run_gnu_cmd(CMD_PATH, &args[1..], false).unwrap_or_else(|e| e);
96+
97+
compare_result(
98+
"expr",
99+
&format!("{:?}", &args[1..]),
100+
&rust_stdout,
101+
&gnu_stdout,
102+
&rust_stderr,
103+
&gnu_stderr,
104+
uumain_exit_code,
105+
gnu_exit_code,
106+
false, // Set to true if you want to fail on stderr diff
107+
);
135108
});

fuzz/fuzz_targets/fuzz_test.rs

Lines changed: 17 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use rand::Rng;
1313
use std::ffi::OsString;
1414

1515
mod fuzz_common;
16-
use crate::fuzz_common::{generate_and_run_uumain, run_gnu_cmd};
16+
use crate::fuzz_common::{compare_result, generate_and_run_uumain, run_gnu_cmd};
1717

1818
#[derive(PartialEq, Debug, Clone)]
1919
enum ArgType {
@@ -204,44 +204,20 @@ fuzz_target!(|_data: &[u8]| {
204204
args.push(OsString::from(generate_test_arg()));
205205
}
206206

207-
let (rust_stdout, rust_stderr, uumain_exit_status) = generate_and_run_uumain(&args, uumain);
208-
209-
// Run GNU test with the provided arguments and compare the output
210-
match run_gnu_cmd(CMD_PATH, &args[1..], false) {
211-
Ok((gnu_stdout, gnu_stderr, gnu_exit_status)) => {
212-
let gnu_stdout = gnu_stdout.trim().to_owned();
213-
println!("gnu_exit_status {}", gnu_exit_status);
214-
println!("uumain_exit_status {}", uumain_exit_status);
215-
if rust_stdout != gnu_stdout || uumain_exit_status != gnu_exit_status {
216-
println!("Discrepancy detected!");
217-
println!("Test: {:?}", &args[1..]);
218-
println!("Rust output: {}", rust_stdout);
219-
println!("GNU output: {}", gnu_stdout);
220-
221-
println!("Rust stderr: {}", rust_stderr);
222-
println!("GNU stderr: {}", gnu_stderr);
223-
println!("My exit status: {}", uumain_exit_status);
224-
println!("GNU exit status: {}", gnu_exit_status);
225-
panic!();
226-
} else {
227-
println!(
228-
"Outputs and exit statuses matched for expression {:?}",
229-
&args[1..]
230-
);
231-
}
232-
}
233-
Err((_gnu_stdout, gnu_stderr, _gnu_exit_code)) => {
234-
if rust_stderr == gnu_stderr {
235-
println!(
236-
"GNU execution failed for input: {:?} stderr: {}",
237-
args, rust_stderr
238-
);
239-
} else {
240-
println!("Input: {:?}", args);
241-
println!("Rust stderr: {}", rust_stderr);
242-
println!("GNU stderr: {}", gnu_stderr);
243-
panic!("Different stderr between Rust & GNU");
244-
}
245-
}
246-
}
207+
let (rust_stdout, rust_stderr, uumain_exit_code) = generate_and_run_uumain(&args, uumain);
208+
209+
let (gnu_stdout, gnu_stderr, gnu_exit_code) =
210+
run_gnu_cmd(CMD_PATH, &args[1..], false).unwrap_or_else(|e| e);
211+
212+
compare_result(
213+
"test",
214+
&format!("{:?}", &args[1..]),
215+
&rust_stdout,
216+
&gnu_stdout,
217+
&rust_stderr,
218+
&gnu_stderr,
219+
uumain_exit_code,
220+
gnu_exit_code,
221+
false, // Set to true if you want to fail on stderr diff
222+
);
247223
});

0 commit comments

Comments
 (0)