From 619c7daaa230fceff4fdbf45766f723c289fa9ca Mon Sep 17 00:00:00 2001 From: Babneet Singh Date: Tue, 27 Aug 2024 16:09:06 -0400 Subject: [PATCH] Fix overflow issues Truncate length while evaluating length of a UTF-8 string - On 32-bit systems, the length is truncated to UDATA_MAX. - For JNI getStringUTFLength, the length is truncated to INT32_MAX. Add restrictions while copying a string to UTF-8 to avoid overflow - The length is restricted to UDATA_MAX to accommodate memory allocation functions, which take a UDATA as the input parameter for length. - The data is stopped from being written to the UTF-8 buffer if its length will be exceeded. Signed-off-by: Babneet Singh --- runtime/j9vm/java11vmi.c | 32 +++- .../java_lang_invoke_MethodHandleNatives.cpp | 80 ++++++--- runtime/oti/j9nonbuilder.h | 5 +- runtime/oti/vm_api.h | 31 +++- runtime/vm/intfunc.c | 1 + runtime/vm/jnimisc.cpp | 17 +- runtime/vm/stringhelpers.cpp | 166 +++++++++++++++--- 7 files changed, 255 insertions(+), 77 deletions(-) diff --git a/runtime/j9vm/java11vmi.c b/runtime/j9vm/java11vmi.c index 9c7ee418cf1..e5ca7eb63d5 100644 --- a/runtime/j9vm/java11vmi.c +++ b/runtime/j9vm/java11vmi.c @@ -789,8 +789,12 @@ JVM_DefineModule(JNIEnv * env, jobject module, jboolean isOpen, jstring version, j9array_t array = (j9array_t)J9_JNI_UNWRAP_REFERENCE(packageArray); j9object_t stringObject = J9JAVAARRAYOFOBJECT_LOAD(currentThread, array, pkgIndex); if (NULL != stringObject) { - UDATA utfLength = vmFuncs->getStringUTF8Length(currentThread, stringObject) + 1; - char *packageName = (char*)j9mem_allocate_memory(utfLength, OMRMEM_CATEGORY_VM); + UDATA utfLength = vmFuncs->getStringUTF8Length(currentThread, stringObject); + char *packageName = NULL; + if (utfLength < UDATA_MAX) { + utfLength += 1; + packageName = (char *)j9mem_allocate_memory(utfLength, OMRMEM_CATEGORY_VM); + } if (NULL == packageName) { oom = TRUE; break; @@ -992,8 +996,12 @@ JVM_AddModuleExports(JNIEnv * env, jobject fromModule, const char *package, jobj #if JAVA_SPEC_VERSION >= 15 if (NULL != packageObj) { j9object_t stringObject = J9_JNI_UNWRAP_REFERENCE(packageObj); - UDATA utfLength = vmFuncs->getStringUTF8Length(currentThread, stringObject) + 1; - char* packageName = (char *)j9mem_allocate_memory(utfLength, OMRMEM_CATEGORY_VM); + UDATA utfLength = vmFuncs->getStringUTF8Length(currentThread, stringObject); + char *packageName = NULL; + if (utfLength < UDATA_MAX) { + utfLength += 1; + packageName = (char *)j9mem_allocate_memory(utfLength, OMRMEM_CATEGORY_VM); + } if (NULL == packageName) { vmFuncs->setNativeOutOfMemoryError(currentThread, 0, 0); goto done; @@ -1066,8 +1074,12 @@ JVM_AddModuleExportsToAll(JNIEnv * env, jobject fromModule, const char *package) #if JAVA_SPEC_VERSION >= 15 if (NULL != packageObj) { j9object_t stringObject = J9_JNI_UNWRAP_REFERENCE(packageObj); - UDATA utfLength = vmFuncs->getStringUTF8Length(currentThread, stringObject) + 1; - char* packageName = (char *)j9mem_allocate_memory(utfLength, OMRMEM_CATEGORY_VM); + UDATA utfLength = vmFuncs->getStringUTF8Length(currentThread, stringObject); + char *packageName = NULL; + if (utfLength < UDATA_MAX) { + utfLength += 1; + packageName = (char *)j9mem_allocate_memory(utfLength, OMRMEM_CATEGORY_VM); + } if (NULL == packageName) { vmFuncs->setNativeOutOfMemoryError(currentThread, 0, 0); goto done; @@ -1306,8 +1318,12 @@ JVM_AddModuleExportsToAllUnnamed(JNIEnv * env, jobject fromModule, const char *p #if JAVA_SPEC_VERSION >= 15 if (NULL != packageObj) { j9object_t stringObject = J9_JNI_UNWRAP_REFERENCE(packageObj); - UDATA utfLength = vmFuncs->getStringUTF8Length(currentThread, stringObject) + 1; - char* packageName = (char *)j9mem_allocate_memory(utfLength, OMRMEM_CATEGORY_VM); + UDATA utfLength = vmFuncs->getStringUTF8Length(currentThread, stringObject); + char *packageName = NULL; + if (utfLength < UDATA_MAX) { + utfLength += 1; + packageName = (char *)j9mem_allocate_memory(utfLength, OMRMEM_CATEGORY_VM); + } if (NULL == packageName) { vmFuncs->setNativeOutOfMemoryError(currentThread, 0, 0); goto done; diff --git a/runtime/jcl/common/java_lang_invoke_MethodHandleNatives.cpp b/runtime/jcl/common/java_lang_invoke_MethodHandleNatives.cpp index 7f694aa5d3b..f942e67534a 100644 --- a/runtime/jcl/common/java_lang_invoke_MethodHandleNatives.cpp +++ b/runtime/jcl/common/java_lang_invoke_MethodHandleNatives.cpp @@ -410,7 +410,10 @@ getClassSignatureLength(J9VMThread *currentThread, J9Class *clazz) j9object_t sigString = J9VMJAVALANGCLASS_CLASSNAMESTRING(currentThread, J9VM_J9CLASS_TO_HEAPCLASS(clazz)); if (NULL != sigString) { /* +2 so that we can fit 'L' and ';' around the class name. */ - signatureLength = vm->internalVMFunctions->getStringUTF8Length(currentThread, sigString) + 2; + signatureLength = vm->internalVMFunctions->getStringUTF8Length(currentThread, sigString); + if (signatureLength <= (UDATA_MAX - 2)) { + signatureLength += 2; + } } else { J9Class *myClass = clazz; UDATA numDims = 0; @@ -470,24 +473,27 @@ getClassSignatureInout(J9VMThread *currentThread, J9Class *clazz, LocalJ9UTF8Buf j9object_t sigString = J9VMJAVALANGCLASS_CLASSNAMESTRING(currentThread, J9VM_J9CLASS_TO_HEAPCLASS(clazz)); if (NULL != sigString) { /* +3 so that we can fit 'L' and ';' around the class name and add null-terminator. */ - UDATA utfLength = vm->internalVMFunctions->getStringUTF8Length(currentThread, sigString) + 3; - if (utfLength <= stringBuffer->remaining()) { - if (J9ROMCLASS_IS_ARRAY(clazz->romClass)) { - vm->internalVMFunctions->copyStringToUTF8Helper( - currentThread, sigString, J9_STR_XLAT, 0, J9VMJAVALANGSTRING_LENGTH(currentThread, sigString), - stringBuffer->cursor, utfLength - 3); - /* Adjust cursor to account for the call to copyStringToUTF8Helper. */ - stringBuffer->advanceN(utfLength - 3); - } else { - stringBuffer->putCharAtCursor('L'); - vm->internalVMFunctions->copyStringToUTF8Helper( - currentThread, sigString, J9_STR_XLAT, 0, J9VMJAVALANGSTRING_LENGTH(currentThread, sigString), - stringBuffer->cursor, utfLength - 3); - /* Adjust cursor to account for the call to copyStringToUTF8Helper. */ - stringBuffer->advanceN(utfLength - 3); - stringBuffer->putCharAtCursor(';'); + UDATA utfLength = vm->internalVMFunctions->getStringUTF8Length(currentThread, sigString); + if (utfLength <= (UDATA_MAX - 3)) { + utfLength += 3; + if (utfLength <= stringBuffer->remaining()) { + if (J9ROMCLASS_IS_ARRAY(clazz->romClass)) { + vm->internalVMFunctions->copyStringToUTF8Helper( + currentThread, sigString, J9_STR_XLAT, 0, J9VMJAVALANGSTRING_LENGTH(currentThread, sigString), + stringBuffer->cursor, utfLength - 3); + /* Adjust cursor to account for the call to copyStringToUTF8Helper. */ + stringBuffer->advanceN(utfLength - 3); + } else { + stringBuffer->putCharAtCursor('L'); + vm->internalVMFunctions->copyStringToUTF8Helper( + currentThread, sigString, J9_STR_XLAT, 0, J9VMJAVALANGSTRING_LENGTH(currentThread, sigString), + stringBuffer->cursor, utfLength - 3); + /* Adjust cursor to account for the call to copyStringToUTF8Helper. */ + stringBuffer->advanceN(utfLength - 3); + stringBuffer->putCharAtCursor(';'); + } + result = true; } - result = true; } } else { J9Class *myClass = clazz; @@ -557,20 +563,34 @@ getJ9UTF8SignatureFromMethodTypeWithMemAlloc(J9VMThread *currentThread, j9object j9object_t ptypes = J9VMJAVALANGINVOKEMETHODTYPE_PTYPES(currentThread, typeObject); U_32 numArgs = J9INDEXABLEOBJECT_SIZE(currentThread, ptypes); UDATA signatureLength = 2; /* space for '(', ')' */ + UDATA tempSignatureLength = 0; + UDATA signatureUtf8Size = 0; + J9UTF8 *result = NULL; + j9object_t rtype = NULL; + J9Class *rclass = NULL; PORT_ACCESS_FROM_JAVAVM(vm); /* Calculate total signature length, including all ptypes and rtype. */ for (U_32 i = 0; i < numArgs; i++) { j9object_t pObject = J9JAVAARRAYOFOBJECT_LOAD(currentThread, ptypes, i); J9Class *pclass = J9VM_J9CLASS_FROM_HEAPCLASS(currentThread, pObject); - signatureLength += getClassSignatureLength(currentThread, pclass); + tempSignatureLength = getClassSignatureLength(currentThread, pclass); + if (signatureLength > (J9UTF8_MAX_LENGTH - tempSignatureLength)) { + goto done; + } + signatureLength += tempSignatureLength; } - j9object_t rtype = J9VMJAVALANGINVOKEMETHODTYPE_RTYPE(currentThread, typeObject); - J9Class *rclass = J9VM_J9CLASS_FROM_HEAPCLASS(currentThread, rtype); - signatureLength += getClassSignatureLength(currentThread, rclass); + rtype = J9VMJAVALANGINVOKEMETHODTYPE_RTYPE(currentThread, typeObject); + rclass = J9VM_J9CLASS_FROM_HEAPCLASS(currentThread, rtype); + tempSignatureLength = getClassSignatureLength(currentThread, rclass); + if (signatureLength > (J9UTF8_MAX_LENGTH - tempSignatureLength)) { + goto done; + } + signatureLength += tempSignatureLength; + + signatureUtf8Size = signatureLength + sizeof(J9UTF8) + 1; /* +1 for a null-terminator */ + result = reinterpret_cast(j9mem_allocate_memory(signatureUtf8Size, OMRMEM_CATEGORY_VM)); - UDATA signatureUtf8Size = signatureLength + sizeof(J9UTF8) + 1; /* +1 for a null-terminator */ - J9UTF8 *result = reinterpret_cast(j9mem_allocate_memory(signatureUtf8Size, OMRMEM_CATEGORY_VM)); if (NULL != result) { LocalJ9UTF8Buffer stringBuffer(result, signatureUtf8Size); @@ -588,6 +608,7 @@ getJ9UTF8SignatureFromMethodTypeWithMemAlloc(J9VMThread *currentThread, j9object stringBuffer.commitLength(); } +done: return result; } @@ -1036,12 +1057,21 @@ Java_java_lang_invoke_MethodHandleNatives_resolve( } else { LocalJ9UTF8Buffer stringBuffer(reinterpret_cast(signatureBuffer), sizeof(signatureBuffer)); signature = getJ9UTF8SignatureFromMethodType(currentThread, typeObject, &stringBuffer); + if (NULL == signature) { + vmFuncs->setCurrentExceptionUTF(currentThread, J9VMCONSTANTPOOL_JAVALANGINTERNALERROR, NULL); + goto done; + } } } else if (J9VMJAVALANGSTRING_OR_NULL(vm) == typeClass) { signature = vmFuncs->copyStringToJ9UTF8WithMemAlloc(currentThread, typeObject, J9_STR_XLAT, "", 0, signatureBuffer, sizeof(signatureBuffer)); } else if (J9VMJAVALANGCLASS(vm) == typeClass) { J9Class *rclass = J9VM_J9CLASS_FROM_HEAPCLASS(currentThread, typeObject); - UDATA signatureLength = getClassSignatureLength(currentThread, rclass) + sizeof(J9UTF8) + 1 /* null-terminator */; + UDATA signatureLength = getClassSignatureLength(currentThread, rclass); + if (signatureLength > J9UTF8_MAX_LENGTH) { + vmFuncs->setCurrentExceptionUTF(currentThread, J9VMCONSTANTPOOL_JAVALANGINTERNALERROR, NULL); + goto done; + } + signatureLength += sizeof(J9UTF8) + 1 /* null-terminator */; LocalJ9UTF8Buffer stringBuffer; if (signatureLength <= sizeof(signatureBuffer)) { stringBuffer = LocalJ9UTF8Buffer(reinterpret_cast(signatureBuffer), sizeof(signatureBuffer)); diff --git a/runtime/oti/j9nonbuilder.h b/runtime/oti/j9nonbuilder.h index 1667d1a2c98..d3adab8003e 100644 --- a/runtime/oti/j9nonbuilder.h +++ b/runtime/oti/j9nonbuilder.h @@ -3599,6 +3599,8 @@ typedef struct J9UTF8 { #pragma warning(pop) #endif /* defined(_MSC_VER) */ +#define J9UTF8_MAX_LENGTH U_16_MAX + typedef struct J9ROMClass { U_32 romSize; U_32 singleScalarStaticCount; @@ -4815,7 +4817,8 @@ typedef struct J9InternalVMFunctions { struct J9Class* ( *internalFindKnownClass)(struct J9VMThread *currentThread, UDATA index, UDATA flags) ; struct J9Class* ( *resolveKnownClass)(struct J9JavaVM * vm, UDATA index) ; UDATA ( *computeHashForUTF8)(const U_8 * string, UDATA size) ; - IDATA ( *getStringUTF8Length)(struct J9VMThread *vmThread, j9object_t string) ; + UDATA ( *getStringUTF8Length)(struct J9VMThread *vmThread, j9object_t string) ; + U_64 ( *getStringUTF8LengthTruncated)(struct J9VMThread *vmThread, j9object_t string, U_64 maxLength) ; void ( *acquireExclusiveVMAccess)(struct J9VMThread * vmThread) ; void ( *releaseExclusiveVMAccess)(struct J9VMThread * vmThread) ; void ( *internalReleaseVMAccess)(struct J9VMThread * currentThread) ; diff --git a/runtime/oti/vm_api.h b/runtime/oti/vm_api.h index 6000df00ebf..d797e270da0 100644 --- a/runtime/oti/vm_api.h +++ b/runtime/oti/vm_api.h @@ -3583,13 +3583,30 @@ copyStringToUTF8Helper(J9VMThread *vmThread, j9object_t string, UDATA stringFlag /** -* @brief -* @param *vm -* @param *string -* @return IDATA -*/ -IDATA -getStringUTF8Length(J9VMThread *vmThread,j9object_t string); + * @brief Find the length of the string object when it is converted to UTF-8. + * + * Note: On 32-bit platforms, the length may be truncated. + * + * @param vm a pointer to J9JavaVM + * @param string a string object + * + * @return the length of the string in UTF-8 + */ +UDATA +getStringUTF8Length(J9VMThread *vmThread, j9object_t string); + +/** + * @brief Find the length of the string object when it is converted to UTF-8, but truncate it + * using maxLength as the upper bound. + * + * @param vm a pointer to J9JavaVM + * @param string a string object + * @param maxLength the upper bound of the length used for truncation + * + * @return the length of the string in UTF-8 + */ +U_64 +getStringUTF8LengthTruncated(J9VMThread *vmThread, j9object_t string, U_64 maxLength); /** diff --git a/runtime/vm/intfunc.c b/runtime/vm/intfunc.c index 6a86d58758c..cd9e54d66d3 100644 --- a/runtime/vm/intfunc.c +++ b/runtime/vm/intfunc.c @@ -75,6 +75,7 @@ J9InternalVMFunctions J9InternalFunctions = { resolveKnownClass, computeHashForUTF8, getStringUTF8Length, + getStringUTF8LengthTruncated, acquireExclusiveVMAccess, releaseExclusiveVMAccess, internalReleaseVMAccess, diff --git a/runtime/vm/jnimisc.cpp b/runtime/vm/jnimisc.cpp index 64e48901fd5..e688729d1f3 100644 --- a/runtime/vm/jnimisc.cpp +++ b/runtime/vm/jnimisc.cpp @@ -829,7 +829,7 @@ getStringUTFLength(JNIEnv *env, jstring string) VM_VMAccess::inlineEnterVMFromJNI(currentThread); j9object_t stringObject = J9_JNI_UNWRAP_REFERENCE(string); - UDATA utfLength = getStringUTF8Length(currentThread, stringObject); + U_64 utfLength = getStringUTF8LengthTruncated(currentThread, stringObject, INT32_MAX); VM_VMAccess::inlineExitVMToJNI(currentThread); return (jsize)utfLength; } @@ -840,14 +840,17 @@ getStringUTFCharsImpl(JNIEnv *env, jstring string, jboolean *isCopy, jboolean en J9VMThread *currentThread = (J9VMThread*)env; VM_VMAccess::inlineEnterVMFromJNI(currentThread); j9object_t stringObject = J9_JNI_UNWRAP_REFERENCE(string); - /* Add 1 for null terminator */ - UDATA utfLength = getStringUTF8Length(currentThread, stringObject) + 1; + UDATA utfLength = getStringUTF8Length(currentThread, stringObject); U_8 *utfChars = NULL; - if (ensureMem32) { - utfChars = (U_8*)jniArrayAllocateMemory32FromThread(currentThread, utfLength); - } else { - utfChars = (U_8*)jniArrayAllocateMemoryFromThread(currentThread, utfLength); + if (utfLength < UDATA_MAX) { + /* Add 1 for a null terminator. */ + utfLength += 1; + if (ensureMem32) { + utfChars = (U_8 *)jniArrayAllocateMemory32FromThread(currentThread, utfLength); + } else { + utfChars = (U_8 *)jniArrayAllocateMemoryFromThread(currentThread, utfLength); + } } if (NULL == utfChars) { diff --git a/runtime/vm/stringhelpers.cpp b/runtime/vm/stringhelpers.cpp index 0bbd04276e5..07471df634a 100644 --- a/runtime/vm/stringhelpers.cpp +++ b/runtime/vm/stringhelpers.cpp @@ -228,16 +228,31 @@ copyStringToUTF8Helper(J9VMThread *vmThread, j9object_t string, UDATA stringFlag /* Manually version J9_STR_XLAT flag checking from the loop for performance as the compiler does not do it */ if ((stringFlags & J9_STR_XLAT) == 0) { for (UDATA i = stringOffset; i < stringOffset + stringLength; i++) { - data += VM_VMHelpers::encodeUTF8CharI8(J9JAVAARRAYOFBYTE_LOAD(vmThread, stringValue, i), data); + I_8 unicode = J9JAVAARRAYOFBYTE_LOAD(vmThread, stringValue, i); + UDATA encodedLength = VM_VMHelpers::encodedUTF8LengthI8(unicode); + + /* Stop writing to utf8Data if utf8DataLength will be exceeded. */ + if (encodedLength > (utf8DataLength - (UDATA)(data - utf8Data))) { + break; + } + + VM_VMHelpers::encodeUTF8CharI8(unicode, data); + data += encodedLength; } } else { for (UDATA i = stringOffset; i < stringOffset + stringLength; i++) { - UDATA encodedLength = VM_VMHelpers::encodeUTF8CharI8(J9JAVAARRAYOFBYTE_LOAD(vmThread, stringValue, i), data); + I_8 unicode = J9JAVAARRAYOFBYTE_LOAD(vmThread, stringValue, i); + UDATA encodedLength = VM_VMHelpers::encodedUTF8LengthI8(unicode); + + /* Stop writing to utf8Data if utf8DataLength will be exceeded. */ + if (encodedLength > (utf8DataLength - (UDATA)(data - utf8Data))) { + break; + } + VM_VMHelpers::encodeUTF8CharI8(unicode, data); if ('.' == *data) { *data = '/'; } - data += encodedLength; } } @@ -245,16 +260,31 @@ copyStringToUTF8Helper(J9VMThread *vmThread, j9object_t string, UDATA stringFlag /* Manually version J9_STR_XLAT flag checking from the loop for performance as the compiler does not do it */ if ((stringFlags & J9_STR_XLAT) == 0) { for (UDATA i = stringOffset; i < stringOffset + stringLength; i++) { - data += VM_VMHelpers::encodeUTF8Char(J9JAVAARRAYOFCHAR_LOAD(vmThread, stringValue, i), data); + U_16 unicode = J9JAVAARRAYOFCHAR_LOAD(vmThread, stringValue, i); + UDATA encodedLength = VM_VMHelpers::encodedUTF8Length(unicode); + + /* Stop writing to utf8Data if utf8DataLength will be exceeded. */ + if (encodedLength > (utf8DataLength - (UDATA)(data - utf8Data))) { + break; + } + + VM_VMHelpers::encodeUTF8Char(unicode, data); + data += encodedLength; } } else { for (UDATA i = stringOffset; i < stringOffset + stringLength; i++) { - UDATA encodedLength = VM_VMHelpers::encodeUTF8Char(J9JAVAARRAYOFCHAR_LOAD(vmThread, stringValue, i), data); + U_16 unicode = J9JAVAARRAYOFCHAR_LOAD(vmThread, stringValue, i); + UDATA encodedLength = VM_VMHelpers::encodedUTF8Length(unicode); + /* Stop writing to utf8Data if utf8DataLength will be exceeded. */ + if (encodedLength > (utf8DataLength - (UDATA)(data - utf8Data))) { + break; + } + + VM_VMHelpers::encodeUTF8Char(unicode, data); if ('.' == *data) { *data = '/'; } - data += encodedLength; } } @@ -263,11 +293,10 @@ copyStringToUTF8Helper(J9VMThread *vmThread, j9object_t string, UDATA stringFlag UDATA returnLength = (UDATA)(data - utf8Data); if ((stringFlags & J9_STR_NULL_TERMINATE_RESULT) != 0) { + Assert_VM_true(returnLength < utf8DataLength); *data = '\0'; - - Assert_VM_true(utf8DataLength >= returnLength + 1); } else { - Assert_VM_true(utf8DataLength >= returnLength); + Assert_VM_true(returnLength <= utf8DataLength); } return returnLength; @@ -279,20 +308,32 @@ copyStringToUTF8WithMemAlloc(J9VMThread *vmThread, j9object_t string, UDATA stri Assert_VM_notNull(prependStr); Assert_VM_notNull(string); - U_8* result = NULL; + U_8 *result = NULL; UDATA stringLength = J9VMJAVALANGSTRING_LENGTH(vmThread, string); - UDATA length = prependStrLength + (stringLength * 3); + U_64 length = prependStrLength + ((U_64)stringLength * 3); if (J9_ARE_ALL_BITS_SET(stringFlags, J9_STR_NULL_TERMINATE_RESULT)) { ++length; } +#if UDATA_MAX < (3 * INT32_MAX) + /* Memory allocation functions take a UDATA as the input parameter for length. + * On 32-bit systems, the length needs to be restricted to UDATA_MAX since the + * maximum UTF-8 length of a Java string can be 3 * Integer.MAX_VALUE. Otherwise, + * there will be overflow. On 64-bit platforms, this is not an issue since UDATA + * and U_64 are the same size. + */ + if (length > UDATA_MAX) { + length = UDATA_MAX; + } +#endif /* UDATA_MAX < (3 * INT32_MAX) */ + PORT_ACCESS_FROM_VMC(vmThread); if (length > bufferLength) { - result = (U_8*)j9mem_allocate_memory(length, OMRMEM_CATEGORY_VM); + result = (U_8 *)j9mem_allocate_memory((UDATA)length, OMRMEM_CATEGORY_VM); } else { - result = (U_8*)buffer; + result = (U_8 *)buffer; } if (NULL != result) { @@ -302,14 +343,17 @@ copyStringToUTF8WithMemAlloc(J9VMThread *vmThread, j9object_t string, UDATA stri memcpy(result, prependStr, prependStrLength); } - computedUtf8Length = copyStringToUTF8Helper(vmThread, string, stringFlags, 0, stringLength, result + prependStrLength, length - prependStrLength); + computedUtf8Length = copyStringToUTF8Helper( + vmThread, string, stringFlags, 0, + stringLength, result + prependStrLength, + (UDATA)(length - prependStrLength)); if (NULL != utf8Length) { *utf8Length = computedUtf8Length + prependStrLength; } } - return (char*)result; + return (char *)result; } J9UTF8* @@ -318,20 +362,34 @@ copyStringToJ9UTF8WithMemAlloc(J9VMThread *vmThread, j9object_t string, UDATA st Assert_VM_notNull(prependStr); Assert_VM_notNull(string); - U_8* result = NULL; + U_8 *result = NULL; UDATA stringLength = J9VMJAVALANGSTRING_LENGTH(vmThread, string); - UDATA length = sizeof(J9UTF8) + prependStrLength + (stringLength * 3); + U_64 length = sizeof(J9UTF8) + prependStrLength + ((U_64)stringLength * 3); if (J9_ARE_ALL_BITS_SET(stringFlags, J9_STR_NULL_TERMINATE_RESULT)) { ++length; } +#if UDATA_MAX < (3 * INT32_MAX) + /* Memory allocation functions take a UDATA as the input parameter for length. + * On 32-bit systems, the length needs to be restricted to UDATA_MAX since the + * maximum UTF-8 length of a Java string can be 3 * Integer.MAX_VALUE. Otherwise, + * there will be overflow. On 64-bit platforms, this is not an issue since UDATA + * and U_64 are the same size. + */ + if (length > UDATA_MAX) { + length = UDATA_MAX; + } +#endif /* UDATA_MAX < (3 * INT32_MAX) */ + PORT_ACCESS_FROM_VMC(vmThread); - if (length > bufferLength) { - result = (U_8*)j9mem_allocate_memory(length, OMRMEM_CATEGORY_VM); + if ((prependStrLength > J9UTF8_MAX_LENGTH) || (length > (J9UTF8_MAX_LENGTH - prependStrLength))) { + result = NULL; + } else if (length > bufferLength) { + result = (U_8 *)j9mem_allocate_memory((UDATA)length, OMRMEM_CATEGORY_VM); } else { - result = (U_8*)buffer; + result = (U_8 *)buffer; } if (NULL != result) { @@ -341,29 +399,79 @@ copyStringToJ9UTF8WithMemAlloc(J9VMThread *vmThread, j9object_t string, UDATA st memcpy(result + sizeof(J9UTF8), prependStr, prependStrLength); } - computedUtf8Length = copyStringToUTF8Helper(vmThread, string, stringFlags, 0, stringLength, result + sizeof(J9UTF8) + prependStrLength, length - sizeof(J9UTF8) - prependStrLength); + computedUtf8Length = copyStringToUTF8Helper( + vmThread, string, stringFlags, 0, stringLength, + result + sizeof(J9UTF8) + prependStrLength, + (UDATA)(length - sizeof(J9UTF8) - prependStrLength)); J9UTF8_SET_LENGTH(result, (U_16)computedUtf8Length + (U_16)prependStrLength); } - return (J9UTF8*)result; + return (J9UTF8 *)result; } -IDATA +UDATA getStringUTF8Length(J9VMThread *vmThread, j9object_t string) { - IDATA utf8Length = 0; + U_64 utf8Length = 0; UDATA unicodeLength = J9VMJAVALANGSTRING_LENGTH(vmThread, string); j9object_t unicodeBytes = J9VMJAVALANGSTRING_VALUE(vmThread, string); - UDATA i; if (IS_STRING_COMPRESSED(vmThread, string)) { - for (i = 0; i < unicodeLength; i++) { - utf8Length += VM_VMHelpers::encodedUTF8LengthI8(J9JAVAARRAYOFBYTE_LOAD(vmThread, unicodeBytes, i)); + for (UDATA i = 0; i < unicodeLength; i++) { + UDATA nextLength = VM_VMHelpers::encodedUTF8LengthI8(J9JAVAARRAYOFBYTE_LOAD(vmThread, unicodeBytes, i)); +#if UDATA_MAX < (3 * INT32_MAX) + /* Truncate on a 32-bit platform since the maximum length of a UTF-8 string is 3 * Integer.MAX_VALUE. + * This is to accommodate memory allocation functions which take a UDATA as the input parameter for + * length. On 64-bit platforms, truncation is not needed since UDATA and U_64 are of the same size. + */ + if (utf8Length > (UDATA_MAX - nextLength)) { + break; + } +#endif /* UDATA_MAX < (3 * INT32_MAX) */ + utf8Length += nextLength; + } + } else { + for (UDATA i = 0; i < unicodeLength; i++) { + UDATA nextLength = VM_VMHelpers::encodedUTF8Length(J9JAVAARRAYOFCHAR_LOAD(vmThread, unicodeBytes, i)); +#if UDATA_MAX < (3 * INT32_MAX) + /* Truncate on a 32-bit platform since the maximum length of a UTF-8 string is 3 * Integer.MAX_VALUE. + * This is to accommodate memory allocation functions which take a UDATA as the input parameter for + * length. On 64-bit platforms, truncation is not needed since UDATA and U_64 are of the same size. + */ + if (utf8Length > (UDATA_MAX - nextLength)) { + break; + } +#endif /* UDATA_MAX < (3 * INT32_MAX) */ + utf8Length += nextLength; + } + } + + return (UDATA)utf8Length; +} + +U_64 +getStringUTF8LengthTruncated(J9VMThread *vmThread, j9object_t string, U_64 maxLength) +{ + U_64 utf8Length = 0; + UDATA unicodeLength = J9VMJAVALANGSTRING_LENGTH(vmThread, string); + j9object_t unicodeBytes = J9VMJAVALANGSTRING_VALUE(vmThread, string); + + if (IS_STRING_COMPRESSED(vmThread, string)) { + for (UDATA i = 0; i < unicodeLength; i++) { + UDATA nextLength = VM_VMHelpers::encodedUTF8LengthI8(J9JAVAARRAYOFBYTE_LOAD(vmThread, unicodeBytes, i)); + if (utf8Length > (maxLength - nextLength)) { + break; + } + utf8Length += nextLength; } } else { - for (i = 0; i < unicodeLength; i++) { - utf8Length += VM_VMHelpers::encodedUTF8Length(J9JAVAARRAYOFCHAR_LOAD(vmThread, unicodeBytes, i)); + for (UDATA i = 0; i < unicodeLength; i++) { + UDATA nextLength = VM_VMHelpers::encodedUTF8Length(J9JAVAARRAYOFCHAR_LOAD(vmThread, unicodeBytes, i)); + if (utf8Length > (maxLength - nextLength)) { + break; + } + utf8Length += nextLength; } }