oracle/graal · error · ArrayIndexOutOfBoundsException
arrayOffset is less than baseOffset
Error message
arrayOffset is less than baseOffset
What it means
In the sun.misc.Unsafe substitution, boundsCheck validates a raw byte offset against an array's layout: offset = baseOffset + index * indexScale. If offset < arrayBaseOffset(klass), the computed index would be negative, and IllegalArrayAccessException('arrayOffset is less than baseOffset') is thrown instead of reading out of bounds.
Source
Thrown at espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/standard/Target_sun_misc_Unsafe.java:572
/**
* Checks if a memory access in a guest array is within bounds.
*
* @param o guest array object
* @param offset raw byte offset into array
* @param accessSize number of bytes to be accessed
* @param language the EspressoLanguage instance
* @throws IllegalArrayAccessException if the access is out of bounds
*/
private static void boundsCheck(
@JavaType(Object.class) StaticObject o, long offset, long accessSize, EspressoLanguage language) throws IllegalArrayAccessException {
// offset = baseOffset + index * indexScale
assert o.getKlass().isArray();
Klass klass = o.getKlass();
int baseOffset = arrayBaseOffset(klass);
int indexScale = arrayIndexScale(klass);
if (offset < baseOffset) {
throw new IllegalArrayAccessException("arrayOffset is less than baseOffset");
}
/*
* Ensure memory is aligned for operations like sub-word CAS that may temporarily access
* memory just beyond array bounds.
*/
int maxIndex = alignUpToIntBytes(baseOffset + o.length(language) * indexScale);
if (offset > maxIndex - accessSize) {
throw new IllegalArrayAccessException("arrayOffset is beyond array length");
}
}
/**
* Thrown when {@link #boundsCheck} fails due to an out-of-bounds access.
*/
private static class IllegalArrayAccessException extends Exception {
@Serial private static final long serialVersionUID = 1L;
IllegalArrayAccessException(String msg) {View on GitHub (pinned to a66e9ccd1d)
Solutions
- Recompute offsets with Unsafe.arrayBaseOffset(clazz) + index * Unsafe.arrayIndexScale(clazz) for the exact array class.
- Prefer VarHandle or java.nio buffers over raw Unsafe offsets.
- If a third-party library triggers it, check for an updated version compatible with Espresso's Unsafe semantics.
Example fix
// before long offset = 8L * i; // hardcoded, below baseOffset unsafe.getLong(arr, offset); // after long offset = ((long) unsafe.arrayBaseOffset(long[].class)) + i * unsafe.arrayIndexScale(long[].class); unsafe.getLong(arr, offset);
Defensive patterns
Strategy: validation
Validate before calling
long base = unsafe.arrayBaseOffset(arr.getClass());
long scale = unsafe.arrayIndexScale(arr.getClass());
if (offset < base) throw new IllegalArgumentException("offset below base"); // fail before Unsafe call Try / catch
try {
value = unsafe.getLong(arr, offset);
} catch (Exception e) { // IllegalArrayAccessException surfaces as guest AIOOBE-equivalent
// recompute offset with arrayBaseOffset/arrayIndexScale
} Prevention
- Always derive offsets from arrayBaseOffset/arrayIndexScale of the exact array class.
- Prefer VarHandles or arraycopy over hand-rolled Unsafe math.
When it happens
Trigger: Guest code calling Unsafe.getXxx/putXxx/compareAndSwap on an array with an offset below the array's base offset (e.g. offset 0 or a value computed for a different element type), or misuse of Unsafe.arrayIndexScale math.
Common situations: Libraries using sun.misc.Unsafe with hand-computed offsets (off-heap style code pointed at Java arrays); offsets obtained from one array type applied to another; deliberately hostile/probing code.
Related errors
- arrayOffset is beyond array length
- Branch target %d out of bounds
- Memory access is outside the boundaries of the allocated mem
- Update spans the word, not supported
- Invalid component type
AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14).
Data as JSON: /api/errors/a5b1249e982102c3.
Report an issue: GitHub.