From 90b61a8c5cb5768f93ef935cb6ee4c6d328ab128 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Mon, 20 Nov 2023 22:17:57 +0100 Subject: [PATCH 1/3] fuzzing: add a variable to state if we know it fails or not --- .github/workflows/fuzzing.yml | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/.github/workflows/fuzzing.yml b/.github/workflows/fuzzing.yml index e7a9cb1e3..cbb0574de 100644 --- a/.github/workflows/fuzzing.yml +++ b/.github/workflows/fuzzing.yml @@ -37,16 +37,13 @@ jobs: strategy: matrix: test-target: - [ - fuzz_date, - fuzz_test, - fuzz_expr, - fuzz_parse_glob, - fuzz_parse_size, - fuzz_parse_time, - # adding more fuzz tests here. - # e.g. fuzz_test_a, - ] + - { name: fuzz_test, should_pass: true } + # https://github.com/uutils/coreutils/issues/5311 + - { name: fuzz_date, should_pass: false } + - { name: fuzz_expr, should_pass: true } + - { name: fuzz_parse_glob, should_pass: true } + - { name: fuzz_parse_size, should_pass: true } + - { name: fuzz_parse_time, should_pass: true } steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@nightly @@ -59,16 +56,17 @@ jobs: - name: Restore Cached Corpus uses: actions/cache/restore@v3 with: - key: corpus-cache-${{ matrix.test-target }} + key: corpus-cache-${{ matrix.test-target.name }} path: | - fuzz/corpus/${{ matrix.test-target }} - - name: Run ${{ matrix.test-target }} for XX seconds + fuzz/corpus/${{ matrix.test-target.name }} + - name: Run ${{ matrix.test-target.name }} for XX seconds shell: bash + continue-on-error: ${{ !matrix.test-target.name.should_pass }} run: | - cargo +nightly fuzz run ${{ matrix.test-target }} -- -max_total_time=${{ env.RUN_FOR }} -detect_leaks=0 + cargo +nightly fuzz run ${{ matrix.test-target.name }} -- -max_total_time=${{ env.RUN_FOR }} -detect_leaks=0 - name: Save Corpus Cache uses: actions/cache/save@v3 with: - key: corpus-cache-${{ matrix.test-target }} + key: corpus-cache-${{ matrix.test-target.name }} path: | - fuzz/corpus/${{ matrix.test-target }} + fuzz/corpus/${{ matrix.test-target.name }} From 131c310bcbd0c437f55ca0b45677b3d66be8c4ce Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Tue, 21 Nov 2023 08:49:19 +0100 Subject: [PATCH 2/3] Cargo.toml: default_features -> default-features --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 2f3af2c83..ba701b2d5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -330,7 +330,7 @@ walkdir = "2.4" winapi-util = "0.1.6" windows-sys = { version = "0.48.0", default-features = false } xattr = "1.0.1" -zip = { version = "0.6.6", default_features = false, features = ["deflate"] } +zip = { version = "0.6.6", default-features = false, features = ["deflate"] } hex = "0.4.3" md-5 = "0.10.6" From a0ac3dd22975ceadfb42498d62b216cc264f0a86 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Tue, 21 Nov 2023 12:38:12 +0100 Subject: [PATCH 3/3] fuzz printf (#5556) Co-authored-by: Daniel Hofstetter --- .github/workflows/fuzzing.yml | 1 + fuzz/Cargo.toml | 7 ++ fuzz/fuzz_targets/fuzz_printf.rs | 110 +++++++++++++++++++++++++++++++ 3 files changed, 118 insertions(+) create mode 100644 fuzz/fuzz_targets/fuzz_printf.rs diff --git a/.github/workflows/fuzzing.yml b/.github/workflows/fuzzing.yml index cbb0574de..2274f6905 100644 --- a/.github/workflows/fuzzing.yml +++ b/.github/workflows/fuzzing.yml @@ -41,6 +41,7 @@ jobs: # https://github.com/uutils/coreutils/issues/5311 - { name: fuzz_date, should_pass: false } - { name: fuzz_expr, should_pass: true } + - { name: fuzz_printf, 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/Cargo.toml b/fuzz/Cargo.toml index 549f9a6b7..630af4650 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -16,6 +16,7 @@ uucore = { path = "../src/uucore/" } uu_date = { path = "../src/uu/date/" } uu_test = { path = "../src/uu/test/" } uu_expr = { path = "../src/uu/expr/" } +uu_printf = { path = "../src/uu/printf/" } # Prevent this from interfering with workspaces @@ -28,6 +29,12 @@ path = "fuzz_targets/fuzz_date.rs" test = false doc = false +[[bin]] +name = "fuzz_printf" +path = "fuzz_targets/fuzz_printf.rs" +test = false +doc = false + [[bin]] name = "fuzz_expr" path = "fuzz_targets/fuzz_expr.rs" diff --git a/fuzz/fuzz_targets/fuzz_printf.rs b/fuzz/fuzz_targets/fuzz_printf.rs new file mode 100644 index 000000000..78bb3e3ce --- /dev/null +++ b/fuzz/fuzz_targets/fuzz_printf.rs @@ -0,0 +1,110 @@ +// This file is part of the uutils coreutils package. +// +// For the full copyright and license information, please view the LICENSE +// file that was distributed with this source code. +// spell-checker:ignore parens + +#![no_main] +use libfuzzer_sys::fuzz_target; +use uu_printf::uumain; + +use rand::seq::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 = "printf"; + +fn generate_escape_sequence(rng: &mut impl Rng) -> String { + let escape_sequences = [ + "\\\"", + "\\\\", + "\\a", + "\\b", + "\\c", + "\\e", + "\\f", + "\\n", + "\\r", + "\\t", + "\\v", + "\\000", + "\\x00", + "\\u0000", + "\\U00000000", + "%%", + ]; + escape_sequences.choose(rng).unwrap().to_string() +} + +fn generate_printf() -> String { + let mut rng = rand::thread_rng(); + let format_specifiers = ["%s", "%d", "%f", "%x", "%o", "%c", "%b", "%q"]; + let mut printf_str = String::new(); + // Add a 20% chance of generating an invalid format specifier + if rng.gen_bool(0.2) { + printf_str.push_str("%z"); // Invalid format specifier + } else { + let specifier = *format_specifiers.choose(&mut rng).unwrap(); + printf_str.push_str(specifier); + + // Add a 20% chance of introducing complex format strings + if rng.gen_bool(0.2) { + printf_str.push_str(&format!(" %{}", rng.gen_range(1..=1000))); + } else { + // Add a random string or number after the specifier + if specifier == "%s" { + printf_str.push_str(&format!( + " {}", + generate_random_string(rng.gen_range(1..=10)) + )); + } else { + printf_str.push_str(&format!(" {}", rng.gen_range(1..=1000))); + } + } + } + + // Add a 10% chance of including an escape sequence + if rng.gen_bool(0.1) { + printf_str.push_str(&generate_escape_sequence(&mut rng)); + } + printf_str +} + +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 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( + "printf", + &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 + ); +});