Java 17 bug (?) on Windows with MemorySegment.asSlice and offset =

Maurizio Cimadamore maurizio.cimadamore at oracle.com
Thu May 12 11:19:35 UTC 2022


I've reproduced on Windows.

One thing I noticed is that using file sizes that are a power of two 
will get rid of errors.

So, using this:

```
raf.setLength(1L << 33); //~8gb
```

Works w/o any issues.

I think I managed to get to the bottom of it. I started looking at how 
mapped files were implemented on Windows, and I notice that 
CreateFileMapping do not accept (unlike Linux mmap) a 8-byte value for 
the size/offset. Instead, they split the 8-byte value into two DWORDs 
(high and low). That said, that logic seemed to be handled correctly in 
the JDK code:

```
jlong maxSize = off + len;
     jint lowLen = (jint)(maxSize);
     jint highLen = (jint)(maxSize >> 32);
```

But on Windows, you first get a map handle, and _then_ you get a map 
"view". To get the map view, you have to specify a size again, but this 
time the size is a SIZE_T; this is _not_ handled correcty in the JDK:

```

mapAddress = MapViewOfFile(
         mapping,             /* Handle of file mapping object */
         mapAccess,           /* Read and write access */
         highOffset,          /* High word of offset */
         lowOffset,           /* Low word of offset */
         (DWORD)len);         /* Number of bytes to map */ <------

```

That cast seems questionable, as DWORD is 32-bit, so casting an 8-byte 
value bigger than 2^32 into that will surely fail.

Of course, this was never an issue with ByteBuffer, as you could never 
create such a big memory mapped region. But with memory segments this 
becomes problematic.

Fixing the cast to use SIZE_T instead solves the issue you are seeing.

Thanks
Maurizio

On 12/05/2022 10:54, Maurizio Cimadamore wrote:
> I have spotted something...
>
> The code that creates a mapped buffer is like this:
>
> ```
> return Util.newMappedByteBufferR((int)unmapper.cap,
>                     unmapper.address + unmapper.pagePosition,
>                     unmapper.fd,
>                     unmapper, isSync);
> ```
>
> While the code that creates the mapped segment is like this:
>
> ```
> AbstractMemorySegmentImpl segment = new 
> MappedMemorySegmentImpl(unmapper.address(), unmapper, size,
>                     modes, session);
> ```
>
> While the two are similar, there are some differences:
>
> * the BB version is using unmapper.cap for the size, while the segment 
> version is just passing the size - on further inspection, this 
> difference seems benign, as unmapper.cap seems to be always set to the 
> user specified byte size
> * the BB address is set to unmapper.address + pagePosition - now this 
> is an actual difference between the two snippets - the memory segment 
> version ignores pagePosition.
>
> The second difference seems like a bug - that said, it should only 
> manifest when mapping a file with a non-zero offset, as pagePosition 
> is computed as follows:
>
> ```
> pagePosition = (int)(position % allocationGranularity);
> ```
>
> (where `position` is really the memory mapped offset). That is, if the 
> specified offset is zero, the base address of the mapped segment 
> should always be page-aligned.
>
> So, I don't think this issue, alone, (while a bug) is enough to 
> explain what's going on.
>
> Maurizio
>
>
> On 12/05/2022 10:32, Maurizio Cimadamore wrote:
>> Uhm... this seems worse.
>>
>> Something seems to point at the spatial bounds of the segment not 
>> being set correctly.
>>
>> Maurizio
>>
>> On 12/05/2022 09:45, erel at eth.gl wrote:
>>> (sorry for the frequent messages)
>>>
>>>
>>> In the previous example the exception was thrown from the “force” 
>>> call. A low level error happens with similar code:
>>>
>>> long offset = 3704800000L;
>>>
>>>              ByteBuffer bb = ByteBuffer.allocateDirect(100000);
>>>
>>>              MemorySegment mbb = MemorySegment.ofByteBuffer(bb);
>>>
>>>              while (true) {
>>>
>>>                    System.out.println("offset: " + offset);
>>>
>>>                    MemorySegment target = 
>>> mappedMemorySegment.asSlice(offset, 100000);
>>>
>>>                    offset = offset + 100000;
>>>
>>>                    target.copyFrom(mbb);
>>>
>>>              }
>>>
>>>
>>> Output:
>>>
>>>
>>> WARNING: Using incubator modules: jdk.incubator.foreign
>>>
>>> 64
>>>
>>> amd64
>>>
>>> 17.0.3.1
>>>
>>> byteSize: 8000000000
>>>
>>> offset: 3704800000
>>>
>>> offset: 3704900000
>>>
>>> offset: 3705000000
>>>
>>> #
>>>
>>> # A fatal error has been detected by the Java Runtime Environment:
>>>
>>> #
>>>
>>> #  EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x0000027a6c4f4176, 
>>> pid=16060, tid=2208
>>>
>>> #
>>>
>>> # JRE version: Java(TM) SE Runtime Environment (17.0.3.1+2) (build 
>>> 17.0.3.1+2-LTS-6)
>>>
>>> # Java VM: Java HotSpot(TM) 64-Bit Server VM (17.0.3.1+2-LTS-6, 
>>> mixed mode, sharing, tiered, compressed oops, compressed class ptrs, 
>>> g1 gc, windows-amd64)
>>>
>>> # Problematic frame:
>>>
>>> # v  ~StubRoutines::jlong_disjoint_arraycopy
>>>
>>> #
>>>
>>> # No core dump will be written. Minidumps are not enabled by default 
>>> on client versions of Windows
>>>
>>> #
>>>
>>> # An error report file with more information is saved as:
>>>
>>> # …\hs_err_pid16060.log
>>>
>>> #
>>>
>>> # If you would like to submit a bug report, please visit:
>>>
>>> #   https://bugreport.java.com/bugreport/crash.jsp
>>>
>>> #
>>>
>>>


More information about the panama-dev mailing list