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:
Anand Krishnamoorthi
2025-11-17 14:21:13 -06:00
committed by GitHub
parent ad8c543fb5
commit 688e6128d4
16 changed files with 638 additions and 695 deletions
+1 -1
View File
@@ -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
View File
@@ -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),
}
}
+16 -6
View File
@@ -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) {}
+5 -2
View File
@@ -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
View File
@@ -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,
+5
View File
@@ -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;
+103
View File
@@ -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};