diff --git a/.gitignore b/.gitignore index 33384ff..67bd23a 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,7 @@ Cargo.lock .vscode/ # worktrees -worktrees/ \ No newline at end of file +worktrees/ + +# build folders +**/build \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 2939326..1d8243d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -121,7 +121,7 @@ jsonwebtoken = { version = "9.2.0", optional = true } itertools = { version = "0.12.1", default-features = false, optional = true } serde_yaml = {version = "0.9.16", default-features = false, optional = true } -rand = { version = "0.8.5", default-features = false, optional = true } +rand = { version = "0.8.5", default-features = false, optional = true } [dev-dependencies] anyhow = "1.0.45" diff --git a/README.md b/README.md index be9adb5..b258e9b 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ Regorus is also - *cross-platform* - Written in platform-agnostic Rust. + - *no_std compatible* - Regorus can be used in `no_std` environments too. Most of the builtins are supported. - *current* - We strive to keep Regorus up to date with latest OPA release. Regorus supports `import rego.v1`. - *compliant* - Regorus is mostly compliant with the latest [OPA release v0.64.0](https://github.com/open-policy-agent/opa/releases/tag/v0.64.0). See [OPA Conformance](#opa-conformance) for details. Note that while we behaviorally produce the same results, we don't yet support all the builtins. - *extensible* - Extend the Rego language by implementing custom stateful builtins in Rust. @@ -85,7 +86,7 @@ Regorus is designed with [Confidential Computing](https://confidentialcomputing. it is important to be able to control exactly what is being run. Regorus allows enabling and disabling various components using cargo features. By default all features are enabled. -The default build of regorus example program is 6.4M: +The default build of regorus example program is 6.3M: ```bash $ cargo build -r --example regorus; strip target/release/examples/regorus; ls -lh target/release/examples/regorus -rwxr-xr-x 1 anand staff 6.3M May 11 22:03 target/release/examples/regorus* @@ -108,6 +109,7 @@ Regorus can be used from a variety of languages: - *C*: C binding is generated using [cbindgen](https://github.com/mozilla/cbindgen). [corrosion-rs](https://github.com/corrosion-rs/corrosion) can be used to seamlessly use Regorous in your CMake based projects. See [bindings/c](https://github.com/microsoft/regorus/tree/main/bindings/c). +- *C freestanding*: [bindings/c_no_std](https://github.com/microsoft/regorus/tree/main/bindings/c_no_std) shows how to use Regorus from C environments without a libc. - *C++*: C++ binding is generated using [cbindgen](https://github.com/mozilla/cbindgen). [corrosion-rs](https://github.com/corrosion-rs/corrosion) can be used to seamlessly use Regorous in your CMake based projects. See [bindings/cpp](https://github.com/microsoft/regorus/tree/main/bindings/cpp). diff --git a/bindings/c-nostd/CMakeLists.txt b/bindings/c-nostd/CMakeLists.txt new file mode 100644 index 0000000..1d554be --- /dev/null +++ b/bindings/c-nostd/CMakeLists.txt @@ -0,0 +1,41 @@ +# Copyright (c) Microsoft +# Licensed under the MIT License. + +cmake_minimum_required(VERSION 3.12 FATAL_ERROR) +include(FetchContent) + +FetchContent_Declare( + Corrosion + GIT_REPOSITORY https://github.com/corrosion-rs/corrosion.git + GIT_TAG v0.4 # Optionally specify a commit hash, version tag or branch here +) +FetchContent_MakeAvailable(Corrosion) + +project("regorus-test") + +corrosion_import_crate( + # Path to /bindings/ffi/Cargo.toml + MANIFEST_PATH "../ffi/Cargo.toml" + # Always build regorus in Release mode. + PROFILE "release" + # Only build the "regorus-ffi" crate. + CRATES "regorus-ffi" + + # Turn off std support in regorus-ffi. + NO_DEFAULT_FEATURES + NO_STD + + # custom_allocator allows using a custom memory allocator. + # To use malloc/free remove custom_allocator below. + # Additionally, select specific features in regorus. + # See regorus/opa_no_std + FEATURES "custom_allocator,regorus/semver" + + # Link statically + CRATE_TYPES staticlib FLAGS --crate-type=staticlib + ) + +add_executable(regorus_test_nostd main.c) +# Add path to /bindings/ffi +target_include_directories(regorus_test_nostd PRIVATE "../ffi") +target_link_libraries(regorus_test_nostd regorus-ffi) diff --git a/bindings/c-nostd/main.c b/bindings/c-nostd/main.c new file mode 100644 index 0000000..0ac8804 --- /dev/null +++ b/bindings/c-nostd/main.c @@ -0,0 +1,96 @@ +#include +#include "regorus.h" + + +// Regorus has been built for no_std and cannot access files. +char* file_to_string(const char* file) { + char * buffer = 0; + long length; + FILE * f = fopen (file, "rb"); + + if (f) + { + fseek (f, 0, SEEK_END); + length = ftell (f); + fseek (f, 0, SEEK_SET); + buffer = malloc (length); + if (buffer) + { + fread (buffer, 1, length, f); + } + fclose (f); + } + + return buffer; +} + +// If regorus is built with custom-allocator, then provide implementation. +uint8_t* regorus_aligned_alloc(size_t alignment, size_t size) { + printf("%ld %ld\n", size, alignment); + fflush(stdout); + return aligned_alloc(alignment, size); +} + +void regorus_free(uint8_t* ptr) { + free(ptr); +} + + +int main() { + // Create engine. + RegorusEngine* engine = regorus_engine_new(); + RegorusResult r; + char* buffer = NULL; + + // Load policies. + r = regorus_engine_add_policy(engine, "framework.rego", (buffer = file_to_string("../../../tests/aci/framework.rego"))); + free(buffer); + if (r.status != RegorusStatusOk) + goto error; + regorus_result_drop(r); + + r = regorus_engine_add_policy(engine, "api.rego", (buffer = file_to_string("../../../tests/aci/api.rego"))); + free(buffer); + if (r.status != RegorusStatusOk) + goto error; + regorus_result_drop(r); + + r = regorus_engine_add_policy(engine, "policy.rego", (buffer = file_to_string("../../../tests/aci/policy.rego"))); + free(buffer); + if (r.status != RegorusStatusOk) + goto error; + regorus_result_drop(r); + + // Add data + r = regorus_engine_add_data_json(engine, (buffer = file_to_string("../../../tests/aci/data.json"))); + free(buffer); + if (r.status != RegorusStatusOk) + goto error; + regorus_result_drop(r); + + // Set input + r = regorus_engine_set_input_json(engine, (buffer = file_to_string("../../../tests/aci/input.json"))); + free(buffer); + if (r.status != RegorusStatusOk) + goto error; + regorus_result_drop(r); + + // Eval query + r = regorus_engine_eval_query(engine, "data.framework.mount_overlay=x"); + if (r.status != RegorusStatusOk) + goto error; + + // Print output + printf("%s", r.output); + regorus_result_drop(r); + + + // Free the engine. + regorus_engine_drop(engine); + + return 0; +error: + printf("%s", r.error_message); + + return 1; +} diff --git a/bindings/c/CMakeLists.txt b/bindings/c/CMakeLists.txt index 9cc9222..d0fb422 100644 --- a/bindings/c/CMakeLists.txt +++ b/bindings/c/CMakeLists.txt @@ -18,8 +18,15 @@ corrosion_import_crate( MANIFEST_PATH "../ffi/Cargo.toml" # Always build regorus in Release mode. PROFILE "release" - # Only build the "regorusc" crate. - CRATES "regorus-ffi") + # Only build the "regorus-ffi" crate. + CRATES "regorus-ffi" + + # Select specific features in regorus. + FEATURES "regorus/semver" + + # Link statically + CRATE_TYPES "staticlib" + ) add_executable(regorus_test main.c) # Add path to /bindings/ffi diff --git a/bindings/ffi/Cargo.toml b/bindings/ffi/Cargo.toml index 288b363..419f7a0 100644 --- a/bindings/ffi/Cargo.toml +++ b/bindings/ffi/Cargo.toml @@ -5,13 +5,18 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [lib] -crate-type = ["cdylib"] +crate-type = ["cdylib", "staticlib"] [dependencies] anyhow = "1.0" -regorus = { path = "../.." } +regorus = { path = "../..", default-features = false } serde_json = "1.0.113" +[features] +default = ["std"] +std = ["regorus/std"] +custom_allocator = [] + [build-dependencies] cbindgen = "0.26.0" csbindgen = "1.9.0" diff --git a/bindings/ffi/RegorusFFI.g.cs b/bindings/ffi/RegorusFFI.g.cs index 3b3b12f..84b9ac1 100644 --- a/bindings/ffi/RegorusFFI.g.cs +++ b/bindings/ffi/RegorusFFI.g.cs @@ -85,4 +85,3 @@ namespace RegorusFFI } - \ No newline at end of file diff --git a/bindings/ffi/cbindgen.toml b/bindings/ffi/cbindgen.toml index 6807e21..eeaf7de 100644 --- a/bindings/ffi/cbindgen.toml +++ b/bindings/ffi/cbindgen.toml @@ -137,9 +137,6 @@ bitflags = false - - - ############## Options for How Your Rust library Should Be Parsed ############## [parse] @@ -154,5 +151,5 @@ extra_bindings = [] [parse.expand] crates = [] all_features = false -default_features = true +default_features = false features = [] \ No newline at end of file diff --git a/bindings/ffi/regorus.ffi.hpp b/bindings/ffi/regorus.ffi.hpp index a8b8a64..13a98bd 100644 --- a/bindings/ffi/regorus.ffi.hpp +++ b/bindings/ffi/regorus.ffi.hpp @@ -90,6 +90,10 @@ RegorusResult regorus_engine_set_input_from_json_file(RegorusEngine *engine, con /// * `query`: Rego expression to be evaluate. RegorusResult regorus_engine_eval_query(RegorusEngine *engine, const char *query); +extern uint8_t *regorus_aligned_alloc(uintptr_t alignment, uintptr_t size); + +extern void regorus_free(uint8_t *ptr); + } // extern "C" #endif // REGORUS_FFI_HPP diff --git a/bindings/ffi/regorus.h b/bindings/ffi/regorus.h index 6f80585..db696b9 100644 --- a/bindings/ffi/regorus.h +++ b/bindings/ffi/regorus.h @@ -124,4 +124,8 @@ struct RegorusResult regorus_engine_set_input_from_json_file(struct RegorusEngin */ struct RegorusResult regorus_engine_eval_query(struct RegorusEngine *engine, const char *query); +extern uint8_t *regorus_aligned_alloc(uintptr_t alignment, uintptr_t size); + +extern void regorus_free(uint8_t *ptr); + #endif /* REGORUS_H */ diff --git a/bindings/ffi/src/lib.rs b/bindings/ffi/src/lib.rs index eb4c8ba..1422161 100644 --- a/bindings/ffi/src/lib.rs +++ b/bindings/ffi/src/lib.rs @@ -127,7 +127,6 @@ pub extern "C" fn regorus_engine_drop(engine: *mut RegorusEngine) { /// /// * `path`: A filename to be associated with the policy. /// * `rego`: Rego policy. - #[no_mangle] pub extern "C" fn regorus_engine_add_policy( engine: *mut RegorusEngine, @@ -141,6 +140,7 @@ pub extern "C" fn regorus_engine_add_policy( }()) } +#[cfg(feature = "std")] #[no_mangle] pub extern "C" fn regorus_engine_add_policy_from_file( engine: *mut RegorusEngine, @@ -169,6 +169,7 @@ pub extern "C" fn regorus_engine_add_data_json( }()) } +#[cfg(feature = "std")] #[no_mangle] pub extern "C" fn regorus_engine_add_data_from_json_file( engine: *mut RegorusEngine, @@ -209,6 +210,7 @@ pub extern "C" fn regorus_engine_set_input_json( }()) } +#[cfg(feature = "std")] #[no_mangle] pub extern "C" fn regorus_engine_set_input_from_json_file( engine: *mut RegorusEngine, @@ -246,3 +248,32 @@ pub extern "C" fn regorus_engine_eval_query( Err(e) => to_regorus_result(Err(e)), } } + +#[cfg(feature = "custom_allocator")] +extern "C" { + fn regorus_aligned_alloc(alignment: usize, size: usize) -> *mut u8; + fn regorus_free(ptr: *mut u8); +} + +#[cfg(feature = "custom_allocator")] +mod allocator { + use std::alloc::{GlobalAlloc, Layout}; + + struct RegorusAllocator {} + + unsafe impl GlobalAlloc for RegorusAllocator { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + let size = layout.size(); + let align = layout.align(); + + crate::regorus_aligned_alloc(align, size) + } + + unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) { + crate::regorus_free(ptr) + } + } + + #[global_allocator] + static ALLOCATOR: RegorusAllocator = RegorusAllocator {}; +}