mirror of
https://github.com/microsoft/regorus.git
synced 2026-08-05 02:16:11 +00:00
Policy evaluation at scale needs to be able to set memory limits so that a bad policy does not hog memory or to ensure that policy evaluation itself does not use too much memory which could cause other components to suffer. This PR introduces capability to set and enforce global memory limits. It also lays the groundwork for enabling per evaluation limits in future. Once a global memory limit is set, Regorus maintains per thread counters to track memory activity (allocation, deallocation) of a thread. These counters are periodically flushed to global memory counters. Per thread counters avoid the contention that updating global counters on each alloc/free would cause. Policy evaluation periodically checks these counters and raises errors if allocated memory has exceeded the configured limit. Currently memory limit capability is exposed only to FFI and C#. Also update mimalloc to v2.2.6 Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
130 lines
3.9 KiB
Rust
130 lines
3.9 KiB
Rust
// Copyright (c) Microsoft Corporation.
|
|
// Licensed under the MIT License.
|
|
|
|
#![allow(
|
|
clippy::arithmetic_side_effects,
|
|
clippy::shadow_unrelated,
|
|
clippy::pattern_type_mismatch,
|
|
clippy::as_conversions
|
|
)] // small arithmetic checks are intentional
|
|
|
|
pub mod limits;
|
|
|
|
use crate::ast::*;
|
|
use crate::builtins::*;
|
|
use crate::lexer::*;
|
|
use crate::*;
|
|
|
|
use alloc::collections::BTreeMap;
|
|
|
|
use anyhow::{bail, Result};
|
|
pub fn get_path_string(refr: &Expr, document: Option<&str>) -> Result<String> {
|
|
let mut comps: Vec<&str> = vec![];
|
|
let mut expr = Some(refr);
|
|
while expr.is_some() {
|
|
match expr {
|
|
Some(Expr::RefDot { refr, field, .. }) => {
|
|
comps.push(field.0.text());
|
|
expr = Some(refr);
|
|
}
|
|
Some(Expr::RefBrack { refr, index, .. }) => {
|
|
if let Expr::String { span: s, .. } = index.as_ref() {
|
|
comps.push(s.text());
|
|
}
|
|
expr = Some(refr);
|
|
}
|
|
Some(Expr::Var { span: v, .. }) => {
|
|
comps.push(v.text());
|
|
expr = None;
|
|
}
|
|
_ => bail!("internal error: not a simple ref {expr:?}"),
|
|
}
|
|
}
|
|
if let Some(d) = document {
|
|
comps.push(d);
|
|
};
|
|
comps.reverse();
|
|
Ok(comps.join("."))
|
|
}
|
|
|
|
pub type FunctionTable = BTreeMap<String, (Vec<Ref<Rule>>, u8, Ref<Module>)>;
|
|
|
|
fn get_extra_arg_impl(
|
|
expr: &Expr,
|
|
module: Option<&str>,
|
|
functions: &FunctionTable,
|
|
) -> Result<Option<Ref<Expr>>> {
|
|
if let Expr::Call { fcn, params, .. } = expr {
|
|
let full_path = get_path_string(fcn, module)?;
|
|
let n_args = if let Some((_, n_args, _)) = functions.get(&full_path) {
|
|
*n_args
|
|
} else {
|
|
let path = get_path_string(fcn, None)?;
|
|
if let Some((_, n_args, _)) = functions.get(&path) {
|
|
*n_args
|
|
} else if let Some((_, n_args)) = BUILTINS.get(path.as_str()) {
|
|
*n_args
|
|
} else {
|
|
return Ok(None);
|
|
}
|
|
};
|
|
if (n_args as usize) + 1 == params.len() {
|
|
return Ok(params.last().cloned());
|
|
}
|
|
}
|
|
Ok(None)
|
|
}
|
|
|
|
pub fn get_extra_arg(
|
|
expr: &Expr,
|
|
module: Option<&str>,
|
|
functions: &FunctionTable,
|
|
) -> Option<Ref<Expr>> {
|
|
get_extra_arg_impl(expr, module, functions).unwrap_or_default()
|
|
}
|
|
|
|
pub fn gather_functions(modules: &[Ref<Module>]) -> Result<FunctionTable> {
|
|
let mut table = FunctionTable::new();
|
|
|
|
for module in modules {
|
|
let module_path = get_path_string(&module.package.refr, Some("data"))?;
|
|
for rule in &module.policy {
|
|
if let Rule::Spec {
|
|
span,
|
|
head: RuleHead::Func { refr, args, .. },
|
|
..
|
|
} = rule.as_ref()
|
|
{
|
|
let full_path = get_path_string(refr, Some(module_path.as_str()))?;
|
|
|
|
if let Some((functions, arity, _)) = table.get_mut(&full_path) {
|
|
if args.len() as u8 != *arity {
|
|
bail!(span.error(
|
|
format!("{full_path} was previously defined with {arity} arguments.")
|
|
.as_str()
|
|
));
|
|
}
|
|
functions.push(rule.clone());
|
|
} else {
|
|
table.insert(
|
|
full_path,
|
|
(vec![rule.clone()], args.len() as u8, module.clone()),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Ok(table)
|
|
}
|
|
|
|
pub fn get_root_var(mut expr: &Expr) -> Result<SourceStr> {
|
|
let empty = expr.span().source_str().clone_empty();
|
|
loop {
|
|
match expr {
|
|
Expr::Var { span: v, .. } => return Ok(v.source_str()),
|
|
Expr::RefDot { refr, .. } | Expr::RefBrack { refr, .. } => expr = refr,
|
|
_ => return Ok(empty),
|
|
}
|
|
}
|
|
}
|