diff --git a/fuzz/fuzz_targets/fuzz_non_utf8_paths.rs b/fuzz/fuzz_targets/fuzz_non_utf8_paths.rs index 78a6c2d0a..b02ad1dab 100644 --- a/fuzz/fuzz_targets/fuzz_non_utf8_paths.rs +++ b/fuzz/fuzz_targets/fuzz_non_utf8_paths.rs @@ -17,23 +17,87 @@ use std::path::PathBuf; use uufuzz::{run_gnu_cmd, CommandResult}; // Programs that typically take file/path arguments and should be tested static PATH_PROGRAMS: &[&str] = &[ - // Core file operations "cat", "cp", "mv", "rm", "ln", "link", "unlink", "touch", "truncate", - // Directory operations - "ls", "mkdir", "rmdir", "du", "stat", "mktemp", + "cat", + "cp", + "mv", + "rm", + "ln", + "link", + "unlink", + "touch", // Path operations - "basename", "dirname", "readlink", "realpath", "pathchk", - // File content operations - "head", "tail", "tee", "more", "od", "wc", "cksum", "sum", + "ls", "mkdir", "rmdir", "du", "stat", "mktemp", "df", // Path operations + "ls", + "mkdir", + "rmdir", + "du", + "stat", + "df", + "df", // Path operations + "basename", + "dirname", + "readlink", + "realpath", + "pathchk", // File processing - "sort", "uniq", "split", "csplit", "cut", "tr", "shred", - // File permissions/ownership - "chmod", "chown", "chgrp", "install", + "head", "tail", "tee", "more", "od", "wc", "cksum", "sum", "nl", "tac", // File processing + "head", + "tail", + "tee", + "more", + "od", + "wc", + "cksum", + "sum", + "tac", + "tac", // File processing + "sort", + "uniq", + "split", + "csplit", + "cut", + "tr", + "shred", + "shuf", + "ptx", // Text processing with files - "comm", "join", "paste", "pr", "fmt", "fold", "expand", "unexpand", - // Directory listing variants + "chmod", "chown", "chgrp", "install", "chcon", "runcon", // Text processing with files + "chmod", + "chown", + "chgrp", + "install", + "runcon", + "runcon", // Text processing with files + "comm", + "join", + "paste", + "pr", + "fmt", + "fold", + "expand", "dir", "vdir", + "dir", + "mkfifo", "mknod", + "mkfifo", + "mknod", + // File I/O utilities + // File I/O utilities + "dd", + "sync", + "stdbuf", + "dircolors", + "base32", "base64", "basenc", + "base32", + "base64", + "stty", "tty", + "stty", + "env", "nohup", "nice", "timeout", + "env", + "nohup", + "nice", ]; + "timeout", fn generate_non_utf8_bytes() -> Vec { let mut rng = rand::rng(); @@ -94,7 +158,8 @@ fn test_program_with_non_utf8_path(program: &str, path: &PathBuf) -> CommandResu let path_os = path.as_os_str(); // Use the locally built uutils binary instead of system PATH - let local_binary = "/home/sylvestre/dev/debian/coreutils.disable-loca/target/debug/coreutils"; + let local_binary = std::env::var("CARGO_BIN_FILE_COREUTILS") + .unwrap_or_else(|_| "target/release/coreutils".to_string()); // Build appropriate arguments for each program let local_args = match program { @@ -114,6 +179,22 @@ fn test_program_with_non_utf8_path(program: &str, path: &PathBuf) -> CommandResu OsString::from("root"), path_os.to_owned(), ], + "chcon" => vec![ + OsString::from(program), + OsString::from("system_u:object_r:admin_home_t:s0"), + path_os.to_owned(), + ], + "runcon" => { + let coreutils_binary = std::env::var("CARGO_BIN_FILE_COREUTILS") + .unwrap_or_else(|_| "target/release/coreutils".to_string()); + vec![ + OsString::from(program), + OsString::from("system_u:object_r:admin_home_t:s0"), + OsString::from(coreutils_binary), + OsString::from("cat"), + path_os.to_owned(), + ] + }, // Programs that need source and destination "cp" | "mv" | "ln" | "link" => { let dest_path = path.with_extension("dest"); @@ -122,7 +203,7 @@ fn test_program_with_non_utf8_path(program: &str, path: &PathBuf) -> CommandResu path_os.to_owned(), dest_path.as_os_str().to_owned(), ] - }, + } "install" => { let dest_path = path.with_extension("dest"); vec![ @@ -130,7 +211,7 @@ fn test_program_with_non_utf8_path(program: &str, path: &PathBuf) -> CommandResu path_os.to_owned(), dest_path.as_os_str().to_owned(), ] - }, + } // Programs that need size/truncate operations "truncate" => vec![ OsString::from(program), @@ -147,6 +228,104 @@ fn test_program_with_non_utf8_path(program: &str, path: &PathBuf) -> CommandResu path_os.to_owned(), OsString::from("1"), ], + // File creation programs + "mkfifo" | "mknod" => { + let new_path = path.with_extension("new"); + if program == "mknod" { + vec![ + OsString::from(program), + new_path.as_os_str().to_owned(), + OsString::from("c"), + OsString::from("1"), + OsString::from("3"), + ] + vec![ + } + }, + } + "dd" => vec![ + OsString::from(program), + OsString::from(format!("if={}", path_os.to_string_lossy())), + OsString::from("of=/dev/null"), + OsString::from("bs=1"), + OsString::from("count=1"), + ], + // Hashsum needs algorithm + "hashsum" => vec![ + OsString::from(program), + OsString::from("--md5"), + path_os.to_owned(), + ], + "base32" | "base64" | "basenc" => vec![ + "df" => vec![ + "chroot" => { + "df" => vec![OsString::from(program), path_os.to_owned()], + // chroot needs a directory and command + vec![ + OsString::from(program), + path_os.to_owned(), + OsString::from("true"), + }, + } + "stty" => vec![ + "sync" => vec![OsString::from(program), path_os.to_owned()], + OsString::from(program), + OsString::from("-F"), + path_os.to_owned(), + "tty" => vec![ + "tty" => vec![OsString::from(program)], // tty doesn't take file args, but test anyway + "env" => { + let coreutils_binary = std::env::var("CARGO_BIN_FILE_COREUTILS") + .unwrap_or_else(|_| "target/release/coreutils".to_string()); + vec![ + OsString::from(program), + OsString::from(coreutils_binary), + OsString::from("cat"), + path_os.to_owned(), + ] + }, + "nohup" => { + let coreutils_binary = std::env::var("CARGO_BIN_FILE_COREUTILS") + .unwrap_or_else(|_| "target/release/coreutils".to_string()); + vec![ + OsString::from(program), + OsString::from(coreutils_binary), + OsString::from("cat"), + path_os.to_owned(), + ] + }, + "nice" => { + let coreutils_binary = std::env::var("CARGO_BIN_FILE_COREUTILS") + .unwrap_or_else(|_| "target/release/coreutils".to_string()); + vec![ + OsString::from(program), + OsString::from(coreutils_binary), + OsString::from("cat"), + path_os.to_owned(), + ] + }, + "timeout" => { + let coreutils_binary = std::env::var("CARGO_BIN_FILE_COREUTILS") + .unwrap_or_else(|_| "target/release/coreutils".to_string()); + vec![ + OsString::from(program), + OsString::from("1"), + OsString::from(coreutils_binary), + OsString::from("cat"), + path_os.to_owned(), + ] + }, + "stdbuf" => { + let coreutils_binary = std::env::var("CARGO_BIN_FILE_COREUTILS") + .unwrap_or_else(|_| "target/release/coreutils".to_string()); + vec![ + OsString::from(program), + OsString::from("-o0"), + OsString::from(coreutils_binary), + OsString::from("cat"), + path_os.to_owned(), + ] + }, // Programs that work with multiple files (use just one for testing) "comm" | "join" => { // These need two files, use the same file twice for simplicity @@ -155,7 +334,7 @@ fn test_program_with_non_utf8_path(program: &str, path: &PathBuf) -> CommandResu path_os.to_owned(), path_os.to_owned(), ] - }, + } // Programs that typically take file input _ => vec![OsString::from(program), path_os.to_owned()], }; @@ -238,8 +417,8 @@ fuzz_target!(|_data: &[u8]| { let non_utf8_dir_name = generate_non_utf8_osstring(); let non_utf8_dir = temp_root.join(non_utf8_dir_name); - let local_binary = - "/home/sylvestre/dev/debian/coreutils.disable-loca/target/debug/coreutils"; + let local_binary = std::env::var("CARGO_BIN_FILE_COREUTILS") + .unwrap_or_else(|_| "target/release/coreutils".to_string()); let mkdir_args = vec![OsString::from("mkdir"), non_utf8_dir.as_os_str().to_owned()]; let mkdir_result = run_gnu_cmd(local_binary, &mkdir_args, false, None); diff --git a/src/uu/ptx/src/ptx.rs b/src/uu/ptx/src/ptx.rs index b8352e06f..60ded7c83 100644 --- a/src/uu/ptx/src/ptx.rs +++ b/src/uu/ptx/src/ptx.rs @@ -7,10 +7,12 @@ use std::cmp; use std::collections::{BTreeSet, HashMap, HashSet}; +use std::ffi::OsString; use std::fmt::Write as FmtWrite; use std::fs::File; use std::io::{BufRead, BufReader, BufWriter, Read, Write, stdin, stdout}; use std::num::ParseIntError; +use std::path::Path; use clap::{Arg, ArgAction, Command}; use regex::Regex; @@ -66,13 +68,12 @@ fn read_word_filter_file( option: &str, ) -> std::io::Result> { let filename = matches - .get_one::(option) - .expect("parsing options failed!") - .to_string(); + .get_one::(option) + .expect("parsing options failed!"); let reader: BufReader> = BufReader::new(if filename == "-" { Box::new(stdin()) } else { - let file = File::open(filename)?; + let file = File::open(Path::new(filename))?; Box::new(file) }); let mut words: HashSet = HashSet::new(); @@ -88,12 +89,12 @@ fn read_char_filter_file( option: &str, ) -> std::io::Result> { let filename = matches - .get_one::(option) + .get_one::(option) .expect("parsing options failed!"); let mut reader: Box = if filename == "-" { Box::new(stdin()) } else { - let file = File::open(filename)?; + let file = File::open(Path::new(filename))?; Box::new(file) }; let mut buffer = String::new(); @@ -275,14 +276,14 @@ struct FileContent { type FileMap = HashMap; -fn read_input(input_files: &[String]) -> std::io::Result { +fn read_input(input_files: &[OsString]) -> std::io::Result { let mut file_map: FileMap = HashMap::new(); let mut offset: usize = 0; for filename in input_files { let reader: BufReader> = BufReader::new(if filename == "-" { Box::new(stdin()) } else { - let file = File::open(filename)?; + let file = File::open(Path::new(filename))?; Box::new(file) }); let lines: Vec = reader.lines().collect::>>()?; @@ -292,7 +293,7 @@ fn read_input(input_files: &[String]) -> std::io::Result { let chars_lines: Vec> = lines.iter().map(|x| x.chars().collect()).collect(); let size = lines.len(); file_map.insert( - filename.to_owned(), + filename.to_string_lossy().into_owned(), FileContent { lines, chars_lines, @@ -736,7 +737,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { let output_file; let mut files = matches - .get_many::(options::FILE) + .get_many::(options::FILE) .into_iter() .flatten() .cloned(); @@ -745,18 +746,22 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { input_files = { let mut files = files.collect::>(); if files.is_empty() { - files.push("-".to_string()); + files.push(OsString::from("-")); } files }; output_file = "-".to_string(); } else { - input_files = vec![files.next().unwrap_or("-".to_string())]; - output_file = files.next().unwrap_or("-".to_string()); + input_files = vec![files.next().unwrap_or(OsString::from("-"))]; + output_file = files + .next() + .unwrap_or(OsString::from("-")) + .to_string_lossy() + .into_owned(); if let Some(file) = files.next() { return Err(UUsageError::new( 1, - translate!("ptx-error-extra-operand", "operand" => file.quote()), + translate!("ptx-error-extra-operand", "operand" => file.to_string_lossy().quote()), )); } } @@ -778,7 +783,8 @@ pub fn uu_app() -> Command { Arg::new(options::FILE) .hide(true) .action(ArgAction::Append) - .value_hint(clap::ValueHint::FilePath), + .value_hint(clap::ValueHint::FilePath) + .value_parser(clap::value_parser!(OsString)), ) .arg( Arg::new(options::AUTO_REFERENCE) @@ -856,7 +862,8 @@ pub fn uu_app() -> Command { .long(options::BREAK_FILE) .help(translate!("ptx-help-break-file")) .value_name("FILE") - .value_hint(clap::ValueHint::FilePath), + .value_hint(clap::ValueHint::FilePath) + .value_parser(clap::value_parser!(OsString)), ) .arg( Arg::new(options::IGNORE_CASE) @@ -878,7 +885,8 @@ pub fn uu_app() -> Command { .long(options::IGNORE_FILE) .help(translate!("ptx-help-ignore-file")) .value_name("FILE") - .value_hint(clap::ValueHint::FilePath), + .value_hint(clap::ValueHint::FilePath) + .value_parser(clap::value_parser!(OsString)), ) .arg( Arg::new(options::ONLY_FILE) @@ -886,7 +894,8 @@ pub fn uu_app() -> Command { .long(options::ONLY_FILE) .help(translate!("ptx-help-only-file")) .value_name("FILE") - .value_hint(clap::ValueHint::FilePath), + .value_hint(clap::ValueHint::FilePath) + .value_parser(clap::value_parser!(OsString)), ) .arg( Arg::new(options::REFERENCES)