ADDITIONAL SYSTEM INFORMATION :
openjdk version "25.0.1" 2025-10-21
OpenJDK Runtime Environment (build 25.0.1)
OpenJDK 64-Bit Server VM (build 25.0.1, mixed mode, sharing)
Arch Linux x86_64
A DESCRIPTION OF THE PROBLEM :
I've encountered a case where openjdk blows up with a spurious InternalError from in-bounds getAtIndex access on a mapped MemorySegment.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
A fairly small reproducer test case is provided below.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The test case does not touch out of bounds and should not fail
ACTUAL -
MappedMemorySegmentImpl
start: 0
end: 4294967094
13418024976 is where we blew up (at long index 1677253122)
34359736832 is the size of the segment
java.lang.InternalError: a fault occurred in an unsafe memory access operation
at nu.marginalia.btree.BTreeWriterTest.isSortedN(BTreeWriterTest.java:74)
at nu.marginalia.btree.BTreeWriterTest.wtf(BTreeWriterTest.java:56)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
at java.base/java.lang.reflect.Method.invoke(Method.java:565)
at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:725)
at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149)
---------- BEGIN SOURCE ----------
@Test
void test() {
Path file = Path.of("/tmp/btree.dat");
try (var arena = Arena.ofConfined();
var fc = (FileChannel) Files.newByteChannel(file, StandardOpenOption.READ, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE)
) {
MemorySegment ms = fc.map(FileChannel.MapMode.READ_WRITE, 0, 34359736832L, arena);
isSortedN(ms, 2, 0, 4294967094L);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
boolean isSortedN(MemorySegment segment, int wordSize, long start, long end) {
if (start == end) return true;
System.out.println(segment.getClass().getSimpleName());
System.out.println("start: " + start);
System.out.println("end: " + end);
if (end * 8L >= segment.byteSize())
throw new IllegalArgumentException("End too big");
long i = 0;
try {
long val = segment.getAtIndex(JAVA_LONG, 0);
for (i = start + wordSize; i < end; i += wordSize) {
long next;
try {
next = segment.getAtIndex(JAVA_LONG, i);
} catch (IndexOutOfBoundsException ex) {
throw new IndexOutOfBoundsException("@" + i + "(" + 0 + ":" + segment.byteSize() / 8 + ")");
}
if (next < val)
return false;
val = next;
}
}
catch (InternalError error) {
System.err.println((8*i) + " is where we blew up (at long index " + i + ")");
System.err.println((segment.byteSize()) + " is the size of the segment");
throw error;
}
return true;
}
---------- END SOURCE ----------
openjdk version "25.0.1" 2025-10-21
OpenJDK Runtime Environment (build 25.0.1)
OpenJDK 64-Bit Server VM (build 25.0.1, mixed mode, sharing)
Arch Linux x86_64
A DESCRIPTION OF THE PROBLEM :
I've encountered a case where openjdk blows up with a spurious InternalError from in-bounds getAtIndex access on a mapped MemorySegment.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
A fairly small reproducer test case is provided below.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The test case does not touch out of bounds and should not fail
ACTUAL -
MappedMemorySegmentImpl
start: 0
end: 4294967094
13418024976 is where we blew up (at long index 1677253122)
34359736832 is the size of the segment
java.lang.InternalError: a fault occurred in an unsafe memory access operation
at nu.marginalia.btree.BTreeWriterTest.isSortedN(BTreeWriterTest.java:74)
at nu.marginalia.btree.BTreeWriterTest.wtf(BTreeWriterTest.java:56)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
at java.base/java.lang.reflect.Method.invoke(Method.java:565)
at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:725)
at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149)
---------- BEGIN SOURCE ----------
@Test
void test() {
Path file = Path.of("/tmp/btree.dat");
try (var arena = Arena.ofConfined();
var fc = (FileChannel) Files.newByteChannel(file, StandardOpenOption.READ, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE)
) {
MemorySegment ms = fc.map(FileChannel.MapMode.READ_WRITE, 0, 34359736832L, arena);
isSortedN(ms, 2, 0, 4294967094L);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
boolean isSortedN(MemorySegment segment, int wordSize, long start, long end) {
if (start == end) return true;
System.out.println(segment.getClass().getSimpleName());
System.out.println("start: " + start);
System.out.println("end: " + end);
if (end * 8L >= segment.byteSize())
throw new IllegalArgumentException("End too big");
long i = 0;
try {
long val = segment.getAtIndex(JAVA_LONG, 0);
for (i = start + wordSize; i < end; i += wordSize) {
long next;
try {
next = segment.getAtIndex(JAVA_LONG, i);
} catch (IndexOutOfBoundsException ex) {
throw new IndexOutOfBoundsException("@" + i + "(" + 0 + ":" + segment.byteSize() / 8 + ")");
}
if (next < val)
return false;
val = next;
}
}
catch (InternalError error) {
System.err.println((8*i) + " is where we blew up (at long index " + i + ")");
System.err.println((segment.byteSize()) + " is the size of the segment");
throw error;
}
return true;
}
---------- END SOURCE ----------