Can access closed memory segment

Maurizio Cimadamore maurizio.cimadamore at oracle.com
Tue Jun 18 09:28:59 UTC 2024


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.

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).

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.

Maurizio

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/60c9cb76/attachment.htm>


More information about the panama-dev mailing list