|
27 | 27 |
|
28 | 28 | import static jdk.graal.compiler.core.common.spi.ForeignCallDescriptor.CallSideEffect.HAS_SIDE_EFFECT; |
29 | 29 |
|
| 30 | +import java.lang.reflect.Array; |
30 | 31 | import java.util.HashMap; |
31 | 32 | import java.util.List; |
32 | 33 | import java.util.Map; |
@@ -106,29 +107,40 @@ public static byte readArrayByte(Object o, long offset) { |
106 | 107 | int scaledOffset = getScaledOffset(o, offset); |
107 | 108 | int indexScale = getArrayIndexScale(o); |
108 | 109 | int index = scaledOffset / indexScale; |
109 | | - /* |
110 | | - * The raw bits read from the array expanded to a long. The indexScale number of least |
111 | | - * significant bytes contain the actual data, the rest is not relevant. |
112 | | - */ |
113 | | - long longBits = switch (o) { |
114 | | - case boolean[] bools -> bools[index] ? 1 : 0; |
115 | | - case byte[] bytes -> bytes[index]; |
116 | | - case short[] shorts -> shorts[index]; |
117 | | - case char[] chars -> chars[index]; |
118 | | - case int[] ints -> ints[index]; |
119 | | - case long[] longs -> longs[index]; |
120 | | - case float[] floats -> Float.floatToRawIntBits(floats[index]); |
121 | | - case double[] doubles -> Double.doubleToRawLongBits(doubles[index]); |
122 | | - default -> { |
123 | | - if (WasmGCUnsafeSupport.includeErrorMessage()) { |
124 | | - WasmGCUnsafeSupport.fatalAccessError(o, "Unsupported type for unaligned array access", offset, true); |
| 110 | + if (inWordRemainder(o, offset)) { |
| 111 | + return 0; |
| 112 | + } |
| 113 | + try { |
| 114 | + /* |
| 115 | + * The raw bits read from the array expanded to a long. The indexScale number of least |
| 116 | + * significant bytes contain the actual data, the rest is not relevant. |
| 117 | + */ |
| 118 | + long longBits = switch (o) { |
| 119 | + case boolean[] bools -> bools[index] ? 1 : 0; |
| 120 | + case byte[] bytes -> bytes[index]; |
| 121 | + case short[] shorts -> shorts[index]; |
| 122 | + case char[] chars -> chars[index]; |
| 123 | + case int[] ints -> ints[index]; |
| 124 | + case long[] longs -> longs[index]; |
| 125 | + case float[] floats -> Float.floatToRawIntBits(floats[index]); |
| 126 | + case double[] doubles -> Double.doubleToRawLongBits(doubles[index]); |
| 127 | + default -> { |
| 128 | + if (WasmGCUnsafeSupport.includeErrorMessage()) { |
| 129 | + WasmGCUnsafeSupport.fatalAccessError(o, "Unsupported type for unaligned array access", offset, true); |
| 130 | + } |
| 131 | + throw new UnsupportedOperationException(); |
125 | 132 | } |
126 | | - throw new UnsupportedOperationException(); |
| 133 | + }; |
| 134 | + |
| 135 | + int rightShift = 8 * elementByteOffset(scaledOffset, indexScale); |
| 136 | + return (byte) (longBits >> rightShift); |
| 137 | + } catch (ArrayIndexOutOfBoundsException e) { |
| 138 | + if (WasmGCUnsafeSupport.includeErrorMessage()) { |
| 139 | + WasmGCUnsafeSupport.fatalAccessError(o, e.toString(), offset, true); |
127 | 140 | } |
128 | | - }; |
129 | 141 |
|
130 | | - int rightShift = 8 * elementByteOffset(scaledOffset, indexScale); |
131 | | - return (byte) (longBits >> rightShift); |
| 142 | + throw e; |
| 143 | + } |
132 | 144 | } |
133 | 145 |
|
134 | 146 | @SubstrateForeignCallTarget(stubCallingConvention = false) |
@@ -208,21 +220,35 @@ public static void writeArrayByte(Object o, long offset, byte value) { |
208 | 220 | int index = scaledOffset / indexScale; |
209 | 221 | // Byte offset within the array element value |
210 | 222 | int valueOffset = elementByteOffset(scaledOffset, indexScale); |
211 | | - switch (o) { |
212 | | - case boolean[] bools -> bools[index] = value != 0; |
213 | | - case byte[] bytes -> bytes[index] = value; |
214 | | - case short[] shorts -> shorts[index] = (short) setByte(shorts[index], valueOffset, value); |
215 | | - case char[] chars -> chars[index] = (char) setByte(chars[index], valueOffset, value); |
216 | | - case int[] ints -> ints[index] = (int) setByte(ints[index], valueOffset, value); |
217 | | - case long[] longs -> longs[index] = setByte(longs[index], valueOffset, value); |
218 | | - case float[] floats -> floats[index] = Float.intBitsToFloat((int) setByte(Float.floatToRawIntBits(floats[index]), valueOffset, value)); |
219 | | - case double[] doubles -> doubles[index] = Double.longBitsToDouble(setByte(Double.doubleToRawLongBits(doubles[index]), valueOffset, value)); |
220 | | - default -> { |
221 | | - if (WasmGCUnsafeSupport.includeErrorMessage()) { |
222 | | - WasmGCUnsafeSupport.fatalAccessError(o, "Unsupported type for unaligned array access", offset, true); |
| 223 | + |
| 224 | + if (inWordRemainder(o, offset)) { |
| 225 | + return; |
| 226 | + } |
| 227 | + try { |
| 228 | + switch (o) { |
| 229 | + case boolean[] bools -> bools[index] = value != 0; |
| 230 | + case byte[] bytes -> bytes[index] = value; |
| 231 | + case short[] shorts -> shorts[index] = (short) setByte(shorts[index], valueOffset, value); |
| 232 | + case char[] chars -> chars[index] = (char) setByte(chars[index], valueOffset, value); |
| 233 | + case int[] ints -> ints[index] = (int) setByte(ints[index], valueOffset, value); |
| 234 | + case long[] longs -> longs[index] = setByte(longs[index], valueOffset, value); |
| 235 | + case float[] floats -> |
| 236 | + floats[index] = Float.intBitsToFloat((int) setByte(Float.floatToRawIntBits(floats[index]), valueOffset, value)); |
| 237 | + case double[] doubles -> |
| 238 | + doubles[index] = Double.longBitsToDouble(setByte(Double.doubleToRawLongBits(doubles[index]), valueOffset, value)); |
| 239 | + default -> { |
| 240 | + if (WasmGCUnsafeSupport.includeErrorMessage()) { |
| 241 | + WasmGCUnsafeSupport.fatalAccessError(o, "Unsupported type for unaligned array access", offset, false); |
| 242 | + } |
| 243 | + throw new UnsupportedOperationException(); |
223 | 244 | } |
224 | | - throw new UnsupportedOperationException(); |
225 | 245 | } |
| 246 | + } catch (ArrayIndexOutOfBoundsException e) { |
| 247 | + if (WasmGCUnsafeSupport.includeErrorMessage()) { |
| 248 | + WasmGCUnsafeSupport.fatalAccessError(o, e.toString(), offset, false); |
| 249 | + } |
| 250 | + |
| 251 | + throw e; |
226 | 252 | } |
227 | 253 | } |
228 | 254 |
|
@@ -343,4 +369,23 @@ private static int getArrayBaseOffset(JavaKind kind) { |
343 | 369 | private static int getArrayIndexScale(JavaKind kind) { |
344 | 370 | return ImageSingletons.lookup(ObjectLayout.class).getArrayIndexScale(kind); |
345 | 371 | } |
| 372 | + |
| 373 | + /** |
| 374 | + * Detect out of bounds access that is still within the same 4-byte aligned 4-byte word as the |
| 375 | + * last element. Such accesses are usually undefined behaviour, but to not produce unexpected |
| 376 | + * behavior (i.e. a crash), out of bounds accesses where this method returns {@code true} should |
| 377 | + * not crash and just return an arbitrary value and ignore any writes. |
| 378 | + * <p> |
| 379 | + * {@code jdk.internal.misc.Unsafe#compareAndExchangeByte} uses int accesses that may go out of |
| 380 | + * bounds and does not expect a crash in that case, though it ignores the out-of-bounds bytes. |
| 381 | + * For example, there might be an int access at the first element of a 1-element byte array. |
| 382 | + * |
| 383 | + */ |
| 384 | + private static boolean inWordRemainder(Object o, long offset) { |
| 385 | + int scaledOffset = getScaledOffset(o, offset); |
| 386 | + int indexScale = getArrayIndexScale(o); |
| 387 | + int index = scaledOffset / indexScale; |
| 388 | + int length = Array.getLength(o); |
| 389 | + return index >= length && scaledOffset < ((index * indexScale + 4) & ~0x03); |
| 390 | + } |
346 | 391 | } |
0 commit comments