Skip to content

Commit 18b58d7

Browse files
committed
Prevent ArgumentList struct invokers from crashing the process
1 parent 5f629d8 commit 18b58d7

6 files changed

Lines changed: 169 additions & 56 deletions

File tree

docs/plans/fusion-library-audit.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -912,20 +912,21 @@ Confidence: **Confirmed** by focused regression test and source inspection.
912912

913913
### Audit coverage
914914

915-
The Interception runtime and proxy generator were reviewed across method definitions and converters, invoker factories, interceptor selection/binding, proxy lookup/activation, built-in interceptors, invocation dispatch, generated argument-list operations, trimming keepers, generator syntax filtering, proxy method emission, diagnostics, and source hint production. `InterceptionAuditRegressionTest` contains a failing nested-type contract test and a skipped process-crash test; isolated generator compile cases live under ignored `tmp/interception-generator-audit` and are recorded in validation.
915+
The Interception runtime and proxy generator were reviewed across method definitions and converters, invoker factories, interceptor selection/binding, proxy lookup/activation, built-in interceptors, invocation dispatch, generated argument-list operations, trimming keepers, generator syntax filtering, proxy method emission, diagnostics, and source hint production. `InterceptionAuditRegressionTest` contains a failing nested-type contract test and a subprocess-isolated struct-invocation regression; isolated generator compile cases live under ignored `tmp/interception-generator-audit` and are recorded in validation.
916916

917917
### INT1. Dynamic invocation of a struct instance method can crash the process
918918

919-
Status: **approved — pending implementation**.
919+
Status: **completed**.
920920

921921
Confidence: **Confirmed** by isolated test-host crash (`0xC0000005`).
922922

923923
- Source: `src/ActualLab.Interception/ArgumentList.cs:94-130` and the generated argument-list template emit `Unbox_Any` for a value-type target, then `Callvirt` for an instance method. `Unbox_Any` leaves a value, while a struct instance call requires a managed address.
924924
- Failure: creating/invoking the dynamic delegate for a zero-argument struct instance method aborted the .NET test host with access violation `0xC0000005`; it did not produce a catchable managed exception.
925-
- Test: `InterceptionAuditRegressionTest.ArgumentListInvokerMustSupportValueTypeTargets` preserves the correct contract but is skipped because activating it destabilizes the entire suite. The crash was confirmed before the skip was added.
925+
- Test: `InterceptionAuditRegressionTest.ArgumentListInvokerMustSupportValueTypeTargets` runs each dangerous invocation in a child VSTest process, covering the zero-argument list plus generic/hybrid and simple factories for arities 1 through 10 without exposing the parent test host to native failure.
926926
- Impact: reflected value-type targets can terminate the process, making this a denial-of-service boundary rather than a normal invocation error.
927927
- **Maintainer decision:** implement the recommended address-preserving struct invocation fix.
928-
- **Recommended:** emit address-preserving struct invocation IL and use `Call` where required; add a subprocess-isolated regression for all generated argument-list arities.
928+
- **Resolution:** shared IL helpers now preserve boxed value-type addresses with `Unbox` and select `Call` for static and value-type methods while retaining `Callvirt` for reference-type instance methods. Both `ArgumentList0` and the generated argument-list template use the helpers, and the generated output was refreshed with the repository's T4 tool.
929+
- **Validation:** before the fix, the subprocess regression kept its parent alive while the child host aborted with fatal `0xC0000005`. After the fix, all 21 argument-list shapes invoke and mutate a boxed struct correctly. The focused regression passes, all 8 `ArgumentListTest` cases pass, the surrounding non-benchmark Interception set passes (13 tests), and the nine-target `ActualLab.Interception` build succeeds.
929930
- **Alternative:** reject value-type instance methods before dynamic code generation with a clear exception. Safer than a crash but narrower than the public invoker contract.
930931

931932
### GEN1. Proxy parameters lose passing modifiers and identifier escaping

src/ActualLab.Core/Reflection/ILGeneratorExt.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System.Reflection;
12
using System.Reflection.Emit;
23
using ActualLab.Internal;
34

@@ -9,6 +10,12 @@ namespace ActualLab.Reflection;
910
// ReSharper disable once InconsistentNaming
1011
public static class ILGeneratorExt
1112
{
13+
public static void EmitCall(this ILGenerator il, MethodInfo method)
14+
=> il.Emit(method.IsStatic || method.DeclaringType!.IsValueType ? OpCodes.Call : OpCodes.Callvirt, method);
15+
16+
public static void EmitLoadInstance(this ILGenerator il, Type instanceType)
17+
=> il.Emit(instanceType.IsValueType ? OpCodes.Unbox : OpCodes.Castclass, instanceType);
18+
1219
public static void MaybeEmitCast(this ILGenerator il, Type fromType, Type toType)
1320
{
1421
if (fromType == toType)

0 commit comments

Comments
 (0)