{"record":{"id":"aeada71d67e3b4a8","repo":"oracle/graal","slug":"memory-access-is-outside-the-boundaries-of-the-all","errorCode":null,"errorMessage":"Memory access is outside the boundaries of the allocated memory region","messagePattern":"Memory access is outside the boundaries of the allocated memory region","errorType":"exception","errorClass":"IllegalMemoryAccessException","httpStatus":null,"severity":"error","filePath":"espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/memory/ChunkedNativeMemory.java","lineNumber":302,"sourceCode":"        validateAccess(chunk, chunkOffset + bytes, 0);\n        return wrapChunk(chunk, chunkOffset, bytes);\n    }\n\n    /**\n     * Validates the memory access and as a side effect enforces int lengths for byte-array chunks\n     * by using {@link ChunkedNativeMemory#getChunkSize(Object)}.\n     *\n     * @param chunk the chunk that is accessed.\n     * @param byteIndex the biggest index accessed in the chunk.\n     * @param accessByteSize the byte size of the access.\n     */\n    protected void validateAccess(T chunk, long byteIndex, int accessByteSize) throws IllegalMemoryAccessException {\n        validateAccess(getChunkSize(chunk), byteIndex, accessByteSize);\n    }\n\n    protected void validateAccess(long length, long byteIndex, int accessByteSize) throws IllegalMemoryAccessException {\n        if (byteIndex < 0 || byteIndex > length - accessByteSize) {\n            throw new IllegalMemoryAccessException(\"Memory access is outside the boundaries of the allocated memory region\");\n        }\n    }\n\n    protected abstract void putByteImpl(T chunk, long chunkOffset, byte value, MemoryAccessMode accessMode);\n\n    protected abstract void putShortImpl(T chunk, long chunkOffset, short value, MemoryAccessMode accessMode);\n\n    protected abstract void putIntImpl(T chunk, long chunkOffset, int value, MemoryAccessMode accessMode);\n\n    protected abstract void putLongImpl(T chunk, long chunkOffset, long value, MemoryAccessMode accessMode);\n\n    protected abstract byte getByteImpl(T chunk, long chunkOffset, MemoryAccessMode accessMode);\n\n    protected abstract short getShortImpl(T chunk, long chunkOffset, MemoryAccessMode accessMode);\n\n    protected abstract int getIntImpl(T chunk, long chunkOffset, MemoryAccessMode accessMode);\n\n    protected abstract long getLongImpl(T chunk, long chunkOffset, MemoryAccessMode accessMode);","sourceCodeStart":284,"sourceCodeEnd":320,"githubUrl":"https://github.com/oracle/graal/blob/a66e9ccd1d7bf2552883939aa0788dfd0e294aab/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/memory/ChunkedNativeMemory.java#L284-L320","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nmemory.getInt(chunk, offset, MemoryAccessMode.PLAIN);\n\n// after\nif (offset < 0 || offset > ChunkedNativeMemory.getChunkSize(chunk) - 4) {\n    throw new IndexOutOfBoundsException(\"offset \" + offset + \" out of bounds\");\n}\nmemory.getInt(chunk, offset, MemoryAccessMode.PLAIN);","handlingStrategy":"validation","validationCode":"// before any chunk access: byteIndex in [0, size - accessByteSize]\nlong size = ChunkedNativeMemory.getChunkSize(chunk);\nif (byteIndex < 0 || byteIndex > size - accessByteSize) {\n    throw new IndexOutOfBoundsException(\"offset=\" + byteIndex + \" size=\" + size + \" access=\" + accessByteSize);\n}","typeGuard":null,"tryCatchPattern":"try {\n    memory.getInt(chunk, byteIndex, MemoryAccessMode.PLAIN);\n} catch (IllegalMemoryAccessException e) {\n    // bounds are caller-controlled: report, do not retry with the same offset\n    throw new IndexOutOfBoundsException(e.getMessage());\n}","preventionTips":["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."],"tags":["memory","bounds-check","native","espresso"],"backgroundTag":null,"analyzedSha":"a66e9ccd1d7bf2552883939aa0788dfd0e294aab","analyzedAt":"2026-08-14T13:58:47.161Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}