From 66e0835e72a72d8ff0a97213b5320fc45973ecbc Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Wed, 29 Nov 2023 09:55:55 +0100 Subject: [PATCH 1/3] fuzz the echo command --- fuzz/Cargo.toml | 7 +++ fuzz/fuzz_targets/fuzz_echo.rs | 93 ++++++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+) create mode 100644 fuzz/fuzz_targets/fuzz_echo.rs diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 630af4650..b27f5b586 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -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 @@ -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" diff --git a/fuzz/fuzz_targets/fuzz_echo.rs b/fuzz/fuzz_targets/fuzz_echo.rs new file mode 100644 index 000000000..3d8100853 --- /dev/null +++ b/fuzz/fuzz_targets/fuzz_echo.rs @@ -0,0 +1,93 @@ +#![no_main] +use libfuzzer_sys::fuzz_target; +use uu_echo::uumain; // Changed from uu_printf to uu_echo + +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 = "/usr/bin/echo"; // Changed from "printf" to "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 + // --help and --version are typically not included in fuzzing as they don't change output format + + 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)); // This function should handle echo-specific sequences + } + } + + echo_str +} + +// You should also modify the generate_escape_sequence function to include echo-specific sequences +fn generate_escape_sequence(rng: &mut impl Rng) -> String { + let escape_sequences = [ + "\\\\", "\\a", "\\b", "\\c", "\\e", "\\f", "\\n", "\\r", "\\t", "\\v", + "\\0NNN", // You can randomly generate NNN + "\\xHH", // You can randomly generate HH + // ... other sequences + ]; + escape_sequences.choose(rng).unwrap().to_string() +} + +fuzz_target!(|_data: &[u8]| { + let echo_input = generate_echo(); // Changed from generate_printf to generate_echo + let mut args = vec![OsString::from("echo")]; // Changed from "printf" to "echo" + args.extend(echo_input.split_whitespace().map(OsString::from)); + let rust_result = generate_and_run_uumain(&args, uumain); // uumain function from uu_echo + + 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, + false, // Set to true if you want to fail on stderr diff + ); +}); From 4d5c034eb10d0a07098abb75ddd5771b6d444c05 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Wed, 29 Nov 2023 14:11:43 +0100 Subject: [PATCH 2/3] Run the echo fuzzer in the CI --- .github/workflows/fuzzing.yml | 1 + fuzz/fuzz_targets/fuzz_echo.rs | 21 ++++++++------------- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/.github/workflows/fuzzing.yml b/.github/workflows/fuzzing.yml index 2274f6905..cc1547c87 100644 --- a/.github/workflows/fuzzing.yml +++ b/.github/workflows/fuzzing.yml @@ -42,6 +42,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 } diff --git a/fuzz/fuzz_targets/fuzz_echo.rs b/fuzz/fuzz_targets/fuzz_echo.rs index 3d8100853..826fd6da3 100644 --- a/fuzz/fuzz_targets/fuzz_echo.rs +++ b/fuzz/fuzz_targets/fuzz_echo.rs @@ -1,6 +1,6 @@ #![no_main] use libfuzzer_sys::fuzz_target; -use uu_echo::uumain; // Changed from uu_printf to uu_echo +use uu_echo::uumain; use rand::prelude::SliceRandom; use rand::Rng; @@ -12,7 +12,7 @@ use crate::fuzz_common::{ compare_result, generate_and_run_uumain, generate_random_string, run_gnu_cmd, }; -static CMD_PATH: &str = "/usr/bin/echo"; // Changed from "printf" to "echo" +static CMD_PATH: &str = "echo"; fn generate_echo() -> String { let mut rng = rand::thread_rng(); @@ -22,7 +22,6 @@ fn generate_echo() -> String { 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 - // --help and --version are typically not included in fuzzing as they don't change output format if include_n { echo_str.push_str("-n "); @@ -41,29 +40,25 @@ fn generate_echo() -> String { 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)); // This function should handle echo-specific sequences + echo_str.push_str(&generate_escape_sequence(&mut rng)); } } echo_str } -// You should also modify the generate_escape_sequence function to include echo-specific sequences fn generate_escape_sequence(rng: &mut impl Rng) -> String { let escape_sequences = [ - "\\\\", "\\a", "\\b", "\\c", "\\e", "\\f", "\\n", "\\r", "\\t", "\\v", - "\\0NNN", // You can randomly generate NNN - "\\xHH", // You can randomly generate HH - // ... other sequences + "\\\\", "\\a", "\\b", "\\c", "\\e", "\\f", "\\n", "\\r", "\\t", "\\v", "\\0NNN", "\\xHH", ]; escape_sequences.choose(rng).unwrap().to_string() } fuzz_target!(|_data: &[u8]| { - let echo_input = generate_echo(); // Changed from generate_printf to generate_echo - let mut args = vec![OsString::from("echo")]; // Changed from "printf" to "echo" + 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); // uumain function from uu_echo + let rust_result = generate_and_run_uumain(&args, uumain); let gnu_result = match run_gnu_cmd(CMD_PATH, &args[1..], false) { Ok(result) => result, @@ -88,6 +83,6 @@ fuzz_target!(|_data: &[u8]| { &gnu_result.stderr, rust_result.exit_code, gnu_result.exit_code, - false, // Set to true if you want to fail on stderr diff + true, ); }); From 9ef43191ff28af359989848dc65548aa253e4337 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Mon, 4 Dec 2023 21:20:47 +0100 Subject: [PATCH 3/3] Document that \0NNN and \xHH need more work --- fuzz/fuzz_targets/fuzz_echo.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/fuzz/fuzz_targets/fuzz_echo.rs b/fuzz/fuzz_targets/fuzz_echo.rs index 826fd6da3..fda7fd727 100644 --- a/fuzz/fuzz_targets/fuzz_echo.rs +++ b/fuzz/fuzz_targets/fuzz_echo.rs @@ -51,6 +51,7 @@ 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() }