From f0f64bd3b728c905944aaf3534f618d85ded30c6 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Thu, 28 Sep 2023 08:42:30 +0200 Subject: [PATCH 1/2] fuzz: move the common duplicated code into a function --- fuzz/fuzz_targets/fuzz_common.rs | 47 ++++++++++++++++++++++++ fuzz/fuzz_targets/fuzz_expr.rs | 49 ++----------------------- fuzz/fuzz_targets/fuzz_test.rs | 61 ++++++-------------------------- 3 files changed, 59 insertions(+), 98 deletions(-) diff --git a/fuzz/fuzz_targets/fuzz_common.rs b/fuzz/fuzz_targets/fuzz_common.rs index fb1f498e9..a33d603f1 100644 --- a/fuzz/fuzz_targets/fuzz_common.rs +++ b/fuzz/fuzz_targets/fuzz_common.rs @@ -3,6 +3,8 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. +use libc::{dup, dup2, STDOUT_FILENO}; +use std::ffi::OsString; use std::process::Command; use std::sync::atomic::Ordering; use std::sync::{atomic::AtomicBool, Once}; @@ -28,3 +30,48 @@ pub fn is_gnu_cmd(cmd_path: &str) -> Result<(), std::io::Error> { panic!("Not the GNU implementation"); } } + +pub fn generate_and_run_uumain(args: &mut Vec, uumain_function: F) -> (String, i32) +where + F: FnOnce(std::vec::IntoIter) -> i32, +{ + let uumain_exit_status; + + let original_stdout_fd = unsafe { dup(STDOUT_FILENO) }; + println!("Running test {:?}", &args[1..]); + let mut pipe_fds = [-1; 2]; + unsafe { libc::pipe(pipe_fds.as_mut_ptr()) }; + + { + unsafe { dup2(pipe_fds[1], STDOUT_FILENO) }; + uumain_exit_status = uumain_function(args.clone().into_iter()); + unsafe { dup2(original_stdout_fd, STDOUT_FILENO) }; + unsafe { libc::close(original_stdout_fd) }; + } + unsafe { libc::close(pipe_fds[1]) }; + + let mut captured_output = Vec::new(); + let mut read_buffer = [0; 1024]; + loop { + let bytes_read = unsafe { + libc::read( + pipe_fds[0], + read_buffer.as_mut_ptr() as *mut libc::c_void, + read_buffer.len(), + ) + }; + if bytes_read <= 0 { + break; + } + captured_output.extend_from_slice(&read_buffer[..bytes_read as usize]); + } + + unsafe { libc::close(pipe_fds[0]) }; + + let my_output = String::from_utf8_lossy(&captured_output) + .to_string() + .trim() + .to_owned(); + + (my_output, uumain_exit_status) +} diff --git a/fuzz/fuzz_targets/fuzz_expr.rs b/fuzz/fuzz_targets/fuzz_expr.rs index e364342b8..28fded99e 100644 --- a/fuzz/fuzz_targets/fuzz_expr.rs +++ b/fuzz/fuzz_targets/fuzz_expr.rs @@ -12,9 +12,9 @@ use rand::seq::SliceRandom; use rand::Rng; use std::ffi::OsString; -use libc::{dup, dup2, STDOUT_FILENO}; use std::process::Command; mod fuzz_common; +use crate::fuzz_common::generate_and_run_uumain; use crate::fuzz_common::is_gnu_cmd; static CMD_PATH: &str = "expr"; @@ -108,52 +108,7 @@ fuzz_target!(|_data: &[u8]| { let mut args = vec![OsString::from("expr")]; args.extend(expr.split_whitespace().map(OsString::from)); - // Save the original stdout file descriptor - let original_stdout_fd = unsafe { dup(STDOUT_FILENO) }; - - // Create a pipe to capture stdout - let mut pipe_fds = [-1; 2]; - unsafe { libc::pipe(pipe_fds.as_mut_ptr()) }; - let uumain_exit_code; - { - // Redirect stdout to the write end of the pipe - unsafe { dup2(pipe_fds[1], STDOUT_FILENO) }; - - // Run uumain with the provided arguments - uumain_exit_code = uumain(args.clone().into_iter()); - - // Restore original stdout - unsafe { dup2(original_stdout_fd, STDOUT_FILENO) }; - unsafe { libc::close(original_stdout_fd) }; - } - // Close the write end of the pipe - unsafe { libc::close(pipe_fds[1]) }; - - // Read captured output from the read end of the pipe - let mut captured_output = Vec::new(); - let mut read_buffer = [0; 1024]; - loop { - let bytes_read = unsafe { - libc::read( - pipe_fds[0], - read_buffer.as_mut_ptr() as *mut libc::c_void, - read_buffer.len(), - ) - }; - if bytes_read <= 0 { - break; - } - captured_output.extend_from_slice(&read_buffer[..bytes_read as usize]); - } - - // Close the read end of the pipe - unsafe { libc::close(pipe_fds[0]) }; - - // Convert captured output to a string - let rust_output = String::from_utf8_lossy(&captured_output) - .to_string() - .trim() - .to_owned(); + let (rust_output, uumain_exit_code) = generate_and_run_uumain(&mut args, uumain); // Run GNU expr with the provided arguments and compare the output match run_gnu_expr(&args[1..]) { diff --git a/fuzz/fuzz_targets/fuzz_test.rs b/fuzz/fuzz_targets/fuzz_test.rs index 537e21abd..535696b45 100644 --- a/fuzz/fuzz_targets/fuzz_test.rs +++ b/fuzz/fuzz_targets/fuzz_test.rs @@ -12,9 +12,11 @@ use rand::seq::SliceRandom; use rand::Rng; use std::ffi::OsString; -use libc::{dup, dup2, STDOUT_FILENO}; use std::process::Command; +mod fuzz_common; +use crate::fuzz_common::generate_and_run_uumain; + #[derive(PartialEq, Debug, Clone)] enum ArgType { STRING, @@ -26,8 +28,11 @@ enum ArgType { // Add any other types as needed } +static CMD_PATH: &str = "/usr/bin/test"; + fn run_gnu_test(args: &[OsString]) -> Result<(String, i32), std::io::Error> { - let mut command = Command::new("test"); + let mut command = Command::new(CMD_PATH); + for arg in args { command.arg(arg); } @@ -210,58 +215,12 @@ fuzz_target!(|_data: &[u8]| { let mut rng = rand::thread_rng(); let max_args = rng.gen_range(1..=6); let mut args = vec![OsString::from("test")]; - let uumain_exit_status; for _ in 0..max_args { args.push(OsString::from(generate_test_arg())); } - // Save the original stdout file descriptor - let original_stdout_fd = unsafe { dup(STDOUT_FILENO) }; - println!("Running test {:?}", &args[1..]); - // Create a pipe to capture stdout - let mut pipe_fds = [-1; 2]; - unsafe { libc::pipe(pipe_fds.as_mut_ptr()) }; - - { - // Redirect stdout to the write end of the pipe - unsafe { dup2(pipe_fds[1], STDOUT_FILENO) }; - - // Run uumain with the provided arguments - uumain_exit_status = uumain(args.clone().into_iter()); - - // Restore original stdout - unsafe { dup2(original_stdout_fd, STDOUT_FILENO) }; - unsafe { libc::close(original_stdout_fd) }; - } - // Close the write end of the pipe - unsafe { libc::close(pipe_fds[1]) }; - - // Read captured output from the read end of the pipe - let mut captured_output = Vec::new(); - let mut read_buffer = [0; 1024]; - loop { - let bytes_read = unsafe { - libc::read( - pipe_fds[0], - read_buffer.as_mut_ptr() as *mut libc::c_void, - read_buffer.len(), - ) - }; - if bytes_read <= 0 { - break; - } - captured_output.extend_from_slice(&read_buffer[..bytes_read as usize]); - } - - // Close the read end of the pipe - unsafe { libc::close(pipe_fds[0]) }; - - // Convert captured output to a string - let my_output = String::from_utf8_lossy(&captured_output) - .to_string() - .trim() - .to_owned(); + let (rust_output, uumain_exit_status) = generate_and_run_uumain(&mut args, uumain); // Run GNU test with the provided arguments and compare the output match run_gnu_test(&args[1..]) { @@ -269,10 +228,10 @@ fuzz_target!(|_data: &[u8]| { let gnu_output = gnu_output.trim().to_owned(); println!("gnu_exit_status {}", gnu_exit_status); println!("uumain_exit_status {}", uumain_exit_status); - if my_output != gnu_output || uumain_exit_status != gnu_exit_status { + if rust_output != gnu_output || uumain_exit_status != gnu_exit_status { println!("Discrepancy detected!"); println!("Test: {:?}", &args[1..]); - println!("My output: {}", my_output); + println!("My output: {}", rust_output); println!("GNU output: {}", gnu_output); println!("My exit status: {}", uumain_exit_status); println!("GNU exit status: {}", gnu_exit_status); From a17ede9ef02ca864dc7fcd5bda681e990339b674 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Thu, 28 Sep 2023 15:36:06 +0200 Subject: [PATCH 2/2] Remove the full path to test --- fuzz/fuzz_targets/fuzz_test.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzz/fuzz_targets/fuzz_test.rs b/fuzz/fuzz_targets/fuzz_test.rs index 535696b45..dbd2db54a 100644 --- a/fuzz/fuzz_targets/fuzz_test.rs +++ b/fuzz/fuzz_targets/fuzz_test.rs @@ -28,7 +28,7 @@ enum ArgType { // Add any other types as needed } -static CMD_PATH: &str = "/usr/bin/test"; +static CMD_PATH: &str = "test"; fn run_gnu_test(args: &[OsString]) -> Result<(String, i32), std::io::Error> { let mut command = Command::new(CMD_PATH);