8308341: JNI_GetCreatedJavaVMs returns a partially initialized JVM
Reviewed-by: jsjolen, gziemski
This commit is contained in:
parent
cb40db052c
commit
1e6770fb97
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved.
|
# Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||||
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
#
|
#
|
||||||
# This code is free software; you can redistribute it and/or modify it
|
# This code is free software; you can redistribute it and/or modify it
|
||||||
@ -874,7 +874,7 @@ BUILD_HOTSPOT_JTREG_EXECUTABLES_LIBS_exesigtest := -ljvm
|
|||||||
|
|
||||||
ifeq ($(call isTargetOs, windows), true)
|
ifeq ($(call isTargetOs, windows), true)
|
||||||
BUILD_HOTSPOT_JTREG_EXECUTABLES_CFLAGS_exeFPRegs := -MT
|
BUILD_HOTSPOT_JTREG_EXECUTABLES_CFLAGS_exeFPRegs := -MT
|
||||||
BUILD_HOTSPOT_JTREG_EXCLUDE += exesigtest.c libterminatedThread.c libTestJNI.c libCompleteExit.c libTestPsig.c libnativeStack.c
|
BUILD_HOTSPOT_JTREG_EXCLUDE += exesigtest.c libterminatedThread.c libTestJNI.c libCompleteExit.c libTestPsig.c libnativeStack.c exeGetCreatedJavaVMs.c
|
||||||
BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libatExit := jvm.lib
|
BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libatExit := jvm.lib
|
||||||
BUILD_HOTSPOT_JTREG_EXECUTABLES_LIBS_exedaemonDestroy := jvm.lib
|
BUILD_HOTSPOT_JTREG_EXECUTABLES_LIBS_exedaemonDestroy := jvm.lib
|
||||||
else
|
else
|
||||||
@ -1516,6 +1516,7 @@ else
|
|||||||
BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libatExit += -ljvm
|
BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libatExit += -ljvm
|
||||||
BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libCompleteExit += -lpthread
|
BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libCompleteExit += -lpthread
|
||||||
BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libnativeStack += -lpthread
|
BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libnativeStack += -lpthread
|
||||||
|
BUILD_HOTSPOT_JTREG_EXECUTABLES_LIBS_exeGetCreatedJavaVMs := -ljvm -lpthread
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(ASAN_ENABLED), true)
|
ifeq ($(ASAN_ENABLED), true)
|
||||||
|
@ -3472,7 +3472,14 @@ struct JNINativeInterface_* jni_functions_nocheck() {
|
|||||||
extern const struct JNIInvokeInterface_ jni_InvokeInterface;
|
extern const struct JNIInvokeInterface_ jni_InvokeInterface;
|
||||||
|
|
||||||
// Global invocation API vars
|
// Global invocation API vars
|
||||||
volatile int vm_created = 0;
|
enum VM_Creation_State {
|
||||||
|
NOT_CREATED = 0,
|
||||||
|
IN_PROGRESS,
|
||||||
|
COMPLETE
|
||||||
|
};
|
||||||
|
|
||||||
|
volatile VM_Creation_State vm_created = NOT_CREATED;
|
||||||
|
|
||||||
// Indicate whether it is safe to recreate VM. Recreation is only
|
// Indicate whether it is safe to recreate VM. Recreation is only
|
||||||
// possible after a failed initial creation attempt in some cases.
|
// possible after a failed initial creation attempt in some cases.
|
||||||
volatile int safe_to_recreate_vm = 1;
|
volatile int safe_to_recreate_vm = 1;
|
||||||
@ -3541,7 +3548,7 @@ static jint JNI_CreateJavaVM_inner(JavaVM **vm, void **penv, void *args) {
|
|||||||
// We use Atomic::xchg rather than Atomic::add/dec since on some platforms
|
// We use Atomic::xchg rather than Atomic::add/dec since on some platforms
|
||||||
// the add/dec implementations are dependent on whether we are running
|
// the add/dec implementations are dependent on whether we are running
|
||||||
// on a multiprocessor Atomic::xchg does not have this problem.
|
// on a multiprocessor Atomic::xchg does not have this problem.
|
||||||
if (Atomic::xchg(&vm_created, 1) == 1) {
|
if (Atomic::xchg(&vm_created, IN_PROGRESS) != NOT_CREATED) {
|
||||||
return JNI_EEXIST; // already created, or create attempt in progress
|
return JNI_EEXIST; // already created, or create attempt in progress
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3554,8 +3561,6 @@ static jint JNI_CreateJavaVM_inner(JavaVM **vm, void **penv, void *args) {
|
|||||||
return JNI_ERR;
|
return JNI_ERR;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(vm_created == 1, "vm_created is true during the creation");
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Certain errors during initialization are recoverable and do not
|
* Certain errors during initialization are recoverable and do not
|
||||||
* prevent this method from being called again at a later time
|
* prevent this method from being called again at a later time
|
||||||
@ -3572,9 +3577,11 @@ static jint JNI_CreateJavaVM_inner(JavaVM **vm, void **penv, void *args) {
|
|||||||
if (result == JNI_OK) {
|
if (result == JNI_OK) {
|
||||||
JavaThread *thread = JavaThread::current();
|
JavaThread *thread = JavaThread::current();
|
||||||
assert(!thread->has_pending_exception(), "should have returned not OK");
|
assert(!thread->has_pending_exception(), "should have returned not OK");
|
||||||
/* thread is thread_in_vm here */
|
// thread is thread_in_vm here
|
||||||
*vm = (JavaVM *)(&main_vm);
|
*vm = (JavaVM *)(&main_vm);
|
||||||
*(JNIEnv**)penv = thread->jni_environment();
|
*(JNIEnv**)penv = thread->jni_environment();
|
||||||
|
// mark creation complete for other JNI ops
|
||||||
|
Atomic::release_store(&vm_created, COMPLETE);
|
||||||
|
|
||||||
#if INCLUDE_JVMCI
|
#if INCLUDE_JVMCI
|
||||||
if (EnableJVMCI) {
|
if (EnableJVMCI) {
|
||||||
@ -3637,7 +3644,8 @@ static jint JNI_CreateJavaVM_inner(JavaVM **vm, void **penv, void *args) {
|
|||||||
*(JNIEnv**)penv = 0;
|
*(JNIEnv**)penv = 0;
|
||||||
// reset vm_created last to avoid race condition. Use OrderAccess to
|
// reset vm_created last to avoid race condition. Use OrderAccess to
|
||||||
// control both compiler and architectural-based reordering.
|
// control both compiler and architectural-based reordering.
|
||||||
Atomic::release_store(&vm_created, 0);
|
assert(vm_created == IN_PROGRESS, "must be");
|
||||||
|
Atomic::release_store(&vm_created, NOT_CREATED);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flush stdout and stderr before exit.
|
// Flush stdout and stderr before exit.
|
||||||
@ -3666,7 +3674,7 @@ _JNI_IMPORT_OR_EXPORT_ jint JNICALL JNI_CreateJavaVM(JavaVM **vm, void **penv, v
|
|||||||
_JNI_IMPORT_OR_EXPORT_ jint JNICALL JNI_GetCreatedJavaVMs(JavaVM **vm_buf, jsize bufLen, jsize *numVMs) {
|
_JNI_IMPORT_OR_EXPORT_ jint JNICALL JNI_GetCreatedJavaVMs(JavaVM **vm_buf, jsize bufLen, jsize *numVMs) {
|
||||||
HOTSPOT_JNI_GETCREATEDJAVAVMS_ENTRY((void **) vm_buf, bufLen, (uintptr_t *) numVMs);
|
HOTSPOT_JNI_GETCREATEDJAVAVMS_ENTRY((void **) vm_buf, bufLen, (uintptr_t *) numVMs);
|
||||||
|
|
||||||
if (vm_created == 1) {
|
if (vm_created == COMPLETE) {
|
||||||
if (numVMs != nullptr) *numVMs = 1;
|
if (numVMs != nullptr) *numVMs = 1;
|
||||||
if (bufLen > 0) *vm_buf = (JavaVM *)(&main_vm);
|
if (bufLen > 0) *vm_buf = (JavaVM *)(&main_vm);
|
||||||
} else {
|
} else {
|
||||||
@ -3686,7 +3694,7 @@ static jint JNICALL jni_DestroyJavaVM_inner(JavaVM *vm) {
|
|||||||
jint res = JNI_ERR;
|
jint res = JNI_ERR;
|
||||||
DT_RETURN_MARK(DestroyJavaVM, jint, (const jint&)res);
|
DT_RETURN_MARK(DestroyJavaVM, jint, (const jint&)res);
|
||||||
|
|
||||||
if (vm_created == 0) {
|
if (vm_created != COMPLETE) {
|
||||||
res = JNI_ERR;
|
res = JNI_ERR;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@ -3717,7 +3725,7 @@ static jint JNICALL jni_DestroyJavaVM_inner(JavaVM *vm) {
|
|||||||
ThreadStateTransition::transition_from_native(thread, _thread_in_vm);
|
ThreadStateTransition::transition_from_native(thread, _thread_in_vm);
|
||||||
Threads::destroy_vm();
|
Threads::destroy_vm();
|
||||||
// Don't bother restoring thread state, VM is gone.
|
// Don't bother restoring thread state, VM is gone.
|
||||||
vm_created = 0;
|
vm_created = NOT_CREATED;
|
||||||
return JNI_OK;
|
return JNI_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3851,7 +3859,8 @@ static jint attach_current_thread(JavaVM *vm, void **penv, void *_args, bool dae
|
|||||||
|
|
||||||
jint JNICALL jni_AttachCurrentThread(JavaVM *vm, void **penv, void *_args) {
|
jint JNICALL jni_AttachCurrentThread(JavaVM *vm, void **penv, void *_args) {
|
||||||
HOTSPOT_JNI_ATTACHCURRENTTHREAD_ENTRY(vm, penv, _args);
|
HOTSPOT_JNI_ATTACHCURRENTTHREAD_ENTRY(vm, penv, _args);
|
||||||
if (vm_created == 0) {
|
if (vm_created != COMPLETE) {
|
||||||
|
// Not sure how we could possibly get here.
|
||||||
HOTSPOT_JNI_ATTACHCURRENTTHREAD_RETURN((uint32_t) JNI_ERR);
|
HOTSPOT_JNI_ATTACHCURRENTTHREAD_RETURN((uint32_t) JNI_ERR);
|
||||||
return JNI_ERR;
|
return JNI_ERR;
|
||||||
}
|
}
|
||||||
@ -3864,7 +3873,8 @@ jint JNICALL jni_AttachCurrentThread(JavaVM *vm, void **penv, void *_args) {
|
|||||||
|
|
||||||
jint JNICALL jni_DetachCurrentThread(JavaVM *vm) {
|
jint JNICALL jni_DetachCurrentThread(JavaVM *vm) {
|
||||||
HOTSPOT_JNI_DETACHCURRENTTHREAD_ENTRY(vm);
|
HOTSPOT_JNI_DETACHCURRENTTHREAD_ENTRY(vm);
|
||||||
if (vm_created == 0) {
|
if (vm_created != COMPLETE) {
|
||||||
|
// Not sure how we could possibly get here.
|
||||||
HOTSPOT_JNI_DETACHCURRENTTHREAD_RETURN(JNI_ERR);
|
HOTSPOT_JNI_DETACHCURRENTTHREAD_RETURN(JNI_ERR);
|
||||||
return JNI_ERR;
|
return JNI_ERR;
|
||||||
}
|
}
|
||||||
@ -3927,7 +3937,10 @@ jint JNICALL jni_GetEnv(JavaVM *vm, void **penv, jint version) {
|
|||||||
jint ret = JNI_ERR;
|
jint ret = JNI_ERR;
|
||||||
DT_RETURN_MARK(GetEnv, jint, (const jint&)ret);
|
DT_RETURN_MARK(GetEnv, jint, (const jint&)ret);
|
||||||
|
|
||||||
if (vm_created == 0) {
|
// We can be called by native libraries in the JDK during VM
|
||||||
|
// initialization, so only bail-out if something seems very wrong.
|
||||||
|
// Though how would we get here in that case?
|
||||||
|
if (vm_created == NOT_CREATED) {
|
||||||
*penv = nullptr;
|
*penv = nullptr;
|
||||||
ret = JNI_EDETACHED;
|
ret = JNI_EDETACHED;
|
||||||
return ret;
|
return ret;
|
||||||
@ -3978,7 +3991,8 @@ jint JNICALL jni_GetEnv(JavaVM *vm, void **penv, jint version) {
|
|||||||
|
|
||||||
jint JNICALL jni_AttachCurrentThreadAsDaemon(JavaVM *vm, void **penv, void *_args) {
|
jint JNICALL jni_AttachCurrentThreadAsDaemon(JavaVM *vm, void **penv, void *_args) {
|
||||||
HOTSPOT_JNI_ATTACHCURRENTTHREADASDAEMON_ENTRY(vm, penv, _args);
|
HOTSPOT_JNI_ATTACHCURRENTTHREADASDAEMON_ENTRY(vm, penv, _args);
|
||||||
if (vm_created == 0) {
|
if (vm_created != COMPLETE) {
|
||||||
|
// Not sure how we could possibly get here.
|
||||||
HOTSPOT_JNI_ATTACHCURRENTTHREADASDAEMON_RETURN((uint32_t) JNI_ERR);
|
HOTSPOT_JNI_ATTACHCURRENTTHREADASDAEMON_RETURN((uint32_t) JNI_ERR);
|
||||||
return JNI_ERR;
|
return JNI_ERR;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @test
|
||||||
|
* @library /test/lib
|
||||||
|
* @requires os.family != "Windows"
|
||||||
|
* @run driver TestGetCreatedJavaVMs
|
||||||
|
*/
|
||||||
|
import jdk.test.lib.Utils;
|
||||||
|
import jdk.test.lib.process.ProcessTools;
|
||||||
|
import jdk.test.lib.process.OutputAnalyzer;
|
||||||
|
|
||||||
|
|
||||||
|
public class TestGetCreatedJavaVMs {
|
||||||
|
public static void main(String args[]) throws Exception {
|
||||||
|
ProcessBuilder pb = ProcessTools.createNativeTestProcessBuilder("GetCreatedJavaVMs");
|
||||||
|
OutputAnalyzer output = ProcessTools.executeProcess(pb);
|
||||||
|
output.shouldHaveExitValue(0);
|
||||||
|
output.reportDiagnosticSummary();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,104 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* This code tests concurrent creation of and then attach to a JVM.
|
||||||
|
* Two threads race to create the JVM, the loser then checks GetCreatedJavaVMs
|
||||||
|
* and attaches to the returned JVM. Prior to the fix this could crash as the
|
||||||
|
* JVM is not fully initialized.
|
||||||
|
*/
|
||||||
|
#include "jni.h"
|
||||||
|
#include <string.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#define NUM_THREADS 2
|
||||||
|
|
||||||
|
void *thread_runner(void *threadid) {
|
||||||
|
int tid;
|
||||||
|
tid = (int)(intptr_t)threadid;
|
||||||
|
|
||||||
|
JavaVM *vm;
|
||||||
|
JNIEnv *env = 0;
|
||||||
|
|
||||||
|
JavaVMInitArgs vm_args;
|
||||||
|
JavaVMOption options[0];
|
||||||
|
vm_args.version = JNI_VERSION_1_2;
|
||||||
|
vm_args.nOptions = 0;
|
||||||
|
vm_args.options = options;
|
||||||
|
vm_args.ignoreUnrecognized = JNI_FALSE;
|
||||||
|
|
||||||
|
printf("[%d] BEGIN JNI_CreateJavaVM\n", tid);
|
||||||
|
jint create_res = JNI_CreateJavaVM(&vm, (void **)&env, &vm_args);
|
||||||
|
printf("[%d] END JNI_CreateJavaVM\n", tid);
|
||||||
|
|
||||||
|
if (create_res != JNI_OK) {
|
||||||
|
printf("[%d] Error creating JVM: %d\n", tid, create_res);
|
||||||
|
if (create_res == JNI_EEXIST) {
|
||||||
|
jsize count;
|
||||||
|
printf("[%d] BEGIN JNI_GetCreatedJavaVMs\n", tid);
|
||||||
|
jint get_res = JNI_GetCreatedJavaVMs(&vm, 1, &count);
|
||||||
|
printf("[%d] END JNI_GetCreatedJavaVMs\n", tid);
|
||||||
|
|
||||||
|
if (get_res != JNI_OK) {
|
||||||
|
printf("[%d] Error obtaining created VMs: %d\n", tid, get_res);
|
||||||
|
pthread_exit(NULL);
|
||||||
|
} else {
|
||||||
|
printf("[%d] Obtained %d created VMs\n", tid, count);
|
||||||
|
}
|
||||||
|
if (count > 0) {
|
||||||
|
printf("[%d] BEGIN AttachCurrentThread\n", tid);
|
||||||
|
get_res = (*vm)->AttachCurrentThread(vm, (void **)&env, NULL);
|
||||||
|
printf("[%d] END AttachCurrentThread - %s\n", tid,
|
||||||
|
(get_res == JNI_OK ? "succeeded" : "failed"));
|
||||||
|
if (get_res == JNI_OK) {
|
||||||
|
(*vm)->DetachCurrentThread(vm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pthread_exit(NULL);
|
||||||
|
} else {
|
||||||
|
pthread_exit(NULL);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
printf("[%d] Created a JVM\n", tid);
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_exit(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main (int argc, char* argv[]) {
|
||||||
|
pthread_t threads[NUM_THREADS];
|
||||||
|
for (int i = 0; i < NUM_THREADS; i++ ) {
|
||||||
|
printf("[*] Creating thread %d\n", i);
|
||||||
|
int status = pthread_create(&threads[i], NULL, thread_runner, (void *)(intptr_t)i);
|
||||||
|
if (status != 0) {
|
||||||
|
printf("[*] Error creating thread %d - %d\n", i, status);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int i = 0; i < NUM_THREADS; i++ ) {
|
||||||
|
pthread_join(threads[i], NULL);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user