From 706fbb3044ef56cad14cf4e413b24042a3ea3f13 Mon Sep 17 00:00:00 2001 From: Volkan Yazici Date: Wed, 18 Mar 2026 08:25:29 +0000 Subject: [PATCH] 8378431: Move input validation checks to Java for java.lang.StringUTF16 intrinsics Reviewed-by: dfenacci, rgiulietti, rriggs --- src/hotspot/share/classfile/vmIntrinsics.hpp | 4 +- src/hotspot/share/opto/library_call.cpp | 214 ++++++++---------- src/hotspot/share/opto/library_call.hpp | 4 +- .../share/classes/java/lang/StringUTF16.java | 116 +++++++--- .../TestStringIntrinsicRangeChecks.java | 10 +- 5 files changed, 200 insertions(+), 148 deletions(-) diff --git a/src/hotspot/share/classfile/vmIntrinsics.hpp b/src/hotspot/share/classfile/vmIntrinsics.hpp index 67817682ced..e84acd62284 100644 --- a/src/hotspot/share/classfile/vmIntrinsics.hpp +++ b/src/hotspot/share/classfile/vmIntrinsics.hpp @@ -368,10 +368,10 @@ class methodHandle; do_intrinsic(_inflateStringB, java_lang_StringLatin1, inflate_name, inflateB_signature, F_S) \ do_signature(inflateB_signature, "([BI[BII)V") \ do_intrinsic(_toBytesStringU, java_lang_StringUTF16, toBytes_name, toBytesU_signature, F_S) \ - do_name( toBytes_name, "toBytes") \ + do_name( toBytes_name, "toBytes0") \ do_signature(toBytesU_signature, "([CII)[B") \ do_intrinsic(_getCharsStringU, java_lang_StringUTF16, getCharsU_name, getCharsU_signature, F_S) \ - do_name( getCharsU_name, "getChars") \ + do_name( getCharsU_name, "getChars0") \ do_signature(getCharsU_signature, "([BII[CI)V") \ do_intrinsic(_getCharStringU, java_lang_StringUTF16, getChar_name, getCharStringU_signature, F_S) \ do_signature(getCharStringU_signature, "([BI)C") \ diff --git a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp index 501e35ae5cb..ffc798019b4 100644 --- a/src/hotspot/share/opto/library_call.cpp +++ b/src/hotspot/share/opto/library_call.cpp @@ -843,6 +843,22 @@ void LibraryCallKit::set_result(RegionNode* region, PhiNode* value) { assert(value->type()->basic_type() == result()->bottom_type()->basic_type(), "sanity"); } +RegionNode* LibraryCallKit::create_bailout() { + RegionNode* bailout = new RegionNode(1); + record_for_igvn(bailout); + return bailout; +} + +bool LibraryCallKit::check_bailout(RegionNode* bailout) { + if (bailout->req() > 1) { + bailout = _gvn.transform(bailout)->as_Region(); + Node* frame = _gvn.transform(new ParmNode(C->start(), TypeFunc::FramePtr)); + Node* halt = _gvn.transform(new HaltNode(bailout, frame, "unexpected guard failure in intrinsic")); + C->root()->add_req(halt); + } + return stopped(); +} + //------------------------------generate_guard--------------------------- // Helper function for generating guarded fast-slow graph structures. // The given 'test', if true, guards a slow path. If the test fails @@ -951,36 +967,19 @@ void LibraryCallKit::generate_string_range_check(Node* array, Node* offset, Node* count, bool char_count, - bool halt_on_oob) { + RegionNode* region) { if (stopped()) { return; // already stopped } - RegionNode* bailout = new RegionNode(1); - record_for_igvn(bailout); if (char_count) { // Convert char count to byte count count = _gvn.transform(new LShiftINode(count, intcon(1))); } - // Offset and count must not be negative - generate_negative_guard(offset, bailout, nullptr, halt_on_oob); - generate_negative_guard(count, bailout, nullptr, halt_on_oob); + generate_negative_guard(offset, region, nullptr, true); + generate_negative_guard(count, region, nullptr, true); // Offset + count must not exceed length of array - generate_limit_guard(offset, count, load_array_length(array), bailout, halt_on_oob); - - if (bailout->req() > 1) { - if (halt_on_oob) { - bailout = _gvn.transform(bailout)->as_Region(); - Node* frame = _gvn.transform(new ParmNode(C->start(), TypeFunc::FramePtr)); - Node* halt = _gvn.transform(new HaltNode(bailout, frame, "unexpected guard failure in intrinsic")); - C->root()->add_req(halt); - } else { - PreserveJVMState pjvms(this); - set_control(_gvn.transform(bailout)); - uncommon_trap(Deoptimization::Reason_intrinsic, - Deoptimization::Action_maybe_recompile); - } - } + generate_limit_guard(offset, count, load_array_length(array), region, true); } Node* LibraryCallKit::current_thread_helper(Node*& tls_output, ByteSize handle_offset, @@ -1139,10 +1138,6 @@ bool LibraryCallKit::inline_array_equals(StrIntrinsicNode::ArgEnc ae) { //------------------------------inline_countPositives------------------------------ // int java.lang.StringCoding#countPositives0(byte[] ba, int off, int len) bool LibraryCallKit::inline_countPositives() { - if (too_many_traps(Deoptimization::Reason_intrinsic)) { - return false; - } - assert(callee()->signature()->size() == 3, "countPositives has 3 parameters"); // no receiver since it is static method Node* ba = argument(0); @@ -1150,8 +1145,9 @@ bool LibraryCallKit::inline_countPositives() { Node* len = argument(2); ba = must_be_not_null(ba, true); - generate_string_range_check(ba, offset, len, false, true); - if (stopped()) { + RegionNode* bailout = create_bailout(); + generate_string_range_check(ba, offset, len, false, bailout); + if (check_bailout(bailout)) { return true; } @@ -1283,9 +1279,6 @@ bool LibraryCallKit::inline_string_indexOf(StrIntrinsicNode::ArgEnc ae) { //-----------------------------inline_string_indexOfI----------------------- bool LibraryCallKit::inline_string_indexOfI(StrIntrinsicNode::ArgEnc ae) { - if (too_many_traps(Deoptimization::Reason_intrinsic)) { - return false; - } if (!Matcher::match_rule_supported(Op_StrIndexOf)) { return false; } @@ -1307,9 +1300,10 @@ bool LibraryCallKit::inline_string_indexOfI(StrIntrinsicNode::ArgEnc ae) { Node* tgt_start = array_element_address(tgt, intcon(0), T_BYTE); // Range checks - generate_string_range_check(src, src_offset, src_count, ae != StrIntrinsicNode::LL, true); - generate_string_range_check(tgt, intcon(0), tgt_count, ae == StrIntrinsicNode::UU, true); - if (stopped()) { + RegionNode* bailout = create_bailout(); + generate_string_range_check(src, src_offset, src_count, ae != StrIntrinsicNode::LL, bailout); + generate_string_range_check(tgt, intcon(0), tgt_count, ae == StrIntrinsicNode::UU, bailout); + if (check_bailout(bailout)) { return true; } @@ -1404,7 +1398,11 @@ bool LibraryCallKit::inline_string_indexOfChar(StrIntrinsicNode::ArgEnc ae) { Node* src_count = _gvn.transform(new SubINode(max, from_index)); // Range checks - generate_string_range_check(src, src_offset, src_count, ae == StrIntrinsicNode::U, true); + RegionNode* bailout = create_bailout(); + generate_string_range_check(src, src_offset, src_count, ae == StrIntrinsicNode::U, bailout); + if (check_bailout(bailout)) { + return true; + } // Check for int_ch >= 0 Node* int_ch_cmp = _gvn.transform(new CmpINode(int_ch, intcon(0))); @@ -1454,9 +1452,6 @@ bool LibraryCallKit::inline_string_indexOfChar(StrIntrinsicNode::ArgEnc ae) { // void StringLatin1.inflate0(byte[] src, int srcOff, char[] dst, int dstOff, int len) // void StringLatin1.inflate0(byte[] src, int srcOff, byte[] dst, int dstOff, int len) bool LibraryCallKit::inline_string_copy(bool compress) { - if (too_many_traps(Deoptimization::Reason_intrinsic)) { - return false; - } int nargs = 5; // 2 oops, 3 ints assert(callee()->signature()->size() == nargs, "string copy has 5 arguments"); @@ -1495,9 +1490,10 @@ bool LibraryCallKit::inline_string_copy(bool compress) { } // Range checks - generate_string_range_check(src, src_offset, length, convert_src, true); - generate_string_range_check(dst, dst_offset, length, convert_dst, true); - if (stopped()) { + RegionNode* bailout = create_bailout(); + generate_string_range_check(src, src_offset, length, convert_src, bailout); + generate_string_range_check(dst, dst_offset, length, convert_dst, bailout); + if (check_bailout(bailout)) { return true; } @@ -1545,12 +1541,10 @@ bool LibraryCallKit::inline_string_copy(bool compress) { #endif //_LP64 //------------------------inline_string_toBytesU-------------------------- -// public static byte[] StringUTF16.toBytes(char[] value, int off, int len) +// public static byte[] StringUTF16.toBytes0(char[] value, int off, int len) bool LibraryCallKit::inline_string_toBytesU() { - if (too_many_traps(Deoptimization::Reason_intrinsic)) { - return false; - } // Get the arguments. + assert(callee()->signature()->size() == 3, "character array encoder requires 3 arguments"); Node* value = argument(0); Node* offset = argument(1); Node* length = argument(2); @@ -1558,30 +1552,18 @@ bool LibraryCallKit::inline_string_toBytesU() { Node* newcopy = nullptr; // Set the original stack and the reexecute bit for the interpreter to reexecute - // the bytecode that invokes StringUTF16.toBytes() if deoptimization happens. + // the bytecode that invokes StringUTF16.toBytes0() if deoptimization happens. { PreserveReexecuteState preexecs(this); jvms()->set_should_reexecute(true); - // Check if a null path was taken unconditionally. - value = null_check(value); - - RegionNode* bailout = new RegionNode(1); - record_for_igvn(bailout); - - // Range checks - generate_negative_guard(offset, bailout); - generate_negative_guard(length, bailout); - generate_limit_guard(offset, length, load_array_length(value), bailout); + value = must_be_not_null(value, true); + RegionNode* bailout = create_bailout(); + generate_negative_guard(offset, bailout, nullptr, true); + generate_negative_guard(length, bailout, nullptr, true); + generate_limit_guard(offset, length, load_array_length(value), bailout, true); // Make sure that resulting byte[] length does not overflow Integer.MAX_VALUE - generate_limit_guard(length, intcon(0), intcon(max_jint/2), bailout); - - if (bailout->req() > 1) { - PreserveJVMState pjvms(this); - set_control(_gvn.transform(bailout)); - uncommon_trap(Deoptimization::Reason_intrinsic, - Deoptimization::Action_maybe_recompile); - } - if (stopped()) { + generate_limit_guard(length, intcon(0), intcon(max_jint/2), bailout, true); + if (check_bailout(bailout)) { return true; } @@ -1640,12 +1622,9 @@ bool LibraryCallKit::inline_string_toBytesU() { } //------------------------inline_string_getCharsU-------------------------- -// public void StringUTF16.getChars(byte[] src, int srcBegin, int srcEnd, char dst[], int dstBegin) +// public void StringUTF16.getChars0(byte[] src, int srcBegin, int srcEnd, char dst[], int dstBegin) bool LibraryCallKit::inline_string_getCharsU() { - if (too_many_traps(Deoptimization::Reason_intrinsic)) { - return false; - } - + assert(callee()->signature()->size() == 5, "StringUTF16.getChars0() has 5 arguments"); // Get the arguments. Node* src = argument(0); Node* src_begin = argument(1); @@ -1658,8 +1637,8 @@ bool LibraryCallKit::inline_string_getCharsU() { AllocateArrayNode* alloc = tightly_coupled_allocation(dst); // Check if a null path was taken unconditionally. - src = null_check(src); - dst = null_check(dst); + src = must_be_not_null(src, true); + dst = must_be_not_null(dst, true); if (stopped()) { return true; } @@ -1669,51 +1648,50 @@ bool LibraryCallKit::inline_string_getCharsU() { src_begin = _gvn.transform(new LShiftINode(src_begin, intcon(1))); // Range checks - generate_string_range_check(src, src_begin, length, true); - generate_string_range_check(dst, dst_begin, length, false); - if (stopped()) { + RegionNode* bailout = create_bailout(); + generate_string_range_check(src, src_begin, length, true, bailout); + generate_string_range_check(dst, dst_begin, length, false, bailout); + if (check_bailout(bailout)) { return true; } - if (!stopped()) { - // Calculate starting addresses. - Node* src_start = array_element_address(src, src_begin, T_BYTE); - Node* dst_start = array_element_address(dst, dst_begin, T_CHAR); + // Calculate starting addresses. + Node* src_start = array_element_address(src, src_begin, T_BYTE); + Node* dst_start = array_element_address(dst, dst_begin, T_CHAR); - // Check if array addresses are aligned to HeapWordSize - const TypeInt* tsrc = gvn().type(src_begin)->is_int(); - const TypeInt* tdst = gvn().type(dst_begin)->is_int(); - bool aligned = tsrc->is_con() && ((arrayOopDesc::base_offset_in_bytes(T_BYTE) + tsrc->get_con() * type2aelembytes(T_BYTE)) % HeapWordSize == 0) && - tdst->is_con() && ((arrayOopDesc::base_offset_in_bytes(T_CHAR) + tdst->get_con() * type2aelembytes(T_CHAR)) % HeapWordSize == 0); + // Check if array addresses are aligned to HeapWordSize + const TypeInt* tsrc = gvn().type(src_begin)->is_int(); + const TypeInt* tdst = gvn().type(dst_begin)->is_int(); + bool aligned = tsrc->is_con() && ((arrayOopDesc::base_offset_in_bytes(T_BYTE) + tsrc->get_con() * type2aelembytes(T_BYTE)) % HeapWordSize == 0) && + tdst->is_con() && ((arrayOopDesc::base_offset_in_bytes(T_CHAR) + tdst->get_con() * type2aelembytes(T_CHAR)) % HeapWordSize == 0); - // Figure out which arraycopy runtime method to call (disjoint, uninitialized). - const char* copyfunc_name = "arraycopy"; - address copyfunc_addr = StubRoutines::select_arraycopy_function(T_CHAR, aligned, true, copyfunc_name, true); - Node* call = make_runtime_call(RC_LEAF|RC_NO_FP, - OptoRuntime::fast_arraycopy_Type(), - copyfunc_addr, copyfunc_name, TypeRawPtr::BOTTOM, - src_start, dst_start, ConvI2X(length) XTOP); - // Do not let reads from the cloned object float above the arraycopy. - if (alloc != nullptr) { - if (alloc->maybe_set_complete(&_gvn)) { - // "You break it, you buy it." - InitializeNode* init = alloc->initialization(); - assert(init->is_complete(), "we just did this"); - init->set_complete_with_arraycopy(); - assert(dst->is_CheckCastPP(), "sanity"); - assert(dst->in(0)->in(0) == init, "dest pinned"); - } - // Do not let stores that initialize this object be reordered with - // a subsequent store that would make this object accessible by - // other threads. - // Record what AllocateNode this StoreStore protects so that - // escape analysis can go from the MemBarStoreStoreNode to the - // AllocateNode and eliminate the MemBarStoreStoreNode if possible - // based on the escape status of the AllocateNode. - insert_mem_bar(Op_MemBarStoreStore, alloc->proj_out_or_null(AllocateNode::RawAddress)); - } else { - insert_mem_bar(Op_MemBarCPUOrder); + // Figure out which arraycopy runtime method to call (disjoint, uninitialized). + const char* copyfunc_name = "arraycopy"; + address copyfunc_addr = StubRoutines::select_arraycopy_function(T_CHAR, aligned, true, copyfunc_name, true); + Node* call = make_runtime_call(RC_LEAF|RC_NO_FP, + OptoRuntime::fast_arraycopy_Type(), + copyfunc_addr, copyfunc_name, TypeRawPtr::BOTTOM, + src_start, dst_start, ConvI2X(length) XTOP); + // Do not let reads from the cloned object float above the arraycopy. + if (alloc != nullptr) { + if (alloc->maybe_set_complete(&_gvn)) { + // "You break it, you buy it." + InitializeNode* init = alloc->initialization(); + assert(init->is_complete(), "we just did this"); + init->set_complete_with_arraycopy(); + assert(dst->is_CheckCastPP(), "sanity"); + assert(dst->in(0)->in(0) == init, "dest pinned"); } + // Do not let stores that initialize this object be reordered with + // a subsequent store that would make this object accessible by + // other threads. + // Record what AllocateNode this StoreStore protects so that + // escape analysis can go from the MemBarStoreStoreNode to the + // AllocateNode and eliminate the MemBarStoreStoreNode if possible + // based on the escape status of the AllocateNode. + insert_mem_bar(Op_MemBarStoreStore, alloc->proj_out_or_null(AllocateNode::RawAddress)); + } else { + insert_mem_bar(Op_MemBarCPUOrder); } C->set_has_split_ifs(true); // Has chance for split-if optimization @@ -1725,9 +1703,16 @@ bool LibraryCallKit::inline_string_getCharsU() { // static void StringUTF16.putChar(byte[] val, int index, int c) // static char StringUTF16.getChar(byte[] val, int index) bool LibraryCallKit::inline_string_char_access(bool is_store) { + Node* ch; + if (is_store) { + assert(callee()->signature()->size() == 3, "StringUTF16.putChar() has 3 arguments"); + ch = argument(2); + } else { + assert(callee()->signature()->size() == 2, "StringUTF16.getChar() has 2 arguments"); + ch = nullptr; + } Node* value = argument(0); Node* index = argument(1); - Node* ch = is_store ? argument(2) : nullptr; // This intrinsic accesses byte[] array as char[] array. Computing the offsets // correctly requires matched array shapes. @@ -6185,9 +6170,10 @@ bool LibraryCallKit::inline_encodeISOArray(bool ascii) { } // Check source & target bounds - generate_string_range_check(src, src_offset, length, src_elem == T_BYTE, true); - generate_string_range_check(dst, dst_offset, length, false, true); - if (stopped()) { + RegionNode* bailout = create_bailout(); + generate_string_range_check(src, src_offset, length, src_elem == T_BYTE, bailout); + generate_string_range_check(dst, dst_offset, length, false, bailout); + if (check_bailout(bailout)) { return true; } diff --git a/src/hotspot/share/opto/library_call.hpp b/src/hotspot/share/opto/library_call.hpp index bd4b753d1cb..9b87df645e1 100644 --- a/src/hotspot/share/opto/library_call.hpp +++ b/src/hotspot/share/opto/library_call.hpp @@ -130,6 +130,8 @@ class LibraryCallKit : public GraphKit { virtual int reexecute_sp() { return _reexecute_sp; } // Helper functions to inline natives + RegionNode* create_bailout(); + bool check_bailout(RegionNode* bailout); Node* generate_guard(Node* test, RegionNode* region, float true_prob); Node* generate_slow_guard(Node* test, RegionNode* region); Node* generate_fair_guard(Node* test, RegionNode* region); @@ -143,7 +145,7 @@ class LibraryCallKit : public GraphKit { bool with_opaque = false); void generate_string_range_check(Node* array, Node* offset, Node* length, bool char_count, - bool halt_on_oob = false); + RegionNode* region); Node* current_thread_helper(Node* &tls_output, ByteSize handle_offset, bool is_immutable); Node* generate_current_thread(Node* &tls_output); diff --git a/src/java.base/share/classes/java/lang/StringUTF16.java b/src/java.base/share/classes/java/lang/StringUTF16.java index 27b9ae54a8a..23de31a61b7 100644 --- a/src/java.base/share/classes/java/lang/StringUTF16.java +++ b/src/java.base/share/classes/java/lang/StringUTF16.java @@ -67,30 +67,61 @@ final class StringUTF16 { // Check the size of a UTF16-coded string // Throw an exception if out of range - static int newBytesLength(int len) { - if (len < 0) { - throw new NegativeArraySizeException(); - } - if (len >= MAX_LENGTH) { - throw new OutOfMemoryError("UTF16 String size is " + len + - ", should be less than " + MAX_LENGTH); - } + private static int newBytesLength(int len) { + checkBytesLength(len); return len << 1; } + /** + * Checks if the provided length is a valid UTF-16 string byte array length. + * + * @param length a UTF-16 string byte array length + * + * @throws NegativeArraySizeException if {@code length < 0} + * @throws OutOfMemoryError if {@code length > (Integer.MAX_VALUE / 2)} + */ + private static void checkBytesLength(int length) { + if (length < 0) { + throw new NegativeArraySizeException(); + } + if (length >= MAX_LENGTH) { + throw new OutOfMemoryError("UTF16 String size is " + length + + ", should be less than " + MAX_LENGTH); + } + } + + /** + * Writes the given code point to the specified position of the provided + * UTF-16 string byte array. + *

