From 72a49005ee8c4aeb6dcf3eff4c56576a2b4d0081 Mon Sep 17 00:00:00 2001 From: David Holmes Date: Wed, 28 Aug 2024 21:16:18 +0000 Subject: [PATCH] 8338888: SystemDictionary::class_name_symbol has incorrect length check Reviewed-by: stuefe, kbarrett, coleenp --- .../share/classfile/systemDictionary.cpp | 29 ++++++++--- .../NoClassDefFoundErrorTest.java | 48 +++++++++++++++---- .../libNoClassDefFoundErrorTest.c | 37 +++++++++++++- 3 files changed, 98 insertions(+), 16 deletions(-) diff --git a/src/hotspot/share/classfile/systemDictionary.cpp b/src/hotspot/share/classfile/systemDictionary.cpp index 7f9e6430cc6..e32d124013d 100644 --- a/src/hotspot/share/classfile/systemDictionary.cpp +++ b/src/hotspot/share/classfile/systemDictionary.cpp @@ -261,17 +261,32 @@ Symbol* SystemDictionary::class_name_symbol(const char* name, Symbol* exception, if (name == nullptr) { THROW_MSG_NULL(exception, "No class name given"); } - if ((int)strlen(name) > Symbol::max_length()) { + size_t name_len = strlen(name); + if (name_len > static_cast(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); + // into the constant pool. If necessary report an abridged name + // in the exception message. + if (name_len > static_cast(MaxStringPrintSize)) { + Exceptions::fthrow(THREAD_AND_LOCATION, exception, + "Class name exceeds maximum length of %d: %.*s ... (%zu characters omitted) ... %.*s", + Symbol::max_length(), + MaxStringPrintSize / 2, + name, + name_len - 2 * (MaxStringPrintSize / 2), // allows for odd value + MaxStringPrintSize / 2, + name + name_len - MaxStringPrintSize / 2); + } + else { + Exceptions::fthrow(THREAD_AND_LOCATION, exception, + "Class name exceeds maximum length of %d: %s", + Symbol::max_length(), + name); + } return nullptr; } // 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), + assert(UTF8::is_legal_utf8((const unsigned char*)name, + static_cast(name_len), false), "Class name is not a valid utf8 string."); // Make a new symbol for the class name. diff --git a/test/hotspot/jtreg/runtime/exceptionMsgs/NoClassDefFoundError/NoClassDefFoundErrorTest.java b/test/hotspot/jtreg/runtime/exceptionMsgs/NoClassDefFoundError/NoClassDefFoundErrorTest.java index d369f77f324..b03d6274850 100644 --- a/test/hotspot/jtreg/runtime/exceptionMsgs/NoClassDefFoundError/NoClassDefFoundErrorTest.java +++ b/test/hotspot/jtreg/runtime/exceptionMsgs/NoClassDefFoundError/NoClassDefFoundErrorTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2024, 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 @@ -23,12 +23,12 @@ /* * @test - * @bug 8056900 + * @bug 8056900 8338888 * @summary Verifies message returned with NoClassDefFoundError exception. * @library /test/lib * @modules java.base/jdk.internal.misc * java.compiler - * @run main/native NoClassDefFoundErrorTest + * @run main/native/othervm -Xlog:exceptions=info NoClassDefFoundErrorTest */ import jdk.test.lib.compiler.InMemoryJavaCompiler; @@ -36,8 +36,14 @@ import jdk.internal.misc.Unsafe; public class NoClassDefFoundErrorTest { + // Use the specified name static native void callDefineClass(String className); static native void callFindClass(String className); + // Use a name longer than a Java string - returns false + // if native allocation failed. + static native boolean tryCallDefineClass(); + static native boolean tryCallFindClass(); + static { System.loadLibrary("NoClassDefFoundErrorTest"); } @@ -54,7 +60,7 @@ public class NoClassDefFoundErrorTest { tooBigClassName = tooBigClassName.append(tooBigClassName); } - // Test JVM_DefineClass() with long name. + System.out.println("Test JVM_DefineClass() with long name"); try { unsafe.defineClass(tooBigClassName.toString(), klassbuf, 4, klassbuf.length - 4, null, null); throw new RuntimeException("defineClass did not throw expected NoClassDefFoundError"); @@ -64,7 +70,7 @@ public class NoClassDefFoundErrorTest { } } - // Test JNI_DefineClass() with long name. + System.out.println("Test JNI_DefineClass() with long name"); try { callDefineClass(tooBigClassName.toString()); throw new RuntimeException("DefineClass did not throw expected NoClassDefFoundError"); @@ -74,17 +80,17 @@ public class NoClassDefFoundErrorTest { } } - // Test JNI_FindClass() with long name. + System.out.println("Test JNI_FindClass() with long name"); try { callFindClass(tooBigClassName.toString()); - throw new RuntimeException("DefineClass did not throw expected NoClassDefFoundError"); + throw new RuntimeException("FindClass did not throw expected NoClassDefFoundError"); } catch (NoClassDefFoundError e) { if (!e.getMessage().contains("Class name exceeds maximum length of ")) { throw new RuntimeException("Wrong NoClassDefFoundError: " + e.getMessage()); } } - // Test JNI_FindClass() with null name. + System.out.println("Test JNI_FindClass() with null name"); try { callFindClass(null); throw new RuntimeException("FindClass did not throw expected NoClassDefFoundError"); @@ -93,5 +99,31 @@ public class NoClassDefFoundErrorTest { throw new RuntimeException("Wrong NoClassDefFoundError: " + e.getMessage()); } } + + System.out.println("Test JNI_DefineClass() with giant name"); + try { + if (tryCallDefineClass()) { + throw new RuntimeException("DefineClass did not throw expected NoClassDefFoundError"); + } else { + System.out.println("Test skipped due to native allocation failure"); + } + } catch (NoClassDefFoundError e) { + if (!e.getMessage().contains("Class name exceeds maximum length of ")) { + throw new RuntimeException("Wrong NoClassDefFoundError: " + e.getMessage()); + } + } + + System.out.println("Test JNI_FindClass() with giant name"); + try { + if (tryCallFindClass()) { + throw new RuntimeException("FindClass did not throw expected NoClassDefFoundError"); + } else { + System.out.println("Test skipped due to native allocation failure"); + } + } catch (NoClassDefFoundError e) { + if (!e.getMessage().contains("Class name exceeds maximum length of ")) { + throw new RuntimeException("Wrong NoClassDefFoundError: " + e.getMessage()); + } + } } } diff --git a/test/hotspot/jtreg/runtime/exceptionMsgs/NoClassDefFoundError/libNoClassDefFoundErrorTest.c b/test/hotspot/jtreg/runtime/exceptionMsgs/NoClassDefFoundError/libNoClassDefFoundErrorTest.c index 607d2541a89..023f299a5d4 100644 --- a/test/hotspot/jtreg/runtime/exceptionMsgs/NoClassDefFoundError/libNoClassDefFoundErrorTest.c +++ b/test/hotspot/jtreg/runtime/exceptionMsgs/NoClassDefFoundError/libNoClassDefFoundErrorTest.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2024, 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 @@ -22,6 +22,10 @@ */ #include +#include +#include +#include + JNIEXPORT void JNICALL Java_NoClassDefFoundErrorTest_callDefineClass(JNIEnv *env, jclass klass, jstring className) { @@ -42,3 +46,34 @@ Java_NoClassDefFoundErrorTest_callFindClass(JNIEnv *env, jclass klass, jstring c } +static char* giant_string() { + size_t len = ((size_t)INT_MAX) + 3; + char* c_name = malloc(len * sizeof(char)); + if (c_name != NULL) { + memset(c_name, 'Y', len - 1); + c_name[len - 1] = '\0'; + } + return c_name; +} + +JNIEXPORT jboolean JNICALL +Java_NoClassDefFoundErrorTest_tryCallDefineClass(JNIEnv *env, jclass klass) { + char* c_name = giant_string(); + if (c_name != NULL) { + (*env)->DefineClass(env, c_name, NULL, NULL, 0); + free(c_name); + return JNI_TRUE; + } + return JNI_FALSE; +} + +JNIEXPORT jboolean JNICALL +Java_NoClassDefFoundErrorTest_tryCallFindClass(JNIEnv *env, jclass klass) { + char* c_name = giant_string(); + if (c_name != NULL) { + jclass cls = (*env)->FindClass(env, c_name); + free(c_name); + return JNI_TRUE; + } + return JNI_FALSE; +}