1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-28 11:37:44 +00:00

fuzzing function should also return stderr

This commit is contained in:
Sylvestre Ledru 2023-11-08 23:06:11 +01:00
parent 5fed5443e4
commit 064ad7200c

View file

@ -3,10 +3,11 @@
// For the full copyright and license information, please view the LICENSE // For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code. // file that was distributed with this source code.
use libc::{close, dup, dup2, pipe, STDOUT_FILENO}; use libc::{close, dup, dup2, pipe, STDERR_FILENO, STDOUT_FILENO};
use std::ffi::OsString; use std::ffi::OsString;
use std::io; use std::io;
use std::io::Write; use std::io::Write;
use std::os::fd::RawFd;
use std::process::Command; use std::process::Command;
use std::sync::atomic::Ordering; use std::sync::atomic::Ordering;
use std::sync::{atomic::AtomicBool, Once}; use std::sync::{atomic::AtomicBool, Once};
@ -33,57 +34,90 @@ pub fn is_gnu_cmd(cmd_path: &str) -> Result<(), std::io::Error> {
} }
} }
pub fn generate_and_run_uumain<F>(args: &[OsString], uumain_function: F) -> (String, i32) pub fn generate_and_run_uumain<F>(args: &[OsString], uumain_function: F) -> (String, String, i32)
where where
F: FnOnce(std::vec::IntoIter<OsString>) -> i32, F: FnOnce(std::vec::IntoIter<OsString>) -> i32,
{ {
let uumain_exit_status; let uumain_exit_status;
// Duplicate the stdout file descriptor // Duplicate the stdout and stderr file descriptors
let original_stdout_fd = unsafe { dup(STDOUT_FILENO) }; let original_stdout_fd = unsafe { dup(STDOUT_FILENO) };
if original_stdout_fd == -1 { let original_stderr_fd = unsafe { dup(STDERR_FILENO) };
return ("Failed to duplicate STDOUT_FILENO".to_string(), -1); if original_stdout_fd == -1 || original_stderr_fd == -1 {
}
println!("Running test {:?}", &args[0..]);
let mut pipe_fds = [-1; 2];
if unsafe { pipe(pipe_fds.as_mut_ptr()) } == -1 {
return ("Failed to create a pipe".to_string(), -1);
}
// Redirect stdout to the pipe
unsafe {
if dup2(pipe_fds[1], STDOUT_FILENO) == -1 {
close(pipe_fds[0]);
close(pipe_fds[1]);
return ( return (
"Failed to redirect STDOUT_FILENO to the pipe".to_string(), "Failed to duplicate STDOUT_FILENO or STDERR_FILENO".to_string(),
"".to_string(),
-1, -1,
); );
} }
println!("Running test {:?}", &args[0..]);
let mut pipe_stdout_fds = [-1; 2];
let mut pipe_stderr_fds = [-1; 2];
// Create pipes for stdout and stderr
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);
}
// Redirect stdout and stderr to their respective pipes
if unsafe { dup2(pipe_stdout_fds[1], STDOUT_FILENO) } == -1
|| unsafe { dup2(pipe_stderr_fds[1], STDERR_FILENO) } == -1
{
unsafe {
close(pipe_stdout_fds[0]);
close(pipe_stdout_fds[1]);
close(pipe_stderr_fds[0]);
close(pipe_stderr_fds[1]);
}
return (
"Failed to redirect STDOUT_FILENO or STDERR_FILENO".to_string(),
"".to_string(),
-1,
);
} }
uumain_exit_status = uumain_function(args.to_owned().into_iter()); uumain_exit_status = uumain_function(args.to_owned().into_iter());
io::stdout().flush().unwrap(); io::stdout().flush().unwrap();
io::stderr().flush().unwrap();
// Restore the original stdout // Restore the original stdout and stderr
unsafe { if unsafe { dup2(original_stdout_fd, STDOUT_FILENO) } == -1
if dup2(original_stdout_fd, STDOUT_FILENO) == -1 { || unsafe { dup2(original_stderr_fd, STDERR_FILENO) } == -1
{
return ( return (
"Failed to restore the original STDOUT_FILENO".to_string(), "Failed to restore the original STDOUT_FILENO or STDERR_FILENO".to_string(),
"".to_string(),
-1, -1,
); );
} }
unsafe {
close(original_stdout_fd); close(original_stdout_fd);
close(original_stderr_fd);
} }
unsafe { close(pipe_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();
let captured_stderr = captured_stderr
.splitn(2, ':')
.nth(1)
.unwrap_or("")
.trim()
.to_string();
(captured_stdout, captured_stderr, uumain_exit_status)
}
fn read_from_fd(fd: RawFd) -> String {
let mut captured_output = Vec::new(); let mut captured_output = Vec::new();
let mut read_buffer = [0; 1024]; let mut read_buffer = [0; 1024];
loop { loop {
let bytes_read = unsafe { let bytes_read = unsafe {
libc::read( libc::read(
pipe_fds[0], fd,
read_buffer.as_mut_ptr() as *mut libc::c_void, read_buffer.as_mut_ptr() as *mut libc::c_void,
read_buffer.len(), read_buffer.len(),
) )
@ -93,29 +127,30 @@ where
eprintln!("Failed to read from the pipe"); eprintln!("Failed to read from the pipe");
break; break;
} }
if bytes_read <= 0 { if bytes_read == 0 {
break; break;
} }
captured_output.extend_from_slice(&read_buffer[..bytes_read as usize]); captured_output.extend_from_slice(&read_buffer[..bytes_read as usize]);
} }
unsafe { libc::close(pipe_fds[0]) }; unsafe { libc::close(fd) };
let my_output = String::from_utf8_lossy(&captured_output) String::from_utf8_lossy(&captured_output).into_owned()
.to_string()
.trim()
.to_owned();
(my_output, uumain_exit_status)
} }
pub fn run_gnu_cmd( pub fn run_gnu_cmd(
cmd_path: &str, cmd_path: &str,
args: &[OsString], args: &[OsString],
check_gnu: bool, check_gnu: bool,
) -> Result<(String, i32), io::Error> { ) -> Result<(String, String, i32), (String, String, i32)> {
if check_gnu { if check_gnu {
is_gnu_cmd(cmd_path)?; // Check if it's a GNU implementation 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));
}
}
} }
let mut command = Command::new(cmd_path); let mut command = Command::new(cmd_path);
@ -123,17 +158,25 @@ pub fn run_gnu_cmd(
command.arg(arg); command.arg(arg);
} }
let output = command.output()?; let output = match command.output() {
Ok(output) => output,
Err(e) => return Err((String::new(), e.to_string(), -1)),
};
let exit_code = output.status.code().unwrap_or(-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();
let stderr = stderr
.splitn(2, ':')
.nth(1)
.unwrap_or("")
.trim()
.to_string();
if output.status.success() || !check_gnu { if output.status.success() || !check_gnu {
Ok(( Ok((stdout, stderr, exit_code))
String::from_utf8_lossy(&output.stdout).to_string(),
exit_code,
))
} else { } else {
Err(io::Error::new( Err((stdout, stderr, exit_code))
io::ErrorKind::Other,
format!("GNU command execution failed with exit code {}", exit_code),
))
} }
} }