+ * WARNING: This method does not perform any input validations. + * + * @param val a UTF-16 string byte array + * @param index the index of the character to write the code point to + * @param c a code point + */ + // vmIntrinsics::_putCharStringU @IntrinsicCandidate - // intrinsic performs no bounds checks static void putChar(byte[] val, int index, int c) { - assert index >= 0 && index < length(val) : "Trusted caller missed bounds check"; + assert val != null && index >= 0 && index < length(val) : "Trusted caller violated input constraints"; index <<= 1; val[index++] = (byte)(c >> HI_BYTE_SHIFT); val[index] = (byte)(c >> LO_BYTE_SHIFT); } + /** + * {@return the code point at the the specified position of the provided + * UTF-16 string byte array} + *

+ * WARNING: This method does not perform any input validations. + * + * @param val a UTF-16 string byte array + * @param index the index of the character to get the code point from + */ + // vmIntrinsics::_getCharStringU @IntrinsicCandidate - // intrinsic performs no bounds checks static char getChar(byte[] val, int index) { - assert index >= 0 && index < length(val) : "Trusted caller missed bounds check"; + assert val != null && index >= 0 && index < length(val) : "Trusted caller violated input constraints"; index <<= 1; return (char)(((val[index++] & 0xff) << HI_BYTE_SHIFT) | ((val[index] & 0xff) << LO_BYTE_SHIFT)); @@ -173,14 +204,27 @@ final class StringUTF16 { } /** - * {@return an encoded byte[] for the UTF16 characters in char[]} - * No checking is done on the characters, some may or may not be latin1. - * @param value a char array - * @param off an offset - * @param len a length + * {@return a UTF-16 string byte array produced by encoding the characters + * in the provided character array sub-range} + * + * @param value a character array to encode + * @param off the index of the character to start encoding from + * @param len the number of characters to encode + * + * @throws NegativeArraySizeException if {@code len < 0} + * @throws NullPointerException if {@code value} is null + * @throws OutOfMemoryError if {@code len > (Integer.MAX_VALUE / 2)} + * @throws StringIndexOutOfBoundsException if the sub-range is out of bounds */ - @IntrinsicCandidate static byte[] toBytes(char[] value, int off, int len) { + checkBytesLength(len); + String.checkBoundsOffCount(off, len, value.length); // Implicit null check on `value` + return toBytes0(value, off, len); + } + + // vmIntrinsics::_toBytesStringU + @IntrinsicCandidate + private static byte[] toBytes0(char[] value, int off, int len) { byte[] val = newBytesFor(len); for (int i = 0; i < len; i++) { putChar(val, i, value[off]); @@ -495,12 +539,28 @@ final class StringUTF16 { return result; } - @IntrinsicCandidate + /** + * Copies the specified sub-range of characters from a UTF-16 string byte + * array to the specified character array sub-range. + * + * @param value the source UTF-16 string byte array to copy from + * @param srcBegin the index (inclusive) of the first character in the source sub-range + * @param srcEnd the index (exclusive) of the last character in the source sub-range + * @param dst the target character array to copy to + * @param dstBegin the index (inclusive) of the first character in the target sub-range + * + * @throws NullPointerException if {@code value} or {@code dst} is null + * @throws StringIndexOutOfBoundsException if the sub-ranges are out of bounds + */ static void getChars(byte[] value, int srcBegin, int srcEnd, char[] dst, int dstBegin) { - // We need a range check here because 'getChar' has no checks - if (srcBegin < srcEnd) { - String.checkBoundsOffCount(srcBegin, srcEnd - srcBegin, length(value)); - } + checkBoundsBeginEnd(srcBegin, srcEnd, value); // Implicit null check on `value` via `checkBoundsBeginEnd()` + String.checkBoundsOffCount(dstBegin, srcEnd - srcBegin, dst.length); // Implicit null check on `dst` + getChars0(value, srcBegin, srcEnd, dst, dstBegin); + } + + // vmIntrinsics::_getCharsStringU + @IntrinsicCandidate + private static void getChars0(byte[] value, int srcBegin, int srcEnd, char[] dst, int dstBegin) { for (int i = srcBegin; i < srcEnd; i++) { dst[dstBegin++] = getChar(value, i); } @@ -721,7 +781,7 @@ final class StringUTF16 { return -StringLatin1.compareToCI_UTF16(other, value); } - public static int compareToFC_Latin1(byte[] value, byte[] other) { + static int compareToFC_Latin1(byte[] value, byte[] other) { return -StringLatin1.compareToFC_UTF16(other, value); } @@ -769,7 +829,7 @@ final class StringUTF16 { return 0; } - public static int compareToFC(byte[] value, byte[] other) { + static int compareToFC(byte[] value, byte[] other) { int tlast = length(value); int olast = length(other); int lim = Math.min(tlast, olast); @@ -1970,13 +2030,13 @@ final class StringUTF16 { } } - static final int MAX_LENGTH = Integer.MAX_VALUE >> 1; + private static final int MAX_LENGTH = Integer.MAX_VALUE >> 1; - static void checkIndex(int off, byte[] val) { + private static void checkIndex(int off, byte[] val) { String.checkIndex(off, length(val)); } - static void checkOffset(int off, byte[] val) { + private static void checkOffset(int off, byte[] val) { String.checkOffset(off, length(val)); } diff --git a/test/hotspot/jtreg/compiler/intrinsics/string/TestStringIntrinsicRangeChecks.java b/test/hotspot/jtreg/compiler/intrinsics/string/TestStringIntrinsicRangeChecks.java index a7d2cfe7fa7..ec99bba19d2 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/string/TestStringIntrinsicRangeChecks.java +++ b/test/hotspot/jtreg/compiler/intrinsics/string/TestStringIntrinsicRangeChecks.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, 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 @@ -89,12 +89,16 @@ public class TestStringIntrinsicRangeChecks { for (int srcOff = 0; srcOff < SIZE; ++srcOff) { for (int dstOff = 0; dstOff < SIZE; ++dstOff) { for (int len = 0; len < SIZE; ++len) { + int srcEnd = srcOff + len; + int dstEnd = dstOff + len; // Check for potential overlows in source or destination array boolean srcOverflow = (srcOff + len) > SIZE; boolean srcOverflowB = (2*srcOff + 2*len) > SIZE; boolean dstOverflow = (dstOff + len) > SIZE; boolean dstOverflowB = (2*dstOff + 2*len) > SIZE; - boolean getCharsOver = (srcOff < len) && ((2*(len-1) >= SIZE) || ((dstOff + len - srcOff) > SIZE)); + boolean getCharsOver = srcOff > srcEnd || (2*srcEnd) > SIZE || // src + (2*len) > SIZE || // len + dstOff > dstEnd || dstEnd > SIZE; // dst // Check if an exception is thrown and bail out if result is inconsistent with above // assumptions (for example, an exception was not thrown although an overflow happened). check(compressByte, srcOverflowB || dstOverflow, byteArray, srcOff, SIZE, dstOff, len); @@ -102,7 +106,7 @@ public class TestStringIntrinsicRangeChecks { check(inflateByte, srcOverflow || dstOverflowB, byteArray, srcOff, SIZE, dstOff, len); check(inflateChar, srcOverflow || dstOverflow, byteArray, srcOff, SIZE, dstOff, len); check(toBytes, srcOverflow, charArray, srcOff, len); - check(getChars, getCharsOver, byteArray, srcOff, len, SIZE, dstOff); + check(getChars, getCharsOver, byteArray, srcOff, srcEnd, SIZE, dstOff); } } }