Skip to content

Commit 6a815e8

Browse files
danmoseleyCopilot
andauthored
Add retry for StackTraceTests.ToString_ShowILOffset AMSI hang (#125814)
The `ToString_ShowILOffset` test's `Assembly.Load(byte[])` case triggers an AMSI (Antimalware Scan Interface) scan via Windows Defender RPC. On some Helix CI machines, this RPC call hangs indefinitely on `NtAlpcConnectPort`, causing the 60-second RemoteExecutor timeout. This is an external Windows OS/Defender issue, not a .NET bug (diagnosed in #122690 by @jkotas). ### Changes - **Split** the `Assembly.Load(byte[])` case into its own test method (`ToString_ShowILOffset_ByteArrayLoad`) so an AMSI hang no longer drags down the other two stable cases (LoadFrom and AssemblyBuilder). - **Added retry logic** with up to 3 attempts and a 30s timeout. Each retry spawns a fresh child process via RemoteExecutor, giving the AMSI RPC a new chance to succeed. Fixes #125599 --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 83f84b6 commit 6a815e8

File tree

1 file changed

+38
-18
lines changed

1 file changed

+38
-18
lines changed

src/libraries/System.Diagnostics.StackTrace/tests/StackTraceTests.cs

Lines changed: 38 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
using System.Reflection;
1010
using System.Reflection.Emit;
1111
using System.Runtime.CompilerServices;
12+
using System.Runtime.InteropServices;
1213
using System.Text.RegularExpressions;
1314
using Microsoft.DotNet.RemoteExecutor;
1415
using System.Threading.Tasks;
@@ -541,24 +542,7 @@ public void ToString_ShowILOffset()
541542
}
542543
}, SourceTestAssemblyPath, AssemblyName, regPattern).Dispose();
543544

544-
// Assembly.Load(Byte[]) case
545-
RemoteExecutor.Invoke((asmPath, asmName, p) =>
546-
{
547-
AppContext.SetSwitch("Switch.System.Diagnostics.StackTrace.ShowILOffsets", true);
548-
var inMemBlob = File.ReadAllBytes(asmPath);
549-
var asm2 = Assembly.Load(inMemBlob);
550-
try
551-
{
552-
asm2.GetType("Program").GetMethod("Foo").Invoke(null, null);
553-
}
554-
catch (Exception e)
555-
{
556-
Assert.Contains(asmName, e.InnerException.StackTrace);
557-
Assert.Matches(p, e.InnerException.StackTrace);
558-
}
559-
}, SourceTestAssemblyPath, AssemblyName, regPattern).Dispose();
560-
561-
// AssmblyBuilder.DefineDynamicAssembly() case
545+
// AssemblyBuilder.DefineDynamicAssembly() case
562546
RemoteExecutor.Invoke((p) =>
563547
{
564548
AppContext.SetSwitch("Switch.System.Diagnostics.StackTrace.ShowILOffsets", true);
@@ -583,6 +567,42 @@ public void ToString_ShowILOffset()
583567
}, regPattern).Dispose();
584568
}
585569

570+
// Assembly.Load(byte[]) triggers an AMSI (Antimalware Scan Interface) scan via Windows Defender.
571+
// On some Windows x86 CI machines the AMSI RPC call hangs indefinitely,
572+
// so we retry with a shorter timeout to work around the transient OS issue.
573+
[ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
574+
public void ToString_ShowILOffset_ByteArrayLoad()
575+
{
576+
string AssemblyName = "ExceptionTestAssembly.dll";
577+
string SourceTestAssemblyPath = Path.Combine(Environment.CurrentDirectory, AssemblyName);
578+
string regPattern = @":token 0x([a-f0-9]*)\+0x([a-f0-9]*)";
579+
580+
const int maxAttempts = 3;
581+
for (int attempt = 1; ; attempt++)
582+
{
583+
try
584+
{
585+
var options = new RemoteInvokeOptions { TimeOut = 30_000 };
586+
RemoteExecutor.Invoke((asmPath, asmName, p) =>
587+
{
588+
AppContext.SetSwitch("Switch.System.Diagnostics.StackTrace.ShowILOffsets", true);
589+
var inMemBlob = File.ReadAllBytes(asmPath);
590+
var asm = Assembly.Load(inMemBlob);
591+
TargetInvocationException ex = Assert.Throws<TargetInvocationException>(
592+
() => asm.GetType("Program").GetMethod("Foo").Invoke(null, null));
593+
Assert.Contains(asmName, ex.InnerException.StackTrace);
594+
Assert.Matches(p, ex.InnerException.StackTrace);
595+
}, SourceTestAssemblyPath, AssemblyName, regPattern, options).Dispose();
596+
break;
597+
}
598+
catch (RemoteExecutionException ex) when (OperatingSystem.IsWindows() && RuntimeInformation.ProcessArchitecture == Architecture.X86 && attempt < maxAttempts && ex.Message.Contains("Timed out"))
599+
{
600+
// AMSI hang: on some Windows x86 CI machines, Assembly.Load(byte[]) triggers an AMSI scan
601+
// whose RPC call hangs indefinitely. Retry with a fresh process.
602+
}
603+
}
604+
}
605+
586606
// On Android, stack traces do not include file names and line numbers
587607
// Tracking issue: https://github.com/dotnet/runtime/issues/124087
588608
private static string FileInfoPattern(string fileLinePattern) =>

0 commit comments

Comments
 (0)