1 /*
   2  * Copyright (c) 2003, 2018, 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 <stdarg.h>
  26 #include <stdlib.h>
  27 #include <string.h>
  28 
  29 #include <jvmti.h>
  30 #include "agent_common.h"
  31 
  32 #include "JVMTITools.h"
  33 #include "jvmti_tools.h"
  34 #include "nsk_tools.h"
  35 
  36 extern "C" {
  37 
  38 /* ====================================================================== */
  39 
  40 static const char *classSig =
  41     "Lnsk/jvmti/scenarios/jni_interception/JI01/ji01t001;";
  42 
  43 static jvmtiEnv *jvmti = NULL;
  44 static jrawMonitorID eventLock;
  45 static jvmtiEventCallbacks callbacks;
  46 static jint result = NSK_STATUS_PASSED;
  47 
  48 /* the original JNI function table */
  49 static jniNativeInterface *orig_jni_functions = NULL;
  50 
  51 /* the redirected JNI function table */
  52 static jniNativeInterface *redir_jni_functions = NULL;
  53 
  54 /* number of the redirected JNI function calls */
  55 static volatile int fnd_calls = 0;
  56 
  57 /* ====================================================================== */
  58 /** redirected JNI functions **/
  59 jclass JNICALL MyFindClass(JNIEnv *env, const char *name) {
  60     if (isThreadExpected(jvmti, NULL)) {
  61         fnd_calls++;
  62 
  63         NSK_DISPLAY1("MyFindClass: the function was called successfully: number of calls so far =  %d\n", fnd_calls);
  64     }
  65 
  66     return orig_jni_functions->FindClass(env, name);
  67 }
  68 
  69 /* ====================================================================== */
  70 static jvmtiPhase getVMPhase(jvmtiEnv *jvmti) {
  71     jvmtiPhase phase;
  72 
  73     if (!NSK_JVMTI_VERIFY(jvmti->GetPhase(&phase)))
  74         exit(NSK_STATUS_FAILED);
  75 
  76     return phase;
  77 }
  78 
  79 /* ====================================================================== */
  80 static void doRedirect(jvmtiEnv *jvmti, jvmtiPhase phase) {
  81     jvmtiError err;
  82     NSK_DISPLAY0("doRedirect: obtaining the JNI function table ...\n");
  83 
  84     // Store original function table
  85     err = jvmti->GetJNIFunctionTable(&orig_jni_functions);
  86     if (!NSK_VERIFY((err == JVMTI_ERROR_NONE || phase != JVMTI_PHASE_LIVE)))
  87     {
  88         NSK_COMPLAIN2("TEST FAILED: failed to get original JNI function table during %s: %s\n"
  89                      , TranslatePhase(phase)
  90                      , TranslateError(err)
  91                      );
  92 
  93         result = NSK_STATUS_FAILED;
  94         exit(NSK_STATUS_FAILED);
  95     }
  96     else {
  97         NSK_DISPLAY3("CHECK PASSED: the original JNI function table %s during %s phase: %s\n"
  98                     , (err == JVMTI_ERROR_NONE) ? "has been obtained" : "hasn't been obtained"
  99                     , TranslatePhase(phase)
 100                     , TranslateError(err)
 101                     );
 102     }
 103 
 104     // Get a duplicate of the function table for future modification
 105     if (!NSK_VERIFY(
 106             (err = jvmti->GetJNIFunctionTable(&redir_jni_functions)) == JVMTI_ERROR_NONE || phase != JVMTI_PHASE_LIVE))
 107     {
 108         NSK_COMPLAIN2("TEST FAILED: failed to get JNI function table for interception during %s: %s\n"
 109                      , TranslatePhase(phase)
 110                      , TranslateError(err)
 111                      );
 112 
 113         result = NSK_STATUS_FAILED;
 114         exit(NSK_STATUS_FAILED);
 115     }
 116     else {
 117         NSK_DISPLAY3("CHECK PASSED: the original JNI function table for interception %s during %s phase: %s\n"
 118                     , (err == JVMTI_ERROR_NONE) ? "has been obtained" : "hasn't been obtained"
 119                     , TranslatePhase(phase)
 120                     , TranslateError(err)
 121                     );
 122     }
 123 
 124     // Redefine desired JNI functions
 125     if (phase == JVMTI_PHASE_LIVE) {
 126         NSK_DISPLAY0("doRedirect: overwriting the function FindClass; ...\n");
 127         redir_jni_functions->FindClass = MyFindClass;
 128     }
 129 
 130     // Set new JNI function table
 131     if (!NSK_VERIFY(
 132             (err = jvmti->SetJNIFunctionTable(redir_jni_functions)) == JVMTI_ERROR_NONE || phase != JVMTI_PHASE_LIVE))
 133     {
 134         NSK_COMPLAIN2("TEST FAILED: failed to set redirected JNI function table during %s: %s\n"
 135                      , TranslatePhase(phase)
 136                      , TranslateError(err)
 137                      );
 138 
 139         result = NSK_STATUS_FAILED;
 140         exit(NSK_STATUS_FAILED);
 141     }
 142     else {
 143         NSK_DISPLAY3("CHECK PASSED: the redirected JNI function table %s during %s phase: %s\n"
 144                     , (err == JVMTI_ERROR_NONE) ? "has been set" : "hasn't been set"
 145                     , TranslatePhase(phase)
 146                     , TranslateError(err)
 147                     );
 148     }
 149 }
 150 
 151 /* ====================================================================== */
 152 static void doRestore(jvmtiEnv *jvmti) {
 153     NSK_DISPLAY0("doRestore: restoring the original JNI function table ...\n");
 154 
 155     // Set new JNI function table
 156     if (!NSK_JVMTI_VERIFY(jvmti->SetJNIFunctionTable(orig_jni_functions)))
 157     {
 158         NSK_COMPLAIN0("TEST FAILED: failed to restore original JNI function table\n");
 159 
 160         result = NSK_STATUS_FAILED;
 161         exit(NSK_STATUS_FAILED);
 162     }
 163 
 164     NSK_DISPLAY0("doRestore: the original JNI function table is restored successfully\n");
 165 }
 166 
 167 /* ====================================================================== */
 168 static void lock(jvmtiEnv *jvmti) {
 169     if (!NSK_JVMTI_VERIFY(jvmti->RawMonitorEnter(eventLock)))
 170     {
 171         result = NSK_STATUS_FAILED;
 172         exit(NSK_STATUS_FAILED);
 173     }
 174 }
 175 
 176 /* ====================================================================== */
 177 static void unlock(jvmtiEnv *jvmti) {
 178     if (!NSK_JVMTI_VERIFY(jvmti->RawMonitorExit(eventLock)))
 179     {
 180         result = NSK_STATUS_FAILED;
 181         exit(NSK_STATUS_FAILED);
 182     }
 183 }
 184 
 185 /* ====================================================================== */
 186 static void checkCall(JNIEnv *env
 187                      , int step
 188                      , const char *callBackFunc
 189                      , const char *msg
 190                      , int exFndCalls
 191                      )
 192 {
 193     jclass cls;
 194 
 195     NSK_TRACE(
 196         (cls = env->FindClass(classSig))
 197         );
 198 
 199     NSK_TRACE(
 200         env->ExceptionClear()
 201         );
 202 
 203     // The check should pass if the actual number of invocations is not less that the expected number (fnd_calls >= exFndCalls).
 204     // If the invocation is not expected (exFndCalls == 0), fnd_calls should be also == 0.
 205     if ((exFndCalls > 0 && fnd_calls >= exFndCalls) || (fnd_calls == exFndCalls)) {
 206             NSK_DISPLAY5("CHECK PASSED: %s: the %s JNI function FindClass() has been %s during %s phase\n\t%d intercepted call(s) as expected\n"
 207                         , callBackFunc
 208                         , (step==1) ? "tested" : "original"
 209                         , (step==1) ? "redirected" : "restored"
 210                         , msg
 211                         , fnd_calls
 212                         );
 213 
 214             if (fnd_calls != exFndCalls) {
 215                 NSK_COMPLAIN2("WARNING: the number of occured calls (%d) exceeds the expected number of calls (%d).\n"
 216                              , fnd_calls
 217                              , exFndCalls
 218                              );
 219             }
 220     } else {
 221         result = NSK_STATUS_FAILED;
 222 
 223         NSK_COMPLAIN6("TEST FAILED: %s: the %s JNI function FindClass() has not been %s during %s phase\n\t%d intercepted call(s) instead of %d as expected\n"
 224                      , callBackFunc
 225                      , (step==1) ? "tested" : "original"
 226                      , (step==1) ? "redirected" : "restored"
 227                      , msg
 228                      , fnd_calls
 229                      , exFndCalls
 230                      );
 231     }
 232 }
 233 
 234 /* ====================================================================== */
 235 // callback functions
 236 void JNICALL
 237 VMInit(jvmtiEnv *jvmti, JNIEnv *env, jthread thread) {
 238     jvmtiPhase phase = getVMPhase(jvmti);
 239 
 240     NSK_TRACE(lock(jvmti));
 241 
 242     NSK_DISPLAY1("b) VMInit: the current phase of VM execution %s\n"
 243                 , TranslatePhase(phase)
 244                 );
 245 
 246     // check JNI function table interception
 247     fnd_calls = 0;
 248     NSK_TRACE(doRedirect(jvmti, phase));
 249     NSK_TRACE(checkCall(env, 1, "VMInit", TranslatePhase(phase), 1));
 250 
 251     // check restored JNI function table
 252     fnd_calls = 0;
 253     NSK_TRACE(doRestore(jvmti));
 254     NSK_TRACE(checkCall(env, 2, "VMInit", TranslatePhase(phase), 0));
 255 
 256     NSK_TRACE(unlock(jvmti));
 257 }
 258 
 259 /* ====================================================================== */
 260 void JNICALL
 261 VMDeath(jvmtiEnv *jvmti, JNIEnv *env) {
 262     jvmtiPhase phase = getVMPhase(jvmti);
 263 
 264     NSK_TRACE(lock(jvmti));
 265 
 266     NSK_DISPLAY1("c) VMDeath: the current phase of VM execution %s\n"
 267                 , TranslatePhase(phase)
 268                 );
 269 
 270     // check JNI function table interception
 271     fnd_calls = 0;
 272     NSK_TRACE(doRedirect(jvmti, phase));
 273     NSK_TRACE(checkCall(env, 1, "VMDeath", TranslatePhase(phase), 1));
 274 
 275     // check restored JNI function table
 276     fnd_calls = 0;
 277     NSK_TRACE(doRestore(jvmti));
 278     NSK_TRACE(checkCall(env, 2, "VMDeath", TranslatePhase(phase), 0));
 279 
 280     (void) memset(&callbacks, 0, sizeof(callbacks));
 281 
 282     if (!NSK_JVMTI_VERIFY(jvmti->SetEventCallbacks(&callbacks, sizeof(callbacks))))
 283         result = NSK_STATUS_FAILED;
 284 
 285     NSK_TRACE(unlock(jvmti));
 286 
 287     if (result == NSK_STATUS_FAILED) {
 288         exit(NSK_STATUS_FAILED);
 289     }
 290 }
 291 
 292 /* ====================================================================== */
 293 JNIEXPORT jint JNICALL
 294 Java_nsk_jvmti_scenarios_jni_1interception_JI01_ji01t001_check(JNIEnv *env, jobject obj) {
 295     return result;
 296 }
 297 
 298 /* ====================================================================== */
 299 #ifdef STATIC_BUILD
 300 JNIEXPORT jint JNICALL Agent_OnLoad_ji01t001(JavaVM *jvm, char *options, void *reserved) {
 301     return Agent_Initialize(jvm, options, reserved);
 302 }
 303 JNIEXPORT jint JNICALL Agent_OnAttach_ji01t001(JavaVM *jvm, char *options, void *reserved) {
 304     return Agent_Initialize(jvm, options, reserved);
 305 }
 306 JNIEXPORT jint JNI_OnLoad_ji01t001(JavaVM *jvm, char *options, void *reserved) {
 307     return JNI_VERSION_1_8;
 308 }
 309 #endif
 310 jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) {
 311     if (!NSK_VERIFY(
 312                 nsk_jvmti_parseOptions(options)
 313                 )
 314        )
 315         return JNI_ERR;
 316 
 317 
 318     if (!NSK_VERIFY(jvm->GetEnv((void **) &jvmti, JVMTI_VERSION_1_1) == JNI_OK && jvmti != NULL))
 319         return JNI_ERR;
 320 
 321 
 322     if (!NSK_JVMTI_VERIFY(jvmti->CreateRawMonitor("_event_lock", &eventLock)))
 323         return JNI_ERR;
 324 
 325     NSK_DISPLAY1("a) Trying to intercept JNI functions during %s phase ...\n"
 326                 , TranslatePhase(getVMPhase(jvmti))
 327                 );
 328 
 329     NSK_TRACE(doRedirect(jvmti, getVMPhase(jvmti)));
 330 
 331     NSK_DISPLAY0("Setting event callbacks...\n");
 332 
 333     (void) memset(&callbacks, 0, sizeof(callbacks));
 334     callbacks.VMInit = &VMInit;
 335     callbacks.VMDeath = &VMDeath;
 336 
 337     if (!NSK_JVMTI_VERIFY(jvmti->SetEventCallbacks(&callbacks, sizeof(callbacks))))
 338         return JNI_ERR;
 339 
 340 
 341     NSK_DISPLAY0("Event callbacks are set\nEnabling events...\n");
 342 
 343     if (!NSK_JVMTI_VERIFY(jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_VM_INIT, NULL)))
 344         return JNI_ERR;
 345 
 346 
 347     if (!NSK_JVMTI_VERIFY(jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_VM_DEATH, NULL)))
 348         return JNI_ERR;
 349 
 350     NSK_DISPLAY0("Events are enabled\n");
 351 
 352     return JNI_OK;
 353 }
 354 
 355 }