xtask_fuzz/
lib.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Helpers when writing fuzzing crates that get invoked via `cargo xtask fuzz`.
5//!
6//! Might end up getting deprecated, if/when
7//! <https://github.com/rust-fuzz/cargo-fuzz/issues/346> is resolved
8
9#![forbid(unsafe_code)]
10
11use std::sync::OnceLock;
12
13static IS_REPRO: OnceLock<bool> = OnceLock::new();
14
15/// Check if the `XTASK_FUZZ_REPRO` env var was set.
16///
17/// Caches result in a global static, to avoid redundant lookups.
18pub fn is_repro() -> bool {
19    *IS_REPRO.get_or_init(|| std::env::var("XTASK_FUZZ_REPRO").is_ok())
20}
21
22/// Initialize tracing if this is a repro run.
23pub fn init_tracing_if_repro() {
24    use std::sync::Once;
25    use tracing_subscriber::filter::LevelFilter;
26    use tracing_subscriber::filter::Targets;
27    use tracing_subscriber::fmt::format::FmtSpan;
28    use tracing_subscriber::layer::SubscriberExt;
29    use tracing_subscriber::util::SubscriberInitExt;
30
31    // cargo-fuzz can call our fuzz target multiple times, but we can only initialize tracing once.
32    static INIT: Once = Once::new();
33
34    if is_repro() {
35        INIT.call_once(|| {
36            let targets = if let Ok(var) = std::env::var("OPENVMM_LOG") {
37                var.parse().unwrap()
38            } else {
39                Targets::new().with_default(LevelFilter::TRACE)
40            };
41
42            tracing_subscriber::fmt()
43                .compact()
44                .log_internal_errors(true)
45                .with_max_level(LevelFilter::TRACE)
46                .with_span_events(FmtSpan::NEW | FmtSpan::CLOSE)
47                .finish()
48                .with(targets)
49                .init();
50        });
51    }
52}
53
54/// `eprintln!` that only gets calls when reproducing fuzz test cases locally
55#[macro_export]
56macro_rules! fuzz_eprintln {
57    ($($arg:tt)*) => {
58        if $crate::is_repro() {
59            eprintln!($($arg)*)
60        }
61    };
62}
63
64#[cfg(all(target_os = "linux", target_env = "gnu"))]
65pub use libfuzzer_sys::fuzz_target;
66
67#[cfg(not(all(target_os = "linux", target_env = "gnu")))]
68/// Fake version of `libfuzzer_sys::fuzz_target` for non-linux-gnu targets.
69#[macro_export]
70macro_rules! fuzz_target {
71    ($($tt:tt)*) => {
72        // libfuzzer-sys is only supported on Linux gnu, so add a main function
73        // that references do_fuzz to satisfy rust-analyzer.
74        fn main() {
75            let _ = do_fuzz;
76        }
77    };
78}