Skip to main content

bcore_mutation/operators/
bitcoin_core.rs

1//! Bitcoin Core operator set.
2//!
3//! These lists are kept verbatim from the original `operators.rs` so the
4//! refactor introduces no behavioural change for Bitcoin Core. The operator
5//! order is intentionally tuned (boundary mutations first, etc.). Over time the
6//! generic entries here could be migrated onto [`super::common`], but only with
7//! care to preserve ordering.
8
9use super::{build, MutationOperator, OperatorSet};
10
11pub struct BitcoinCore;
12
13impl OperatorSet for BitcoinCore {
14    fn regex_operators(&self) -> Result<Vec<MutationOperator>, regex::Error> {
15        let operators = vec![
16            (r"--(\b\w+\b)", r"++$1"),
17            (r"(\b\w+\b)--", r"$1++"),
18            //(r"CAmount\s+(\w+)\s*=\s*([0-9]+)", r"CAmount $1 = $2 + 1"),
19            //(r"CAmount\s+(\w+)\s*=\s*([0-9]+)", r"CAmount $1 = $2 - 1"),
20            ("Misbehaving", "//Misbehaving"),
21            ("continue", "break"),
22            ("break", "continue"),
23            ("std::all_of", "std::any_of"),
24            ("std::any_of", "std::all_of"),
25            ("std::min", "std::max"),
26            ("std::max", "std::min"),
27            ("std::begin", "std::end"),
28            ("std::end", "std::begin"),
29            ("true", "false"),
30            ("false", "true"),
31            (r" / ", " * "),
32            // Boundary (off-by-one) mutations first — hardest to kill
33            (r" >= ", " > "),
34            (r" <= ", " < "),
35            (r" > ", " >= "),
36            (r" < ", " <= "),
37            // Direction flips — easier to detect
38            (r" >= ", " <= "),
39            (r" <= ", " >= "),
40            (r" > ", " < "),
41            (r" < ", " > "),
42            // Cross-boundary
43            (r" > ", " <= "),
44            (r" < ", " >= "),
45            (r"&&", "||"),
46            (r"\|\|", "&&"),
47            (r" == ", " != "),
48            (r" != ", " == "),
49            (" - ", " + "),
50            (r" \+ ", " - "),
51            (r" \+ ", " * "),
52            (r" \+ ", " / "),
53            (r"\((-?\d+)\)", r"($1 - 1)"),
54            (r"\((-?\d+)\)", r"($1 + 1)"),
55            (r"\b(if|else\s+if|while)\s*\(([^()]*)\)", r"$1 (1==1)"),
56            (r"\b(if|else\s+if|while)\s*\(([^()]*)\)", r"$1 (1==0)"),
57            (r".*\berase\(.+", ""),
58            (
59                r"^\s*[a-zA-Z_]\w*(?:::[a-zA-Z_]\w*)*(?:(?:->|\.)[a-zA-Z_]\w*)*\s*\([^;]*\)\s*;$",
60                "",
61            ),
62            (r"^.*if\s*\(.*\)\s*continue;.*$", ""),
63            (r"^.*if\s*\(.*\)\s*return;.*$", ""),
64            (r"^.*if\s*\(.*\)\s*return.*;.*$", ""),
65            (r"^(.*for\s*\(.*;.*;.*\)\s*\{.*)$", r"$1break;"),
66            (r"^(.*while\s*\(.*\)\s*\{.*)$", r"$1break;"),
67            /* Seems they're unproductive
68            (
69                r"\b(int64_t|uint64_t|int32_t|uint32_t)\s+(\w+)\s*=\s*(.*?);$",
70                r"$1 $2 = ($3) + 1;",
71            ),
72            (
73                r"\b(int64_t|uint64_t|int32_t|uint32_t)\s+(\w+)\s*=\s*(.*?);$",
74                r"$1 $2 = ($3) - 1;",
75            ),
76            (
77                r"static\s+const\s+size_t\s+(\w+)\s*=\s*([^;]+);",
78                r"static const size_t $1 = $2 - 1;",
79            ),
80            (
81                r"static\s+const\s+size_t\s+(\w+)\s*=\s*([^;]+);",
82                r"static const size_t $1 = $2 + 1;",
83            ),
84            //(r"NodeClock::now\(\)", r"NodeClock::now() - 1"),
85            //(r"NodeClock::now\(\)", r"NodeClock::now() + 1"),*/
86        ];
87
88        build(operators)
89    }
90
91    fn security_operators(&self) -> Result<Vec<MutationOperator>, regex::Error> {
92        let operators = vec![
93            ("==", "="),
94            (r" - ", " + "),
95            (r"\s\+\s", "-"),
96            (
97                r"std::array<\s*([\w:]+)\s*,\s*(\d+)\s*>",
98                r"std::array<$1, $2 - 2>",
99            ),
100            (
101                r"\b((?:int16_t|uint16_t|int32_t|uint32_t|int64_t|uint64_t|int)\s*[\(\{])([^\)\}]*)[\)\}]",
102                "$2",
103            ),
104            (r"ignore\((\s*(\d+)\s*)\)", r"ignore($2 + 100)"),
105            (r"(\w+)\[(\w+)\]", r"$1[$2 + 5]"),
106            (
107                r"^\s*(?:\(void\)\s*)?[a-zA-Z_][\w:]*\s*\([\w\s,]*\)\s*;\s*$",
108                "",
109            ),
110            (r"if\s*\(\s*(.*?)\s*\|\|\s*(.*?)\s*\)", r"if($2||$1)"),
111            (
112                r"GetSelectionAmount\(\)",
113                r"GetSelectionAmount() + std::numeric_limits<CAmount>::max() - 1",
114            ),
115            (r"resetBlock\(\);", ""),
116            (
117                r"\w+(\.|->)GetMedianTimePast\(\)",
118                "std::numeric_limits<int64_t>::max()",
119            ),
120            ("break", ""),
121        ];
122
123        build(operators)
124    }
125
126    fn test_operators(&self) -> 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        build(operators)
134    }
135
136    fn do_not_mutate_patterns(&self) -> Vec<&'static str> {
137        vec![
138            "/",
139            "//",
140            "#",
141            "*",
142            "assert",
143            "self.log",
144            "Assume",
145            "CHECK_NONFATAL",
146            "/*",
147            "LogPrintf",
148            "LogPrint",
149            "LogDebug",
150            "strprintf",
151            "G_FUZZING",
152            // no-op for FindAndDelete
153            "if (nFound > 0)",
154        ]
155    }
156
157    fn do_not_mutate_py_patterns(&self) -> Vec<&'static str> {
158        vec![
159            "wait_for",
160            "wait_until",
161            "check_",
162            "for",
163            "expected_error",
164            "def",
165            "send_and_ping",
166            "test_",
167            "rehash",
168            "start_",
169            "solve()",
170            "restart_",
171            "stop_",
172            "connect_",
173            "sync_",
174            "class",
175            "return",
176            "generate(",
177            "continue",
178            "sleep",
179            "break",
180            "getcontext().prec",
181            "if",
182            "else",
183            "assert",
184        ]
185    }
186
187    fn do_not_mutate_unit_patterns(&self) -> Vec<&'static str> {
188        vec![
189            "while",
190            "for",
191            "if",
192            "test_",
193            "_test",
194            "reset",
195            "class",
196            "return",
197            "continue",
198            "break",
199            "else",
200            "reserve",
201            "resize",
202            "static",
203            "void",
204            "BOOST_",
205            "LOCK(",
206            "LOCK2(",
207            "Test",
208            "Assert",
209            "EXCLUSIVE_LOCKS_REQUIRED",
210            "catch",
211        ]
212    }
213
214    fn skip_if_contain_patterns(&self) -> Vec<&'static str> {
215        vec!["EnableFuzzDeterminism", "nLostUnk", "RPCArg::Type::"]
216    }
217
218    fn test_line_skip_prefixes(&self) -> Vec<&'static str> {
219        vec![
220            "assert",
221            "BOOST_",
222            "EXPECT_",
223            "ASSERT_",
224            "CHECK_",
225            "REQUIRE_",
226            "wait_for",
227            "wait_until",
228            "send_and_ping",
229        ]
230    }
231}