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.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 /*
  27  * Copyright 2003 Wily Technology, Inc.
  28  */
  29 
  30 #include    <jni.h>
  31 #include    <jvmti.h>
  32 
  33 #include    "JPLISAssert.h"
  34 #include    "Utilities.h"
  35 #include    "JavaExceptions.h"
  36 
  37 /**
  38  * This module contains utility routines for manipulating Java throwables
  39  * and JNIEnv throwable state from native code.
  40  */
  41 
  42 static jthrowable   sFallbackInternalError  = NULL;
  43 
  44 /*
  45  * Local forward declarations.
  46  */
  47 
  48 /* insist on having a throwable. If we already have one, return it.
  49  * If not, map to fallback
  50  */
  51 jthrowable
  52 forceFallback(jthrowable potentialException);
  53 
  54 
  55 jthrowable
  56 forceFallback(jthrowable potentialException) {
  57     if ( potentialException == NULL ) {
  58         return sFallbackInternalError;
  59     }
  60     else {
  61         return potentialException;
  62     }
  63 }
  64 
  65 /**
  66  *  Returns true if it properly sets up a fallback exception
  67  */
  68 jboolean
  69 initializeFallbackError(JNIEnv* jnienv) {
  70     jplis_assert(isSafeForJNICalls(jnienv));
  71     sFallbackInternalError = createInternalError(jnienv, NULL);
  72     jplis_assert(isSafeForJNICalls(jnienv));
  73     return (sFallbackInternalError != NULL);
  74 }
  75 
  76 
  77 /*
  78  *  Map everything to InternalError.
  79  */
  80 jthrowable
  81 mapAllCheckedToInternalErrorMapper( JNIEnv *    jnienv,
  82                                     jthrowable  throwableToMap) {
  83     jthrowable  mappedThrowable = NULL;
  84     jstring     message         = NULL;
  85 
  86     jplis_assert(throwableToMap != NULL);
  87     jplis_assert(isSafeForJNICalls(jnienv));
  88     jplis_assert(!isUnchecked(jnienv, throwableToMap));
  89 
  90     message = getMessageFromThrowable(jnienv, throwableToMap);
  91     mappedThrowable = createInternalError(jnienv, message);
  92 
  93     jplis_assert(isSafeForJNICalls(jnienv));
  94     return mappedThrowable;
  95 }
  96 
  97 
  98 jboolean
  99 checkForThrowable(  JNIEnv*     jnienv) {
 100     return (*jnienv)->ExceptionCheck(jnienv);
 101 }
 102 
 103 jboolean
 104 isSafeForJNICalls(  JNIEnv * jnienv) {
 105     return !(*jnienv)->ExceptionCheck(jnienv);
 106 }
 107 
 108 
 109 void
 110 logThrowable(   JNIEnv * jnienv) {
 111     if ( checkForThrowable(jnienv) ) {
 112         (*jnienv)->ExceptionDescribe(jnienv);
 113     }
 114 }
 115 
 116 
 117 
 118 /**
 119  *  Creates an exception or error with the fully qualified classname (ie java/lang/Error)
 120  *  and message passed to its constructor
 121  */
 122 jthrowable
 123 createThrowable(    JNIEnv *        jnienv,
 124                     const char *    className,
 125                     jstring         message) {
 126     jthrowable  exception           = NULL;
 127     jmethodID   constructor         = NULL;
 128     jclass      exceptionClass      = NULL;
 129     jboolean    errorOutstanding    = JNI_FALSE;
 130 
 131     jplis_assert(className != NULL);
 132     jplis_assert(isSafeForJNICalls(jnienv));
 133 
 134     /* create new VMError with message from exception */
 135     exceptionClass = (*jnienv)->FindClass(jnienv, className);
 136     errorOutstanding = checkForAndClearThrowable(jnienv);
 137     jplis_assert(!errorOutstanding);
 138 
 139     if (!errorOutstanding) {
 140         constructor = (*jnienv)->GetMethodID(   jnienv,
 141                                                 exceptionClass,
 142                                                 "<init>",
 143                                                 "(Ljava/lang/String;)V");
 144         errorOutstanding = checkForAndClearThrowable(jnienv);
 145         jplis_assert(!errorOutstanding);
 146     }
 147 
 148     if (!errorOutstanding) {
 149         exception = (*jnienv)->NewObject(jnienv, exceptionClass, constructor, message);
 150         errorOutstanding = checkForAndClearThrowable(jnienv);
 151         jplis_assert(!errorOutstanding);
 152     }
 153 
 154     jplis_assert(isSafeForJNICalls(jnienv));
 155     return exception;
 156 }
 157 
 158 jthrowable
 159 createInternalError(JNIEnv * jnienv, jstring message) {
 160     return createThrowable( jnienv,
 161                             "java/lang/InternalError",
 162                             message);
 163 }
 164 
 165 jthrowable
 166 createThrowableFromJVMTIErrorCode(JNIEnv * jnienv, jvmtiError errorCode) {
 167     const char * throwableClassName = NULL;
 168     const char * message            = NULL;
 169     jstring messageString           = NULL;
 170 
 171     switch ( errorCode ) {
 172         case JVMTI_ERROR_NULL_POINTER:
 173                 throwableClassName = "java/lang/NullPointerException";
 174                 break;
 175 
 176         case JVMTI_ERROR_ILLEGAL_ARGUMENT:
 177                 throwableClassName = "java/lang/IllegalArgumentException";
 178                 break;
 179 
 180         case JVMTI_ERROR_OUT_OF_MEMORY:
 181                 throwableClassName = "java/lang/OutOfMemoryError";
 182                 break;
 183 
 184         case JVMTI_ERROR_CIRCULAR_CLASS_DEFINITION:
 185                 throwableClassName = "java/lang/ClassCircularityError";
 186                 break;
 187 
 188         case JVMTI_ERROR_FAILS_VERIFICATION:
 189                 throwableClassName = "java/lang/VerifyError";
 190                 break;
 191 
 192         case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_ADDED:
 193                 throwableClassName = "java/lang/UnsupportedOperationException";
 194                 message = "class redefinition failed: attempted to add a method";
 195                 break;
 196 
 197         case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_SCHEMA_CHANGED:
 198                 throwableClassName = "java/lang/UnsupportedOperationException";
 199                 message = "class redefinition failed: attempted to change the schema (add/remove fields)";
 200                 break;
 201 
 202         case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_HIERARCHY_CHANGED:
 203                 throwableClassName = "java/lang/UnsupportedOperationException";
 204                 message = "class redefinition failed: attempted to change superclass or interfaces";
 205                 break;
 206 
 207         case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_DELETED:
 208                 throwableClassName = "java/lang/UnsupportedOperationException";
 209                 message = "class redefinition failed: attempted to delete a method";
 210                 break;
 211 
 212         case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_CLASS_MODIFIERS_CHANGED:
 213                 throwableClassName = "java/lang/UnsupportedOperationException";
 214                 message = "class redefinition failed: attempted to change the class modifiers";
 215                 break;
 216 
 217         case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_CLASS_ATTRIBUTE_CHANGED:
 218                 throwableClassName = "java/lang/UnsupportedOperationException";
 219                 message = "class redefinition failed: attempted to change the class NestHost or NestMembers attribute";
 220                 break;
 221 
 222         case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_MODIFIERS_CHANGED:
 223                 throwableClassName = "java/lang/UnsupportedOperationException";
 224                 message = "class redefinition failed: attempted to change method modifiers";
 225                 break;
 226 
 227         case JVMTI_ERROR_UNSUPPORTED_VERSION:
 228                 throwableClassName = "java/lang/UnsupportedClassVersionError";
 229                 break;
 230 
 231         case JVMTI_ERROR_NAMES_DONT_MATCH:
 232                 throwableClassName = "java/lang/NoClassDefFoundError";
 233                 message = "class names don't match";
 234                 break;
 235 
 236         case JVMTI_ERROR_INVALID_CLASS_FORMAT:
 237                 throwableClassName = "java/lang/ClassFormatError";
 238                 break;
 239 
 240         case JVMTI_ERROR_UNMODIFIABLE_CLASS:
 241                 throwableClassName = "java/lang/instrument/UnmodifiableClassException";
 242                 break;
 243 
 244         case JVMTI_ERROR_INVALID_CLASS:
 245                 throwableClassName = "java/lang/InternalError";
 246                 message = "class redefinition failed: invalid class";
 247                 break;
 248 
 249         case JVMTI_ERROR_CLASS_LOADER_UNSUPPORTED:
 250                 throwableClassName = "java/lang/UnsupportedOperationException";
 251                 message = "unsupported operation";
 252                 break;
 253 
 254         case JVMTI_ERROR_INTERNAL:
 255         default:
 256                 throwableClassName = "java/lang/InternalError";
 257                 break;
 258         }
 259 
 260     if ( message != NULL ) {
 261         jboolean errorOutstanding;
 262 
 263         messageString = (*jnienv)->NewStringUTF(jnienv, message);
 264         errorOutstanding = checkForAndClearThrowable(jnienv);
 265         jplis_assert_msg(!errorOutstanding, "can't create exception java string");
 266     }
 267     return createThrowable( jnienv,
 268                             throwableClassName,
 269                             messageString);
 270 
 271 }
 272 
 273 
 274 /**
 275  *  Calls toString() on the given message which is the same call made by
 276  *  Exception when passed a throwable to its constructor
 277  */
 278 jstring
 279 getMessageFromThrowable(    JNIEnv*     jnienv,
 280                             jthrowable  exception) {
 281     jclass      exceptionClass      = NULL;
 282     jmethodID   method              = NULL;
 283     jstring     message             = NULL;
 284     jboolean    errorOutstanding    = JNI_FALSE;
 285 
 286     jplis_assert(isSafeForJNICalls(jnienv));
 287 
 288     /* call getMessage on exception */
 289     exceptionClass = (*jnienv)->GetObjectClass(jnienv, exception);
 290     errorOutstanding = checkForAndClearThrowable(jnienv);
 291     jplis_assert(!errorOutstanding);
 292 
 293     if (!errorOutstanding) {
 294         method = (*jnienv)->GetMethodID(jnienv,
 295                                         exceptionClass,
 296                                         "toString",
 297                                         "()Ljava/lang/String;");
 298         errorOutstanding = checkForAndClearThrowable(jnienv);
 299         jplis_assert(!errorOutstanding);
 300     }
 301 
 302     if (!errorOutstanding) {
 303         message = (*jnienv)->CallObjectMethod(jnienv, exception, method);
 304         errorOutstanding = checkForAndClearThrowable(jnienv);
 305         jplis_assert(!errorOutstanding);
 306     }
 307 
 308     jplis_assert(isSafeForJNICalls(jnienv));
 309 
 310     return message;
 311 }
 312 
 313 
 314 /**
 315  *  Returns whether the exception given is an unchecked exception:
 316  *  a subclass of Error or RuntimeException
 317  */
 318 jboolean
 319 isUnchecked(    JNIEnv*     jnienv,
 320                 jthrowable  exception) {
 321     jboolean result = JNI_FALSE;
 322 
 323     jplis_assert(isSafeForJNICalls(jnienv));
 324     result =    (exception == NULL) ||
 325                 isInstanceofClassName(jnienv, exception, "java/lang/Error") ||
 326                 isInstanceofClassName(jnienv, exception, "java/lang/RuntimeException");
 327     jplis_assert(isSafeForJNICalls(jnienv));
 328     return result;
 329 }
 330 
 331 /*
 332  *  Returns the current throwable, if any. Clears the throwable state.
 333  *  Clients can use this to preserve the current throwable state on the stack.
 334  */
 335 jthrowable
 336 preserveThrowable(JNIEnv * jnienv) {
 337     jthrowable result = (*jnienv)->ExceptionOccurred(jnienv);
 338     if ( result != NULL ) {
 339         (*jnienv)->ExceptionClear(jnienv);
 340     }
 341     return result;
 342 }
 343 
 344 /*
 345  *  Installs the supplied throwable into the JNIEnv if the throwable is not null.
 346  *  Clients can use this to preserve the current throwable state on the stack.
 347  */
 348 void
 349 restoreThrowable(   JNIEnv *    jnienv,
 350                     jthrowable  preservedException) {
 351     throwThrowable( jnienv,
 352                     preservedException);
 353     return;
 354 }
 355 
 356 void
 357 throwThrowable(     JNIEnv *    jnienv,
 358                     jthrowable  exception) {
 359     if ( exception != NULL ) {
 360         jint result = (*jnienv)->Throw(jnienv, exception);
 361         jplis_assert_msg(result == JNI_OK, "throwThrowable failed to re-throw");
 362     }
 363     return;
 364 }
 365 
 366 
 367 /*
 368  *  Always clears the JNIEnv throwable state. Returns true if an exception was present
 369  *  before the clearing operation.
 370  */
 371 jboolean
 372 checkForAndClearThrowable(  JNIEnv *    jnienv) {
 373     jboolean result = (*jnienv)->ExceptionCheck(jnienv);
 374     if ( result ) {
 375         (*jnienv)->ExceptionClear(jnienv);
 376     }
 377     return result;
 378 }
 379 
 380 /* creates a java.lang.InternalError and installs it into the JNIEnv */
 381 void
 382 createAndThrowInternalError(JNIEnv * jnienv) {
 383     jthrowable internalError = createInternalError( jnienv, NULL);
 384     throwThrowable(jnienv, forceFallback(internalError));
 385 }
 386 
 387 void
 388 createAndThrowThrowableFromJVMTIErrorCode(JNIEnv * jnienv, jvmtiError errorCode) {
 389     jthrowable throwable = createThrowableFromJVMTIErrorCode(jnienv, errorCode);
 390     throwThrowable(jnienv, forceFallback(throwable));
 391 }
 392 
 393 void
 394 mapThrownThrowableIfNecessary(  JNIEnv *                jnienv,
 395                                 CheckedExceptionMapper  mapper) {
 396     jthrowable  originalThrowable   = NULL;
 397     jthrowable  resultThrowable     = NULL;
 398 
 399     originalThrowable = preserveThrowable(jnienv);
 400 
 401     /* the throwable is now cleared, so JNI calls are safe */
 402     if ( originalThrowable != NULL ) {
 403         /* if there is an exception: we can just throw it if it is unchecked. If checked,
 404          * we need to map it (mapper is conditional, will vary by usage, hence the callback)
 405          */
 406         if ( isUnchecked(jnienv, originalThrowable) ) {
 407             resultThrowable = originalThrowable;
 408         }
 409         else {
 410             resultThrowable = (*mapper) (jnienv, originalThrowable);
 411         }
 412     }
 413 
 414     /* re-establish the correct throwable */
 415     if ( resultThrowable != NULL ) {
 416         throwThrowable(jnienv, forceFallback(resultThrowable));
 417     }
 418 
 419 }