Skip to content

[GR-58673] Validate reachable object before propagating it. #11037

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 1 commit into from
Apr 15, 2025
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
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* The Universal Permissive License (UPL), Version 1.0
Expand Down Expand Up @@ -194,8 +194,9 @@ interface DuringSetupAccess extends FeatureAccess {

/**
* Register a callback that is executed when an object of type {@code clazz}, or any of its
* subtypes, is marked as reachable during heap scanning. The callback may be executed for
* the same object by multiple worker threads concurrently.
* subtypes, is marked as reachable during heap scanning. The callback is executed before
* the object is added to the shadow heap. The callback may be executed for the same object
* by multiple worker threads concurrently.
*
* @since 24.2
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,14 @@ public void notifyClassReachabilityListener(AnalysisUniverse universe, DuringAna
}
}

/**
* Run validation checks for reachable objects before registering them in the shadow heap.
*
* @param obj the object to validate
*/
public void validateReachableObject(Object obj) {
}

/**
* Register newly created type.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -587,15 +587,25 @@ private ImageHeapConstant markReachable(ImageHeapConstant imageHeapConstant, Sca
}

protected void onObjectReachable(ImageHeapConstant imageHeapConstant, ScanReason reason, Consumer<ScanReason> onAnalysisModified) {
AnalysisType objectType = metaAccess.lookupJavaType(imageHeapConstant);
imageHeap.addReachableObject(objectType, imageHeapConstant);

AnalysisType type = imageHeapConstant.getType();
Object object = bb.getSnippetReflectionProvider().asObject(Object.class, imageHeapConstant);
/* Simulated constants don't have a backing object and don't need to be processed. */
if (object != null) {
AnalysisType objectType = imageHeapConstant.getType();
if (imageHeapConstant.isBackedByHostedObject()) {
/* Simulated constants don't have a backing object and don't need to be processed. */
try {
type.notifyObjectReachable(universe.getConcurrentAnalysisAccess(), object, reason);
Object object = bb.getSnippetReflectionProvider().asObject(Object.class, imageHeapConstant);
/*
* Before adding the object to ImageHeap.reachableObjects, where it could be read by
* other threads, run validation checks, e.g., verify that the object's type can be
* initialized at build time. Also run the validation before exposing the object to
* other reachability hooks to avoid propagating an invalid object.
*/
hostVM.validateReachableObject(object);
/*
* Note that reachability hooks can also reject objects based on specific validation
* conditions, e.g., a started Thread should never be added to the image heap, but
* the structure of the object is valid, as ensured by the validity check above.
*/
objectType.notifyObjectReachable(universe.getConcurrentAnalysisAccess(), object, reason);
} catch (UnsupportedFeatureException e) {
/* Enhance the unsupported feature message with the object trace and rethrow. */
StringBuilder backtrace = new StringBuilder();
Expand All @@ -604,6 +614,8 @@ protected void onObjectReachable(ImageHeapConstant imageHeapConstant, ScanReason
}
}

imageHeap.addReachableObject(objectType, imageHeapConstant);

markTypeInstantiated(objectType, reason);
if (imageHeapConstant instanceof ImageHeapObjectArray imageHeapArray) {
AnalysisType arrayType = imageHeapArray.getType();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@

import com.oracle.graal.pointsto.BigBang;
import com.oracle.graal.pointsto.ObjectScanner;
import com.oracle.graal.pointsto.constraints.UnsupportedFeatureException;
import com.oracle.graal.pointsto.heap.ImageHeapConstant;
import com.oracle.graal.pointsto.heap.ImageHeapScanner;
import com.oracle.graal.pointsto.infrastructure.SubstitutionProcessor;
Expand Down Expand Up @@ -309,7 +310,10 @@ public void registerObjectToConstantReplacer(Function<Object, ImageHeapConstant>

/**
* Register a callback that is executed when an object of the specified type or any of its
* subtypes is marked as reachable.
* subtypes is marked as reachable. The callback is executed before the object is added to
* the shadow heap. A callback may throw an {@link UnsupportedFeatureException} to reject an
* object based on specific validation rules. This will stop the image build and report how
* the object was reached.
*
* @since 24.0
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
import java.util.function.BiPredicate;
import java.util.function.Function;

import com.oracle.svm.hosted.classinitialization.ClassInitializationFeature;
import org.graalvm.nativeimage.AnnotationAccess;
import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.Platform;
Expand Down Expand Up @@ -360,6 +361,11 @@ public boolean isRelocatedPointer(JavaConstant constant) {
return constant instanceof RelocatableConstant;
}

@Override
public void validateReachableObject(Object obj) {
ImageSingletons.lookup(ClassInitializationFeature.class).checkImageHeapInstance(obj);
}

@Override
public void registerType(AnalysisType analysisType) {
DynamicHub hub = createHub(analysisType);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@

import org.graalvm.nativeimage.impl.clinit.ClassInitializationTracking;

import com.oracle.graal.pointsto.ObjectScanner;
import com.oracle.graal.pointsto.constraints.UnsupportedFeatureException;
import com.oracle.graal.pointsto.meta.AnalysisMethod;
import com.oracle.graal.pointsto.meta.AnalysisType;
Expand Down Expand Up @@ -142,11 +141,10 @@ private static void initializeNativeImagePackagesAtBuildTime(ClassInitialization
public void duringSetup(DuringSetupAccess a) {
FeatureImpl.DuringSetupAccessImpl access = (FeatureImpl.DuringSetupAccessImpl) a;
classInitializationSupport = access.getHostVM().getClassInitializationSupport();
access.registerObjectReachableCallback(Object.class, this::checkImageHeapInstance);
}

@SuppressWarnings("unused")
private void checkImageHeapInstance(DuringAnalysisAccess access, Object obj, ObjectScanner.ScanReason reason) {
public void checkImageHeapInstance(Object obj) {
/*
* Note that initializeAtBuildTime also memoizes the class as InitKind.BUILD_TIME, which
* means that the user cannot later manually register it as RERUN or RUN_TIME.
Expand Down
Loading