/src/nom/fuzz/fuzz_targets/fuzz_arithmetic.rs
Line | Count | Source |
1 | | #![no_main] |
2 | | use libfuzzer_sys::fuzz_target; |
3 | | use std::str; |
4 | | |
5 | | extern crate nom; |
6 | | |
7 | | use nom::{ |
8 | | branch::alt, |
9 | | bytes::complete::tag, |
10 | | character::complete::char, |
11 | | character::complete::{digit1 as digit, space0 as space}, |
12 | | combinator::{map, map_res, verify}, |
13 | | multi::fold_many0, |
14 | | sequence::{delimited, pair, terminated}, |
15 | | IResult, Parser |
16 | | }; |
17 | | |
18 | | use std::cell::RefCell; |
19 | | use std::str::FromStr; |
20 | | |
21 | | thread_local! { |
22 | | pub static LEVEL: RefCell<u32> = RefCell::new(0); |
23 | | } |
24 | | |
25 | 26 | fn reset() { |
26 | 26 | LEVEL.with(|l| { |
27 | 26 | *l.borrow_mut() = 0; |
28 | 26 | }); |
29 | 26 | } |
30 | | |
31 | 55.8k | fn incr(i: &str) -> IResult<&str, ()> { |
32 | 55.8k | LEVEL.with(|l| { |
33 | 55.8k | *l.borrow_mut() += 1; |
34 | | |
35 | | // limit the number of recursions, the fuzzer keeps running into them |
36 | 55.8k | if *l.borrow() >= 8192 { |
37 | 1 | return Err(nom::Err::Failure(nom::error::Error::new( |
38 | 1 | i, |
39 | 1 | nom::error::ErrorKind::Count, |
40 | 1 | ))); |
41 | | } else { |
42 | 55.8k | Ok((i, ())) |
43 | | } |
44 | 55.8k | }) |
45 | 55.8k | } |
46 | | |
47 | 42.3k | fn decr() { |
48 | 42.3k | LEVEL.with(|l| { |
49 | 42.3k | *l.borrow_mut() -= 1; |
50 | 42.3k | }); |
51 | 42.3k | } |
52 | | |
53 | 13.4k | fn parens(i: &str) -> IResult<&str, i64> { |
54 | 13.4k | delimited( |
55 | | space, |
56 | 13.4k | delimited(terminated(tag("("), incr), expr, map(tag(")"), |_| decr())), |
57 | | space, |
58 | 13.4k | ).parse(i) |
59 | 13.4k | } |
60 | | |
61 | 47.7k | fn factor(i: &str) -> IResult<&str, i64> { |
62 | 47.7k | alt(( |
63 | 47.7k | map_res(delimited(space, digit, space), FromStr::from_str), |
64 | 47.7k | parens, |
65 | 47.7k | )).parse(i) |
66 | 47.7k | } |
67 | | |
68 | 28.9k | fn term(i: &str) -> IResult<&str, i64> { |
69 | 28.9k | incr(i)?; |
70 | 28.9k | let (i, init) = factor(i).map_err(|e| { |
71 | 2.59k | decr(); |
72 | 2.59k | e |
73 | 2.59k | })?; |
74 | | |
75 | 26.3k | let res = fold_many0( |
76 | 26.3k | alt(( |
77 | 26.3k | pair(char('*'), factor), |
78 | 26.3k | pair(char('/'), verify(factor, |i| *i != 0)), |
79 | | )), |
80 | | || init, |
81 | 15.7k | |acc, (op, val): (char, i64)| { |
82 | 15.7k | if op == '*' { |
83 | 8.38k | acc.saturating_mul(val) |
84 | | } else { |
85 | 7.34k | match acc.checked_div(val) { |
86 | 6.85k | Some(v) => v, |
87 | | // we get a division with overflow because we can get acc = i64::MIN and val = -1 |
88 | | // the division by zero is already checked earlier by verify |
89 | 493 | None => i64::MAX, |
90 | | } |
91 | | } |
92 | 15.7k | }, |
93 | 26.3k | ).parse(i); |
94 | | |
95 | 26.3k | decr(); |
96 | 26.3k | res |
97 | 28.9k | } |
98 | | |
99 | 13.4k | fn expr(i: &str) -> IResult<&str, i64> { |
100 | 13.4k | incr(i)?; |
101 | 13.4k | let (i, init) = term(i).map_err(|e| { |
102 | 2.66k | decr(); |
103 | 2.66k | e |
104 | 2.66k | })?; |
105 | | |
106 | 10.7k | let res = fold_many0( |
107 | 10.7k | pair(alt((char('+'), char('-'))), term), |
108 | | || init, |
109 | 14.8k | |acc, (op, val): (char, i64)| { |
110 | 14.8k | if op == '+' { |
111 | 2.83k | acc.saturating_add(val) |
112 | | } else { |
113 | 11.9k | acc.saturating_sub(val) |
114 | | } |
115 | 14.8k | }, |
116 | 10.7k | ).parse(i); |
117 | | |
118 | 10.7k | decr(); |
119 | 10.7k | res |
120 | 13.4k | } |
121 | | |
122 | | fuzz_target!(|data: &[u8]| { |
123 | | reset(); |
124 | | // fuzzed code goes here |
125 | | let _ = match str::from_utf8(data) { |
126 | | Ok(v) => { |
127 | | //println!("v: {}", v); |
128 | | factor(v) |
129 | | } |
130 | | Err(_) => factor("2"), |
131 | | }; |
132 | | }); |