Minimalistic Fuzzer in Rust
Fuzzing is a powerful technique for discovering bugs in software by providing it with a large number of random, often malformed, inputs. This challenge asks you to implement a basic, command-line fuzzing infrastructure in Rust that can be used to test a simple target function. The goal is to create a framework that generates random inputs and feeds them to a target function, reporting any crashes or panics.
Problem Description
You are tasked with building a rudimentary fuzzer in Rust. The fuzzer will take a target function as input (defined as a closure) and repeatedly generate random byte arrays of varying lengths. These byte arrays will be passed to the target function. The fuzzer should monitor the target function for panics (Rust's equivalent of exceptions) and report any panics encountered. The fuzzer should run for a specified number of iterations.
What needs to be achieved:
- Input Generation: Generate random byte arrays of lengths between 1 and 256 bytes.
- Target Function Execution: Pass each generated byte array to the provided target function.
- Panic Detection: Detect and report any panics that occur during the execution of the target function.
- Iteration Control: Run the fuzzing process for a specified number of iterations.
- Command-Line Arguments: Accept the number of iterations as a command-line argument.
Key Requirements:
- The target function must accept a
&[u8](byte slice) as input. - The fuzzer should handle panics gracefully and continue fuzzing.
- The fuzzer should print a message indicating a panic occurred, along with the iteration number.
- The fuzzer should use the
randcrate for random number generation.
Expected Behavior:
The fuzzer should run for the specified number of iterations, generating random byte arrays and feeding them to the target function. If the target function panics, the fuzzer should print a message indicating the panic and the iteration number. If the target function completes without panicking for all iterations, the fuzzer should print a success message.
Edge Cases to Consider:
- Very short input arrays (1 byte).
- Very long input arrays (256 bytes).
- Target functions that might take a long time to execute (though this challenge focuses on panic detection, not performance).
- Target functions that might return early.
Examples
Example 1:
Input: Target function: `|data: &[u8]| { if data.len() > 10 { panic!("Too long!"); } }`, Iterations: 100
Output: (After 100 iterations, potentially with panics)
Panic detected at iteration 42
Panic detected at iteration 78
Fuzzing complete. 2 panics detected.
Explanation: The target function panics when the input data is longer than 10 bytes. The fuzzer generates random byte arrays and passes them to the function. When the length exceeds 10, a panic is detected and reported.
Example 2:
Input: Target function: `|data: &[u8]| { println!("Received: {:?}", data.len()); }`, Iterations: 50
Output: (After 50 iterations)
Received: 123
Received: 5
Received: 200
...
Fuzzing complete. 0 panics detected.
Explanation: The target function simply prints the length of the received data. No panics occur, so the fuzzer continues and reports 0 panics at the end.
Example 3: (Edge Case)
Input: Target function: `|data: &[u8]| { if data.len() == 0 { panic!("Empty input!"); } }`, Iterations: 20
Output: (After 20 iterations, potentially with panics)
Panic detected at iteration 5
Panic detected at iteration 18
Fuzzing complete. 2 panics detected.
Explanation: The target function panics if the input is empty. The fuzzer generates random byte arrays, and occasionally an empty array will be generated, triggering the panic.
Constraints
- Iteration Count: The number of iterations must be a positive integer provided as a command-line argument.
- Input Length: Random input lengths should be between 1 and 256 (inclusive).
- Dependencies: You are allowed to use the
randcrate. No other external crates are permitted. - Performance: While not the primary focus, avoid excessively inefficient code. The fuzzer should be able to run for 1000 iterations within a reasonable time (e.g., under 10 seconds) on a standard laptop.
- Error Handling: Handle potential errors when parsing the command-line argument gracefully.
Notes
- Consider using
std::process::exitto terminate the program if the command-line argument parsing fails. - The
panic!macro is your primary tool for detecting errors in the target function. - The
rand::random()function can be used to generate randomu8values. - Think about how to efficiently generate random byte arrays of varying lengths.
Vec::with_capacitycan be helpful. - This is a minimalistic fuzzer. It does not include advanced features like input mutation or coverage guidance. The focus is on the core mechanics of generating random inputs and detecting panics.
- Use
cargo new --bin fuzzerto create a new Rust project. Addrand = "0.8"to yourCargo.tomlfile.