Skip to content

Commit 0444539

Browse files
committed
[GR-61998] Prevent walking image heap continuations with unpatched references and carrier threads unexpectedly leaking into frames.
PullRequest: graal/20053
2 parents 47bb651 + 2958b90 commit 0444539

File tree

5 files changed

+51
-17
lines changed

5 files changed

+51
-17
lines changed

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/StoredContinuationAccess.java

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
*/
2525
package com.oracle.svm.core.heap;
2626

27+
import static com.oracle.svm.core.Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE;
28+
2729
import org.graalvm.nativeimage.ImageSingletons;
2830
import org.graalvm.nativeimage.StackValue;
2931
import org.graalvm.nativeimage.c.function.CodePointer;
@@ -169,10 +171,24 @@ private static void setIP(StoredContinuation cont, CodePointer ip) {
169171
cont.ip = ip;
170172
}
171173

174+
@Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true)
175+
public static boolean shouldWalkContinuation(StoredContinuation s) {
176+
/*
177+
* StoredContinuations in the image heap are read-only and should not be visited. They may
178+
* contain unresolved uncompressed references which are patched only on a platform thread's
179+
* stack before resuming the continuation. We should typically not see such objects during
180+
* GC, but they might be visited due to card marking when they are near an object which has
181+
* been modified at runtime.
182+
*/
183+
return !Heap.getHeap().isInImageHeap(s);
184+
}
185+
172186
@AlwaysInline("De-virtualize calls to ObjectReferenceVisitor")
173187
@Uninterruptible(reason = "StoredContinuation must not move.", callerMustBe = true)
174188
public static boolean walkReferences(StoredContinuation s, ObjectReferenceVisitor visitor) {
175-
assert !Heap.getHeap().isInImageHeap(s) : "StoredContinuations in the image heap are read-only and don't need to be visited";
189+
if (!shouldWalkContinuation(s)) {
190+
return true;
191+
}
176192

177193
JavaStackWalk walk = StackValue.get(JavaStackWalker.sizeOfJavaStackWalk());
178194
JavaStackWalker.initializeForContinuation(walk, s);
@@ -199,7 +215,9 @@ public static boolean walkReferences(StoredContinuation s, ObjectReferenceVisito
199215
@AlwaysInline("De-virtualize calls to visitor.")
200216
@Uninterruptible(reason = "StoredContinuation must not move.", callerMustBe = true)
201217
public static void walkFrames(StoredContinuation s, ContinuationStackFrameVisitor visitor, ContinuationStackFrameVisitorData data) {
202-
assert !Heap.getHeap().isInImageHeap(s) : "StoredContinuations in the image heap are read-only and don't need to be visited";
218+
if (!shouldWalkContinuation(s)) {
219+
return;
220+
}
203221

204222
JavaStackWalk walk = StackValue.get(JavaStackWalker.sizeOfJavaStackWalk());
205223
JavaStackWalker.initializeForContinuation(walk, s);

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/ContinuationInternals.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
*/
2525
package com.oracle.svm.core.thread;
2626

27-
import jdk.graal.compiler.word.Word;
2827
import org.graalvm.nativeimage.ImageSingletons;
2928
import org.graalvm.nativeimage.c.function.CodePointer;
3029
import org.graalvm.word.Pointer;
@@ -39,6 +38,8 @@
3938
import com.oracle.svm.core.stack.StackOverflowCheck;
4039
import com.oracle.svm.core.util.VMError;
4140

41+
import jdk.graal.compiler.word.Word;
42+
4243
/** Implementation of and access to {@link Target_jdk_internal_vm_Continuation} internals. */
4344
@InternalVMMethod
4445
public final class ContinuationInternals {
@@ -162,4 +163,14 @@ private static Integer doYield1(Target_jdk_internal_vm_Continuation c) {
162163
KnownIntrinsics.farReturn(null, returnSP, returnIP, false);
163164
throw VMError.shouldNotReachHereAtRuntime();
164165
}
166+
167+
/**
168+
* Avoids references to the current carrier thread from leaking into the caller frame, which
169+
* prevents persisting continuation stacks in (auxiliary) image heaps.
170+
*/
171+
@NeverInline("Prevent a reference to the current carrier thread from leaking into the caller frame.")
172+
static Target_jdk_internal_vm_Continuation getContinuationFromCarrier() {
173+
Target_java_lang_Thread carrier = JavaThreads.toTarget(Target_java_lang_Thread.currentCarrierThread());
174+
return carrier.cont;
175+
}
165176
}

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/JavaThreads.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
import java.util.concurrent.atomic.AtomicInteger;
3232
import java.util.concurrent.atomic.AtomicLong;
3333

34-
import jdk.graal.compiler.word.Word;
3534
import org.graalvm.nativeimage.CurrentIsolate;
3635
import org.graalvm.nativeimage.ImageSingletons;
3736
import org.graalvm.nativeimage.IsolateThread;
@@ -62,6 +61,7 @@
6261
import jdk.graal.compiler.core.common.SuppressFBWarnings;
6362
import jdk.graal.compiler.replacements.ReplacementsUtil;
6463
import jdk.graal.compiler.serviceprovider.JavaVersionUtil;
64+
import jdk.graal.compiler.word.Word;
6565

6666
/**
6767
* Implements operations on {@linkplain Target_java_lang_Thread Java threads}, which are on a higher
@@ -209,6 +209,7 @@ public static boolean isCurrentThreadVirtual() {
209209
* Indicates whether the current thread is <em>truly</em> virtual (see {@link #isVirtual}) and
210210
* currently pinned to its carrier thread.
211211
*/
212+
@NeverInline("Prevent a reference to the current carrier thread from leaking into the caller frame.")
212213
public static boolean isCurrentThreadVirtualAndPinned() {
213214
Target_java_lang_Thread carrier = JavaThreads.toTarget(Target_java_lang_Thread.currentCarrierThread());
214215
return carrier != null && carrier.vthread != null && Target_jdk_internal_vm_Continuation.isPinned(carrier.cont.getScope());

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/Target_java_lang_Thread.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -463,7 +463,13 @@ private static void clearInterruptEvent() {
463463
@Alias //
464464
native void clearInterrupt();
465465

466-
/** Carrier threads only: the current innermost continuation. */
466+
/**
467+
* Carrier threads only: the current innermost continuation.
468+
*
469+
* Use {@link ContinuationInternals#getContinuationFromCarrier()} instead to avoid references to
470+
* the current carrier thread from leaking into a stack frame, which prevents persisting
471+
* continuation stacks in (auxiliary) image heaps.
472+
*/
467473
@Alias //
468474
Target_jdk_internal_vm_Continuation cont;
469475

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/Target_jdk_internal_vm_Continuation.java

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,7 @@ boolean isEmpty() {
9292
@Substitute
9393
@NeverInline("access stack pointer")
9494
private static int isPinned0(Target_jdk_internal_vm_ContinuationScope scope) {
95-
Target_java_lang_Thread carrier = JavaThreads.toTarget(Target_java_lang_Thread.currentCarrierThread());
96-
Target_jdk_internal_vm_Continuation cont = carrier.cont;
95+
Target_jdk_internal_vm_Continuation cont = ContinuationInternals.getContinuationFromCarrier();
9796
if (cont != null) {
9897
while (true) {
9998
if (cont.pinCount != 0) {
@@ -148,8 +147,7 @@ static void enterSpecial(Target_jdk_internal_vm_Continuation c, @SuppressWarning
148147

149148
@Substitute
150149
private static int doYield() {
151-
Target_java_lang_Thread carrier = JavaThreads.toTarget(Target_java_lang_Thread.currentCarrierThread());
152-
Target_jdk_internal_vm_Continuation cont = carrier.cont;
150+
Target_jdk_internal_vm_Continuation cont = ContinuationInternals.getContinuationFromCarrier();
153151
int pinnedReason = isPinned0(cont.getScope());
154152
if (pinnedReason != 0) {
155153
return pinnedReason;
@@ -181,23 +179,23 @@ void enter0() {
181179

182180
@Substitute
183181
public static void pin() {
184-
Target_java_lang_Thread carrier = JavaThreads.toTarget(Target_java_lang_Thread.currentCarrierThread());
185-
if (carrier.cont != null) {
186-
if (carrier.cont.pinCount + 1 == 0) { // unsigned arithmetic
182+
Target_jdk_internal_vm_Continuation cont = ContinuationInternals.getContinuationFromCarrier();
183+
if (cont != null) {
184+
if (cont.pinCount + 1 == 0) { // unsigned arithmetic
187185
throw new IllegalStateException("Pin overflow");
188186
}
189-
carrier.cont.pinCount++;
187+
cont.pinCount++;
190188
}
191189
}
192190

193191
@Substitute
194192
public static void unpin() {
195-
Target_java_lang_Thread carrier = JavaThreads.toTarget(Target_java_lang_Thread.currentCarrierThread());
196-
if (carrier.cont != null) {
197-
if (carrier.cont.pinCount == 0) {
193+
Target_jdk_internal_vm_Continuation cont = ContinuationInternals.getContinuationFromCarrier();
194+
if (cont != null) {
195+
if (cont.pinCount == 0) {
198196
throw new IllegalStateException("Pin underflow");
199197
}
200-
carrier.cont.pinCount--;
198+
cont.pinCount--;
201199
}
202200
}
203201

0 commit comments

Comments
 (0)