Skip to content

Commit

Permalink
fuzz: add the capability to pipe info into fuzz
Browse files Browse the repository at this point in the history
  • Loading branch information
sylvestre committed Dec 17, 2023
1 parent b2e26f2 commit edb58c2
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 20 deletions.
84 changes: 74 additions & 10 deletions fuzz/fuzz_targets/fuzz_common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,19 @@
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.

use libc::STDIN_FILENO;
use libc::{close, dup, dup2, pipe, STDERR_FILENO, STDOUT_FILENO};
use rand::prelude::SliceRandom;
use rand::Rng;
use std::ffi::OsString;
use std::io;
use std::io::Seek;
use std::io::SeekFrom;
use std::io::Write;
use std::os::fd::AsRawFd;
use std::os::fd::RawFd;
use std::process::Command;
use std::process::Stdio;
use std::sync::atomic::Ordering;
use std::sync::{atomic::AtomicBool, Once};

Expand Down Expand Up @@ -49,7 +54,11 @@ pub fn is_gnu_cmd(cmd_path: &str) -> Result<(), std::io::Error> {
}
}

pub fn generate_and_run_uumain<F>(args: &[OsString], uumain_function: F) -> CommandResult
pub fn generate_and_run_uumain<F>(
args: &[OsString],
uumain_function: F,
pipe_input: Option<&str>,
) -> CommandResult
where
F: FnOnce(std::vec::IntoIter<OsString>) -> i32,
{
Expand Down Expand Up @@ -95,6 +104,26 @@ where
};
}

let original_stdin_fd = if let Some(input_str) = pipe_input {
// we have pipe input
let mut input_file = tempfile::tempfile().unwrap();
write!(input_file, "{}", input_str).unwrap();
input_file.seek(SeekFrom::Start(0)).unwrap();

// Redirect stdin to read from the in-memory file
let original_stdin_fd = unsafe { dup(STDIN_FILENO) };
if original_stdin_fd == -1 || unsafe { dup2(input_file.as_raw_fd(), STDIN_FILENO) } == -1 {
return CommandResult {
stdout: "Failed to set up stdin redirection".to_string(),
stderr: "".to_string(),
exit_code: -1,
};
}
Some(original_stdin_fd)
} else {
None
};

let uumain_exit_status = uumain_function(args.to_owned().into_iter());

io::stdout().flush().unwrap();
Expand All @@ -118,6 +147,18 @@ where
close(pipe_stderr_fds[1]);
}

