8233624: Enhance JNI linkage

Tue, 22 Sep 2020 13:09:39 +0300

author
vkempik
date
Tue, 22 Sep 2020 13:09:39 +0300
changeset 10006
d0f692037e7b
parent 10005
c92ba514724d
child 10007
cb1e375e88a9

8233624: Enhance JNI linkage
Reviewed-by: dholmes, jrose, rhalade, mschoene, mbalao, andrew

src/share/vm/prims/nativeLookup.cpp file | annotate | diff | comparison | revisions
src/share/vm/runtime/globals.hpp file | annotate | diff | comparison | revisions
     1.1 --- a/src/share/vm/prims/nativeLookup.cpp	Mon Sep 28 01:52:34 2020 +0100
     1.2 +++ b/src/share/vm/prims/nativeLookup.cpp	Tue Sep 22 13:09:39 2020 +0300
     1.3 @@ -61,27 +61,118 @@
     1.4  #endif
     1.5  
     1.6  
     1.7 -static void mangle_name_on(outputStream* st, Symbol* name, int begin, int end) {
     1.8 +/*
     1.9 +
    1.10 +The JNI specification defines the mapping from a Java native method name to
    1.11 +a C native library implementation function name as follows:
    1.12 +
    1.13 +  The mapping produces a native method name by concatenating the following components
    1.14 +  derived from a `native` method declaration:
    1.15 +
    1.16 +  1. the prefix Java_
    1.17 +  2. given the binary name, in internal form, of the class which declares the native method:
    1.18 +     the result of escaping the name.
    1.19 +  3. an underscore ("_")
    1.20 +  4. the escaped method name
    1.21 +  5. if the native method declaration is overloaded: two underscores ("__") followed by the
    1.22 +   escaped parameter descriptor (JVMS 4.3.3) of the method declaration.
    1.23 +
    1.24 +  Escaping leaves every alphanumeric ASCII character (A-Za-z0-9) unchanged, and replaces each
    1.25 +  UTF-16 code unit n the table below with the corresponding escape sequence. If the name to be
    1.26 +  escaped contains a surrogate pair, then the high-surrogate code unit and the low-surrogate code
    1.27 +  unit are escaped separately. The result of escaping is a string consisting only of the ASCII
    1.28 +  characters A-Za-z0-9 and underscore.
    1.29 +
    1.30 +  ------------------------------                  ------------------------------------
    1.31 +  UTF-16 code unit                                Escape sequence
    1.32 +  ------------------------------                  ------------------------------------
    1.33 +  Forward slash (/, U+002F)                       _
    1.34 +  Underscore (_, U+005F)                          _1
    1.35 +  Semicolon (;, U+003B)                           _2
    1.36 +  Left square bracket ([, U+005B)                 _3
    1.37 +  Any UTF-16 code unit \u_WXYZ_ that does not     _0wxyz where w, x, y, and z are the lower-case
    1.38 +  represent alphanumeric ASCII (A-Za-z0-9),       forms of the hexadecimal digits W, X, Y, and Z.
    1.39 +  forward slash, underscore, semicolon,           (For example, U+ABCD becomes _0abcd.)
    1.40 +  or left square bracket
    1.41 +  ------------------------------                  ------------------------------------
    1.42 +
    1.43 +  Note that escape sequences can safely begin _0, _1, etc, because class and method
    1.44 +  names in Java source code never begin with a number. However, that is not the case in
    1.45 +  class files that were not generated from Java source code.
    1.46 +
    1.47 +  To preserve the 1:1 mapping to a native method name, the VM checks the resulting name as
    1.48 +  follows. If the process of escaping any precursor string from the native  method declaration
    1.49 +  (class or method name, or argument type) causes a "0", "1", "2", or "3" character
    1.50 +  from the precursor string to appear unchanged in the result *either* immediately after an
    1.51 +  underscore *or* at the beginning of the escaped string (where it will follow an underscore
    1.52 +  in the fully assembled name), then the escaping process is said to have "failed".
    1.53 +  In such cases, no native library search is performed, and the attempt to link the native
    1.54 +  method invocation will throw UnsatisfiedLinkError.
    1.55 +
    1.56 +
    1.57 +For example:
    1.58 +
    1.59 +  package/my_class/method
    1.60 +
    1.61 +and
    1.62 +
    1.63 +  package/my/1class/method
    1.64 +
    1.65 +both map to
    1.66 +
    1.67 +  Java_package_my_1class_method
    1.68 +
    1.69 +To address this potential conflict we need only check if the character after
    1.70 +/ is a digit 0..3, or if the first character after an injected '_' seperator
    1.71 +is a digit 0..3. If we encounter an invalid identifier we reset the
    1.72 +stringStream and return false. Otherwise the stringStream contains the mapped
    1.73 +name and we return true.
    1.74 +
    1.75 +To address legacy compatibility, the UseLegacyJNINameEscaping flag can be set
    1.76 +which skips the extra checks.
    1.77 +
    1.78 +*/
    1.79 +static bool map_escaped_name_on(stringStream* st, Symbol* name, int begin, int end) {
    1.80    char* bytes = (char*)name->bytes() + begin;
    1.81    char* end_bytes = (char*)name->bytes() + end;
    1.82 +  bool check_escape_char = true; // initially true as first character here follows '_'
    1.83    while (bytes < end_bytes) {
    1.84      jchar c;
    1.85      bytes = UTF8::next(bytes, &c);
    1.86      if (c <= 0x7f && isalnum(c)) {
    1.87 +      if (check_escape_char && (c >= '0' && c <= '3') &&
    1.88 +          !UseLegacyJNINameEscaping) {
    1.89 +        // This is a non-Java identifier and we won't escape it to
    1.90 +        // ensure no name collisions with a Java identifier.
    1.91 +        if (PrintJNIResolving) {
    1.92 +          ResourceMark rm;
    1.93 +          tty->print_cr("[Lookup of native method with non-Java identifier rejected: %s]",
    1.94 +                                  name->as_C_string());
    1.95 +        }
    1.96 +        st->reset();  // restore to "" on error
    1.97 +        return false;
    1.98 +      }
    1.99        st->put((char) c);
   1.100 +      check_escape_char = false;
   1.101      } else {
   1.102 -           if (c == '_') st->print("_1");
   1.103 -      else if (c == '/') st->print("_");
   1.104 +      check_escape_char = false;
   1.105 +      if (c == '_') st->print("_1");
   1.106 +      else if (c == '/') {
   1.107 +        st->print("_");
   1.108 +        // Following a / we must have non-escape character
   1.109 +        check_escape_char = true;
   1.110 +      }
   1.111        else if (c == ';') st->print("_2");
   1.112        else if (c == '[') st->print("_3");
   1.113        else               st->print("_%.5x", c);
   1.114      }
   1.115    }
   1.116 +  return true;
   1.117  }
   1.118  
   1.119  
   1.120 -static void mangle_name_on(outputStream* st, Symbol* name) {
   1.121 -  mangle_name_on(st, name, 0, name->utf8_length());
   1.122 +static bool map_escaped_name_on(stringStream* st, Symbol* name) {
   1.123 +  return map_escaped_name_on(st, name, 0, name->utf8_length());
   1.124  }
   1.125  
   1.126  
   1.127 @@ -90,10 +181,14 @@
   1.128    // Prefix
   1.129    st.print("Java_");
   1.130    // Klass name
   1.131 -  mangle_name_on(&st, method->klass_name());
   1.132 +  if (!map_escaped_name_on(&st, method->klass_name())) {
   1.133 +    return NULL;
   1.134 +  }
   1.135    st.print("_");
   1.136    // Method name
   1.137 -  mangle_name_on(&st, method->name());
   1.138 +  if (!map_escaped_name_on(&st, method->name())) {
   1.139 +    return NULL;
   1.140 +  }
   1.141    return st.as_string();
   1.142  }
   1.143  
   1.144 @@ -103,16 +198,20 @@
   1.145    // Prefix
   1.146    st.print("JavaCritical_");
   1.147    // Klass name
   1.148 -  mangle_name_on(&st, method->klass_name());
   1.149 +  if (!map_escaped_name_on(&st, method->klass_name())) {
   1.150 +    return NULL;
   1.151 +  }
   1.152    st.print("_");
   1.153    // Method name
   1.154 -  mangle_name_on(&st, method->name());
   1.155 +  if (!map_escaped_name_on(&st, method->name())) {
   1.156 +    return NULL;
   1.157 +  }
   1.158    return st.as_string();
   1.159  }
   1.160  
   1.161  
   1.162  char* NativeLookup::long_jni_name(methodHandle method) {
   1.163 -  // Signature ignore the wrapping parenteses and the trailing return type
   1.164 +  // Signatures ignore the wrapping parentheses and the trailing return type
   1.165    stringStream st;
   1.166    Symbol* signature = method->signature();
   1.167    st.print("__");
   1.168 @@ -120,7 +219,10 @@
   1.169    int end;
   1.170    for (end = 0; end < signature->utf8_length() && signature->byte_at(end) != ')'; end++);
   1.171    // skip first '('
   1.172 -  mangle_name_on(&st, signature, 1, end);
   1.173 +  if (!map_escaped_name_on(&st, signature, 1, end)) {
   1.174 +    return NULL;
   1.175 +  }
   1.176 +
   1.177    return st.as_string();
   1.178  }
   1.179  
   1.180 @@ -247,6 +349,11 @@
   1.181    in_base_library = false;
   1.182    // Compute pure name
   1.183    char* pure_name = pure_jni_name(method);
   1.184 +  if (pure_name == NULL) {
   1.185 +    // JNI name mapping rejected this method so return
   1.186 +    // NULL to indicate UnsatisfiedLinkError should be thrown.
   1.187 +    return NULL;
   1.188 +  }
   1.189  
   1.190    // Compute argument size
   1.191    int args_size = 1                             // JNIEnv
   1.192 @@ -260,6 +367,11 @@
   1.193  
   1.194    // Compute long name
   1.195    char* long_name = long_jni_name(method);
   1.196 +  if (long_name == NULL) {
   1.197 +    // JNI name mapping rejected this method so return
   1.198 +    // NULL to indicate UnsatisfiedLinkError should be thrown.
   1.199 +    return NULL;
   1.200 +  }
   1.201  
   1.202    // 2) Try JNI long style
   1.203    entry = lookup_style(method, pure_name, long_name, args_size, true,  in_base_library, CHECK_NULL);
   1.204 @@ -299,6 +411,11 @@
   1.205  
   1.206    // Compute critical name
   1.207    char* critical_name = critical_jni_name(method);
   1.208 +  if (critical_name == NULL) {
   1.209 +    // JNI name mapping rejected this method so return
   1.210 +    // NULL to indicate UnsatisfiedLinkError should be thrown.
   1.211 +    return NULL;
   1.212 +  }
   1.213  
   1.214    // Compute argument size
   1.215    int args_size = 1                             // JNIEnv
   1.216 @@ -312,6 +429,11 @@
   1.217  
   1.218    // Compute long name
   1.219    char* long_name = long_jni_name(method);
   1.220 +  if (long_name == NULL) {
   1.221 +    // JNI name mapping rejected this method so return
   1.222 +    // NULL to indicate UnsatisfiedLinkError should be thrown.
   1.223 +    return NULL;
   1.224 +  }
   1.225  
   1.226    // 2) Try JNI long style
   1.227    entry = lookup_critical_style(method, critical_name, long_name, args_size, true);
     2.1 --- a/src/share/vm/runtime/globals.hpp	Mon Sep 28 01:52:34 2020 +0100
     2.2 +++ b/src/share/vm/runtime/globals.hpp	Tue Sep 22 13:09:39 2020 +0300
     2.3 @@ -706,6 +706,9 @@
     2.4    product(bool, CriticalJNINatives, true,                                   \
     2.5            "Check for critical JNI entry points")                            \
     2.6                                                                              \
     2.7 +  product(bool, UseLegacyJNINameEscaping, false,                            \
     2.8 +          "Use the original JNI name escaping scheme")                      \
     2.9 +                                                                            \
    2.10    notproduct(bool, StressCriticalJNINatives, false,                         \
    2.11            "Exercise register saving code in critical natives")              \
    2.12                                                                              \

mercurial