Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The native AOT DLL library crash failed to generate a dump or catch an exception #92687

Closed
GameRisker opened this issue Sep 27, 2023 · 11 comments

Comments

@GameRisker
Copy link

I used c# native aot to export the library for golang to use. I deliberately write a null pointer access in the library, go server invoke API directly after the crash.I can't find any information related to the crash. I can't find the dump file.

[UnmanagedCallersOnly(EntryPoint = "NativeGetLeng")]
public static int NativeGetLeng()
{
try
{
List list = new List();
list = null;
return list.Count;
}
catch (System.NullReferenceException e)
{
Log.Info(e.ToString());
return 0;
}
}

Why can't catch System.NullReferenceException.
Is there any way to solve this problem?

@ghost ghost added the untriaged New issue has not been triaged by the area owner label Sep 27, 2023
@dotnet-issue-labeler dotnet-issue-labeler bot added the needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners label Sep 27, 2023
@MichalStrehovsky MichalStrehovsky added area-NativeAOT-coreclr and removed needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners labels Sep 27, 2023
@ghost
Copy link

ghost commented Sep 27, 2023

Tagging subscribers to this area: @agocke, @MichalStrehovsky, @jkotas
See info in area-owners.md if you want to be subscribed.

Issue Details

I used c# native aot to export the library for golang to use. I deliberately write a null pointer access in the library, go server invoke API directly after the crash.I can't find any information related to the crash. I can't find the dump file.

[UnmanagedCallersOnly(EntryPoint = "NativeGetLeng")]
public static int NativeGetLeng()
{
try
{
List list = new List();
list = null;
return list.Count;
}
catch (System.NullReferenceException e)
{
Log.Info(e.ToString());
return 0;
}
}

Why can't catch System.NullReferenceException.
Is there any way to solve this problem?

Author: GameRisker
Assignees: -
Labels:

untriaged, area-NativeAOT-coreclr

Milestone: -

@MichalStrehovsky
Copy link
Member

To make sure I understand the issue correctly:

When the above code executes, the NativeGetLeng method doesn't return and instead the process terminates without generating a dump. Is that correct? What is the exit code of the process?

Did you try debugging through this in LLDB or GDB?

@GameRisker
Copy link
Author

Exception 0xc0000005 0x0 0x0 0x7ffe2912fee0
PC=0x7ffe2912fee0
signal arrived during external code execution

runtime.cgocall(0x1556ae4, 0xc000557b90)
C:/Program Files/Go/src/runtime/cgocall.go:158 +0x4a fp=0xc000557b68 sp=0xc000557b30 pc=0x54560a
TarsGame/server/gamesrv/bubblelib._Cfunc_NativeGetLeng()
_cgo_gotypes.go:564 +0x56 fp=0xc000557b90 sp=0xc000557b68 pc=0x1502f36

@GameRisker
Copy link
Author

Yes, there was no dump. I have not used lldb gdb.

@MichalStrehovsky
Copy link
Member

Yes, there was no dump. I have not used lldb gdb.

I assumed this was on Linux, but now I see the Windows file paths.

Are you linking the C# code statically (building into a LIB and linking with cgo), or dynamically (C# code is in a DLL that you load at runtime)?

@GameRisker
Copy link
Author

package test

//#cgo LDFLAGS: -L./ -lGame -lstdc++ -lm
//#include <stdlib.h>
//#include "LibWarp.h"
import "C"
import (
"fmt"
"io/ioutil"
"os"
"unsafe"
)

func init() {
C.NativeGetLeng()
}

I develop on windows, I am linking the C# code statically.

@MichalStrehovsky
Copy link
Member

Can you try compiling the C# to a DLL and link dynamically? It kind of looks like the golang runtime thinks it's supposed to handle the nullref but then it can't and I'm wondering if linking to a DLL would work around.

@GameRisker
Copy link
Author

Sorry, I made a mistake. I used link dynamically.
I have uploaded a demo, which may be more convenient for you to understand my problem.

PS D:\Kit\Crash\CrashGo\bin> ./Debug.exe
create succeed.
Exception 0xc0000005 0x0 0x0 0x7ffe44d74fd0
PC=0x7ffe44d74fd0
signal arrived during external code execution