// Restore the original stdin if it was modified
if let Some(fd) = original_stdin_fd {
if unsafe { dup2(fd, STDIN_FILENO) } == -1 {
return CommandResult {
stdout: "Failed to restore the original STDIN".to_string(),
stderr: "".to_string(),
exit_code: -1,
};
}
unsafe { close(fd) };
}

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();
let captured_stderr = captured_stderr
Expand Down Expand Up @@ -165,6 +206,7 @@ pub fn run_gnu_cmd(
cmd_path: &str,
args: &[OsString],
check_gnu: bool,
pipe_input: Option<&str>,
) -> Result<CommandResult, CommandResult> {
if check_gnu {
match is_gnu_cmd(cmd_path) {
Expand All @@ -185,18 +227,40 @@ pub fn run_gnu_cmd(
command.arg(arg);
}

let output = match command.output() {
Ok(output) => output,
Err(e) => {
return Err(CommandResult {
stdout: String::new(),
stderr: e.to_string(),
exit_code: -1,
});
let output = if let Some(input_str) = pipe_input {
// We have an pipe input
command.stdin(Stdio::piped()).stdout(Stdio::piped());

let mut child = command.spawn().expect("Failed to execute command");
let child_stdin = child.stdin.as_mut().unwrap();
child_stdin
.write_all(input_str.as_bytes())
.expect("Failed to write to stdin");

match child.wait_with_output() {
Ok(output) => output,
Err(e) => {
return Err(CommandResult {
stdout: String::new(),
stderr: e.to_string(),
exit_code: -1,
});
}
}
} else {
// Just run with args
match command.output() {
Ok(output) => output,
Err(e) => {
return Err(CommandResult {
stdout: String::new(),
stderr: e.to_string(),
exit_code: -1,
});
}
}
};
let exit_code = output.status.code().unwrap_or(-1);

// Here we get stdout and stderr as Strings
let stdout = String::from_utf8_lossy(&output.stdout).to_string();
let stderr = String::from_utf8_lossy(&output.stderr).to_string();
Expand Down
4 changes: 2 additions & 2 deletions fuzz/fuzz_targets/fuzz_echo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,9 @@ fuzz_target!(|_data: &[u8]| {
let echo_input = generate_echo();
let mut args = vec![OsString::from("echo")];
args.extend(echo_input.split_whitespace().map(OsString::from));
let rust_result = generate_and_run_uumain(&args, uumain);
let rust_result = generate_and_run_uumain(&args, uumain, None);

let gnu_result = match run_gnu_cmd(CMD_PATH, &args[1..], false) {
let gnu_result = match run_gnu_cmd(CMD_PATH, &args[1..], false, None) {
Ok(result) => result,
Err(error_result) => {
eprintln!("Failed to run GNU command:");
Expand Down
4 changes: 2 additions & 2 deletions fuzz/fuzz_targets/fuzz_expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,9 @@ 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_result = generate_and_run_uumain(&args, uumain, None);

let gnu_result = match run_gnu_cmd(CMD_PATH, &args[1..], false) {
let gnu_result = match run_gnu_cmd(CMD_PATH, &args[1..], false, None) {
Ok(result) => result,
Err(error_result) => {
eprintln!("Failed to run GNU command:");
Expand Down
4 changes: 2 additions & 2 deletions fuzz/fuzz_targets/fuzz_printf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,9 @@ fuzz_target!(|_data: &[u8]| {
let printf_input = generate_printf();
let mut args = vec![OsString::from("printf")];
args.extend(printf_input.split_whitespace().map(OsString::from));
let rust_result = generate_and_run_uumain(&args, uumain);
let rust_result = generate_and_run_uumain(&args, uumain, None);

let gnu_result = match run_gnu_cmd(CMD_PATH, &args[1..], false) {
let gnu_result = match run_gnu_cmd(CMD_PATH, &args[1..], false, None) {
Ok(result) => result,
Err(error_result) => {
eprintln!("Failed to run GNU command:");
Expand Down
4 changes: 2 additions & 2 deletions fuzz/fuzz_targets/fuzz_seq.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,9 @@ fuzz_target!(|_data: &[u8]| {
let mut args = vec![OsString::from("seq")];
args.extend(seq.split_whitespace().map(OsString::from));

let rust_result = generate_and_run_uumain(&args, uumain);
let rust_result = generate_and_run_uumain(&args, uumain, None);

let gnu_result = match run_gnu_cmd(CMD_PATH, &args[1..], false) {
let gnu_result = match run_gnu_cmd(CMD_PATH, &args[1..], false, None) {
Ok(result) => result,
Err(error_result) => {
eprintln!("Failed to run GNU command:");
Expand Down
4 changes: 2 additions & 2 deletions fuzz/fuzz_targets/fuzz_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,9 +184,9 @@ fuzz_target!(|_data: &[u8]| {
args.push(OsString::from(generate_test_arg()));
}

let rust_result = generate_and_run_uumain(&args, uumain);
let rust_result = generate_and_run_uumain(&args, uumain, None);

let gnu_result = match run_gnu_cmd(CMD_PATH, &args[1..], false) {
let gnu_result = match run_gnu_cmd(CMD_PATH, &args[1..], false, None) {
Ok(result) => result,
Err(error_result) => {
eprintln!("Failed to run GNU command:");
Expand Down

0 comments on commit edb58c2

Please sign in to comment.