Files

97 lines
3.1 KiB
Rust
Raw Permalink Normal View History

2023-03-20 20:55:45 +01:00
// 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_expr::uumain;
2026-03-13 10:47:43 +01:00
use rand::RngExt;
2025-04-08 17:30:43 +02:00
use rand::prelude::IndexedRandom;
2023-10-18 10:11:13 +02:00
use std::{env, ffi::OsString};
2023-03-20 20:55:45 +01:00
use uufuzz::CommandResult;
use uufuzz::{compare_result, generate_and_run_uumain, generate_random_string, run_gnu_cmd};
2023-03-20 20:55:45 +01:00
static CMD_PATH: &str = "expr";
fn generate_expr(max_depth: u32) -> String {
2025-01-30 10:57:17 +01:00
let mut rng = rand::rng();
let ops = [
2024-04-01 00:56:22 +02:00
"+", "-", "*", "/", "%", "<", ">", "=", "&", "|", "!=", "<=", ">=", ":", "index", "length",
"substr",
];
2023-03-20 20:55:45 +01:00
let mut expr = String::new();
let mut depth = 0;
let mut last_was_operator = false;
while depth <= max_depth {
if last_was_operator || depth == 0 {
// Add a number
2025-01-30 10:57:17 +01:00
expr.push_str(&rng.random_range(1..=100).to_string());
2023-03-20 20:55:45 +01:00
last_was_operator = false;
} else {
// 90% chance to add an operator followed by a number
2025-01-30 10:57:17 +01:00
if rng.random_bool(0.9) {
2023-03-20 20:55:45 +01:00
let op = *ops.choose(&mut rng).unwrap();
2025-04-07 22:56:21 -04:00
expr.push_str(&format!(" {op} "));
2023-03-20 20:55:45 +01:00
last_was_operator = true;
}
// 10% chance to add a random string (potentially invalid syntax)
else {
2025-01-30 10:57:17 +01:00
let random_str = generate_random_string(rng.random_range(1..=10));
2023-03-20 20:55:45 +01:00
expr.push_str(&random_str);
last_was_operator = false;
}
}
depth += 1;
}
// Ensure the expression ends with a number if it ended with an operator
if last_was_operator {
2025-01-30 10:57:17 +01:00
expr.push_str(&rng.random_range(1..=100).to_string());
2023-03-20 20:55:45 +01:00
}
expr
}
fuzz_target!(|_data: &[u8]| {
2025-01-30 10:57:17 +01:00
let mut rng = rand::rng();
let expr = generate_expr(rng.random_range(0..=20));
2023-03-20 20:55:45 +01:00
let mut args = vec![OsString::from("expr")];
args.extend(expr.split_whitespace().map(OsString::from));
2023-10-18 10:11:13 +02:00
// Use C locale to avoid false positives, like in https://github.com/uutils/coreutils/issues/5378,
// because uutils expr doesn't support localization yet
// TODO remove once uutils expr supports localization
2025-03-08 15:36:51 +01:00
unsafe {
env::set_var("LC_COLLATE", "C");
}
let rust_result = generate_and_run_uumain(&args, uumain, None);
2023-10-18 10:11:13 +02:00
let gnu_result = match run_gnu_cmd(CMD_PATH, &args[1..], false, None) {
2023-11-10 16:01:38 +01:00
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,
}
}
};
2023-11-08 23:31:41 +01:00
2023-11-09 00:04:14 +01:00
compare_result(
"expr",
&format!("{:?}", &args[1..]),
None,
2023-12-17 17:57:21 +01:00
&rust_result,
&gnu_result,
2023-11-09 00:04:14 +01:00
false, // Set to true if you want to fail on stderr diff
);
2023-03-20 20:55:45 +01:00
});