Skip to main content

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