-
Bug
-
Resolution: Fixed
-
P3
-
None
-
repo-panama
When passing a struct like the following to native code:
struct S{
short f0;
short f1;
short f2;
};
We get an IndexOutOfBoundsException on SysV (with -XX:+ShowHiddenFrames):
java.lang.IndexOutOfBoundsException: Out of bound access on segment MemorySegment{ array: Optional.empty address:140675014129552 limit: 6 }; new offset = 0; new length = 8
at java.base/jdk.internal.foreign.AbstractMemorySegmentImpl.outOfBoundException(AbstractMemorySegmentImpl.java:421)
at java.base/jdk.internal.foreign.AbstractMemorySegmentImpl.apply(AbstractMemorySegmentImpl.java:402)
at java.base/jdk.internal.foreign.AbstractMemorySegmentImpl.apply(AbstractMemorySegmentImpl.java:69)
at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:98)
at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckIndex(Preconditions.java:124)
at java.base/jdk.internal.util.Preconditions.checkIndex(Preconditions.java:448)
at java.base/jdk.internal.foreign.AbstractMemorySegmentImpl.checkBounds(AbstractMemorySegmentImpl.java:391)
at java.base/jdk.internal.foreign.AbstractMemorySegmentImpl.checkAccess(AbstractMemorySegmentImpl.java:351)
at java.base/java.lang.invoke.VarHandleSegmentAsLongs.checkAddress(VarHandleSegmentAsLongs.java:81)
at java.base/java.lang.invoke.VarHandleSegmentAsLongs.get(VarHandleSegmentAsLongs.java:108)
at java.base/java.lang.invoke.VarHandleGuards.guard_LJ_J(VarHandleGuards.java:230)
at java.base/java.lang.foreign.MemorySegment.get(MemorySegment.java:1572)
at java.base/jdk.internal.foreign.abi.DowncallStub/0x00000008010aec00.invoke(Unknown Source)
at java.base/java.lang.invoke.LambdaForm$DMH/0x00000008010af400.invokeStaticInit(LambdaForm$DMH)
at java.base/java.lang.invoke.LambdaForm$MH/0x00000008010be000.invoke(LambdaForm$MH)
at java.base/java.lang.invoke.LambdaForm$MH/0x00000008010bac00.invoke(LambdaForm$MH)
at java.base/java.lang.invoke.Invokers$Holder.invokeExact_MT(Invokers$Holder)
at java.base/java.lang.invoke.MethodHandle.invokeWithArguments(MethodHandle.java:733)
at TestOOB.testOOB(TestOOB.java:71)
The exact conditions for this are passing a struct by-value whose size is not a power of two. When the linker tries to load the value of this struct, it will try to use an 8 byte load, but this will fail, since the segment is only 6 bytes in size.
GCC will use an over-sized 8 byte load when calling a function that accepts this struct:
mov rdi, QWORD PTR [rsp+8]
call F
And when returning, GCC generates some kind of masking to make sure only 6 bytes are written:
F:
movabs rdx, 0xFFFF00000000
mov eax, edi
and rdi, rdx
or rax, rdi
ret
To fix this, we might need to do an unchecked load of the value (and mask + unchecked write for returns)
struct S{
short f0;
short f1;
short f2;
};
We get an IndexOutOfBoundsException on SysV (with -XX:+ShowHiddenFrames):
java.lang.IndexOutOfBoundsException: Out of bound access on segment MemorySegment{ array: Optional.empty address:140675014129552 limit: 6 }; new offset = 0; new length = 8
at java.base/jdk.internal.foreign.AbstractMemorySegmentImpl.outOfBoundException(AbstractMemorySegmentImpl.java:421)
at java.base/jdk.internal.foreign.AbstractMemorySegmentImpl.apply(AbstractMemorySegmentImpl.java:402)
at java.base/jdk.internal.foreign.AbstractMemorySegmentImpl.apply(AbstractMemorySegmentImpl.java:69)
at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:98)
at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckIndex(Preconditions.java:124)
at java.base/jdk.internal.util.Preconditions.checkIndex(Preconditions.java:448)
at java.base/jdk.internal.foreign.AbstractMemorySegmentImpl.checkBounds(AbstractMemorySegmentImpl.java:391)
at java.base/jdk.internal.foreign.AbstractMemorySegmentImpl.checkAccess(AbstractMemorySegmentImpl.java:351)
at java.base/java.lang.invoke.VarHandleSegmentAsLongs.checkAddress(VarHandleSegmentAsLongs.java:81)
at java.base/java.lang.invoke.VarHandleSegmentAsLongs.get(VarHandleSegmentAsLongs.java:108)
at java.base/java.lang.invoke.VarHandleGuards.guard_LJ_J(VarHandleGuards.java:230)
at java.base/java.lang.foreign.MemorySegment.get(MemorySegment.java:1572)
at java.base/jdk.internal.foreign.abi.DowncallStub/0x00000008010aec00.invoke(Unknown Source)
at java.base/java.lang.invoke.LambdaForm$DMH/0x00000008010af400.invokeStaticInit(LambdaForm$DMH)
at java.base/java.lang.invoke.LambdaForm$MH/0x00000008010be000.invoke(LambdaForm$MH)
at java.base/java.lang.invoke.LambdaForm$MH/0x00000008010bac00.invoke(LambdaForm$MH)
at java.base/java.lang.invoke.Invokers$Holder.invokeExact_MT(Invokers$Holder)
at java.base/java.lang.invoke.MethodHandle.invokeWithArguments(MethodHandle.java:733)
at TestOOB.testOOB(TestOOB.java:71)
The exact conditions for this are passing a struct by-value whose size is not a power of two. When the linker tries to load the value of this struct, it will try to use an 8 byte load, but this will fail, since the segment is only 6 bytes in size.
GCC will use an over-sized 8 byte load when calling a function that accepts this struct:
mov rdi, QWORD PTR [rsp+8]
call F
And when returning, GCC generates some kind of masking to make sure only 6 bytes are written:
F:
movabs rdx, 0xFFFF00000000
mov eax, edi
and rdi, rdx
or rax, rdi
ret
To fix this, we might need to do an unchecked load of the value (and mask + unchecked write for returns)