xtask/tasks/fuzz/
cargo_fuzz.rs1use crate::shell::XtaskShell;
7use anyhow::Context;
8use std::path::Path;
9use std::path::PathBuf;
10
11pub(super) enum CargoFuzzCommand {
12 Build,
13 Run { artifact: Option<PathBuf> },
14 Fmt { input: PathBuf },
15 Cmin,
16 Tmin { test_case: PathBuf },
17 Coverage,
18}
19
20impl CargoFuzzCommand {
21 fn to_args<'a, 'b: 'a>(&'b self, target: &'a str) -> Vec<&'a str> {
22 match self {
23 CargoFuzzCommand::Build => {
24 vec!["build", target]
25 }
26 CargoFuzzCommand::Run { artifact } => {
27 let mut args = vec!["run", target];
28 if let Some(artifact) = artifact {
29 args.push(artifact.to_str().unwrap())
30 }
31 args
32 }
33 CargoFuzzCommand::Fmt { input } => {
34 vec!["fmt", target, input.to_str().unwrap()]
35 }
36 CargoFuzzCommand::Cmin => {
37 vec!["cmin", target]
38 }
39 CargoFuzzCommand::Tmin { test_case } => {
40 vec!["tmin", target, test_case.to_str().unwrap()]
41 }
42 CargoFuzzCommand::Coverage => {
43 vec!["coverage", target]
44 }
45 }
46 }
47
48 pub(super) fn invoke(
49 self,
50 target_name: &str,
51 fuzz_dir: &Path,
52 target_options: &[String],
53 toolchain: Option<&str>,
54 extra: &[String],
55 ) -> anyhow::Result<()> {
56 if which::which("cargo-fuzz").is_err() {
57 anyhow::bail!("could not find cargo-fuzz! did you run `cargo install cargo-fuzz`?");
58 }
59
60 let sh = XtaskShell::new()?;
61 if matches!(&self, CargoFuzzCommand::Run { artifact: Some(_) }) {
62 sh.set_var("XTASK_FUZZ_REPRO", "1");
63 }
64
65 let mut toolchain_check_cmd = sh.cmd("rustc");
66 if let Some(toolchain_override) = toolchain {
67 toolchain_check_cmd = toolchain_check_cmd.arg(format!("+{}", toolchain_override));
68 }
69 let result = toolchain_check_cmd
70 .arg("-V")
71 .output()
72 .context("could not detect toolchain! did you run `rustup toolchain install`?")?;
73 let output = std::str::from_utf8(&result.stdout)?.to_ascii_lowercase();
74 let is_nightly = output.contains("-nightly") || output.contains("-dev");
75
76 let mut cmd = sh.cmd("cargo");
77 if let Some(toolchain_override) = toolchain {
78 cmd = cmd.arg(format!("+{}", toolchain_override));
79 }
80 cmd = cmd.arg("fuzz");
81 cmd = cmd.args(self.to_args(target_name));
82 cmd = cmd.arg("--fuzz-dir").arg(fuzz_dir);
83
84 if is_nightly {
85 } else if std::env::var_os("CARGO").is_some() {
87 log::warn!(
91 "Running on a stable toolchain in a `cargo xtask` invocation, disabling sanitizers"
92 );
93 log::warn!(
94 "To enable sanitizers, run {} directly, or switch to a nightly toolchain",
95 std::env::current_exe()?.display()
96 );
97 cmd = cmd.args(["-s", "none"]);
98 } else {
99 log::warn!("Running on a stable toolchain, enabling sanitizers via RUSTC_BOOTSTRAP");
101 cmd = cmd.env("RUSTC_BOOTSTRAP", "1");
102 }
103
104 cmd = cmd.args(extra);
105 if self.supports_target_options() && !target_options.is_empty() {
106 if !extra.iter().any(|x| x == "--") {
107 cmd = cmd.arg("--");
108 }
109 cmd = cmd.args(target_options);
110 }
111
112 cmd.run()?;
113
114 Ok(())
115 }
116
117 fn supports_target_options(&self) -> bool {
118 matches!(self, CargoFuzzCommand::Run { .. })
119 }
120}