Skip to content

Commit ed63b71

Browse files
committed
8308341: JNI_GetCreatedJavaVMs returns a partially initialized JVM
8309231: ProblemList vmTestbase/nsk/jvmti/scenarios/jni_interception/JI05/ji05t001/TestDescription.java 8309171: Test vmTestbase/nsk/jvmti/scenarios/jni_interception/JI05/ji05t001/TestDescription.java fails after JDK-8308341 Reviewed-by: phh Backport-of: 1e6770fb978e630b38a70a05120c50f723bb66dc
1 parent be22b1a commit ed63b71

File tree

4 files changed

+174
-15
lines changed

4 files changed

+174
-15
lines changed

make/test/JtregNativeHotspot.gmk

+2-1
Original file line numberDiff line numberDiff line change
@@ -873,7 +873,7 @@ BUILD_HOTSPOT_JTREG_EXECUTABLES_LIBS_exesigtest := -ljvm
873873

874874
ifeq ($(call isTargetOs, windows), true)
875875
BUILD_HOTSPOT_JTREG_EXECUTABLES_CFLAGS_exeFPRegs := -MT
876-
BUILD_HOTSPOT_JTREG_EXCLUDE += exesigtest.c libterminatedThread.c libTestJNI.c libnativeStack.c
876+
BUILD_HOTSPOT_JTREG_EXCLUDE += exesigtest.c libterminatedThread.c libTestJNI.c libnativeStack.c exeGetCreatedJavaVMs.c
877877
BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libatExit := jvm.lib
878878
else
879879
BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libbootclssearch_agent += -lpthread
@@ -1511,6 +1511,7 @@ else
15111511
BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libterminatedThread += -lpthread
15121512
BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libatExit += -ljvm
15131513
BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libnativeStack += -lpthread
1514+
BUILD_HOTSPOT_JTREG_EXECUTABLES_LIBS_exeGetCreatedJavaVMs := -ljvm -lpthread
15141515
endif
15151516

15161517
# This evaluation is expensive and should only be done if this target was

src/hotspot/share/prims/jni.cpp

