|
| 1 | +# uufuzz |
| 2 | + |
| 3 | +A Rust library for **differential fuzzing** of command-line utilities. Originally designed for testing uutils coreutils against GNU coreutils, but can be used to compare any two implementations of command-line tools. |
| 4 | + |
| 5 | +Differential fuzzing is a testing technique that compares the behavior of two implementations of the same functionality using randomly generated inputs. This helps identify bugs, inconsistencies, and security vulnerabilities by finding cases where implementations diverge unexpectedly. |
| 6 | + |
| 7 | +## Features |
| 8 | + |
| 9 | +- **Command Execution**: Run and capture output from both Rust and reference implementations |
| 10 | +- **Result Comparison**: Detailed comparison of stdout, stderr, and exit codes with diff output |
| 11 | +- **Input Generation**: Utilities for generating random strings, files, and test inputs |
| 12 | +- **GNU Compatibility**: Built-in support for detecting and running GNU coreutils |
| 13 | +- **Pretty Output**: Colorized and formatted test result display |
| 14 | + |
| 15 | +## Usage |
| 16 | + |
| 17 | +Add to your `Cargo.toml`: |
| 18 | + |
| 19 | +```toml |
| 20 | +[dependencies] |
| 21 | +uufuzz = "0.1.0" |
| 22 | +``` |
| 23 | + |
| 24 | +### Basic Example |
| 25 | + |
| 26 | +```rust |
| 27 | +use std::ffi::OsString; |
| 28 | +use uufuzz::{generate_and_run_uumain, run_gnu_cmd, compare_result}; |
| 29 | + |
| 30 | +// Your utility's main function |
| 31 | +fn my_echo_main(args: std::vec::IntoIter<OsString>) -> i32 { |
| 32 | + // Implementation here |
| 33 | + 0 |
| 34 | +} |
| 35 | + |
| 36 | +// Test against GNU implementation |
| 37 | +let args = vec![OsString::from("echo"), OsString::from("hello")]; |
| 38 | + |
| 39 | +// Run your implementation |
| 40 | +let rust_result = generate_and_run_uumain(&args, my_echo_main, None); |
| 41 | + |
| 42 | +// Run GNU implementation |
| 43 | +let gnu_result = run_gnu_cmd("echo", &args[1..], false, None).unwrap(); |
| 44 | + |
| 45 | +// Compare results |
| 46 | +compare_result("echo", "hello", None, &rust_result, &gnu_result, true); |
| 47 | +``` |
| 48 | + |
| 49 | +### With Pipe Input |
| 50 | + |
| 51 | +```rust |
| 52 | +let pipe_input = "test data"; |
| 53 | +let rust_result = generate_and_run_uumain(&args, my_cat_main, Some(pipe_input)); |
| 54 | +let gnu_result = run_gnu_cmd("cat", &args[1..], false, Some(pipe_input)).unwrap(); |
| 55 | +compare_result("cat", "", Some(pipe_input), &rust_result, &gnu_result, true); |
| 56 | +``` |
| 57 | + |
| 58 | +### Random Input Generation |
| 59 | + |
| 60 | +```rust |
| 61 | +use uufuzz::{generate_random_string, generate_random_file}; |
| 62 | + |
| 63 | +// Generate random string up to 50 characters |
| 64 | +let random_input = generate_random_string(50); |
| 65 | + |
| 66 | +// Generate random temporary file |
| 67 | +let file_path = generate_random_file().expect("Failed to create file"); |
| 68 | +``` |
| 69 | + |
| 70 | +## Use Cases |
| 71 | + |
| 72 | +### Fuzzing Testing |
| 73 | +Perfect for libFuzzer-based differential fuzzing: |
| 74 | + |
| 75 | +```rust |
| 76 | +#![no_main] |
| 77 | +use libfuzzer_sys::fuzz_target; |
| 78 | +use uufuzz::*; |
| 79 | + |
| 80 | +fuzz_target!(|_data: &[u8]| { |
| 81 | + let args = generate_test_args(); |
| 82 | + let rust_result = generate_and_run_uumain(&args, my_utility_main, None); |
| 83 | + let gnu_result = run_gnu_cmd("utility", &args[1..], false, None).unwrap(); |
| 84 | + compare_result("utility", &format!("{:?}", args), None, &rust_result, &gnu_result, true); |
| 85 | +}); |
| 86 | +``` |
| 87 | + |
| 88 | +### Integration Testing |
| 89 | +Use in regular test suites to verify compatibility: |
| 90 | + |
| 91 | +```rust |
| 92 | +#[test] |
| 93 | +fn test_basic_functionality() { |
| 94 | + let args = vec![OsString::from("sort"), OsString::from("-n")]; |
| 95 | + let input = "3\n1\n2\n"; |
| 96 | + |
| 97 | + let rust_result = generate_and_run_uumain(&args, sort_main, Some(input)); |
| 98 | + let gnu_result = run_gnu_cmd("sort", &args[1..], false, Some(input)).unwrap(); |
| 99 | + |
| 100 | + assert_eq!(rust_result.stdout, gnu_result.stdout); |
| 101 | + assert_eq!(rust_result.exit_code, gnu_result.exit_code); |
| 102 | +} |
| 103 | +``` |
| 104 | + |
| 105 | +## Environment Variables |
| 106 | + |
| 107 | +- `LC_ALL=C` - Automatically set when running GNU commands for consistent behavior |
| 108 | + |
| 109 | +## Platform Support |
| 110 | + |
| 111 | +- **Linux**: Full support with GNU coreutils |
| 112 | +- **macOS**: Works with GNU coreutils via Homebrew (`brew install coreutils`) |
| 113 | +- **Windows**: Limited support (depends on available reference implementations) |
| 114 | + |
| 115 | +## Examples |
| 116 | + |
| 117 | +The library includes several working examples in the `examples/` directory: |
| 118 | + |
| 119 | +### Running Examples |
| 120 | + |
| 121 | +```bash |
| 122 | +# Basic differential comparison |
| 123 | +cargo run --example basic_echo |
| 124 | + |
| 125 | +# Pipe input handling |
| 126 | +cargo run --example pipe_input |
| 127 | + |
| 128 | +# Simple integration testing (recommended approach) |
| 129 | +cargo run --example simple_integration |
| 130 | + |
| 131 | +# Complex integration testing (demonstrates file descriptor handling issues) |
| 132 | +cargo run --example integration_testing |
| 133 | +``` |
| 134 | + |
| 135 | +## License |
| 136 | + |
| 137 | +Licensed under the MIT License, same as uutils coreutils. |
0 commit comments