dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
14.93k stars 4.64k forks source link

AV in runtime when catching exception thrown in other ALC with new EH on win-arm64 #105483

Closed jakobbotsch closed 3 weeks ago

jakobbotsch commented 1 month ago

Run the following program on win-arm64:

using System;
using System.Runtime.Intrinsics;

public class C0
{
    public ulong F1;
}

public class Program
{
    public static IRuntime s_rt;
    public static Vector64<ushort> s_1;
    public static double s_7;
    public static void Main()
    {
        CollectibleALC alc = new CollectibleALC();
        System.Reflection.Assembly asm = alc.LoadFromAssemblyPath(System.Reflection.Assembly.GetExecutingAssembly().Location);
        System.Reflection.MethodInfo mi = asm.GetType(typeof(Program).FullName).GetMethod(nameof(MainInner));
        System.Type runtimeTy = asm.GetType(typeof(Runtime).FullName);
        try
        {
            mi.Invoke(null, new object[] { System.Activator.CreateInstance(runtimeTy) });
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex);
        }
    }

    public static void MainInner(IRuntime rt)
    {
        C0 vr0 = default(C0);
        C0 vr1 = default(C0);
        vr1.F1 = vr0.F1;
    }
}

public interface IRuntime
{
    void WriteLine<T>(string site, T value);
}

public class Runtime : IRuntime
{
    public void WriteLine<T>(string site, T value) => System.Console.WriteLine(value);
}

public class CollectibleALC : System.Runtime.Loader.AssemblyLoadContext
{
    public CollectibleALC() : base(true)
    {
    }
}

The exception seems to be catchable only without legacy exception handling:

