mirror of
https://github.com/microsoft/regorus.git
synced 2026-08-05 02:16:11 +00:00
* fix(ffi): eliminate aliasing UB via to_shared_ref migration Add to_shared_ref() helper that creates &T (shared reference) from raw pointers instead of &mut T. This eliminates undefined behavior caused by violating Rust's aliasing invariant when C# SafeHandle permits concurrent FFI calls on the same handle. With &mut T, the compiler may assume exclusive (noalias) access and reorder or elide reads/writes — a miscompilation risk when another thread holds a reference to the same object. Switching to &T removes that assumption; actual mutation is mediated by the interior RwLock inside Handle<T>, which is the sole synchronization mechanism. Migrated sites: - rvm.rs: 20 non-drop call sites - engine.rs: 30 non-drop call sites + with_unwind_guard for timer fns - compiled_policy.rs: 2 call sites - Fix null-data UB in regorus_program_deserialize_binary Drop paths retain to_ref() where exclusive access is guaranteed by the caller contract (preventing use-after-free). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat(ffi): add Azure Policy JSON compilation FFI and C# bindings - AliasRegistry builder pattern: RegorusAliasRegistryBuilder (mutable, single-threaded) + RegorusAliasRegistry (immutable, Arc-wrapped) - Azure Policy JSON compilation: regorus_compile_azure_policy_rule and regorus_compile_azure_policy_definition with alias registry support - regorus_rvm_set_context for host-supplied ambient data - C# AliasRegistryBuilder and AliasRegistry classes with convenience factories (FromJson, FromManifest, Empty) - C# AzurePolicyCompiler static class for policy rule/definition compilation - Compile functions take *const RegorusAliasRegistry (read-only via to_shared_ref for concurrent compilation safety) - Fix pre-existing clippy warnings across multiple crates Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
246 lines
7.2 KiB
C#
246 lines
7.2 KiB
C#
// Copyright (c) Microsoft Corporation.
|
|
// Licensed under the MIT License.
|
|
|
|
using System;
|
|
using Regorus.Internal;
|
|
|
|
#nullable enable
|
|
namespace Regorus
|
|
{
|
|
/// <summary>
|
|
/// Execution mode for the RVM runtime.
|
|
/// </summary>
|
|
public enum ExecutionMode : byte
|
|
{
|
|
/// <summary>
|
|
/// Run to completion without yielding.
|
|
/// </summary>
|
|
RunToCompletion = 0,
|
|
|
|
/// <summary>
|
|
/// Suspendable execution mode.
|
|
/// </summary>
|
|
Suspendable = 1,
|
|
}
|
|
|
|
/// <summary>
|
|
/// Wrapper for the Regorus RVM runtime.
|
|
/// </summary>
|
|
public unsafe sealed class Rvm : SafeHandleWrapper
|
|
{
|
|
public Rvm()
|
|
: base(RegorusRvmHandle.Create(), nameof(Rvm))
|
|
{
|
|
}
|
|
|
|
private Rvm(RegorusRvmHandle handle)
|
|
: base(handle, nameof(Rvm))
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create an RVM instance backed by a compiled policy (for default rule evaluation).
|
|
/// </summary>
|
|
public static Rvm CreateWithPolicy(CompiledPolicy policy)
|
|
{
|
|
if (policy is null)
|
|
{
|
|
throw new ArgumentNullException(nameof(policy));
|
|
}
|
|
|
|
return policy.UseHandleForInterop(policyPtr =>
|
|
{
|
|
var result = API.regorus_rvm_new_with_policy((RegorusCompiledPolicy*)policyPtr);
|
|
return GetRvmResult(result);
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// Load a program into the VM.
|
|
/// </summary>
|
|
public void LoadProgram(Program program)
|
|
{
|
|
if (program is null)
|
|
{
|
|
throw new ArgumentNullException(nameof(program));
|
|
}
|
|
|
|
program.UseHandleForInterop(programPtr =>
|
|
{
|
|
UseHandle(vmPtr =>
|
|
{
|
|
CheckAndDropResult(API.regorus_rvm_load_program((RegorusRvm*)vmPtr, (RegorusProgram*)programPtr));
|
|
return 0;
|
|
});
|
|
return 0;
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set the data document for the VM.
|
|
/// </summary>
|
|
public void SetDataJson(string dataJson)
|
|
{
|
|
Utf8Marshaller.WithUtf8(dataJson, dataPtr =>
|
|
{
|
|
UseHandle(vmPtr =>
|
|
{
|
|
CheckAndDropResult(API.regorus_rvm_set_data((RegorusRvm*)vmPtr, (byte*)dataPtr));
|
|
return 0;
|
|
});
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set the input document for the VM.
|
|
/// </summary>
|
|
public void SetInputJson(string inputJson)
|
|
{
|
|
Utf8Marshaller.WithUtf8(inputJson, inputPtr =>
|
|
{
|
|
UseHandle(vmPtr =>
|
|
{
|
|
CheckAndDropResult(API.regorus_rvm_set_input((RegorusRvm*)vmPtr, (byte*)inputPtr));
|
|
return 0;
|
|
});
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set the context document for the VM.
|
|
/// The context provides host-supplied ambient data (e.g. resourceGroup(),
|
|
/// subscription()) that Azure Policy functions can access via LoadContext
|
|
/// instructions.
|
|
/// </summary>
|
|
public void SetContextJson(string contextJson)
|
|
{
|
|
Utf8Marshaller.WithUtf8(contextJson, contextPtr =>
|
|
{
|
|
UseHandle(vmPtr =>
|
|
{
|
|
CheckAndDropResult(API.regorus_rvm_set_context((RegorusRvm*)vmPtr, (byte*)contextPtr));
|
|
return 0;
|
|
});
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set the execution mode (0 = run-to-completion, 1 = suspendable).
|
|
/// </summary>
|
|
public void SetExecutionMode(byte mode)
|
|
{
|
|
UseHandle(vmPtr =>
|
|
{
|
|
CheckAndDropResult(API.regorus_rvm_set_execution_mode((RegorusRvm*)vmPtr, mode));
|
|
return 0;
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set the execution mode.
|
|
/// </summary>
|
|
public void SetExecutionMode(ExecutionMode mode)
|
|
{
|
|
SetExecutionMode((byte)mode);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Execute the program and return the JSON result.
|
|
/// </summary>
|
|
public string? Execute()
|
|
{
|
|
return UseHandle(vmPtr =>
|
|
{
|
|
return CheckAndDropResult(API.regorus_rvm_execute((RegorusRvm*)vmPtr));
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// Execute a named entry point.
|
|
/// </summary>
|
|
public string? ExecuteEntryPoint(string entryPoint)
|
|
{
|
|
return Utf8Marshaller.WithUtf8(entryPoint, entryPtr =>
|
|
{
|
|
return UseHandle(vmPtr =>
|
|
{
|
|
return CheckAndDropResult(API.regorus_rvm_execute_entry_point_by_name((RegorusRvm*)vmPtr, (byte*)entryPtr));
|
|
});
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// Execute an entry point by index.
|
|
/// </summary>
|
|
public string? ExecuteEntryPoint(ulong index)
|
|
{
|
|
return UseHandle(vmPtr =>
|
|
{
|
|
return CheckAndDropResult(API.regorus_rvm_execute_entry_point_by_index((RegorusRvm*)vmPtr, (UIntPtr)index));
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// Resume execution with an optional value.
|
|
/// </summary>
|
|
public string? Resume(string? resumeValueJson)
|
|
{
|
|
if (resumeValueJson is null)
|
|
{
|
|
return UseHandle(vmPtr =>
|
|
{
|
|
return CheckAndDropResult(API.regorus_rvm_resume((RegorusRvm*)vmPtr, null, has_value: false));
|
|
});
|
|
}
|
|
|
|
return Utf8Marshaller.WithUtf8(resumeValueJson, valuePtr =>
|
|
{
|
|
return UseHandle(vmPtr =>
|
|
{
|
|
return CheckAndDropResult(API.regorus_rvm_resume((RegorusRvm*)vmPtr, (byte*)valuePtr, has_value: true));
|
|
});
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get the current execution state.
|
|
/// </summary>
|
|
public string? GetExecutionState()
|
|
{
|
|
return UseHandle(vmPtr =>
|
|
{
|
|
return CheckAndDropResult(API.regorus_rvm_get_execution_state((RegorusRvm*)vmPtr));
|
|
});
|
|
}
|
|
|
|
private static Rvm GetRvmResult(RegorusResult result)
|
|
{
|
|
try
|
|
{
|
|
if (result.status != RegorusStatus.Ok)
|
|
{
|
|
var message = Utf8Marshaller.FromUtf8(result.error_message);
|
|
throw result.status.CreateException(message);
|
|
}
|
|
|
|
if (result.data_type != RegorusDataType.Pointer || result.pointer_value == null)
|
|
{
|
|
throw new Exception("Expected RVM pointer but got different data type");
|
|
}
|
|
|
|
var handle = RegorusRvmHandle.FromPointer((IntPtr)result.pointer_value);
|
|
return new Rvm(handle);
|
|
}
|
|
finally
|
|
{
|
|
API.regorus_result_drop(result);
|
|
}
|
|
}
|
|
|
|
private static string? CheckAndDropResult(RegorusResult result)
|
|
{
|
|
return ResultHelpers.GetStringResult(result);
|
|
}
|
|
}
|
|
}
|