runtime.cgocall(0xe7ceb8, 0xc00009bf58)
C:/Program Files/Go/src/runtime/cgocall.go:158 +0x4a fp=0xc00009bf30 sp=0xc00009bef8 pc=0xe13aaa
main._Cfunc_NativeGetLeng()
_cgo_gotypes.go:59 +0x56 fp=0xc00009bf58 sp=0xc00009bf30 pc=0xe7ccf6
main.main()
H:/BubbleSpaceV1.0/ConsTools/Kit/Crash/CrashGo/main.go:16 +0x85 fp=0xc00009bf80 sp=0xc00009bf58 pc=0xe7cda5
runtime.main()
C:/Program Files/Go/src/runtime/proc.go:250 +0x1c8 fp=0xc00009bfe0 sp=0xc00009bf80 pc=0xe498e8
runtime.goexit()
C:/Program Files/Go/src/runtime/asm_amd64.s:1594 +0x1 fp=0xc00009bfe8 sp=0xc00009bfe0 pc=0xe6fb81

goroutine 2 [force gc (idle)]:
runtime.gopark(0xe98d70, 0xee67b0, 0x11, 0x14, 0x1)
C:/Program Files/Go/src/runtime/proc.go:363 +0xfd fp=0xc00005bf88 sp=0xc00005bf58 pc=0xe49cdd
runtime.goparkunlock(0x0?, 0x0?, 0x0?, 0x0?)
C:/Program Files/Go/src/runtime/proc.go:369 +0x2a fp=0xc00005bfb8 sp=0xc00005bf88 pc=0xe49d6a
runtime.forcegchelper()
C:/Program Files/Go/src/runtime/proc.go:302 +0xa5 fp=0xc00005bfe0 sp=0xc00005bfb8 pc=0xe49b05
runtime.goexit()
C:/Program Files/Go/src/runtime/asm_amd64.s:1594 +0x1 fp=0xc00005bfe8 sp=0xc00005bfe0 pc=0xe6fb81
created by runtime.init.6
C:/Program Files/Go/src/runtime/proc.go:290 +0x25

goroutine 3 [GC sweep wait]:
runtime.gopark(0xe98d70, 0xee68c0, 0xc, 0x14, 0x1)
C:/Program Files/Go/src/runtime/proc.go:363 +0xfd fp=0xc00005df68 sp=0xc00005df38 pc=0xe49cdd
runtime.goparkunlock(0x0?, 0x0?, 0x0?, 0x0?)
C:/Program Files/Go/src/runtime/proc.go:369 +0x2a fp=0xc00005df98 sp=0xc00005df68 pc=0xe49d6a
runtime.bgsweep(0x0?)
C:/Program Files/Go/src/runtime/mgcsweep.go:278 +0x98 fp=0xc00005dfc8 sp=0xc00005df98 pc=0xe32e78
runtime.gcenable.func1()
C:/Program Files/Go/src/runtime/mgc.go:178 +0x26 fp=0xc00005dfe0 sp=0xc00005dfc8 pc=0xe27466
runtime.goexit()
C:/Program Files/Go/src/runtime/asm_amd64.s:1594 +0x1 fp=0xc00005dfe8 sp=0xc00005dfe0 pc=0xe6fb81
created by runtime.gcenable
C:/Program Files/Go/src/runtime/mgc.go:178 +0x6b

