jni crashes on exit
Anton Tarasov
anton.tarasov at jetbrains.com
Fri Feb 7 08:31:44 UTC 2020
The bug: https://bugs.openjdk.java.net/browse/JDK-8238676
Thanks,
Anton.
On Fri, Feb 7, 2020 at 11:02 AM Anton Tarasov <anton.tarasov at jetbrains.com>
wrote:
> 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