Spindle is a simple and efficient expression and byte sequence generator to aid fuzz testing parsers and de-serializers.
Usage
Spindle offers a syntax similar to Extended Backus–Naur form which compiles to a state machine --Grammar--that produces structured matching arbitrary sentences from an unstructured feed of bytes.
Spindle integrates with libfuzzer and cargo-fuzz: Unstructured bytes, from the arbitrary crate, are manipulated by the fuzzer based on code coverage.
Spindle can be used to generate database expressions, big decimal strings, JSON, and other syntaxes, as well as slightly malformed variants of correct expressions to test interesting edge cases of parser or de-serializer.
Example
use Grammar;
use Unstructured;
let math: Grammar = r#"
expr : u16 | paren | expr symbol expr ;
paren : "(" expr symbol expr ")" ;
symbol : r"-|\+|\*|÷" ;
"#.parse.unwrap;
let mut u = new;
let sentence: String = math.expression.unwrap; // (30057+(12594+((((25976+(0*0))*0*0)*0)*0)*0*0))
The state machine traversal always starts at the first rule. In the example,
expris the first rule and evaluates to eitheru16,paren, or the concatenation ofexprandsymbolandexpr.;delimits different rules.u16is a pre-defined data types that directly evaluates tou16::arbitrary(u)parenevaluates to the concatenation of the literal"(",expr,symbol,exprand,")".symbolevaluates to the an arbitrary string matching the regex-|\+|\*|÷.
Usage in Fuzzer
use Grammar;
use fuzz_target;
use ;
use LazyLock;
static GRAMMAR: = new;
;
fuzz_target!;
For more examples, see the examples folder.
Pre-defined Rules
Stringevaluates tostr::arbitrary(u)u16evaluates tou16::arbitrary(u)
Visitor
A Visitor is some state that is initialized before traversal and mutated as different rules are visited during the traversal, e.g. visit_or. Vistors that are already implemented are String and Vec<u8> for output buffers, and u64 for classification.
Users can use their own implementation of Visitor, for example if they want to
- use a different output buffer
- use a different classification
- gather data
- build an abstract syntax tree
Example
use ;
let math: Grammar = r#"
expr : u16 | paren | expr symbol expr ;
paren : "(" expr symbol expr ")" ;
symbol : r"-|\+|\*|÷" ;
"#.parse.unwrap;
/// Detects if any prime numbers were generated.
;
let mut u = new;
let : = math.expression.unwrap;
let expr: String = math.expression.unwrap;
assert!;
Example
In this example, a math specific abstract syntax tree (AST) is built during the arbitrary traversal.
use ;
let math: Grammar = r#"
expr : u16 | paren | expr symbol expr ;
paren : "(" expr symbol expr ")" ;
symbol : r"-|\+|\*|÷" ;
"#.parse.unwrap;
let mut u = new;
// MathAst { cur_op: None, stack: [Expr(Num(13108), '*', Num(0))] }
let tree: MathAst = math.expression.unwrap;