1 /* 2 * Copyright (c) 2019, 2020, 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 <string.h> 25 #include "jvmti.h" 26 27 extern "C" { 28 29 static const char* EXP_INTERF_SIG = "LP/Q/Test;"; 30 static const char* SIG_START = "LP/Q/HiddenClassSig"; 31 static const size_t SIG_START_LEN = strlen(SIG_START); 32 static const int ACC_INTERFACE = 0x0200; // Interface class modifiers bit 33 34 static jvmtiEnv *jvmti = NULL; 35 static jint class_load_count = 0; 36 static bool failed = false; 37 38 #define LOG0(str) { printf(str); fflush(stdout); } 39 #define LOG1(str, arg) { printf(str, arg); fflush(stdout); } 40 #define LOG2(str, arg1, arg2) { printf(str, arg1, arg2); fflush(stdout); } 41 42 #define CHECK_JVMTI_ERROR(jni, err, msg) \ 43 if (err != JVMTI_ERROR_NONE) { \ 44 LOG1("CHECK_JVMTI_ERROR: JVMTI function returned error: %d\n", err); \ 45 jni->FatalError(msg); \ 46 return; \ 47 } 48 49 /* Return the jmethodID of j.l.Class.isHidden() method. */ 50 static jmethodID 51 is_hidden_mid(JNIEnv* jni) { 52 char* csig = NULL; 53 jint count = 0; 54 jmethodID *methods = NULL; 55 jclass clazz = jni->FindClass("java/lang/Class"); 56 if (clazz == NULL) { 57 jni->FatalError("is_hidden_mid: Error: FindClass returned NULL for java/lang/Class\n"); 58 return NULL; 59 } 60 61 // find the jmethodID of j.l.Class.isHidden() method 62 jmethodID mid = jni->GetMethodID(clazz, "isHidden", "()Z"); 63 if (mid == NULL) { 64 jni->FatalError("is_hidden_mid: Error in jni GetMethodID: Cannot find j.l.Class.isHidden method\n"); 65 } 66 return mid; 67 } 68 69 /* Return true if the klass is hidden. */ 70 static bool 71 is_hidden(JNIEnv* jni, jclass klass) { 72 static jmethodID is_hid_mid = NULL; 73 74 if (is_hid_mid == NULL) { 75 is_hid_mid = is_hidden_mid(jni); 76 } 77 // invoke j.l.Class.isHidden() method 78 return jni->CallBooleanMethod(klass, is_hid_mid); 79 } 80 81 /* Check the class signature matches the expected. */ 82 static void 83 check_class_signature(jvmtiEnv* jvmti, JNIEnv* jni, jclass klass, bool is_hidden, const char* exp_sig) { 84 jint class_modifiers = 0; 85 char* sig = NULL; 86 char* gsig = NULL; 87 jvmtiError err; 88 89 err = jvmti->GetClassSignature(klass, &sig, &gsig); 90 CHECK_JVMTI_ERROR(jni, err, "check_hidden_class: Error in JVMTI GetClassSignature"); 91 92 LOG1("check_class_signature: class with sig: %s\n", sig); 93 LOG1("check_class_signature: class with gsig: %s\n", gsig); 94 95 if (strcmp(sig, exp_sig) != 0) { 96 LOG2("check_class_signature: FAIL: Hidden class signature %s does not mach expected: %s\n", sig, exp_sig); 97 failed = true; 98 } 99 if (is_hidden && gsig == NULL) { 100 LOG0("check_class_signature: FAIL: unexpected NULL generic signature for hidden class\n"); 101 failed = true; 102 } 103 } 104 105 /* Test hidden class flags: it should not be interface, array nor modifiable. */ 106 static void 107 check_hidden_class_flags(jvmtiEnv* jvmti, JNIEnv* jni, jclass klass) { 108 jint modifiers = 0; 109 jboolean flag = false; 110 jvmtiError err; 111 112 err = jvmti->GetClassModifiers(klass, &modifiers); 113 CHECK_JVMTI_ERROR(jni, err, "check_hidden_class_flags: Error in JVMTI GetClassModifiers"); 114 LOG1("check_hidden_class_flags: hidden class modifiers: 0x%x\n", modifiers); 115 if ((modifiers & ACC_INTERFACE) != 0) { 116 LOG0("check_hidden_class_flags: FAIL: unexpected ACC_INTERFACE bit in hidden class modifiers\n"); 117 failed = true; 118 return; 119 } 120 121 err = jvmti->IsInterface(klass, &flag); 122 CHECK_JVMTI_ERROR(jni, err, "check_hidden_class_flags: Error in JVMTI IsInterface"); 123 if (flag) { 124 LOG0("check_hidden_class_flags: FAIL: hidden class is not expected to be interface\n"); 125 failed = true; 126 return; 127 } 128 129 err = jvmti->IsArrayClass(klass, &flag); 130 CHECK_JVMTI_ERROR(jni, err, "check_hidden_class_flags: Error in JVMTI IsArrayClass"); 131 if (flag) { 132 LOG0("check_hidden_class_flags: FAIL: hidden class is not expected to be array\n"); 133 failed = true; 134 return; 135 } 136 err = jvmti->IsModifiableClass(klass, &flag); 137 CHECK_JVMTI_ERROR(jni, err, "check_hidden_class_flags: Error in JVMTI IsModifiableClass"); 138 if (flag) { 139 LOG0("check_hidden_class_flags: FAIL: hidden class is not expected to be modifiable\n"); 140 failed = true; 141 } 142 } 143 144 /* Test GetClassLoaderClasses: it should not return any hidden classes. */ 145 static void 146 check_hidden_class_loader(jvmtiEnv* jvmti, JNIEnv* jni, jclass klass) { 147 jint count = 0; 148 jobject loader = NULL; 149 jclass* loader_classes = NULL; 150 jboolean found = false; 151 jvmtiError err; 152 153 err = jvmti->GetClassLoader(klass, &loader); 154 CHECK_JVMTI_ERROR(jni, err, "check_hidden_class_loader: Error in JVMTI GetClassLoader"); 155 156 err = jvmti->GetClassLoaderClasses(loader, &count, &loader_classes); 157 CHECK_JVMTI_ERROR(jni, err, "check_hidden_class_loader: Error in JVMTI GetClassLoaderClasses"); 158 159 for (int idx = 0; idx < count; idx++) { 160 char* sig = NULL; 161 jclass kls = loader_classes[idx]; 162 163 if (!is_hidden(jni, kls)) { 164 continue; 165 } 166 err = jvmti->GetClassSignature(kls, &sig, NULL); 167 CHECK_JVMTI_ERROR(jni, err, "check_hidden_class_loader: Error in JVMTI GetClassSignature"); 168 169 LOG1("check_hidden_class_loader: FAIL: JVMTI GetClassLoaderClasses returned hidden class: %s\n", sig); 170 failed = true; 171 return; 172 } 173 LOG0("check_hidden_class_loader: not found hidden class in its loader classes as expected\n"); 174 } 175 176 /* Test the hidden class implements expected interface. */ 177 static void 178 check_hidden_class_impl_interf(jvmtiEnv* jvmti, JNIEnv* jni, jclass klass) { 179 char* sig = NULL; 180 jint count = 0; 181 jclass* interfaces = NULL; 182 jvmtiError err; 183 184 // check that hidden class implements just one interface 185 err = jvmti->GetImplementedInterfaces(klass, &count, &interfaces); 186 CHECK_JVMTI_ERROR(jni, err, "check_hidden_class_impl_interf: Error in JVMTI GetImplementedInterfaces"); 187 if (count != 1) { 188 LOG1("check_hidden_class_impl_interf: FAIL: implemented interfaces count: %d, expected to be 1\n", count); 189 failed = true; 190 return; 191 } 192 193 // check the interface signature is matching the expected 194 err = jvmti->GetClassSignature(interfaces[0], &sig, NULL); 195 CHECK_JVMTI_ERROR(jni, err, "check_hidden_class_impl_interf: Error in JVMTI GetClassSignature for implemented interface"); 196 197 if (strcmp(sig, EXP_INTERF_SIG) != 0) { 198 LOG2("check_hidden_class_impl_interf: FAIL: implemented interface signature: %s, expected to be: %s\n", 199 sig, EXP_INTERF_SIG); 200 failed = true; 201 } 202 } 203 204 /* Test hidden class. */ 205 static void 206 check_hidden_class(jvmtiEnv* jvmti, JNIEnv* jni, jclass klass, const char* exp_sig) { 207 char* source_file_name = NULL; 208 209 LOG1("\n### Native agent: check_hidden_class started: class: %s\n", exp_sig); 210 211 check_class_signature(jvmti, jni, klass, true /* not hidden */, exp_sig); 212 if (failed) return; 213 214 check_hidden_class_flags(jvmti, jni, klass); 215 if (failed) return; 216 217 check_hidden_class_loader(jvmti, jni, klass); 218 if (failed) return; 219 220 check_hidden_class_impl_interf(jvmti, jni, klass); 221 if (failed) return; 222 223 LOG0("### Native agent: check_hidden_class finished\n"); 224 } 225 226 /* Test hidden class array. */ 227 static void 228 check_hidden_class_array(jvmtiEnv* jvmti, JNIEnv* jni, jclass klass_array, const char* exp_sig) { 229 char* source_file_name = NULL; 230 231 LOG1("\n### Native agent: check_hidden_class_array started: array: %s\n", exp_sig); 232 233 check_class_signature(jvmti, jni, klass_array, false /* is hidden */, exp_sig); 234 if (failed) return; 235 236 LOG0("### Native agent: check_hidden_class_array finished\n"); 237 } 238 239 /* Enable CLASS_LOAD event notification mode. */ 240 static void JNICALL 241 VMInit(jvmtiEnv* jvmti, JNIEnv* jni, jthread thread) { 242 jvmtiError err; 243 244 printf("VMInit event: SIG_START: %s, SIG_START_LEN: %d\n", SIG_START, (int)SIG_START_LEN); 245 fflush(stdout); 246 247 err = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_CLASS_LOAD, NULL); 248 CHECK_JVMTI_ERROR(jni, err, "VMInit event: Error in enabling ClassLoad events notification"); 249 } 250 251 /* Check CLASS_LOAD event is generated for the given hidden class. */ 252 static void JNICALL 253 ClassLoad(jvmtiEnv* jvmti, JNIEnv* jni, jthread thread, jclass klass) { 254 char* sig = NULL; 255 char* gsig = NULL; 256 char* src_name = NULL; 257 jvmtiError err; 258 259 err = jvmti->GetClassSignature(klass, &sig, &gsig); 260 CHECK_JVMTI_ERROR(jni, err, "ClassLoad event: Error in JVMTI GetClassSignature"); 261 262 if (strlen(sig) > strlen(SIG_START) && 263 strncmp(sig, SIG_START, SIG_START_LEN) == 0 && 264 is_hidden(jni, klass)) { 265 class_load_count++; 266 if (gsig == NULL) { 267 LOG0("ClassLoad event: FAIL: GetClassSignature returned NULL generic signature for hidden class\n"); 268 failed = true; 269 } 270 LOG1("ClassLoad event: hidden class with sig: %s\n", sig); 271 LOG1("ClassLoad event: hidden class with gsig: %s\n", gsig); 272 } 273 } 274 275 JNIEXPORT jint JNICALL 276 Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) { 277 jvmtiEventCallbacks callbacks; 278 jvmtiCapabilities caps; 279 jvmtiError err; 280 281 LOG0("Agent_OnLoad: started\n"); 282 if (jvm->GetEnv((void **) (&jvmti), JVMTI_VERSION) != JNI_OK) { 283 LOG0("Agent_OnLoad: Error in GetEnv in obtaining jvmtiEnv*\n"); 284 failed = true; 285 return JNI_ERR; 286 } 287 288 // set required event callbacks 289 memset(&callbacks, 0, sizeof(callbacks)); 290 callbacks.ClassLoad = &ClassLoad; 291 callbacks.VMInit = &VMInit; 292 293 err = jvmti->SetEventCallbacks(&callbacks, sizeof(jvmtiEventCallbacks)); 294 if (err != JVMTI_ERROR_NONE) { 295 LOG1("Agent_OnLoad: Error in JVMTI SetEventCallbacks: %d\n", err); 296 failed = true; 297 return JNI_ERR; 298 } 299 300 // add required capabilities 301 memset(&caps, 0, sizeof(caps)); 302 caps.can_get_source_file_name = 1; 303 err = jvmti->AddCapabilities(&caps); 304 if (err != JVMTI_ERROR_NONE) { 305 LOG1("Agent_OnLoad: Error in JVMTI AddCapabilities: %d\n", err); 306 failed = true; 307 return JNI_ERR; 308 } 309 310 // enable VM_INIT event notification mode 311 err = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_VM_INIT, NULL); 312 if (err != JVMTI_ERROR_NONE) { 313 LOG1("Agent_OnLoad: Error in JVMTI SetEventNotificationMode: %d\n", err); 314 failed = true; 315 return JNI_ERR; 316 } 317 318 LOG0("Agent_OnLoad: finished\n"); 319 return JNI_OK; 320 } 321 322 /* Native method: checkHiddenClass(). */ 323 JNIEXPORT void JNICALL 324 Java_P_Q_HiddenClassSigTest_checkHiddenClass(JNIEnv *jni, jclass klass, jclass hidden_klass, jstring exp_sig_str) { 325 const char* exp_sig = jni->GetStringUTFChars(exp_sig_str, NULL); 326 327 if (exp_sig == NULL) { 328 jni->FatalError("check_hidden_class: Error: JNI GetStringChars returned NULL for jstring\n"); 329 return; 330 } 331 check_hidden_class(jvmti, jni, hidden_klass, exp_sig); 332 333 jni->ReleaseStringUTFChars(exp_sig_str, exp_sig); 334 } 335 336 /* Native method: checkHiddenClassArray(). */ 337 JNIEXPORT void JNICALL 338 Java_P_Q_HiddenClassSigTest_checkHiddenClassArray(JNIEnv *jni, jclass klass, jclass hidden_klass_array, jstring exp_sig_str) { 339 const char* exp_sig = jni->GetStringUTFChars(exp_sig_str, NULL); 340 341 if (exp_sig == NULL) { 342 jni->FatalError("check_hidden_class_array: Error: JNI GetStringChars returned NULL for jstring\n"); 343 return; 344 } 345 check_hidden_class_array(jvmti, jni, hidden_klass_array, exp_sig); 346 347 jni->ReleaseStringUTFChars(exp_sig_str, exp_sig); 348 } 349 350 /* Native method: checkFailed(). */ 351 JNIEXPORT jboolean JNICALL 352 Java_P_Q_HiddenClassSigTest_checkFailed(JNIEnv *jni, jclass klass) { 353 if (class_load_count == 0) { 354 LOG0("Native Agent: missed ClassLoad event for hidden class\n"); 355 failed = true; 356 } 357 return failed; 358 } 359 360 } // extern "C"