PS C:\jakob\publish> $env:DOTNET_LegacyExceptionHandling=0
PS C:\jakob\publish> C:\jakob\Core_Root\corerun.exe "C:\jakob\playground\playground.dll"
PS C:\jakob\publish> echo $LASTEXITCODE
-1073741819
PS C:\jakob\publish> $env:DOTNET_LegacyExceptionHandling=1
PS C:\jakob\publish> C:\jakob\Core_Root\corerun.exe "C:\jakob\playground\playground.dll"
System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.
 ---> System.NullReferenceException: Object reference not set to an instance of an object.
   at Program.MainInner(IRuntime rt)
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
   at System.Reflection.MethodBaseInvoker.InvokeDirectByRefWithFewArgs(Object obj, Span`1 copyOfArgs, BindingFlags invokeAttr)
   --- End of inner exception stack trace ---
   at System.Reflection.MethodBaseInvoker.InvokeDirectByRefWithFewArgs(Object obj, Span`1 copyOfArgs, BindingFlags invokeAttr)
   at System.Reflection.MethodBaseInvoker.InvokeWithOneArg(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at Program.Main()
PS C:\jakob\publish> echo $LASTEXITCODE
0
PS C:\jakob\publish>
jakobbotsch commented 1 month ago

cc @janvorli

janvorli commented 1 month ago

@jakobbotsch which commit have you built your runtime from? With the .NET 9 preview 6 official build it works fine on arm64 windows.

janvorli commented 1 month ago

I am unable to repro even with the latest main

jakobbotsch commented 1 month ago

@janvorli My commit is 5377b586afa69b3872aa244bf46f7980dc5b92da. This is happening on an arm64 box with SVE enabled, although it seems odd if that would be related.

janvorli commented 1 month ago

I wouldn't actually be that surprised if it was somehow related to the SVE. For hardware exceptions, the EH gets the full captured context and maybe something gets wrong with the SVE stuff when we restore the context when catching the exception. It might make sense to give it a try with preview 5 or 4 official builds (I assume the SVE support is more recent).

jakobbotsch commented 1 month ago

I also was unable to reproduce it on my other win-arm64 machine.

I attached windbg and it looks like it is hitting an assert:

Assert failure(PID 11712 [0x00002dc0], Thread: 3968 [0x0f80]): (pThread->IsExecutingOnAltStack() || (m_Next == FRAME_TOP) || (PBYTE(m_Next) + (2 * GetOsPageSize())) > PBYTE(this)) && "Pushing a frame out of order ?"

CORECLR! GetCLRRuntimeHost + 0x39EC0 (0x00007ffb`6bedfcf0)
CORECLR! GetCLRRuntimeHost + 0xBD170 (0x00007ffb`6bf62fa0)
CORECLR! <no symbol> + 0x0 (0x00007ffb`6be518b4)
SYSTEM.PRIVATE.CORELIB! <no symbol> + 0x0 (0x00007ffb`6ac09fa0)
SYSTEM.PRIVATE.CORELIB! <no symbol> + 0x0 (0x00007ffb`6ac092bc)
CORECLR! <no symbol> + 0x0 (0x00007ffb`6be51e64)
CORECLR! GetCLRRuntimeHost + 0x1275EC (0x00007ffb`6bfcd41c)
CORECLR! GetCLRRuntimeHost + 0x127E38 (0x00007ffb`6bfcdc68)
CORECLR! GetCLRRuntimeHost + 0x22C2C (0x00007ffb`6bec8a5c)
CORECLR! GetCLRRuntimeHost + 0x1AFCC (0x00007ffb`6bec0dfc)
    File: C:\dev\dotnet\runtime\src\coreclr\vm\frames.cpp:394
    Image: C:\jakob\Core_Root\corerun.exe

(2dc0.f80): Break instruction exception - code 80000003 (first chance)
KERNELBASE!DebugBreak:
00007ffb`f16faba0 d43e0000 brk         #0xF000
0:000> k
Unable to read dynamic function table entries
Unable to read dynamic function table entries
Unable to read dynamic function table entries
Unable to read dynamic function table entries
Unable to read dynamic function table entries
Unable to read dynamic function table entries
 # Child-SP          RetAddr               Call Site
00 00000012`96d7b240 00007ffb`6c260e7c     KERNELBASE!DebugBreak
01 00000012`96d7b240 00007ffb`6bedfcf0     coreclr!DbgAssertDialog+0x174 [C:\dev\dotnet\runtime\src\coreclr\utilcode\debug.cpp @ 434] 
02 00000012`96d7b310 00007ffb`6bf62fa0     coreclr!Frame::Push+0xf0 [C:\dev\dotnet\runtime\src\coreclr\vm\frames.cpp @ 396] 
03 00000012`96d7b340 00007ffb`6be518b4     coreclr!ExternalMethodFixupWorker+0xd8 [C:\dev\dotnet\runtime\src\coreclr\vm\prestub.cpp @ 3160] 
04 00000012`96d7b530 00007ffb`6ac09fa0     coreclr!DelayLoad_MethodCall+0x58 [C:\dev\dotnet\runtime\artifacts\obj\coreclr\windows.arm64.Checked\vm\wks\asmhelpers.asm @ 5864] 
05 00000012`96d7b660 00007ffb`6ac092bc     System_Private_CoreLib+0x569fa0
06 00000012`96d7b7c0 00007ffb`6be51e64     System_Private_CoreLib+0x5692bc
07 00000012`96d7b7f0 00007ffb`6bfcd41c     coreclr!CallDescrWorkerInternal+0x84 [C:\dev\dotnet\runtime\artifacts\obj\coreclr\windows.arm64.Checked\vm\wks\CallDescrWorkerARM64.asm @ 4989] 
08 00000012`96d7b810 00007ffb`6bfcdc68     coreclr!CallDescrWorkerWithHandler+0x124 [C:\dev\dotnet\runtime\src\coreclr\vm\callhelpers.cpp @ 63] 
09 00000012`96d7b860 00007ffb`6bec8a5c     coreclr!DispatchCallSimple+0xf8 [C:\dev\dotnet\runtime\src\coreclr\vm\callhelpers.cpp @ 217] 
0a 00000012`96d7b940 00007ffb`6bec0dfc     coreclr!HandleManagedFaultNew+0x1d4 [C:\dev\dotnet\runtime\src\coreclr\vm\excep.cpp @ 6305] 
0b 00000012`96d7cf50 00007ffb`f56ba330     coreclr!CLRVectoredExceptionHandlerShim+0x24c [C:\dev\dotnet\runtime\src\coreclr\vm\excep.cpp @ 7284] 
0c 00000012`96d7cfc0 00007ffb`f56f259c     ntdll!RtlAddVectoredExceptionHandler+0x3b0
0d 00000012`96d7d060 00007ffb`f580cc20     ntdll!RtlPcToFileHeader+0xd2c
0e 00000012`96d7d6a0 00007ffb`0cdc004c     ntdll!KiUserExceptionDispatcher+0x40
0f 00000012`96d7dba0 00007ffb`6be51e64     0x00007ffb`0cdc004c
10 00000012`96d7dba0 00007ffb`6be51e64     coreclr!CallDescrWorkerInternal+0x84 [C:\dev\dotnet\runtime\artifacts\obj\coreclr\windows.arm64.Checked\vm\wks\CallDescrWorkerARM64.asm @ 4989] 
11 00000012`96d7dbc0 00007ffb`6bfcd41c     coreclr!CallDescrWorkerInternal+0x84 [C:\dev\dotnet\runtime\artifacts\obj\coreclr\windows.arm64.Checked\vm\wks\CallDescrWorkerARM64.asm @ 4989] 
12 00000012`96d7dbd0 00007ffb`6c0c02ec     coreclr!CallDescrWorkerWithHandler+0x124 [C:\dev\dotnet\runtime\src\coreclr\vm\callhelpers.cpp @ 63] 
13 00000012`96d7dc20 00007ffb`6ac58270     coreclr!RuntimeMethodHandle::InvokeMethod+0x9fc [C:\dev\dotnet\runtime\src\coreclr\vm\reflectioninvocation.cpp @ 676] 
14 00000012`96d7e1e0 00007ffb`6ac578a0     System_Private_CoreLib+0x5b8270
15 00000012`96d7e250 00007ffb`0c972770     System_Private_CoreLib+0x5b78a0
16 00000012`96d7e2f0 00000000`00000000     0x00007ffb`0c972770
janvorli commented 1 month ago

The issue happens due to SVE when hardware exception is propagated over runtime native code, the reflection being one of those cases. We use the flags from the original context captured by the OS, which contains the XSTATE bit, but for unwinding, we use a CONTEXT structure in the REGDISPLAY that doesn't contain any XSTATE - and yet we keep the XSTATE flag set. I have tried to explicitly remove XSTATE from the flags on the context before we restore the context to the first native frame / COMPlusThrow function and the issue is gone.

jkotas commented 1 month ago

The fix is being reverted in https://github.com/dotnet/runtime/pull/105641 . It breaks debugging.