+26-14
Original file line numberDiff line numberDiff line change
@@ -3527,7 +3527,15 @@ static void post_thread_start_event(const JavaThread* jt) {
35273527
extern const struct JNIInvokeInterface_ jni_InvokeInterface;
35283528

35293529
// Global invocation API vars
3530-
volatile int vm_created = 0;
3530+
enum VM_Creation_State {
3531+
NOT_CREATED = 0,
3532+
IN_PROGRESS, // Most JNI operations are permitted during this phase to
3533+
// allow for initialization actions by libraries and agents.
3534+
COMPLETE
3535+
};
3536+
3537+
volatile VM_Creation_State vm_created = NOT_CREATED;
3538+
35313539
// Indicate whether it is safe to recreate VM. Recreation is only
35323540
// possible after a failed initial creation attempt in some cases.
35333541
volatile int safe_to_recreate_vm = 1;
@@ -3596,7 +3604,7 @@ static jint JNI_CreateJavaVM_inner(JavaVM **vm, void **penv, void *args) {
35963604
// We use Atomic::xchg rather than Atomic::add/dec since on some platforms
35973605
// the add/dec implementations are dependent on whether we are running
35983606
// on a multiprocessor Atomic::xchg does not have this problem.
3599-
if (Atomic::xchg(&vm_created, 1) == 1) {
3607+
if (Atomic::xchg(&vm_created, IN_PROGRESS) != NOT_CREATED) {
36003608
return JNI_EEXIST; // already created, or create attempt in progress
36013609
}
36023610

@@ -3609,8 +3617,6 @@ static jint JNI_CreateJavaVM_inner(JavaVM **vm, void **penv, void *args) {
36093617
return JNI_ERR;
36103618
}
36113619

3612-
assert(vm_created == 1, "vm_created is true during the creation");
3613-
36143620
/**
36153621
* Certain errors during initialization are recoverable and do not
36163622
* prevent this method from being called again at a later time
@@ -3627,9 +3633,11 @@ static jint JNI_CreateJavaVM_inner(JavaVM **vm, void **penv, void *args) {
36273633
if (result == JNI_OK) {
36283634
JavaThread *thread = JavaThread::current();
36293635
assert(!thread->has_pending_exception(), "should have returned not OK");
3630-
/* thread is thread_in_vm here */
3636+
// thread is thread_in_vm here
36313637
*vm = (JavaVM *)(&main_vm);
36323638
*(JNIEnv**)penv = thread->jni_environment();
3639+
// mark creation complete for other JNI ops
3640+
Atomic::release_store(&vm_created, COMPLETE);
36333641

36343642
#if INCLUDE_JVMCI
36353643
if (EnableJVMCI) {
@@ -3694,7 +3702,8 @@ static jint JNI_CreateJavaVM_inner(JavaVM **vm, void **penv, void *args) {
36943702
*(JNIEnv**)penv = 0;
36953703
// reset vm_created last to avoid race condition. Use OrderAccess to
36963704
// control both compiler and architectural-based reordering.
3697-
Atomic::release_store(&vm_created, 0);
3705+
assert(vm_created == IN_PROGRESS, "must be");
3706+
Atomic::release_store(&vm_created, NOT_CREATED);
36983707
}
36993708

37003709
// Flush stdout and stderr before exit.
@@ -3723,7 +3732,7 @@ _JNI_IMPORT_OR_EXPORT_ jint JNICALL JNI_CreateJavaVM(JavaVM **vm, void **penv, v
37233732
_JNI_IMPORT_OR_EXPORT_ jint JNICALL JNI_GetCreatedJavaVMs(JavaVM **vm_buf, jsize bufLen, jsize *numVMs) {
37243733
HOTSPOT_JNI_GETCREATEDJAVAVMS_ENTRY((void **) vm_buf, bufLen, (uintptr_t *) numVMs);
37253734

3726-
if (vm_created == 1) {
3735+
if (vm_created == COMPLETE) {
37273736
if (numVMs != NULL) *numVMs = 1;
37283737
if (bufLen > 0) *vm_buf = (JavaVM *)(&main_vm);
37293738
} else {
@@ -3743,7 +3752,7 @@ static jint JNICALL jni_DestroyJavaVM_inner(JavaVM *vm) {
37433752
jint res = JNI_ERR;
37443753
DT_RETURN_MARK(DestroyJavaVM, jint, (const jint&)res);
37453754

3746-
if (vm_created == 0) {
3755+
if (vm_created == NOT_CREATED) {
37473756
res = JNI_ERR;
37483757
return res;
37493758
}
@@ -3767,7 +3776,7 @@ static jint JNICALL jni_DestroyJavaVM_inner(JavaVM *vm) {
37673776
ThreadStateTransition::transition_from_native(thread, _thread_in_vm);
37683777
Threads::destroy_vm();
37693778
// Don't bother restoring thread state, VM is gone.
3770-
vm_created = 0;
3779+
vm_created = NOT_CREATED;
37713780
return JNI_OK;
37723781
}
37733782

@@ -3904,7 +3913,8 @@ static jint attach_current_thread(JavaVM *vm, void **penv, void *_args, bool dae
39043913

39053914
jint JNICALL jni_AttachCurrentThread(JavaVM *vm, void **penv, void *_args) {
39063915
HOTSPOT_JNI_ATTACHCURRENTTHREAD_ENTRY(vm, penv, _args);
3907-
if (vm_created == 0) {
3916+
if (vm_created == NOT_CREATED) {
3917+
// Not sure how we could possibly get here.
39083918
HOTSPOT_JNI_ATTACHCURRENTTHREAD_RETURN((uint32_t) JNI_ERR);
39093919
return JNI_ERR;
39103920
}
@@ -3917,7 +3927,8 @@ jint JNICALL jni_AttachCurrentThread(JavaVM *vm, void **penv, void *_args) {
39173927

39183928
jint JNICALL jni_DetachCurrentThread(JavaVM *vm) {
39193929
HOTSPOT_JNI_DETACHCURRENTTHREAD_ENTRY(vm);
3920-
if (vm_created == 0) {
3930+
if (vm_created == NOT_CREATED) {
3931+
// Not sure how we could possibly get here.
39213932
HOTSPOT_JNI_DETACHCURRENTTHREAD_RETURN(JNI_ERR);
39223933
return JNI_ERR;
39233934
}
@@ -3980,7 +3991,7 @@ jint JNICALL jni_GetEnv(JavaVM *vm, void **penv, jint version) {
39803991
jint ret = JNI_ERR;
39813992
DT_RETURN_MARK(GetEnv, jint, (const jint&)ret);
39823993

3983-
if (vm_created == 0) {
3994+
if (vm_created == NOT_CREATED) {
39843995
*penv = NULL;
39853996
ret = JNI_EDETACHED;
39863997
return ret;
@@ -4031,8 +4042,9 @@ jint JNICALL jni_GetEnv(JavaVM *vm, void **penv, jint version) {
40314042

40324043
jint JNICALL jni_AttachCurrentThreadAsDaemon(JavaVM *vm, void **penv, void *_args) {
40334044
HOTSPOT_JNI_ATTACHCURRENTTHREADASDAEMON_ENTRY(vm, penv, _args);
4034-
if (vm_created == 0) {
4035-
HOTSPOT_JNI_ATTACHCURRENTTHREADASDAEMON_RETURN((uint32_t) JNI_ERR);
4045+
if (vm_created == NOT_CREATED) {
4046+
// Not sure how we could possibly get here.
4047+
HOTSPOT_JNI_ATTACHCURRENTTHREADASDAEMON_RETURN((uint32_t) JNI_ERR);
40364048
return JNI_ERR;
40374049
}
40384050

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
/*
25+
* @test
26+
* @library /test/lib
27+
* @requires os.family != "Windows"
28+
* @run driver TestGetCreatedJavaVMs
29+
*/
30+
import jdk.test.lib.Utils;
31+
import jdk.test.lib.process.ProcessTools;
32+
import jdk.test.lib.process.OutputAnalyzer;
33+
34+
35+
public class TestGetCreatedJavaVMs {
36+
public static void main(String args[]) throws Exception {
37+
ProcessBuilder pb = ProcessTools.createNativeTestProcessBuilder("GetCreatedJavaVMs");
38+
OutputAnalyzer output = ProcessTools.executeProcess(pb);
39+
output.shouldHaveExitValue(0);
40+
output.reportDiagnosticSummary();
41+
}
42+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
/*
2+
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
/* This code tests concurrent creation of and then attach to a JVM.
25+
* Two threads race to create the JVM, the loser then checks GetCreatedJavaVMs
26+
* and attaches to the returned JVM. Prior to the fix this could crash as the
27+
* JVM is not fully initialized.
28+
*/
29+
#include "jni.h"
30+
#include <string.h>
31+
#include <pthread.h>
32+
#include <stdio.h>
33+
#include <stdint.h>
34+
#include <stdlib.h>
35+
36+
#define NUM_THREADS 2
37+
38+
void *thread_runner(void *threadid) {
39+
int tid;
40+
tid = (int)(intptr_t)threadid;
41+
42+
JavaVM *vm;
43+
JNIEnv *env = 0;
44+
45+
JavaVMInitArgs vm_args;
46+
JavaVMOption options[0];
47+
vm_args.version = JNI_VERSION_1_2;
48+
vm_args.nOptions = 0;
49+
vm_args.options = options;
50+
vm_args.ignoreUnrecognized = JNI_FALSE;
51+
52+
printf("[%d] BEGIN JNI_CreateJavaVM\n", tid);
53+
jint create_res = JNI_CreateJavaVM(&vm, (void **)&env, &vm_args);
54+
printf("[%d] END JNI_CreateJavaVM\n", tid);
55+
56+
if (create_res != JNI_OK) {
57+
printf("[%d] Error creating JVM: %d\n", tid, create_res);
58+
if (create_res == JNI_EEXIST) {
59+
jsize count;
60+
printf("[%d] BEGIN JNI_GetCreatedJavaVMs\n", tid);
61+
jint get_res = JNI_GetCreatedJavaVMs(&vm, 1, &count);
62+
printf("[%d] END JNI_GetCreatedJavaVMs\n", tid);
63+
64+
if (get_res != JNI_OK) {
65+
printf("[%d] Error obtaining created VMs: %d\n", tid, get_res);
66+
pthread_exit(NULL);
67+
} else {
68+
printf("[%d] Obtained %d created VMs\n", tid, count);
69+
}
70+
if (count > 0) {
71+
printf("[%d] BEGIN AttachCurrentThread\n", tid);
72+
get_res = (*vm)->AttachCurrentThread(vm, (void **)&env, NULL);
73+
printf("[%d] END AttachCurrentThread - %s\n", tid,
74+
(get_res == JNI_OK ? "succeeded" : "failed"));
75+
if (get_res == JNI_OK) {
76+
(*vm)->DetachCurrentThread(vm);
77+
}
78+
}
79+
pthread_exit(NULL);
80+
} else {
81+
pthread_exit(NULL);
82+
}
83+
} else {
84+
printf("[%d] Created a JVM\n", tid);
85+
}
86+
87+
pthread_exit(NULL);
88+
}
89+
90+
int main (int argc, char* argv[]) {
91+
pthread_t threads[NUM_THREADS];
92+
for (int i = 0; i < NUM_THREADS; i++ ) {
93+
printf("[*] Creating thread %d\n", i);
94+
int status = pthread_create(&threads[i], NULL, thread_runner, (void *)(intptr_t)i);
95+
if (status != 0) {
96+
printf("[*] Error creating thread %d - %d\n", i, status);
97+
exit(-1);
98+
}
99+
}
100+
for (int i = 0; i < NUM_THREADS; i++ ) {
101+
pthread_join(threads[i], NULL);
102+
}
103+
return 0;
104+
}

0 commit comments

Comments
 (0)