Thu, 12 Sep 2019 15:15:22 -0400
8178870: instrumentation.retransformClasses cause coredump
Summary: Don't double-free cached class bytes on redefinition loading failure.
Reviewed-by: sspitsyn, jiangli
1.1 --- a/src/share/vm/prims/jvmtiRedefineClasses.cpp Wed Sep 04 19:40:35 2019 +0100 1.2 +++ b/src/share/vm/prims/jvmtiRedefineClasses.cpp Thu Sep 12 15:15:22 2019 -0400 1.3 @@ -151,6 +151,11 @@ 1.4 ClassLoaderData* cld = _scratch_classes[i]->class_loader_data(); 1.5 // Free the memory for this class at class unloading time. Not before 1.6 // because CMS might think this is still live. 1.7 + InstanceKlass* ik = get_ik(_class_defs[i].klass); 1.8 + if (ik->get_cached_class_file() == ((InstanceKlass*)_scratch_classes[i])->get_cached_class_file()) { 1.9 + // Don't double-free cached_class_file copied from the original class if error. 1.10 + ((InstanceKlass*)_scratch_classes[i])->set_cached_class_file(NULL); 1.11 + } 1.12 cld->add_to_deallocate_list((InstanceKlass*)_scratch_classes[i]); 1.13 } 1.14 } 1.15 @@ -4019,12 +4024,12 @@ 1.16 // with them was cached on the scratch class, move to the_class. 1.17 // Note: we still want to do this if nothing needed caching since it 1.18 // should get cleared in the_class too. 1.19 - if (the_class->get_cached_class_file_bytes() == 0) { 1.20 + if (the_class->get_cached_class_file() == 0) { 1.21 // the_class doesn't have a cache yet so copy it 1.22 the_class->set_cached_class_file(scratch_class->get_cached_class_file()); 1.23 } 1.24 - else if (scratch_class->get_cached_class_file_bytes() != 1.25 - the_class->get_cached_class_file_bytes()) { 1.26 + else if (scratch_class->get_cached_class_file() != 1.27 + the_class->get_cached_class_file()) { 1.28 // The same class can be present twice in the scratch classes list or there 1.29 // are multiple concurrent RetransformClasses calls on different threads. 1.30 // In such cases we have to deallocate scratch_class cached_class_file.
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 2.2 +++ b/test/runtime/RedefineTests/RedefineDoubleDelete.java Thu Sep 12 15:15:22 2019 -0400 2.3 @@ -0,0 +1,81 @@ 2.4 +/* 2.5 + * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. 2.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 2.7 + * 2.8 + * This code is free software; you can redistribute it and/or modify it 2.9 + * under the terms of the GNU General Public License version 2 only, as 2.10 + * published by the Free Software Foundation. 2.11 + * 2.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 2.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 2.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 2.15 + * version 2 for more details (a copy is included in the LICENSE file that 2.16 + * accompanied this code). 2.17 + * 2.18 + * You should have received a copy of the GNU General Public License version 2.19 + * 2 along with this work; if not, write to the Free Software Foundation, 2.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 2.21 + * 2.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 2.23 + * or visit www.oracle.com if you need additional information or have any 2.24 + * questions. 2.25 + */ 2.26 + 2.27 +/* 2.28 + * @test 2.29 + * @bug 8178870 2.30 + * @summary Redefine class with CFLH twice to test deleting the cached_class_file 2.31 + */ 2.32 + 2.33 +public class RedefineDoubleDelete { 2.34 + 2.35 + // Class gets a redefinition error because it adds a data member 2.36 + public static String newB = 2.37 + "class RedefineDoubleDelete$B {" + 2.38 + " int count1 = 0;" + 2.39 + "}"; 2.40 + 2.41 + public static String newerB = 2.42 + "class RedefineDoubleDelete$B { " + 2.43 + " int faa() { System.out.println(\"baa\"); return 2; }" + 2.44 + "}"; 2.45 + 2.46 + // The ClassFileLoadHook for this class turns foo into faa and prints out faa. 2.47 + static class B { 2.48 + int faa() { System.out.println("foo"); return 1; } 2.49 + } 2.50 + 2.51 + public static void main(String args[]) throws Exception { 2.52 + 2.53 + B b = new B(); 2.54 + int val = b.faa(); 2.55 + if (val != 1) { 2.56 + throw new RuntimeException("return value wrong " + val); 2.57 + } 2.58 + 2.59 + // Redefine B twice to get cached_class_file in both B scratch classes 2.60 + try { 2.61 + RedefineClassHelper.redefineClass(B.class, newB); 2.62 + } catch (java.lang.UnsupportedOperationException e) { 2.63 + // this is expected 2.64 + } 2.65 + try { 2.66 + RedefineClassHelper.redefineClass(B.class, newB); 2.67 + } catch (java.lang.UnsupportedOperationException e) { 2.68 + // this is expected 2.69 + } 2.70 + 2.71 + // Do a full GC. 2.72 + System.gc(); 2.73 + 2.74 + // Redefine with a compatible class 2.75 + RedefineClassHelper.redefineClass(B.class, newerB); 2.76 + val = b.faa(); 2.77 + if (val != 2) { 2.78 + throw new RuntimeException("return value wrong " + val); 2.79 + } 2.80 + 2.81 + // Do another full GC to clean things up. 2.82 + System.gc(); 2.83 + } 2.84 +}
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 3.2 +++ b/test/runtime/RedefineTests/libRedefineDoubleDelete.c Thu Sep 12 15:15:22 2019 -0400 3.3 @@ -0,0 +1,164 @@ 3.4 +/* 3.5 + * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. 3.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 3.7 + * 3.8 + * This code is free software; you can redistribute it and/or modify it 3.9 + * under the terms of the GNU General Public License version 2 only, as 3.10 + * published by the Free Software Foundation. 3.11 + * 3.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 3.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 3.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 3.15 + * version 2 for more details (a copy is included in the LICENSE file that 3.16 + * accompanied this code). 3.17 + * 3.18 + * You should have received a copy of the GNU General Public License version 3.19 + * 2 along with this work; if not, write to the Free Software Foundation, 3.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 3.21 + * 3.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 3.23 + * or visit www.oracle.com if you need additional information or have any 3.24 + * questions. 3.25 + */ 3.26 + 3.27 +#include <stdio.h> 3.28 +#include <string.h> 3.29 +#include "jvmti.h" 3.30 + 3.31 +#ifdef __cplusplus 3.32 +extern "C" { 3.33 +#endif 3.34 + 3.35 +#ifndef JNI_ENV_ARG 3.36 + 3.37 +#ifdef __cplusplus 3.38 +#define JNI_ENV_ARG(x, y) y 3.39 +#define JNI_ENV_PTR(x) x 3.40 +#else 3.41 +#define JNI_ENV_ARG(x,y) x, y 3.42 +#define JNI_ENV_PTR(x) (*x) 3.43 +#endif 3.44 + 3.45 +#endif 3.46 + 3.47 +#define TranslateError(err) "JVMTI error" 3.48 + 3.49 +static jvmtiEnv *jvmti = NULL; 3.50 + 3.51 +static jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved); 3.52 + 3.53 +JNIEXPORT 3.54 +jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) { 3.55 + return Agent_Initialize(jvm, options, reserved); 3.56 +} 3.57 + 3.58 +JNIEXPORT 3.59 +jint JNICALL Agent_OnAttach(JavaVM *jvm, char *options, void *reserved) { 3.60 + return Agent_Initialize(jvm, options, reserved); 3.61 +} 3.62 + 3.63 +JNIEXPORT 3.64 +jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) { 3.65 + return JNI_VERSION_1_8; 3.66 +} 3.67 + 3.68 + 3.69 +static jint newClassDataLen = 0; 3.70 +static unsigned char* newClassData = NULL; 3.71 + 3.72 +static jint 3.73 +getBytecodes(jvmtiEnv *jvmti_env, 3.74 + jint class_data_len, const unsigned char* class_data) { 3.75 + int i; 3.76 + jint res; 3.77 + 3.78 + newClassDataLen = class_data_len; 3.79 + res = (*jvmti_env)->Allocate(jvmti_env, newClassDataLen, &newClassData); 3.80 + if (res != JNI_OK) { 3.81 + printf(" Unable to allocate bytes\n"); 3.82 + return JNI_ERR; 3.83 + } 3.84 + for (i = 0; i < newClassDataLen; i++) { 3.85 + newClassData[i] = class_data[i]; 3.86 + // Rewrite oo in class to aa 3.87 + if (i > 0 && class_data[i] == 'o' && class_data[i-1] == 'o') { 3.88 + newClassData[i] = newClassData[i-1] = 'a'; 3.89 + } 3.90 + } 3.91 + printf(" ... copied bytecode: %d bytes\n", (int)newClassDataLen); 3.92 + return JNI_OK; 3.93 +} 3.94 + 3.95 + 3.96 +static void JNICALL 3.97 +Callback_ClassFileLoadHook(jvmtiEnv *jvmti_env, JNIEnv *env, 3.98 + jclass class_being_redefined, 3.99 + jobject loader, const char* name, jobject protection_domain, 3.100 + jint class_data_len, const unsigned char* class_data, 3.101 + jint *new_class_data_len, unsigned char** new_class_data) { 3.102 + if (name != NULL && strcmp(name, "RedefineDoubleDelete$B") == 0) { 3.103 + if (newClassData == NULL) { 3.104 + jint res = getBytecodes(jvmti_env, class_data_len, class_data); 3.105 + if (res == JNI_ERR) { 3.106 + printf(">>> ClassFileLoadHook event: class name %s FAILED\n", name); 3.107 + return; 3.108 + } 3.109 + // Only change for first CFLH event. 3.110 + *new_class_data_len = newClassDataLen; 3.111 + *new_class_data = newClassData; 3.112 + } 3.113 + printf(">>> ClassFileLoadHook event: class name %s\n", name); 3.114 + } 3.115 +} 3.116 + 3.117 +static 3.118 +jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) { 3.119 + jint res, size; 3.120 + jvmtiCapabilities caps; 3.121 + jvmtiEventCallbacks callbacks; 3.122 + jvmtiError err; 3.123 + 3.124 + res = JNI_ENV_PTR(jvm)->GetEnv(JNI_ENV_ARG(jvm, (void **) &jvmti), 3.125 + JVMTI_VERSION_1_2); 3.126 + if (res != JNI_OK || jvmti == NULL) { 3.127 + printf(" Error: wrong result of a valid call to GetEnv!\n"); 3.128 + return JNI_ERR; 3.129 + } 3.130 + 3.131 + printf("Enabling following capabilities: can_generate_all_class_hook_events, " 3.132 + "can_retransform_classes, can_redefine_classes"); 3.133 + memset(&caps, 0, sizeof(caps)); 3.134 + caps.can_generate_all_class_hook_events = 1; 3.135 + caps.can_retransform_classes = 1; 3.136 + caps.can_redefine_classes = 1; 3.137 + printf("\n"); 3.138 + 3.139 + err = (*jvmti)->AddCapabilities(jvmti, &caps); 3.140 + if (err != JVMTI_ERROR_NONE) { 3.141 + printf(" Error in AddCapabilites: %s (%d)\n", TranslateError(err), err); 3.142 + return JNI_ERR; 3.143 + } 3.144 + 3.145 + size = (jint)sizeof(callbacks); 3.146 + 3.147 + memset(&callbacks, 0, sizeof(callbacks)); 3.148 + callbacks.ClassFileLoadHook = Callback_ClassFileLoadHook; 3.149 + 3.150 + err = (*jvmti)->SetEventCallbacks(jvmti, &callbacks, size); 3.151 + if (err != JVMTI_ERROR_NONE) { 3.152 + printf(" Error in SetEventCallbacks: %s (%d)\n", TranslateError(err), err); 3.153 + return JNI_ERR; 3.154 + } 3.155 + 3.156 + err = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, NULL); 3.157 + if (err != JVMTI_ERROR_NONE) { 3.158 + printf(" Error in SetEventNotificationMode: %s (%d)\n", TranslateError(err), err); 3.159 + return JNI_ERR; 3.160 + } 3.161 + 3.162 + return JNI_OK; 3.163 +} 3.164 + 3.165 +#ifdef __cplusplus 3.166 +} 3.167 +#endif
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 4.2 +++ b/test/runtime/RedefineTests/test8178870.sh Thu Sep 12 15:15:22 2019 -0400 4.3 @@ -0,0 +1,87 @@ 4.4 +#!/bin/sh 4.5 + 4.6 +# 4.7 +# Copyright (c) 2019, Red Hat, Inc. All rights reserved. 4.8 +# 4.9 +# This code is free software; you can redistribute it and/or modify it 4.10 +# under the terms of the GNU General Public License version 2 only, as 4.11 +# published by the Free Software Foundation. 4.12 +# 4.13 +# This code is distributed in the hope that it will be useful, but WITHOUT 4.14 +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 4.15 +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 4.16 +# version 2 for more details (a copy is included in the LICENSE file that 4.17 +# accompanied this code). 4.18 +# 4.19 +# You should have received a copy of the GNU General Public License version 4.20 +# 2 along with this work; if not, write to the Free Software Foundation, 4.21 +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 4.22 +# 4.23 +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 4.24 +# or visit www.oracle.com if you need additional information or have any 4.25 +# questions. 4.26 +# 4.27 +# 4.28 + 4.29 +## @test test.sh 4.30 +## @bug 8178870 4.31 +## @summary test instrumentation.retransformClasses 4.32 +## @run shell test.sh 4.33 + 4.34 +if [ "${TESTSRC}" = "" ] 4.35 +then 4.36 + TESTSRC=${PWD} 4.37 + echo "TESTSRC not set. Using "${TESTSRC}" as default" 4.38 +fi 4.39 +echo "TESTSRC=${TESTSRC}" 4.40 +## Adding common setup Variables for running shell tests. 4.41 +. ${TESTSRC}/../../test_env.sh 4.42 + 4.43 +LIB_SRC=${TESTSRC}/../../testlibrary/ 4.44 + 4.45 +# set platform-dependent variables 4.46 +OS=`uname -s` 4.47 +echo "Testing on " $OS 4.48 +case "$OS" in 4.49 + Linux) 4.50 + cc_cmd=`which gcc` 4.51 + if [ "x$cc_cmd" == "x" ]; then 4.52 + echo "WARNING: gcc not found. Cannot execute test." 2>&1 4.53 + exit 0; 4.54 + fi 4.55 + ;; 4.56 + Solaris) 4.57 + cc_cmd=`which cc` 4.58 + if [ "x$cc_cmd" == "x" ]; then 4.59 + echo "WARNING: cc not found. Cannot execute test." 2>&1 4.60 + exit 0; 4.61 + fi 4.62 + ;; 4.63 + *) 4.64 + echo "Test passed. Only on Linux and Solaris" 4.65 + exit 0; 4.66 + ;; 4.67 +esac 4.68 + 4.69 +THIS_DIR=. 4.70 + 4.71 +cp ${TESTSRC}/RedefineDoubleDelete.java ${THIS_DIR} 4.72 +mkdir -p ${THIS_DIR}/classes 4.73 +${TESTJAVA}/bin/javac -sourcepath ${LIB_SRC} -d ${THIS_DIR}/classes ${LIB_SRC}RedefineClassHelper.java 4.74 +${TESTJAVA}/bin/javac -cp .:${THIS_DIR}/classes:${TESTJAVA}/lib/tools.jar -d ${THIS_DIR} RedefineDoubleDelete.java 4.75 + 4.76 +$cc_cmd -fPIC -shared -o ${THIS_DIR}${FS}libRedefineDoubleDelete.so \ 4.77 + -I${TESTJAVA}/include -I${TESTJAVA}/include/linux \ 4.78 + ${TESTSRC}/libRedefineDoubleDelete.c 4.79 + 4.80 +LD_LIBRARY_PATH=${THIS_DIR} 4.81 +echo LD_LIBRARY_PATH = ${LD_LIBRARY_PATH} 4.82 +export LD_LIBRARY_PATH 4.83 + 4.84 +# Install redefineagent.jar 4.85 +${TESTJAVA}/bin${FS}java -cp ${THIS_DIR}/classes RedefineClassHelper 4.86 + 4.87 +echo 4.88 +echo ${TESTJAVA}/bin/java -agentlib:RedefineDoubleDelete RedefineDoubleDelete 4.89 +${TESTJAVA}/bin/java -cp .:${THIS_DIR}${FS}classes -javaagent:redefineagent.jar -agentlib:RedefineDoubleDelete RedefineDoubleDelete 4.90 +