Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions src/hotspot/share/jvmci/jvmciCompilerToVM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2253,14 +2253,21 @@ static jobject read_field_value(Handle obj, long displacement, jchar type_char,
if (!aligned) {
JVMCI_THROW_MSG_NULL(IllegalArgumentException, "read is unaligned");
}
if (obj->is_array()) {
// Disallow reading after the last element of an array
size_t array_length = arrayOop(obj())->length();
int lh = obj->klass()->layout_helper();
size_t size_in_bytes = array_length << Klass::layout_helper_log2_element_size(lh);
size_in_bytes += Klass::layout_helper_header_size(lh);
if ((size_t) displacement + basic_type_elemsize > size_in_bytes) {
JVMCI_THROW_MSG_NULL(IllegalArgumentException, "reading after last array element");
}
}
if (basic_type == T_OBJECT) {
if (obj->is_objArray()) {
if (displacement < arrayOopDesc::base_offset_in_bytes(T_OBJECT)) {
JVMCI_THROW_MSG_NULL(IllegalArgumentException, "reading from array header");
}
if (displacement + heapOopSize > arrayOopDesc::base_offset_in_bytes(T_OBJECT) + arrayOop(obj())->length() * heapOopSize) {
JVMCI_THROW_MSG_NULL(IllegalArgumentException, "reading after last array element");
}
if (((displacement - arrayOopDesc::base_offset_in_bytes(T_OBJECT)) % heapOopSize) != 0) {
JVMCI_THROW_MSG_NULL(IllegalArgumentException, "misaligned object read from array");
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2025, 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
Expand Down Expand Up @@ -41,11 +41,7 @@
import java.lang.reflect.Array;
import java.util.List;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.*;

/**
* Tests for {@link ConstantReflectionProvider}. It assumes an implementation of the interface that
Expand Down Expand Up @@ -138,4 +134,42 @@ public void unboxTest() {

assertNull(constantReflection.unboxPrimitive(JavaConstant.NULL_POINTER));
}

static class ArrayConstants {
static final byte[] BYTE_ARRAY_CONST = new byte[]{0};
static final Object[] OBJECT_ARRAY_CONST = new Object[]{null};
}

@Test
public void readOnePastLastArrayElementTest() {
for (ConstantValue cv : readConstants(ArrayConstants.class)) {
if (cv.boxed != null && cv.boxed.getClass().isArray()) {
JavaKind kind = metaAccess.lookupJavaType(cv.value).getComponentType().getJavaKind();
long offset = metaAccess.getArrayBaseOffset(kind) + (long) metaAccess.getArrayIndexScale(kind) * Array.getLength(cv.boxed);
Copy link
Member

@dougxc dougxc Apr 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understand correctly, this tests a read of an element one past the end of the array.
Can you please also add a test for a read that is partially out-of-bounds:

long offset = 1 + metaAccess.getArrayBaseOffset(kind) + (long) metaAccess.getArrayIndexScale(kind) * (Array.getLength(cv.boxed) - 1);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a test for a long read from array[array.index - 1] because adding + 1 would make the read unaligned (which is also not allowed). Please check it out.

// read array[array.length]
assertThrows(IllegalArgumentException.class, () -> {
if (kind == JavaKind.Object) {
constantReflection.getMemoryAccessProvider().readObjectConstant(cv.value, offset);
} else {
constantReflection.getMemoryAccessProvider().readPrimitiveConstant(kind, cv.value, offset, kind.getBitCount());
}
});
}
}
}

static class IntArrayConstants {
static final int[] INT_ARRAY_CONST = new int[]{0};
}

@Test
public void readPartiallyOutOfBoundsTest() {
for (ConstantValue cv : readConstants(IntArrayConstants.class)) {
JavaKind kind = metaAccess.lookupJavaType(cv.value).getComponentType().getJavaKind();
long offset = metaAccess.getArrayBaseOffset(kind) + (long) metaAccess.getArrayIndexScale(kind) * (Array.getLength(cv.boxed) - 1);
// read a long from array[array.length - 1], which is partially out of bounds
JavaKind accessKind = JavaKind.Long;
assertThrows(IllegalArgumentException.class, () -> constantReflection.getMemoryAccessProvider().readPrimitiveConstant(accessKind, cv.value, offset, accessKind.getBitCount()));
}
}
}