mirror of
https://github.com/microsoft/regorus.git
synced 2026-08-05 02:16:11 +00:00
feat(ffi): unwind safety: shield FFI entrypoints with panic guard (#546)
This PR implements widely accepted Rust programming practices for dealing with panics across ABI (programming language) boundaries. - Add panic_guard.rs to wrap FFI calls and prevent panic across FFI/ABI boundary (undefined behavior). - Capture per-thread backtraces via a temporary panic hook - After a panic, subsequent invocations are poisoned. - Integrate with_unwind_guard across the engine, schema registry, and target registry exportis Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
This commit is contained in:
committed by
GitHub
parent
9426b2ec02
commit
80686d6ed1
@@ -0,0 +1,84 @@
|
||||
#if REGORUS_FFI_TEST_HOOKS
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Regorus.Internal;
|
||||
|
||||
namespace Regorus.Tests;
|
||||
|
||||
[TestClass]
|
||||
public sealed class PanicGuardTests
|
||||
{
|
||||
[TestInitialize]
|
||||
public void Initialize()
|
||||
{
|
||||
API.regorus_engine_test_reset_poison();
|
||||
}
|
||||
|
||||
[TestCleanup]
|
||||
public void Cleanup()
|
||||
{
|
||||
API.regorus_engine_test_reset_poison();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Panic_produces_invalid_operation_exception()
|
||||
{
|
||||
var panic = Assert.ThrowsException<InvalidOperationException>(TriggerPanic);
|
||||
StringAssert.Contains(panic.Message, "panicked", "panic message should capture payload");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Poison_flag_blocks_subsequent_calls()
|
||||
{
|
||||
_ = Assert.ThrowsException<InvalidOperationException>(TriggerPanic);
|
||||
var poisoned = Assert.ThrowsException<InvalidOperationException>(TriggerPanic);
|
||||
StringAssert.Contains(poisoned.Message, "poisoned", "poisoned message should explain guard state");
|
||||
}
|
||||
|
||||
private static unsafe void TriggerPanic()
|
||||
{
|
||||
var result = API.regorus_engine_test_trigger_panic();
|
||||
try
|
||||
{
|
||||
if (result.status == RegorusStatus.Ok)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var message = PtrToStringUtf8((IntPtr)result.error_message);
|
||||
throw result.status.CreateException(message);
|
||||
}
|
||||
finally
|
||||
{
|
||||
API.regorus_result_drop(result);
|
||||
}
|
||||
}
|
||||
|
||||
private static string? PtrToStringUtf8(IntPtr ptr)
|
||||
{
|
||||
#if NETSTANDARD2_1
|
||||
return Marshal.PtrToStringUTF8(ptr);
|
||||
#else
|
||||
if (ptr == IntPtr.Zero)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var len = 0;
|
||||
while (Marshal.ReadByte(ptr, len) != 0)
|
||||
{
|
||||
len++;
|
||||
}
|
||||
|
||||
var buffer = new byte[len];
|
||||
Marshal.Copy(ptr, buffer, 0, buffer.Length);
|
||||
return System.Text.Encoding.UTF8.GetString(buffer);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user