mirror of
https://github.com/microsoft/regorus.git
synced 2026-08-05 02:16:11 +00:00
feat!: add cooperative execution-time limits across engine, VM, and binding (#539)
- Introduce ExecutionTimer/ExecutionTimerConfig to allow limiting evaluating time. - To amortize time checking costs, checking interval can be configured via the notion of work units - A global fallback time limit can be set to universally limit all evaluation in addition to engine level limit setting. - Implement limnits in interpreter and RVM. In RVM, also handle suspend/resume so that time during pause is not counted. - Add engine-level APIs to set/clear per-engine timer configuration and apply global fallback defaults. - Surface execution-time limits through FFI and C# bindings - Add C# tests and example usage to validate engine overrides, global fallback behavior, and compiled policy enforcement. - Expand docs for execution-time limit - Add interpreter YAML cases and VM unit tests for time-limit behavior and deterministic time sources. Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
This commit is contained in:
committed by
GitHub
parent
e68e852ee3
commit
394625d4bc
@@ -52,6 +52,7 @@ pub extern "C" fn regorus_compiled_policy_eval_with_input(
|
||||
})
|
||||
}
|
||||
|
||||
/// Configure the execution timer for evaluations of this compiled policy.
|
||||
/// Get information about the compiled policy including metadata about modules,
|
||||
/// target configuration, and resource types.
|
||||
///
|
||||
|
||||
@@ -5,6 +5,7 @@ use crate::common::{
|
||||
from_c_str, to_ref, to_regorus_result, to_regorus_string_result, RegorusResult, RegorusStatus,
|
||||
};
|
||||
use crate::compiled_policy::RegorusCompiledPolicy;
|
||||
use crate::limits::RegorusExecutionTimerConfig;
|
||||
use crate::lock::{new_handle, read, try_read, try_write, Handle, ReadGuard, WriteGuard};
|
||||
use crate::panic_guard::with_unwind_guard;
|
||||
use alloc::boxed::Box;
|
||||
@@ -450,6 +451,39 @@ pub extern "C" fn regorus_engine_set_strict_builtin_errors(
|
||||
})
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
/// Configure the execution timer for a specific engine instance.
|
||||
pub extern "C" fn regorus_engine_set_execution_timer_config(
|
||||
engine: *mut RegorusEngine,
|
||||
config: *const RegorusExecutionTimerConfig,
|
||||
) -> RegorusResult {
|
||||
to_regorus_result(|| -> Result<()> {
|
||||
let engine = to_ref(engine)?;
|
||||
let config = unsafe {
|
||||
config
|
||||
.as_ref()
|
||||
.copied()
|
||||
.ok_or_else(|| anyhow!("execution timer config pointer is null"))?
|
||||
};
|
||||
let mut guard = engine.try_write()?;
|
||||
guard.set_execution_timer_config(config.to_execution_timer_config()?);
|
||||
Ok(())
|
||||
}())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
/// Clear the engine-specific execution timer configuration.
|
||||
pub extern "C" fn regorus_engine_clear_execution_timer_config(
|
||||
engine: *mut RegorusEngine,
|
||||
) -> RegorusResult {
|
||||
to_regorus_result(|| -> Result<()> {
|
||||
let engine = to_ref(engine)?;
|
||||
let mut guard = engine.try_write()?;
|
||||
guard.clear_execution_timer_config();
|
||||
Ok(())
|
||||
}())
|
||||
}
|
||||
|
||||
/// Get pretty printed coverage report.
|
||||
///
|
||||
/// See https://docs.rs/regorus/latest/regorus/coverage/struct.Report.html#method.to_string_pretty
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
use crate::common::{RegorusResult, RegorusStatus};
|
||||
use crate::common::{to_regorus_result, RegorusResult, RegorusStatus};
|
||||
use alloc::format;
|
||||
use anyhow::{anyhow, Result};
|
||||
use core::num::NonZeroU32;
|
||||
use core::time::Duration;
|
||||
use regorus::utils::limits::{self, ExecutionTimerConfig};
|
||||
|
||||
#[cfg(feature = "allocator-memory-limits")]
|
||||
fn some_or_none(flag: bool, value: u64) -> Option<u64> {
|
||||
@@ -131,6 +135,45 @@ fn feature_disabled(function: &str) -> RegorusResult {
|
||||
)
|
||||
}
|
||||
|
||||
/// FFI representation of [`ExecutionTimerConfig`].
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct RegorusExecutionTimerConfig {
|
||||
/// Wall-clock limit expressed in nanoseconds.
|
||||
pub limit_ns: u64,
|
||||
/// Number of work units between timer checks (must be non-zero).
|
||||
pub check_interval: u32,
|
||||
}
|
||||
|
||||
impl RegorusExecutionTimerConfig {
|
||||
pub fn to_execution_timer_config(self) -> Result<ExecutionTimerConfig> {
|
||||
let check_interval = NonZeroU32::new(self.check_interval)
|
||||
.ok_or_else(|| anyhow!("execution_timer.check_interval must be non-zero"))?;
|
||||
let limit = Duration::from_nanos(self.limit_ns);
|
||||
|
||||
Ok(ExecutionTimerConfig {
|
||||
limit,
|
||||
check_interval,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_set_fallback_execution_timer_config(
|
||||
config: RegorusExecutionTimerConfig,
|
||||
) -> RegorusResult {
|
||||
to_regorus_result(|| -> Result<()> {
|
||||
limits::set_fallback_execution_timer_config(Some(config.to_execution_timer_config()?));
|
||||
Ok(())
|
||||
}())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn regorus_clear_fallback_execution_timer_config() -> RegorusResult {
|
||||
limits::set_fallback_execution_timer_config(None);
|
||||
RegorusResult::ok_void()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{
|
||||
|
||||
Reference in New Issue
Block a user