mirror of
https://github.com/microsoft/regorus.git
synced 2026-08-05 02:16:11 +00:00
feat: Detect incorrect multi-threaded use from c based ffi (#499)
Add runtime detection for shared handle misuse wrap the FFI engine handle with parking_lot::RwLock when the new contention_checks feature is enabled, surfacing a clear “handle is already in use” error instead of allowing undefined behavior keep the feature optional so no_std builds or environments that supply their own synchronization can opt out caution users that this guards the handle itself but does not make the engine’s operations globally thread-safe on its own Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
This commit is contained in:
committed by
GitHub
parent
ad8c543fb5
commit
688e6128d4
@@ -9,7 +9,7 @@ extern "C" {
|
||||
|
||||
#[cfg(feature = "custom_allocator")]
|
||||
mod allocator {
|
||||
use std::alloc::{GlobalAlloc, Layout};
|
||||
use core::alloc::{GlobalAlloc, Layout};
|
||||
|
||||
struct RegorusAllocator {}
|
||||
|
||||
|
||||
+25
-22
@@ -1,9 +1,12 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
use alloc::ffi::CString;
|
||||
use alloc::format;
|
||||
use alloc::string::{String, ToString};
|
||||
use anyhow::{anyhow, bail, Result};
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::os::raw::{c_char, c_longlong};
|
||||
use core::ffi::{c_char, c_longlong, c_void, CStr};
|
||||
use core::ptr;
|
||||
|
||||
/// Status of a call on `RegorusEngine`.
|
||||
#[repr(C)]
|
||||
@@ -74,7 +77,7 @@ pub struct RegorusResult {
|
||||
|
||||
/// Pointer value.
|
||||
/// Valid when data_type is Pointer.
|
||||
pub(crate) pointer_value: *mut std::os::raw::c_void,
|
||||
pub(crate) pointer_value: *mut c_void,
|
||||
|
||||
/// Errors produced by the call.
|
||||
/// Owned by Rust.
|
||||
@@ -87,11 +90,11 @@ impl RegorusResult {
|
||||
Self {
|
||||
status: RegorusStatus::Ok,
|
||||
data_type: RegorusDataType::None,
|
||||
output: std::ptr::null_mut(),
|
||||
output: ptr::null_mut(),
|
||||
bool_value: false,
|
||||
int_value: 0,
|
||||
pointer_value: std::ptr::null_mut(),
|
||||
error_message: std::ptr::null_mut(),
|
||||
pointer_value: ptr::null_mut(),
|
||||
error_message: ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,8 +106,8 @@ impl RegorusResult {
|
||||
output: to_c_str(output),
|
||||
bool_value: false,
|
||||
int_value: 0,
|
||||
pointer_value: std::ptr::null_mut(),
|
||||
error_message: std::ptr::null_mut(),
|
||||
pointer_value: ptr::null_mut(),
|
||||
error_message: ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,11 +117,11 @@ impl RegorusResult {
|
||||
Self {
|
||||
status: RegorusStatus::Ok,
|
||||
data_type: RegorusDataType::Boolean,
|
||||
output: std::ptr::null_mut(),
|
||||
output: ptr::null_mut(),
|
||||
bool_value: value,
|
||||
int_value: 0,
|
||||
pointer_value: std::ptr::null_mut(),
|
||||
error_message: std::ptr::null_mut(),
|
||||
pointer_value: ptr::null_mut(),
|
||||
error_message: ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,24 +131,24 @@ impl RegorusResult {
|
||||
Self {
|
||||
status: RegorusStatus::Ok,
|
||||
data_type: RegorusDataType::Integer,
|
||||
output: std::ptr::null_mut(),
|
||||
output: ptr::null_mut(),
|
||||
bool_value: false,
|
||||
int_value: value as c_longlong,
|
||||
pointer_value: std::ptr::null_mut(),
|
||||
error_message: std::ptr::null_mut(),
|
||||
pointer_value: ptr::null_mut(),
|
||||
error_message: ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a successful result with pointer value.
|
||||
pub(crate) fn ok_pointer(pointer: *mut std::os::raw::c_void) -> Self {
|
||||
pub(crate) fn ok_pointer(pointer: *mut c_void) -> Self {
|
||||
Self {
|
||||
status: RegorusStatus::Ok,
|
||||
data_type: RegorusDataType::Pointer,
|
||||
output: std::ptr::null_mut(),
|
||||
output: ptr::null_mut(),
|
||||
bool_value: false,
|
||||
int_value: 0,
|
||||
pointer_value: pointer,
|
||||
error_message: std::ptr::null_mut(),
|
||||
error_message: ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,11 +157,11 @@ impl RegorusResult {
|
||||
Self {
|
||||
status,
|
||||
data_type: RegorusDataType::None,
|
||||
output: std::ptr::null_mut(),
|
||||
output: ptr::null_mut(),
|
||||
bool_value: false,
|
||||
int_value: 0,
|
||||
pointer_value: std::ptr::null_mut(),
|
||||
error_message: std::ptr::null_mut(),
|
||||
pointer_value: ptr::null_mut(),
|
||||
error_message: ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,10 +170,10 @@ impl RegorusResult {
|
||||
Self {
|
||||
status,
|
||||
data_type: RegorusDataType::None,
|
||||
output: std::ptr::null_mut(),
|
||||
output: ptr::null_mut(),
|
||||
bool_value: false,
|
||||
int_value: 0,
|
||||
pointer_value: std::ptr::null_mut(),
|
||||
pointer_value: ptr::null_mut(),
|
||||
error_message: to_c_str(message),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,13 +2,15 @@
|
||||
// Licensed under the MIT License.
|
||||
use crate::common::{from_c_str, RegorusResult, RegorusStatus};
|
||||
use crate::compiled_policy::RegorusCompiledPolicy;
|
||||
use alloc::boxed::Box;
|
||||
use alloc::format;
|
||||
use alloc::vec::Vec;
|
||||
use core::ffi::{c_char, c_void};
|
||||
use regorus::{compile_policy_with_entrypoint, PolicyModule, Value};
|
||||
|
||||
#[cfg(feature = "azure_policy")]
|
||||
use regorus::compile_policy_for_target;
|
||||
|
||||
use std::os::raw::c_char;
|
||||
|
||||
/// FFI wrapper for PolicyModule struct.
|
||||
#[repr(C)]
|
||||
pub struct RegorusPolicyModule {
|
||||
@@ -83,7 +85,7 @@ pub extern "C" fn regorus_compile_policy_with_entrypoint(
|
||||
Ok(compiled_policy) => {
|
||||
let wrapped_policy = RegorusCompiledPolicy { compiled_policy };
|
||||
let boxed_policy = Box::new(wrapped_policy);
|
||||
RegorusResult::ok_pointer(Box::into_raw(boxed_policy) as *mut std::os::raw::c_void)
|
||||
RegorusResult::ok_pointer(Box::into_raw(boxed_policy) as *mut c_void)
|
||||
}
|
||||
Err(e) => RegorusResult::err_with_message(
|
||||
RegorusStatus::CompilationFailed,
|
||||
@@ -152,7 +154,7 @@ pub extern "C" fn regorus_compile_policy_for_target(
|
||||
Ok(compiled_policy) => {
|
||||
let wrapped_policy = RegorusCompiledPolicy { compiled_policy };
|
||||
let boxed_policy = Box::new(wrapped_policy);
|
||||
RegorusResult::ok_pointer(Box::into_raw(boxed_policy) as *mut std::os::raw::c_void)
|
||||
RegorusResult::ok_pointer(Box::into_raw(boxed_policy) as *mut c_void)
|
||||
}
|
||||
Err(e) => RegorusResult::err_with_message(
|
||||
RegorusStatus::CompilationFailed,
|
||||
@@ -184,7 +186,7 @@ fn convert_c_modules_to_rust(
|
||||
let id = match from_c_str(module_ref.id) {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
eprintln!("Invalid module ID at index {}: {}", i, e);
|
||||
report_module_error(i, "module ID", &e);
|
||||
return Err(RegorusStatus::InvalidModuleId);
|
||||
}
|
||||
};
|
||||
@@ -192,7 +194,7 @@ fn convert_c_modules_to_rust(
|
||||
let content = match from_c_str(module_ref.content) {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
eprintln!("Invalid module content at index {}: {}", i, e);
|
||||
report_module_error(i, "module content", &e);
|
||||
return Err(RegorusStatus::InvalidPolicy);
|
||||
}
|
||||
};
|
||||
@@ -206,3 +208,11 @@ fn convert_c_modules_to_rust(
|
||||
|
||||
Ok(policy_modules)
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
fn report_module_error(index: usize, kind: &str, err: &anyhow::Error) {
|
||||
eprintln!("Invalid {} at index {}: {}", kind, index, err);
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
fn report_module_error(_index: usize, _kind: &str, _err: &anyhow::Error) {}
|
||||
|
||||
@@ -2,8 +2,11 @@
|
||||
// Licensed under the MIT License.
|
||||
|
||||
use crate::common::*;
|
||||
use alloc::boxed::Box;
|
||||
use alloc::string::String;
|
||||
use anyhow::Result;
|
||||
use std::os::raw::c_char;
|
||||
use core::ffi::c_char;
|
||||
use core::ptr;
|
||||
|
||||
/// Wrapper for `regorus::CompiledPolicy`.
|
||||
#[derive(Clone)]
|
||||
@@ -16,7 +19,7 @@ pub struct RegorusCompiledPolicy {
|
||||
pub extern "C" fn regorus_compiled_policy_drop(compiled_policy: *mut RegorusCompiledPolicy) {
|
||||
if let Ok(cp) = to_ref(compiled_policy) {
|
||||
unsafe {
|
||||
let _ = Box::from_raw(std::ptr::from_mut(cp));
|
||||
let _ = Box::from_raw(ptr::from_mut(cp));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+160
-72
@@ -5,13 +5,66 @@ use crate::common::{
|
||||
from_c_str, to_ref, to_regorus_result, to_regorus_string_result, RegorusResult, RegorusStatus,
|
||||
};
|
||||
use crate::compiled_policy::RegorusCompiledPolicy;
|
||||
use anyhow::Result;
|
||||
use std::os::raw::c_char;
|
||||
use crate::lock::{new_handle, read, try_read, try_write, Handle, ReadGuard, WriteGuard};
|
||||
use alloc::boxed::Box;
|
||||
use alloc::format;
|
||||
use alloc::string::String;
|
||||
use anyhow::{anyhow, Result};
|
||||
use core::ffi::{c_char, c_void};
|
||||
use core::ptr;
|
||||
|
||||
/// Wrapper for `regorus::Engine`.
|
||||
#[derive(Clone)]
|
||||
pub struct RegorusEngine {
|
||||
engine: ::regorus::Engine,
|
||||
engine: Handle<::regorus::Engine>,
|
||||
}
|
||||
|
||||
impl RegorusEngine {
|
||||
fn new(engine: ::regorus::Engine) -> Self {
|
||||
Self {
|
||||
engine: new_handle(engine),
|
||||
}
|
||||
}
|
||||
|
||||
fn contention_error() -> anyhow::Error {
|
||||
anyhow!(
|
||||
"regorus engine handle is already in use; clone the engine before sharing across threads"
|
||||
)
|
||||
}
|
||||
|
||||
fn try_write(&self) -> Result<WriteGuard<'_, ::regorus::Engine>> {
|
||||
try_write(&self.engine).ok_or_else(Self::contention_error)
|
||||
}
|
||||
|
||||
fn try_read(&self) -> Result<ReadGuard<'_, ::regorus::Engine>> {
|
||||
try_read(&self.engine).ok_or_else(Self::contention_error)
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for RegorusEngine {
|
||||
fn clone(&self) -> Self {
|
||||
let guard = read(&self.engine);
|
||||
Self::new((*guard).clone())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(test, feature = "contention_checks", feature = "std"))]
|
||||
mod tests {
|
||||
use super::RegorusEngine;
|
||||
|
||||
#[test]
|
||||
fn detects_handle_contention() {
|
||||
let engine = RegorusEngine::new(::regorus::Engine::new());
|
||||
|
||||
let _first_guard = engine.try_write().expect("initial lock should succeed");
|
||||
let err = engine
|
||||
.try_write()
|
||||
.expect_err("contention detection must reject the second lock");
|
||||
|
||||
assert!(
|
||||
err.to_string().contains("engine handle is already in use"),
|
||||
"unexpected error message: {err}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -25,7 +78,7 @@ pub extern "C" fn regorus_engine_new() -> *mut RegorusEngine {
|
||||
// instead of raising errors in certain failure scenarios.
|
||||
engine.set_strict_builtin_errors(false);
|
||||
|
||||
Box::into_raw(Box::new(RegorusEngine { engine }))
|
||||
Box::into_raw(Box::new(RegorusEngine::new(engine)))
|
||||
}
|
||||
|
||||
/// Clone a [`RegorusEngine`]
|
||||
@@ -37,7 +90,7 @@ pub extern "C" fn regorus_engine_new() -> *mut RegorusEngine {
|
||||
pub extern "C" fn regorus_engine_clone(engine: *mut RegorusEngine) -> *mut RegorusEngine {
|
||||
match to_ref(engine) {
|
||||
Ok(e) => Box::into_raw(Box::new(e.clone())),
|
||||
_ => std::ptr::null_mut(),
|
||||
_ => ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +98,7 @@ pub extern "C" fn regorus_engine_clone(engine: *mut RegorusEngine) -> *mut Regor
|
||||
pub extern "C" fn regorus_engine_drop(engine: *mut RegorusEngine) {
|
||||
if let Ok(e) = to_ref(engine) {
|
||||
unsafe {
|
||||
let _ = Box::from_raw(std::ptr::from_mut(e));
|
||||
let _ = Box::from_raw(ptr::from_mut(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -64,9 +117,9 @@ pub extern "C" fn regorus_engine_add_policy(
|
||||
rego: *const c_char,
|
||||
) -> RegorusResult {
|
||||
to_regorus_string_result(|| -> Result<String> {
|
||||
to_ref(engine)?
|
||||
.engine
|
||||
.add_policy(from_c_str(path)?, from_c_str(rego)?)
|
||||
let engine = to_ref(engine)?;
|
||||
let mut guard = engine.try_write()?;
|
||||
guard.add_policy(from_c_str(path)?, from_c_str(rego)?)
|
||||
}())
|
||||
}
|
||||
|
||||
@@ -77,9 +130,9 @@ pub extern "C" fn regorus_engine_add_policy_from_file(
|
||||
path: *const c_char,
|
||||
) -> RegorusResult {
|
||||
to_regorus_string_result(|| -> Result<String> {
|
||||
to_ref(engine)?
|
||||
.engine
|
||||
.add_policy_from_file(from_c_str(path)?)
|
||||
let engine = to_ref(engine)?;
|
||||
let mut guard = engine.try_write()?;
|
||||
guard.add_policy_from_file(from_c_str(path)?)
|
||||
}())
|
||||
}
|
||||
|
||||
@@ -93,9 +146,9 @@ pub extern "C" fn regorus_engine_add_data_json(
|
||||
data: *const c_char,
|
||||
) -> RegorusResult {
|
||||
to_regorus_result(|| -> Result<()> {
|
||||
to_ref(engine)?
|
||||
.engine
|
||||
.add_data(regorus::Value::from_json_str(&from_c_str(data)?)?)
|
||||
let engine = to_ref(engine)?;
|
||||
let mut guard = engine.try_write()?;
|
||||
guard.add_data(regorus::Value::from_json_str(&from_c_str(data)?)?)
|
||||
}())
|
||||
}
|
||||
|
||||
@@ -105,8 +158,9 @@ pub extern "C" fn regorus_engine_add_data_json(
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_engine_get_packages(engine: *mut RegorusEngine) -> RegorusResult {
|
||||
to_regorus_string_result(|| -> Result<String> {
|
||||
serde_json::to_string_pretty(&to_ref(engine)?.engine.get_packages()?)
|
||||
.map_err(anyhow::Error::msg)
|
||||
let engine = to_ref(engine)?;
|
||||
let guard = engine.try_read()?;
|
||||
serde_json::to_string_pretty(&guard.get_packages()?).map_err(anyhow::Error::msg)
|
||||
}())
|
||||
}
|
||||
|
||||
@@ -116,7 +170,9 @@ pub extern "C" fn regorus_engine_get_packages(engine: *mut RegorusEngine) -> Reg
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_engine_get_policies(engine: *mut RegorusEngine) -> RegorusResult {
|
||||
to_regorus_string_result(|| -> Result<String> {
|
||||
to_ref(engine)?.engine.get_policies_as_json()
|
||||
let engine = to_ref(engine)?;
|
||||
let guard = engine.try_read()?;
|
||||
guard.get_policies_as_json()
|
||||
}())
|
||||
}
|
||||
|
||||
@@ -127,9 +183,9 @@ pub extern "C" fn regorus_engine_add_data_from_json_file(
|
||||
path: *const c_char,
|
||||
) -> RegorusResult {
|
||||
to_regorus_result(|| -> Result<()> {
|
||||
to_ref(engine)?
|
||||
.engine
|
||||
.add_data(regorus::Value::from_json_file(from_c_str(path)?)?)
|
||||
let engine = to_ref(engine)?;
|
||||
let mut guard = engine.try_write()?;
|
||||
guard.add_data(regorus::Value::from_json_file(from_c_str(path)?)?)
|
||||
}())
|
||||
}
|
||||
|
||||
@@ -139,7 +195,9 @@ pub extern "C" fn regorus_engine_add_data_from_json_file(
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_engine_clear_data(engine: *mut RegorusEngine) -> RegorusResult {
|
||||
to_regorus_result(|| -> Result<()> {
|
||||
to_ref(engine)?.engine.clear_data();
|
||||
let engine = to_ref(engine)?;
|
||||
let mut guard = engine.try_write()?;
|
||||
guard.clear_data();
|
||||
Ok(())
|
||||
}())
|
||||
}
|
||||
@@ -154,9 +212,9 @@ pub extern "C" fn regorus_engine_set_input_json(
|
||||
input: *const c_char,
|
||||
) -> RegorusResult {
|
||||
to_regorus_result(|| -> Result<()> {
|
||||
to_ref(engine)?
|
||||
.engine
|
||||
.set_input(regorus::Value::from_json_str(&from_c_str(input)?)?);
|
||||
let engine = to_ref(engine)?;
|
||||
let mut guard = engine.try_write()?;
|
||||
guard.set_input(regorus::Value::from_json_str(&from_c_str(input)?)?);
|
||||
Ok(())
|
||||
}())
|
||||
}
|
||||
@@ -168,9 +226,9 @@ pub extern "C" fn regorus_engine_set_input_from_json_file(
|
||||
path: *const c_char,
|
||||
) -> RegorusResult {
|
||||
to_regorus_result(|| -> Result<()> {
|
||||
to_ref(engine)?
|
||||
.engine
|
||||
.set_input(regorus::Value::from_json_file(from_c_str(path)?)?);
|
||||
let engine = to_ref(engine)?;
|
||||
let mut guard = engine.try_write()?;
|
||||
guard.set_input(regorus::Value::from_json_file(from_c_str(path)?)?);
|
||||
Ok(())
|
||||
}())
|
||||
}
|
||||
@@ -185,9 +243,9 @@ pub extern "C" fn regorus_engine_eval_query(
|
||||
query: *const c_char,
|
||||
) -> RegorusResult {
|
||||
let output = || -> Result<String> {
|
||||
let results = to_ref(engine)?
|
||||
.engine
|
||||
.eval_query(from_c_str(query)?, false)?;
|
||||
let engine = to_ref(engine)?;
|
||||
let mut guard = engine.try_write()?;
|
||||
let results = guard.eval_query(from_c_str(query)?, false)?;
|
||||
Ok(serde_json::to_string_pretty(&results)?)
|
||||
}();
|
||||
match output {
|
||||
@@ -206,10 +264,9 @@ pub extern "C" fn regorus_engine_eval_rule(
|
||||
rule: *const c_char,
|
||||
) -> RegorusResult {
|
||||
let output = || -> Result<String> {
|
||||
to_ref(engine)?
|
||||
.engine
|
||||
.eval_rule(from_c_str(rule)?)?
|
||||
.to_json_str()
|
||||
let engine = to_ref(engine)?;
|
||||
let mut guard = engine.try_write()?;
|
||||
guard.eval_rule(from_c_str(rule)?)?.to_json_str()
|
||||
}();
|
||||
match output {
|
||||
Ok(out) => RegorusResult::ok_string(out),
|
||||
@@ -228,7 +285,9 @@ pub extern "C" fn regorus_engine_set_enable_coverage(
|
||||
enable: bool,
|
||||
) -> RegorusResult {
|
||||
to_regorus_result(|| -> Result<()> {
|
||||
to_ref(engine)?.engine.set_enable_coverage(enable);
|
||||
let engine = to_ref(engine)?;
|
||||
let mut guard = engine.try_write()?;
|
||||
guard.set_enable_coverage(enable);
|
||||
Ok(())
|
||||
}())
|
||||
}
|
||||
@@ -240,9 +299,9 @@ pub extern "C" fn regorus_engine_set_enable_coverage(
|
||||
#[cfg(feature = "coverage")]
|
||||
pub extern "C" fn regorus_engine_get_coverage_report(engine: *mut RegorusEngine) -> RegorusResult {
|
||||
let output = || -> Result<String> {
|
||||
Ok(serde_json::to_string_pretty(
|
||||
&to_ref(engine)?.engine.get_coverage_report()?,
|
||||
)?)
|
||||
let engine = to_ref(engine)?;
|
||||
let guard = engine.try_read()?;
|
||||
Ok(serde_json::to_string_pretty(&guard.get_coverage_report()?)?)
|
||||
}();
|
||||
match output {
|
||||
Ok(out) => RegorusResult::ok_string(out),
|
||||
@@ -260,7 +319,9 @@ pub extern "C" fn regorus_engine_set_strict_builtin_errors(
|
||||
strict: bool,
|
||||
) -> RegorusResult {
|
||||
to_regorus_result(|| -> Result<()> {
|
||||
to_ref(engine)?.engine.set_strict_builtin_errors(strict);
|
||||
let engine = to_ref(engine)?;
|
||||
let mut guard = engine.try_write()?;
|
||||
guard.set_strict_builtin_errors(strict);
|
||||
Ok(())
|
||||
}())
|
||||
}
|
||||
@@ -274,10 +335,9 @@ pub extern "C" fn regorus_engine_get_coverage_report_pretty(
|
||||
engine: *mut RegorusEngine,
|
||||
) -> RegorusResult {
|
||||
let output = || -> Result<String> {
|
||||
to_ref(engine)?
|
||||
.engine
|
||||
.get_coverage_report()?
|
||||
.to_string_pretty()
|
||||
let engine = to_ref(engine)?;
|
||||
let guard = engine.try_read()?;
|
||||
guard.get_coverage_report()?.to_string_pretty()
|
||||
}();
|
||||
match output {
|
||||
Ok(out) => RegorusResult::ok_string(out),
|
||||
@@ -292,7 +352,9 @@ pub extern "C" fn regorus_engine_get_coverage_report_pretty(
|
||||
#[cfg(feature = "coverage")]
|
||||
pub extern "C" fn regorus_engine_clear_coverage_data(engine: *mut RegorusEngine) -> RegorusResult {
|
||||
to_regorus_result(|| -> Result<()> {
|
||||
to_ref(engine)?.engine.clear_coverage_data();
|
||||
let engine = to_ref(engine)?;
|
||||
let mut guard = engine.try_write()?;
|
||||
guard.clear_coverage_data();
|
||||
Ok(())
|
||||
}())
|
||||
}
|
||||
@@ -307,7 +369,9 @@ pub extern "C" fn regorus_engine_set_gather_prints(
|
||||
enable: bool,
|
||||
) -> RegorusResult {
|
||||
to_regorus_result(|| -> Result<()> {
|
||||
to_ref(engine)?.engine.set_gather_prints(enable);
|
||||
let engine = to_ref(engine)?;
|
||||
let mut guard = engine.try_write()?;
|
||||
guard.set_gather_prints(enable);
|
||||
Ok(())
|
||||
}())
|
||||
}
|
||||
@@ -318,9 +382,9 @@ pub extern "C" fn regorus_engine_set_gather_prints(
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_engine_take_prints(engine: *mut RegorusEngine) -> RegorusResult {
|
||||
let output = || -> Result<String> {
|
||||
Ok(serde_json::to_string_pretty(
|
||||
&to_ref(engine)?.engine.take_prints()?,
|
||||
)?)
|
||||
let engine = to_ref(engine)?;
|
||||
let mut guard = engine.try_write()?;
|
||||
Ok(serde_json::to_string_pretty(&guard.take_prints()?)?)
|
||||
}();
|
||||
match output {
|
||||
Ok(out) => RegorusResult::ok_string(out),
|
||||
@@ -334,7 +398,11 @@ pub extern "C" fn regorus_engine_take_prints(engine: *mut RegorusEngine) -> Rego
|
||||
#[no_mangle]
|
||||
#[cfg(feature = "ast")]
|
||||
pub extern "C" fn regorus_engine_get_ast_as_json(engine: *mut RegorusEngine) -> RegorusResult {
|
||||
let output = || -> Result<String> { to_ref(engine)?.engine.get_ast_as_json() }();
|
||||
let output = || -> Result<String> {
|
||||
let engine = to_ref(engine)?;
|
||||
let guard = engine.try_read()?;
|
||||
guard.get_ast_as_json()
|
||||
}();
|
||||
match output {
|
||||
Ok(out) => RegorusResult::ok_string(out),
|
||||
Err(e) => to_regorus_result(Err(e)),
|
||||
@@ -350,8 +418,9 @@ pub extern "C" fn regorus_engine_get_policy_package_names(
|
||||
engine: *mut RegorusEngine,
|
||||
) -> RegorusResult {
|
||||
let output = || -> Result<String> {
|
||||
serde_json::to_string_pretty(&to_ref(engine)?.engine.get_policy_package_names()?)
|
||||
.map_err(anyhow::Error::msg)
|
||||
let engine = to_ref(engine)?;
|
||||
let guard = engine.try_read()?;
|
||||
serde_json::to_string_pretty(&guard.get_policy_package_names()?).map_err(anyhow::Error::msg)
|
||||
}();
|
||||
match output {
|
||||
Ok(out) => RegorusResult::ok_string(out),
|
||||
@@ -368,8 +437,9 @@ pub extern "C" fn regorus_engine_get_policy_parameters(
|
||||
engine: *mut RegorusEngine,
|
||||
) -> RegorusResult {
|
||||
let output = || -> Result<String> {
|
||||
serde_json::to_string_pretty(&to_ref(engine)?.engine.get_policy_parameters()?)
|
||||
.map_err(anyhow::Error::msg)
|
||||
let engine = to_ref(engine)?;
|
||||
let guard = engine.try_read()?;
|
||||
serde_json::to_string_pretty(&guard.get_policy_parameters()?).map_err(anyhow::Error::msg)
|
||||
}();
|
||||
match output {
|
||||
Ok(out) => RegorusResult::ok_string(out),
|
||||
@@ -386,7 +456,9 @@ pub extern "C" fn regorus_engine_set_rego_v0(
|
||||
enable: bool,
|
||||
) -> RegorusResult {
|
||||
let output = || -> Result<()> {
|
||||
to_ref(engine)?.engine.set_rego_v0(enable);
|
||||
let engine = to_ref(engine)?;
|
||||
let mut guard = engine.try_write()?;
|
||||
guard.set_rego_v0(enable);
|
||||
Ok(())
|
||||
}();
|
||||
match output {
|
||||
@@ -404,21 +476,35 @@ pub extern "C" fn regorus_engine_set_rego_v0(
|
||||
#[no_mangle]
|
||||
#[cfg(feature = "azure_policy")]
|
||||
pub extern "C" fn regorus_engine_compile_for_target(engine: *mut RegorusEngine) -> RegorusResult {
|
||||
match to_ref(engine) {
|
||||
Ok(e) => match e.engine.compile_for_target() {
|
||||
Ok(compiled_policy) => {
|
||||
let wrapped_policy = RegorusCompiledPolicy { compiled_policy };
|
||||
let boxed_policy = Box::new(wrapped_policy);
|
||||
RegorusResult::ok_pointer(Box::into_raw(boxed_policy) as *mut std::os::raw::c_void)
|
||||
}
|
||||
Err(e) => RegorusResult::err_with_message(
|
||||
RegorusStatus::CompilationFailed,
|
||||
format!("Failed to compile for target: {e}"),
|
||||
),
|
||||
},
|
||||
let engine = match to_ref(engine) {
|
||||
Ok(engine) => engine,
|
||||
Err(e) => {
|
||||
return RegorusResult::err_with_message(
|
||||
RegorusStatus::InvalidArgument,
|
||||
format!("Failed to get engine reference: {e}"),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
let mut guard = match engine.try_write() {
|
||||
Ok(guard) => guard,
|
||||
Err(e) => {
|
||||
return RegorusResult::err_with_message(
|
||||
RegorusStatus::Error,
|
||||
format!("Failed to lock engine: {e}"),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
match guard.compile_for_target() {
|
||||
Ok(compiled_policy) => {
|
||||
let wrapped_policy = RegorusCompiledPolicy { compiled_policy };
|
||||
let boxed_policy = Box::new(wrapped_policy);
|
||||
RegorusResult::ok_pointer(Box::into_raw(boxed_policy) as *mut c_void)
|
||||
}
|
||||
Err(e) => RegorusResult::err_with_message(
|
||||
RegorusStatus::InvalidArgument,
|
||||
format!("Failed to get engine reference: {e}"),
|
||||
RegorusStatus::CompilationFailed,
|
||||
format!("Failed to compile for target: {e}"),
|
||||
),
|
||||
}
|
||||
}
|
||||
@@ -437,14 +523,16 @@ pub extern "C" fn regorus_engine_compile_with_entrypoint(
|
||||
let result = || -> Result<RegorusCompiledPolicy> {
|
||||
let rule_str = from_c_str(rule)?;
|
||||
let rule_rc: regorus::Rc<str> = rule_str.into();
|
||||
let compiled_policy = to_ref(engine)?.engine.compile_with_entrypoint(&rule_rc)?;
|
||||
let engine = to_ref(engine)?;
|
||||
let mut guard = engine.try_write()?;
|
||||
let compiled_policy = guard.compile_with_entrypoint(&rule_rc)?;
|
||||
Ok(RegorusCompiledPolicy { compiled_policy })
|
||||
}();
|
||||
|
||||
match result {
|
||||
Ok(wrapped_policy) => {
|
||||
let boxed_policy = Box::new(wrapped_policy);
|
||||
RegorusResult::ok_pointer(Box::into_raw(boxed_policy) as *mut std::os::raw::c_void)
|
||||
RegorusResult::ok_pointer(Box::into_raw(boxed_policy) as *mut c_void)
|
||||
}
|
||||
Err(e) => RegorusResult::err_with_message(
|
||||
RegorusStatus::CompilationFailed,
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
mod allocator;
|
||||
mod common;
|
||||
mod compile;
|
||||
mod compiled_policy;
|
||||
mod effect_registry;
|
||||
mod engine;
|
||||
mod lock;
|
||||
mod schema_registry;
|
||||
mod target_registry;
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
//! Abstractions over synchronization primitives used by the FFI layer.
|
||||
//!
|
||||
//! For `std` builds we rely on `parking_lot::RwLock` so we can detect
|
||||
//! contention across threads. For `no_std` builds we fall back to
|
||||
//! `RefCell`, which still lets us detect aliasing within a single thread.
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
extern crate alloc;
|
||||
|
||||
#[cfg(all(feature = "std", feature = "contention_checks"))]
|
||||
mod locking {
|
||||
use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||
use std::sync::Arc;
|
||||
|
||||
pub(crate) type Handle<T> = Arc<RwLock<T>>;
|
||||
pub(crate) type ReadGuard<'a, T> = RwLockReadGuard<'a, T>;
|
||||
pub(crate) type WriteGuard<'a, T> = RwLockWriteGuard<'a, T>;
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn new_handle<T>(value: T) -> Handle<T> {
|
||||
Arc::new(RwLock::new(value))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn try_write<'a, T>(handle: &'a Handle<T>) -> Option<WriteGuard<'a, T>> {
|
||||
handle.try_write()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn try_read<'a, T>(handle: &'a Handle<T>) -> Option<ReadGuard<'a, T>> {
|
||||
handle.try_read()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn read<'a, T>(handle: &'a Handle<T>) -> ReadGuard<'a, T> {
|
||||
handle.read()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "std", not(feature = "contention_checks")))]
|
||||
mod locking {
|
||||
use std::cell::{Ref, RefCell, RefMut};
|
||||
use std::rc::Rc;
|
||||
|
||||
pub(crate) type Handle<T> = Rc<RefCell<T>>;
|
||||
pub(crate) type ReadGuard<'a, T> = Ref<'a, T>;
|
||||
pub(crate) type WriteGuard<'a, T> = RefMut<'a, T>;
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn new_handle<T>(value: T) -> Handle<T> {
|
||||
Rc::new(RefCell::new(value))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn try_write<'a, T>(handle: &'a Handle<T>) -> Option<WriteGuard<'a, T>> {
|
||||
handle.try_borrow_mut().ok()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn try_read<'a, T>(handle: &'a Handle<T>) -> Option<ReadGuard<'a, T>> {
|
||||
handle.try_borrow().ok()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn read<'a, T>(handle: &'a Handle<T>) -> ReadGuard<'a, T> {
|
||||
handle.borrow()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
mod locking {
|
||||
use alloc::rc::Rc;
|
||||
use core::cell::{Ref, RefCell, RefMut};
|
||||
|
||||
pub(crate) type Handle<T> = Rc<RefCell<T>>;
|
||||
pub(crate) type ReadGuard<'a, T> = Ref<'a, T>;
|
||||
pub(crate) type WriteGuard<'a, T> = RefMut<'a, T>;
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn new_handle<T>(value: T) -> Handle<T> {
|
||||
Rc::new(RefCell::new(value))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn try_write<'a, T>(handle: &'a Handle<T>) -> Option<WriteGuard<'a, T>> {
|
||||
handle.try_borrow_mut().ok()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn try_read<'a, T>(handle: &'a Handle<T>) -> Option<ReadGuard<'a, T>> {
|
||||
handle.try_borrow().ok()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn read<'a, T>(handle: &'a Handle<T>) -> ReadGuard<'a, T> {
|
||||
handle.borrow()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) use locking::{new_handle, read, try_read, try_write, Handle, ReadGuard, WriteGuard};
|
||||
Reference in New Issue
Block a user