trident_client/commander/
fuzz.rs1use crate::coverage::Coverage;
2use crate::coverage::NotificationType;
3use crate::utils::generate_unique_fuzz_filename;
4use fehler::throw;
5use fehler::throws;
6use std::collections::HashMap;
7use tokio::process::Command;
8use trident_config::coverage::Coverage as CoverageConfig;
9use trident_config::TridentConfig;
10
11use super::Commander;
12use super::Error;
13
14impl Commander {
15 #[throws]
16 pub async fn run(&self, target: String, with_exit_code: bool, seed: Option<String>) {
17 let config = TridentConfig::new();
18
19 if config.get_metrics() {
20 std::env::set_var("FUZZING_METRICS", "true");
21
22 if config.get_metrics_json() {
23 let json_path = generate_unique_fuzz_filename("fuzzing_metrics", &target, "json")
24 .await
25 .map_err(|e| {
26 Error::Anyhow(anyhow::anyhow!(
27 "Failed to generate fuzzing metrics path: {:?}",
28 e
29 ))
30 })?;
31 std::env::set_var("FUZZING_JSON", json_path.to_string_lossy().to_string());
32 }
33
34 if config.get_metrics_dashboard() {
35 let dashboard_path =
36 generate_unique_fuzz_filename("fuzzing_dashboard", &target, "html")
37 .await
38 .map_err(|e| {
39 Error::Anyhow(anyhow::anyhow!(
40 "Failed to generate dashboard path: {:?}",
41 e
42 ))
43 })?;
44 std::env::set_var(
45 "FUZZING_DASHBOARD",
46 dashboard_path.to_string_lossy().to_string(),
47 );
48 }
49 }
50
51 if config.get_regression() {
52 let regression_path = generate_unique_fuzz_filename("regression", &target, "json")
53 .await
54 .map_err(|e| {
55 Error::Anyhow(anyhow::anyhow!(
56 "Failed to generate regression path: {:?}",
57 e
58 ))
59 })?;
60 std::env::set_var(
61 "FUZZING_REGRESSION",
62 regression_path.to_string_lossy().to_string(),
63 );
64 }
65
66 let coverage_config = config.get_coverage();
67 if coverage_config.get_enable() {
68 self.run_with_coverage(&target, &config, coverage_config, seed, with_exit_code)
69 .await?;
70 } else {
71 self.run_default(&target, seed, with_exit_code).await?;
72 }
73 }
74
75 #[throws]
76 pub async fn run_default(&self, target: &str, seed: Option<String>, with_exit_code: bool) {
77 let mut env_vars = HashMap::new();
78 if with_exit_code {
79 env_vars.insert("TRIDENT_WITH_EXIT_CODE", "1".to_string());
80 }
81 let mut child = self.spawn_fuzzer(target, env_vars, seed)?;
82 Self::handle_child(&mut child, with_exit_code).await?;
83 }
84
85 #[throws]
86 pub async fn run_with_coverage(
87 &self,
88 target: &str,
89 config: &TridentConfig,
90 coverage_config: CoverageConfig,
91 seed: Option<String>,
92 with_exit_code: bool,
93 ) {
94 if let Err(err) = coverage_config.validate() {
95 throw!(Error::Anyhow(anyhow::anyhow!(err)));
96 }
97
98 let coverage = Coverage::new(
99 &self.get_target_dir()?,
100 target,
101 coverage_config.get_attach_extension(),
102 coverage_config.get_format(),
103 coverage_config.get_loopcount(),
104 config.coverage_server_port(),
105 );
106
107 if coverage.check_llvm_tools_installed().await.is_err() {
108 coverage.prompt_and_install_llvm_tools().await?;
109 }
110
111 coverage.clean().await?;
112
113 let mut env_vars = self.setup_coverage_env_vars(&coverage, config).await?;
114 if with_exit_code {
115 env_vars.insert("TRIDENT_WITH_EXIT_CODE", "1".to_string());
116 }
117 let mut child = self.spawn_fuzzer(target, env_vars, seed)?;
118
119 coverage.notify_extension(NotificationType::Setup).await?;
120 Self::handle_child(&mut child, with_exit_code).await?;
121
122 coverage.generate_report().await?;
123 }
124
125 #[throws]
126 async fn setup_coverage_env_vars(
127 &self,
128 coverage: &Coverage,
129 config: &TridentConfig,
130 ) -> HashMap<&str, String> {
131 let mut rustflags = std::env::var("RUSTFLAGS").unwrap_or_default();
132 rustflags.push_str(&coverage.get_rustflags());
133
134 let mut env_vars: HashMap<&str, String> = HashMap::new();
135 env_vars.insert("RUSTFLAGS", rustflags);
136 env_vars.insert("LLVM_PROFILE_FILE", coverage.get_profraw_file());
137 env_vars.insert("CARGO_LLVM_COV_TARGET_DIR", coverage.get_target_dir());
138 env_vars.insert("FUZZER_LOOPCOUNT", coverage.get_loopcount().to_string());
139 env_vars.insert(
140 "COVERAGE_SERVER_PORT",
141 config.coverage_server_port().to_string(),
142 );
143 env_vars.insert("COLLECT_COVERAGE", "1".to_string());
145
146 env_vars
147 }
148
149 #[throws]
150 fn spawn_fuzzer(
151 &self,
152 target: &str,
153 mut env_vars: HashMap<&str, String>,
154 seed: Option<String>,
155 ) -> tokio::process::Child {
156 if let Some(seed) = seed {
157 let _decoded_seed = hex::decode(&seed)
160 .unwrap_or_else(|_| panic!("The seed is not a valid hex string: {}", seed));
161
162 env_vars.insert("TRIDENT_FUZZ_SEED", seed);
163 }
164
165 Command::new("cargo")
166 .envs(env_vars)
167 .arg("run")
168 .arg("--bin")
169 .arg(target)
170 .args(["--profile", "release"])
171 .spawn()?
172 }
173
174 #[throws]
175 pub async fn run_debug(&self, target: String, seed: String) {
176 let config = TridentConfig::new();
177
178 if config.get_metrics() {
179 if config.get_metrics_json() {
180 let json_path = generate_unique_fuzz_filename("fuzzing_metrics", &target, "json")
181 .await
182 .map_err(|e| {
183 Error::Anyhow(anyhow::anyhow!(
184 "Failed to generate fuzzing metrics path: {:?}",
185 e
186 ))
187 })?;
188 std::env::set_var("FUZZING_JSON", json_path.to_string_lossy().to_string());
189 }
190
191 if config.get_metrics_dashboard() {
192 let dashboard_path =
193 generate_unique_fuzz_filename("fuzzing_dashboard", &target, "html")
194 .await
195 .map_err(|e| {
196 Error::Anyhow(anyhow::anyhow!(
197 "Failed to generate dashboard path: {:?}",
198 e
199 ))
200 })?;
201 std::env::set_var(
202 "FUZZING_DASHBOARD",
203 dashboard_path.to_string_lossy().to_string(),
204 );
205 }
206 }
207
208 if config.get_regression() {
209 let regression_path = generate_unique_fuzz_filename("regression", &target, "json")
210 .await
211 .map_err(|e| {
212 Error::Anyhow(anyhow::anyhow!(
213 "Failed to generate regression path: {:?}",
214 e
215 ))
216 })?;
217 std::env::set_var(
218 "FUZZING_REGRESSION",
219 regression_path.to_string_lossy().to_string(),
220 );
221
222 println!("FUZZING_REGRESSION: {}", regression_path.to_string_lossy());
223 }
224
225 let debug_path = generate_unique_fuzz_filename("trident_logs", &seed, "log")
226 .await
227 .map_err(|e| {
228 Error::Anyhow(anyhow::anyhow!(
229 "Failed to generate debug fuzzing path: {:?}",
230 e
231 ))
232 })?;
233
234 std::env::set_var(
235 "TRIDENT_FUZZ_DEBUG_PATH",
236 debug_path.to_string_lossy().to_string(),
237 );
238
239 std::env::set_var("TRIDENT_FUZZ_DEBUG", seed);
240
241 let mut child = Command::new("cargo")
242 .arg("run")
243 .arg("--bin")
244 .arg(target)
245 .args(["--profile", "release"])
246 .spawn()?;
247
248 Self::handle_child(&mut child, false).await?;
249 }
250}