fix(ffi): eliminate aliasing UB + add Azure Policy JSON compilation FFI (#727)

* 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>
This commit is contained in:
Anand Krishnamoorthi
2026-05-22 12:50:16 -05:00
committed by GitHub
parent 5467cd9e69
commit 86b4a279fa
26 changed files with 1986 additions and 315 deletions

View File

@@ -232,6 +232,9 @@ allow if {
Console.WriteLine("\n8. RVM host await (suspend/resume):");
DemonstrateRvmHostAwait();
Console.WriteLine("\n9. Azure Policy JSON compilation:");
DemonstrateAzurePolicyJsonCompilation();
}
static void DemonstrateConcurrentEvaluation(Regorus.CompiledPolicy compiledPolicy)
@@ -492,4 +495,80 @@ allow if {
var resumed = vm.Resume("{\"tier\":\"gold\"}");
Console.WriteLine($"HostAwait resumed result: {resumed}");
}
// Azure Policy JSON constants
private const string STORAGE_ALIASES_JSON = @"[{
""namespace"": ""Microsoft.Storage"",
""resourceTypes"": [{
""resourceType"": ""storageAccounts"",
""capabilities"": ""SupportsTags, SupportsLocation"",
""aliases"": [
{
""name"": ""Microsoft.Storage/storageAccounts/supportsHttpsTrafficOnly"",
""defaultPath"": ""properties.supportsHttpsTrafficOnly"",
""paths"": []
}
]
}]
}]";
private const string HTTPS_DENY_RULE = @"{
""if"": {
""allOf"": [
{ ""field"": ""type"", ""equals"": ""Microsoft.Storage/storageAccounts"" },
{ ""field"": ""Microsoft.Storage/storageAccounts/supportsHttpsTrafficOnly"", ""equals"": false }
]
},
""then"": { ""effect"": ""deny"" }
}";
static void DemonstrateAzurePolicyJsonCompilation()
{
// 1. Set up alias registry
using var registry = Regorus.AliasRegistry.FromJson(STORAGE_ALIASES_JSON);
Console.WriteLine("Loaded storage account aliases");
// 2. Compile the JSON policy rule directly (no Rego needed)
using var program = Regorus.AzurePolicyCompiler.CompilePolicyRule(registry, HTTPS_DENY_RULE);
Console.WriteLine("Compiled Azure Policy JSON rule to RVM program");
// 3. Normalize an ARM resource
var armResource = @"{
""type"": ""Microsoft.Storage/storageAccounts"",
""name"": ""insecurestorage"",
""location"": ""eastus"",
""properties"": { ""supportsHttpsTrafficOnly"": false }
}";
var envelope = registry.NormalizeAndWrap(armResource, apiVersion: null, contextJson: "{}", parametersJson: "{}");
Console.WriteLine($"Normalized ARM resource to evaluation envelope");
// 4. Execute in the RVM
// Note: For policies using context functions (subscription(), resourceGroup()),
// call vm.SetContextJson(contextJson) before execution. The context from
// NormalizeAndWrap is in the envelope but must also be set on the VM separately.
using var vm = new Regorus.Rvm();
vm.LoadProgram(program);
vm.SetInputJson(envelope!);
// vm.SetContextJson(contextJson); // ← required for context-dependent policies
var result = vm.ExecuteEntryPoint("main");
Console.WriteLine($"Evaluation result (non-compliant): {result}");
// 5. Test with a compliant resource
var compliantResource = @"{
""type"": ""Microsoft.Storage/storageAccounts"",
""name"": ""securestorage"",
""location"": ""eastus"",
""properties"": { ""supportsHttpsTrafficOnly"": true }
}";
var compliantEnvelope = registry.NormalizeAndWrap(compliantResource, apiVersion: null, contextJson: "{}", parametersJson: "{}");
using var vm2 = new Regorus.Rvm();
vm2.LoadProgram(program);
vm2.SetInputJson(compliantEnvelope!);
var compliantResult = vm2.ExecuteEntryPoint("main");
Console.WriteLine($"Evaluation result (compliant): {compliantResult}");
// 6. Demonstrate program serialization
var binary = program.SerializeBinary();
Console.WriteLine($"Serialized program size: {binary.Length} bytes");
}
}