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