1 /*
   2  * Copyright (c) 2016, 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 #include <stdio.h>
  25 #include <string.h>
  26 #include "jvmti.h"
  27 
  28 #ifdef __cplusplus
  29 extern "C" {
  30 #endif
  31 
  32 #ifndef JNI_ENV_ARG
  33 
  34 #ifdef __cplusplus
  35 #define JNI_ENV_ARG(x, y) y
  36 #define JNI_ENV_PTR(x) x
  37 #else
  38 #define JNI_ENV_ARG(x,y) x, y
  39 #define JNI_ENV_PTR(x) (*x)
  40 #endif
  41 
  42 #endif
  43 
  44 #define TranslateError(err) "JVMTI error"
  45 
  46 #define PASSED 0
  47 #define FAILED 2
  48 
  49 static const char *EXPECTED_NAME = "java/util/Collections";
  50 static const char *EXC_CNAME = "java/lang/Exception";
  51 
  52 static jvmtiEnv *jvmti = NULL;
  53 static jint result = PASSED;
  54 static jboolean printdump = JNI_FALSE;
  55 
  56 static jboolean with_early_vm_start_capability = JNI_FALSE;
  57 static jboolean with_early_class_hook_capability = JNI_FALSE;
  58 
  59 static jboolean found_class_in_vm_start = JNI_FALSE;
  60 static jboolean found_class_in_primordial = JNI_FALSE;
  61 static jboolean found_class_in_cflh_events = JNI_FALSE;
  62 
  63 static int cflh_events_primordial_count = 0;
  64 static int cflh_events_vm_start_count = 0;
  65 
  66 static jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved);
  67 
  68 JNIEXPORT
  69 jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {
  70     return Agent_Initialize(jvm, options, reserved);
  71 }
  72 
  73 JNIEXPORT
  74 jint JNICALL Agent_OnAttach(JavaVM *jvm, char *options, void *reserved) {
  75     return Agent_Initialize(jvm, options, reserved);
  76 }
  77 
  78 JNIEXPORT
  79 jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {
  80     return JNI_VERSION_9;
  81 }
  82 
  83 static
  84 jint throw_exc(JNIEnv *env, char *msg) {
  85     jclass exc_class = JNI_ENV_PTR(env)->FindClass(JNI_ENV_ARG(env, EXC_CNAME));
  86 
  87     if (exc_class == NULL) {
  88         printf("throw_exc: Error in FindClass(env, %s)\n", EXC_CNAME);
  89         return -1;
  90     }
  91     return JNI_ENV_PTR(env)->ThrowNew(JNI_ENV_ARG(env, exc_class), msg);
  92 }
  93 
  94 static void JNICALL
  95 Callback_ClassFileLoadHook(jvmtiEnv *jvmti_env, JNIEnv *env,
  96                            jclass class_being_redefined,
  97                            jobject loader, const char* name, jobject protection_domain,
  98                            jint class_data_len, const unsigned char* class_data,
  99                            jint *new_class_data_len, unsigned char** new_class_data) {
 100     jvmtiPhase phase;
 101     jvmtiError err;
 102 
 103     err = (*jvmti)->GetPhase(jvmti_env,&phase);
 104     if (err != JVMTI_ERROR_NONE) {
 105         printf("ClassFileLoadHook event: GetPhase error: %s (%d)\n", TranslateError(err), err);
 106         result = FAILED;
 107         return;
 108     }
 109 
 110     if (phase == JVMTI_PHASE_PRIMORDIAL || phase == JVMTI_PHASE_START) {
 111         if (phase == JVMTI_PHASE_START) {
 112             cflh_events_vm_start_count++;
 113             if(!strcmp(name, EXPECTED_NAME)) {
 114                 found_class_in_vm_start = JNI_TRUE;
 115             }
 116         } else {
 117             cflh_events_primordial_count++;
 118             if(!strcmp(name, EXPECTED_NAME)) {
 119                 found_class_in_primordial = JNI_TRUE;
 120             }
 121         }
 122     }
 123 
 124     if(!strcmp(name, EXPECTED_NAME)) {
 125         found_class_in_cflh_events = JNI_TRUE;
 126     }
 127 
 128     if (printdump == JNI_TRUE) {
 129         printf(">>>    ClassFileLoadHook event: phase(%d), class name %s\n", phase, name);
 130     }
 131 }
 132 
 133 static
 134 jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) {
 135     jint res, size;
 136     jvmtiCapabilities caps;
 137     jvmtiEventCallbacks callbacks;
 138     jvmtiError err;
 139 
 140     if (options != NULL) {
 141         if (strstr(options, "with_early_vmstart") != NULL) {
 142             with_early_vm_start_capability = JNI_TRUE;
 143         }
 144         if (strstr(options, "with_early_class_hook") != NULL) {
 145             with_early_class_hook_capability = JNI_TRUE;
 146         }
 147         if (strstr(options, "printdump") != NULL) {
 148             printdump = JNI_TRUE;
 149         }
 150     }
 151 
 152     res = JNI_ENV_PTR(jvm)->GetEnv(JNI_ENV_ARG(jvm, (void **) &jvmti),
 153         JVMTI_VERSION_9);
 154     if (res != JNI_OK || jvmti == NULL) {
 155         printf("    Error: wrong result of a valid call to GetEnv!\n");
 156         return JNI_ERR;
 157     }
 158 
 159     printf("Enabling following capabilities: can_generate_all_class_hook_events");
 160     memset(&caps, 0, sizeof(caps));
 161     caps.can_generate_all_class_hook_events = 1;
 162     if (with_early_vm_start_capability == JNI_TRUE) {
 163         printf(", can_generate_early_vmstart");
 164         caps.can_generate_early_vmstart = 1;
 165     }
 166     if (with_early_class_hook_capability == JNI_TRUE) {
 167         printf(", can_generate_early_class_hook_events");
 168         caps.can_generate_early_class_hook_events = 1;
 169     }
 170     printf("\n");
 171 
 172     err = (*jvmti)->AddCapabilities(jvmti, &caps);
 173     if (err != JVMTI_ERROR_NONE) {
 174         printf("    Error in AddCapabilites: %s (%d)\n", TranslateError(err), err);
 175         return JNI_ERR;
 176     }
 177 
 178     size = (jint)sizeof(callbacks);
 179 
 180     memset(&callbacks, 0, sizeof(callbacks));
 181     callbacks.ClassFileLoadHook = Callback_ClassFileLoadHook;
 182 
 183     err = (*jvmti)->SetEventCallbacks(jvmti, &callbacks, size);
 184     if (err != JVMTI_ERROR_NONE) {
 185         printf("    Error in SetEventCallbacks: %s (%d)\n", TranslateError(err), err);
 186         return JNI_ERR;
 187     }
 188 
 189     err = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, NULL);
 190     if (err != JVMTI_ERROR_NONE) {
 191         printf("    Error in SetEventNotificationMode: %s (%d)\n", TranslateError(err), err);
 192         return JNI_ERR;
 193     }
 194 
 195     return JNI_OK;
 196 }
 197 
 198 JNIEXPORT jint JNICALL
 199 Java_MAAClassFileLoadHook_check(JNIEnv *env, jclass cls) {
 200     jobject loader = NULL;
 201 
 202     if (jvmti == NULL) {
 203         throw_exc(env, "JVMTI client was not properly loaded!\n");
 204         return FAILED;
 205     }
 206 
 207     /*
 208      * Expecting that we always get ClassFileLoadHook events in the VM Start phase
 209      */
 210     if (cflh_events_vm_start_count == 0) {
 211         throw_exc(env, "Didn't get ClassFileLoadHook events in start phase!\n");
 212         return FAILED;
 213     }
 214 
 215     if (with_early_class_hook_capability == JNI_TRUE) {
 216        /*
 217         * Expecting that we get ClassFileLoadHook events in the Primordial phase
 218         * when can_generate_all_class_hook_events and can_generate_early_class_hook_events
 219         * capabilities are enabled
 220         */
 221         if (cflh_events_primordial_count == 0) {
 222             throw_exc(env, "Didn't get ClassFileLoadHook events in primordial phase!\n");
 223             return FAILED;
 224         }
 225     } else {
 226        /*
 227         * Expecting that we don't get ClassFileLoadHook events in the Primordial phase
 228         * when can_generate_early_class_hook_events capability is disabled
 229         */
 230         if (cflh_events_primordial_count != 0) {
 231             throw_exc(env, "Get ClassFileLoadHook events in primordial phase!\n");
 232             return FAILED;
 233         }
 234     }
 235 
 236 
 237     if (with_early_vm_start_capability == JNI_TRUE) {
 238         /*
 239          * Expecting that "java/util/Collections" class from java.base module is present in the
 240          * ClassFileLoadHook events during VM Start phase when can_generate_early_vmstart
 241          * capability is enabled
 242          */
 243         printf("Expecting to find '%s' class in ClassFileLoadHook events during VM start phase.\n", EXPECTED_NAME);
 244         if (found_class_in_vm_start == JNI_FALSE) {
 245             throw_exc(env, "Unable to find expected class in ClassLoad events during start phase!\n");
 246             return FAILED;
 247         }
 248     } else if (with_early_class_hook_capability == JNI_TRUE) {
 249         /*
 250          * Expecting that "java/util/Collections" class from java.base module is present in the
 251          * ClassFileLoadHook events during Primordial phase when can_generate_all_class_hook_events
 252          * and can_generate_early_class_hook_events capabilities are enabled and can_generate_early_vmstart
 253          * capability is disabled
 254          */
 255         printf("Expecting to find '%s' class in ClassFileLoadHook events during VM primordial phase.\n", EXPECTED_NAME);
 256         if (found_class_in_primordial == JNI_FALSE) {
 257             throw_exc(env, "Unable to find expected class in ClassFileLoadHook events during primordial phase!\n");
 258             return FAILED;
 259         }
 260     } else {
 261         /*
 262          * Expecting that "java/util/Collections" class from java.base module is not present in the
 263          * ClassFileLoadHook events when can_generate_all_class_hook_events, can_generate_early_class_hook_events
 264          * and can_generate_early_vmstart capabilities are disabled
 265          */
 266         printf("Expecting that '%s' class is absent in ClassLoadHook events.\n", EXPECTED_NAME);;
 267         if (found_class_in_cflh_events == JNI_TRUE) {
 268             throw_exc(env, "Class is found in ClassFileLoadHook events!\n");
 269             return FAILED;
 270         }
 271     }
 272 
 273     return result;
 274 }
 275 
 276 #ifdef __cplusplus
 277 }
 278 #endif