8338731: MemoryLayout::offsetHandle can return a negative offset

Reviewed-by: pminborg, psandoz
This commit is contained in:
Maurizio Cimadamore 2024-08-28 10:22:34 +00:00
parent 2e174c6367
commit 1ff9ac7233
3 changed files with 22 additions and 9 deletions

View File

@ -574,7 +574,9 @@ public sealed interface MemoryLayout
* <p>
* For any given dynamic argument {@code x_i}, it must be that {@code 0 <= x_i < size_i},
* where {@code size_i} is the size of the open path element associated with {@code x_i}.
* Otherwise, the returned method handle throws {@link IndexOutOfBoundsException}.
* Otherwise, the returned method handle throws {@link IndexOutOfBoundsException}. Moreover,
* the value of {@code b} must be such that the computation for {@code offset} does not overflow,
* or the returned method handle throws {@link ArithmeticException}.
*
* @apiNote The returned method handle can be used to compute a layout offset,
* similarly to {@link #byteOffset(PathElement...)}, but more flexibly, as

View File

@ -66,7 +66,7 @@ public class LayoutPath {
private static final MethodHandle MH_SLICE_LAYOUT;
private static final MethodHandle MH_CHECK_ENCL_LAYOUT;
private static final MethodHandle MH_SEGMENT_RESIZE;
private static final MethodHandle MH_ADD;
private static final MethodHandle MH_ADD_EXACT;
static {
try {
@ -81,7 +81,7 @@ public class LayoutPath {
MethodType.methodType(void.class, MemorySegment.class, long.class, MemoryLayout.class));
MH_SEGMENT_RESIZE = lookup.findStatic(LayoutPath.class, "resizeSegment",
MethodType.methodType(MemorySegment.class, MemorySegment.class));
MH_ADD = lookup.findStatic(Long.class, "sum",
MH_ADD_EXACT = lookup.findStatic(Math.class, "addExact",
MethodType.methodType(long.class, long.class, long.class));
} catch (Throwable ex) {
throw new ExceptionInInitializerError(ex);
@ -244,15 +244,18 @@ public class LayoutPath {
}
public MethodHandle offsetHandle() {
MethodHandle mh = MethodHandles.insertArguments(MH_ADD, 0, offset);
MethodHandle mh = MH_ADD_EXACT;
for (int i = strides.length - 1; i >= 0; i--) {
MethodHandle collector = MethodHandles.insertArguments(MH_ADD_SCALED_OFFSET, 2, strides[i], bounds[i]);
// (J, ...) -> J to (J, J, ...) -> J
// i.e. new coord is prefixed. Last coord will correspond to innermost layout
mh = MethodHandles.collectArguments(mh, 0, collector);
// (J, J, ...) -> J to (J, J, J, ...) -> J
// 1. the leading argument is the base offset (externally provided).
// 2. index arguments are added. The last index correspond to the innermost layout.
// 3. overflow can only occur at the outermost layer, due to the final addition with the base offset.
// This is because the layout API ensures (by construction) that all offsets generated from layout paths
// are always < Long.MAX_VALUE.
mh = MethodHandles.collectArguments(mh, 1, collector);
}
return mh;
return MethodHandles.insertArguments(mh, 1, offset);
}
private MemoryLayout rootLayout() {

View File

@ -332,6 +332,14 @@ public class TestLayoutPaths {
}
}
@Test(dataProvider = "testLayouts", expectedExceptions = ArithmeticException.class)
public void testOffsetHandleOverflow(MemoryLayout layout, PathElement[] pathElements, long[] indexes,
long expectedByteOffset) throws Throwable {
MethodHandle byteOffsetHandle = layout.byteOffsetHandle(pathElements);
byteOffsetHandle = byteOffsetHandle.asSpreader(long[].class, indexes.length);
byteOffsetHandle.invoke(Long.MAX_VALUE, indexes);
}
@Test(dataProvider = "testLayouts")
public void testVarHandleBadSegment(MemoryLayout layout, PathElement[] pathElements, long[] indexes,
long expectedByteOffset) throws Throwable {