Skip to content

Commit

Permalink
Properly handle static interface methods as entrypoints (#1275)
Browse files Browse the repository at this point in the history
When a static Interface method is selected as an Entrypoint, WALA
creates an invokeinterface instead of an invokestatic instruction in
FakeRootMethod. This will lead to an out-of-array read when resolving
the receiver for an interface invocation.

A test can be simply an Android app generated by Android Studio for a
Basic Activity demo (either Java or Kotlin). In my case, the problem
occurs for handling:

40149 = invokeinterface < Application,
Landroidx/window/layout/WindowMetricsCalculator,
getOrCreate()Landroidx/window/layout/WindowMetricsCalculator; > @32353
exception:40150

And the exception is as follows:

java.lang.ArrayIndexOutOfBoundsException: Index 0 out of bounds for
length 0
at
com.ibm.wala.ipa.callgraph.propagation.SSAPropagationCallGraphBuilder.lambda$getTargetsForCall$0(SSAPropagationCallGraphBuilder.java:2072)
at
com.ibm.wala.ipa.callgraph.propagation.SSAPropagationCallGraphBuilder$CrossProductRec.rec(SSAPropagationCallGraphBuilder.java:542)
at
com.ibm.wala.ipa.callgraph.propagation.SSAPropagationCallGraphBuilder.iterateCrossProduct(SSAPropagationCallGraphBuilder.java:2055)
at
com.ibm.wala.ipa.callgraph.propagation.SSAPropagationCallGraphBuilder.getTargetsForCall(SSAPropagationCallGraphBuilder.java:2079)
at
com.ibm.wala.ipa.callgraph.propagation.SSAPropagationCallGraphBuilder$ConstraintVisitor.visitInvokeInternal(SSAPropagationCallGraphBuilder.java:1159)
at
com.ibm.wala.ipa.callgraph.propagation.SSAPropagationCallGraphBuilder$ConstraintVisitor.visitInvoke(SSAPropagationCallGraphBuilder.java:1115)
at
com.ibm.wala.ssa.SSAInvokeInstruction.visit(SSAInvokeInstruction.java:94)
at
com.ibm.wala.ipa.callgraph.propagation.SSAPropagationCallGraphBuilder.addBlockInstructionConstraints(SSAPropagationCallGraphBuilder.java:273)
at
com.ibm.wala.ipa.callgraph.propagation.SSAPropagationCallGraphBuilder.addNodeInstructionConstraints(SSAPropagationCallGraphBuilder.java:250)
at
com.ibm.wala.ipa.callgraph.propagation.SSAPropagationCallGraphBuilder.unconditionallyAddConstraintsFromNode(SSAPropagationCallGraphBuilder.java:226)
at
com.ibm.wala.ipa.callgraph.propagation.SSAPropagationCallGraphBuilder.addConstraintsFromNode(SSAPropagationCallGraphBuilder.java:191)
at
com.ibm.wala.ipa.callgraph.propagation.PropagationCallGraphBuilder.addConstraintsFromNewNodes(PropagationCallGraphBuilder.java:308)
at
com.ibm.wala.ipa.callgraph.propagation.StandardSolver.solve(StandardSolver.java:53)
at
com.ibm.wala.ipa.callgraph.propagation.PropagationCallGraphBuilder.makeCallGraph(PropagationCallGraphBuilder.java:248)

This commit prioritizes a static method over an interface method and
addresses the above problem.

---------

Co-authored-by: Manu Sridharan <msridhar@gmail.com>
  • Loading branch information
hjjandy and msridhar authored Jun 26, 2023
1 parent c621140 commit 6f9e6ce
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 8 deletions.
16 changes: 8 additions & 8 deletions core/src/main/java/com/ibm/wala/ipa/callgraph/Entrypoint.java
Original file line number Diff line number Diff line change
Expand Up @@ -70,17 +70,17 @@ public CallSiteReference makeSite(int programCounter) {
return CallSiteReference.make(
programCounter, method.getReference(), IInvokeInstruction.Dispatch.SPECIAL);
} else {
if (method.getDeclaringClass().isInterface()) {
// It is important to check for static methods before interface methods, since if an interface
// contains a static method, it should be called via static dispatch, not interface dispatch
if (method.isStatic()) {
return CallSiteReference.make(
programCounter, method.getReference(), IInvokeInstruction.Dispatch.STATIC);
} else if (method.getDeclaringClass().isInterface()) {
return CallSiteReference.make(
programCounter, method.getReference(), IInvokeInstruction.Dispatch.INTERFACE);
} else {
if (method.isStatic()) {
return CallSiteReference.make(
programCounter, method.getReference(), IInvokeInstruction.Dispatch.STATIC);
} else {
return CallSiteReference.make(
programCounter, method.getReference(), IInvokeInstruction.Dispatch.VIRTUAL);
}
return CallSiteReference.make(
programCounter, method.getReference(), IInvokeInstruction.Dispatch.VIRTUAL);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.ibm.wala.core.tests.callGraph;

import com.ibm.wala.core.tests.util.TestConstants;
import com.ibm.wala.ipa.callgraph.AnalysisCacheImpl;
import com.ibm.wala.ipa.callgraph.AnalysisOptions;
import com.ibm.wala.ipa.callgraph.AnalysisScope;
import com.ibm.wala.ipa.callgraph.CallGraph;
import com.ibm.wala.ipa.callgraph.Entrypoint;
import com.ibm.wala.ipa.callgraph.impl.DefaultEntrypoint;
import com.ibm.wala.ipa.cha.ClassHierarchy;
import com.ibm.wala.ipa.cha.ClassHierarchyException;
import com.ibm.wala.ipa.cha.ClassHierarchyFactory;
import com.ibm.wala.types.ClassLoaderReference;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.CancelException;
import java.io.IOException;
import java.util.List;
import org.junit.Assert;
import org.junit.Test;

public class StaticInterfaceMethodTest {

@Test
public void staticInterfaceMethodAsEntrypoint()
throws ClassHierarchyException, IllegalArgumentException, CancelException, IOException {

AnalysisScope scope =
CallGraphTestUtil.makeJ2SEAnalysisScope(
TestConstants.WALA_TESTDATA, CallGraphTestUtil.REGRESSION_EXCLUSIONS);
ClassHierarchy cha = ClassHierarchyFactory.make(scope);
MethodReference staticInterfaceMethodRef =
MethodReference.findOrCreate(
TypeReference.findOrCreate(
ClassLoaderReference.Application,
"LstaticInterfaceMethod/InterfaceWithStaticMethod"),
"test",
"()V");
Iterable<Entrypoint> entrypoints =
List.of(new DefaultEntrypoint(staticInterfaceMethodRef, cha));
AnalysisOptions options = CallGraphTestUtil.makeAnalysisOptions(scope, entrypoints);

CallGraph cg = CallGraphTestUtil.buildZeroCFA(options, new AnalysisCacheImpl(), cha, false);

Assert.assertEquals(1, cg.getNodes(staticInterfaceMethodRef).size());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package staticInterfaceMethod;

public interface InterfaceWithStaticMethod {
static void test() {}
}

0 comments on commit 6f9e6ce

Please sign in to comment.