From 307497e50062314c73ac65d7d2ba09ef0f562e3e Mon Sep 17 00:00:00 2001 From: Theresa Mammarella Date: Mon, 22 Jul 2024 10:42:10 -0400 Subject: [PATCH] Enforce maximum name size during class loading Class names are stored as a UTF8 string and length is represented by two bytes Signed-off-by: Theresa Mammarella --- runtime/j9vm/j7vmi.c | 20 +++++++++++++++++++- runtime/jcl/common/jcldefine.c | 16 ++++++++++++++++ runtime/nls/j9vm/j9vm.nls | 7 +++++++ runtime/oti/j9nonbuilder.h | 4 ++++ runtime/vm/classsupport.c | 16 +++++++++++++++- 5 files changed, 61 insertions(+), 2 deletions(-) diff --git a/runtime/j9vm/j7vmi.c b/runtime/j9vm/j7vmi.c index 922e2cbbc5f..55c9a288e94 100644 --- a/runtime/j9vm/j7vmi.c +++ b/runtime/j9vm/j7vmi.c @@ -2566,14 +2566,32 @@ jvmDefineClassHelper(JNIEnv *env, jobject classLoaderObject, if (NULL != className) { j9object_t classNameObject = J9_JNI_UNWRAP_REFERENCE(className); + + /* Perform maximum length check to avoid copy in extreme cases. */ + if (J9VMJAVALANGSTRING_LENGTH(currentThread, classNameObject) > J9VM_MAX_CLASS_NAME_LENGTH) { + vmFuncs->setCurrentExceptionNLS(currentThread, + J9VMCONSTANTPOOL_JAVALANGCLASSNOTFOUNDEXCEPTION, + J9NLS_VM_CLASS_NAME_EXCEEDS_MAX_LENGTH); + goto done; + } + utf8Name = (U_8*)vmFuncs->copyStringToUTF8WithMemAlloc(currentThread, classNameObject, J9_STR_NULL_TERMINATE_RESULT, "", 0, utf8NameStackBuffer, J9VM_PACKAGE_NAME_BUFFER_LENGTH, &utf8Length); if (NULL == utf8Name) { vmFuncs->setNativeOutOfMemoryError(currentThread, 0, 0); goto done; } + if (utf8Length > J9VM_MAX_CLASS_NAME_LENGTH) { + vmFuncs->setCurrentExceptionNLS(currentThread, + J9VMCONSTANTPOOL_JAVALANGCLASSNOTFOUNDEXCEPTION, + J9NLS_VM_CLASS_NAME_EXCEEDS_MAX_LENGTH); + goto done; + } + if (CLASSNAME_INVALID == vmFuncs->verifyQualifiedName(currentThread, utf8Name, utf8Length, CLASSNAME_VALID_NON_ARRARY)) { - vmFuncs->setCurrentException(currentThread, J9VMCONSTANTPOOL_JAVALANGNOCLASSDEFFOUNDERROR, (UDATA *)*(j9object_t*)className); + vmFuncs->setCurrentException(currentThread, + J9VMCONSTANTPOOL_JAVALANGNOCLASSDEFFOUNDERROR, + (UDATA *)*(j9object_t *)className); goto done; } } diff --git a/runtime/jcl/common/jcldefine.c b/runtime/jcl/common/jcldefine.c index 7d19ddeb4dc..2e5ac042b3d 100644 --- a/runtime/jcl/common/jcldefine.c +++ b/runtime/jcl/common/jcldefine.c @@ -25,6 +25,7 @@ #include "jclprots.h" #include "j9protos.h" #include "j9jclnls.h" +#include "j9vmnls.h" jclass defineClassCommon(JNIEnv *env, jobject classLoaderObject, @@ -100,6 +101,14 @@ defineClassCommon(JNIEnv *env, jobject classLoaderObject, stringFlags |= J9_STR_XLAT; } + /* Perform maximum length check to avoid copy in extreme cases. */ + if (J9VMJAVALANGSTRING_LENGTH(currentThread, classNameObject) > J9VM_MAX_CLASS_NAME_LENGTH) { + vmFuncs->setCurrentExceptionNLS(currentThread, + J9VMCONSTANTPOOL_JAVALANGCLASSNOTFOUNDEXCEPTION, + J9NLS_VM_CLASS_NAME_EXCEEDS_MAX_LENGTH); + goto done; + } + utf8Name = (U_8*)vmFuncs->copyStringToUTF8WithMemAlloc(currentThread, classNameObject, stringFlags, "", 0, utf8NameStackBuffer, J9VM_PACKAGE_NAME_BUFFER_LENGTH, &utf8Length); if (NULL == utf8Name) { @@ -107,6 +116,13 @@ defineClassCommon(JNIEnv *env, jobject classLoaderObject, goto done; } + if (utf8Length > J9VM_MAX_CLASS_NAME_LENGTH) { + vmFuncs->setCurrentExceptionNLS(currentThread, + J9VMCONSTANTPOOL_JAVALANGCLASSNOTFOUNDEXCEPTION, + J9NLS_VM_CLASS_NAME_EXCEEDS_MAX_LENGTH); + goto done; + } + if (validateName && (CLASSNAME_INVALID == vmFuncs->verifyQualifiedName(currentThread, utf8Name, utf8Length, CLASSNAME_VALID_NON_ARRARY))) { /* We don't yet know if the class being defined is exempt. Setting this option tells * defineClassCommon() to fail if it discovers that the class is not exempt. That failure diff --git a/runtime/nls/j9vm/j9vm.nls b/runtime/nls/j9vm/j9vm.nls index 7abf301b724..688208e809a 100644 --- a/runtime/nls/j9vm/j9vm.nls +++ b/runtime/nls/j9vm/j9vm.nls @@ -2431,3 +2431,10 @@ J9NLS_VM_CRIU_DISCLAIM_CLASSES_INVALID_PAGE_SIZE.explanation=-XX:+DisclaimClasse J9NLS_VM_CRIU_DISCLAIM_CLASSES_INVALID_PAGE_SIZE.system_action=The JVM will print a warning. J9NLS_VM_CRIU_DISCLAIM_CLASSES_INVALID_PAGE_SIZE.user_response=Set page size to 4K or use -XX:-DisclaimClasses. # END NON-TRANSLATABLE + +J9NLS_VM_CLASS_NAME_EXCEEDS_MAX_LENGTH=Class name exceeds maximum length +# START NON-TRANSLATABLE +J9NLS_VM_CLASS_NAME_EXCEEDS_MAX_LENGTH.explanation=Class name cannot be longer than 65535 characters. +J9NLS_VM_CLASS_NAME_EXCEEDS_MAX_LENGTH.system_action=The JVM will throw a ClassNotFoundException. +J9NLS_VM_CLASS_NAME_EXCEEDS_MAX_LENGTH.user_response=Shorten the class name. +# END NON-TRANSLATABLE diff --git a/runtime/oti/j9nonbuilder.h b/runtime/oti/j9nonbuilder.h index 199c2f3ecb4..08f27290113 100644 --- a/runtime/oti/j9nonbuilder.h +++ b/runtime/oti/j9nonbuilder.h @@ -36,6 +36,10 @@ #include "j9javaaccessflags.h" #define J9VM_MAX_HIDDEN_FIELDS_PER_CLASS 8 +/* Class names are stored in the VM as CONSTANT_Utf8_info which stores + * length in two bytes. + */ +#define J9VM_MAX_CLASS_NAME_LENGTH 0xFFFF #define J9VM_DLT_HISTORY_SIZE 16 #define J9VM_OBJECT_MONITOR_CACHE_SIZE 32 diff --git a/runtime/vm/classsupport.c b/runtime/vm/classsupport.c index 1f99a502ccc..82ae71455d6 100644 --- a/runtime/vm/classsupport.c +++ b/runtime/vm/classsupport.c @@ -316,6 +316,14 @@ internalFindClassString(J9VMThread* currentThread, j9object_t moduleName, j9obje stringFlags |= J9_STR_XLAT; } + /* Perform maximum length check to avoid copy in extreme cases. */ + if (J9VMJAVALANGSTRING_LENGTH(currentThread, className) > J9VM_MAX_CLASS_NAME_LENGTH) { + setCurrentExceptionNLS(currentThread, + J9VMCONSTANTPOOL_JAVALANGCLASSNOTFOUNDEXCEPTION, + J9NLS_VM_CLASS_NAME_EXCEEDS_MAX_LENGTH); + return NULL; + } + utf8Name = (U_8*)copyStringToUTF8WithMemAlloc(currentThread, className, stringFlags, "", 0, (char *)localBuf, J9VM_PACKAGE_NAME_BUFFER_LENGTH, &utf8Length); if (NULL == utf8Name) { /* Throw out-of-memory */ @@ -324,7 +332,13 @@ internalFindClassString(J9VMThread* currentThread, j9object_t moduleName, j9obje } /* Make sure the name is legal */ - if ((CLASSNAME_INVALID == allowedBitsForClassName) + if (utf8Length > J9VM_MAX_CLASS_NAME_LENGTH) { + if (CLASSNAME_INVALID != allowedBitsForClassName) { + setCurrentExceptionNLS(currentThread, + J9VMCONSTANTPOOL_JAVALANGCLASSNOTFOUNDEXCEPTION, + J9NLS_VM_CLASS_NAME_EXCEEDS_MAX_LENGTH); + } + } else if ((CLASSNAME_INVALID == allowedBitsForClassName) || (CLASSNAME_INVALID != verifyQualifiedName(currentThread, utf8Name, utf8Length, allowedBitsForClassName)) ) { if (NULL != moduleName) {