diff --git a/src/java.base/share/classes/java/lang/foreign/MemorySegment.java b/src/java.base/share/classes/java/lang/foreign/MemorySegment.java index 9fadc2f0f56..74658a5bbf6 100644 --- a/src/java.base/share/classes/java/lang/foreign/MemorySegment.java +++ b/src/java.base/share/classes/java/lang/foreign/MemorySegment.java @@ -761,8 +761,13 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * arena: that is, if the provided arena is a {@linkplain Arena#ofConfined() confined arena}, * the returned segment can only be accessed by the arena's owner thread, regardless * of the confinement restrictions associated with this segment. In other words, this - * method returns a segment that behaves as if it had been allocated using the - * provided arena. + * method returns a segment that can be used as any other segment allocated using the + * provided arena. However, The returned segment is backed by the same memory region + * as that of the original segment. As such, the region of memory backing the + * returned segment is deallocated only when this segment's arena is closed. + * This might lead to use-after-free issues, as the returned segment can be + * accessed after its region of memory has been deallocated via this + * segment's arena. *

* Clients can specify an optional cleanup action that should be executed when the * provided scope becomes invalid. This cleanup action receives a fresh memory @@ -811,9 +816,14 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl { * compatibly with the confinement restrictions associated with the provided arena: * that is, if the provided arena is a {@linkplain Arena#ofConfined() confined arena}, * the returned segment can only be accessed by the arena's owner thread, regardless - * of the confinement restrictions associated with this segment. In other words, - * this method returns a segment that behaves as if it had been allocated using the - * provided arena. + * of the confinement restrictions associated with this segment. In other words, this + * method returns a segment that can be used as any other segment allocated using the + * provided arena. However, The returned segment is backed by the same memory region + * as that of the original segment. As such, the region of memory backing the + * returned segment is deallocated only when this segment's arena is closed. + * This might lead to use-after-free issues, as the returned segment can be + * accessed after its region of memory has been deallocated via this + * segment's arena. *

* Clients can specify an optional cleanup action that should be executed when the * provided scope becomes invalid. This cleanup action receives a fresh memory diff --git a/test/jdk/java/foreign/TestSegments.java b/test/jdk/java/foreign/TestSegments.java index b361abac3df..a76bc69fbd8 100644 --- a/test/jdk/java/foreign/TestSegments.java +++ b/test/jdk/java/foreign/TestSegments.java @@ -42,6 +42,7 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.function.IntFunction; import java.util.function.Supplier; +import static java.lang.foreign.ValueLayout.JAVA_BYTE; import static java.lang.foreign.ValueLayout.JAVA_INT; import static org.testng.Assert.*; @@ -392,6 +393,24 @@ public class TestSegments { assertEquals(counter.get(), 3); } + @Test + void testReinterpretArenaClose() { + MemorySegment segment; + try (Arena arena = Arena.ofConfined()){ + try (Arena otherArena = Arena.ofConfined()) { + segment = arena.allocate(100); + segment = segment.reinterpret(otherArena, null); + } + final MemorySegment sOther = segment; + assertThrows(IllegalStateException.class, () -> sOther.get(JAVA_BYTE, 0)); + segment = segment.reinterpret(arena, null); + final MemorySegment sOriginal = segment; + sOriginal.get(JAVA_BYTE, 0); + } + final MemorySegment closed = segment; + assertThrows(IllegalStateException.class, () -> closed.get(JAVA_BYTE, 0)); + } + @Test void testThrowInCleanup() { AtomicInteger counter = new AtomicInteger();