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/VM_DEATH event notification modes
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(JVMTI_EVENT_VM_INIT): %d\n", err);
365 failed = true;
366 return JNI_ERR;
367 }
368 err = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_VM_DEATH, nullptr);
369 if (err != JVMTI_ERROR_NONE) {
370 LOG1("Agent_OnLoad: Error in JVMTI SetEventNotificationMode(JVMTI_EVENT_VM_DEATH): %d\n", err);
371 failed = true;
372 return JNI_ERR;
373 }
374
375 LOG0("Agent_OnLoad: finished\n");
376 return JNI_OK;
377 }
378
379 /* Native method: checkHiddenClass(). */
380 JNIEXPORT void JNICALL
381 Java_P_Q_HiddenClassSigTest_checkHiddenClass(JNIEnv *jni, jclass klass, jclass hidden_klass, jstring exp_sig_str) {
382 const char* exp_sig = jni->GetStringUTFChars(exp_sig_str, nullptr);
383
384 if (exp_sig == nullptr) {
385 jni->FatalError("check_hidden_class: Error: JNI GetStringChars returned null for jstring\n");
386 return;
387 }
388 check_hidden_class(jvmti, jni, hidden_klass, exp_sig);
389
390 jni->ReleaseStringUTFChars(exp_sig_str, exp_sig);
391 }
392
393 /* Native method: checkHiddenClassArray(). */
394 JNIEXPORT void JNICALL
395 Java_P_Q_HiddenClassSigTest_checkHiddenClassArray(JNIEnv *jni, jclass klass, jclass hidden_klass_array, jstring exp_sig_str) {
396 const char* exp_sig = jni->GetStringUTFChars(exp_sig_str, nullptr);
397
398 if (exp_sig == nullptr) {
399 jni->FatalError("check_hidden_class_array: Error: JNI GetStringChars returned null for jstring\n");
400 return;
401 }
402 check_hidden_class_array(jvmti, jni, hidden_klass_array, exp_sig);
403
404 jni->ReleaseStringUTFChars(exp_sig_str, exp_sig);
405 }
406
407 /* Native method: checkFailed(). */
408 JNIEXPORT jboolean JNICALL
409 Java_P_Q_HiddenClassSigTest_checkFailed(JNIEnv *jni, jclass klass) {
410 if (class_load_count == 0) {
411 // expected ClassLoad event was not generated for hidden class
412 LOG0("Native Agent: FAIL: missed ClassLoad event for hidden class\n");
413 failed = true;
414 }
415 if (class_prep_count == 0) {
416 // expected ClassPrepare event was not generated for hidden class
417 LOG0("Native Agent: FAIL: missed ClassPrepare event for hidden class\n");
418 failed = true;
419 }
420 return failed;
421 }
422
423 } // extern "C"
--- EOF ---