Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8303017

Passing by-value structs whose size is not power of 2 doesn't work on all platforms

XMLWordPrintable

      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)

            jvernee Jorn Vernee
            jvernee Jorn Vernee
            Votes:
            0 Vote for this issue
            Watchers:
            5 Start watching this issue

              Created:
              Updated:
              Resolved: