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