Skip to content

Commit cfc913e

Browse files
committed
Fail gracefully when invoking method pointers to null or uncompiled methods.
1 parent 591cdec commit cfc913e

File tree

6 files changed

+95
-29
lines changed

6 files changed

+95
-29
lines changed
Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -43,16 +43,33 @@
4343
import com.oracle.svm.util.ReflectionUtil;
4444

4545
/**
46-
* Provides a stub method that can be used to fill otherwise unused vtable slots. Instead of a
47-
* segfault, this method provides a full diagnostic output with a stack trace.
46+
* Provides stub methods that can be used for uninitialized method pointers. Instead of a segfault,
47+
* the stubs provide full diagnostic output with a stack trace.
4848
*/
49-
public final class InvalidVTableEntryHandler {
50-
public static final Method HANDLER_METHOD = ReflectionUtil.lookupMethod(InvalidVTableEntryHandler.class, "invalidVTableEntryHandler");
51-
public static final String MSG = "Fatal error: Virtual method call used an illegal vtable entry that was seen as unused by the static analysis";
49+
public final class InvalidMethodPointerHandler {
50+
public static final Method INVALID_VTABLE_ENTRY_HANDLER_METHOD = ReflectionUtil.lookupMethod(InvalidMethodPointerHandler.class, "invalidVTableEntryHandler");
51+
public static final String INVALID_VTABLE_ENTRY_MSG = "Fatal error: Virtual method call used an illegal vtable entry that was seen as unused by the static analysis";
52+
53+
public static final Method METHOD_POINTER_INVALID_HANDLER_METHOD = ReflectionUtil.lookupMethod(InvalidMethodPointerHandler.class, "methodPointerInvalidHandler");
54+
public static final String METHOD_POINTER_INVALID_MSG = "Fatal error: Method pointer invoked on a method that is null, was not registered for compilation, or was not seen as invoked by the static analysis";
5255

5356
@StubCallingConvention
5457
@NeverInline("We need a separate frame that stores all registers")
5558
private static void invalidVTableEntryHandler() {
59+
Pointer callerSP = KnownIntrinsics.readCallerStackPointer();
60+
CodePointer callerIP = KnownIntrinsics.readReturnAddress();
61+
failFatally(callerSP, callerIP, INVALID_VTABLE_ENTRY_MSG);
62+
}
63+
64+
@StubCallingConvention
65+
@NeverInline("We need a separate frame that stores all registers")
66+
private static void methodPointerInvalidHandler() {
67+
Pointer callerSP = KnownIntrinsics.readCallerStackPointer();
68+
CodePointer callerIP = KnownIntrinsics.readReturnAddress();
69+
failFatally(callerSP, callerIP, METHOD_POINTER_INVALID_MSG);
70+
}
71+
72+
private static void failFatally(Pointer callerSP, CodePointer callerIP, String message) {
5673
VMThreads.StatusSupport.setStatusIgnoreSafepoints();
5774
StackOverflowCheck.singleton().disableStackOverflowChecksForFatalError();
5875

@@ -63,13 +80,11 @@ private static void invalidVTableEntryHandler() {
6380
* from the method that has the illegal vtable call. That can be helpful when debugging the
6481
* cause of the fatal error.
6582
*/
66-
Pointer callerSP = KnownIntrinsics.readCallerStackPointer();
67-
CodePointer callerIP = KnownIntrinsics.readReturnAddress();
6883
LogHandler logHandler = ImageSingletons.lookup(LogHandler.class);
69-
Log log = Log.enterFatalContext(logHandler, callerIP, MSG, null);
84+
Log log = Log.enterFatalContext(logHandler, callerIP, message, null);
7085
if (log != null) {
7186
SubstrateDiagnostics.printFatalError(log, callerSP, callerIP, WordFactory.nullPointer(), true);
72-
log.string(MSG).newline();
87+
log.string(message).newline();
7388
}
7489
logHandler.fatalError();
7590
}

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ClassInitializationFeature.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
import org.graalvm.compiler.graph.Node;
4545
import org.graalvm.compiler.options.OptionValues;
4646
import org.graalvm.compiler.phases.util.Providers;
47+
import org.graalvm.nativeimage.c.function.CFunctionPointer;
4748

4849
import com.oracle.graal.pointsto.constraints.UnsupportedFeatureException;
4950
import com.oracle.graal.pointsto.meta.AnalysisMetaAccess;
@@ -354,11 +355,13 @@ private static ClassInitializationInfo buildRuntimeInitializationInfo(FeatureImp
354355
* information.
355356
*/
356357
assert type.isLinked();
358+
CFunctionPointer classInitializerFunction = null;
357359
AnalysisMethod classInitializer = type.getClassInitializer();
358360
if (classInitializer != null) {
359361
assert classInitializer.getCode() != null;
360362
access.registerAsCompiled(classInitializer);
363+
classInitializerFunction = MethodPointer.factory(classInitializer);
361364
}
362-
return new ClassInitializationInfo(MethodPointer.factory(classInitializer));
365+
return new ClassInitializationInfo(classInitializerFunction);
363366
}
364367
}

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImage.java

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@
7777
import com.oracle.svm.core.BuildArtifacts;
7878
import com.oracle.svm.core.BuildArtifacts.ArtifactType;
7979
import com.oracle.svm.core.FrameAccess;
80+
import com.oracle.svm.core.InvalidMethodPointerHandler;
8081
import com.oracle.svm.core.Isolates;
8182
import com.oracle.svm.core.SubstrateOptions;
8283
import com.oracle.svm.core.SubstrateUtil;
@@ -564,14 +565,24 @@ private static boolean checkEmbeddedOffset(ProgbitsSectionImpl sectionImpl, fina
564565
return true;
565566
}
566567

567-
private static void markFunctionRelocationSite(final ProgbitsSectionImpl sectionImpl, final int offset, final RelocatableBuffer.Info info) {
568+
private void markFunctionRelocationSite(final ProgbitsSectionImpl sectionImpl, final int offset, final RelocatableBuffer.Info info) {
568569
assert info.getTargetObject() instanceof CFunctionPointer : "Wrong type for FunctionPointer relocation: " + info.getTargetObject().toString();
569570
final int functionPointerRelocationSize = 8;
570571
assert info.getRelocationSize() == functionPointerRelocationSize : "Function relocation: " + info.getRelocationSize() + " should be " + functionPointerRelocationSize + " bytes.";
571572
// References to functions are via relocations to the symbol for the function.
572-
ResolvedJavaMethod method = ((MethodPointer) info.getTargetObject()).getMethod();
573+
MethodPointer methodPointer = (MethodPointer) info.getTargetObject();
574+
HostedMethod target = null;
575+
boolean valid = methodPointer.isValid();
576+
if (valid) {
577+
ResolvedJavaMethod method = methodPointer.getMethod();
578+
target = (method instanceof HostedMethod) ? (HostedMethod) method : heap.getUniverse().lookup(method);
579+
valid = target.isCompiled();
580+
}
581+
if (!valid) {
582+
target = metaAccess.lookupJavaMethod(InvalidMethodPointerHandler.METHOD_POINTER_INVALID_HANDLER_METHOD);
583+
}
573584
// A reference to a method. Mark the relocation site using the symbol name.
574-
sectionImpl.markRelocationSite(offset, RelocationKind.getDirect(functionPointerRelocationSize), localSymbolNameForMethod(method), false, 0L);
585+
sectionImpl.markRelocationSite(offset, RelocationKind.getDirect(functionPointerRelocationSize), localSymbolNameForMethod(target), false, 0L);
575586
}
576587

577588
private static boolean isAddendAligned(Architecture arch, long addend, RelocationKind kind) {

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageHeapWriter.java

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,19 +38,24 @@
3838
import org.graalvm.nativeimage.ImageSingletons;
3939
import org.graalvm.nativeimage.c.function.CFunctionPointer;
4040
import org.graalvm.nativeimage.c.function.RelocatedPointer;
41+
import org.graalvm.nativeimage.hosted.Feature;
4142
import org.graalvm.word.WordBase;
4243

44+
import com.oracle.graal.pointsto.meta.AnalysisMethod;
4345
import com.oracle.graal.pointsto.util.AnalysisError;
4446
import com.oracle.objectfile.ObjectFile;
4547
import com.oracle.svm.core.FrameAccess;
48+
import com.oracle.svm.core.InvalidMethodPointerHandler;
4649
import com.oracle.svm.core.StaticFieldsSupport;
50+
import com.oracle.svm.core.annotate.AutomaticFeature;
4751
import com.oracle.svm.core.config.ConfigurationValues;
4852
import com.oracle.svm.core.config.ObjectLayout;
4953
import com.oracle.svm.core.heap.Heap;
5054
import com.oracle.svm.core.heap.ObjectHeader;
5155
import com.oracle.svm.core.hub.DynamicHub;
5256
import com.oracle.svm.core.image.ImageHeapLayoutInfo;
5357
import com.oracle.svm.core.meta.SubstrateObjectConstant;
58+
import com.oracle.svm.hosted.FeatureImpl;
5459
import com.oracle.svm.hosted.config.HybridLayout;
5560
import com.oracle.svm.hosted.image.NativeImageHeap.ObjectInfo;
5661
import com.oracle.svm.hosted.meta.HostedClass;
@@ -243,13 +248,19 @@ private void addNonDataRelocation(RelocatableBuffer buffer, int index, Relocated
243248
assert pointer instanceof CFunctionPointer : "unknown relocated pointer " + pointer;
244249
assert pointer instanceof MethodPointer : "cannot create relocation for unknown FunctionPointer " + pointer;
245250

246-
ResolvedJavaMethod method = ((MethodPointer) pointer).getMethod();
247-
HostedMethod hMethod = method instanceof HostedMethod ? (HostedMethod) method : heap.getUniverse().lookup(method);
248-
if (hMethod.isCompiled()) {
249-
// Only compiled methods inserted in vtables require relocation.
250-
int pointerSize = ConfigurationValues.getTarget().wordSize;
251-
addDirectRelocationWithoutAddend(buffer, index, pointerSize, pointer);
251+
RelocatedPointer target = pointer;
252+
MethodPointer methodPointer = ((MethodPointer) target);
253+
boolean valid = methodPointer.isValid();
254+
if (valid) {
255+
ResolvedJavaMethod method = methodPointer.getMethod();
256+
HostedMethod hMethod = (method instanceof HostedMethod) ? (HostedMethod) method : heap.getUniverse().lookup(method);
257+
valid = hMethod.isCompiled();
252258
}
259+
if (!valid) {
260+
target = ImageSingletons.lookup(MethodPointerInvalidHandlerFeature.class).getHandler();
261+
}
262+
int pointerSize = ConfigurationValues.getTarget().wordSize;
263+
addDirectRelocationWithoutAddend(buffer, index, pointerSize, target);
253264
}
254265

255266
private static void writePrimitive(RelocatableBuffer buffer, int index, JavaConstant con) {
@@ -398,3 +409,21 @@ private void writeObject(ObjectInfo info, RelocatableBuffer buffer) {
398409
}
399410
}
400411
}
412+
413+
@AutomaticFeature
414+
final class MethodPointerInvalidHandlerFeature implements Feature {
415+
private CFunctionPointer handler;
416+
417+
@Override
418+
public void beforeAnalysis(BeforeAnalysisAccess a) {
419+
FeatureImpl.BeforeAnalysisAccessImpl access = (FeatureImpl.BeforeAnalysisAccessImpl) a;
420+
AnalysisMethod notCompiledMethod = access.getMetaAccess().lookupJavaMethod(InvalidMethodPointerHandler.METHOD_POINTER_INVALID_HANDLER_METHOD);
421+
access.registerAsCompiled(notCompiledMethod);
422+
handler = MethodPointer.factory(notCompiledMethod);
423+
}
424+
425+
CFunctionPointer getHandler() {
426+
assert handler != null;
427+
return handler;
428+
}
429+
}

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/MethodPointer.java

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,40 +29,48 @@
2929
import org.graalvm.nativeimage.c.function.CFunctionPointer;
3030
import org.graalvm.word.ComparableWord;
3131

32+
import com.oracle.svm.core.InvalidMethodPointerHandler;
33+
3234
import jdk.vm.ci.meta.ResolvedJavaMethod;
3335

3436
/**
3537
* A pointer to the compiled code of a method.
3638
*/
3739
public class MethodPointer implements CFunctionPointer {
40+
private static final MethodPointer INVALID = new MethodPointer(null);
3841

3942
private final ResolvedJavaMethod method;
4043

4144
public static CFunctionPointer factory(ResolvedJavaMethod method) {
42-
if (method == null) {
43-
return null;
44-
} else {
45-
return new MethodPointer(method);
46-
}
45+
return (method != null) ? new MethodPointer(method) : INVALID;
4746
}
4847

4948
protected MethodPointer(ResolvedJavaMethod method) {
50-
assert method != null;
5149
this.method = method;
5250
}
5351

52+
public boolean isValid() {
53+
return (method != null);
54+
}
55+
5456
public ResolvedJavaMethod getMethod() {
57+
assert isValid();
5558
return method;
5659
}
5760

61+
/**
62+
* Always {@code false} because even a pointer to {@code null} or to a method that is not
63+
* compiled will eventually be replaced by
64+
* {@link InvalidMethodPointerHandler#METHOD_POINTER_INVALID_HANDLER_METHOD}.
65+
*/
5866
@Override
5967
public boolean isNull() {
6068
return false;
6169
}
6270

6371
@Override
6472
public boolean isNonNull() {
65-
return true;
73+
return !isNull();
6674
}
6775

6876
@Override

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/UniverseBuilder.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@
6363
import com.oracle.graal.pointsto.meta.AnalysisUniverse;
6464
import com.oracle.graal.pointsto.results.AbstractAnalysisResultsBuilder;
6565
import com.oracle.svm.core.FunctionPointerHolder;
66-
import com.oracle.svm.core.InvalidVTableEntryHandler;
66+
import com.oracle.svm.core.InvalidMethodPointerHandler;
6767
import com.oracle.svm.core.StaticFieldsSupport;
6868
import com.oracle.svm.core.SubstrateOptions;
6969
import com.oracle.svm.core.SubstrateUtil;
@@ -674,7 +674,7 @@ private void buildVTables() {
674674
* To avoid segfaults when jumping to address 0, all unused vtable entries are filled with a
675675
* stub that reports a fatal error.
676676
*/
677-
HostedMethod invalidVTableEntryHandler = hMetaAccess.lookupJavaMethod(InvalidVTableEntryHandler.HANDLER_METHOD);
677+
HostedMethod invalidVTableEntryHandler = hMetaAccess.lookupJavaMethod(InvalidMethodPointerHandler.INVALID_VTABLE_ENTRY_HANDLER_METHOD);
678678

679679
for (HostedType type : hUniverse.getTypes()) {
680680
if (type.isArray()) {
@@ -999,6 +999,6 @@ final class InvalidVTableEntryFeature implements Feature {
999999
@Override
10001000
public void beforeAnalysis(BeforeAnalysisAccess a) {
10011001
BeforeAnalysisAccessImpl access = (BeforeAnalysisAccessImpl) a;
1002-
access.registerAsCompiled(InvalidVTableEntryHandler.HANDLER_METHOD);
1002+
access.registerAsCompiled(InvalidMethodPointerHandler.INVALID_VTABLE_ENTRY_HANDLER_METHOD);
10031003
}
10041004
}

0 commit comments

Comments
 (0)