Can access closed memory segment

forax at univ-mlv.fr forax at univ-mlv.fr
Tue Jun 18 09:48:57 UTC 2024


> From: "Maurizio Cimadamore" <maurizio.cimadamore at oracle.com>
> To: "Remi Forax" <forax at univ-mlv.fr>, "panama-dev" <panama-dev at openjdk.org>
> Sent: Tuesday, June 18, 2024 11:28:59 AM
> Subject: Re: Can access closed memory segment

> So… I have some questions on the test… I ran it here and all looks fine (e.g. no
> crash), which is I think what you are assuming to take as evidence of a bug.

> But… the update to the STATE variable is plain. E.g. STATE is not a volatile
> variable and yet you are using it to synchronize work across the
> closing/accessing threads.

> As an experiment, I made STATE volatile, and now the program reliably crashes
> with this:

> Exception in thread “Thread-0” java.lang.IllegalStateException: Already closed
> at
> java.base/jdk.internal.foreign.MemorySessionImpl.alreadyClosed(MemorySessionImpl.java:310)
> at
> java.base/jdk.internal.misc.ScopedMemoryAccess$ScopedAccessError.newRuntimeException(ScopedMemoryAccess.java:113)
> at
> java.base/jdk.internal.misc.ScopedMemoryAccess.getByte(ScopedMemoryAccess.java:502)
> at
> java.base/java.lang.invoke.VarHandleSegmentAsBytes.get(VarHandleSegmentAsBytes.java:108)
> at
> java.base/jdk.internal.foreign.AbstractMemorySegmentImpl.get(AbstractMemorySegmentImpl.java:728)
> at SharedArenaCloseBug.run(SharedArenaCloseBug.java:11)
> at java.base/java.lang.Thread.run(Thread.java:1570)

> Which seems correct for a use-after-free.

The problem of volatile is that it has a broad effect, reading a volatile forces all next read to be read from the RAM, so here it can be the effect of volatile. 

> I also noticed that STATE = 1 in the closing thread is set after starting the
> accessing thread.

> So, a possible execution order here is one where:

>    * the accessing thread doesn’t see the update to STATE = 1 at all (because,
>     plain read)
>     * the closing thread writes 42
>     * the accessing thread sees 42 (but still sees STATE = 0)
>     * the accessing thread completes
>     * the closing thread closes the segment

> Since your test rely on the fact that STATE should be “1” when value is written,
> and the segment is closed, I think a plain write/read is not sufficient to
> establish this kind of ordering between the two threads (and that would explain
> while adding a “volatile” on STATE “fixes” things).

yes, this is a possible ordering. 

> As further test, I’ve tweaked the accessing thread like this:
> if (STATE == 0 ) { ... } else {
>      System.out.println( "NOPE" );
> }

> And verified that “NOPE” is never printed. So the accessing thread doesn’t
> really seem to ever see STATE != 0.

yes, you are right, so the VM now optimizes the static non-final read, something that was not done in the past, 
so yes, my test is bogus, i'm less worry now. 

> Maurizio

Rémi 

> On 18/06/2024 08:54, Remi Forax wrote:

>> Hello,
>> I think i've found an issue in the current implementation of shared arena
>> close(),
>> when closing the scope the VM only checks if the arena is on stack but the
>> memory segment can be stored in a static final.

>> In the following code, i'm able to read the segment after the shared arena is
>> closed.

>> public class SharedArenaCloseBug {
>>   static final Arena SHARED = Arena.ofShared();
>>   static final MemorySegment SEGMENT = SHARED.allocate(16);
>>   static int STATE;

>>   void run() {
>>     for(;;) {
>>       if (STATE == 0) {
>>         var value = SEGMENT.get(ValueLayout.JAVA_BYTE, 0L);
>>         if (value == 42) {
>>           return;
>>         }
>>       }
>>     }
>>   }

>>   public static void main(String[] args) throws InterruptedException {
>>     var bug = new SharedArenaCloseBug();
>>     var thread = new Thread(bug::run);
>>     thread.start();
>>     Thread.sleep(100);
>>     STATE = 1;
>>     System.out.println("state 1");
>>     Thread.sleep(100);
>>     System.out.println("close");
>>     SEGMENT.set(ValueLayout.JAVA_BYTE, 0L, (byte) 42);
>>     SHARED.close();
>>     Thread.sleep(100);
>>     System.out.println("state 0");
>>     STATE = 0;
>>     Thread.sleep(100);
>>     System.out.println("join");
>>     thread.join();
>>   }
>> }

>> regards,
>> Rémi

>-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/panama-dev/attachments/20240618/1f5b2011/attachment-0001.htm>


More information about the panama-dev mailing list