From 64ec8b3e5c8a8d44c92591710d73b833f13c1500 Mon Sep 17 00:00:00 2001
From: Leonid Mesnik <lmesnik@openjdk.org>
Date: Fri, 4 Jun 2021 17:22:01 +0000
Subject: [PATCH] 8212155: Race condition when posting dynamic_code_generated
 event leads to JVM crash

Reviewed-by: sspitsyn, dcubed
---
 src/hotspot/share/prims/jvmtiExport.cpp       | 14 +++--
 .../DynamicCodeGeneratedTest.java             | 57 +++++++++++++++++++
 .../libDynamicCodeGenerated.cpp               | 57 +++++++++++++++++++
 3 files changed, 122 insertions(+), 6 deletions(-)
 create mode 100644 test/hotspot/jtreg/serviceability/jvmti/DynamicCodeGenerated/DynamicCodeGeneratedTest.java
 create mode 100644 test/hotspot/jtreg/serviceability/jvmti/DynamicCodeGenerated/libDynamicCodeGenerated.cpp

diff --git a/src/hotspot/share/prims/jvmtiExport.cpp b/src/hotspot/share/prims/jvmtiExport.cpp
index b3951a913f1..dd0f1885c8b 100644
--- a/src/hotspot/share/prims/jvmtiExport.cpp
+++ b/src/hotspot/share/prims/jvmtiExport.cpp
@@ -2289,13 +2289,15 @@ void JvmtiExport::post_dynamic_code_generated_while_holding_locks(const char* na
   // register the stub with the current dynamic code event collector
   // Cannot take safepoint here so do not use state_for to get
   // jvmti thread state.
+  // The collector and/or state might be NULL if JvmtiDynamicCodeEventCollector
+  // has been initialized while JVMTI_EVENT_DYNAMIC_CODE_GENERATED was disabled.
   JvmtiThreadState* state = JavaThread::current()->jvmti_thread_state();
-  // state can only be NULL if the current thread is exiting which
-  // should not happen since we're trying to post an event
-  guarantee(state != NULL, "attempt to register stub via an exiting thread");
-  JvmtiDynamicCodeEventCollector* collector = state->get_dynamic_code_event_collector();
-  guarantee(collector != NULL, "attempt to register stub without event collector");
-  collector->register_stub(name, code_begin, code_end);
+  if (state != NULL) {
+    JvmtiDynamicCodeEventCollector *collector = state->get_dynamic_code_event_collector();
+    if (collector != NULL) {
+      collector->register_stub(name, code_begin, code_end);
+    }
+  }
 }
 
 // Collect all the vm internally allocated objects which are visible to java world
diff --git a/test/hotspot/jtreg/serviceability/jvmti/DynamicCodeGenerated/DynamicCodeGeneratedTest.java b/test/hotspot/jtreg/serviceability/jvmti/DynamicCodeGenerated/DynamicCodeGeneratedTest.java
new file mode 100644
index 00000000000..649fb319102
--- /dev/null
+++ b/test/hotspot/jtreg/serviceability/jvmti/DynamicCodeGenerated/DynamicCodeGeneratedTest.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2021, 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
+ * @bug 8212155
+ * @summary Test concurrent enabling and posting of DynamicCodeGenerated events.
+ * @requires vm.jvmti
+ * @library /test/lib
+ * @run main/othervm/native -agentlib:DynamicCodeGenerated DynamicCodeGeneratedTest
+ */
+
+import java.lang.ref.Reference;
+
+public class DynamicCodeGeneratedTest {
+    static {
+        System.loadLibrary("DynamicCodeGenerated");
+    }
+    public static native void changeEventNotificationMode();
+
+    public static void main(String[] args) {
+        // Try to enable DynamicCodeGenerated event while it is posted
+        // using JvmtiDynamicCodeEventCollector from VtableStubs::find_stub
+        Thread t = new Thread(() -> {
+            changeEventNotificationMode();
+        });
+        t.setDaemon(true);
+        t.start();
+
+        for (int i = 0; i < 2000; i++) {
+            new Thread(() -> {
+                String result = "string" + System.currentTimeMillis();
+                Reference.reachabilityFence(result);
+            }).start();
+        }
+    }
+}
diff --git a/test/hotspot/jtreg/serviceability/jvmti/DynamicCodeGenerated/libDynamicCodeGenerated.cpp b/test/hotspot/jtreg/serviceability/jvmti/DynamicCodeGenerated/libDynamicCodeGenerated.cpp
new file mode 100644
index 00000000000..45c98dcba32
--- /dev/null
+++ b/test/hotspot/jtreg/serviceability/jvmti/DynamicCodeGenerated/libDynamicCodeGenerated.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2021, 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.
+ */
+
+#include <string.h>
+#include <jvmti.h>
+
+static jvmtiEnv* jvmti = NULL;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+JNIEXPORT
+void JNICALL Java_DynamicCodeGeneratedTest_changeEventNotificationMode(JNIEnv* jni, jclass cls) {
+  while (true) {
+    jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_DYNAMIC_CODE_GENERATED, NULL);
+    jvmti->SetEventNotificationMode(JVMTI_DISABLE, JVMTI_EVENT_DYNAMIC_CODE_GENERATED, NULL);
+  }
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+void JNICALL DynamicCodeGenerated(jvmtiEnv* jvmti, const char* name, const void* address, jint length) {
+
+}
+
+jint Agent_OnLoad(JavaVM* vm, char* options, void* reserved) {
+    vm->GetEnv((void**)&jvmti, JVMTI_VERSION_1_0);
+    jvmtiEventCallbacks callbacks;
+    memset(&callbacks, 0, sizeof(callbacks));
+    callbacks.DynamicCodeGenerated = DynamicCodeGenerated;
+    jvmti->SetEventCallbacks(&callbacks, sizeof(callbacks));
+
+    return 0;
+}