JDK-8260509 : JNI-originated "guarantee(thread->can_call_java()) failed: cannot make java calls from the native compiler" crash
  • Type: Bug
  • Component: hotspot
  • Sub-Component: runtime
  • Affected Version: 11-pool,15-pool
  • Priority: P4
  • Status: In Progress
  • Resolution: Unresolved
  • Submitted: 2021-01-27
  • Updated: 2021-02-25
Related Reports
Relates :  
Relates :  
Description
the crash manifests as follows:

Current CompileTask:
C1:4333045 636192    b  3       com.intellij.spring.model.xml.DomSpringBeanImpl$$EnhancerByJetBrainsMainCglib$$ff4745b3::CGLIB$STATICHOOK144 (903 bytes)

Stack: [0x00007f40c4e8b000,0x00007f40c4f8c000],  sp=0x00007f40c4f889f0,  free space=1014k
Native frames: (J=compiled Java code, A=aot compiled Java code, j=interpreted, Vv=VM code, C=native code)
V  [libjvm.so+0xd666df]  VMError::report_and_die(int, char const*, char const*, __va_list_tag*, Thread*, unsigned char*, void*, void*, char const*, int, unsigned long)+0x37f
V  [libjvm.so+0xd6737f]  VMError::report_and_die(Thread*, void*, char const*, int, char const*, char const*, __va_list_tag*)+0x2f
V  [libjvm.so+0x653007]  report_vm_error(char const*, int, char const*, char const*, ...)+0xf7
V  [libjvm.so+0x83546b]  JavaCallWrapper::JavaCallWrapper(methodHandle const&, Handle, JavaValue*, Thread*)+0x2db
V  [libjvm.so+0x8372ce]  JavaCalls::call_helper(JavaValue*, methodHandle const&, JavaCallArguments*, Thread*)+0x19e
V  [libjvm.so+0x8ad9c0]  jni_invoke_nonstatic(JNIEnv_*, JavaValue*, _jobject*, JNICallType, _jmethodID*, JNI_ArgumentPusher*, Thread*) [clone .constprop.244]+0x2c0
V  [libjvm.so+0x8b07c3]  jni_CallVoidMethod+0x163
C  [jna15702419600633290455.tmp+0x13644]  ffi_call_unix64+0x4c

It seems like JDK-8217765 has not ruled out all possibilities for this guarantee. Another one is originated in JNI.

The image attached demonstrates the flow leading to the crash
Comments
Ignoring the "the application shouldn't be doing this" argument, I do agree that we can make it harder for the application to do the wrong thing and avoid the crash. But this goes beyond can_call_java() as I think all VM defined JavaThreads should be rejected by the attach/detach API.
25-02-2021

The sample program is not valid as nothing in JNI is declared to be usable from a signal handler. The program demonstrates the crux of the problem - you cannot try to attach a completely unknown thread to the JVM. The signal handler has no idea what thread it is in, or whether it can attach (notwithstanding none of the JNI functions claim to be signal safe). Sorry but this is just broken application logic. Also note that attaching an already attached thread is a no-op. In such a case you cannot know if you need to detach again. If you detach it when you shouldn't then you will break the application. But if you fail to detach a thread that wasn't attached then you can introduce all kinds of other brokenness. You simply cannot, in any reliable way, decide whether to attach/detach an unknown thread and attempt to execute Java code in it.
24-02-2021

I attached standalone reproducer for the problem. Please, look at the HelloJNI.c - on my opinion, this program is valid from JNI point of view, so VM should deny AttachCurrentThread call rather than crash.
24-02-2021

An application/library should not be hijacking unknown threads and trying to use them for its own purposes!
07-02-2021

The root of the problem is the fact that CompilerThread is inherited from JavaThread (is_Java_thread() == True) but actually is not a java thread and can't call Java. JNI application has no possibility to distinguish between true java thread and compiler one.
06-02-2021

That fix is not correct. If a thread is already attached to the VM we don't care whether it can call Java or not. And no JavaThread that can't call Java should ever be attempting to re-attach to the VM! The bug is in this library call: C [jna15702419600633290455.tmp+0x13644] ffi_call_unix64+0x4c as we appear to be hijacking a VM thread to invoke Java via JNI.
28-01-2021

fix being tested: diff --git a/src/hotspot/share/prims/jni.cpp b/src/hotspot/share/prims/jni.cpp index 9c4ecd8ceb9..dd6741a5f09 100644 --- a/src/hotspot/share/prims/jni.cpp +++ b/src/hotspot/share/prims/jni.cpp @@ -3761,7 +3761,7 @@ static jint attach_current_thread(JavaVM *vm, void **penv, void *_args, bool dae Thread* t = Thread::current_or_null(); if (t != NULL) { // If executing from an atexit hook we may be in the VMThread. - if (t->is_Java_thread()) { + if (t->is_Java_thread() && t->can_call_java()) { // If the thread has been attached this operation is a no-op *(JNIEnv**)penv = t->as_Java_thread()->jni_environment(); return JNI_OK; @@ -3891,7 +3891,7 @@ jint JNICALL jni_DetachCurrentThread(JavaVM *vm) { } // If executing from an atexit hook we may be in the VMThread. - if (!current->is_Java_thread()) { + if (!current->is_Java_thread() || !current->can_call_java()) { HOTSPOT_JNI_DETACHCURRENTTHREAD_RETURN((uint32_t) JNI_ERR); return JNI_ERR; } @@ -3951,7 +3951,7 @@ jint JNICALL jni_GetEnv(JavaVM *vm, void **penv, jint version) { #endif // !JVMPI_VERSION_1 Thread* thread = Thread::current_or_null(); - if (thread != NULL && thread->is_Java_thread()) { + if (thread != NULL && thread->is_Java_thread() && thread->can_call_java()) { if (Threads::is_supported_jni_version_including_1_1(version)) { *(JNIEnv**)penv = thread->as_Java_thread()->jni_environment(); ret = JNI_OK;
27-01-2021