From 761f9048715597a77932d1a88e12ef9d766e8a3d Mon Sep 17 00:00:00 2001 From: Dan Moseley Date: Thu, 19 Mar 2026 18:25:04 -0600 Subject: [PATCH 1/3] Split Assembly.Load(byte[]) test case and add retry for AMSI hang The ToString_ShowILOffset test's Assembly.Load(byte[]) case triggers an AMSI (Antimalware Scan Interface) scan via Windows Defender RPC. On some CI machines, this RPC call hangs indefinitely on NtAlpcConnectPort, causing a 60-second RemoteExecutor timeout (see #125599, #122690). Split the byte[] case into its own test method (ToString_ShowILOffset_ByteArrayLoad) with a shorter 30s timeout and up to 3 retry attempts. This isolates the flaky AMSI-dependent case from the other two stable cases (LoadFrom and AssemblyBuilder), and retries with a fresh child process when the OS-level hang occurs. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../tests/StackTraceTests.cs | 58 +++++++++++++------ 1 file changed, 40 insertions(+), 18 deletions(-) diff --git a/src/libraries/System.Diagnostics.StackTrace/tests/StackTraceTests.cs b/src/libraries/System.Diagnostics.StackTrace/tests/StackTraceTests.cs index 7b7fa769de3935..b10ccdb07cbff1 100644 --- a/src/libraries/System.Diagnostics.StackTrace/tests/StackTraceTests.cs +++ b/src/libraries/System.Diagnostics.StackTrace/tests/StackTraceTests.cs @@ -541,24 +541,7 @@ public void ToString_ShowILOffset() } }, SourceTestAssemblyPath, AssemblyName, regPattern).Dispose(); - // Assembly.Load(Byte[]) case - RemoteExecutor.Invoke((asmPath, asmName, p) => - { - AppContext.SetSwitch("Switch.System.Diagnostics.StackTrace.ShowILOffsets", true); - var inMemBlob = File.ReadAllBytes(asmPath); - var asm2 = Assembly.Load(inMemBlob); - try - { - asm2.GetType("Program").GetMethod("Foo").Invoke(null, null); - } - catch (Exception e) - { - Assert.Contains(asmName, e.InnerException.StackTrace); - Assert.Matches(p, e.InnerException.StackTrace); - } - }, SourceTestAssemblyPath, AssemblyName, regPattern).Dispose(); - - // AssmblyBuilder.DefineDynamicAssembly() case + // AssemblyBuilder.DefineDynamicAssembly() case RemoteExecutor.Invoke((p) => { AppContext.SetSwitch("Switch.System.Diagnostics.StackTrace.ShowILOffsets", true); @@ -583,6 +566,45 @@ public void ToString_ShowILOffset() }, regPattern).Dispose(); } + // Assembly.Load(byte[]) triggers an AMSI (Antimalware Scan Interface) scan via Windows Defender. + // On some CI machines the AMSI RPC call hangs indefinitely, + // so we retry with a shorter timeout to work around the transient OS issue. + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + public void ToString_ShowILOffset_ByteArrayLoad() + { + string AssemblyName = "ExceptionTestAssembly.dll"; + string SourceTestAssemblyPath = Path.Combine(Environment.CurrentDirectory, AssemblyName); + string regPattern = @":token 0x([a-f0-9]*)\+0x([a-f0-9]*)"; + + const int maxAttempts = 3; + for (int attempt = 1; attempt <= maxAttempts; attempt++) + { + try + { + var options = new RemoteInvokeOptions { TimeOut = 30_000 }; + RemoteExecutor.Invoke((asmPath, asmName, p) => + { + AppContext.SetSwitch("Switch.System.Diagnostics.StackTrace.ShowILOffsets", true); + var inMemBlob = File.ReadAllBytes(asmPath); + var asm = Assembly.Load(inMemBlob); + try + { + asm.GetType("Program").GetMethod("Foo").Invoke(null, null); + } + catch (Exception e) + { + Assert.Contains(asmName, e.InnerException.StackTrace); + Assert.Matches(p, e.InnerException.StackTrace); + } + }, SourceTestAssemblyPath, AssemblyName, regPattern, options).Dispose(); + break; + } + catch (RemoteExecutionException) when (attempt < maxAttempts) + { + } + } + } + // On Android, stack traces do not include file names and line numbers // Tracking issue: https://github.com/dotnet/runtime/issues/124087 private static string FileInfoPattern(string fileLinePattern) => From 28fda3ef6d8f54ef0fc5d00fdad3b9b212bab182 Mon Sep 17 00:00:00 2001 From: Dan Moseley Date: Thu, 19 Mar 2026 18:59:49 -0600 Subject: [PATCH 2/3] Address PR feedback: limit retry to Windows timeout only - Restrict retry to Windows only (AMSI is Windows-specific) per @jkotas - Only retry on timeout, not assertion failures (check message) - Use Assert.Throws for explicit throw assertion Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../tests/StackTraceTests.cs | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/libraries/System.Diagnostics.StackTrace/tests/StackTraceTests.cs b/src/libraries/System.Diagnostics.StackTrace/tests/StackTraceTests.cs index b10ccdb07cbff1..364c2ccf5de0c6 100644 --- a/src/libraries/System.Diagnostics.StackTrace/tests/StackTraceTests.cs +++ b/src/libraries/System.Diagnostics.StackTrace/tests/StackTraceTests.cs @@ -567,7 +567,7 @@ public void ToString_ShowILOffset() } // Assembly.Load(byte[]) triggers an AMSI (Antimalware Scan Interface) scan via Windows Defender. - // On some CI machines the AMSI RPC call hangs indefinitely, + // On some Windows x86 CI machines the AMSI RPC call hangs indefinitely, // so we retry with a shorter timeout to work around the transient OS issue. [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] public void ToString_ShowILOffset_ByteArrayLoad() @@ -577,7 +577,7 @@ public void ToString_ShowILOffset_ByteArrayLoad() string regPattern = @":token 0x([a-f0-9]*)\+0x([a-f0-9]*)"; const int maxAttempts = 3; - for (int attempt = 1; attempt <= maxAttempts; attempt++) + for (int attempt = 1; ; attempt++) { try { @@ -587,20 +587,17 @@ public void ToString_ShowILOffset_ByteArrayLoad() AppContext.SetSwitch("Switch.System.Diagnostics.StackTrace.ShowILOffsets", true); var inMemBlob = File.ReadAllBytes(asmPath); var asm = Assembly.Load(inMemBlob); - try - { - asm.GetType("Program").GetMethod("Foo").Invoke(null, null); - } - catch (Exception e) - { - Assert.Contains(asmName, e.InnerException.StackTrace); - Assert.Matches(p, e.InnerException.StackTrace); - } + TargetInvocationException ex = Assert.Throws( + () => asm.GetType("Program").GetMethod("Foo").Invoke(null, null)); + Assert.Contains(asmName, ex.InnerException.StackTrace); + Assert.Matches(p, ex.InnerException.StackTrace); }, SourceTestAssemblyPath, AssemblyName, regPattern, options).Dispose(); break; } - catch (RemoteExecutionException) when (attempt < maxAttempts) + catch (RemoteExecutionException ex) when (OperatingSystem.IsWindows() && attempt < maxAttempts && ex.Message.Contains("Timed out")) { + // AMSI hang: on some Windows CI machines, Assembly.Load(byte[]) triggers an AMSI scan + // whose RPC call hangs indefinitely. Retry with a fresh process. } } } From 2caaf3f1745d0f517dad5deb5469afa8e22a473e Mon Sep 17 00:00:00 2001 From: Dan Moseley Date: Thu, 19 Mar 2026 19:09:04 -0600 Subject: [PATCH 3/3] Scope AMSI retry to Windows x86 only Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../System.Diagnostics.StackTrace/tests/StackTraceTests.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Diagnostics.StackTrace/tests/StackTraceTests.cs b/src/libraries/System.Diagnostics.StackTrace/tests/StackTraceTests.cs index 364c2ccf5de0c6..0145acfb9aecd8 100644 --- a/src/libraries/System.Diagnostics.StackTrace/tests/StackTraceTests.cs +++ b/src/libraries/System.Diagnostics.StackTrace/tests/StackTraceTests.cs @@ -9,6 +9,7 @@ using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Text.RegularExpressions; using Microsoft.DotNet.RemoteExecutor; using System.Threading.Tasks; @@ -594,9 +595,9 @@ public void ToString_ShowILOffset_ByteArrayLoad() }, SourceTestAssemblyPath, AssemblyName, regPattern, options).Dispose(); break; } - catch (RemoteExecutionException ex) when (OperatingSystem.IsWindows() && attempt < maxAttempts && ex.Message.Contains("Timed out")) + catch (RemoteExecutionException ex) when (OperatingSystem.IsWindows() && RuntimeInformation.ProcessArchitecture == Architecture.X86 && attempt < maxAttempts && ex.Message.Contains("Timed out")) { - // AMSI hang: on some Windows CI machines, Assembly.Load(byte[]) triggers an AMSI scan + // AMSI hang: on some Windows x86 CI machines, Assembly.Load(byte[]) triggers an AMSI scan // whose RPC call hangs indefinitely. Retry with a fresh process. } }