Skip to content

Commit

Permalink
Automatic merge of master into galahad
Browse files Browse the repository at this point in the history
  • Loading branch information
OracleLabsAutomation committed Jul 31, 2024
2 parents df47b71 + 41c2c2b commit b72e5e9
Show file tree
Hide file tree
Showing 4 changed files with 128 additions and 20 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*
* Copyright (c) 2024, 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.graal.compiler.truffle.test.strings;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Method;
import java.util.List;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;

import jdk.graal.compiler.nodes.StructuredGraph;
import jdk.graal.compiler.replacements.nodes.CalcStringAttributesNode;

@RunWith(Parameterized.class)
public class TStringOpsCalcStringAttributesLatin1ConstantRegionOOBTest extends TStringOpsConstantTest<CalcStringAttributesNode> {

private static final MethodHandle calcLatin1;

static {
try {
Method methodLatin1 = T_STRING_OPS_CLASS.getDeclaredMethod("calcStringAttributesLatin1", com.oracle.truffle.api.nodes.Node.class, Object.class, int.class, int.class);
methodLatin1.setAccessible(true);
calcLatin1 = MethodHandles.lookup().unreflect(methodLatin1);
} catch (NoSuchMethodException | IllegalAccessException e) {
throw new ExceptionInInitializerError(e);
}
}

@Parameters(name = "{index}: offset={0}, length={1}")
public static List<Object[]> data() {
return List.of(
// (offset + length) >= array.length
new Object[]{1, 3},
// (offset + length) integer overflow
new Object[]{Integer.MAX_VALUE, 3},
new Object[]{-1, 2},
new Object[]{2, -1});
}

public TStringOpsCalcStringAttributesLatin1ConstantRegionOOBTest(int offset, int length) {
super(CalcStringAttributesNode.class, new byte[]{'a', 'b', 'c', -1}, offset, length);
}

@Test
public void testConstantRegionOutOfBounds() throws Throwable {
setConstantArgs(arrayA, offsetA, lengthA);
// Make calcStringAttributesLatin1 branch reachable (must be in bounds)
runTestMethod(arrayA, 2, 2, true);
// Run and compile with constant offset + length out of bounds in the not taken branch.
runTestMethod(arrayA, offsetA, lengthA, false);
test("runTestMethod", arrayA, offsetA, lengthA, false);
}

public static long runTestMethod(Object array, int offset, int length, boolean condition) throws Throwable {
if (condition) {
return (int) calcLatin1.invokeExact(DUMMY_LOCATION, array, offset, length);
} else {
return -1;
}
}

@Override
protected void checkLowTierGraph(StructuredGraph graph) {
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, 2024, 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 @@ -29,7 +29,6 @@
import jdk.graal.compiler.core.common.GraalOptions;
import jdk.graal.compiler.core.common.Stride;
import jdk.graal.compiler.debug.Assertions;
import jdk.graal.compiler.debug.GraalError;
import jdk.graal.compiler.nodes.ConstantNode;
import jdk.graal.compiler.nodes.NodeView;
import jdk.graal.compiler.nodes.ValueNode;
Expand Down Expand Up @@ -76,6 +75,8 @@ public static boolean shouldConstantFoldArrayOperation(CanonicalizerTool tool, i
* as the array element's bit width, e.g. reading a {@code char} value from a {@code byte}
* array.
*
* @param typePunnedOffset array offset in relation to the type punned element size, e.g.
* "number of char elements in this byte array".
* @param typePunnedLength array length in relation to the type punned element size, e.g.
* "number of char elements in this byte array".
* @param typePunnedStride type punned array element size.
Expand All @@ -84,16 +85,16 @@ public static boolean shouldConstantFoldArrayOperation(CanonicalizerTool tool, i
* @param arrayKind actual array kind, e.g. {@link JavaKind#Byte} for a {@code byte} array we
* want to read a {@code char} from.
*/
public static boolean boundsCheckTypePunned(int typePunnedLength, Stride typePunnedStride, int arrayLength, JavaKind arrayKind) {
return typePunnedLength * typePunnedStride.value <= arrayLength * arrayKind.getByteCount();
}

public static void boundsCheckTypePunnedGuarantee(int typePunnedLength, Stride typePunnedStride, int arrayLength, JavaKind arrayKind) {
GraalError.guarantee(boundsCheckTypePunned(typePunnedLength, typePunnedStride, arrayLength, arrayKind), Assertions.errorMessageContext(
"typePunnedLength", typePunnedLength,
"typePunnedStride", typePunnedStride.value,
"arrayLength", arrayLength,
"arrayKind", arrayKind));
public static boolean boundsCheckTypePunned(long typePunnedOffset, int typePunnedLength, Stride typePunnedStride, int arrayLength, JavaKind arrayKind) {
/*
* Note: A simple (offset + length |<=| array.length) check does not protect against illegal
* negative offset or length values for which (offset + length) might appear to be in bounds
* while the start of the range is out of bounds or greater than the end.
*/
long arrayByteLength = (long) arrayLength * arrayKind.getByteCount();
long byteOffset = typePunnedOffset * typePunnedStride.value;
long byteLength = (long) typePunnedLength * typePunnedStride.value;
return Long.compareUnsigned(byteOffset + byteLength, arrayByteLength) <= 0 && byteOffset >= 0 && byteLength >= 0;
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013, 2024, 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 @@ -30,6 +30,7 @@

import org.graalvm.word.LocationIdentity;

import jdk.graal.compiler.core.common.NumUtil;
import jdk.graal.compiler.core.common.Stride;
import jdk.graal.compiler.core.common.spi.ForeignCallDescriptor;
import jdk.graal.compiler.core.common.type.StampFactory;
Expand Down Expand Up @@ -305,12 +306,26 @@ public Node canonical(CanonicalizerTool tool) {
// arrayOffset is given in bytes, scale it to the stride.
long arrayBaseOffsetBytesConstant = arrayOffset.asJavaConstant().asLong();
arrayBaseOffsetBytesConstant -= getArrayBaseOffset(tool.getMetaAccess(), arrayPointer, constantArrayKind);
final int arrayOffsetConstant = (int) (arrayBaseOffsetBytesConstant / stride.value);
final long arrayOffsetConstantScaled = arrayBaseOffsetBytesConstant >> stride.log2;

final int arrayLengthConstant = arrayLength.asJavaConstant().asInt();
ConstantReflectionUtil.boundsCheckTypePunnedGuarantee(arrayLengthConstant, stride, actualArrayLength, constantArrayKind);

final int fromIndexConstant = fromIndex.asJavaConstant().asInt();
if (!ConstantReflectionUtil.boundsCheckTypePunned(arrayOffsetConstantScaled, arrayLengthConstant, stride, actualArrayLength, constantArrayKind) ||
(fromIndexConstant < 0 || fromIndexConstant > arrayLengthConstant)) {
/*
* One or more arguments is out of bounds for the given constant array.
*
* This may happen when this node is in a branch that won't be taken for the given
* constant input values, but is still visible in the current compilation unit.
* Since this isn't necessarily an error, we want to handle this case gracefully and
* simply give up on trying to constant-fold. Explicit range checks preceding the
* intrinsic call will take care of throwing an exception. Alternatively, we could
* deoptimize here.
*/
return this;
}
final int arrayOffsetConstant = NumUtil.safeToInt(arrayOffsetConstantScaled);

final int[] valuesConstant = new int[searchValues.size()];
for (int i = 0; i < searchValues.size(); i++) {
valuesConstant[i] = searchValues.get(i).asJavaConstant().asInt();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2021, 2024, 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 @@ -47,6 +47,7 @@

import org.graalvm.word.LocationIdentity;

import jdk.graal.compiler.core.common.NumUtil;
import jdk.graal.compiler.core.common.Stride;
import jdk.graal.compiler.core.common.spi.ForeignCallDescriptor;
import jdk.graal.compiler.core.common.type.StampFactory;
Expand Down Expand Up @@ -179,11 +180,10 @@ public Node canonical(CanonicalizerTool tool) {
// arrayOffset is given in bytes, scale it to the stride.
long arrayBaseOffsetBytesConstant = offset.asJavaConstant().asLong();
arrayBaseOffsetBytesConstant -= tool.getMetaAccess().getArrayBaseOffset(constantArrayKind);
final int offsetConstant = (int) (arrayBaseOffsetBytesConstant / stride.value);
final long offsetConstantScaled = arrayBaseOffsetBytesConstant >> stride.log2;

final int lengthConstant = length.asJavaConstant().asInt();

if (!ConstantReflectionUtil.boundsCheckTypePunned(lengthConstant, stride, actualArrayLength, constantArrayKind)) {
if (!ConstantReflectionUtil.boundsCheckTypePunned(offsetConstantScaled, lengthConstant, stride, actualArrayLength, constantArrayKind)) {
/*
* This may happen when this node is in a branch that won't be taken for the given
* array, but is still visible in the current compilation unit, e.g. for compact
Expand All @@ -208,6 +208,7 @@ public Node canonical(CanonicalizerTool tool) {
*/
return this;
}
final int offsetConstant = NumUtil.safeToInt(offsetConstantScaled);

if (ConstantReflectionUtil.shouldConstantFoldArrayOperation(tool, lengthConstant)) {
switch (encoding) {
Expand Down

0 comments on commit b72e5e9

Please sign in to comment.