Extend test 712-varhandle-invocations to check negative array indices.
Check that varhandle intrisics do not crash and delegate to slow path
when array index is negative. The runtime should throw an out-of-bounds
exception, which is caught and ignored by the test. Not throwing an
exception fails the test as well.
Bug: 71781600
Test: art/test.py -r -t 712-varhandle-invocations --host
Test: art/test.py -r -t 712-varhandle-invocations --target
Test: Manually change/remove the generated instructions that check for
negative array indices on arm64/x86_64 and ensure that the above test
crash in the generated code, as expected.
Change-Id: Id2c31b639ab63fd1f3fb7d521f9e6a3676535fe5
diff --git a/test/712-varhandle-invocations/util-src/generate_java.py b/test/712-varhandle-invocations/util-src/generate_java.py
index 5d4bced..763fb20 100644
--- a/test/712-varhandle-invocations/util-src/generate_java.py
+++ b/test/712-varhandle-invocations/util-src/generate_java.py
@@ -576,6 +576,12 @@
dictionary['lookup'] = var_handle_kind.get_lookup(dictionary)
dictionary['field_declarations'] = ";\n".join(var_handle_kind.get_field_declarations(dictionary))
dictionary['read_value'] = var_handle_kind.get_value(dictionary)
+
+ # For indexable types we need to check out-of-bounds access at negative index.
+ # We always generate the check, but comment it out for non-indexable types.
+ dictionary['coordinates_negative_index'] = coordinates.replace('index', '-16')
+ dictionary['indexable_only'] = "//" if not re.search('Array|ByteBuffer', var_handle_kind.name) else ""
+
return dictionary
def emit_accessor_test(var_handle_kind, accessor, var_type, output_path):
@@ -587,11 +593,21 @@
if accessor.access_mode_form == AccessModeForm.GET:
test_template = Template("""
${var_type} value = (${var_type}) vh.${accessor_method}(${coordinates});
- assertEquals(${initial_value}, value);""")
+ assertEquals(${initial_value}, value);
+ // Check for out of bounds access (for indexable types only).
+ ${indexable_only} try {
+ ${indexable_only} value = (${var_type}) vh.${accessor_method}(${coordinates_negative_index});
+ ${indexable_only} failUnreachable();
+ ${indexable_only} } catch (IndexOutOfBoundsException ex) {}""")
elif accessor.access_mode_form == AccessModeForm.SET:
test_template = Template("""
vh.${accessor_method}(${coordinates}${updated_value});
- assertEquals(${updated_value}, ${read_value});""")
+ assertEquals(${updated_value}, ${read_value});
+ // Check for out of bounds access (for indexable types only).
+ ${indexable_only} try {
+ ${indexable_only} vh.${accessor_method}(${coordinates_negative_index}${updated_value});
+ ${indexable_only} failUnreachable();
+ ${indexable_only} } catch (IndexOutOfBoundsException ex) {}""")
elif accessor.access_mode_form == AccessModeForm.STRONG_COMPARE_AND_SET:
test_template = Template("""
assertEquals(${initial_value}, ${read_value});
@@ -602,7 +618,12 @@
// Test an update that should fail.
applied = (boolean) vh.${accessor_method}(${coordinates}${initial_value}, ${initial_value});
assertFalse(applied);
- assertEquals(${updated_value}, ${read_value});""")
+ assertEquals(${updated_value}, ${read_value});
+ // Check for out of bounds access (for indexable types only).
+ ${indexable_only} try {
+ ${indexable_only} applied = (boolean) vh.${accessor_method}(${coordinates_negative_index}${updated_value}, ${updated_value});
+ ${indexable_only} failUnreachable();
+ ${indexable_only} } catch (IndexOutOfBoundsException ex) {}""")
elif accessor.access_mode_form == AccessModeForm.WEAK_COMPARE_AND_SET:
test_template = Template("""
assertEquals(${initial_value}, ${read_value});
@@ -617,7 +638,12 @@
// Test an update that should fail.
applied = (boolean) vh.${accessor_method}(${coordinates}${initial_value}, ${initial_value});
assertFalse(applied);
- assertEquals(${updated_value}, ${read_value});""")
+ assertEquals(${updated_value}, ${read_value});
+ // Check for out of bounds access (for indexable types only).
+ ${indexable_only} try {
+ ${indexable_only} applied = (boolean) vh.${accessor_method}(${coordinates_negative_index}${updated_value}, ${updated_value});
+ ${indexable_only} failUnreachable();
+ ${indexable_only} } catch (IndexOutOfBoundsException ex) {}""")
elif accessor.access_mode_form == AccessModeForm.COMPARE_AND_EXCHANGE:
test_template = Template("""
// This update should succeed.
@@ -627,23 +653,43 @@
// This update should fail.
witness_value = (${var_type}) vh.${accessor_method}(${coordinates}${initial_value}, ${initial_value});
assertEquals(${updated_value}, witness_value);
- assertEquals(${updated_value}, ${read_value});""")
+ assertEquals(${updated_value}, ${read_value});
+ // Check for out of bounds access (for indexable types only).
+ ${indexable_only} try {
+ ${indexable_only} witness_value = (${var_type}) vh.${accessor_method}(${coordinates_negative_index}${updated_value}, ${updated_value});
+ ${indexable_only} failUnreachable();
+ ${indexable_only} } catch (IndexOutOfBoundsException ex) {}""")
elif accessor.access_mode_form == AccessModeForm.GET_AND_SET:
test_template = Template("""
${var_type} old_value = (${var_type}) vh.${accessor_method}(${coordinates}${updated_value});
assertEquals(${initial_value}, old_value);
- assertEquals(${updated_value}, ${read_value});""")
+ assertEquals(${updated_value}, ${read_value});
+ // Check for out of bounds access (for indexable types only).
+ ${indexable_only} try {
+ ${indexable_only} old_value = (${var_type}) vh.${accessor_method}(${coordinates_negative_index}${updated_value});
+ ${indexable_only} failUnreachable();
+ ${indexable_only} } catch (IndexOutOfBoundsException ex) {}""")
elif accessor.access_mode_form == AccessModeForm.GET_AND_UPDATE_BITWISE:
if var_type.supports_bitwise == True:
expansions['binop'] = accessor.get_java_bitwise_operator()
test_template = Template("""
${var_type} old_value = (${var_type}) vh.${accessor_method}(${coordinates}${updated_value});
assertEquals(${initial_value}, old_value);
- assertEquals(${initial_value} ${binop} ${updated_value}, ${read_value});""")
+ assertEquals(${initial_value} ${binop} ${updated_value}, ${read_value});
+ // Check for out of bounds access (for indexable types only).
+ ${indexable_only} try {
+ ${indexable_only} old_value = (${var_type}) vh.${accessor_method}(${coordinates_negative_index}${updated_value});
+ ${indexable_only} failUnreachable();
+ ${indexable_only} } catch (IndexOutOfBoundsException ex) {}""")
else:
test_template = Template("""
vh.${accessor_method}(${coordinates}${initial_value}, ${updated_value});
- failUnreachable();""")
+ failUnreachable();
+ // Check for out of bounds access (for indexable types only).
+ ${indexable_only} try {
+ ${indexable_only} vh.${accessor_method}(${coordinates_negative_index}${updated_value}, ${updated_value});
+ ${indexable_only} failUnreachable();
+ ${indexable_only} } catch (IndexOutOfBoundsException ex) {}""")
elif accessor.access_mode_form == AccessModeForm.GET_AND_UPDATE_NUMERIC:
if var_type.supports_numeric == True:
expansions['binop'] = accessor.get_java_numeric_operator()
@@ -651,11 +697,21 @@
${var_type} old_value = (${var_type}) vh.${accessor_method}(${coordinates}${updated_value});
assertEquals(${initial_value}, old_value);
${var_type} expected_value = (${var_type}) (${initial_value} ${binop} ${updated_value});
- assertEquals(expected_value, ${read_value});""")
+ assertEquals(expected_value, ${read_value});
+ // Check for out of bounds access (for indexable types only).
+ ${indexable_only} try {
+ ${indexable_only} old_value = (${var_type}) vh.${accessor_method}(${coordinates_negative_index}${updated_value});
+ ${indexable_only} failUnreachable();
+ ${indexable_only} } catch (IndexOutOfBoundsException ex) {}""")
else:
test_template = Template("""
vh.${accessor_method}(${coordinates}${initial_value}, ${updated_value});
- failUnreachable();""")
+ failUnreachable();
+ // Check for out of bounds access (for indexable types only).
+ ${indexable_only} try {
+ ${indexable_only} vh.${accessor_method}(${coordinates_negative_index}${updated_value}, ${updated_value});
+ ${indexable_only} failUnreachable();
+ ${indexable_only} } catch (IndexOutOfBoundsException ex) {}""")
else:
raise ValueError(accessor.access_mode_form)