From f740d901aaf3af4eeafd100e0ae9839e786cae72 Mon Sep 17 00:00:00 2001 From: strawberry Date: Wed, 10 Apr 2024 01:19:38 -0400 Subject: [PATCH] fix up build.rs to use CONFIG_STATIC, revamp README Signed-off-by: strawberry --- Cargo.toml | 12 ++-------- README.md | 70 +++++++++++++++++++++++++++++++++++++++++++++++++----- build.rs | 65 ++++++++++++++++++++++++++++---------------------- 3 files changed, 102 insertions(+), 45 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 319b709..a016263 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ version = "0.1.0+12" rust-version = "1.63.0" [features] -default = ["light", "gcc", "static"] +default = ["static", "gcc", "light"] ## compiler clang = [] @@ -27,17 +27,9 @@ gcc = [] ## linking method dynamic = [] - - -# todo: remove this warning -# https://github.com/girlbossceo/hardened_malloc-rs/issues/5 -# IF USING CLANG: -# -# REQUIRES BUILDING YOUR CRATE WITH `RUSTFLAGS='-Clink-arg=-fuse-ld=lld'` -# REQUIRES llvm, llvm-ar, lld, clang, libclang, AND llvm-libs static = [] ## hardened_malloc config -light = [] # "standard" feature is "default.mk" config in hardened_malloc +light = [] standard = [] \ No newline at end of file diff --git a/README.md b/README.md index 55c7674..3cdcc04 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,75 @@ -# hardened_malloc-sys +# hardened_malloc-rs -the sys repo, rust wrapper +Rust wrapper library for GrapheneOS's [hardened_malloc](https://github.com/GrapheneOS/hardened_malloc) that can be integrated as the [global memory allocator](https://doc.rust-lang.org/std/alloc/index.html) in your Rust crate. -to build, just run `cargo build -r` which will build the light variant by default. -if you want default (called `standard` in this crate) variant, do `cargo build -r --features=standard --no-default-features` +### Why? -### TODO: +The default memory allocator apart of your C library (glibc, musl, etc) is still used in your Rust crate unless you build with the other various memory allocators out there such as [jemalloc](https://crates.io/crates/tikv-jemalloc-sys), [mimalloc](https://crates.io/crates/mimalloc), etc. However the majority of memory allocators out there are too performance-focused (jemalloc) or are focused on a balance between security and performance. + +A memory allocator like GrapheneOS's hardened_malloc is purely security focused and is perfect for a security focused usecase, but the light variant makes hardened_malloc significantly suitable for replacing your default C library's malloc while still retaining a lot of the security properties and ends up speeding up the performance of your application. + +Additionally, building hardened_malloc in your binary instead of relying on LD_PRELOAD'ing creates is more secure as it can be used to create position independent code (`-fPIE`/`-fPIC`) and prevents interposition of exported symbols (aka using LD_PRELOAD to drop in your malloc) with `-fno-semantic-interposition`. And it can benefit from optimisations by the compiler and linker that would not be otherwise available from a dynamic shared library. + +https://github.com/GrapheneOS/hardened_malloc?tab=readme-ov-file#individual-applications + +> It can offer slightly better performance when integrated into the C standard library and there are other opportunities for similar hardening within C standard library and dynamic linker implementations. For example, a library region can be implemented to offer similar isolation for dynamic libraries as this allocator offers across different size classes. The intention is that this will be offered as part of hardened variants of the Bionic and musl C standard libraries. + +### Building + +The default features used are "static", "gcc", "light". + +To configure, you MUST build without default features. The list of features to configure are: + +- `static` - creates a static library of libhardened_malloc for static linking your crate +- `dynamic` - creates a dynamically-linked library of libhardened_malloc +- `gcc` - builds hardened_malloc with gcc as `$CC` +- `clang` - builds hardened_malloc with clang as `$CC` +- `light` - builds hardened_malloc with the light variant/config (balance between performance and security) +- `standard` - builds hardened_malloc with the default variant/config (more secure) + +You cannot enable both of the same type of feature at the moment (e.g. cannot enable gcc and clang at the same time). + +### Usage + +In your Cargo.toml's dependencies (example): + +```toml +hardened_malloc-rs = { version = "0.1", features = ["static", "clang", "light"], default-features = false } +``` + +In your crate's main.rs: + +```rs +#[cfg(all(not(target_env = "msvc"), not(target_os = "macos")))] +use hardened_malloc_rs::HardenedMalloc; + +#[cfg(all(not(target_env = "msvc"), not(target_os = "macos")))] +#[global_allocator] +static GLOBAL: HardenedMalloc = HardenedMalloc; +``` + +### Note + +This [requires a fork of hardened_malloc](https://github.com/girlbossceo/hardened_malloc/commits/main/) to skip the LTO linking stage if doing static Clang/LLVM builds. LTO on Clang/LLVM will produce LLVM IR bitcode which is not compatible with GNU linker (`ld`) and `ar` without requiring the top level crate to use LLVM linker (`lld`) across the **entire** dependency graph and to use `llvm-ar`. GCC is unaffected. + +See https://github.com/girlbossceo/hardened_malloc-rs/issues/5 for more details. + +[FatLTO](https://llvm.org/docs/FatLTO.html) using `-ffat-lto-objects` seems like it would solve this compatibility issue, but this is a lld and gold plugin feature only. + +### Minimum Supported Rust Version (MSRV) + +hardened_malloc states the most ancient set of dependencies that can be used to build hardened_malloc is Debian 12 (bookworm), aka the latest stable. Debian 12 has Rust 1.63 in their repos which will be the MSRV of hardened_malloc-rs. + +See https://github.com/GrapheneOS/hardened_malloc?tab=readme-ov-file#dependencies + +##### TODO: + +- [ ] support building this crate as is to output the .so/.a file - [ ] test if this even works - [ ] add support for explicit make config args on top of choosing variant - [ ] make build script better overall - [ ] support C preprocessor macro definitions - [ ] maybe add support for building both variants if both are specified, or dont use a default light variant -- [ ] add support for hardened_malloc `make clean` upon `cargo clean` - [ ] potentially add support for cross-compiling so i can build on apple silicon for linux x86? - [ ] add support for hardened_malloc's tests and our own tests - [ ] add github CI/CD diff --git a/build.rs b/build.rs index 80e8532..c20c74b 100644 --- a/build.rs +++ b/build.rs @@ -53,28 +53,28 @@ fn check_compiler_and_linker(compiler: &'static str, linker: &'static str) -> (& Err(e) => panic!("linker check failed with error: {e}"), } - if linker.contains("lld") { - println!("checking if llvm-ar exists as lld is being used (static build)"); - - let llvm_ar_ret = Command::new("llvm-ar").arg("--version").status(); - - match llvm_ar_ret.map(|status| (status.success(), status.code())) { - Ok((true, _)) => println!("llvm-ar check exited successfully"), - Ok((false, Some(exit_code))) => panic!("llvm-ar check failed with error code {exit_code}"), - Ok((false, None)) => panic!("llvm-ar check exited with no error code, possibly killed by system"), - Err(e) => panic!("llvm-ar check failed with error: {e}"), - } - } - (compiler, linker) } fn main() { + #[cfg(all( + not(feature = "gcc"), + not(feature = "clang"), + not(feature = "static"), + not(feature = "dynamic"), + not(feature = "light"), + not(feature = "standard") + ))] + compile_error!("At least one of each category of feature must be enabled."); + #[cfg(all(feature = "gcc", feature = "clang"))] - compile_error!("gcc OR clang must be enabled, not both."); + compile_error!("gcc OR clang compiler must be enabled, not both."); #[cfg(all(feature = "static", feature = "dynamic"))] - compile_error!("static OR dynamic must be enabled, not both."); + compile_error!("static OR dynamic linking must be enabled, not both."); + + #[cfg(all(feature = "light", feature = "standard"))] + compile_error!("light OR standard variant must be enabled, not both."); println!("cargo:rerun-if-changed=build.rs"); println!("cargo:rerun-if-changed=src/hardened_malloc/"); @@ -91,14 +91,10 @@ fn main() { update_submodules(); } - let (compiler, linker) = if cfg!(feature = "gcc") && cfg!(feature = "dynamic") { + let (compiler, _linker) = if cfg!(feature = "gcc") { check_compiler_and_linker("gcc", "ld") - } else if cfg!(feature = "gcc") && cfg!(feature = "static") { - check_compiler_and_linker("gcc", "gcc-ar") - } else if cfg!(feature = "clang") && cfg!(feature = "dynamic") { - check_compiler_and_linker("clang", "ld") } else { - check_compiler_and_linker("clang", "ld.lld") + check_compiler_and_linker("clang", "ld") }; // "default" is hardened_malloc's default.mk. this crate's feature uses @@ -109,14 +105,25 @@ fn main() { "default" }; - let build_args: Vec = vec![ - format!("VARIANT={}", variant), - format!("V={}", "1"), // verbose (?) - format!("OUT={}", &out_dir), - format!("CC={}", compiler), - ]; + let build_args: Vec = if cfg!(features = "clang") && cfg!(features = "static") { + vec![ + format!("VARIANT={}", variant), + format!("CONFIG_STATIC=true"), // only intended to be used by clang + format!("V={}", "1"), // verbose (?) + format!("OUT={}", &out_dir), + format!("CC={}", compiler), + ] + } else { + vec![ + format!("VARIANT={}", variant), + format!("V={}", "1"), // verbose (?) + format!("OUT={}", &out_dir), + format!("CC={}", compiler), + ] + }; - // TODO: handle support for explicit make flags like N_ARENA=1 and such + // TODO: handle support for explicit make flags like N_ARENA=1 and such (should + // this be crate features on top of the existing variant features/configs?) let mut make_command = Command::new("make"); println!("running {:?} with args {:?}", make_command, build_args); @@ -158,7 +165,7 @@ fn main() { out_dir.clone() + "/new.o", ]; - let mut ar_command = Command::new("llvm-ar"); + let mut ar_command = Command::new("ar"); println!("running {:?} with args {:?}", ar_command, ar_args);