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|for)\s*\(([^()]*)\)", r"$1 (1==1)"),
53        (r"\b(if|else\s+if|while|for)\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        "assert",
144        "self.log",
145        "Assume",
146        "CHECK_NONFATAL",
147        "/*",
148        "LogPrintf",
149        "LogPrint",
150        "strprintf",
151        "G_FUZZING",
152    ]
153}
154
155pub fn get_do_not_mutate_py_patterns() -> Vec<&'static str> {
156    vec![
157        "wait_for",
158        "wait_until",
159        "check_",
160        "for",
161        "expected_error",
162        "def",
163        "send_and_ping",
164        "test_",
165        "rehash",
166        "start_",
167        "solve()",
168        "restart_",
169        "stop_",
170        "connect_",
171        "sync_",
172        "class",
173        "return",
174        "generate(",
175        "continue",
176        "sleep",
177        "break",
178        "getcontext().prec",
179        "if",
180        "else",
181        "assert",
182    ]
183}
184
185pub fn get_do_not_mutate_unit_patterns() -> Vec<&'static str> {
186    vec![
187        "while",
188        "for",
189        "if",
190        "test_",
191        "_test",
192        "reset",
193        "class",
194        "return",
195        "continue",
196        "break",
197        "else",
198        "reserve",
199        "resize",
200        "static",
201        "void",
202        "BOOST_",
203        "LOCK(",
204        "LOCK2(",
205        "Test",
206        "Assert",
207        "EXCLUSIVE_LOCKS_REQUIRED",
208        "catch",
209    ]
210}
211
212pub fn get_skip_if_contain_patterns() -> Vec<&'static str> {
213    vec!["EnableFuzzDeterminism", "nLostUnk", "RPCArg::Type::"]
214}
215
216// Helper function to check if a line should be mutated by test operators
217// This replaces the negative lookahead functionality
218pub fn should_mutate_test_line(line: &str) -> bool {
219    let line_trimmed = line.trim();
220
221    // Don't mutate lines that start with assert or other test-specific patterns
222    let skip_patterns = vec![
223        "assert",
224        "BOOST_",
225        "EXPECT_",
226        "ASSERT_",
227        "CHECK_",
228        "REQUIRE_",
229        "wait_for",
230        "wait_until",
231        "send_and_ping",
232    ];
233
234    for pattern in skip_patterns {
235        if line_trimmed.starts_with(pattern) {
236            return false;
237        }
238    }
239
240    // Only mutate if it looks like a function call
241    let function_call_pattern =
242        Regex::new(r"^\s*(?:\w+(?:\.|->|::))*(\w+)\s*\([^)]*\)\s*;?\s*$").unwrap();
243    function_call_pattern.is_match(line)
244}