8378431: Move input validation checks to Java for java.lang.StringUTF16 intrinsics

Reviewed-by: dfenacci, rgiulietti, rriggs
This commit is contained in:
Volkan Yazici 2026-03-18 08:25:29 +00:00
parent d8f19bf961
commit 706fbb3044
5 changed files with 200 additions and 148 deletions

View File

@ -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") \

View File

@ -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;
}

View File

@ -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);

View File

@ -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.
* <p>
* <b>WARNING: This method does not perform any input validations.</b>
*
* @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}
* <p>
* <b>WARNING: This method does not perform any input validations.</b>
*
* @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));
}

View File

@ -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);
}
}
}