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:
Coleen Phillimore 2020-05-26 09:44:17 -04:00
parent a689a1108c
commit 6aa15ad7ab
8 changed files with 152 additions and 100 deletions

@ -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);
}