8166358: Re-enable String verification in java_lang_String::create_from_str()
Check for invalid strings in class names in debug mode, and only verify valid strings in create_for_str(). Reviewed-by: dholmes, hseigel
This commit is contained in:
parent
a689a1108c
commit
6aa15ad7ab
src/hotspot/share
test/hotspot/jtreg/runtime/jni/FindClassUtf8
@ -286,8 +286,7 @@ Handle java_lang_String::create_from_unicode(const jchar* unicode, int length, T
|
||||
char* expected = UNICODE::as_utf8(unicode, length);
|
||||
char* actual = as_utf8_string(h_obj());
|
||||
if (strcmp(expected, actual) != 0) {
|
||||
tty->print_cr("Unicode conversion failure: %s --> %s", expected, actual);
|
||||
ShouldNotReachHere();
|
||||
fatal("Unicode conversion failure: %s --> %s", expected, actual);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -324,19 +323,16 @@ Handle java_lang_String::create_from_str(const char* utf8_str, TRAPS) {
|
||||
}
|
||||
|
||||
#ifdef ASSERT
|
||||
// This check is too strict because the input string is not necessarily valid UTF8.
|
||||
// This check is too strict when the input string is not a valid UTF8.
|
||||
// For example, it may be created with arbitrary content via jni_NewStringUTF.
|
||||
/*
|
||||
{
|
||||
if (UTF8::is_legal_utf8((const unsigned char*)utf8_str, (int)strlen(utf8_str), false)) {
|
||||
ResourceMark rm;
|
||||
const char* expected = utf8_str;
|
||||
char* actual = as_utf8_string(h_obj());
|
||||
if (strcmp(expected, actual) != 0) {
|
||||
tty->print_cr("String conversion failure: %s --> %s", expected, actual);
|
||||
ShouldNotReachHere();
|
||||
fatal("String conversion failure: %s --> %s", expected, actual);
|
||||
}
|
||||
}
|
||||
*/
|
||||
#endif
|
||||
|
||||
return h_obj;
|
||||
@ -376,8 +372,7 @@ Handle java_lang_String::create_from_symbol(Symbol* symbol, TRAPS) {
|
||||
const char* expected = symbol->as_utf8();
|
||||
char* actual = as_utf8_string(h_obj());
|
||||
if (strncmp(expected, actual, utf8_len) != 0) {
|
||||
tty->print_cr("Symbol conversion failure: %s --> %s", expected, actual);
|
||||
ShouldNotReachHere();
|
||||
fatal("Symbol conversion failure: %s --> %s", expected, actual);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -84,6 +84,7 @@
|
||||
#include "services/diagnosticCommand.hpp"
|
||||
#include "services/threadService.hpp"
|
||||
#include "utilities/macros.hpp"
|
||||
#include "utilities/utf8.hpp"
|
||||
#if INCLUDE_CDS
|
||||
#include "classfile/systemDictionaryShared.hpp"
|
||||
#endif
|
||||
@ -232,6 +233,27 @@ bool SystemDictionary::is_platform_class_loader(oop class_loader) {
|
||||
// ----------------------------------------------------------------------------
|
||||
// Resolving of classes
|
||||
|
||||
Symbol* SystemDictionary::class_name_symbol(const char* name, Symbol* exception, TRAPS) {
|
||||
if (name == NULL) {
|
||||
THROW_MSG_0(exception, "No class name given");
|
||||
}
|
||||
if ((int)strlen(name) > Symbol::max_length()) {
|
||||
// It's impossible to create this class; the name cannot fit
|
||||
// into the constant pool.
|
||||
Exceptions::fthrow(THREAD_AND_LOCATION, exception,
|
||||
"Class name exceeds maximum length of %d: %s",
|
||||
Symbol::max_length(),
|
||||
name);
|
||||
return NULL;
|
||||
}
|
||||
// Callers should ensure that the name is never an illegal UTF8 string.
|
||||
assert(UTF8::is_legal_utf8((const unsigned char*)name, (int)strlen(name), false),
|
||||
"Class name is not a valid utf8 string.");
|
||||
|
||||
// Make a new symbol for the class name.
|
||||
return SymbolTable::new_symbol(name);
|
||||
}
|
||||
|
||||
// Forwards to resolve_or_null
|
||||
|
||||
Klass* SystemDictionary::resolve_or_fail(Symbol* class_name, Handle class_loader, Handle protection_domain, bool throw_error, TRAPS) {
|
||||
|
@ -668,6 +668,9 @@ public:
|
||||
return !m->is_public() && m->method_holder() == SystemDictionary::Object_klass();
|
||||
}
|
||||
|
||||
// Return Symbol or throw exception if name given is can not be a valid Symbol.
|
||||
static Symbol* class_name_symbol(const char* name, Symbol* exception, TRAPS);
|
||||
|
||||
protected:
|
||||
// Setup link to hierarchy
|
||||
static void add_to_hierarchy(InstanceKlass* k, TRAPS);
|
||||
|
@ -316,23 +316,11 @@ JNI_ENTRY(jclass, jni_DefineClass(JNIEnv *env, const char *name, jobject loaderR
|
||||
jclass cls = NULL;
|
||||
DT_RETURN_MARK(DefineClass, jclass, (const jclass&)cls);
|
||||
|
||||
TempNewSymbol class_name = NULL;
|
||||
// Since exceptions can be thrown, class initialization can take place
|
||||
// if name is NULL no check for class name in .class stream has to be made.
|
||||
if (name != NULL) {
|
||||
const int str_len = (int)strlen(name);
|
||||
if (str_len > Symbol::max_length()) {
|
||||
// It's impossible to create this class; the name cannot fit
|
||||
// into the constant pool.
|
||||
Exceptions::fthrow(THREAD_AND_LOCATION,
|
||||
vmSymbols::java_lang_NoClassDefFoundError(),
|
||||
"Class name exceeds maximum length of %d: %s",
|
||||
Symbol::max_length(),
|
||||
name);
|
||||
return 0;
|
||||
}
|
||||
class_name = SymbolTable::new_symbol(name);
|
||||
}
|
||||
// Class resolution will get the class name from the .class stream if the name is null.
|
||||
TempNewSymbol class_name = name == NULL ? NULL :
|
||||
SystemDictionary::class_name_symbol(name, vmSymbols::java_lang_NoClassDefFoundError(),
|
||||
CHECK_NULL);
|
||||
|
||||
ResourceMark rm(THREAD);
|
||||
ClassFileStream st((u1*)buf, bufLen, NULL, ClassFileStream::verify);
|
||||
Handle class_loader (THREAD, JNIHandles::resolve(loaderRef));
|
||||
@ -374,19 +362,10 @@ JNI_ENTRY(jclass, jni_FindClass(JNIEnv *env, const char *name))
|
||||
jclass result = NULL;
|
||||
DT_RETURN_MARK(FindClass, jclass, (const jclass&)result);
|
||||
|
||||
// Sanity check the name: it cannot be null or larger than the maximum size
|
||||
// name we can fit in the constant pool.
|
||||
if (name == NULL) {
|
||||
THROW_MSG_0(vmSymbols::java_lang_NoClassDefFoundError(), "No class name given");
|
||||
}
|
||||
if ((int)strlen(name) > Symbol::max_length()) {
|
||||
Exceptions::fthrow(THREAD_AND_LOCATION,
|
||||
vmSymbols::java_lang_NoClassDefFoundError(),
|
||||
"Class name exceeds maximum length of %d: %s",
|
||||
Symbol::max_length(),
|
||||
name);
|
||||
return 0;
|
||||
}
|
||||
// This should be ClassNotFoundException imo.
|
||||
TempNewSymbol class_name =
|
||||
SystemDictionary::class_name_symbol(name, vmSymbols::java_lang_NoClassDefFoundError(),
|
||||
CHECK_NULL);
|
||||
|
||||
//%note jni_3
|
||||
Handle protection_domain;
|
||||
@ -418,8 +397,7 @@ JNI_ENTRY(jclass, jni_FindClass(JNIEnv *env, const char *name))
|
||||
}
|
||||
}
|
||||
|
||||
TempNewSymbol sym = SymbolTable::new_symbol(name);
|
||||
result = find_class_from_class_loader(env, sym, true, loader,
|
||||
result = find_class_from_class_loader(env, class_name, true, loader,
|
||||
protection_domain, true, thread);
|
||||
|
||||
if (log_is_enabled(Debug, class, resolve) && result != NULL) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2001, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2001, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -43,6 +43,7 @@
|
||||
#include "runtime/jfieldIDWorkaround.hpp"
|
||||
#include "runtime/jniHandles.inline.hpp"
|
||||
#include "runtime/thread.inline.hpp"
|
||||
#include "utilities/utf8.hpp"
|
||||
|
||||
// Complain every extra number of unplanned local refs
|
||||
#define CHECK_JNI_LOCAL_REF_CAP_WARN_THRESHOLD 32
|
||||
@ -134,6 +135,8 @@ static const char * fatal_wrong_field = "Wrong field ID passed to JNI";
|
||||
static const char * fatal_instance_field_not_found = "Instance field not found in JNI get/set field operations";
|
||||
static const char * fatal_instance_field_mismatch = "Field type (instance) mismatch in JNI get/set field operations";
|
||||
static const char * fatal_non_string = "JNI string operation received a non-string";
|
||||
static const char * fatal_non_utf8_class_name1 = "JNI class name is not a valid UTF8 string \"";
|
||||
static const char * fatal_non_utf8_class_name2 = "\"";
|
||||
|
||||
|
||||
// When in VM state:
|
||||
@ -489,6 +492,13 @@ void jniCheck::validate_class_descriptor(JavaThread* thr, const char* name) {
|
||||
warn_bad_class_descriptor1, name, warn_bad_class_descriptor2);
|
||||
ReportJNIWarning(thr, msg);
|
||||
}
|
||||
|
||||
// Verify that the class name given is a valid utf8 string
|
||||
if (!UTF8::is_legal_utf8((const unsigned char*)name, (int)strlen(name), false)) {
|
||||
char msg[JVM_MAXPATHLEN];
|
||||
jio_snprintf(msg, JVM_MAXPATHLEN, "%s%s%s", fatal_non_utf8_class_name1, name, fatal_non_utf8_class_name2);
|
||||
ReportJNIFatalError(thr, msg);
|
||||
}
|
||||
}
|
||||
|
||||
Klass* jniCheck::validate_class(JavaThread* thr, jclass clazz, bool allow_primitive) {
|
||||
|
@ -810,12 +810,13 @@ JVM_ENTRY(jclass, JVM_FindClassFromBootLoader(JNIEnv* env,
|
||||
const char* name))
|
||||
JVMWrapper("JVM_FindClassFromBootLoader");
|
||||
|
||||
// Java libraries should ensure that name is never null...
|
||||
// Java libraries should ensure that name is never null or illegal.
|
||||
if (name == NULL || (int)strlen(name) > Symbol::max_length()) {
|
||||
// It's impossible to create this class; the name cannot fit
|
||||
// into the constant pool.
|
||||
return NULL;
|
||||
}
|
||||
assert(UTF8::is_legal_utf8((const unsigned char*)name, (int)strlen(name), false), "illegal UTF name");
|
||||
|
||||
TempNewSymbol h_name = SymbolTable::new_symbol(name);
|
||||
Klass* k = SystemDictionary::resolve_or_null(h_name, CHECK_NULL);
|
||||
@ -834,14 +835,10 @@ JVM_ENTRY(jclass, JVM_FindClassFromCaller(JNIEnv* env, const char* name,
|
||||
jboolean init, jobject loader,
|
||||
jclass caller))
|
||||
JVMWrapper("JVM_FindClassFromCaller throws ClassNotFoundException");
|
||||
// Java libraries should ensure that name is never null...
|
||||
if (name == NULL || (int)strlen(name) > Symbol::max_length()) {
|
||||
// It's impossible to create this class; the name cannot fit
|
||||
// into the constant pool.
|
||||
THROW_MSG_0(vmSymbols::java_lang_ClassNotFoundException(), name);
|
||||
}
|
||||
|
||||
TempNewSymbol h_name = SymbolTable::new_symbol(name);
|
||||
TempNewSymbol h_name =
|
||||
SystemDictionary::class_name_symbol(name, vmSymbols::java_lang_ClassNotFoundException(),
|
||||
CHECK_NULL);
|
||||
|
||||
oop loader_oop = JNIHandles::resolve(loader);
|
||||
oop from_class = JNIHandles::resolve(caller);
|
||||
@ -870,20 +867,9 @@ JVM_END
|
||||
JVM_ENTRY(jclass, JVM_FindClassFromClass(JNIEnv *env, const char *name,
|
||||
jboolean init, jclass from))
|
||||
JVMWrapper("JVM_FindClassFromClass");
|
||||
if (name == NULL) {
|
||||
THROW_MSG_0(vmSymbols::java_lang_NoClassDefFoundError(), "No class name given");
|
||||
}
|
||||
if ((int)strlen(name) > Symbol::max_length()) {
|
||||
// It's impossible to create this class; the name cannot fit
|
||||
// into the constant pool.
|
||||
Exceptions::fthrow(THREAD_AND_LOCATION,
|
||||
vmSymbols::java_lang_NoClassDefFoundError(),
|
||||
"Class name exceeds maximum length of %d: %s",
|
||||
Symbol::max_length(),
|
||||
name);
|
||||
return 0;
|
||||
}
|
||||
TempNewSymbol h_name = SymbolTable::new_symbol(name);
|
||||
TempNewSymbol h_name =
|
||||
SystemDictionary::class_name_symbol(name, vmSymbols::java_lang_ClassNotFoundException(),
|
||||
CHECK_NULL);
|
||||
oop from_class_oop = JNIHandles::resolve(from);
|
||||
Klass* from_class = (from_class_oop == NULL)
|
||||
? (Klass*)NULL
|
||||
@ -949,23 +935,10 @@ static jclass jvm_define_class_common(JNIEnv *env, const char *name,
|
||||
ClassLoader::perf_app_classfile_bytes_read()->inc(len);
|
||||
}
|
||||
|
||||
// Since exceptions can be thrown, class initialization can take place
|
||||
// if name is NULL no check for class name in .class stream has to be made.
|
||||
TempNewSymbol class_name = NULL;
|
||||
if (name != NULL) {
|
||||
const int str_len = (int)strlen(name);
|
||||
if (str_len > Symbol::max_length()) {
|
||||
// It's impossible to create this class; the name cannot fit
|
||||
// into the constant pool.
|
||||
Exceptions::fthrow(THREAD_AND_LOCATION,
|
||||
vmSymbols::java_lang_NoClassDefFoundError(),
|
||||
"Class name exceeds maximum length of %d: %s",
|
||||
Symbol::max_length(),
|
||||
name);
|
||||
return 0;
|
||||
}
|
||||
class_name = SymbolTable::new_symbol(name, str_len);
|
||||
}
|
||||
// Class resolution will get the class name from the .class stream if the name is null.
|
||||
TempNewSymbol class_name = name == NULL ? NULL :
|
||||
SystemDictionary::class_name_symbol(name, vmSymbols::java_lang_NoClassDefFoundError(),
|
||||
CHECK_NULL);
|
||||
|
||||
ResourceMark rm(THREAD);
|
||||
ClassFileStream st((u1*)buf, len, source, ClassFileStream::verify);
|
||||
@ -1054,24 +1027,10 @@ static jclass jvm_lookup_define_class(JNIEnv *env, jclass lookup, const char *na
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Since exceptions can be thrown, class initialization can take place
|
||||
// if name is NULL no check for class name in .class stream has to be made.
|
||||
TempNewSymbol class_name = NULL;
|
||||
if (name != NULL) {
|
||||
const int str_len = (int)strlen(name);
|
||||
if (str_len > Symbol::max_length()) {
|
||||
// It's impossible to create this class; the name cannot fit
|
||||
// into the constant pool.
|
||||
Exceptions::fthrow(THREAD_AND_LOCATION,
|
||||
vmSymbols::java_lang_NoClassDefFoundError(),
|
||||
"Class name exceeds maximum length of %d: %s",
|
||||
Symbol::max_length(),
|
||||
name);
|
||||
return 0;
|
||||
}
|
||||
class_name = SymbolTable::new_symbol(name, str_len);
|
||||
}
|
||||
// Class resolution will get the class name from the .class stream if the name is null.
|
||||
TempNewSymbol class_name = name == NULL ? NULL :
|
||||
SystemDictionary::class_name_symbol(name, vmSymbols::java_lang_NoClassDefFoundError(),
|
||||
CHECK_NULL);
|
||||
|
||||
Handle protection_domain (THREAD, JNIHandles::resolve(pd));
|
||||
const char* source = is_nestmate ? host_class->external_name() : "__JVM_LookupDefineClass__";
|
||||
|
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/* @test
|
||||
* @bug 8166358
|
||||
* @summary verify that -Xcheck:jni finds a bad utf8 name for class name.
|
||||
* @library /test/lib
|
||||
* @run main/native/othervm FindClassUtf8 test
|
||||
*/
|
||||
|
||||
import jdk.test.lib.process.ProcessTools;
|
||||
import jdk.test.lib.process.OutputAnalyzer;
|
||||
|
||||
public final class FindClassUtf8 {
|
||||
|
||||
static {
|
||||
System.loadLibrary("FindClassUtf8");
|
||||
}
|
||||
|
||||
native static void nTest();
|
||||
|
||||
public static void main(String... args) throws Exception {
|
||||
if (args.length == 1) {
|
||||
// run java -Xcheck:jni FindClassUtf8 and check that the -Xcheck:jni message comes out.
|
||||
ProcessTools.executeTestJvm("-Xcheck:jni", "-XX:-CreateCoredumpOnCrash", "FindClassUtf8")
|
||||
.shouldContain("JNI class name is not a valid UTF8 string")
|
||||
.shouldNotHaveExitValue(0); // you get a core dump from -Xcheck:jni failures
|
||||
} else {
|
||||
// Run the test
|
||||
nTest();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include "jni.h"
|
||||
|
||||
JNIEXPORT void JNICALL Java_FindClassUtf8_nTest(JNIEnv* env, jclass jclazz)
|
||||
{
|
||||
const uint64_t chars = 0x5b3132315d20f818UL; // f8 is invalid utf8
|
||||
|
||||
jclass badClass = (*env)->FindClass(env, (const char*)&chars);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user