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