Skip to content

Commit

Permalink
Merge pull request uutils#5600 from sylvestre/fuzz-echo
Browse files Browse the repository at this point in the history
Fuzz the echo command
  • Loading branch information
sylvestre authored Dec 4, 2023
2 parents 8a6ee38 + 9ef4319 commit d913057
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 0 deletions.
1 change: 1 addition & 0 deletions .github/workflows/fuzzing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ jobs:
- { name: fuzz_date, should_pass: false }
- { name: fuzz_expr, should_pass: true }
- { name: fuzz_printf, should_pass: false }
- { name: fuzz_echo, should_pass: false }
- { name: fuzz_parse_glob, should_pass: true }
- { name: fuzz_parse_size, should_pass: true }
- { name: fuzz_parse_time, should_pass: true }
Expand Down
7 changes: 7 additions & 0 deletions fuzz/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ uu_date = { path = "../src/uu/date/" }
uu_test = { path = "../src/uu/test/" }
uu_expr = { path = "../src/uu/expr/" }
uu_printf = { path = "../src/uu/printf/" }
uu_echo = { path = "../src/uu/echo/" }


# Prevent this from interfering with workspaces
Expand All @@ -35,6 +36,12 @@ path = "fuzz_targets/fuzz_printf.rs"
test = false
doc = false

[[bin]]
name = "fuzz_echo"
path = "fuzz_targets/fuzz_echo.rs"
test = false
doc = false

[[bin]]
name = "fuzz_expr"
path = "fuzz_targets/fuzz_expr.rs"
Expand Down
89 changes: 89 additions & 0 deletions fuzz/fuzz_targets/fuzz_echo.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
#![no_main]
use libfuzzer_sys::fuzz_target;
use uu_echo::uumain;

use rand::prelude::SliceRandom;
use rand::Rng;
use std::ffi::OsString;

mod fuzz_common;
use crate::fuzz_common::CommandResult;
use crate::fuzz_common::{
compare_result, generate_and_run_uumain, generate_random_string, run_gnu_cmd,
};

static CMD_PATH: &str = "echo";

fn generate_echo() -> String {
let mut rng = rand::thread_rng();
let mut echo_str = String::new();

// Randomly decide whether to include options
let include_n = rng.gen_bool(0.1); // 10% chance
let include_e = rng.gen_bool(0.1); // 10% chance
let include_E = rng.gen_bool(0.1); // 10% chance

if include_n {
echo_str.push_str("-n ");
}
if include_e {
echo_str.push_str("-e ");
}
if include_E {
echo_str.push_str("-E ");
}

// Add a random string
echo_str.push_str(&generate_random_string(rng.gen_range(1..=10)));

// Include escape sequences if -e is enabled
if include_e {
// Add a 10% chance of including an escape sequence
if rng.gen_bool(0.1) {
echo_str.push_str(&generate_escape_sequence(&mut rng));
}
}

echo_str
}

fn generate_escape_sequence(rng: &mut impl Rng) -> String {
let escape_sequences = [
"\\\\", "\\a", "\\b", "\\c", "\\e", "\\f", "\\n", "\\r", "\\t", "\\v", "\\0NNN", "\\xHH",
];
// \0NNN and \xHH need more work
escape_sequences.choose(rng).unwrap().to_string()
}

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 gnu_result = match run_gnu_cmd(CMD_PATH, &args[1..], false) {
Ok(result) => result,
Err(error_result) => {
eprintln!("Failed to run GNU command:");
eprintln!("Stderr: {}", error_result.stderr);
eprintln!("Exit Code: {}", error_result.exit_code);
CommandResult {
stdout: String::new(),
stderr: error_result.stderr,
exit_code: error_result.exit_code,
}
}
};

compare_result(
"echo",
&format!("{:?}", &args[1..]),
&rust_result.stdout,
&gnu_result.stdout,
&rust_result.stderr,
&gnu_result.stderr,
rust_result.exit_code,
gnu_result.exit_code,
true,
);
});

0 comments on commit d913057

Please sign in to comment.