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 child = self.spawn_fuzzer(target, HashMap::new(), seed)?;
78 Self::handle_child(&mut child, with_exit_code).await?;
79 }
80
81 #[throws]
82 pub async fn run_with_coverage(
83 &self,
84 target: &str,
85 config: &TridentConfig,
86 coverage_config: CoverageConfig,
87 seed: Option<String>,
88 with_exit_code: bool,
89 ) {
90 if let Err(err) = coverage_config.validate() {
91 throw!(Error::Anyhow(anyhow::anyhow!(err)));
92 }
93
94 let coverage = Coverage::new(
95 &self.get_target_dir()?,
96 target,
97 coverage_config.get_attach_extension(),
98 coverage_config.get_format(),
99 coverage_config.get_loopcount(),
100 config.coverage_server_port(),
101 );
102
103 if coverage.check_llvm_tools_installed().await.is_err() {
104 coverage.prompt_and_install_llvm_tools().await?;
105 }
106
107 coverage.clean().await?;
108
109 let env_vars = self.setup_coverage_env_vars(&coverage, config).await?;
110 let mut child = self.spawn_fuzzer(target, env_vars, seed)?;
111
112 coverage.notify_extension(NotificationType::Setup).await?;
113 Self::handle_child(&mut child, with_exit_code).await?;
114
115 coverage.generate_report().await?;
116 }
117
118 #[throws]
119 async fn setup_coverage_env_vars(
120 &self,
121 coverage: &Coverage,
122 config: &TridentConfig,
123 ) -> HashMap<&str, String> {
124 let mut rustflags = std::env::var("RUSTFLAGS").unwrap_or_default();
125 rustflags.push_str(&coverage.get_rustflags());
126
127 let mut env_vars: HashMap<&str, String> = HashMap::new();
128 env_vars.insert("RUSTFLAGS", rustflags);
129 env_vars.insert("LLVM_PROFILE_FILE", coverage.get_profraw_file());
130 env_vars.insert("CARGO_LLVM_COV_TARGET_DIR", coverage.get_target_dir());
131 env_vars.insert("FUZZER_LOOPCOUNT", coverage.get_loopcount().to_string());
132 env_vars.insert(
133 "COVERAGE_SERVER_PORT",
134 config.coverage_server_port().to_string(),
135 );
136 env_vars.insert("COLLECT_COVERAGE", "1".to_string());
138
139 env_vars
140 }
141
142 #[throws]
143 fn spawn_fuzzer(
144 &self,
145 target: &str,
146 mut env_vars: HashMap<&str, String>,
147 seed: Option<String>,
148 ) -> tokio::process::Child {
149 if let Some(seed) = seed {
150 let _decoded_seed = hex::decode(&seed)
153 .unwrap_or_else(|_| panic!("The seed is not a valid hex string: {}", seed));
154
155 env_vars.insert("TRIDENT_FUZZ_SEED", seed);
156 }
157
158 Command::new("cargo")
159 .envs(env_vars)
160 .arg("run")
161 .arg("--bin")
162 .arg(target)
163 .args(["--profile", "release"])
164 .spawn()?
165 }
166
167 #[throws]
168 pub async fn run_debug(&self, target: String, seed: String) {
169 let config = TridentConfig::new();
170
171 if config.get_metrics() {
172 if config.get_metrics_json() {
173 let json_path = generate_unique_fuzz_filename("fuzzing_metrics", &target, "json")
174 .await
175 .map_err(|e| {
176 Error::Anyhow(anyhow::anyhow!(
177 "Failed to generate fuzzing metrics path: {:?}",
178 e
179 ))
180 })?;
181 std::env::set_var("FUZZING_JSON", json_path.to_string_lossy().to_string());
182 }
183
184 if config.get_metrics_dashboard() {
185 let dashboard_path =
186 generate_unique_fuzz_filename("fuzzing_dashboard", &target, "html")
187 .await
188 .map_err(|e| {
189 Error::Anyhow(anyhow::anyhow!(
190 "Failed to generate dashboard path: {:?}",
191 e
192 ))
193 })?;
194 std::env::set_var(
195 "FUZZING_DASHBOARD",
196 dashboard_path.to_string_lossy().to_string(),
197 );
198 }
199 }
200
201 if config.get_regression() {
202 let regression_path = generate_unique_fuzz_filename("regression", &target, "json")
203 .await
204 .map_err(|e| {
205 Error::Anyhow(anyhow::anyhow!(
206 "Failed to generate regression path: {:?}",
207 e
208 ))
209 })?;
210 std::env::set_var(
211 "FUZZING_REGRESSION",
212 regression_path.to_string_lossy().to_string(),
213 );
214
215 println!("FUZZING_REGRESSION: {}", regression_path.to_string_lossy());
216 }
217
218 let debug_path = generate_unique_fuzz_filename("trident_logs", &seed, "log")
219 .await
220 .map_err(|e| {
221 Error::Anyhow(anyhow::anyhow!(
222 "Failed to generate debug fuzzing path: {:?}",
223 e
224 ))
225 })?;
226
227 std::env::set_var(
228 "TRIDENT_FUZZ_DEBUG_PATH",
229 debug_path.to_string_lossy().to_string(),
230 );
231
232 std::env::set_var("TRIDENT_FUZZ_DEBUG", seed);
233
234 let mut child = Command::new("cargo")
235 .arg("run")
236 .arg("--bin")
237 .arg(target)
238 .args(["--profile", "release"])
239 .spawn()?;
240
241 Self::handle_child(&mut child, false).await?;
242 }
243}