Skip to content

Commit 6a07e93

Browse files
committed
Add $destroyed flag to wrapper types to prevent use-after-free bugs
1 parent 5c585e8 commit 6a07e93

File tree

2 files changed

+28
-9
lines changed

2 files changed

+28
-9
lines changed

SwiftKit/src/main/java/org/swift/swiftkit/SwiftInstance.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
import java.lang.foreign.GroupLayout;
1818
import java.lang.foreign.MemorySegment;
19+
import java.util.concurrent.atomic.AtomicBoolean;
1920

2021
public interface SwiftInstance {
2122

@@ -39,4 +40,13 @@ public interface SwiftInstance {
3940
default boolean isReferenceType() {
4041
return this instanceof SwiftHeapObject;
4142
}
43+
44+
/**
45+
* Exposes a boolean value which can be used to indicate if the object was destroyed.
46+
* <p/>
47+
* This is exposing the object, rather than performing the action because we don't want to accidentally
48+
* form a strong reference to the {@code SwiftInstance} which could prevent the cleanup from running,
49+
* if using an GC managed instance (e.g. using an {@link AutoSwiftMemorySession}.
50+
*/
51+
AtomicBoolean $statusDestroyedFlag();
4252
}

SwiftKit/src/test/java/org/swift/swiftkit/AutoArenaTest.java

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,11 @@
1818

1919
import java.lang.foreign.GroupLayout;
2020
import java.lang.foreign.MemorySegment;
21-
import java.lang.ref.Cleaner;
2221
import java.util.concurrent.CountDownLatch;
22+
import java.util.concurrent.atomic.AtomicBoolean;
2323

2424
public class AutoArenaTest {
2525

26-
2726
@Test
2827
@SuppressWarnings("removal") // System.runFinalization() will be removed
2928
public void cleaner_releases_native_resource() {
@@ -34,16 +33,21 @@ public void cleaner_releases_native_resource() {
3433

3534
// we're retaining the `object`, register it with the arena:
3635
AutoSwiftMemorySession arena = (AutoSwiftMemorySession) SwiftArena.ofAuto();
37-
arena.register(object, new SwiftHeapObjectCleanup(object.$memorySegment(), object.$swiftType()) {
38-
@Override
39-
public void run() throws UnexpectedRetainCountException {
40-
cleanupLatch.countDown();
41-
}
42-
});
36+
37+
var statusDestroyedFlag = object.$statusDestroyedFlag();
38+
Runnable markAsDestroyed = () -> statusDestroyedFlag.set(true);
39+
40+
arena.register(object,
41+
new SwiftHeapObjectCleanup(object.$memorySegment(), object.$swiftType(), markAsDestroyed) {
42+
@Override
43+
public void run() throws UnexpectedRetainCountException {
44+
cleanupLatch.countDown();
45+
}
46+
});
4347

4448
// Release the object and hope it gets GC-ed soon
4549

46-
//noinspection UnusedAssignment
50+
// noinspection UnusedAssignment
4751
object = null;
4852

4953
var i = 1_000;
@@ -76,5 +80,10 @@ public FakeSwiftHeapObject() {
7680
public SwiftAnyType $swiftType() {
7781
return null;
7882
}
83+
84+
@Override
85+
public AtomicBoolean $statusDestroyedFlag() {
86+
return new AtomicBoolean();
87+
}
7988
}
8089
}

0 commit comments

Comments
 (0)