Skip to content

Fail when invoking invalid method pointers #3874

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Oct 8, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ private void linkCompiledBatches(BatchExecutor executor, DebugContext debug, int
stackMapDumper.close();

HostedMethod firstMethod = (HostedMethod) getFirstCompilation().getMethods()[0];
buildRuntimeMetadata(MethodPointer.factory(firstMethod), WordFactory.signed(textSectionInfo.getCodeSize()));
buildRuntimeMetadata(new MethodPointer(firstMethod), WordFactory.signed(textSectionInfo.getCodeSize()));
}

private void llvmOptimize(DebugContext debug, String outputPath, String inputPath) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,16 +43,33 @@
import com.oracle.svm.util.ReflectionUtil;

/**
* Provides a stub method that can be used to fill otherwise unused vtable slots. Instead of a
* segfault, this method provides a full diagnostic output with a stack trace.
* Provides stub methods that can be used for uninitialized method pointers. Instead of a segfault,
* the stubs provide full diagnostic output with a stack trace.
*/
public final class InvalidVTableEntryHandler {
public static final Method HANDLER_METHOD = ReflectionUtil.lookupMethod(InvalidVTableEntryHandler.class, "invalidVTableEntryHandler");
public static final String MSG = "Fatal error: Virtual method call used an illegal vtable entry that was seen as unused by the static analysis";
public final class InvalidMethodPointerHandler {
public static final Method INVALID_VTABLE_ENTRY_HANDLER_METHOD = ReflectionUtil.lookupMethod(InvalidMethodPointerHandler.class, "invalidVTableEntryHandler");
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";

public static final Method METHOD_POINTER_NOT_COMPILED_HANDLER_METHOD = ReflectionUtil.lookupMethod(InvalidMethodPointerHandler.class, "methodPointerNotCompiledHandler");
public static final String METHOD_POINTER_NOT_COMPILED_MSG = "Fatal error: Method pointer invoked on a method that was not compiled because it was not seen as invoked by the static analysis nor was it directly registered for compilation";

@StubCallingConvention
@NeverInline("We need a separate frame that stores all registers")
private static void invalidVTableEntryHandler() {
Pointer callerSP = KnownIntrinsics.readCallerStackPointer();
CodePointer callerIP = KnownIntrinsics.readReturnAddress();
failFatally(callerSP, callerIP, INVALID_VTABLE_ENTRY_MSG);
}

@StubCallingConvention
@NeverInline("We need a separate frame that stores all registers")
private static void methodPointerNotCompiledHandler() {
Pointer callerSP = KnownIntrinsics.readCallerStackPointer();
CodePointer callerIP = KnownIntrinsics.readReturnAddress();
failFatally(callerSP, callerIP, METHOD_POINTER_NOT_COMPILED_MSG);
}

private static void failFatally(Pointer callerSP, CodePointer callerIP, String message) {
VMThreads.StatusSupport.setStatusIgnoreSafepoints();
StackOverflowCheck.singleton().disableStackOverflowChecksForFatalError();

Expand All @@ -63,13 +80,11 @@ private static void invalidVTableEntryHandler() {
* from the method that has the illegal vtable call. That can be helpful when debugging the
* cause of the fatal error.
*/
Pointer callerSP = KnownIntrinsics.readCallerStackPointer();
CodePointer callerIP = KnownIntrinsics.readReturnAddress();
LogHandler logHandler = ImageSingletons.lookup(LogHandler.class);
Log log = Log.enterFatalContext(logHandler, callerIP, MSG, null);
Log log = Log.enterFatalContext(logHandler, callerIP, message, null);
if (log != null) {
SubstrateDiagnostics.printFatalError(log, callerSP, callerIP, WordFactory.nullPointer(), true);
log.string(MSG).newline();
log.string(message).newline();
}
logHandler.fatalError();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,6 @@ public void beforeCompilation(BeforeCompilationAccess a) {
CompilationAccessImpl config = (CompilationAccessImpl) a;
config.registerAsImmutable(ImageSingletons.lookup(DeoptimizationSupport.class));
HostedMetaAccess metaAccess = config.getMetaAccess();
DeoptimizationSupport.setDeoptStubPointer(MethodPointer.factory(metaAccess.lookupJavaMethod(deoptStubMethod)));
DeoptimizationSupport.setDeoptStubPointer(new MethodPointer(metaAccess.lookupJavaMethod(deoptStubMethod)));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
import org.graalvm.compiler.graph.Node;
import org.graalvm.compiler.options.OptionValues;
import org.graalvm.compiler.phases.util.Providers;
import org.graalvm.nativeimage.c.function.CFunctionPointer;

import com.oracle.graal.pointsto.constraints.UnsupportedFeatureException;
import com.oracle.graal.pointsto.meta.AnalysisMetaAccess;
Expand Down Expand Up @@ -340,7 +341,7 @@ private static ClassInitializationInfo buildRuntimeInitializationInfo(FeatureImp
/* Synthesize a VerifyError to be thrown at run time. */
AnalysisMethod throwVerifyError = access.getMetaAccess().lookupJavaMethod(ExceptionSynthesizer.throwExceptionMethod(VerifyError.class));
access.registerAsCompiled(throwVerifyError);
return new ClassInitializationInfo(MethodPointer.factory(throwVerifyError));
return new ClassInitializationInfo(new MethodPointer(throwVerifyError));
} catch (Throwable t) {
/*
* All other linking errors will be reported as NoClassDefFoundError when initialization
Expand All @@ -354,11 +355,13 @@ private static ClassInitializationInfo buildRuntimeInitializationInfo(FeatureImp
* information.
*/
assert type.isLinked();
CFunctionPointer classInitializerFunction = null;
AnalysisMethod classInitializer = type.getClassInitializer();
if (classInitializer != null) {
assert classInitializer.getCode() != null;
access.registerAsCompiled(classInitializer);
classInitializerFunction = new MethodPointer(classInitializer);
}
return new ClassInitializationInfo(MethodPointer.factory(classInitializer));
return new ClassInitializationInfo(classInitializerFunction);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ public AnalysisMethod registerJavaStubForMethod(AnalysisMethod method) {
if (value == null) {
assert !bb.getUniverse().sealed();
AnalysisMethod nativeStub = registerStubForMethod(method, () -> CEntryPointData.create(method));
CFunctionPointer nativeStubAddress = MethodPointer.factory(nativeStub);
CFunctionPointer nativeStubAddress = new MethodPointer(nativeStub);
String stubName = SubstrateUtil.uniqueShortName(method);
ResolvedJavaType holderClass = bb.getMetaAccess().lookupJavaType(IsolateLeaveStub.class).getWrapped();
CEntryPointJavaCallStubMethod stub = new CEntryPointJavaCallStubMethod(method.getWrapped(), stubName, holderClass, nativeStubAddress);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ public Object apply(Object source) {
* Only during compilation and native image writing, we do the actual
* replacement.
*/
return MethodPointer.factory(hStub);
return new MethodPointer(hStub);
}
}
return source;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ public void layoutMethods(DebugContext debug, String imageName, BigBang bb, Fork
codeCacheSize = NumUtil.roundUp(codeCacheSize + compilation.getTargetCodeSize(), SubstrateOptions.codeAlignment());
}

buildRuntimeMetadata(MethodPointer.factory(firstMethod), WordFactory.unsigned(codeCacheSize));
buildRuntimeMetadata(new MethodPointer(firstMethod), WordFactory.unsigned(codeCacheSize));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
import org.graalvm.compiler.debug.Indent;
import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.c.function.CFunctionPointer;
import org.graalvm.nativeimage.hosted.Feature;

import com.oracle.graal.pointsto.meta.AnalysisMethod;
import com.oracle.graal.pointsto.meta.AnalysisType;
Expand All @@ -77,9 +78,11 @@
import com.oracle.svm.core.BuildArtifacts;
import com.oracle.svm.core.BuildArtifacts.ArtifactType;
import com.oracle.svm.core.FrameAccess;
import com.oracle.svm.core.InvalidMethodPointerHandler;
import com.oracle.svm.core.Isolates;
import com.oracle.svm.core.SubstrateOptions;
import com.oracle.svm.core.SubstrateUtil;
import com.oracle.svm.core.annotate.AutomaticFeature;
import com.oracle.svm.core.c.CConst;
import com.oracle.svm.core.c.CGlobalDataImpl;
import com.oracle.svm.core.c.CHeader;
Expand All @@ -96,6 +99,7 @@
import com.oracle.svm.core.meta.SubstrateObjectConstant;
import com.oracle.svm.core.option.HostedOptionValues;
import com.oracle.svm.core.util.UserError;
import com.oracle.svm.hosted.FeatureImpl;
import com.oracle.svm.hosted.NativeImageOptions;
import com.oracle.svm.hosted.c.CGlobalDataFeature;
import com.oracle.svm.hosted.c.NativeLibraries;
Expand Down Expand Up @@ -564,14 +568,19 @@ private static boolean checkEmbeddedOffset(ProgbitsSectionImpl sectionImpl, fina
return true;
}

private static void markFunctionRelocationSite(final ProgbitsSectionImpl sectionImpl, final int offset, final RelocatableBuffer.Info info) {
private void markFunctionRelocationSite(final ProgbitsSectionImpl sectionImpl, final int offset, final RelocatableBuffer.Info info) {
assert info.getTargetObject() instanceof CFunctionPointer : "Wrong type for FunctionPointer relocation: " + info.getTargetObject().toString();
final int functionPointerRelocationSize = 8;
assert info.getRelocationSize() == functionPointerRelocationSize : "Function relocation: " + info.getRelocationSize() + " should be " + functionPointerRelocationSize + " bytes.";
// References to functions are via relocations to the symbol for the function.
ResolvedJavaMethod method = ((MethodPointer) info.getTargetObject()).getMethod();
MethodPointer methodPointer = (MethodPointer) info.getTargetObject();
ResolvedJavaMethod method = methodPointer.getMethod();
HostedMethod target = (method instanceof HostedMethod) ? (HostedMethod) method : heap.getUniverse().lookup(method);
if (!target.isCompiled()) {
target = metaAccess.lookupJavaMethod(InvalidMethodPointerHandler.METHOD_POINTER_NOT_COMPILED_HANDLER_METHOD);
}
// A reference to a method. Mark the relocation site using the symbol name.
sectionImpl.markRelocationSite(offset, RelocationKind.getDirect(functionPointerRelocationSize), localSymbolNameForMethod(method), false, 0L);
sectionImpl.markRelocationSite(offset, RelocationKind.getDirect(functionPointerRelocationSize), localSymbolNameForMethod(target), false, 0L);
}

private static boolean isAddendAligned(Architecture arch, long addend, RelocationKind kind) {
Expand Down Expand Up @@ -956,3 +965,12 @@ protected NativeTextSectionImpl(RelocatableBuffer relocatableBuffer, ObjectFile
protected final NativeImageCodeCache codeCache;
}
}

@AutomaticFeature
final class MethodPointerInvalidHandlerFeature implements Feature {
@Override
public void beforeAnalysis(BeforeAnalysisAccess a) {
FeatureImpl.BeforeAnalysisAccessImpl access = (FeatureImpl.BeforeAnalysisAccessImpl) a;
access.registerAsCompiled(InvalidMethodPointerHandler.METHOD_POINTER_NOT_COMPILED_HANDLER_METHOD);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,11 @@
import com.oracle.svm.hosted.image.NativeImageHeap.ObjectInfo;
import com.oracle.svm.hosted.meta.HostedClass;
import com.oracle.svm.hosted.meta.HostedField;
import com.oracle.svm.hosted.meta.HostedMethod;
import com.oracle.svm.hosted.meta.MaterializedConstantFields;
import com.oracle.svm.hosted.meta.MethodPointer;

import jdk.vm.ci.meta.JavaConstant;
import jdk.vm.ci.meta.JavaKind;
import jdk.vm.ci.meta.ResolvedJavaMethod;
import sun.misc.Unsafe;

/**
Expand Down Expand Up @@ -242,14 +240,8 @@ private void addNonDataRelocation(RelocatableBuffer buffer, int index, Relocated
mustBeReferenceAligned(index);
assert pointer instanceof CFunctionPointer : "unknown relocated pointer " + pointer;
assert pointer instanceof MethodPointer : "cannot create relocation for unknown FunctionPointer " + pointer;

ResolvedJavaMethod method = ((MethodPointer) pointer).getMethod();
HostedMethod hMethod = method instanceof HostedMethod ? (HostedMethod) method : heap.getUniverse().lookup(method);
if (hMethod.isCompiled()) {
// Only compiled methods inserted in vtables require relocation.
int pointerSize = ConfigurationValues.getTarget().wordSize;
addDirectRelocationWithoutAddend(buffer, index, pointerSize, pointer);
}
int pointerSize = ConfigurationValues.getTarget().wordSize;
addDirectRelocationWithoutAddend(buffer, index, pointerSize, pointer);
}

private static void writePrimitive(RelocatableBuffer buffer, int index, JavaConstant con) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@

import static com.oracle.svm.core.util.VMError.shouldNotReachHere;

import java.util.Objects;

import org.graalvm.nativeimage.c.function.CFunctionPointer;
import org.graalvm.word.ComparableWord;

Expand All @@ -34,20 +36,11 @@
/**
* A pointer to the compiled code of a method.
*/
public class MethodPointer implements CFunctionPointer {

public final class MethodPointer implements CFunctionPointer {
private final ResolvedJavaMethod method;

public static CFunctionPointer factory(ResolvedJavaMethod method) {
if (method == null) {
return null;
} else {
return new MethodPointer(method);
}
}

protected MethodPointer(ResolvedJavaMethod method) {
assert method != null;
public MethodPointer(ResolvedJavaMethod method) {
Objects.requireNonNull(method);
this.method = method;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
import com.oracle.graal.pointsto.meta.AnalysisUniverse;
import com.oracle.graal.pointsto.results.AbstractAnalysisResultsBuilder;
import com.oracle.svm.core.FunctionPointerHolder;
import com.oracle.svm.core.InvalidVTableEntryHandler;
import com.oracle.svm.core.InvalidMethodPointerHandler;
import com.oracle.svm.core.StaticFieldsSupport;
import com.oracle.svm.core.SubstrateOptions;
import com.oracle.svm.core.SubstrateUtil;
Expand Down Expand Up @@ -674,7 +674,7 @@ private void buildVTables() {
* To avoid segfaults when jumping to address 0, all unused vtable entries are filled with a
* stub that reports a fatal error.
*/
HostedMethod invalidVTableEntryHandler = hMetaAccess.lookupJavaMethod(InvalidVTableEntryHandler.HANDLER_METHOD);
HostedMethod invalidVTableEntryHandler = hMetaAccess.lookupJavaMethod(InvalidMethodPointerHandler.INVALID_VTABLE_ENTRY_HANDLER_METHOD);

for (HostedType type : hUniverse.getTypes()) {
if (type.isArray()) {
Expand Down Expand Up @@ -925,7 +925,7 @@ private void buildHubs() {
* We install a CodePointer in the vtable; when generating relocation info, we will
* know these point into .text
*/
vtable[idx] = MethodPointer.factory(type.vtable[idx]);
vtable[idx] = new MethodPointer(type.vtable[idx]);
}

// pointer maps in Dynamic Hub
Expand Down Expand Up @@ -999,6 +999,6 @@ final class InvalidVTableEntryFeature implements Feature {
@Override
public void beforeAnalysis(BeforeAnalysisAccess a) {
BeforeAnalysisAccessImpl access = (BeforeAnalysisAccessImpl) a;
access.registerAsCompiled(InvalidVTableEntryHandler.HANDLER_METHOD);
access.registerAsCompiled(InvalidMethodPointerHandler.INVALID_VTABLE_ENTRY_HANDLER_METHOD);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -124,13 +124,13 @@ public boolean isStatic() {
void finishBeforeCompilation(CompilationAccessImpl access) {
HostedUniverse hUniverse = access.getUniverse();
AnalysisUniverse aUniverse = access.getUniverse().getBigBang().getUniverse();
varargsCallWrapper = MethodPointer.factory(hUniverse.lookup(aUniverse.lookup(varargsCallWrapperMethod)));
arrayCallWrapper = MethodPointer.factory(hUniverse.lookup(aUniverse.lookup(arrayCallWrapperMethod)));
valistCallWrapper = MethodPointer.factory(hUniverse.lookup(aUniverse.lookup(valistCallWrapperMethod)));
varargsCallWrapper = new MethodPointer(hUniverse.lookup(aUniverse.lookup(varargsCallWrapperMethod)));
arrayCallWrapper = new MethodPointer(hUniverse.lookup(aUniverse.lookup(arrayCallWrapperMethod)));
valistCallWrapper = new MethodPointer(hUniverse.lookup(aUniverse.lookup(valistCallWrapperMethod)));
if (!Modifier.isStatic(modifiers) && !Modifier.isAbstract(modifiers)) {
varargsNonvirtualCallWrapper = MethodPointer.factory(hUniverse.lookup(aUniverse.lookup(varargsNonvirtualCallWrapperMethod)));
arrayNonvirtualCallWrapper = MethodPointer.factory(hUniverse.lookup(aUniverse.lookup(arrayNonvirtualCallWrapperMethod)));
valistNonvirtualCallWrapper = MethodPointer.factory(hUniverse.lookup(aUniverse.lookup(valistNonvirtualCallWrapperMethod)));
varargsNonvirtualCallWrapper = new MethodPointer(hUniverse.lookup(aUniverse.lookup(varargsNonvirtualCallWrapperMethod)));
arrayNonvirtualCallWrapper = new MethodPointer(hUniverse.lookup(aUniverse.lookup(arrayNonvirtualCallWrapperMethod)));
valistNonvirtualCallWrapper = new MethodPointer(hUniverse.lookup(aUniverse.lookup(valistNonvirtualCallWrapperMethod)));
}
setHidingSubclasses(access.getMetaAccess(), this::anyMatchIgnoreReturnType);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ private static CFunctionPointer prepareCallTrampoline(CompilationAccessImpl acce
HostedMethod hostedTrampoline = access.getUniverse().lookup(analysisTrampoline);
hostedTrampoline.compilationInfo.setCustomParseFunction(trampolineMethod.createCustomParseFunction());
hostedTrampoline.compilationInfo.setCustomCompileFunction(trampolineMethod.createCustomCompileFunction());
return MethodPointer.factory(hostedTrampoline);
return new MethodPointer(hostedTrampoline);
}

private static ResolvedJavaMethod getSingleMethod(MetaAccessProvider metaAccess, Class<?> holder) {
Expand All @@ -203,7 +203,7 @@ private static ResolvedJavaMethod getSingleMethod(MetaAccessProvider metaAccess,

private static CFunctionPointer getStubFunctionPointer(CompilationAccessImpl access, HostedMethod method) {
AnalysisMethod stub = CEntryPointCallStubSupport.singleton().getStubForMethod(method.getWrapped());
return MethodPointer.factory(access.getUniverse().lookup(stub));
return new MethodPointer(access.getUniverse().lookup(stub));
}

private void fillJNIInvocationInterfaceTable(CompilationAccessImpl access, CFunctionPointer[] table, CFunctionPointer defaultValue) {
Expand Down Expand Up @@ -236,7 +236,7 @@ private void fillJNIFunctionsTable(CompilationAccessImpl access, CFunctionPointe
HostedMethod hostedMethod = access.getUniverse().lookup(analysisMethod);

int offset = field.getOffsetInfo().getProperty();
setFunctionPointerTable(table, offset, MethodPointer.factory(hostedMethod));
setFunctionPointerTable(table, offset, new MethodPointer(hostedMethod));
}
for (CallVariant variant : CallVariant.values()) {
CFunctionPointer trampoline = prepareCallTrampoline(access, variant, false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ private Object createAccessor(Executable member) {
private CFunctionPointer register(ResolvedJavaMethod method) {
AnalysisMethod aMethod = method instanceof AnalysisMethod ? (AnalysisMethod) method : analysisAccess.getUniverse().lookup(method);
analysisAccess.registerAsCompiled(aMethod);
return MethodPointer.factory(aMethod);
return new MethodPointer(aMethod);
}

protected ResolvedJavaMethod createReflectiveInvokeMethod(String name, ResolvedJavaMethod prototype, Method method) {
Expand Down