bcore_mutation/
operators.rs

1use regex::Regex;
2#[derive(Debug, Clone)]
3pub struct MutationOperator {
4    pub pattern: Regex,
5    pub replacement: String,
6}
7
8impl MutationOperator {
9    pub fn new(pattern: &str, replacement: &str) -> Result<Self, regex::Error> {
10        Ok(MutationOperator {
11            pattern: Regex::new(pattern)?,
12            replacement: replacement.to_string(),
13        })
14    }
15}
16
17pub fn get_regex_operators() -> Result<Vec<MutationOperator>, regex::Error> {
18    let operators = vec![
19        (r"--(\b\w+\b)", r"++$1"),
20        (r"(\b\w+\b)--", r"$1++"),
21        //(r"CAmount\s+(\w+)\s*=\s*([0-9]+)", r"CAmount $1 = $2 + 1"),
22        //(r"CAmount\s+(\w+)\s*=\s*([0-9]+)", r"CAmount $1 = $2 - 1"),
23        ("continue", "break"),
24        ("break", "continue"),
25        ("std::all_of", "std::any_of"),
26        ("std::any_of", "std::all_of"),
27        ("std::min", "std::max"),
28        ("std::max", "std::min"),
29        ("std::begin", "std::end"),
30        ("std::end", "std::begin"),
31        ("true", "false"),
32        ("false", "true"),
33        (r" / ", " * "),
34        (r" > ", " < "),
35        (r" > ", " >= "),
36        (r" > ", " <= "),
37        (r" < ", " > "),
38        (r" < ", " <= "),
39        (r" < ", " >= "),
40        (r" >= ", " <= "),
41        (r" >= ", " > "),
42        (r"&&", "||"),
43        (r"\|\|", "&&"),
44        (r" == ", " != "),
45        (r" != ", " == "),
46        (" - ", " + "),
47        (r" \+ ", " - "),
48        (r" \+ ", " * "),
49        (r" \+ ", " / "),
50        (r"\((-?\d+)\)", r"($1 - 1)"),
51        (r"\((-?\d+)\)", r"($1 + 1)"),
52        (r"\b(if|else\s+if|while)\s*\(([^()]*)\)", r"$1 (1==1)"),
53        (r"\b(if|else\s+if|while)\s*\(([^()]*)\)", r"$1 (1==0)"),
54        (r".*\berase\(.+", ""),
55        (r"^.*if\s*\(.*\)\s*continue;.*$", ""),
56        (r"^.*if\s*\(.*\)\s*return;.*$", ""),
57        (r"^.*if\s*\(.*\)\s*return.*;.*$", ""),
58        (r"^(.*for\s*\(.*;.*;.*\)\s*\{.*)$", r"$1break;"),
59        (r"^(.*while\s*\(.*\)\s*\{.*)$", r"$1break;"),
60        /* Seems they're unproductive
61        (
62            r"\b(int64_t|uint64_t|int32_t|uint32_t)\s+(\w+)\s*=\s*(.*?);$",
63            r"$1 $2 = ($3) + 1;",
64        ),
65        (
66            r"\b(int64_t|uint64_t|int32_t|uint32_t)\s+(\w+)\s*=\s*(.*?);$",
67            r"$1 $2 = ($3) - 1;",
68        ),
69        (
70            r"static\s+const\s+size_t\s+(\w+)\s*=\s*([^;]+);",
71            r"static const size_t $1 = $2 - 1;",
72        ),
73        (
74            r"static\s+const\s+size_t\s+(\w+)\s*=\s*([^;]+);",
75            r"static const size_t $1 = $2 + 1;",
76        ),
77        //(r"NodeClock::now\(\)", r"NodeClock::now() - 1"),
78        //(r"NodeClock::now\(\)", r"NodeClock::now() + 1"),*/
79    ];
80
81    operators
82        .into_iter()
83        .map(|(pattern, replacement)| MutationOperator::new(pattern, replacement))
84        .collect()
85}
86
87pub fn get_security_operators() -> Result<Vec<MutationOperator>, regex::Error> {
88    let operators = vec![
89        ("==", "="),
90        (r" - ", " + "),
91        (r"\s\+\s", "-"),
92        (
93            r"std::array<\s*([\w:]+)\s*,\s*(\d+)\s*>",
94            r"std::array<$1, $2 - 2>",
95        ),
96        (
97            r"\b((?:int16_t|uint16_t|int32_t|uint32_t|int64_t|uint64_t|int)\s*[\(\{])([^\)\}]*)[\)\}]",
98            "$2",
99        ),
100        (r"ignore\((\s*(\d+)\s*)\)", r"ignore($2 + 100)"),
101        (r"(\w+)\[(\w+)\]", r"$1[$2 + 5]"),
102        (
103            r"^\s*(?:\(void\)\s*)?[a-zA-Z_][\w:]*\s*\([\w\s,]*\)\s*;\s*$",
104            "",
105        ),
106        (r"if\s*\(\s*(.*?)\s*\|\|\s*(.*?)\s*\)", r"if($2||$1)"),
107        (
108            r"GetSelectionAmount\(\)",
109            r"GetSelectionAmount() + std::numeric_limits<CAmount>::max() - 1",
110        ),
111        (r"resetBlock\(\);", ""),
112        (
113            r"\w+(\.|->)GetMedianTimePast\(\)",
114            "std::numeric_limits<int64_t>::max()",
115        ),
116        ("break", ""),
117    ];
118
119    operators
120        .into_iter()
121        .map(|(pattern, replacement)| MutationOperator::new(pattern, replacement))
122        .collect()
123}
124
125pub fn get_test_operators() -> Result<Vec<MutationOperator>, regex::Error> {
126    // Instead of using negative lookahead, we'll use a simpler approach
127    // This will match function calls but we'll filter out assert functions in the application logic
128    let operators = vec![
129        (r"^\s*(?:\w+(?:\.|->|::))*(\w+)\s*\([^)]*\)\s*;?\s*$", ""), // Function calls (will be filtered by skip logic)
130    ];
131
132    operators
133        .into_iter()
134        .map(|(pattern, replacement)| MutationOperator::new(pattern, replacement))
135        .collect()
136}
137
138pub fn get_do_not_mutate_patterns() -> Vec<&'static str> {
139    vec![
140        "/",
141        "//",
142        "#",
143        "*",
144        "assert",
145        "self.log",
146        "Assume",
147        "CHECK_NONFATAL",
148        "/*",
149        "LogPrintf",
150        "LogPrint",
151        "strprintf",
152        "G_FUZZING",
153    ]
154}
155
156pub fn get_do_not_mutate_py_patterns() -> Vec<&'static str> {
157    vec![
158        "wait_for",
159        "wait_until",
160        "check_",
161        "for",
162        "expected_error",
163        "def",
164        "send_and_ping",
165        "test_",
166        "rehash",
167        "start_",
168        "solve()",
169        "restart_",
170        "stop_",
171        "connect_",
172        "sync_",
173        "class",
174        "return",
175        "generate(",
176        "continue",
177        "sleep",
178        "break",
179        "getcontext().prec",
180        "if",
181        "else",
182        "assert",
183    ]
184}
185
186pub fn get_do_not_mutate_unit_patterns() -> Vec<&'static str> {
187    vec![
188        "while",
189        "for",
190        "if",
191        "test_",
192        "_test",
193        "reset",
194        "class",
195        "return",
196        "continue",
197        "break",
198        "else",
199        "reserve",
200        "resize",
201        "static",
202        "void",
203        "BOOST_",
204        "LOCK(",
205        "LOCK2(",
206        "Test",
207        "Assert",
208        "EXCLUSIVE_LOCKS_REQUIRED",
209        "catch",
210    ]
211}
212
213pub fn get_skip_if_contain_patterns() -> Vec<&'static str> {
214    vec!["EnableFuzzDeterminism", "nLostUnk", "RPCArg::Type::"]
215}
216
217// Helper function to check if a line should be mutated by test operators
218// This replaces the negative lookahead functionality
219pub fn should_mutate_test_line(line: &str) -> bool {
220    let line_trimmed = line.trim();
221
222    // Don't mutate lines that start with assert or other test-specific patterns
223    let skip_patterns = vec![
224        "assert",
225        "BOOST_",
226        "EXPECT_",
227        "ASSERT_",
228        "CHECK_",
229        "REQUIRE_",
230        "wait_for",
231        "wait_until",
232        "send_and_ping",
233    ];
234
235    for pattern in skip_patterns {
236        if line_trimmed.starts_with(pattern) {
237            return false;
238        }
239    }
240
241    // Only mutate if it looks like a function call
242    let function_call_pattern =
243        Regex::new(r"^\s*(?:\w+(?:\.|->|::))*(\w+)\s*\([^)]*\)\s*;?\s*$").unwrap();
244    function_call_pattern.is_match(line)
245}