From 0121096e84dd84b7953b4c315bbb3842571080ef Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Fri, 9 Jul 2021 20:07:56 +0200 Subject: [PATCH] Fixes for no_std build (#214) * builds on no_std * fixed std build * nightly fmt on CI * nightly fmt on CI (again) * fmt * no_std build on unix * more mem * added no_std from #212 to gh workflow * more ci, less nightly * clippy * more toolchains? * docu * y u no build * more ci? * next try * fixed dockr * more dockerfile fixes * ondisk corpus fixed * panic:? * ubunutu Co-authored-by: Andrea Fioraldi --- .github/workflows/build_and_test.yml | 25 ++++- Dockerfile | 8 ++ fuzzers/baby_fuzzer/README.md | 1 + fuzzers/baby_no_std/.gitignore | 1 + fuzzers/baby_no_std/Cargo.toml | 24 +++++ fuzzers/baby_no_std/README.md | 8 ++ fuzzers/baby_no_std/src/main.rs | 141 +++++++++++++++++++++++++++ libafl/Cargo.toml | 25 ++--- libafl/src/bolts/os/mod.rs | 5 +- libafl/src/bolts/os/pipes.rs | 7 +- libafl/src/corpus/ondisk.rs | 7 +- libafl/src/events/llmp.rs | 5 +- libafl/src/executors/inprocess.rs | 27 ++--- libafl/src/executors/timeout.rs | 1 + libafl/src/fuzzer.rs | 4 +- libafl/src/lib.rs | 5 +- 16 files changed, 256 insertions(+), 38 deletions(-) create mode 100644 fuzzers/baby_no_std/.gitignore create mode 100644 fuzzers/baby_no_std/Cargo.toml create mode 100644 fuzzers/baby_no_std/README.md create mode 100644 fuzzers/baby_no_std/src/main.rs diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index fcb53dbdf6..27ffe7c362 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -71,10 +71,32 @@ jobs: with: profile: minimal toolchain: stable + - name: Add nightly rustfmt and clippy + run: rustup toolchain install nightly --component rustfmt --component clippy --allow-downgrade - name: Install deps run: sudo apt-get install -y llvm llvm-dev clang nasm - name: Build and run example fuzzers run: ./scripts/build_all_fuzzers.sh + nostd-build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: nightly + - name: Add nightly rustfmt and clippy + run: rustup toolchain install nightly && rustup target add --toolchain nightly aarch64-unknown-none && rustup component add --toolchain nightly rust-src + - name: Build aarch64-unknown-none + run: cd ./fuzzers/baby_no_std && cargo +nightly build -Zbuild-std=core,alloc --target aarch64-unknown-none -v --release && cd ../.. + - name: run x86_64 until panic! + run: cd ./fuzzers/baby_no_std && cargo +nightly run || test $? -eq 1 || exit 1 + build-docker: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Build docker + run: docker build -t libafl . windows: runs-on: windows-latest steps: @@ -113,9 +135,10 @@ jobs: with: profile: minimal toolchain: stable + - name: Add nightly rustfmt and clippy + run: rustup toolchain install nightly --component rustfmt --component clippy --allow-downgrade - name: Install deps run: brew install llvm libpng nasm - - name: Increase map sizes run: ./scripts/shmem_limits_macos.sh - name: Build and run example fuzzers diff --git a/Dockerfile b/Dockerfile index 83cfd9aad0..a05fea51e9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -37,8 +37,14 @@ COPY libafl_frida/Cargo.toml libafl_frida/build.rs libafl_frida/ COPY scripts/dummy.rs libafl_frida/src/lib.rs COPY libafl_frida/src/gettls.c libafl_frida/src/gettls.c +COPY libafl_qemu/Cargo.toml libafl_qemu/build.rs libafl_qemu/ +COPY scripts/dummy.rs libafl_qemu/src/lib.rs +COPY libafl_qemu/src/weaks.c libafl_qemu/src/weaks.c + COPY libafl_cc/Cargo.toml libafl_cc/Cargo.toml COPY scripts/dummy.rs libafl_cc/src/lib.rs +COPY libafl_cc/build.rs libafl_cc/build.rs +COPY libafl_cc/src/cmplog-routines-pass.cc libafl_cc/src/cmplog-routines-pass.cc COPY libafl_targets/Cargo.toml libafl_targets/build.rs libafl_targets/ COPY libafl_targets/src libafl_targets/src @@ -73,6 +79,8 @@ RUN touch libafl/src/lib.rs COPY libafl_targets/src libafl_targets/src RUN touch libafl_targets/src/lib.rs COPY libafl_frida/src libafl_frida/src +RUN touch libafl_qemu/src/lib.rs +COPY libafl_qemu/src libafl_qemu/src RUN touch libafl_frida/src/lib.rs RUN cargo build && cargo build --release diff --git a/fuzzers/baby_fuzzer/README.md b/fuzzers/baby_fuzzer/README.md index d7766a569b..42fd9a5011 100644 --- a/fuzzers/baby_fuzzer/README.md +++ b/fuzzers/baby_fuzzer/README.md @@ -5,3 +5,4 @@ This is a minimalistic example about how to create a libafl based fuzzer. It runs on a single core until a crash occurs and then exits. The tested program is a simple Rust function without any instrumentation. +For real fuzzing, you will want to add some sort to add coverage or other feedback. \ No newline at end of file diff --git a/fuzzers/baby_no_std/.gitignore b/fuzzers/baby_no_std/.gitignore new file mode 100644 index 0000000000..a977a2ca5b --- /dev/null +++ b/fuzzers/baby_no_std/.gitignore @@ -0,0 +1 @@ +libpng-* \ No newline at end of file diff --git a/fuzzers/baby_no_std/Cargo.toml b/fuzzers/baby_no_std/Cargo.toml new file mode 100644 index 0000000000..e353ef4172 --- /dev/null +++ b/fuzzers/baby_no_std/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "baby_no_std" +version = "0.5.0" +authors = ["Andrea Fioraldi ", "Dominik Maier "] +edition = "2018" + +[profile.dev] +panic = "abort" + +[profile.release] +panic = "abort" +lto = true +codegen-units = 1 +opt-level = 3 +debug = true + +[dependencies] +libafl = { default-features = false, path = "../../libafl/" } +static-alloc = "0.2.3" + +[target.'cfg(unix)'.dependencies] +libc = "0.2" +cstr_core = "0.2.3" + diff --git a/fuzzers/baby_no_std/README.md b/fuzzers/baby_no_std/README.md new file mode 100644 index 0000000000..fe3fd4432f --- /dev/null +++ b/fuzzers/baby_no_std/README.md @@ -0,0 +1,8 @@ +# Baby `no_std` + +This is a minimalistic example how to create a libafl based fuzzer that works on `no_std` environments like TEEs, Kernels or on barew metal. + +It runs on a single core until a crash occurs and then calls the panic handler. + +The tested program is a simple Rust function without any instrumentation. +For real fuzzing, you will want to add some sort to add coverage or other feedback. \ No newline at end of file diff --git a/fuzzers/baby_no_std/src/main.rs b/fuzzers/baby_no_std/src/main.rs new file mode 100644 index 0000000000..0a56698164 --- /dev/null +++ b/fuzzers/baby_no_std/src/main.rs @@ -0,0 +1,141 @@ +#![no_std] +// Embedded targets: build with no_main +#![cfg_attr(not(any(windows, unix)), no_main)] +// Embedded needs alloc error handlers which only work on nightly right now... +#![cfg_attr(not(any(windows, unix)), feature(default_alloc_error_handler))] + +use libafl::{ + bolts::{current_nanos, rands::StdRand, tuples::tuple_list}, + corpus::{InMemoryCorpus, QueueCorpusScheduler}, + events::SimpleEventManager, + executors::{inprocess::InProcessExecutor, ExitKind}, + feedbacks::{CrashFeedback, MapFeedbackState, MaxMapFeedback}, + fuzzer::{Fuzzer, StdFuzzer}, + generators::RandPrintablesGenerator, + inputs::{BytesInput, HasTargetBytes}, + mutators::scheduled::{havoc_mutations, StdScheduledMutator}, + observers::StdMapObserver, + stages::mutational::StdMutationalStage, + state::StdState, + stats::SimpleStats, +}; + +#[cfg(any(windows, unix))] +use cstr_core::CString; +#[cfg(any(windows, unix))] +use libc::{c_char, printf}; + +#[cfg(not(any(windows, unix)))] +use core::panic::PanicInfo; +use static_alloc::Bump; + +#[global_allocator] +static A: Bump<[u8; 512 * 1024 * 1024]> = Bump::uninit(); + +#[cfg(not(any(windows, unix)))] +#[panic_handler] +fn panic(_info: &PanicInfo) -> ! { + loop {} +} + +/// Coverage map with explicit assignments due to the lack of instrumentation +static mut SIGNALS: [u8; 16] = [0; 16]; + +/// Assign a signal to the signals map +fn signals_set(idx: usize) { + unsafe { SIGNALS[idx] = 1 }; +} + +#[allow(clippy::similar_names)] +pub fn main() { + // The closure that we want to fuzz + let mut harness = |input: &BytesInput| { + let target = input.target_bytes(); + let buf = target.as_slice(); + signals_set(0); + if !buf.is_empty() && buf[0] == b'a' { + signals_set(1); + if buf.len() > 1 && buf[1] == b'b' { + signals_set(2); + if buf.len() > 2 && buf[2] == b'c' { + panic!("=)"); + } + } + } + ExitKind::Ok + }; + + // Create an observation channel using the signals map + let observer = StdMapObserver::new("signals", unsafe { &mut SIGNALS }); + + // The state of the edges feedback. + let feedback_state = MapFeedbackState::with_observer(&observer); + + // Feedback to rate the interestingness of an input + let feedback = MaxMapFeedback::new(&feedback_state, &observer); + + // A feedback to choose if an input is a solution or not + let objective = CrashFeedback::new(); + + // create a State from scratch + let mut state = StdState::new( + // RNG + StdRand::with_seed(current_nanos()), + // Corpus that will be evolved, we keep it in memory for performance + InMemoryCorpus::new(), + // Corpus in which we store solutions (crashes in this example), + // on disk so the user can get them after stopping the fuzzer + InMemoryCorpus::new(), + // States of the feedbacks. + // They are the data related to the feedbacks that you want to persist in the State. + tuple_list!(feedback_state), + ); + + // The Stats trait define how the fuzzer stats are reported to the user + let stats = SimpleStats::new(|s| { + // TODO: Print `s` here, if your target permits it. + #[cfg(any(windows, unix))] + unsafe { + printf( + [b'%' as c_char, b's' as c_char, b'\n' as c_char, 0 as c_char].as_ptr(), + CString::new(s).unwrap().as_ptr() as *const c_char, + ); + } + }); + + // The event manager handle the various events generated during the fuzzing loop + // such as the notification of the addition of a new item to the corpus + let mut mgr = SimpleEventManager::new(stats); + + // A queue policy to get testcasess from the corpus + let scheduler = QueueCorpusScheduler::new(); + + // A fuzzer with feedbacks and a corpus scheduler + let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); + + // Create the executor for an in-process function with just one observer + let mut executor = InProcessExecutor::new( + &mut harness, + tuple_list!(observer), + &mut fuzzer, + &mut state, + &mut mgr, + ) + .expect("Failed to create the Executor"); + + // Generator of printable bytearrays of max size 32 + let mut generator = RandPrintablesGenerator::new(32); + + // Generate 8 initial inputs + state + .generate_initial_inputs(&mut fuzzer, &mut executor, &mut generator, &mut mgr, 8) + .expect("Failed to generate the initial corpus"); + + // Setup a mutational stage with a basic bytes mutator + let mutator = StdScheduledMutator::new(havoc_mutations()); + let mut stages = tuple_list!(StdMutationalStage::new(mutator)); + + fuzzer + .fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr) + .expect("Error in the fuzzing loop"); +} diff --git a/libafl/Cargo.toml b/libafl/Cargo.toml index 52c1a5747b..5bd162d0b8 100644 --- a/libafl/Cargo.toml +++ b/libafl/Cargo.toml @@ -37,7 +37,7 @@ harness = false [features] default = ["std", "anymap_debug", "derive", "llmp_compression"] -std = ["serde_json"] # print, env, launcher ... support +std = ["serde_json", "hostname", "core_affinity", "nix", "serde/std"] # print, env, launcher ... support anymap_debug = ["serde_json"] # uses serde_json to Debug the anymap trait. Disable for smaller footprint. derive = ["libafl_derive"] # provide derive(SerdeAny) macro. rand_trait = ["rand_core"] # If set, libafl's rand implementations will implement `rand::Rng` @@ -53,25 +53,27 @@ path = "./examples/llmp_test/main.rs" required-features = ["std"] [dependencies] -libafl_derive = { optional = true, path = "../libafl_derive", version = "0.5.0" } -tuple_list = "0.1.2" -hashbrown = { version = "0.9", features = ["serde", "ahash-compile-time-rng"] } # A faster hashmap, nostd compatible -num = "0.4.0" +tuple_list = { version = "0.1.2", git = "https://github.com/domenukk/tuple_list" } +hashbrown = { version = "0.9", features = ["serde", "ahash-compile-time-rng"], default-features=false } # A faster hashmap, nostd compatible +num = { version = "0.4.0", default-features = false } xxhash-rust = { version = "0.8.2", features = ["xxh3"] } # xxh3 hashing for rust serde = { version = "1.0", default-features = false, features = ["alloc"] } # serialization lib -erased-serde = "0.3.12" +erased-serde = { version = "0.3.12", default-features = false, features = ["alloc"] } # erased serde postcard = { version = "0.5.1", features = ["alloc"] } # no_std compatible serde serialization fromat static_assertions = "1.1.0" ctor = "0.1.20" +num_enum = { version = "0.5.1", default-features = false } +typed-builder = "0.9.0" # Implement the builder pattern at compiletime +ahash = { version = "0.7", default-features=false, features=["compile-time-rng"] } # The hash function already used in hashbrown + +libafl_derive = { version = "0.5.0", optional = true, path = "../libafl_derive" } serde_json = { version = "1.0", optional = true, default-features = false, features = ["alloc"] } # an easy way to debug print SerdeAnyMap miniz_oxide = { version = "0.4.4", optional = true} -core_affinity = { version = "0.5", git = "https://github.com/s1341/core_affinity_rs" } -num_enum = "0.5.1" # compression/decomrpession lib -hostname = "^0.3" # Is there really no gethostname in the stdlib? -typed-builder = "0.9.0" # Implement the builder pattern at compiletime -ahash ="0.7" # The hash function already used in hashbrown +core_affinity = { version = "0.5", git = "https://github.com/s1341/core_affinity_rs", optional = true } +hostname = { version = "^0.3", optional = true } # Is there really no gethostname in the stdlib? rand = { version = "0.8.1", optional = true } # rand_core = { version = "0.6.2", optional = true } # This dependency allows us to export our RomuRand as rand::Rng. +nix = { version = "0.20.0", optional = true } [target.'cfg(target_os = "android")'.dependencies] backtrace = { version = "0.3", optional = true, default-features = false, features = ["std", "libbacktrace"] } # for llmp_debug @@ -81,7 +83,6 @@ backtrace = { version = "0.3", optional = true } # for llmp_debug [target.'cfg(unix)'.dependencies] libc = "0.2" # For (*nix) libc -nix = "0.20.0" uds = "0.2.3" lock_api = "0.4.3" regex = "1.4.5" diff --git a/libafl/src/bolts/os/mod.rs b/libafl/src/bolts/os/mod.rs index 289c371f60..8cf8842277 100644 --- a/libafl/src/bolts/os/mod.rs +++ b/libafl/src/bolts/os/mod.rs @@ -1,7 +1,5 @@ //! Operating System specific abstractions -use alloc::vec::Vec; - #[cfg(any(unix, all(windows, feature = "std")))] use crate::Error; @@ -14,7 +12,7 @@ pub mod ashmem_server; #[cfg(unix)] pub mod unix_signals; -#[cfg(unix)] +#[cfg(all(unix, feature = "std"))] pub mod pipes; #[cfg(all(unix, feature = "std"))] @@ -101,6 +99,7 @@ pub fn dup2(fd: i32, device: i32) -> Result<(), Error> { /// `./fuzzer --cores 1,2-4,6` -> clients run in cores 1,2,3,4,6 /// ` ./fuzzer --cores all` -> one client runs on each available core #[must_use] +#[cfg(feature = "std")] pub fn parse_core_bind_arg(args: &str) -> Option> { let mut cores: Vec = vec![]; if args == "all" { diff --git a/libafl/src/bolts/os/pipes.rs b/libafl/src/bolts/os/pipes.rs index 243211dc20..43261bedb9 100644 --- a/libafl/src/bolts/os/pipes.rs +++ b/libafl/src/bolts/os/pipes.rs @@ -1,9 +1,7 @@ //! Unix `pipe` wrapper for `LibAFL` use crate::Error; -use nix::unistd::{close, pipe}; - #[cfg(feature = "std")] -use nix::unistd::{read, write}; +use nix::unistd::{close, pipe, read, write}; #[cfg(feature = "std")] use std::{ io::{self, ErrorKind, Read, Write}, @@ -13,12 +11,14 @@ use std::{ #[cfg(not(feature = "std"))] type RawFd = i32; +#[cfg(feature = "std")] #[derive(Debug, Clone)] pub struct Pipe { read_end: Option, write_end: Option, } +#[cfg(feature = "std")] impl Pipe { pub fn new() -> Result { let (read_end, write_end) = pipe()?; @@ -91,6 +91,7 @@ impl Write for Pipe { } } +#[cfg(feature = "std")] impl Drop for Pipe { fn drop(&mut self) { if let Some(read_end) = self.read_end { diff --git a/libafl/src/corpus/ondisk.rs b/libafl/src/corpus/ondisk.rs index 8e9503846d..5f42d6f192 100644 --- a/libafl/src/corpus/ondisk.rs +++ b/libafl/src/corpus/ondisk.rs @@ -3,9 +3,10 @@ use alloc::vec::Vec; use core::cell::RefCell; use serde::{Deserialize, Serialize}; +use std::path::PathBuf; #[cfg(feature = "std")] -use std::{fs, fs::File, io::Write, path::PathBuf}; +use std::{fs, fs::File, io::Write}; use crate::{corpus::Corpus, corpus::Testcase, inputs::Input, state::HasMetadata, Error}; @@ -128,7 +129,7 @@ where Ok(Self { entries: vec![], current: None, - dir_path, + dir_path: dir_path, meta_format: None, }) } @@ -143,7 +144,7 @@ where Ok(Self { entries: vec![], current: None, - dir_path, + dir_path: dir_path, meta_format, }) } diff --git a/libafl/src/events/llmp.rs b/libafl/src/events/llmp.rs index 803f32ba85..95b09a388b 100644 --- a/libafl/src/events/llmp.rs +++ b/libafl/src/events/llmp.rs @@ -5,9 +5,11 @@ use alloc::{ vec::Vec, }; use core::{marker::PhantomData, time::Duration}; -use core_affinity::CoreId; use serde::{de::DeserializeOwned, Serialize}; +#[cfg(feature = "std")] +use core_affinity::CoreId; + #[cfg(feature = "std")] use core::ptr::{addr_of, read_volatile}; @@ -689,6 +691,7 @@ where } /// The kind of manager we're creating right now +#[cfg(feature = "std")] #[derive(Debug, Clone, Copy)] pub enum ManagerKind { /// Any kind will do diff --git a/libafl/src/executors/inprocess.rs b/libafl/src/executors/inprocess.rs index 6a6dc6cca5..3c98721af3 100644 --- a/libafl/src/executors/inprocess.rs +++ b/libafl/src/executors/inprocess.rs @@ -1,12 +1,11 @@ //! The [`InProcessExecutor`] is a libfuzzer-like executor, that will simply call a function. //! It should usually be paired with extra error-handling, such as a restarting event manager, to be effective. -use core::marker::PhantomData; +use core::{ffi::c_void, marker::PhantomData, ptr}; #[cfg(any(unix, all(windows, feature = "std")))] use core::{ - ffi::c_void, - ptr::{self, write_volatile}, + ptr::write_volatile, sync::atomic::{compiler_fence, Ordering}, }; @@ -28,6 +27,8 @@ use crate::{ }; /// The inmem executor simply calls a target function, then returns afterwards. +#[allow(dead_code)] +#[derive(Debug)] pub struct InProcessExecutor<'a, H, I, OT, S> where H: FnMut(&I) -> ExitKind, @@ -54,9 +55,9 @@ where #[inline] fn run_target( &mut self, - fuzzer: &mut Z, - state: &mut S, - mgr: &mut EM, + _fuzzer: &mut Z, + _state: &mut S, + _mgr: &mut EM, input: &I, ) -> Result { #[cfg(unix)] @@ -74,9 +75,9 @@ where data.timeout_handler = self.timeout_handler; // Direct raw pointers access /aliasing is pretty undefined behavior. // Since the state and event may have moved in memory, refresh them right before the signal may happen - write_volatile(&mut data.state_ptr, state as *mut _ as *mut c_void); - write_volatile(&mut data.event_mgr_ptr, mgr as *mut _ as *mut c_void); - write_volatile(&mut data.fuzzer_ptr, fuzzer as *mut _ as *mut c_void); + write_volatile(&mut data.state_ptr, _state as *mut _ as *mut c_void); + write_volatile(&mut data.event_mgr_ptr, _mgr as *mut _ as *mut c_void); + write_volatile(&mut data.fuzzer_ptr, _fuzzer as *mut _ as *mut c_void); compiler_fence(Ordering::SeqCst); } #[cfg(all(windows, feature = "std"))] @@ -94,9 +95,9 @@ where //data.timeout_handler = self.timeout_handler; // Direct raw pointers access /aliasing is pretty undefined behavior. // Since the state and event may have moved in memory, refresh them right before the signal may happen - write_volatile(&mut data.state_ptr, state as *mut _ as *mut c_void); - write_volatile(&mut data.event_mgr_ptr, mgr as *mut _ as *mut c_void); - write_volatile(&mut data.fuzzer_ptr, fuzzer as *mut _ as *mut c_void); + write_volatile(&mut data.state_ptr, _state as *mut _ as *mut c_void); + write_volatile(&mut data.event_mgr_ptr, _mgr as *mut _ as *mut c_void); + write_volatile(&mut data.fuzzer_ptr, _fuzzer as *mut _ as *mut c_void); compiler_fence(Ordering::SeqCst); } @@ -210,6 +211,8 @@ where Ok(Self { harness_fn, observers, + crash_handler: ptr::null(), + timeout_handler: ptr::null(), phantom: PhantomData, }) } diff --git a/libafl/src/executors/timeout.rs b/libafl/src/executors/timeout.rs index 998f0e6549..85c91bb12a 100644 --- a/libafl/src/executors/timeout.rs +++ b/libafl/src/executors/timeout.rs @@ -1,5 +1,6 @@ //! A `TimeoutExecutor` sets a timeout before each target run +#[cfg(any(windows, unix))] use core::time::Duration; use crate::{ diff --git a/libafl/src/fuzzer.rs b/libafl/src/fuzzer.rs index 2b5bbd6bc3..6a798b656a 100644 --- a/libafl/src/fuzzer.rs +++ b/libafl/src/fuzzer.rs @@ -332,7 +332,7 @@ where #[cfg(not(feature = "introspection"))] let is_solution = self .objective_mut() - .is_interesting(state, manager, &input, observers, &exit_kind)?; + .is_interesting(state, manager, &input, observers, exit_kind)?; #[cfg(feature = "introspection")] let is_solution = self @@ -345,7 +345,7 @@ where #[cfg(not(feature = "introspection"))] let is_corpus = self .feedback_mut() - .is_interesting(state, manager, &input, observers, &exit_kind)?; + .is_interesting(state, manager, &input, observers, exit_kind)?; #[cfg(feature = "introspection")] let is_corpus = self diff --git a/libafl/src/lib.rs b/libafl/src/lib.rs index 0558648a14..c5405b1069 100644 --- a/libafl/src/lib.rs +++ b/libafl/src/lib.rs @@ -45,6 +45,9 @@ use core::fmt; #[cfg(feature = "std")] use std::{env::VarError, io, num::ParseIntError, string::FromUtf8Error}; +#[cfg(all(unix, feature = "std"))] +use nix; + /// Main error struct for AFL #[derive(Debug)] pub enum Error { @@ -120,7 +123,7 @@ impl From for Error { } } -#[cfg(unix)] +#[cfg(all(unix, feature = "std"))] impl From for Error { fn from(err: nix::Error) -> Self { Self::Unknown(format!("{:?}", err))