goroutine 4 [GC scavenge wait]:
runtime.gopark(0xe98d70, 0xee6900, 0xd, 0x14, 0x2)
C:/Program Files/Go/src/runtime/proc.go:363 +0xfd fp=0xc00006bf48 sp=0xc00006bf18 pc=0xe49cdd
runtime.goparkunlock(0xe9bf28?, 0x1?, 0x0?, 0x0?)
C:/Program Files/Go/src/runtime/proc.go:369 +0x2a fp=0xc00006bf78 sp=0xc00006bf48 pc=0xe49d6a
runtime.(*scavengerState).park(0xee6900)
C:/Program Files/Go/src/runtime/mgcscavenge.go:389 +0x4b fp=0xc00006bfa0 sp=0xc00006bf78 pc=0xe30b0b
runtime.bgscavenge(0x0?)
C:/Program Files/Go/src/runtime/mgcscavenge.go:617 +0x45 fp=0xc00006bfc8 sp=0xc00006bfa0 pc=0xe310e5
runtime.gcenable.func2()
C:/Program Files/Go/src/runtime/mgc.go:179 +0x26 fp=0xc00006bfe0 sp=0xc00006bfc8 pc=0xe27406
runtime.goexit()
C:/Program Files/Go/src/runtime/asm_amd64.s:1594 +0x1 fp=0xc00006bfe8 sp=0xc00006bfe0 pc=0xe6fb81
created by runtime.gcenable
C:/Program Files/Go/src/runtime/mgc.go:179 +0xaa
rax 0x21b3e003e68
rbx 0xc00009bf58
rcx 0x0
rdi 0xc00009bf58
rsi 0xee69a0
rbp 0x8971bffa10
rsp 0x8971bff9a0
r8 0x21b3e003e88
r9 0x0
r10 0x25af7ee9040
r11 0x0
r12 0x10
r13 0x0
r14 0xc000058000
r15 0x20
rip 0x7ffe44d74fd0
rflags 0x10246
cs 0x33
fs 0x53
gs 0x2b

Crash.zip

@MichalStrehovsky
Copy link
Member

The reduced repro of the problem (without any Native AOT, just plain C) is this:

package main

//static volatile char* Memory;
//
//void* AddVectoredExceptionHandler(unsigned long First, void* Handler);
//void* VirtualAlloc(void* lpAddress, void* dwSize, int flAllocationType, int flProtect);
//int VirtualProtect(void* lpAddress, void* dwSize, int flNewProtect, int* lpflOldProtect);
//
//long VectoredExceptionHandler(void* ExceptionInfo)
//{
//	int old;
//	VirtualProtect((void*)Memory, (void*)4096, 0x04, &old);
//	return 0xffffffff;
//}
//
//int f()
//{
//	AddVectoredExceptionHandler(1, &VectoredExceptionHandler);
//	Memory = (char*)VirtualAlloc(0, (void*)4096, 0x00001000 | 0x00002000, 0x02);
//	*Memory = 42;
//	return *Memory;
//}
import "C"

func main() {
	C.f()
}

The above program installs an exception handler, allocates a piece of read only memory, and tries writing to it. This raises an exception at runtime, but the exception handler enables writing to the memory region and restarts execution. So this function is just a roundabout way to return 42.

When invoked from C, or even as a DllImport from Native AOT, this works fine. When invoked from Go however, the Go runtime intercepts the exception and terminates the program. The Go runtime should instead let the called function handle this. There’s nothing we can do about this from .NET side. The bug needs to be fixed in Go.

Looks like several people have ran into this already outside of Native AOT. Here’s one issue, but looking through the bug tracker, there are more associated issues about exception handling not working correctly under CGO: golang/go#52763

The bug should not affect regular exception throws in Native AOT, but will affect the ability of catching things like null references, division by zero, integer overflows, and data misalignment exceptions (i.e. exceptions raised by the CPU).

@ghost ghost removed the untriaged New issue has not been triaged by the area owner label Sep 28, 2023
@MichalStrehovsky
Copy link
Member

The bug should not affect regular exception throws in Native AOT, but will affect the ability of catching things like null references, division by zero, integer overflows, and data misalignment exceptions (i.e. exceptions raised by the CPU).

Cc @AaronRobinsonMSFT in case someone runs into this with DNNE and Golang - knowing this might save you some investigation time.

@AaronRobinsonMSFT
Copy link
Member

Thanks @MichalStrehovsky.

This is similar to issues we have when the JVM and CoreCLR are in the same process. CoreCLR installs a signal handler and can causing significant disruption with the JVM when trying to handle exceptions.

The gist here is loading multiple runtimes, regardless of language, can cause significant and unforeseen issues, particularly where exceptions are involved.

@ghost ghost locked as resolved and limited conversation to collaborators Nov 3, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

3 participants