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