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