1 /*
2  * Copyright (c) 2016, 2017, 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 <stdio.h>
25 #include <string.h>
26 #include "jvmti.h"
27 
28 #ifdef __cplusplus
29 extern "C" {
30 #endif
31 
32 #ifndef JNI_ENV_ARG
33 
34 #ifdef __cplusplus
35 #define JNI_ENV_ARG(x, y) y
36 #define JNI_ENV_PTR(x) x
37 #else
38 #define JNI_ENV_ARG(x,y) x, y
39 #define JNI_ENV_PTR(x) (*x)
40 #endif
41 
42 #endif
43 
44 #define TranslateError(err) "JVMTI error"
45 
46 #define PASSED 0
47 #define FAILED 2
48 
49 static const char *EXC_CNAME = "java/lang/Exception";
50 static const char* MOD_CNAME = "Ljava/lang/Module;";
51 
52 static jvmtiEnv *jvmti = NULL;
53 static jint result = PASSED;
54 
55 static jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved);
56 
57 JNIEXPORT
Agent_OnLoad(JavaVM * jvm,char * options,void * reserved)58 jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {
59     return Agent_Initialize(jvm, options, reserved);
60 }
61 
62 JNIEXPORT
Agent_OnAttach(JavaVM * jvm,char * options,void * reserved)63 jint JNICALL Agent_OnAttach(JavaVM *jvm, char *options, void *reserved) {
64     return Agent_Initialize(jvm, options, reserved);
65 }
66 
67 JNIEXPORT
JNI_OnLoad(JavaVM * jvm,void * reserved)68 jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {
69     return JNI_VERSION_1_8;
70 }
71 
72 static
Agent_Initialize(JavaVM * jvm,char * options,void * reserved)73 jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) {
74     jint res = JNI_ENV_PTR(jvm)->GetEnv(JNI_ENV_ARG(jvm, (void **) &jvmti),
75                                         JVMTI_VERSION_9);
76     if (res != JNI_OK || jvmti == NULL) {
77         printf("    Error: wrong result of a valid call to GetEnv!\n");
78         return JNI_ERR;
79     }
80     return JNI_OK;
81 }
82 
83 static
throw_exc(JNIEnv * env,char * msg)84 void throw_exc(JNIEnv *env, char *msg) {
85     jclass exc_class = JNI_ENV_PTR(env)->FindClass(JNI_ENV_ARG(env, EXC_CNAME));
86     jint rt = JNI_OK;
87 
88     if (exc_class == NULL) {
89         printf("throw_exc: Error in FindClass(env, %s)\n", EXC_CNAME);
90         return;
91     }
92     rt = JNI_ENV_PTR(env)->ThrowNew(JNI_ENV_ARG(env, exc_class), msg);
93     if (rt == JNI_ERR) {
94         printf("throw_exc: Error in JNI ThrowNew(env, %s)\n", msg);
95     }
96 }
97 
98 static
jlM(JNIEnv * env)99 jclass jlM(JNIEnv *env) {
100     jclass cls = NULL;
101 
102     cls = JNI_ENV_PTR(env)->FindClass(JNI_ENV_ARG(env, MOD_CNAME));
103     if (cls == NULL) {
104         printf("    Error in JNI FindClass: %s\n", MOD_CNAME);
105     }
106     return cls;
107 }
108 
109 jmethodID
get_method(JNIEnv * env,jclass clazz,const char * name,const char * sig)110 get_method(JNIEnv *env, jclass clazz, const char * name, const char *sig) {
111     jmethodID method = NULL;
112 
113     method = JNI_ENV_PTR(env)->GetMethodID(JNI_ENV_ARG(env, clazz), name, sig);
114     if (method == NULL) {
115         printf("    Error in JNI GetMethodID %s with signature %s", name, sig);
116     }
117     return method;
118 }
119 
120 static
can_module_read(JNIEnv * env,jobject module,jobject to_module)121 jboolean can_module_read(JNIEnv *env, jobject module, jobject to_module) {
122     static jmethodID mCanRead = NULL;
123     jboolean res = JNI_FALSE;
124 
125     if (mCanRead == NULL) {
126         const char* sign = "(Ljava/lang/Module;)Z";
127         mCanRead = get_method(env, jlM(env), "canRead", sign);
128     }
129     res = JNI_ENV_PTR(env)->CallBooleanMethod(JNI_ENV_ARG(env, module),
130                                               mCanRead, to_module);
131     return res;
132 }
133 
134 static
check_add_module_reads(JNIEnv * env,jclass cls,jobject unnamedModule,jobject baseModule,jobject instrModule)135 jint check_add_module_reads(JNIEnv *env,
136                             jclass  cls,
137                             jobject unnamedModule,
138                             jobject baseModule,
139                             jobject instrModule) {
140     jvmtiError err = JVMTI_ERROR_NONE;
141     jboolean can = JNI_FALSE;
142 
143     // Add an invalid read edge from NULL module
144     printf("Check #N1:\n");
145     err = (*jvmti)->AddModuleReads(jvmti, NULL, baseModule);
146     if (err != JVMTI_ERROR_NULL_POINTER) {
147         printf("#N1: jvmtiError from AddModuleReads: %d\n", err);
148         throw_exc(env, "Check #N1: failed to return JVMTI_ERROR_NULL_POINTER for module==NULL");
149         return FAILED;
150     }
151 
152     // Add an invalid read edge to NULL module
153     printf("Check #N2:\n");
154     err = (*jvmti)->AddModuleReads(jvmti, baseModule, NULL);
155     if (err != JVMTI_ERROR_NULL_POINTER) {
156         printf("#N2: jvmtiError from AddModuleReads: %d\n", err);
157         throw_exc(env, "Check #N2: failed to return JVMTI_ERROR_NULL_POINTER for to_module==NULL");
158         return FAILED;
159     }
160 
161     // Add an invalid read edge from invalid module (cls)
162     printf("Check #I1:\n");
163     err = (*jvmti)->AddModuleReads(jvmti, cls, baseModule);
164     if (err != JVMTI_ERROR_INVALID_MODULE) {
165         printf("#I1: jvmtiError from AddModuleReads: %d\n", err);
166         throw_exc(env, "Check #I1: failed to return JVMTI_ERROR_INVALID_MODULE for module==cls");
167         return FAILED;
168     }
169 
170     // Add an invalid read edge to invalid module (cls)
171     printf("Check #I2:\n");
172     err = (*jvmti)->AddModuleReads(jvmti, baseModule, cls);
173     if (err != JVMTI_ERROR_INVALID_MODULE) {
174         printf("#I2: jvmtiError from AddModuleReads: %d\n", err);
175         throw_exc(env, "Check #I2: failed to return JVMTI_ERROR_INVALID_MODULE for to_module==cls");
176         return FAILED;
177     }
178 
179     // Check the edge baseModule->instrModule is absent
180     printf("Check #C0:\n");
181     can = can_module_read(env, baseModule, instrModule);
182     if (can != JNI_FALSE) {
183         throw_exc(env, "Check #C0: read edge from base to instr is unexpected");
184         return FAILED;
185     }
186 
187     // Add read edge baseModule->instrModule
188     printf("Check #C1:\n");
189     err = (*jvmti)->AddModuleReads(jvmti, baseModule, instrModule);
190     if (err != JVMTI_ERROR_NONE) {
191         printf("#C1: jvmtiError from AddModuleReads: %d\n", err);
192         throw_exc(env, "Check #C1: error in add reads from base to instr");
193         return FAILED;
194     }
195 
196     // Check the read edge baseModule->instrModule is present now
197     printf("Check #C2:\n");
198     can = can_module_read(env, baseModule, instrModule);
199     if (can == JNI_FALSE) {
200         throw_exc(env, "Check #C2: failed to add reads from base to instr");
201         return FAILED;
202     }
203 
204     // Check the read edge baseModule->unnamedModule is absent
205     printf("Check #C3:\n");
206     can = can_module_read(env, baseModule, unnamedModule);
207     if (can != JNI_FALSE) {
208         throw_exc(env, "Check #C3: got unexpected read edge from base to unnamed");
209         return FAILED;
210     }
211 
212     // Add read edge baseModule->unnamedModule
213     printf("Check #C4:\n");
214     err = (*jvmti)->AddModuleReads(jvmti, baseModule, unnamedModule);
215     if (err != JVMTI_ERROR_NONE) {
216         printf("#C4: jvmtiError from AddModuleReads: %d\n", err);
217         throw_exc(env, "Check #C4: failed to ignore adding read edge from base to unnamed");
218         return FAILED;
219     }
220 
221     // Check the read edge baseModule->unnamedModule is present now
222     printf("Check #C5:\n");
223     can = can_module_read(env, baseModule, unnamedModule);
224     if (can == JNI_FALSE) {
225         throw_exc(env, "Check #C5: did not get expected read edge from base to unnamed");
226         return FAILED;
227     }
228 
229     // Check the read edge unnamedModule->instrModule is absent
230     printf("Check #C6:\n");
231     can = can_module_read(env, unnamedModule, instrModule);
232     if (can == JNI_FALSE) {
233         throw_exc(env, "Check #C6: did not get expected read edge from unnamed to instr");
234         return FAILED;
235     }
236 
237     // Add read edge unnamedModule->instrModule
238     printf("Check #C7:\n");
239     err = (*jvmti)->AddModuleReads(jvmti, unnamedModule, instrModule);
240     if (err != JVMTI_ERROR_NONE) {
241         printf("#C7: jvmtiError from AddModuleReads: %d\n", err);
242         throw_exc(env, "Check #C7: failed to ignore adding read edge from unnamed to instr");
243         return FAILED;
244     }
245     return PASSED;
246 }
247 
248 JNIEXPORT jint JNICALL
Java_MyPackage_AddModuleReadsTest_check(JNIEnv * env,jclass cls,jobject unnamedModule,jobject baseModule,jobject instrModule)249 Java_MyPackage_AddModuleReadsTest_check(JNIEnv *env,
250                                         jclass cls,
251                                         jobject unnamedModule,
252                                         jobject baseModule,
253                                         jobject instrModule) {
254     if (jvmti == NULL) {
255         throw_exc(env, "JVMTI client was not properly loaded!\n");
256         return FAILED;
257     }
258 
259     printf("\n*** Checks for JVMTI AddModuleReads ***\n\n");
260     result = check_add_module_reads(env, cls, unnamedModule, baseModule, instrModule);
261     if (result != PASSED) {
262         return result;
263     }
264     return result;
265 }
266 
267 #ifdef __cplusplus
268 }
269 #endif
270