8178870: instrumentation.retransformClasses cause coredump

Thu, 12 Sep 2019 15:15:22 -0400

author
zgu
date
Thu, 12 Sep 2019 15:15:22 -0400
changeset 9748
628176d22495
parent 9747
760b28d87178
child 9749
fea2c7f50ce8

8178870: instrumentation.retransformClasses cause coredump
Summary: Don't double-free cached class bytes on redefinition loading failure.
Reviewed-by: sspitsyn, jiangli

src/share/vm/prims/jvmtiRedefineClasses.cpp file | annotate | diff | comparison | revisions
test/runtime/RedefineTests/RedefineDoubleDelete.java file | annotate | diff | comparison | revisions
test/runtime/RedefineTests/libRedefineDoubleDelete.c file | annotate | diff | comparison | revisions
test/runtime/RedefineTests/test8178870.sh file | annotate | diff | comparison | revisions
     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 +

mercurial