jni crashes on exit
Anton Tarasov
anton.tarasov at jetbrains.com
Fri Feb 7 08:02:02 UTC 2020
Hi David,
I'll file a bug then.
Yes, I actually saw that check also, and even tried to change it to:
if (thread != NULL && thread->is_Java_thread())
like in jni_GetEnv(..). However, I did not return JNI_ERR immediately but
let the method attach the thread, however that just caused the process to
hang on exit.
Thanks,
Anton.
On Fri, Feb 7, 2020 at 10:32 AM David Holmes <david.holmes at oracle.com>
wrote:
> 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