oracle/graal · error · IllegalMemoryAccessException
Memory access is outside the boundaries of the allocated mem
Error message
Memory access is outside the boundaries of the allocated memory region
What it means
Thrown by Espresso's ChunkedNativeMemory when a guest memory access (get/put of byte/short/int/long etc.) falls outside the bounds of the allocated chunk. The check is 'byteIndex < 0 || byteIndex > length - accessByteSize', so both negative offsets and offsets that would make a multi-byte access cross the end of the region are rejected. This is Espresso's equivalent of the JVM's AIOOBE for native/foreign memory, mapped to a guest IndexOutOfBoundsException-style error.
Source
Thrown at espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/memory/ChunkedNativeMemory.java:302
validateAccess(chunk, chunkOffset + bytes, 0);
return wrapChunk(chunk, chunkOffset, bytes);
}
/**
* Validates the memory access and as a side effect enforces int lengths for byte-array chunks
* by using {@link ChunkedNativeMemory#getChunkSize(Object)}.
*
* @param chunk the chunk that is accessed.
* @param byteIndex the biggest index accessed in the chunk.
* @param accessByteSize the byte size of the access.
*/
protected void validateAccess(T chunk, long byteIndex, int accessByteSize) throws IllegalMemoryAccessException {
validateAccess(getChunkSize(chunk), byteIndex, accessByteSize);
}
protected void validateAccess(long length, long byteIndex, int accessByteSize) throws IllegalMemoryAccessException {
if (byteIndex < 0 || byteIndex > length - accessByteSize) {
throw new IllegalMemoryAccessException("Memory access is outside the boundaries of the allocated memory region");
}
}
protected abstract void putByteImpl(T chunk, long chunkOffset, byte value, MemoryAccessMode accessMode);
protected abstract void putShortImpl(T chunk, long chunkOffset, short value, MemoryAccessMode accessMode);
protected abstract void putIntImpl(T chunk, long chunkOffset, int value, MemoryAccessMode accessMode);
protected abstract void putLongImpl(T chunk, long chunkOffset, long value, MemoryAccessMode accessMode);
protected abstract byte getByteImpl(T chunk, long chunkOffset, MemoryAccessMode accessMode);
protected abstract short getShortImpl(T chunk, long chunkOffset, MemoryAccessMode accessMode);
protected abstract int getIntImpl(T chunk, long chunkOffset, MemoryAccessMode accessMode);
protected abstract long getLongImpl(T chunk, long chunkOffset, MemoryAccessMode accessMode);View on GitHub (pinned to a66e9ccd1d)
Solutions
- Check byteIndex >= 0 && byteIndex + accessByteSize <= region size before the access (use getChunkSize(chunk) or the tracked length).
- Audit loops over native memory for off-by-one bounds (<= vs <) and for the width of the widest access (long = 8 bytes).
- If the offset comes from guest computation, validate/overflow-check it at the boundary before converting to a chunk offset.
- If the chunk was reallocated or freed, refresh the chunk reference and re-read its size.
Example fix
// before
memory.getInt(chunk, offset, MemoryAccessMode.PLAIN);
// after
if (offset < 0 || offset > ChunkedNativeMemory.getChunkSize(chunk) - 4) {
throw new IndexOutOfBoundsException("offset " + offset + " out of bounds");
}
memory.getInt(chunk, offset, MemoryAccessMode.PLAIN); Defensive patterns
Strategy: validation
Validate before calling
// before any chunk access: byteIndex in [0, size - accessByteSize]
long size = ChunkedNativeMemory.getChunkSize(chunk);
if (byteIndex < 0 || byteIndex > size - accessByteSize) {
throw new IndexOutOfBoundsException("offset=" + byteIndex + " size=" + size + " access=" + accessByteSize);
} Try / catch
try {
memory.getInt(chunk, byteIndex, MemoryAccessMode.PLAIN);
} catch (IllegalMemoryAccessException e) {
// bounds are caller-controlled: report, do not retry with the same offset
throw new IndexOutOfBoundsException(e.getMessage());
} Prevention
- Always validate offset plus access width (1/2/4/8) against the chunk size before access.
- Use long arithmetic for offsets to avoid int overflow wrapping negative.
- After any reallocation, re-read the chunk size before further access.
- Encapsulate raw chunk access behind helpers that always bounds-check.
When it happens
Trigger: Calling putByte/putInt/getLong (or any accessor on ChunkedNativeMemory) with an index+accessSize that exceeds getChunkSize(chunk); guest code indexing past the end of a direct ByteBuffer-backed chunk; computing an offset with int overflow that wraps negative; accessing a freed/reallocated chunk whose size shrank.
Common situations: Guest Java code doing manual pointer arithmetic over a native buffer, off-by-one loops over array-like native memory, using a stale chunk reference after reallocation, or truncation bugs where a long offset is cast to int before the call.
Related errors
- Branch target %d out of bounds
- Not starting with digits: {size}
- Unit prefix can be at most one character: {size}
- Unrecognized unit prefix: {size} use `T`, `G`, `M`, or `k`.
- arrayOffset is less than baseOffset
AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14).
Data as JSON: /api/errors/aeada71d67e3b4a8.
Report an issue: GitHub.