Skip to content

Commit

Permalink
Make bolts work without alloc (#1401)
Browse files Browse the repository at this point in the history
* Make bolts work without alloc

* Use core::Error where available

* unstable_feature -> nightly

* windows no_alloc
  • Loading branch information
domenukk authored Aug 4, 2023
1 parent a0c03fc commit dfaf06a
Show file tree
Hide file tree
Showing 12 changed files with 132 additions and 50 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/build_and_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ jobs:
run: cargo test
- name: Test libafl no_std
run: cd libafl && cargo test --no-default-features
- name: Test libafl_bolts no_std no_alloc
run: cd libafl_bolts && cargo test --no-default-features
- name: Test libafl_targets no_std
run: cd libafl_targets && cargo test --no-default-features

Expand Down Expand Up @@ -285,6 +287,8 @@ jobs:
run: cd ./libafl && cargo test --no-default-features
- name: libafl armv6m-none-eabi (32 bit no_std) clippy
run: cd ./libafl && cargo clippy --target thumbv6m-none-eabi --no-default-features
- name: Build no_std no_alloc bolts
run: cd ./libafl_bolts && cargo +nightly build -Zbuild-std=core --target aarch64-unknown-none --no-default-features -v --release && cd ../

build-docker:
runs-on: ubuntu-latest
Expand Down
2 changes: 1 addition & 1 deletion libafl/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ serde_json = { version = "1.0", default-features = false, features = ["alloc"] }
bytecount = "0.6.3"

[dependencies]
libafl_bolts = { version = "0.10.1", path = "../libafl_bolts", default-features = false }
libafl_bolts = { version = "0.10.1", path = "../libafl_bolts", default-features = false, features = ["alloc"] }
libafl_derive = { version = "0.10.1", path = "../libafl_derive", optional = true }

rustversion = "1.0"
Expand Down
2 changes: 1 addition & 1 deletion libafl/build.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#[rustversion::nightly]
fn main() {
println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rustc-cfg=unstable_feature");
println!("cargo:rustc-cfg=nightly");
}

#[rustversion::not(nightly)]
Expand Down
6 changes: 3 additions & 3 deletions libafl/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ Welcome to `LibAFL`
#![allow(incomplete_features)]
#![no_std]
// For `type_eq`
#![cfg_attr(unstable_feature, feature(specialization))]
#![cfg_attr(nightly, feature(specialization))]
// For `type_id` and owned things
#![cfg_attr(unstable_feature, feature(intrinsics))]
#![cfg_attr(nightly, feature(intrinsics))]
// For `std::simd`
#![cfg_attr(unstable_feature, feature(portable_simd))]
#![cfg_attr(nightly, feature(portable_simd))]
#![warn(clippy::cargo)]
#![allow(ambiguous_glob_reexports)]
#![deny(clippy::cargo_common_metadata)]
Expand Down
4 changes: 2 additions & 2 deletions libafl/src/observers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ pub mod concolic;
pub mod value;

// Rust is breaking this with 'error: intrinsic safety mismatch between list of intrinsics within the compiler and core library intrinsics for intrinsic `type_id`' and so we disable this component for the moment
//#[cfg(unstable_feature)]
//#[cfg(nightly)]
//pub mod owned;
//#[cfg(unstable_feature)]
//#[cfg(nightly)]
//pub use owned::*;
use alloc::{
string::{String, ToString},
Expand Down
27 changes: 14 additions & 13 deletions libafl_bolts/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@ edition = "2021"
categories = ["development-tools::testing", "emulators", "embedded", "os", "no-std"]

[features]
default = ["std", "derive", "llmp_compression", "llmp_small_maps", "rand_trait", "prelude", "gzip", "serdeany_autoreg"]
std = ["serde_json", "serde_json/std", "hostname", "nix", "serde/std", "once_cell", "uuid", "byteorder", "backtrace", "uds", "serial_test"] # print, env, launcher ... support
default = ["std", "derive", "llmp_compression", "llmp_small_maps", "rand_trait", "prelude", "gzip", "serdeany_autoreg", "alloc"]
std = ["serde_json", "serde_json/std", "hostname", "nix", "serde/std", "once_cell", "uuid", "byteorder", "backtrace", "uds", "serial_test", "alloc"] # print, env, ... support
alloc = ["serde/alloc", "hashbrown", "postcard", "erased-serde/alloc", "ahash"] # Enables all features that allocate in no_std
derive = ["libafl_derive"] # provide derive(SerdeAny) macro.
rand_trait = ["rand_core"] # If set, libafl's rand implementations will implement `rand::Rng`
python = ["pyo3"]
python = ["pyo3", "std"]
prelude = [] # Expose libafl::prelude for access without additional using directives
cli = ["clap"] # expose libafl_bolts::cli for easy commandline parsing
qemu_cli = ["cli"] # Commandline flags for qemu-based fuzzers
qemu_cli = ["cli"] # Commandline flagr for qemu-based fuzzers
frida_cli = ["cli"] # Commandline flags for frida-based fuzzers
errors_backtrace = ["backtrace"]
gzip = ["miniz_oxide"] # Enables gzip compression in certain parts of the lib
Expand All @@ -28,10 +29,10 @@ gzip = ["miniz_oxide"] # Enables gzip compression in certain parts of the lib
serdeany_autoreg = ["ctor"] # Automatically register all `#[derive(SerdeAny)]` types at startup.

# LLMP features
llmp_bind_public = [] # If set, llmp will bind to 0.0.0.0, allowing cross-device communication. Binds to localhost by default.
llmp_compression = ["gzip"] # llmp compression using GZip
llmp_debug = [] # Enables debug output for LLMP
llmp_small_maps = [] # reduces initial map size for llmp
llmp_bind_public = ["alloc"] # If set, llmp will bind to 0.0.0.0, allowing cross-device communication. Binds to localhost by default.
llmp_compression = ["alloc", "gzip"] # llmp compression using GZip
llmp_debug = ["alloc"] # Enables debug output for LLMP
llmp_small_maps = ["alloc"] # reduces initial map size for llmp

[build-dependencies]
rustversion = "1.0"
Expand All @@ -45,13 +46,13 @@ libafl_derive = { version = "0.10.1", optional = true, path = "../libafl_derive"

rustversion = "1.0"
tuple_list = { version = "0.1.3" }
hashbrown = { version = "0.14", features = ["serde", "ahash"], default-features=false } # A faster hashmap, nostd compatible
hashbrown = { version = "0.14", features = ["serde", "ahash"], default-features=false, optional = true } # A faster hashmap, nostd compatible
xxhash-rust = { version = "0.8.5", features = ["xxh3"] } # xxh3 hashing for rust
serde = { version = "1.0", default-features = false, features = ["alloc", "derive"] } # serialization lib
erased-serde = { version = "0.3.21", default-features = false, features = ["alloc"] } # erased serde
postcard = { version = "1.0", features = ["alloc"] } # no_std compatible serde serialization format
serde = { version = "1.0", default-features = false, features = ["derive"] } # serialization lib
erased-serde = { version = "0.3.21", default-features = false, optional = true } # erased serde
postcard = { version = "1.0", features = ["alloc"], optional = true } # no_std compatible serde serialization format
num_enum = { version = "0.5.7", default-features = false }
ahash = { version = "0.8", default-features=false } # The hash function already used in hashbrown
ahash = { version = "0.8", default-features=false, optional = true } # The hash function already used in hashbrown
backtrace = {version = "0.3", optional = true} # Used to get the stacktrace in StacktraceObserver

ctor = { optional = true, version = "0.2" }
Expand Down
2 changes: 1 addition & 1 deletion libafl_bolts/build.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#[rustversion::nightly]
fn main() {
println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rustc-cfg=unstable_feature");
println!("cargo:rustc-cfg=nightly");
}

#[rustversion::not(nightly)]
Expand Down
56 changes: 47 additions & 9 deletions libafl_bolts/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ Welcome to `LibAFL`
#![allow(incomplete_features)]
#![no_std]
// For `type_eq`
#![cfg_attr(unstable_feature, feature(specialization))]
#![cfg_attr(nightly, feature(specialization))]
// For `type_id` and owned things
#![cfg_attr(unstable_feature, feature(intrinsics))]
#![cfg_attr(nightly, feature(intrinsics))]
// For `std::simd`
#![cfg_attr(unstable_feature, feature(portable_simd))]
#![cfg_attr(nightly, feature(portable_simd))]
// For `core::error`
#![cfg_attr(nightly, feature(error_in_core))]
#![warn(clippy::cargo)]
#![allow(ambiguous_glob_reexports)]
#![deny(clippy::cargo_common_metadata)]
Expand Down Expand Up @@ -78,6 +80,7 @@ Welcome to `LibAFL`
#[cfg(feature = "std")]
#[macro_use]
extern crate std;
#[cfg(feature = "alloc")]
#[macro_use]
#[doc(hidden)]
pub extern crate alloc;
Expand All @@ -98,10 +101,11 @@ pub mod launcher {}
#[allow(unused_imports)]
#[macro_use]
extern crate libafl_derive;
#[cfg(feature = "alloc")]
use alloc::string::{FromUtf8Error, String};
use core::{
array::TryFromSliceError,
fmt,
fmt::{self, Display},
num::{ParseIntError, TryFromIntError},
};
#[cfg(feature = "std")]
Expand All @@ -110,6 +114,21 @@ use std::{env::VarError, io};
#[cfg(feature = "libafl_derive")]
pub use libafl_derive::SerdeAny;

/// We need some sort of "[`String`]" for errors in `no_alloc`...
/// We can only support `'static` without allocator, so let's do that.
#[cfg(not(feature = "alloc"))]
type String = &'static str;

/// We also need a non-allocating format...
/// This one simply returns the `fmt` string.
/// Good enough for simple errors, for anything else, use the `alloc` feature.
#[cfg(not(feature = "alloc"))]
macro_rules! format {
($fmt:literal) => {{
$fmt
}};
}

/// We need fixed names for many parts of this lib.
pub trait Named {
/// Provide the name of this element.
Expand Down Expand Up @@ -276,7 +295,7 @@ impl Error {
}
}

impl fmt::Display for Error {
impl Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Serialize(s, b) => {
Expand Down Expand Up @@ -339,6 +358,7 @@ impl fmt::Display for Error {
}

/// Stringify the postcard serializer error
#[cfg(feature = "alloc")]
impl From<postcard::Error> for Error {
fn from(err: postcard::Error) -> Self {
Self::serialize(format!("{err:?}"))
Expand Down Expand Up @@ -368,39 +388,46 @@ impl From<io::Error> for Error {
}
}

#[cfg(feature = "alloc")]
impl From<FromUtf8Error> for Error {
#[allow(unused_variables)]
fn from(err: FromUtf8Error) -> Self {
Self::unknown(format!("Could not convert byte / utf-8: {err:?}"))
}
}

#[cfg(feature = "std")]
impl From<VarError> for Error {
#[allow(unused_variables)]
fn from(err: VarError) -> Self {
Self::empty(format!("Could not get env var: {err:?}"))
}
}

impl From<ParseIntError> for Error {
#[allow(unused_variables)]
fn from(err: ParseIntError) -> Self {
Self::unknown(format!("Failed to parse Int: {err:?}"))
}
}

impl From<TryFromIntError> for Error {
#[allow(unused_variables)]
fn from(err: TryFromIntError) -> Self {
Self::illegal_state(format!("Expected conversion failed: {err:?}"))
}
}

impl From<TryFromSliceError> for Error {
#[allow(unused_variables)]
fn from(err: TryFromSliceError) -> Self {
Self::illegal_argument(format!("Could not convert slice: {err:?}"))
}
}

#[cfg(windows)]
impl From<windows::core::Error> for Error {
#[allow(unused_variables)]
fn from(err: windows::core::Error) -> Self {
Self::unknown(format!("Windows API error: {err:?}"))
}
Expand All @@ -422,9 +449,12 @@ impl From<pyo3::PyErr> for Error {
}
}

#[cfg(feature = "std")]
#[cfg(all(not(nightly), feature = "std"))]
impl std::error::Error for Error {}

#[cfg(nightly)]
impl core::error::Error for Error {}

/// The purpose of this module is to alleviate imports of many components by adding a glob import.
#[cfg(feature = "prelude")]
pub mod prelude {
Expand All @@ -439,6 +469,7 @@ pub unsafe extern "C" fn external_current_millis() -> u64 {
1000
}

#[cfg(feature = "alloc")]
pub mod anymap;
#[cfg(feature = "std")]
pub mod build_id;
Expand All @@ -454,18 +485,22 @@ pub mod core_affinity;
pub mod cpu;
#[cfg(feature = "std")]
pub mod fs;
#[cfg(feature = "alloc")]
pub mod llmp;
#[cfg(all(feature = "std", unix))]
pub mod minibsod;
pub mod os;
#[cfg(feature = "alloc")]
pub mod ownedref;
pub mod rands;
#[cfg(feature = "alloc")]
pub mod serdeany;
pub mod shmem;
#[cfg(feature = "std")]
pub mod staterestore;
pub mod tuples;

#[cfg(feature = "alloc")]
use alloc::vec::Vec;
use core::{iter::Iterator, ops::AddAssign, time};
#[cfg(feature = "std")]
Expand Down Expand Up @@ -499,6 +534,7 @@ pub trait AsMutSlice {
fn as_mut_slice(&mut self) -> &mut [Self::Entry];
}

#[cfg(feature = "alloc")]
impl<T> AsSlice for Vec<T> {
type Entry = T;

Expand All @@ -507,6 +543,7 @@ impl<T> AsSlice for Vec<T> {
}
}

#[cfg(feature = "alloc")]
impl<T> AsMutSlice for Vec<T> {
type Entry = T;

Expand Down Expand Up @@ -653,6 +690,7 @@ pub fn current_milliseconds() -> u64 {
}

/// Format a `Duration` into a HMS string
#[cfg(feature = "alloc")]
#[must_use]
pub fn format_duration_hms(duration: &time::Duration) -> String {
let secs = duration.as_secs();
Expand Down Expand Up @@ -802,9 +840,9 @@ pub mod bolts_prelude {
pub use super::minibsod::*;
#[cfg(feature = "std")]
pub use super::staterestore::*;
pub use super::{
anymap::*, cpu::*, llmp::*, os::*, ownedref::*, rands::*, serdeany::*, shmem::*, tuples::*,
};
#[cfg(feature = "alloc")]
pub use super::{anymap::*, llmp::*, ownedref::*, rands::*, serdeany::*, shmem::*, tuples::*};
pub use super::{cpu::*, os::*, rands::*};
}

#[cfg(feature = "python")]
Expand Down
Loading

0 comments on commit dfaf06a

Please sign in to comment.