jni crashes on exit

David Holmes david.holmes at oracle.com
Fri Feb 7 07:30:25 UTC 2020


Hi Anton,

Yes please file a bug. I think I already see the problem though. If the 
attach request is coming from the VMThread that is not valid. But the 
attach logic simply does this to check for an already attached thread:

  Thread* t = Thread::current_or_null();
   if (t != NULL) {
     // If the thread has been attached this operation is a no-op
     *(JNIEnv**)penv = ((JavaThread*) t)->jni_environment();
     return JNI_OK;
   }

so in the VMThread case we are casting the thread pointer to a 
JavaThread to get the JNIEnv but that is just garbage. We need to check 
that the attached thread is in fact a JavaThread else return JNI_ERR.

Cheers,
David

On 7/02/2020 4:35 pm, Anton Tarasov wrote:
> Hello,
> 
> I'm experiencing a crash in JNI with jdk11(15) on MS Windows. I was able to
> reproduce it with a standalone test case.
> 
> In short: a java app loads a native lib and forces exit, the native lib
> registers an exit handler where it successfully obtains JNIEnv and calls a
> function on it.
> 
> Please find the compile instructions and the code below.
> 
> Build and run the app:
> 
> $ java JavaMain
> 
> - - - - - - - - - -
> In: JNI_OnLoad
> In: at_exit_handler
> calling: jvm->GetEnv: JNI_EDETACHED
> calling: jvm->AttachCurrentThreadAsDaemon: JNI_OK [env != NULL,
> env->functions == 00000000]
> #
> # A fatal error has been detected by the Java Runtime Environment:
> #
> #  EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x00007ff9bb9165a6,
> pid=13912, tid=13020
> #
> # JRE version: OpenJDK Runtime Environment (15.0+8) (build 15-ea+8-219)
> # Java VM: OpenJDK 64-Bit Server VM (15-ea+8-219, mixed mode, sharing,
> tiered, compressed oops, g1 gc, windows-amd64)
> # Problematic frame:
> # C  [main.dll+0x65a6]
> #
> - - - - - - - - - -
> 
> The problem here is that AttachCurrentThreadAsDaemon returns JNI_OK and
> non-null JNIEnv* but null env->functions.
> 
> The stack trace I obtained is this (JVM is not yet destroyed, there's only
> one thread left alive):
> 
> - - - - - - - - - -
>    main.dll!at_exit_handler() Line 19 C++
>    main.dll!__crt_seh_guarded_call<int>::operator()<void <lambda>(void),int
> <lambda>(void) & __ptr64,void <lambda>(void)
>> (__acrt_lock_and_call::__l3::void <lambda>(void) && setup,
> _execute_onexit_table::__l22::int <lambda>(void) & action,
> __acrt_lock_and_call::__l4::void <lambda>(void) && cleanup) Line 199 C++
>    main.dll!_execute_onexit_table(_onexit_table_t * table) Line 222 C++
>    main.dll!common_exit(const int return_code, const _crt_exit_cleanup_mode
> cleanup_mode, const _crt_exit_return_mode return_mode) Line 215 C++
>    [External Code]
>    jvm.dll!os::win32::exit_process_or_thread(os::win32::Ept what, int
> exit_code) Line 3987 C++
>    jvm.dll!VM_Operation::evaluate() Line 68 C++
>    jvm.dll!VMThread::evaluate_operation(VM_Operation * op) Line 414 C++
>    jvm.dll!VMThread::loop() Line 548 C++
>    jvm.dll!VMThread::run() Line 315 C++
>    jvm.dll!Thread::call_run() Line 393 C++
>    jvm.dll!thread_native_entry(Thread * thread) Line 460 C++
>    [External Code]
> - - - - - - - - - -
> 
> Now run the app with "skip_exit" option.
> 
> $ java JavaMain skip_exit
> 
> - - - - - - - - - -
> In: JNI_OnLoad
> In: at_exit_handler
> calling: jvm->GetEnv: JNI_EDETACHED
> calling: jvm->AttachCurrentThreadAsDaemon: JNI_ERR
> - - - - - - - - - -
> 
> Now it's ok, the stack trace (JVM has already been destroyed):
> 
> - - - - - - - - - -
>    main.dll!at_exit_handler() Line 25 C++
>    main.dll!__crt_seh_guarded_call<int>::operator()<void <lambda>(void),int
> <lambda>(void) & __ptr64,void <lambda>(void)
>> (__acrt_lock_and_call::__l3::void <lambda>(void) && setup,
> _execute_onexit_table::__l22::int <lambda>(void) & action,
> __acrt_lock_and_call::__l4::void <lambda>(void) && cleanup) Line 199 C++
>    main.dll!_execute_onexit_table(_onexit_table_t * table) Line 222 C++
>    main.dll!common_exit(const int return_code, const _crt_exit_cleanup_mode
> cleanup_mode, const _crt_exit_return_mode return_mode) Line 215 C++
>    [External Code]
>    java.exe!00007ff6fd4213d7() Unknown
>    [External Code]
> - - - - - - - - - -
> 
> Obtaining JNIEnv from the process exit handler may seem useless, however
> the situation is possible when a chain of destructors are called on exit,
> causing JNI wrappers to release its refs without checking in which
> circumstances the destructor is called (as was the real app case). I'd
> expect that JVM always returns JNI_ERR in such a case.
> 
> Should I file a bug?
> 
> With best regards,
> Anton.
> 
> INSTRUCTIONS and CODE:
> 
> //
> // build main.dll with ms cl.exe
> //
> $ "c:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat"
> amd64
> $ cl /I %JDK_REPO%/src/java.base/share/native/include /I
> %JDK_REPO%/src/java.base/windows/native/include
> %JDK_REPO%/build/windows-x86_64-normal-server-release/images/jdk/lib/jvm.lib
> /Z7 /LD main.cpp
> 
> //
> // JavaMain.java
> //
> public class JavaMain {
>      public static void main(String[] args) {
>          System.loadLibrary("main");
>          if (args.length == 0 || !args[0].equals("skip_exit")) {
>              System.exit(0);
>          }
>      }
> }
> 
> //
> // main.cpp
> //
> #include <jni.h>
> #include <stdio.h>
> #include <stdlib.h>
> 
> static JavaVM *jvm;
> 
> void at_exit_handler() {
>      fprintf(stdout, "In: at_exit_handler\n");
>      JNIEnv *env;
>      fprintf(stdout, "calling: jvm->GetEnv");
>      jint res = jvm->GetEnv((void**)&env, JNI_VERSION_1_6);
>      if (res == JNI_EDETACHED) {
>          fprintf(stdout, ": JNI_EDETACHED\ncalling:
> jvm->AttachCurrentThreadAsDaemon");
>          res = jvm->AttachCurrentThreadAsDaemon((void **)&env, NULL);
>      }
>      if (res == JNI_OK) {
>          if (env) {
>              fprintf(stderr, ": JNI_OK [env != NULL, env->functions ==
> %08x]\n", env->functions);
>              jint ver = env->GetVersion(); // <-- CRASH !!!
>              fprintf(stderr, "env->GetVersion() %d\n", ver);
>          } else if (!env) {
>              fprintf(stdout, ": JNI_OK [env == NULL]\n");
>          }
>      } else {
>          fprintf(stderr, ": JNI_ERR\n");
>      }
> }
> 
> jint JNI_OnLoad(JavaVM *vm, void *reserved)
> {
>      fprintf(stdout, "In: JNI_OnLoad\n");
> 
> //    fprintf(stdout, "press ENTER to continue...\n");
> //    getchar();
> 
>      jvm = vm;
>      atexit(at_exit_handler);
> 
>      return JNI_VERSION_1_6;
> }
> 


More information about the hotspot-runtime-dev mailing list