1 /*
   2  * Copyright (c) 2003, 2018, 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 <stdlib.h>
  26 #include <string.h>
  27 #include "jvmti.h"
  28 #include "agent_common.h"
  29 
  30 #ifdef __cplusplus
  31 extern "C" {
  32 #endif
  33 
  34 #ifndef JNI_ENV_ARG
  35 
  36 #ifdef __cplusplus
  37 #define JNI_ENV_ARG(x, y) y
  38 #define JNI_ENV_ARG1(x)
  39 #define JNI_ENV_PTR(x) x
  40 #else
  41 #define JNI_ENV_ARG(x,y) x, y
  42 #define JNI_ENV_ARG1(x) x
  43 #define JNI_ENV_PTR(x) (*x)
  44 #endif
  45 
  46 #endif
  47 
  48 #define JVMTI_ENV_ARG JNI_ENV_ARG
  49 #define JVMTI_ENV_ARG1 JNI_ENV_ARG1
  50 #define JVMTI_ENV_PTR JNI_ENV_PTR
  51 
  52 #define JVMTI_ERROR_CHECK(str,res) if ( res != JVMTI_ERROR_NONE) { printf(str); printf("%d\n",res); return res;}
  53 #define JVMTI_ERROR_CHECK_EXPECTED_ERROR(str,res,err) if ( res != err) { printf(str); printf("unexpected error %d\n",res); return res;}
  54 
  55 #define JVMTI_ERROR_CHECK_VOID(str,res) if ( res != JVMTI_ERROR_NONE) { printf(str); printf("%d\n",res); iGlobalStatus = 2; }
  56 
  57 #define JVMTI_ERROR_CHECK_EXPECTED_ERROR_VOID(str,res,err) if ( res != err) { printf(str); printf("unexpected error %d\n",res); iGlobalStatus = 2; }
  58 
  59 static jvmtiEnv *jvmti;
  60 static jint iGlobalStatus = 0;
  61 static jvmtiCapabilities jvmti_caps;
  62 static jvmtiEventCallbacks callbacks;
  63 static int boot_class_count = 0;
  64 
  65 static const char* BOOT_CLASS =
  66     "nsk/jvmti/unit/functions/AddToBootstrapClassLoaderSearch/Boot";
  67 
  68 static const char* CLASSPATH = "java.class.path";
  69 
  70 static char segment[3000] = ".";
  71 
  72 int printdump = 0;
  73 
  74 
  75 void debug_printf(const char *fmt, ...) {
  76     va_list args;
  77 
  78     va_start(args, fmt);
  79     if (printdump) {
  80         vprintf(fmt, args);
  81     }
  82     va_end(args);
  83 }
  84 
  85 
  86 /*
  87  * Check that it is not possible to add to the boot class path during the Start phase
  88  */
  89 void JNICALL
  90 vmStart(jvmtiEnv *jvmti, JNIEnv* jni) {
  91    jvmtiError res;
  92 
  93    debug_printf("VMStart event done\n");
  94 
  95    res = JVMTI_ENV_PTR(jvmti)->AddToBootstrapClassLoaderSearch(JVMTI_ENV_ARG(jvmti, segment));
  96    JVMTI_ERROR_CHECK_EXPECTED_ERROR_VOID("VMStart: AddToBootstrapClassLoaderSearch returned error ",
  97       res, JVMTI_ERROR_WRONG_PHASE);
  98 }
  99 
 100 /*
 101  * Check that it is possible to add to the boot class path before VMDeath event return.
 102  */
 103 void JNICALL
 104 vmDeath(jvmtiEnv *jvmti, JNIEnv* jni) {
 105    jvmtiError res;
 106 
 107    debug_printf("VMDeath event done\n");
 108 
 109    res = JVMTI_ENV_PTR(jvmti)->AddToBootstrapClassLoaderSearch(JVMTI_ENV_ARG(jvmti, segment));
 110    /* In the live phase, anything other than an existing JAR file is an invalid path.
 111       So, check that JVMTI_ERROR_ILLEGAL_ARGUMENT error is thrown.
 112    */
 113    JVMTI_ERROR_CHECK_EXPECTED_ERROR_VOID("VMDeath: AddToBootstrapClassLoaderSearch returned error ",
 114       res, JVMTI_ERROR_ILLEGAL_ARGUMENT);
 115 }
 116 
 117 /*
 118  * Check that it is possible to add to the boot class path during the Live phase
 119  */
 120 void JNICALL vmInit(jvmtiEnv *jvmti_env, JNIEnv *env, jthread thread) {
 121     jvmtiError res;
 122 
 123     debug_printf("VMInit event  done\n");
 124 
 125     res = JVMTI_ENV_PTR(jvmti)->AddToBootstrapClassLoaderSearch(JVMTI_ENV_ARG(jvmti, segment));
 126     /* In the live phase, anything other than an existing JAR file is an invalid path.
 127        So, check that JVMTI_ERROR_ILLEGAL_ARGUMENT error is thrown.
 128     */
 129     JVMTI_ERROR_CHECK_EXPECTED_ERROR_VOID("VMInit: AddToBootstrapClassLoaderSearch returned error ",
 130         res, JVMTI_ERROR_ILLEGAL_ARGUMENT);
 131 }
 132 
 133 /*
 134  * Check that it is not possible to add to the boot class path during the Primordial phase
 135  */
 136 void JNICALL
 137 NativeMethodBind(jvmtiEnv* jvmti, JNIEnv *jni,
 138                  jthread thread, jmethodID method,
 139                  void* address, void** new_address_ptr) {
 140    jvmtiPhase phase;
 141    jvmtiError res;
 142 
 143    res = JVMTI_ENV_PTR(jvmti)->GetPhase(JVMTI_ENV_ARG(jvmti, &phase));
 144    JVMTI_ERROR_CHECK_VOID("GetPhase returned error", res);
 145 
 146    if (phase == JVMTI_PHASE_PRIMORDIAL) {
 147       debug_printf("Primordial phase\n");
 148 
 149       res = JVMTI_ENV_PTR(jvmti)->AddToBootstrapClassLoaderSearch(JVMTI_ENV_ARG(jvmti, segment));
 150       JVMTI_ERROR_CHECK_EXPECTED_ERROR_VOID("Primordial: AddToBootstrapClassLoaderSearch returned error ",
 151          res, JVMTI_ERROR_WRONG_PHASE);
 152    }
 153 }
 154 
 155 
 156 void JNICALL
 157 classFileLoadEvent(jvmtiEnv *jvmti_env, JNIEnv *env,
 158                         jclass redefined_class,
 159                         jobject loader,const char* name,
 160                         jobject protection_domain,
 161                         jint class_data_len,
 162                         const unsigned char* class_data,
 163                         jint* new_class_data_len,
 164                         unsigned char** new_class_data) {
 165 
 166     if (name != NULL && (strcmp(name, BOOT_CLASS) == 0)) {
 167         debug_printf("Received class file load hook event for class: \n\t%s\n",
 168             name);
 169         debug_printf("Received class loader: 0x%p \n", loader);
 170         /* Check to make sure Boot class got loaded from bootstrap class path.*/
 171 
 172       if (loader == NULL) {
 173          boot_class_count++;
 174       }
 175    }
 176 }
 177 
 178 
 179 void init_callbacks() {
 180     memset((void *)&callbacks, 0, sizeof(jvmtiEventCallbacks));
 181 
 182     callbacks.VMInit = vmInit;
 183     callbacks.VMStart = vmStart;
 184     callbacks.VMDeath = vmDeath;
 185     callbacks.NativeMethodBind = NativeMethodBind;
 186     callbacks.ClassFileLoadHook = classFileLoadEvent;
 187 }
 188 
 189 
 190 #ifdef STATIC_BUILD
 191 JNIEXPORT jint JNICALL Agent_OnLoad_JvmtiTest(JavaVM *jvm, char *options, void *reserved) {
 192     return Agent_Initialize(jvm, options, reserved);
 193 }
 194 JNIEXPORT jint JNICALL Agent_OnAttach_JvmtiTest(JavaVM *jvm, char *options, void *reserved) {
 195     return Agent_Initialize(jvm, options, reserved);
 196 }
 197 JNIEXPORT jint JNI_OnLoad_JvmtiTest(JavaVM *jvm, char *options, void *reserved) {
 198     return JNI_VERSION_1_8;
 199 }
 200 #endif
 201 jint Agent_Initialize(JavaVM * jvm, char *options, void *reserved) {
 202     jint res;
 203     char *idx;
 204 
 205     debug_printf("Agent_OnLoad event done\n");
 206 
 207     if (options && strlen(options) > 0) {
 208         if (strstr(options, "printdump")) {
 209             printdump = 1;
 210         }
 211 
 212         strncpy(segment, options, (size_t) sizeof(segment)/sizeof(char));
 213         segment[(size_t) sizeof(segment)/sizeof(char) - 1] = 0;
 214         idx = strchr(segment, ',');
 215         if (idx != NULL) *idx = 0;
 216     }
 217 
 218     res = JNI_ENV_PTR(jvm)->
 219         GetEnv(JNI_ENV_ARG(jvm, (void **) &jvmti), JVMTI_VERSION_1_1);
 220     if (res < 0) {
 221         printf("Wrong result of a valid call to GetEnv!\n");
 222         return JNI_ERR;
 223     }
 224 
 225     /* Add capabilities */
 226     res = JVMTI_ENV_PTR(jvmti)->GetPotentialCapabilities(JVMTI_ENV_ARG(jvmti, &jvmti_caps));
 227     JVMTI_ERROR_CHECK("GetPotentialCapabilities returned error", res);
 228 
 229     res = JVMTI_ENV_PTR(jvmti)->AddCapabilities(JVMTI_ENV_ARG(jvmti, &jvmti_caps));
 230     JVMTI_ERROR_CHECK("GetAddCapabilities returned error", res);
 231 
 232 
 233     /* Enable events */
 234     init_callbacks();
 235     res = JVMTI_ENV_PTR(jvmti)->SetEventCallbacks(JVMTI_ENV_ARG(jvmti, &callbacks), sizeof(callbacks));
 236     JVMTI_ERROR_CHECK("SetEventCallbacks returned error", res);
 237 
 238     res = JVMTI_ENV_PTR(jvmti)->SetEventNotificationMode(JVMTI_ENV_ARG(jvmti,JVMTI_ENABLE),JVMTI_EVENT_VM_START,NULL);
 239     JVMTI_ERROR_CHECK("SetEventNotificationMode for VM_START returned error", res);
 240 
 241     res = JVMTI_ENV_PTR(jvmti)->SetEventNotificationMode(JVMTI_ENV_ARG(jvmti,JVMTI_ENABLE),JVMTI_EVENT_VM_INIT,NULL);
 242     JVMTI_ERROR_CHECK("SetEventNotificationMode for VM_INIT returned error", res);
 243 
 244     res = JVMTI_ENV_PTR(jvmti)->SetEventNotificationMode(JVMTI_ENV_ARG(jvmti,JVMTI_ENABLE),JVMTI_EVENT_NATIVE_METHOD_BIND,NULL);
 245     JVMTI_ERROR_CHECK("SetEventNotificationMode for NATIVE_METHOD_BIND returned error", res);
 246 
 247     res = JVMTI_ENV_PTR(jvmti)->SetEventNotificationMode(JVMTI_ENV_ARG(jvmti,JVMTI_ENABLE),JVMTI_EVENT_VM_DEATH,NULL);
 248     JVMTI_ERROR_CHECK("SetEventNotificationMode for VM_DEATH returned error", res);
 249 
 250     res = JVMTI_ENV_PTR(jvmti)->SetEventNotificationMode(JVMTI_ENV_ARG(jvmti,JVMTI_ENABLE),JVMTI_EVENT_CLASS_FILE_LOAD_HOOK,NULL);
 251     JVMTI_ERROR_CHECK("SetEventNotificationMode CLASS_FILE_LOAD_HOOK returned error", res);
 252 
 253     strcat(segment, "/newclass");
 254     debug_printf("segment=%s\n", segment);
 255     res = JVMTI_ENV_PTR(jvmti)->AddToBootstrapClassLoaderSearch(JVMTI_ENV_ARG(jvmti, segment));
 256     JVMTI_ERROR_CHECK("AddToBootStrapClassLoaderSearch returned error", res);
 257 
 258     return JNI_OK;
 259 }
 260 
 261 JNIEXPORT jint JNICALL
 262 Java_nsk_jvmti_unit_functions_AddToBootstrapClassLoaderSearch_JvmtiTest_GetResult(JNIEnv * env, jclass cls) {
 263 
 264     if (boot_class_count != 1) {
 265         printf("Error: no ClassFileLoadHook event for Boot class loaded from bootstrap class path\n");
 266         iGlobalStatus = 2;
 267     }
 268     return iGlobalStatus;
 269 }
 270 
 271 
 272 #ifdef __cplusplus
 273 }
 274 #endif