Coverage Report

Created: 2026-09-28 07:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
});