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