VarHandles: add an array store exception check
Adds a missing check for stores to reference arrays.
Adds additional tests for exceptions for reference array stores.
Bug: 206407164
Test: art/test/run-test --host
Test: art/test/run-test --host --jvm
Change-Id: Ia13608b431edb2d70ba7c1addc3d0c9bdcc4357d
diff --git a/runtime/mirror/var_handle.cc b/runtime/mirror/var_handle.cc
index 8f17c58..6788dce 100644
--- a/runtime/mirror/var_handle.cc
+++ b/runtime/mirror/var_handle.cc
@@ -1697,6 +1697,29 @@
UNREACHABLE();
}
+bool ArrayElementVarHandle::CheckArrayStore(AccessMode access_mode,
+ ShadowFrameGetter getter,
+ ObjPtr<ObjectArray<Object>> array) {
+ // This method checks the element being inserted into the array is correctly assignable.
+ // NB This method assumes it is called from `ArrayElementVarHandle::Access()` and `getter`
+ // has already consumed the array and index arguments.
+ ObjPtr<Object> new_element;
+ switch (GetAccessModeTemplate(access_mode)) {
+ case AccessModeTemplate::kGet:
+ return true; // Not a store.
+ case AccessModeTemplate::kCompareAndExchange:
+ case AccessModeTemplate::kCompareAndSet:
+ getter.GetReference(); // Skip the comperand.
+ new_element = getter.GetReference();
+ break;
+ case AccessModeTemplate::kGetAndUpdate:
+ case AccessModeTemplate::kSet:
+ new_element = getter.GetReference();
+ break;
+ }
+ return array->CheckAssignable(new_element);
+}
+
bool ArrayElementVarHandle::Access(AccessMode access_mode,
ShadowFrame* shadow_frame,
const InstructionOperands* const operands,
@@ -1710,7 +1733,7 @@
return false;
}
- ObjPtr<Array> target_array(raw_array->AsArray());
+ ObjPtr<Array> target_array = raw_array->AsArray();
// The target array element is the second co-ordinate type preceeding var type arguments.
const int target_element = getter.Get();
@@ -1722,8 +1745,12 @@
const Primitive::Type primitive_type = GetVarType()->GetPrimitiveType();
switch (primitive_type) {
case Primitive::Type::kPrimNot: {
- MemberOffset target_element_offset =
- target_array->AsObjectArray<Object>()->OffsetOfElement(target_element);
+ ObjPtr<ObjectArray<Object>> object_array = target_array->AsObjectArray<Object>();
+ if (!CheckArrayStore(access_mode, getter, object_array)) {
+ DCHECK(Thread::Current()->IsExceptionPending());
+ return false;
+ }
+ MemberOffset target_element_offset = object_array->OffsetOfElement(target_element);
return FieldAccessor<ObjPtr<Object>>::Dispatch(access_mode,
target_array,
target_element_offset,
diff --git a/runtime/mirror/var_handle.h b/runtime/mirror/var_handle.h
index 864a53b..e5b807d 100644
--- a/runtime/mirror/var_handle.h
+++ b/runtime/mirror/var_handle.h
@@ -270,13 +270,17 @@
// The corresponding managed class in libart java.lang.invoke.ArrayElementVarHandle.
class MANAGED ArrayElementVarHandle : public VarHandle {
public:
- bool Access(AccessMode access_mode,
- ShadowFrame* shadow_frame,
- const InstructionOperands* const operands,
- JValue* result)
- REQUIRES_SHARED(Locks::mutator_lock_);
+ bool Access(AccessMode access_mode,
+ ShadowFrame* shadow_frame,
+ const InstructionOperands* const operands,
+ JValue* result) REQUIRES_SHARED(Locks::mutator_lock_);
private:
+ static bool CheckArrayStore(AccessMode access_mode,
+ ShadowFrameGetter getter,
+ ObjPtr<ObjectArray<Object>> array)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
friend class VarHandleTest;
DISALLOW_IMPLICIT_CONSTRUCTORS(ArrayElementVarHandle);
};
diff --git a/test/712-varhandle-invocations/expected-stdout.txt b/test/712-varhandle-invocations/expected-stdout.txt
index a274e0b..1ff1b30 100644
--- a/test/712-varhandle-invocations/expected-stdout.txt
+++ b/test/712-varhandle-invocations/expected-stdout.txt
@@ -3291,6 +3291,7 @@
TooFewArgumentsCausingWrongMethodTypeTest...OK
ReturnTypeCausingWrongMethodTypeTest...OK
UnsupportedAccessModePreemptsWrongMethodTypeExceptionTest...OK
+ArrayStoreTest...OK
FieldCoordinateTypeTest...OK
ArrayElementOutOfBoundsIndexTest...OK
ArrayElementBadIndexTypeTest...OK
@@ -3338,4 +3339,4 @@
SupertypeTest...OK
InterfaceTest...OK
ImplicitBoxingIntegerTest...OK
-3340 successes, 0 skips, 0 failures.
+3341 successes, 0 skips, 0 failures.
diff --git a/test/712-varhandle-invocations/src/VarHandleArrayTests.java b/test/712-varhandle-invocations/src/VarHandleArrayTests.java
new file mode 100644
index 0000000..19f840b
--- /dev/null
+++ b/test/712-varhandle-invocations/src/VarHandleArrayTests.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
+
+// These tests cover DoVarHandleInvokeCommon in interpreter_common.cc.
+
+public class VarHandleArrayTests {
+ public static class ArrayStoreTest extends VarHandleUnitTest {
+ private static final Integer ZERO = Integer.valueOf(0);
+ private static final Integer ONE = Integer.valueOf(1);
+ private static final Integer TWO = Integer.valueOf(2);
+
+ private final Integer[] values = new Integer[10];
+
+ private void testIntegerArrayVarHandle() {
+ final VarHandle vh = MethodHandles.arrayElementVarHandle(Integer[].class);
+
+ // AccessModeTemplate::kSet
+ vh.set(values, 0, ZERO);
+ assertEquals(0, values[0].intValue());
+ vh.set((Object[]) values, 1, ONE);
+ assertEquals(ONE, values[1]);
+ assertThrowsAIOBE(() -> vh.set(values, values.length, null));
+ assertThrowsCCE(() -> vh.set(values, 6, new Object()));
+ assertThrowsCCE(() -> vh.set((Object[]) values, 6, new Object()));
+ assertThrowsNPE(() -> vh.set((Integer[]) null, 6, ONE));
+ assertThrowsWMTE(() -> vh.set(values, 'c'));
+ assertThrowsWMTE(() -> vh.set((Object[]) values, 5, 'c'));
+
+ // AccessModeTemplate::kGetAndUpdate
+ assertEquals(ZERO, (Integer) vh.getAndSet(values, 0, ONE));
+ assertEquals(ONE, values[0]);
+ assertThrowsAIOBE(() -> vh.getAndSet(values, values.length, null));
+ assertThrowsCCE(() -> vh.getAndSet(values, 6, new Object()));
+ assertThrowsCCE(() -> vh.getAndSet((Object[]) values, 6, new Object()));
+ assertThrowsNPE(() -> vh.getAndSet((Integer[]) null, 6, ONE));
+ assertThrowsWMTE(() -> vh.getAndSet(values, 'c'));
+ assertThrowsWMTE(() -> vh.getAndSet((Object[]) values, 5, 'c'));
+
+ // AccessModeTemplate::kCompareAndExchange
+ assertEquals(ONE, (Integer) vh.compareAndExchange(values, 0, ONE, TWO));
+ assertEquals(TWO, values[0]);
+ assertEquals(TWO, (Integer) vh.compareAndExchange(values, 0, ONE, ZERO));
+ assertEquals(TWO, values[0]);
+ assertThrowsAIOBE(() -> vh.compareAndExchange(values, values.length, null, null));
+ assertThrowsCCE(() -> vh.compareAndExchange(values, 6, 6, new Object()));
+ assertThrowsCCE(() -> vh.compareAndExchange((Object[]) values, 6, 6, new Object()));
+ assertThrowsNPE(() -> vh.compareAndExchange((Integer[]) null, 6, ONE, ONE));
+ assertThrowsWMTE(() -> vh.compareAndExchange(values, null, 'c'));
+ assertThrowsWMTE(() -> vh.compareAndExchange((Object[]) values, 5, null, 'c'));
+
+ // AccessModeTemplate::kCompareAndSet
+ assertEquals(true, (boolean) vh.compareAndSet(values, 0, TWO, ONE));
+ assertEquals(ONE, values[0]);
+ assertEquals(false, (boolean) vh.compareAndSet(values, 0, ZERO, TWO));
+ assertEquals(ONE, values[0]);
+ assertThrowsAIOBE(() -> vh.compareAndSet(values, values.length, null, null));
+ assertThrowsCCE(() -> vh.compareAndSet(values, 6, 6, new Object()));
+ assertThrowsCCE(() -> vh.compareAndSet((Object[]) values, 6, 6, new Object()));
+ assertThrowsNPE(() -> vh.compareAndSet((Integer[]) null, 6, ONE, ONE));
+ assertThrowsWMTE(() -> vh.compareAndSet(values, null, 'c'));
+ assertThrowsWMTE(() -> vh.compareAndSet((Object[]) values, 5, null, 'c'));
+ }
+
+ private void testObjectArrayVarHandle() {
+ final VarHandle vho = MethodHandles.arrayElementVarHandle(Object[].class);
+
+ // AccessModeTemplate::kSet
+ vho.set(values, 0, ONE);
+ assertEquals(ONE, values[0]);
+ assertThrowsAIOBE(() -> vho.set(values, values.length, null));
+ assertThrowsASE(() -> vho.set(values, 0, new Object()));
+ assertThrowsASE(() -> vho.set(values, 0, "hello"));
+ assertThrowsNPE(() -> vho.set(null, 0, ZERO));
+ assertThrowsWMTE(() -> vho.set(0, ZERO));
+ assertThrowsWMTE(() -> vho.set(values, ZERO));
+
+ // AccessModeTemplate::kGetAndUpdate
+ assertEquals(ONE, vho.getAndSetAcquire(values, 0, TWO));
+ assertThrowsAIOBE(() -> vho.getAndSetRelease(values, values.length, null));
+ assertThrowsASE(() -> vho.getAndSet(values, 0, new Object()));
+ assertThrowsASE(() -> vho.getAndSet(values, 0, "hello"));
+ assertThrowsNPE(() -> vho.getAndSet(null, 0, ZERO));
+ assertThrowsWMTE(() -> vho.getAndSet(0, ZERO));
+ assertThrowsWMTE(() -> vho.getAndSet(values, ZERO));
+
+ // AccessModeTemplate::kCompareAndExchange
+ assertEquals(TWO, vho.compareAndExchange(values, 0, TWO, ZERO));
+ assertThrowsAIOBE(() -> vho.compareAndExchange(values, values.length, ONE, TWO));
+ assertThrowsASE(() -> vho.compareAndExchange(values, 0, ONE, new Object()));
+ assertThrowsASE(() -> vho.compareAndExchange(values, 0, ONE, "hello"));
+ assertThrowsNPE(() -> vho.compareAndExchange(null, 0, ONE, ZERO));
+ assertThrowsWMTE(() -> vho.compareAndExchange(0, ZERO, ONE));
+ assertThrowsWMTE(() -> vho.compareAndExchange(values, ONE, ZERO));
+
+ // AccessModeTemplate::kCompareAndSet
+ assertEquals(true, (boolean) vho.compareAndSet(values, 0, ZERO, ONE));
+ assertThrowsAIOBE(() -> vho.compareAndSet(values, values.length, ONE, TWO));
+ assertThrowsASE(() -> vho.compareAndSet(values, 0, ONE, new Object()));
+ assertThrowsASE(() -> vho.compareAndSet(values, 0, ONE, "hello"));
+ assertThrowsNPE(() -> vho.compareAndSet(null, 0, ONE, ZERO));
+ assertThrowsWMTE(() -> vho.compareAndSet(0, ZERO, ONE));
+ assertThrowsWMTE(() -> vho.compareAndSet(values, ONE, ZERO));
+ }
+
+ @Override
+ protected void doTest() throws Exception {
+ testIntegerArrayVarHandle();
+ testObjectArrayVarHandle();
+ }
+
+ public static void main(String[] args) {
+ new ArrayStoreTest().run();
+ }
+ }
+
+ public static void main(String[] args) {
+ ArrayStoreTest.main(args);
+ }
+}