1use serde::{Deserialize, Serialize};
4use std::collections::BTreeMap;
5
6#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
8#[serde(rename_all = "snake_case")]
9pub enum SymbolicStorageLayout {
10 #[default]
12 Solidity,
13 Generic,
15 ZeroInit,
17}
18
19#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
21#[serde(rename_all = "snake_case")]
22pub enum SymbolicExplorationOrder {
23 #[default]
25 Bfs,
26 Dfs,
28}
29
30#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
32pub struct SymbolicConfig {
33 pub enabled: bool,
35 pub seed_corpus: bool,
37 pub use_fuzz_corpus: bool,
39 pub corpus_seed_limit: usize,
41 pub use_fuzz_frontiers: bool,
43 pub frontier_limit: usize,
45 #[serde(default, skip_serializing_if = "Vec::is_empty")]
47 pub frontier_ids: Vec<u64>,
48 #[serde(default, skip_serializing_if = "Vec::is_empty")]
50 pub frontier_pcs: Vec<usize>,
51 #[serde(default, skip_serializing_if = "Vec::is_empty")]
53 pub frontier_selectors: Vec<String>,
54 pub solver: String,
56 #[serde(default, skip_serializing_if = "Option::is_none")]
58 pub solver_command: Option<String>,
59 #[serde(default, skip_serializing_if = "Vec::is_empty")]
61 pub solver_portfolio: Vec<String>,
62 pub timeout: Option<u32>,
65 #[serde(default, rename = "loop", skip_serializing_if = "Option::is_none")]
67 pub loop_bound: Option<u32>,
68 #[serde(default, skip_serializing_if = "Option::is_none")]
70 pub depth: Option<u32>,
71 #[serde(default, skip_serializing_if = "Option::is_none")]
73 pub width: Option<u32>,
74 pub max_depth: u32,
76 pub max_paths: u32,
78 pub invariant_depth: u32,
80 #[serde(default)]
82 pub exploration_order: SymbolicExplorationOrder,
83 pub max_solver_queries: u32,
85 pub default_dynamic_length: u32,
87 pub max_dynamic_length: u32,
89 pub array_lengths: Vec<u32>,
91 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
93 pub dynamic_lengths: BTreeMap<String, Vec<u32>>,
94 #[serde(default, skip_serializing_if = "Vec::is_empty")]
96 pub default_array_lengths: Vec<u32>,
97 #[serde(default, skip_serializing_if = "Vec::is_empty")]
99 pub default_bytes_lengths: Vec<u32>,
100 pub max_calldata_bytes: u32,
102 pub symbolic_call_targets: bool,
104 pub dump_smt: bool,
106 pub storage_layout: SymbolicStorageLayout,
108}
109
110impl Default for SymbolicConfig {
111 fn default() -> Self {
112 Self {
113 enabled: false,
114 seed_corpus: false,
115 use_fuzz_corpus: false,
116 corpus_seed_limit: 32,
117 use_fuzz_frontiers: false,
118 frontier_limit: 256,
119 frontier_ids: Vec::new(),
120 frontier_pcs: Vec::new(),
121 frontier_selectors: Vec::new(),
122 solver: "z3".to_string(),
123 solver_command: None,
124 solver_portfolio: Vec::new(),
125 timeout: Some(30),
126 loop_bound: None,
127 depth: None,
128 width: None,
129 max_depth: 10_000,
130 max_paths: 1_024,
131 invariant_depth: 10,
132 exploration_order: SymbolicExplorationOrder::default(),
133 max_solver_queries: 10_000,
134 default_dynamic_length: 2,
135 max_dynamic_length: 256,
136 array_lengths: Vec::new(),
137 dynamic_lengths: BTreeMap::new(),
138 default_array_lengths: Vec::new(),
139 default_bytes_lengths: Vec::new(),
140 max_calldata_bytes: 4_096,
141 symbolic_call_targets: false,
142 dump_smt: false,
143 storage_layout: SymbolicStorageLayout::Solidity,
144 }
145 }
146}
147
148impl SymbolicConfig {
149 pub fn execution_depth(&self) -> u32 {
154 self.depth.unwrap_or(self.max_depth)
155 }
156
157 pub fn path_width(&self) -> u32 {
162 self.width.unwrap_or(self.max_paths)
163 }
164}
165
166#[cfg(test)]
167mod tests {
168 use super::*;
169
170 #[test]
171 fn missing_exploration_order_defaults_to_bfs() {
172 let value = serde_json::json!({
173 "enabled": false,
174 "seed_corpus": false,
175 "use_fuzz_corpus": false,
176 "corpus_seed_limit": 32,
177 "use_fuzz_frontiers": false,
178 "frontier_limit": 256,
179 "frontier_ids": [],
180 "frontier_pcs": [],
181 "frontier_selectors": [],
182 "solver": "z3",
183 "timeout": 30,
184 "max_depth": 10000,
185 "max_paths": 1024,
186 "invariant_depth": 10,
187 "max_solver_queries": 10000,
188 "default_dynamic_length": 2,
189 "max_dynamic_length": 256,
190 "array_lengths": [],
191 "max_calldata_bytes": 4096,
192 "symbolic_call_targets": false,
193 "dump_smt": false,
194 "storage_layout": "solidity"
195 });
196
197 let config: SymbolicConfig = serde_json::from_value(value).unwrap();
198
199 assert_eq!(config.exploration_order, SymbolicExplorationOrder::Bfs);
200 }
201}