Skip to content
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

Rework the call graph algorithm to annote call edges with the corresponding invoke statement #936

Merged
merged 44 commits into from
Jul 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
1553caa
introduces Invokable Statements on edges of the call graph to know th…
JonasKlauke May 17, 2024
4815002
util function to get a get a specific invoke stmt for testing
JonasKlauke May 17, 2024
324ab07
applied API changes
JonasKlauke May 17, 2024
951df14
fixed Javadoc
JonasKlauke May 17, 2024
587c077
fmt
JonasKlauke May 17, 2024
cb74d71
adapted test to new invokable stmt
JonasKlauke May 17, 2024
ece4097
fixed compile issues caused by API changes
JonasKlauke May 17, 2024
1f65974
removed wrong deprecated tag on method
JonasKlauke May 17, 2024
0acc12c
fixed comparison of Optionals instead of the content in the invoke st…
JonasKlauke May 17, 2024
5c39930
fixed warnings
JonasKlauke May 17, 2024
6bbd674
fixed core testcases caused by the invoke expression changes
JonasKlauke May 17, 2024
21e57da
adapted the jimple comparator to the optional in returned by method g…
JonasKlauke May 17, 2024
31da35b
added getEntryMethod to the Callgraph Interface
JonasKlauke May 24, 2024
fbd6300
fixed warnings
JonasKlauke May 24, 2024
dacacaf
added new util method to get assignment statements with static fields
JonasKlauke May 24, 2024
1d3f900
added new util method to get assignment statements with static fields
JonasKlauke May 24, 2024
063f8b9
moved clinit calls of constructor calls to the corresponding new stat…
JonasKlauke May 24, 2024
7927058
assignment statements can invoke a clinit call if it contains a new, …
JonasKlauke May 24, 2024
dec114f
fixed warnings
JonasKlauke May 24, 2024
0b010d3
added method to check if an array is an array of primitives
JonasKlauke May 24, 2024
a2b0c39
fixed warnings
JonasKlauke May 24, 2024
4f25d06
adapted all sootup.callgraph tests to the new invoke statements in th…
JonasKlauke May 24, 2024
1188598
improved call graphs tests
JonasKlauke May 24, 2024
f0fedce
adapted sootup.tests to the changes in the call graph algorithm
JonasKlauke May 24, 2024
fdc5f58
simplified util method in CallGraphTest
JonasKlauke May 24, 2024
2ed9606
removed left op flag from the method that will grab the first fitting…
JonasKlauke May 24, 2024
66706fb
fmt
JonasKlauke May 24, 2024
6e871e7
shorter method names
JonasKlauke May 27, 2024
c5c28bb
made isArrayTypeOfPrimitives public static
JonasKlauke May 27, 2024
189962b
removed duplicate dependency
JonasKlauke May 27, 2024
bb1baa3
introduced invokesStaticInitializer by adapting doesInvokes to only c…
JonasKlauke May 27, 2024
c42a253
added testcase for bug report 903
JonasKlauke May 27, 2024
531b25a
fmt
JonasKlauke May 27, 2024
4d0d78d
Merge remote-tracking branch 'origin/develop' into feature/InvokesCG
JonasKlauke Jun 5, 2024
4529219
fmt
JonasKlauke Jun 5, 2024
796dc7d
added isInvokableStmt and asInvokableStmt to the Stmt API
JonasKlauke Jul 15, 2024
02907b0
fixed warnings
JonasKlauke Jul 15, 2024
38bd53e
moved all Util methods into one class
JonasKlauke Jul 15, 2024
47f0e16
Merge remote-tracking branch 'origin/develop' into feature/InvokesCG
JonasKlauke Jul 15, 2024
c7fcbe9
adapted qilin to the Callgraph API
JonasKlauke Jul 15, 2024
856a318
removed unnecessary import
JonasKlauke Jul 15, 2024
a8e125d
roll back edge class
JonasKlauke Jul 16, 2024
9378770
Merge remote-tracking branch 'origin/develop' into feature/InvokesCG
JonasKlauke Jul 25, 2024
8028c0e
clean up after merge
JonasKlauke Jul 25, 2024
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 @@ -49,6 +49,7 @@ public abstract class AbstractJimpleBasedICFG implements BiDiInterproceduralCFG<
protected LoadingCache<Body, StmtGraph<?>> bodyToStmtGraph =
IDESolver.DEFAULT_CACHE_BUILDER.build(
new CacheLoader<Body, StmtGraph<?>>() {
@Nonnull
@Override
public StmtGraph<?> load(@Nonnull Body body) {
return makeGraph(body);
Expand All @@ -59,6 +60,7 @@ public StmtGraph<?> load(@Nonnull Body body) {
protected LoadingCache<SootMethod, List<Value>> methodToParameterRefs =
IDESolver.DEFAULT_CACHE_BUILDER.build(
new CacheLoader<SootMethod, List<Value>>() {
@Nonnull
@Override
public List<Value> load(@Nonnull SootMethod m) {
return new ArrayList<>(m.getBody().getParameterLocals());
Expand All @@ -69,6 +71,7 @@ public List<Value> load(@Nonnull SootMethod m) {
protected LoadingCache<SootMethod, Set<Stmt>> methodToCallsFromWithin =
IDESolver.DEFAULT_CACHE_BUILDER.build(
new CacheLoader<SootMethod, Set<Stmt>>() {
@Nonnull
@Override
public Set<Stmt> load(@Nonnull SootMethod m) {
return getCallsFromWithinMethod(m);
Expand Down Expand Up @@ -185,7 +188,7 @@ public boolean setOwnerStatement(Stmt u, Body b) {

@Override
public boolean isCallStmt(Stmt stmt) {
return stmt.containsInvokeExpr();
return stmt.isInvokableStmt() && stmt.asInvokableStmt().containsInvokeExpr();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,11 @@ public static Set<Pair<MethodSignature, CalleeMethodSignature>> getCallEdges(
SootMethod method = view.getMethod(caller).orElse(null);
if (method != null && method.hasBody()) {
for (Stmt s : method.getBody().getStmtGraph().getNodes()) {
if (s.containsInvokeExpr()) {
if (s.isInvokableStmt() && s.asInvokableStmt().containsInvokeExpr()) {
AbstractInvokeExpr invokeExpr = s.asInvokableStmt().getInvokeExpr().get();
CalleeMethodSignature callee =
new CalleeMethodSignature(
s.getInvokeExpr().getMethodSignature(),
findCallGraphEdgeType(s.getInvokeExpr()),
s);
invokeExpr.getMethodSignature(), findCallGraphEdgeType(invokeExpr), s);
callEdges.add(new ImmutablePair<>(caller, callee));
}
}
Expand Down Expand Up @@ -116,7 +115,7 @@ public enum CallGraphEdgeType {
/** Due to call to Class.newInstance(..) when reflection log is enabled. */
REFL_CLASS_NEWINSTANCE("REFL_CLASS_NEWINSTANCE");

private String name;
private final String name;

CallGraphEdgeType(String name) {
this.name = name;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,9 @@ public static Map<Integer, MethodSignature> computeCalls(
for (BasicBlock<?> block : blocks) {
List<Stmt> stmts = block.getStmts();
for (Stmt stmt : stmts) {
if (stmt.containsInvokeExpr()) {
MethodSignature target = stmt.getInvokeExpr().getMethodSignature();
if (stmt.isInvokableStmt() && stmt.asInvokableStmt().containsInvokeExpr()) {
MethodSignature target =
stmt.asInvokableStmt().getInvokeExpr().get().getMethodSignature();
int hashCode = stmt.hashCode();
calls.put(hashCode, target);
// compute all the classes that are made to the subclasses as well
Expand Down Expand Up @@ -112,7 +113,7 @@ public static Set<MethodSignature> getMethodSignatureInSubClass(
if (!callGraph.containsMethod(source) || !callGraph.containsMethod(target)) {
return Collections.emptySet();
}
return callGraph.callsFrom(source).stream()
return callGraph.callTargetsFrom(source).stream()
.filter(
methodSignature ->
!methodSignature.equals(target)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@
import sootup.callgraph.CallGraphAlgorithm;
import sootup.callgraph.ClassHierarchyAnalysisAlgorithm;
import sootup.core.graph.StmtGraph;
import sootup.core.jimple.common.expr.AbstractInvokeExpr;
import sootup.core.jimple.common.stmt.InvokableStmt;
import sootup.core.jimple.common.stmt.Stmt;
import sootup.core.model.SootMethod;
import sootup.core.signatures.MethodSignature;
Expand Down Expand Up @@ -71,7 +73,9 @@ public class JimpleBasedInterproceduralCFG extends AbstractJimpleBasedICFG {
@Override
public Collection<SootMethod> load(Stmt stmt) {
ArrayList<SootMethod> res = new ArrayList<>();
MethodSignature methodSignature = stmt.getInvokeExpr().getMethodSignature();
if (!stmt.isInvokableStmt() && !stmt.asInvokableStmt().containsInvokeExpr()) return res;
MethodSignature methodSignature =
stmt.asInvokableStmt().getInvokeExpr().get().getMethodSignature();
Optional<? extends SootMethod> smOpt = view.getMethod(methodSignature);
if (smOpt.isPresent()) {
SootMethod sm = smOpt.get();
Expand Down Expand Up @@ -99,7 +103,7 @@ public Collection<Stmt> load(SootMethod method) {
ArrayList<Stmt> res = new ArrayList<>();
// only retain callers that are explicit call sites or
// Thread.start()
Set<MethodSignature> callsToMethod = cg.callsTo(method.getSignature());
Set<MethodSignature> callsToMethod = cg.callSourcesTo(method.getSignature());
for (MethodSignature methodSignature : callsToMethod) {
Stmt stmt = filterEdgeAndGetCallerStmt(methodSignature);
if (stmt != null) {
Expand Down Expand Up @@ -179,7 +183,7 @@ private void computeAllCalls(
signatureToStmtGraph.put(methodSignature, stmtGraph);
}
}
callGraph.callsFrom(methodSignature).stream()
callGraph.callTargetsFrom(methodSignature).stream()
.filter(methodSignature1 -> !visitedMethods.contains(methodSignature1))
.forEach(
nextMethodSignature ->
Expand Down Expand Up @@ -219,12 +223,13 @@ public static Set<Pair<MethodSignature, CalleeMethodSignature>> getCallEdges(
final SootMethod method = methodOpt.get();
if (method.hasBody()) {
for (Stmt s : method.getBody().getStmtGraph().getNodes()) {
if (s.containsInvokeExpr()) {
// TODO: Consider calls to clinit methods caused by static fields
// Assignment statements without invokeExpressions
if (s instanceof InvokableStmt && ((InvokableStmt) s).containsInvokeExpr()) {
AbstractInvokeExpr expr = ((InvokableStmt) s).getInvokeExpr().get();
CalleeMethodSignature callee =
new CalleeMethodSignature(
s.getInvokeExpr().getMethodSignature(),
CGEdgeUtil.findCallGraphEdgeType(s.getInvokeExpr()),
s);
expr.getMethodSignature(), CGEdgeUtil.findCallGraphEdgeType(expr), s);
callEdges.add(new ImmutablePair<>(caller, callee));
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import sootup.core.jimple.common.expr.JSpecialInvokeExpr;
import sootup.core.jimple.common.expr.JStaticInvokeExpr;
import sootup.core.jimple.common.expr.JVirtualInvokeExpr;
import sootup.core.jimple.common.stmt.InvokableStmt;
import sootup.core.jimple.common.stmt.Stmt;
import sootup.core.jimple.visitor.ExprVisitor;
import sootup.core.model.SootClass;
Expand Down Expand Up @@ -163,7 +164,9 @@ public void testGetCallEdges() {

List<Stmt> invokesStmts =
m.getBody().getStmts().stream()
.filter(Stmt::containsInvokeExpr)
.filter(Stmt::isInvokableStmt)
.map(Stmt::asInvokableStmt)
.filter(InvokableStmt::containsInvokeExpr)
.collect(Collectors.toList());
assertEquals(invokesStmts.size(), 3);
MethodSignature constructorMethodSignature =
Expand Down Expand Up @@ -222,7 +225,10 @@ private void checkPair(
assertNotNull(virtualCall);
Stmt virtualStmt =
invokesStmts.stream()
.filter(stmt -> stmt.getInvokeExpr().getClass() == invokeClass)
.filter(Stmt::isInvokableStmt)
.map(Stmt::asInvokableStmt)
.filter(InvokableStmt::containsInvokeExpr)
.filter(stmt -> stmt.getInvokeExpr().get().getClass() == invokeClass)
.findAny()
.orElse(null);
assertNotNull(virtualStmt);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
public class IFDSTaintAnalysisProblem
extends DefaultJimpleIFDSTabulationProblem<Value, InterproceduralCFG<Stmt, SootMethod>> {

private SootMethod entryMethod;
private final SootMethod entryMethod;

protected InterproceduralCFG<Stmt, SootMethod> icfg;

Expand Down Expand Up @@ -74,18 +74,18 @@ public FlowFunction<Value> getNormalFlowFunction(Stmt curr, Stmt succ) {

@Override
public FlowFunction<Value> getCallFlowFunction(Stmt callStmt, SootMethod destinationMethod) {
return getCallFlow(callStmt, destinationMethod);
return getCallFlow(callStmt.asInvokableStmt(), destinationMethod);
}

@Override
public FlowFunction<Value> getReturnFlowFunction(
Stmt callSite, SootMethod calleeMethod, Stmt exitStmt, Stmt returnSite) {
return getReturnFlow(callSite, calleeMethod, exitStmt, returnSite);
return getReturnFlow(callSite.asInvokableStmt(), calleeMethod, exitStmt, returnSite);
}

@Override
public FlowFunction<Value> getCallToReturnFlowFunction(Stmt callSite, Stmt returnSite) {
return getCallToReturnFlow(callSite, returnSite);
return getCallToReturnFlow(callSite.asInvokableStmt(), returnSite);
}
};
}
Expand All @@ -107,36 +107,33 @@ FlowFunction<Value> getNormalFlow(Stmt curr, Stmt succ) {
return new Gen<>(leftOp, zeroValue());
}
}
return new FlowFunction<Value>() {
@Override
public Set<Value> computeTargets(Value source) {
// source = {v.f*} some local and all its fields
// Kill T = ...
if (source == leftOp) {
return Collections.emptySet();
}
Set<Value> res = new HashSet<Value>();
res.add(source);
// x = T
if (source == rightOp) {
res.add(leftOp);
}
return res;
return source -> {
// source = {v.f*} some local and all its fields
// Kill T = ...
if (source == leftOp) {
return Collections.emptySet();
}
Set<Value> res = new HashSet<>();
res.add(source);
// x = T
if (source == rightOp) {
res.add(leftOp);
}
return res;
};
}
return Identity.v();
}

FlowFunction<Value> getCallFlow(Stmt callStmt, final SootMethod destinationMethod) {
FlowFunction<Value> getCallFlow(InvokableStmt callStmt, final SootMethod destinationMethod) {
if ("<clinit>".equals(destinationMethod.getName())) {
return KillAll.v();
}

AbstractInvokeExpr ie = callStmt.getInvokeExpr();
AbstractInvokeExpr ie = callStmt.getInvokeExpr().get();

final List<Immediate> callArgs = ie.getArgs();
final List<Value> paramLocals = new ArrayList<Value>();
final List<Value> paramLocals = new ArrayList<>();
for (int i = 0; i < destinationMethod.getParameterCount(); i++) {
paramLocals.add(destinationMethod.getBody().getParameterLocal(i));
}
Expand All @@ -154,28 +151,25 @@ FlowFunction<Value> getCallFlow(Stmt callStmt, final SootMethod destinationMetho
}
final Value baseF = base;

return new FlowFunction<Value>() {
@Override
public Set<Value> computeTargets(Value source) {
Set<Value> ret = new HashSet<>();
if (source instanceof JStaticFieldRef) {
ret.add(source);
}
// Tainted func parameters
for (int i = 0; i < callArgs.size(); i++) {
if (callArgs.get(i).equivTo(source) && i < paramLocals.size()) {
ret.add(paramLocals.get(i));
}
return source -> {
Set<Value> ret = new HashSet<>();
if (source instanceof JStaticFieldRef) {
ret.add(source);
}
// Tainted func parameters
for (int i = 0; i < callArgs.size(); i++) {
if (callArgs.get(i).equivTo(source) && i < paramLocals.size()) {
ret.add(paramLocals.get(i));
}
return ret;
}
return ret;
};
}

FlowFunction<Value> getReturnFlow(
final Stmt callSite, final SootMethod calleeMethod, Stmt exitStmt, Stmt returnSite) {
final InvokableStmt callSite, final SootMethod calleeMethod, Stmt exitStmt, Stmt returnSite) {

AbstractInvokeExpr ie = callSite.getInvokeExpr();
AbstractInvokeExpr ie = callSite.getInvokeExpr().get();

Value base = null;
if (ie instanceof JVirtualInvokeExpr) {
Expand Down Expand Up @@ -203,44 +197,38 @@ FlowFunction<Value> getReturnFlow(
}
}
}
return new FlowFunction<Value>() {
@Override
public Set<Value> computeTargets(Value source) {
Set<Value> ret = new HashSet<>();
if (source instanceof JStaticFieldRef) {
ret.add(source);
}
if (callSite instanceof AbstractDefinitionStmt && source == retOp) {
AbstractDefinitionStmt defnStmt = (AbstractDefinitionStmt) callSite;
ret.add(defnStmt.getLeftOp());
}
if (baseF != null && source.equals(calleeMethod.getBody().getThisLocal())) {
ret.add(baseF);
}
return ret;
return source -> {
Set<Value> ret = new HashSet<>();
if (source instanceof JStaticFieldRef) {
ret.add(source);
}
if (callSite instanceof AbstractDefinitionStmt && source == retOp) {
AbstractDefinitionStmt defnStmt = (AbstractDefinitionStmt) callSite;
ret.add(defnStmt.getLeftOp());
}
if (baseF != null && source.equals(calleeMethod.getBody().getThisLocal())) {
ret.add(baseF);
}
return ret;
};
}
if (exitStmt instanceof JReturnVoidStmt) {
return new FlowFunction<Value>() {
@Override
public Set<Value> computeTargets(Value source) {
Set<Value> ret = new HashSet<Value>();
if (source instanceof JStaticFieldRef) {
ret.add(source);
}
if (baseF != null && source.equals(calleeMethod.getBody().getThisLocal())) {
ret.add(baseF);
}
return ret;
return source -> {
Set<Value> ret = new HashSet<>();
if (source instanceof JStaticFieldRef) {
ret.add(source);
}
if (baseF != null && source.equals(calleeMethod.getBody().getThisLocal())) {
ret.add(baseF);
}
return ret;
};
}
return KillAll.v();
}

FlowFunction<Value> getCallToReturnFlow(final Stmt callSite, Stmt returnSite) {
AbstractInvokeExpr ie = callSite.getInvokeExpr();
FlowFunction<Value> getCallToReturnFlow(final InvokableStmt callSite, Stmt returnSite) {
AbstractInvokeExpr ie = callSite.getInvokeExpr().get();
final List<Immediate> callArgs = ie.getArgs();

Value base = null;
Expand All @@ -267,25 +255,22 @@ FlowFunction<Value> getCallToReturnFlow(final Stmt callSite, Stmt returnSite) {

// use assumption if no callees to analyze
if (icfg.getCalleesOfCallAt(callSite).isEmpty()) {
return new FlowFunction<Value>() {
@Override
public Set<Value> computeTargets(Value source) {
Set<Value> ret = new HashSet<Value>();
ret.add(source);
// taint leftOp if base is tainted
if (baseF != null && leftOpF != null && source == baseF) {
ret.add(leftOpF);
}
// taint leftOp if one of the args is tainted
if (leftOpF != null && callArgs.contains(source)) {
ret.add(leftOpF);
}
// taint base if one of the args is tainted and has no callee in known methods
if (baseF != null && callArgs.contains(source)) {
ret.add(baseF);
}
return ret;
return source -> {
Set<Value> ret = new HashSet<>();
ret.add(source);
// taint leftOp if base is tainted
if (baseF != null && leftOpF != null && source == baseF) {
ret.add(leftOpF);
}
// taint leftOp if one of the args is tainted
if (leftOpF != null && callArgs.contains(source)) {
ret.add(leftOpF);
}
// taint base if one of the args is tainted and has no callee in known methods
if (baseF != null && callArgs.contains(source)) {
ret.add(baseF);
}
return ret;
};
}
return Identity.v();
Expand Down
Loading
Loading