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(
  74             NSK_CPP_STUB2(
  75                 GetPhase
  76                 , jvmti
  77                 , &phase
  78                 )
  79             )
  80        )
  81         exit(NSK_STATUS_FAILED);
  82 
  83     return phase;
  84 }
  85 
  86 /* ====================================================================== */
  87 static void doRedirect(jvmtiEnv *jvmti, jvmtiPhase phase) {
  88     jvmtiError err;
  89     NSK_DISPLAY0("doRedirect: obtaining the JNI function table ...\n");
  90 
  91     // Store original function table
  92     if (!NSK_VERIFY(
  93                 (err = NSK_CPP_STUB2(
  94                     GetJNIFunctionTable
  95                     , jvmti
  96                     , &orig_jni_functions
  97                     )) == JVMTI_ERROR_NONE || phase != JVMTI_PHASE_LIVE
  98             )
  99        )
 100     {
 101         NSK_COMPLAIN2("TEST FAILED: failed to get original JNI function table during %s: %s\n"
 102                      , TranslatePhase(phase)
 103                      , TranslateError(err)
 104                      );
 105 
 106         result = NSK_STATUS_FAILED;
 107         exit(NSK_STATUS_FAILED);
 108     }
 109     else {
 110         NSK_DISPLAY3("CHECK PASSED: the original JNI function table %s during %s phase: %s\n"
 111                     , (err == JVMTI_ERROR_NONE) ? "has been obtained" : "hasn't been obtained"
 112                     , TranslatePhase(phase)
 113                     , TranslateError(err)
 114                     );
 115     }
 116 
 117     // Get a duplicate of the function table for future modification
 118     if (!NSK_VERIFY(
 119             (err = NSK_CPP_STUB2(
 120                 GetJNIFunctionTable
 121                 , jvmti
 122                 , &redir_jni_functions
 123                 )) == JVMTI_ERROR_NONE || phase != JVMTI_PHASE_LIVE
 124             )
 125        )
 126     {
 127         NSK_COMPLAIN2("TEST FAILED: failed to get JNI function table for interception during %s: %s\n"
 128                      , TranslatePhase(phase)
 129                      , TranslateError(err)
 130                      );
 131 
 132         result = NSK_STATUS_FAILED;
 133         exit(NSK_STATUS_FAILED);
 134     }
 135     else {
 136         NSK_DISPLAY3("CHECK PASSED: the original JNI function table for interception %s during %s phase: %s\n"
 137                     , (err == JVMTI_ERROR_NONE) ? "has been obtained" : "hasn't been obtained"
 138                     , TranslatePhase(phase)
 139                     , TranslateError(err)
 140                     );
 141     }
 142 
 143     // Redefine desired JNI functions
 144     if (phase == JVMTI_PHASE_LIVE) {
 145         NSK_DISPLAY0("doRedirect: overwriting the function FindClass; ...\n");
 146         redir_jni_functions->FindClass = MyFindClass;
 147     }
 148 
 149     // Set new JNI function table
 150     if (!NSK_VERIFY(
 151             (err = NSK_CPP_STUB2(
 152                 SetJNIFunctionTable
 153                 , jvmti
 154                 , redir_jni_functions
 155                 )) == JVMTI_ERROR_NONE || phase != JVMTI_PHASE_LIVE
 156             )
 157        )
 158     {
 159         NSK_COMPLAIN2("TEST FAILED: failed to set redirected JNI function table during %s: %s\n"
 160                      , TranslatePhase(phase)
 161                      , TranslateError(err)
 162                      );
 163 
 164         result = NSK_STATUS_FAILED;
 165         exit(NSK_STATUS_FAILED);
 166     }
 167     else {
 168         NSK_DISPLAY3("CHECK PASSED: the redirected JNI function table %s during %s phase: %s\n"
 169                     , (err == JVMTI_ERROR_NONE) ? "has been set" : "hasn't been set"
 170                     , TranslatePhase(phase)
 171                     , TranslateError(err)
 172                     );
 173     }
 174 }
 175 
 176 /* ====================================================================== */
 177 static void doRestore(jvmtiEnv *jvmti) {
 178     NSK_DISPLAY0("doRestore: restoring the original JNI function table ...\n");
 179 
 180     // Set new JNI function table
 181     if (!NSK_JVMTI_VERIFY(
 182             NSK_CPP_STUB2(
 183                 SetJNIFunctionTable
 184                 , jvmti
 185                 , orig_jni_functions
 186                 )
 187             )
 188        )
 189     {
 190         NSK_COMPLAIN0("TEST FAILED: failed to restore original JNI function table\n");
 191 
 192         result = NSK_STATUS_FAILED;
 193         exit(NSK_STATUS_FAILED);
 194     }
 195 
 196     NSK_DISPLAY0("doRestore: the original JNI function table is restored successfully\n");
 197 }
 198 
 199 /* ====================================================================== */
 200 static void lock(jvmtiEnv *jvmti) {
 201     if (!NSK_JVMTI_VERIFY(
 202             NSK_CPP_STUB2(
 203                 RawMonitorEnter
 204                 , jvmti
 205                 , eventLock
 206                 )
 207             )
 208        )
 209     {
 210         result = NSK_STATUS_FAILED;
 211         exit(NSK_STATUS_FAILED);
 212     }
 213 }
 214 
 215 /* ====================================================================== */
 216 static void unlock(jvmtiEnv *jvmti) {
 217     if (!NSK_JVMTI_VERIFY(
 218             NSK_CPP_STUB2(
 219                 RawMonitorExit
 220                 , jvmti
 221                 , eventLock
 222                 )
 223             )
 224        )
 225     {
 226         result = NSK_STATUS_FAILED;
 227         exit(NSK_STATUS_FAILED);
 228     }
 229 }
 230 
 231 /* ====================================================================== */
 232 static void checkCall(JNIEnv *env
 233                      , int step
 234                      , const char *callBackFunc
 235                      , const char *msg
 236                      , int exFndCalls
 237                      )
 238 {
 239     jclass cls;
 240 
 241     NSK_TRACE(
 242         (cls = NSK_CPP_STUB2(
 243                 FindClass
 244                 , env
 245                 , classSig
 246                 ))
 247         );
 248 
 249     NSK_TRACE(
 250         NSK_CPP_STUB1(ExceptionClear, env)
 251         );
 252 
 253     // The check should pass if the actual number of invocations is not less that the expected number (fnd_calls >= exFndCalls).
 254     // If the invocation is not expected (exFndCalls == 0), fnd_calls should be also == 0.
 255     if ((exFndCalls > 0 && fnd_calls >= exFndCalls) || (fnd_calls == exFndCalls)) {
 256             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"
 257                         , callBackFunc
 258                         , (step==1) ? "tested" : "original"
 259                         , (step==1) ? "redirected" : "restored"
 260                         , msg
 261                         , fnd_calls
 262                         );
 263 
 264             if (fnd_calls != exFndCalls) {
 265                 NSK_COMPLAIN2("WARNING: the number of occured calls (%d) exceeds the expected number of calls (%d).\n"
 266                              , fnd_calls
 267                              , exFndCalls
 268                              );
 269             }
 270     } else {
 271         result = NSK_STATUS_FAILED;
 272 
 273         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"
 274                      , callBackFunc
 275                      , (step==1) ? "tested" : "original"
 276                      , (step==1) ? "redirected" : "restored"
 277                      , msg
 278                      , fnd_calls
 279                      , exFndCalls
 280                      );
 281     }
 282 }
 283 
 284 /* ====================================================================== */
 285 // callback functions
 286 void JNICALL
 287 VMInit(jvmtiEnv *jvmti, JNIEnv *env, jthread thread) {
 288     jvmtiPhase phase = getVMPhase(jvmti);
 289 
 290     NSK_TRACE(lock(jvmti));
 291 
 292     NSK_DISPLAY1("b) VMInit: the current phase of VM execution %s\n"
 293                 , TranslatePhase(phase)
 294                 );
 295 
 296     // check JNI function table interception
 297     fnd_calls = 0;
 298     NSK_TRACE(doRedirect(jvmti, phase));
 299     NSK_TRACE(checkCall(env, 1, "VMInit", TranslatePhase(phase), 1));
 300 
 301     // check restored JNI function table
 302     fnd_calls = 0;
 303     NSK_TRACE(doRestore(jvmti));
 304     NSK_TRACE(checkCall(env, 2, "VMInit", TranslatePhase(phase), 0));
 305 
 306     NSK_TRACE(unlock(jvmti));
 307 }
 308 
 309 /* ====================================================================== */
 310 void JNICALL
 311 VMDeath(jvmtiEnv *jvmti, JNIEnv *env) {
 312     jvmtiPhase phase = getVMPhase(jvmti);
 313 
 314     NSK_TRACE(lock(jvmti));
 315 
 316     NSK_DISPLAY1("c) VMDeath: the current phase of VM execution %s\n"
 317                 , TranslatePhase(phase)
 318                 );
 319 
 320     // check JNI function table interception
 321     fnd_calls = 0;
 322     NSK_TRACE(doRedirect(jvmti, phase));
 323     NSK_TRACE(checkCall(env, 1, "VMDeath", TranslatePhase(phase), 1));
 324 
 325     // check restored JNI function table
 326     fnd_calls = 0;
 327     NSK_TRACE(doRestore(jvmti));
 328     NSK_TRACE(checkCall(env, 2, "VMDeath", TranslatePhase(phase), 0));
 329 
 330     (void) memset(&callbacks, 0, sizeof(callbacks));
 331 
 332     if (!NSK_JVMTI_VERIFY(
 333             NSK_CPP_STUB3(
 334                 SetEventCallbacks
 335                 , jvmti
 336                 , &callbacks
 337                 , sizeof(callbacks)
 338                 )
 339             )
 340        )
 341         result = NSK_STATUS_FAILED;
 342 
 343     NSK_TRACE(unlock(jvmti));
 344 
 345     if (result == NSK_STATUS_FAILED) {
 346         exit(NSK_STATUS_FAILED);
 347     }
 348 }
 349 
 350 /* ====================================================================== */
 351 JNIEXPORT jint JNICALL
 352 Java_nsk_jvmti_scenarios_jni_1interception_JI01_ji01t001_check(JNIEnv *env, jobject obj) {
 353     return result;
 354 }
 355 
 356 /* ====================================================================== */
 357 #ifdef STATIC_BUILD
 358 JNIEXPORT jint JNICALL Agent_OnLoad_ji01t001(JavaVM *jvm, char *options, void *reserved) {
 359     return Agent_Initialize(jvm, options, reserved);
 360 }
 361 JNIEXPORT jint JNICALL Agent_OnAttach_ji01t001(JavaVM *jvm, char *options, void *reserved) {
 362     return Agent_Initialize(jvm, options, reserved);
 363 }
 364 JNIEXPORT jint JNI_OnLoad_ji01t001(JavaVM *jvm, char *options, void *reserved) {
 365     return JNI_VERSION_1_8;
 366 }
 367 #endif
 368 jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) {
 369     if (!NSK_VERIFY(
 370                 nsk_jvmti_parseOptions(options)
 371                 )
 372        )
 373         return JNI_ERR;
 374 
 375 
 376     if (!NSK_VERIFY(
 377             NSK_CPP_STUB3(
 378                 GetEnv
 379                 , jvm
 380                 , (void **) &jvmti
 381                 , JVMTI_VERSION_1_1
 382                 ) == JNI_OK
 383             && jvmti != NULL
 384             )
 385        )
 386         return JNI_ERR;
 387 
 388 
 389     if (!NSK_JVMTI_VERIFY(
 390             NSK_CPP_STUB3(
 391                 CreateRawMonitor
 392                 , jvmti
 393                 , "_event_lock"
 394                 , &eventLock
 395                 )
 396             )
 397        )
 398         return JNI_ERR;
 399 
 400     NSK_DISPLAY1("a) Trying to intercept JNI functions during %s phase ...\n"
 401                 , TranslatePhase(getVMPhase(jvmti))
 402                 );
 403 
 404     NSK_TRACE(doRedirect(jvmti, getVMPhase(jvmti)));
 405 
 406     NSK_DISPLAY0("Setting event callbacks...\n");
 407 
 408     (void) memset(&callbacks, 0, sizeof(callbacks));
 409     callbacks.VMInit = &VMInit;
 410     callbacks.VMDeath = &VMDeath;
 411 
 412     if (!NSK_JVMTI_VERIFY(
 413             NSK_CPP_STUB3(
 414                 SetEventCallbacks
 415                 , jvmti
 416                 , &callbacks
 417                 , sizeof(callbacks)
 418                 )
 419             )
 420        )
 421         return JNI_ERR;
 422 
 423 
 424     NSK_DISPLAY0("Event callbacks are set\nEnabling events...\n");
 425 
 426     if (!NSK_JVMTI_VERIFY(
 427             NSK_CPP_STUB4(
 428                 SetEventNotificationMode
 429                 , jvmti
 430                 , JVMTI_ENABLE
 431                 , JVMTI_EVENT_VM_INIT
 432                 , NULL
 433                 )
 434             )
 435        )
 436         return JNI_ERR;
 437 
 438 
 439     if (!NSK_JVMTI_VERIFY(
 440             NSK_CPP_STUB4(
 441                 SetEventNotificationMode
 442                 , jvmti
 443                 , JVMTI_ENABLE
 444                 , JVMTI_EVENT_VM_DEATH
 445                 , NULL
 446                 )
 447             )
 448        )
 449         return JNI_ERR;
 450 
 451     NSK_DISPLAY0("Events are enabled\n");
 452 
 453     return JNI_OK;
 454 }
 455 
 456 }