From 2746c199cf58cb8e4e9837fee299de5453396087 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Fri, 10 Nov 2023 16:01:38 +0100 Subject: [PATCH] use a command result structure --- fuzz/fuzz_targets/fuzz_common.rs | 90 +++++++++++++++++++++++--------- fuzz/fuzz_targets/fuzz_expr.rs | 32 ++++++++---- fuzz/fuzz_targets/fuzz_test.rs | 30 +++++++---- 3 files changed, 107 insertions(+), 45 deletions(-) diff --git a/fuzz/fuzz_targets/fuzz_common.rs b/fuzz/fuzz_targets/fuzz_common.rs index 4d7968666..0fe2306e7 100644 --- a/fuzz/fuzz_targets/fuzz_common.rs +++ b/fuzz/fuzz_targets/fuzz_common.rs @@ -12,6 +12,19 @@ use std::process::Command; use std::sync::atomic::Ordering; use std::sync::{atomic::AtomicBool, Once}; +/// Represents the result of running a command, including its standard output, +/// standard error, and exit code. +pub struct CommandResult { + /// The standard output (stdout) of the command as a string. + pub stdout: String, + + /// The standard error (stderr) of the command as a string. + pub stderr: String, + + /// The exit code of the command. + pub exit_code: i32, +} + static CHECK_GNU: Once = Once::new(); static IS_GNU: AtomicBool = AtomicBool::new(false); @@ -34,7 +47,7 @@ pub fn is_gnu_cmd(cmd_path: &str) -> Result<(), std::io::Error> { } } -pub fn generate_and_run_uumain(args: &[OsString], uumain_function: F) -> (String, String, i32) +pub fn generate_and_run_uumain(args: &[OsString], uumain_function: F) -> CommandResult where F: FnOnce(std::vec::IntoIter) -> i32, { @@ -42,11 +55,11 @@ where let original_stdout_fd = unsafe { dup(STDOUT_FILENO) }; let original_stderr_fd = unsafe { dup(STDERR_FILENO) }; if original_stdout_fd == -1 || original_stderr_fd == -1 { - return ( - "Failed to duplicate STDOUT_FILENO or STDERR_FILENO".to_string(), - "".to_string(), - -1, - ); + return CommandResult { + stdout: "Failed to duplicate STDOUT_FILENO or STDERR_FILENO".to_string(), + stderr: "".to_string(), + exit_code: -1, + }; } println!("Running test {:?}", &args[0..]); let mut pipe_stdout_fds = [-1; 2]; @@ -56,7 +69,11 @@ where if unsafe { pipe(pipe_stdout_fds.as_mut_ptr()) } == -1 || unsafe { pipe(pipe_stderr_fds.as_mut_ptr()) } == -1 { - return ("Failed to create pipes".to_string(), "".to_string(), -1); + return CommandResult { + stdout: "Failed to create pipes".to_string(), + stderr: "".to_string(), + exit_code: -1, + }; } // Redirect stdout and stderr to their respective pipes @@ -69,11 +86,11 @@ where close(pipe_stderr_fds[0]); close(pipe_stderr_fds[1]); } - return ( - "Failed to redirect STDOUT_FILENO or STDERR_FILENO".to_string(), - "".to_string(), - -1, - ); + return CommandResult { + stdout: "Failed to redirect STDOUT_FILENO or STDERR_FILENO".to_string(), + stderr: "".to_string(), + exit_code: -1, + }; } let uumain_exit_status = uumain_function(args.to_owned().into_iter()); @@ -85,18 +102,19 @@ where if unsafe { dup2(original_stdout_fd, STDOUT_FILENO) } == -1 || unsafe { dup2(original_stderr_fd, STDERR_FILENO) } == -1 { - return ( - "Failed to restore the original STDOUT_FILENO or STDERR_FILENO".to_string(), - "".to_string(), - -1, - ); + return CommandResult { + stdout: "Failed to restore the original STDOUT_FILENO or STDERR_FILENO".to_string(), + stderr: "".to_string(), + exit_code: -1, + }; } unsafe { close(original_stdout_fd); close(original_stderr_fd); + + close(pipe_stdout_fds[1]); + close(pipe_stderr_fds[1]); } - unsafe { close(pipe_stdout_fds[1]) }; - unsafe { close(pipe_stderr_fds[1]) }; let captured_stdout = read_from_fd(pipe_stdout_fds[0]).trim().to_string(); let captured_stderr = read_from_fd(pipe_stderr_fds[0]).to_string(); @@ -107,7 +125,11 @@ where .trim() .to_string(); - (captured_stdout, captured_stderr, uumain_exit_status) + CommandResult { + stdout: captured_stdout, + stderr: captured_stderr, + exit_code: uumain_exit_status, + } } fn read_from_fd(fd: RawFd) -> String { @@ -141,13 +163,17 @@ pub fn run_gnu_cmd( cmd_path: &str, args: &[OsString], check_gnu: bool, -) -> Result<(String, String, i32), (String, String, i32)> { +) -> Result { if check_gnu { match is_gnu_cmd(cmd_path) { Ok(_) => {} // if the check passes, do nothing Err(e) => { // Convert the io::Error into the function's error type - return Err((String::new(), e.to_string(), -1)); + return Err(CommandResult { + stdout: String::new(), + stderr: e.to_string(), + exit_code: -1, + }); } } } @@ -159,7 +185,13 @@ pub fn run_gnu_cmd( let output = match command.output() { Ok(output) => output, - Err(e) => return Err((String::new(), e.to_string(), -1)), + Err(e) => { + return Err(CommandResult { + stdout: String::new(), + stderr: e.to_string(), + exit_code: -1, + }); + } }; let exit_code = output.status.code().unwrap_or(-1); @@ -174,9 +206,17 @@ pub fn run_gnu_cmd( .to_string(); if output.status.success() || !check_gnu { - Ok((stdout, stderr, exit_code)) + Ok(CommandResult { + stdout, + stderr, + exit_code, + }) } else { - Err((stdout, stderr, exit_code)) + Err(CommandResult { + stdout, + stderr, + exit_code, + }) } } diff --git a/fuzz/fuzz_targets/fuzz_expr.rs b/fuzz/fuzz_targets/fuzz_expr.rs index 4f0ad3c4a..90daa107f 100644 --- a/fuzz/fuzz_targets/fuzz_expr.rs +++ b/fuzz/fuzz_targets/fuzz_expr.rs @@ -13,8 +13,8 @@ use rand::Rng; use std::{env, ffi::OsString}; mod fuzz_common; +use crate::fuzz_common::CommandResult; use crate::fuzz_common::{compare_result, generate_and_run_uumain, run_gnu_cmd}; - static CMD_PATH: &str = "expr"; fn generate_random_string(max_length: usize) -> String { @@ -88,21 +88,31 @@ fuzz_target!(|_data: &[u8]| { // because uutils expr doesn't support localization yet // TODO remove once uutils expr supports localization env::set_var("LC_COLLATE", "C"); + let rust_result = generate_and_run_uumain(&args, uumain); - let (rust_stdout, rust_stderr, uumain_exit_code) = generate_and_run_uumain(&args, uumain); - - let (gnu_stdout, gnu_stderr, gnu_exit_code) = - run_gnu_cmd(CMD_PATH, &args[1..], false).unwrap_or_else(|e| e); + let gnu_result = match run_gnu_cmd(CMD_PATH, &args[1..], false) { + Ok(result) => result, + Err(error_result) => { + eprintln!("Failed to run GNU command:"); + eprintln!("Stderr: {}", error_result.stderr); + eprintln!("Exit Code: {}", error_result.exit_code); + CommandResult { + stdout: String::new(), + stderr: error_result.stderr, + exit_code: error_result.exit_code, + } + } + }; compare_result( "expr", &format!("{:?}", &args[1..]), - &rust_stdout, - &gnu_stdout, - &rust_stderr, - &gnu_stderr, - uumain_exit_code, - gnu_exit_code, + &rust_result.stdout, + &gnu_result.stdout, + &rust_result.stderr, + &gnu_result.stderr, + rust_result.exit_code, + gnu_result.exit_code, false, // Set to true if you want to fail on stderr diff ); }); diff --git a/fuzz/fuzz_targets/fuzz_test.rs b/fuzz/fuzz_targets/fuzz_test.rs index d11212666..0f2328c55 100644 --- a/fuzz/fuzz_targets/fuzz_test.rs +++ b/fuzz/fuzz_targets/fuzz_test.rs @@ -13,6 +13,7 @@ use rand::Rng; use std::ffi::OsString; mod fuzz_common; +use crate::fuzz_common::CommandResult; use crate::fuzz_common::{compare_result, generate_and_run_uumain, run_gnu_cmd}; #[derive(PartialEq, Debug, Clone)] @@ -204,20 +205,31 @@ fuzz_target!(|_data: &[u8]| { args.push(OsString::from(generate_test_arg())); } - let (rust_stdout, rust_stderr, uumain_exit_code) = generate_and_run_uumain(&args, uumain); + let rust_result = generate_and_run_uumain(&args, uumain); - let (gnu_stdout, gnu_stderr, gnu_exit_code) = - run_gnu_cmd(CMD_PATH, &args[1..], false).unwrap_or_else(|e| e); + let gnu_result = match run_gnu_cmd(CMD_PATH, &args[1..], false) { + Ok(result) => result, + Err(error_result) => { + eprintln!("Failed to run GNU command:"); + eprintln!("Stderr: {}", error_result.stderr); + eprintln!("Exit Code: {}", error_result.exit_code); + CommandResult { + stdout: String::new(), + stderr: error_result.stderr, + exit_code: error_result.exit_code, + } + } + }; compare_result( "test", &format!("{:?}", &args[1..]), - &rust_stdout, - &gnu_stdout, - &rust_stderr, - &gnu_stderr, - uumain_exit_code, - gnu_exit_code, + &rust_result.stdout, + &gnu_result.stdout, + &rust_result.stderr, + &gnu_result.stderr, + rust_result.exit_code, + gnu_result.exit_code, false, // Set to true if you want to fail on stderr diff ); });