8328877: [JNI] The JNI Specification needs to address the limitations of integer UTF-8 String lengths

Reviewed-by: cjplummer, alanb
This commit is contained in:
David Holmes 2024-09-04 03:41:42 +00:00
parent bbb516163d
commit 90f3f43257
9 changed files with 208 additions and 27 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2005, 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
@ -19,7 +19,7 @@
* 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.
*
*
*/
provider hotspot_jni {
@ -129,7 +129,7 @@ provider hotspot_jni {
probe CallNonvirtualVoidMethodA__return();
probe CallNonvirtualVoidMethod__entry(void*, void*, void*, uintptr_t);
probe CallNonvirtualVoidMethod__return();
probe CallNonvirtualVoidMethodV__entry(void*, void*, void*, uintptr_t);
probe CallNonvirtualVoidMethodV__entry(void*, void*, void*, uintptr_t);
probe CallNonvirtualVoidMethodV__return();
probe CallObjectMethodA__entry(void*, void*, uintptr_t);
probe CallObjectMethodA__return(void*);
@ -200,14 +200,14 @@ provider hotspot_jni {
probe CallStaticVoidMethodA__entry(void*, void*, uintptr_t);
probe CallStaticVoidMethodA__return();
probe CallStaticVoidMethod__entry(void*, void*, uintptr_t);
probe CallStaticVoidMethod__return();
probe CallStaticVoidMethodV__entry(void*, void*, uintptr_t);
probe CallStaticVoidMethod__return();
probe CallStaticVoidMethodV__entry(void*, void*, uintptr_t);
probe CallStaticVoidMethodV__return();
probe CallVoidMethodA__entry(void*, void*, uintptr_t);
probe CallVoidMethodA__entry(void*, void*, uintptr_t);
probe CallVoidMethodA__return();
probe CallVoidMethod__entry(void*, void*, uintptr_t);
probe CallVoidMethod__return();
probe CallVoidMethodV__entry(void*, void*, uintptr_t);
probe CallVoidMethod__entry(void*, void*, uintptr_t);
probe CallVoidMethod__return();
probe CallVoidMethodV__entry(void*, void*, uintptr_t);
probe CallVoidMethodV__return();
probe CreateJavaVM__entry(void**, void**, void*);
probe CreateJavaVM__return(uint32_t);
@ -229,7 +229,7 @@ provider hotspot_jni {
probe ExceptionCheck__return(uintptr_t);
probe ExceptionClear__entry(void*);
probe ExceptionClear__return();
probe ExceptionDescribe__entry(void*);
probe ExceptionDescribe__entry(void*);
probe ExceptionDescribe__return();
probe ExceptionOccurred__entry(void*);
probe ExceptionOccurred__return(void*);
@ -352,6 +352,8 @@ provider hotspot_jni {
probe GetStringUTFChars__return(const char*);
probe GetStringUTFLength__entry(void*, void*);
probe GetStringUTFLength__return(uintptr_t);
probe GetStringUTFLengthAsLong__entry(void*, void*);
probe GetStringUTFLengthAsLong__return(uintptr_t);
probe GetStringUTFRegion__entry(void*, void*, uintptr_t, uintptr_t, char*);
probe GetStringUTFRegion__return();
probe GetSuperclass__entry(void*, void*);
@ -388,13 +390,13 @@ provider hotspot_jni {
probe NewLocalRef__return(void*);
probe NewLongArray__entry(void*, uintptr_t);
probe NewLongArray__return(void*);
probe NewObjectA__entry(void*, void*, uintptr_t);
probe NewObjectA__entry(void*, void*, uintptr_t);
probe NewObjectA__return(void*);
probe NewObjectArray__entry(void*, uintptr_t, void*, void*);
probe NewObjectArray__return(void*);
probe NewObject__entry(void*, void*, uintptr_t);
probe NewObject__entry(void*, void*, uintptr_t);
probe NewObject__return(void*);
probe NewObjectV__entry(void*, void*, uintptr_t);
probe NewObjectV__entry(void*, void*, uintptr_t);
probe NewObjectV__return(void*);
probe NewShortArray__entry(void*, uintptr_t);
probe NewShortArray__return(void*);
@ -408,7 +410,7 @@ provider hotspot_jni {
probe PopLocalFrame__return(void*);
probe PushLocalFrame__entry(void*, uint32_t);
probe PushLocalFrame__return(uint32_t);
probe RegisterNatives__entry(void*, void*, const void*, uint32_t);
probe RegisterNatives__entry(void*, void*, const void*, uint32_t);
probe RegisterNatives__return(uint32_t);
probe ReleaseBooleanArrayElements__entry(void*, void*, uintptr_t*, uint32_t);
probe ReleaseBooleanArrayElements__return();
@ -490,13 +492,13 @@ provider hotspot_jni {
probe SetStaticShortField__return();
probe Throw__entry(void*, void*);
probe Throw__return(intptr_t);
probe ThrowNew__entry(void*, void*, const char*);
probe ThrowNew__return(intptr_t);
probe ThrowNew__entry(void*, void*, const char*);
probe ThrowNew__return(intptr_t);
probe ToReflectedField__entry(void*, void*, uintptr_t, uintptr_t);
probe ToReflectedField__return(void*);
probe ToReflectedMethod__entry(void*, void*, uintptr_t, uintptr_t);
probe ToReflectedMethod__return(void*);
probe UnregisterNatives__entry(void*, void*);
probe UnregisterNatives__entry(void*, void*);
probe UnregisterNatives__return(uint32_t);
};
@ -505,4 +507,3 @@ provider hotspot_jni {
#pragma D attributes Private/Private/Unknown provider hotspot_jni function
#pragma D attributes Standard/Standard/Common provider hotspot_jni name
#pragma D attributes Evolving/Evolving/Common provider hotspot_jni args

View File

@ -102,7 +102,7 @@
#include "jfr/jfr.hpp"
#endif
static jint CurrentVersion = JNI_VERSION_21;
static jint CurrentVersion = JNI_VERSION_24;
#if defined(_WIN32) && !defined(USE_VECTORED_EXCEPTION_HANDLING)
extern LONG WINAPI topLevelExceptionFilter(_EXCEPTION_POINTERS* );
@ -2221,13 +2221,21 @@ JNI_END
JNI_ENTRY(jsize, jni_GetStringUTFLength(JNIEnv *env, jstring string))
HOTSPOT_JNI_GETSTRINGUTFLENGTH_ENTRY(env, string);
HOTSPOT_JNI_GETSTRINGUTFLENGTH_ENTRY(env, string);
oop java_string = JNIHandles::resolve_non_null(string);
jsize ret = java_lang_String::utf8_length_as_int(java_string);
HOTSPOT_JNI_GETSTRINGUTFLENGTH_RETURN(ret);
return ret;
JNI_END
JNI_ENTRY(jlong, jni_GetStringUTFLengthAsLong(JNIEnv *env, jstring string))
HOTSPOT_JNI_GETSTRINGUTFLENGTHASLONG_ENTRY(env, string);
oop java_string = JNIHandles::resolve_non_null(string);
size_t ret = java_lang_String::utf8_length(java_string);
HOTSPOT_JNI_GETSTRINGUTFLENGTHASLONG_RETURN(ret);
return checked_cast<jlong>(ret);
JNI_END
JNI_ENTRY(const char*, jni_GetStringUTFChars(JNIEnv *env, jstring string, jboolean *isCopy))
HOTSPOT_JNI_GETSTRINGUTFCHARS_ENTRY(env, string, (uintptr_t *) isCopy);
@ -3398,7 +3406,11 @@ struct JNINativeInterface_ jni_NativeInterface = {
// Virtual threads
jni_IsVirtualThread
jni_IsVirtualThread,
// Large UTF8 support
jni_GetStringUTFLengthAsLong
};

View File

@ -1511,6 +1511,27 @@ JNI_ENTRY_CHECKED(jsize,
checkString(thr, str);
)
jsize result = UNCHECKED()->GetStringUTFLength(env,str);
jlong full_length = UNCHECKED()->GetStringUTFLengthAsLong(env,str);
if (full_length > result) {
ResourceMark rm(thr);
stringStream ss;
ss.print("WARNING: large String with modified UTF-8 length " JLONG_FORMAT
" is reporting a reduced length of %d - use GetStringUTFLengthAsLong instead",
full_length, result);
NativeReportJNIWarning(thr, ss.as_string());
}
functionExit(thr);
return result;
JNI_END
JNI_ENTRY_CHECKED(jlong,
checked_jni_GetStringUTFLengthAsLong(JNIEnv *env,
jstring str))
functionEnter(thr);
IN_VM(
checkString(thr, str);
)
jlong result = UNCHECKED()->GetStringUTFLengthAsLong(env,str);
functionExit(thr);
return result;
JNI_END
@ -2283,7 +2304,12 @@ struct JNINativeInterface_ checked_jni_NativeInterface = {
// Virtual threads
checked_jni_IsVirtualThread
checked_jni_IsVirtualThread,
// Large UTF8 support
checked_jni_GetStringUTFLengthAsLong
};

View File

@ -984,6 +984,7 @@ jboolean Threads::is_supported_jni_version(jint version) {
if (version == JNI_VERSION_19) return JNI_TRUE;
if (version == JNI_VERSION_20) return JNI_TRUE;
if (version == JNI_VERSION_21) return JNI_TRUE;
if (version == JNI_VERSION_24) return JNI_TRUE;
return JNI_FALSE;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 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
@ -783,6 +783,10 @@
#define HOTSPOT_JNI_GETSTRINGUTFLENGTH_ENTRY_ENABLED() 0
#define HOTSPOT_JNI_GETSTRINGUTFLENGTH_RETURN(arg0)
#define HOTSPOT_JNI_GETSTRINGUTFLENGTH_RETURN_ENABLED() 0
#define HOTSPOT_JNI_GETSTRINGUTFLENGTHASLONG_ENTRY(arg0, arg1)
#define HOTSPOT_JNI_GETSTRINGUTFLENGTHASLONG_ENTRY_ENABLED() 0
#define HOTSPOT_JNI_GETSTRINGUTFLENGTHASLONG_RETURN(arg0)
#define HOTSPOT_JNI_GETSTRINGUTFLENGTHASLONG_RETURN_ENABLED() 0
#define HOTSPOT_JNI_GETSTRINGUTFREGION_ENTRY(arg0, arg1, arg2, arg3, arg4)
#define HOTSPOT_JNI_GETSTRINGUTFREGION_ENTRY_ENABLED() 0
#define HOTSPOT_JNI_GETSTRINGUTFREGION_RETURN()

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1996, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1996, 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
@ -775,6 +775,12 @@ struct JNINativeInterface_ {
jboolean (JNICALL *IsVirtualThread)
(JNIEnv* env, jobject obj);
/* Large UTF8 Support */
jlong (JNICALL *GetStringUTFLengthAsLong)
(JNIEnv *env, jstring str);
};
/*
@ -1623,6 +1629,9 @@ struct JNIEnv_ {
jsize GetStringUTFLength(jstring str) {
return functions->GetStringUTFLength(this,str);
}
jlong GetStringUTFLengthAsLong(jstring str) {
return functions->GetStringUTFLengthAsLong(this,str);
}
const char* GetStringUTFChars(jstring str, jboolean *isCopy) {
return functions->GetStringUTFChars(this,str,isCopy);
}
@ -1993,6 +2002,7 @@ JNI_OnUnload(JavaVM *vm, void *reserved);
#define JNI_VERSION_19 0x00130000
#define JNI_VERSION_20 0x00140000
#define JNI_VERSION_21 0x00150000
#define JNI_VERSION_24 0x00180000
#ifdef __cplusplus
} /* extern "C" */

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 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
@ -27,12 +27,12 @@
*/
public class JniVersion {
public static final int JNI_VERSION_21 = 0x00150000;
public static final int JNI_VERSION_24 = 0x00180000;
public static void main(String... args) throws Exception {
System.loadLibrary("JniVersion");
int res = getJniVersion();
if (res != JNI_VERSION_21) {
if (res != JNI_VERSION_24) {
throw new Exception("Unexpected value returned from getJniVersion(): 0x" + Integer.toHexString(res));
}
}

View File

@ -0,0 +1,73 @@
/*
* Copyright (c) 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
* 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 8328877
* @summary Test warning for GetStringUTFLength and functionality of GetStringUTFLengthAsLong
* @requires vm.bits == 64
* @library /test/lib
* @modules java.management
* @run main/native TestLargeUTF8Length launch
*/
import java.util.Arrays;
import jdk.test.lib.Utils;
import jdk.test.lib.process.ProcessTools;
import jdk.test.lib.process.OutputAnalyzer;
public class TestLargeUTF8Length {
static {
System.loadLibrary("TestLargeUTF8Length");
}
static native void checkUTF8Length(String s, long utf8Length);
static void test() {
int length = Integer.MAX_VALUE/2 + 1;
char character = (char)0XD1; // N with tilde
long utf8Length = 2L * length;
char[] chrs = new char[length];
Arrays.fill(chrs, character);
String s = new String(chrs);
checkUTF8Length(s, utf8Length);
}
public static void main(String[] args) throws Throwable {
if (args == null || args.length == 0) {
test();
return;
}
OutputAnalyzer oa = ProcessTools.executeTestJava("-Xms9G",
"-Xmx9G",
"-Xcheck:jni",
"-Djava.library.path=" + Utils.TEST_NATIVE_PATH,
"TestLargeUTF8Length");
String warning = "WARNING: large String with modified UTF-8 length .*" +
"is reporting a reduced length of .* - use GetStringUTFLengthAsLong instead";
oa.shouldHaveExitValue(0);
oa.stdoutShouldMatch(warning);
oa.reportDiagnosticSummary();
}
}

View File

@ -0,0 +1,54 @@
/*
* Copyright (c) 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
* 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 <jni.h>
#include <limits.h>
#include <stdio.h>
JNIEXPORT void JNICALL
Java_TestLargeUTF8Length_checkUTF8Length(JNIEnv *env, jclass clz,
jstring str, jlong expected_length) {
jlong utf8_length;
// First get truncated length to generate warning
utf8_length = (*env)->GetStringUTFLength(env, str);
if (utf8_length != INT_MAX - 1) {
printf("Error: expected length of %d, but got %lld\n", INT_MAX - 1,
(long long) utf8_length);
(*env)->FatalError(env, "Unexpected truncated length");
}
// Now get true length
utf8_length = (*env)->GetStringUTFLengthAsLong(env, str);
if (utf8_length != expected_length ) {
printf("Error: expected length of %lld, but got %lld\n",
(long long) expected_length, (long long) utf8_length);
(*env)->FatalError(env, "Unexpected true length");
}
}