Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions fuzz/fuzz_targets/fuzz_common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand All @@ -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<F>(args: &mut Vec<OsString>, uumain_function: F) -> (String, i32)
where
F: FnOnce(std::vec::IntoIter<OsString>) -> 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)
}
49 changes: 2 additions & 47 deletions fuzz/fuzz_targets/fuzz_expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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..]) {
Expand Down
61 changes: 10 additions & 51 deletions fuzz/fuzz_targets/fuzz_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -26,8 +28,11 @@ enum ArgType {
// Add any other types as needed
}

static CMD_PATH: &str = "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);
}
Expand Down Expand Up @@ -210,69 +215,23 @@ 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..]) {
Ok((gnu_output, gnu_exit_status)) => {
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);
Expand Down