-
-
Notifications
You must be signed in to change notification settings - Fork 110
Add support for var-args method calls with @PolymorphicSignature causing unmatched method signatures #1333
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
base: develop
Are you sure you want to change the base?
Add support for var-args method calls with @PolymorphicSignature causing unmatched method signatures #1333
Changes from 11 commits
cde2e34
732834e
fa59d4a
4436c41
f24ff9a
eed2df1
ece4faa
9c20cf2
9eb4cb8
5a95bdf
3aeebd4
1646566
d2a4f11
1d450b4
2348c11
07de6b4
706fc81
bc8763d
2988813
19397b2
5a00c10
5261278
ba19f9b
a8aa9d6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -49,6 +49,8 @@ | |
| import sootup.core.types.ClassType; | ||
| import sootup.core.types.VoidType; | ||
| import sootup.core.views.View; | ||
| import sootup.java.core.AnnotationUsage; | ||
| import sootup.java.core.JavaSootClass; | ||
|
|
||
| /** | ||
| * The AbstractCallGraphAlgorithm class is the super class of all call graph algorithm. It provides | ||
|
|
@@ -61,10 +63,42 @@ public abstract class AbstractCallGraphAlgorithm implements CallGraphAlgorithm { | |
|
|
||
| @NonNull protected final View view; | ||
| @NonNull protected final TypeHierarchy typeHierarchy; | ||
| @NonNull protected final List<MethodSignature> preanalysis; | ||
|
|
||
| protected AbstractCallGraphAlgorithm(@NonNull View view) { | ||
| this.view = view; | ||
| this.typeHierarchy = view.getTypeHierarchy(); | ||
| this.preanalysis = getMethodsWithPolymorphicAnnotation(); | ||
| } | ||
|
|
||
| /** | ||
| * Collects all method signatures from the current view that are annotated with {@code | ||
| * java.lang.invoke.MethodHandle$PolymorphicSignature}. | ||
| * | ||
| * @return a list of method signatures annotated with {@code @PolymorphicSignature} | ||
| */ | ||
| protected List<MethodSignature> getMethodsWithPolymorphicAnnotation() { | ||
| List<MethodSignature> polymorphicMethodSigs = new ArrayList<>(); | ||
| ClassType polymorphicAnnotationType = | ||
| view.getIdentifierFactory() | ||
| .getClassType("java.lang.invoke.MethodHandle$PolymorphicSignature"); | ||
| view.getClasses() | ||
| .filter(sootClass -> sootClass instanceof JavaSootClass) | ||
| .map(sootClass -> (JavaSootClass) sootClass) | ||
| .flatMap(javaSootClass -> javaSootClass.getMethods().stream()) | ||
| .forEach( | ||
| javaSootMethod -> { | ||
| Iterable<AnnotationUsage> annotationUsages = javaSootMethod.getAnnotations(); | ||
| if (annotationUsages.equals(Collections.emptyList())) { | ||
| return; | ||
| } | ||
|
moebarni marked this conversation as resolved.
Outdated
|
||
| for (AnnotationUsage annotationUsage : annotationUsages) { | ||
| if (annotationUsage.getAnnotation().equals(polymorphicAnnotationType)) { | ||
| polymorphicMethodSigs.add(javaSootMethod.getSignature()); | ||
|
moebarni marked this conversation as resolved.
Outdated
|
||
| } | ||
| } | ||
| }); | ||
| return polymorphicMethodSigs; | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -592,18 +626,7 @@ protected static Optional<? extends SootMethod> findConcreteMethod( | |
| } | ||
|
|
||
| // search method in interfaces | ||
| Optional<? extends SootMethod> defaultMethod = findDefaultMethod(view, startClass, methodSig); | ||
| if (defaultMethod.isPresent()) { | ||
| return defaultMethod; | ||
| } | ||
|
|
||
| logger.warn( | ||
| "Could not find \"" | ||
| + sig.getSubSignature() | ||
| + "\" in " | ||
| + sig.getDeclClassType().getClassName() | ||
| + " and in its superclasses and interfaces"); | ||
| return Optional.empty(); | ||
| return findDefaultMethod(view, startClass, methodSig); | ||
| } | ||
|
|
||
| protected static Optional<SootMethod> findMethodInHierarchy( | ||
|
|
@@ -667,4 +690,30 @@ protected static Optional<? extends SootMethod> findDefaultMethod( | |
| protected boolean isInterface(ClassType classType) { | ||
| return typeHierarchy.isInterface(classType); | ||
| } | ||
|
|
||
| /** | ||
| * Attempts to resolve a previously unresolved method signature by comparing it with all known | ||
| * method signatures annotated with {@code @PolymorphicSignature}. If a match is found (ignoring | ||
| * the parameters), the corresponding SootMethod is returned. | ||
| * | ||
| * @param targetMethodSignature the signature of the searched method | ||
| * @return the found method object, or null if the method was not found. | ||
| */ | ||
| protected Optional<? extends SootMethod> findMatchingPolymorphicMethod( | ||
|
moebarni marked this conversation as resolved.
Outdated
|
||
| @NonNull MethodSignature targetMethodSignature) { | ||
| for (MethodSignature polymorphicMethodSigs : preanalysis) { | ||
|
moebarni marked this conversation as resolved.
Outdated
|
||
| if (targetMethodSignature.getDeclClassType().equals(polymorphicMethodSigs.getDeclClassType()) | ||
| && targetMethodSignature.getName().equals(polymorphicMethodSigs.getName())) { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. two cases are not covered which me might have to investigate.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Created tests.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No subclasses of java.lang.invoke.MethodHandle/VarHandle possible, both classes have package-private constructors and all their implementations are package-private.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can you access the annotation outside of the package?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No: 'java.lang.invoke.MethodHandle.PolymorphicSignature' is not public in 'java.lang.invoke.MethodHandle'. Cannot be accessed from outside package |
||
| // return the actualTargetMethodOpt | ||
| return view.getMethod(polymorphicMethodSigs).map(sootMethod -> (SootMethod) sootMethod); | ||
|
moebarni marked this conversation as resolved.
Outdated
|
||
| } | ||
| } | ||
| logger.warn( | ||
| "Could not find \"" | ||
| + targetMethodSignature.getSubSignature() | ||
| + "\" in " | ||
| + targetMethodSignature.getDeclClassType().getClassName() | ||
| + " and in its superclasses and interfaces"); | ||
| return Optional.empty(); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -34,6 +34,7 @@ | |
| import sootup.core.jimple.common.expr.JSpecialInvokeExpr; | ||
| import sootup.core.jimple.common.stmt.InvokableStmt; | ||
| import sootup.core.jimple.common.stmt.JAssignStmt; | ||
| import sootup.core.jimple.common.stmt.Stmt; | ||
| import sootup.core.model.MethodModifier; | ||
| import sootup.core.model.SootClass; | ||
| import sootup.core.model.SootMethod; | ||
|
|
@@ -96,13 +97,21 @@ protected List<ClassType> collectInstantiatedClassesInMethod(SootMethod method) | |
| if (method == null || method.isAbstract() || method.isNative()) { | ||
| return Collections.emptyList(); | ||
| } | ||
|
|
||
| Set<ClassType> instantiated = | ||
| method.getBody().getStmts().stream() | ||
| .filter(stmt -> stmt instanceof JAssignStmt) | ||
| .filter(Stmt::isJAssignStmt) | ||
| .map(stmt -> ((JAssignStmt) stmt).getRightOp()) | ||
| .filter(value -> value instanceof JNewExpr) | ||
| .map(value -> ((JNewExpr) value).getType()) | ||
| .flatMap( | ||
| value -> { | ||
| // ClassType as returnType | ||
| if (value instanceof AbstractInvokeExpr && value.getType() instanceof ClassType) { | ||
| return Stream.of((ClassType) value.getType()); | ||
| // new-expression | ||
| } else if (value instanceof JNewExpr) { | ||
| return Stream.of(((JNewExpr) value).getType()); | ||
| } | ||
| return Stream.empty(); | ||
| }) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we are only checking for new expressions. Method calls are not considered because first you do not check if they are constructor and second because if you would check for constructors you would consider supertypes as instaniated if the constructor contains a super() call.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Rollback applied. New approach required to treat java.lang.invoke.MethodHandle (with its package-private constructor) as instantiated during RTA analysis. |
||
| .collect(Collectors.toSet()); | ||
| List<ClassType> newInstantiatedClassTypes = | ||
| instantiated.stream() | ||
|
|
@@ -141,9 +150,14 @@ protected Stream<MethodSignature> resolveCall( | |
| if (actualTargetMethod == null) { | ||
| // method not implemented, search for implementation in super classes or ínterfaces | ||
| actualTargetMethod = findConcreteMethod(view, targetMethodSignature).orElse(null); | ||
| // method implementation isn't contained in the view. return the called method as target | ||
| if (actualTargetMethod == null) { | ||
| return Stream.of(targetMethodSignature); | ||
| // method implementation isn't contained in the view. | ||
| // check if method got the PolymorphicSignature annotation | ||
| actualTargetMethod = findMatchingPolymorphicMethod(targetMethodSignature).orElse(null); | ||
| if (actualTargetMethod == null) { | ||
| // method couldn't be resolved, return the called method as target | ||
| return Stream.of(targetMethodSignature); | ||
| } | ||
| } | ||
| } | ||
| // special invokes and static invokes can only have one specific target | ||
|
|
@@ -159,7 +173,6 @@ protected Stream<MethodSignature> resolveCall( | |
| .subtypesOf(targetMethodSignature.getDeclClassType()) | ||
| .flatMap(classType -> view.getClass(classType).stream()) | ||
| .toList(); | ||
|
|
||
| // get all targets of these subtypes | ||
| Stream<MethodSignature> targets = | ||
| resolveAllCallTargets( | ||
|
|
@@ -317,8 +330,9 @@ protected void preProcessingMethod( | |
| if (method == null) { | ||
| return; | ||
| } | ||
|
|
||
| System.out.println("PreProcessed Method: " + method); | ||
|
moebarni marked this conversation as resolved.
Outdated
|
||
| List<ClassType> newInstantiatedClasses = collectInstantiatedClassesInMethod(method); | ||
| System.out.println("New Instantiated Classes: " + newInstantiatedClasses); | ||
|
moebarni marked this conversation as resolved.
Outdated
|
||
| newInstantiatedClasses.forEach( | ||
| classType -> includeIgnoredCallsToClass(classType, cg, workList)); | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| package e1; | ||
|
|
||
| import java.lang.invoke.MethodHandle; | ||
| import java.lang.invoke.MethodHandles; | ||
| import java.lang.invoke.MethodType; | ||
|
|
||
| public class MethodHandleInvokeExample1 { | ||
| public static void main(String[] args) throws Throwable { | ||
| MethodHandle handle = MethodHandles.lookup() | ||
| .findVirtual(String.class, "length", MethodType.methodType(int.class)); | ||
|
|
||
| handle.invoke("Hello"); | ||
| } | ||
| } | ||
|
moebarni marked this conversation as resolved.
Outdated
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| package e2; | ||
|
|
||
| import java.lang.invoke.MethodHandle; | ||
| import java.lang.invoke.MethodHandles; | ||
| import java.lang.invoke.MethodType; | ||
|
|
||
| public class MethodHandleInvokeExample2 { | ||
| public static void main(String[] args) throws Throwable { | ||
| MethodHandle handle = MethodHandles.lookup() | ||
| .findVirtual(String.class, "substring", MethodType.methodType(String.class, int.class, int.class)); | ||
|
|
||
| handle.invoke("HelloWorld", 0, 5); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| package e3; | ||
|
|
||
| import java.lang.invoke.MethodHandles; | ||
| import java.lang.invoke.VarHandle; | ||
|
|
||
| public class VarHandleGetExample3 { | ||
| static int value = 42; | ||
|
|
||
| public static void main(String[] args) throws Throwable { | ||
| VarHandle handle = MethodHandles.lookup() | ||
| .findStaticVarHandle(VarHandleGetExample3.class, "value", int.class); | ||
|
|
||
| handle.get(); | ||
| } | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.