All Downloads are FREE. Search and download functionalities are using the official Maven repository.

boomerang.callgraph.BoomerangResolver Maven / Gradle / Ivy

There is a newer version: 3.2.2
Show newest version
package boomerang.callgraph;

import boomerang.BackwardQuery;
import boomerang.Boomerang;
import boomerang.ForwardQuery;
import boomerang.Query;
import boomerang.SolverCreationListener;
import boomerang.WeightedBoomerang;
import boomerang.results.ExtractAllocationSiteStateListener;
import boomerang.scene.CallGraph;
import boomerang.scene.ControlFlowGraph.Edge;
import boomerang.scene.DataFlowScope;
import boomerang.scene.DeclaredMethod;
import boomerang.scene.InvokeExpr;
import boomerang.scene.Method;
import boomerang.scene.Statement;
import boomerang.scene.Type;
import boomerang.scene.Val;
import boomerang.scene.WrappedClass;
import boomerang.solver.AbstractBoomerangSolver;
import boomerang.solver.ForwardBoomerangSolver;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sync.pds.solver.nodes.INode;
import sync.pds.solver.nodes.Node;
import wpds.impl.Weight;

public class BoomerangResolver implements ICallerCalleeResolutionStrategy {
  public static final Factory FACTORY = (solver, cg) -> new BoomerangResolver(solver, cg);

  private static final Logger logger = LoggerFactory.getLogger(BoomerangResolver.class);

  public enum NoCalleeFoundFallbackOptions {
    PRECOMPUTED,
    BYPASS
  };

  private static final String THREAD_CLASS = "java.lang.Thread";
  private static final String THREAD_START_SIGNATURE = "";
  private static final String THREAD_RUN_SUB_SIGNATURE = "void run()";

  private static NoCalleeFoundFallbackOptions FALLBACK_OPTION = NoCalleeFoundFallbackOptions.BYPASS;
  private static Multimap didNotFindMethodLog = HashMultimap.create();

  private CallGraph precomputedCallGraph;
  private WeightedBoomerang solver;
  private Set queriedInvokeExprAndAllocationSitesFound = Sets.newHashSet();
  private Set queriedInvokeExpr = Sets.newHashSet();
  ;

  public BoomerangResolver(CallGraph cg, DataFlowScope scope) {
    this.solver = new Boomerang(cg, scope);
    this.precomputedCallGraph = cg;
  }

  public BoomerangResolver(WeightedBoomerang solver, CallGraph initialCallGraph) {
    this(solver, true, initialCallGraph);
  }

  public BoomerangResolver(
      WeightedBoomerang solver,
      boolean enableExceptions,
      CallGraph initialCallGraph) {
    this.solver = solver;
    this.precomputedCallGraph = initialCallGraph;
  }

  @Override
  public void computeFallback(ObservableDynamicICFG observableDynamicICFG) {
    int refined = 0;
    int precomputed = 0;
    for (Statement s : Lists.newArrayList(queriedInvokeExpr)) {
      if (!queriedInvokeExprAndAllocationSitesFound.contains(s)) {
        logger.debug("Call graph ends at {}", s);
        precomputed++;
        if (FALLBACK_OPTION == NoCalleeFoundFallbackOptions.PRECOMPUTED) {
          for (CallGraph.Edge e : precomputedCallGraph.edgesOutOf(s)) {
            // TODO Refactor. Should not be required, if the backward analysis is sound (data-flow
            // of static fields)
            observableDynamicICFG.addCallIfNotInGraph(e.src(), e.tgt());
          }
        }
        if (FALLBACK_OPTION == NoCalleeFoundFallbackOptions.BYPASS) {
          observableDynamicICFG.notifyNoCalleeFound(s);
        }
      } else {
        refined++;
      }
    }
    logger.debug("Refined edges {}, fallback to precomputed {}", refined, precomputed);
  }

  @Override
  public Method resolveSpecialInvoke(InvokeExpr ie) {
    Collection methodFromClassOrFromSuperclass =
        getMethodFromClassOrFromSuperclass(ie.getMethod(), ie.getMethod().getDeclaringClass());
    if (methodFromClassOrFromSuperclass.size() > 1) {
      throw new RuntimeException(
          "Illegal state, a special call should exactly resolve to one target");
    }
    return Iterables.getFirst(methodFromClassOrFromSuperclass, null);
  }

  @Override
  public Method resolveStaticInvoke(InvokeExpr ie) {
    Collection methodFromClassOrFromSuperclass =
        getMethodFromClassOrFromSuperclass(ie.getMethod(), ie.getMethod().getDeclaringClass());
    if (methodFromClassOrFromSuperclass.size() > 1) {
      throw new RuntimeException(
          "Illegal state, a static call should exactly resolve to one target");
    }
    return Iterables.getFirst(methodFromClassOrFromSuperclass, null);
  }

  @Override
  public Collection resolveInstanceInvoke(Statement stmt) {
    return queryForCallees(stmt);
  }

  private Collection queryForCallees(Statement resolvingStmt) {
    logger.debug("Queried for callees of '{}'.", resolvingStmt);
    // Construct BackwardQuery, so we know which types the object might have
    InvokeExpr invokeExpr = resolvingStmt.getInvokeExpr();
    queriedInvokeExpr.add(resolvingStmt);
    Val value = invokeExpr.getBase();

    Collection res = new ArrayList<>();

    // Not using cfg here because we are iterating backward
    for (Statement pred :
        resolvingStmt.getMethod().getControlFlowGraph().getPredsOf(resolvingStmt)) {
      BackwardQuery query = BackwardQuery.make(new Edge(pred, resolvingStmt), value);
      solver.solve(query, false);
      res.addAll(forAnyAllocationSiteOfQuery(query, resolvingStmt, pred));
    }

    return res;
  }

  @SuppressWarnings("rawtypes")
  private Collection forAnyAllocationSiteOfQuery(
      BackwardQuery query, Statement resolvingStmt, Statement callSite) {
    IterateSolvers callback = new IterateSolvers(query, callSite, resolvingStmt);
    solver.registerSolverCreationListener(callback);
    return callback.results;
  }

  private Collection getMethodFromClassOrFromSuperclass(
      DeclaredMethod method, WrappedClass sootClass) {
    Set res = Sets.newHashSet();
    WrappedClass originalClass = sootClass;
    while (sootClass != null) {
      for (Method candidate : sootClass.getMethods()) {
        if (candidate.getSubSignature().equals(method.getSubSignature())) {
          res.add(candidate);
        }
      }
      handlingForThreading(method, sootClass, res);
      if (!res.isEmpty()) return res;
      if (sootClass.hasSuperclass()) {
        sootClass = sootClass.getSuperclass();
      } else {
        logDidNotFindMethod(method, originalClass);
        return res;
      }
    }
    logDidNotFindMethod(method, originalClass);
    return res;
  }

  private void logDidNotFindMethod(DeclaredMethod method, WrappedClass originalClass) {
    if (didNotFindMethodLog.put(method, originalClass)) {
      logger.debug("Did not find method {} for class {}", method, originalClass);
    }
  }

  private void handlingForThreading(
      DeclaredMethod method, WrappedClass sootClass, Set res) {
    // throw new RuntimeException("Threading not implemented");
    // if (Scene.v().getFastHierarchy().isSubclass(sootClass,
    // Scene.v().getSootClass(THREAD_CLASS)))
    // {
    // if (method.getSignature().equals(THREAD_START_SIGNATURE)) {
    // for (SootMethod candidate : sootClass.getMethods()) {
    // if (candidate.getSubSignature().equals(THREAD_RUN_SUB_SIGNATURE)) {
    // res.add(candidate);
    // }
    // }
    // }
    // }
  }

  private final class IterateSolvers implements SolverCreationListener {
    private final BackwardQuery query;
    private final Statement invokeExpr;
    private final Collection results = new ArrayList<>();

    private IterateSolvers(BackwardQuery query, Statement unit, Statement invokeExpr) {
      this.query = query;
      this.invokeExpr = invokeExpr;
    }

    @Override
    public void onCreatedSolver(Query q, AbstractBoomerangSolver solver) {
      if (solver instanceof ForwardBoomerangSolver) {
        ForwardQuery forwardQuery = (ForwardQuery) q;
        ForwardBoomerangSolver forwardBoomerangSolver = (ForwardBoomerangSolver) solver;
        for (INode> initialState :
            forwardBoomerangSolver.getFieldAutomaton().getInitialStates()) {
          forwardBoomerangSolver
              .getFieldAutomaton()
              .registerListener(
                  new ExtractAllocationSiteStateListener(initialState, query, (ForwardQuery) q) {

                    @Override
                    protected void allocationSiteFound(
                        ForwardQuery allocationSite, BackwardQuery query) {
                      logger.debug("Found AllocationSite '{}'.", forwardQuery);
                      queriedInvokeExprAndAllocationSitesFound.add(invokeExpr);
                      Type type = forwardQuery.getType();
                      if (type.isRefType()) {
                        for (Method calleeMethod :
                            getMethodFromClassOrFromSuperclass(
                                invokeExpr.getInvokeExpr().getMethod(), type.getWrappedClass())) {
                          results.add(calleeMethod);
                        }
                      } else if (type.isArrayType()) {
                        Type base = type.getArrayBaseType();
                        if (base.isRefType()) {
                          for (Method calleeMethod :
                              getMethodFromClassOrFromSuperclass(
                                  invokeExpr.getInvokeExpr().getMethod(), base.getWrappedClass())) {
                            results.add(calleeMethod);
                          }
                        }
                      }
                    }
                    ;
                  });
        }
      }
    }

    @Override
    public int hashCode() {
      final int prime = 31;
      int result = 1;
      result = prime * result + getOuterType().hashCode();
      result = prime * result + ((query == null) ? 0 : query.hashCode());
      result = prime * result + ((invokeExpr == null) ? 0 : invokeExpr.hashCode());
      return result;
    }

    @Override
    public boolean equals(Object obj) {
      if (this == obj) return true;
      if (obj == null) return false;
      if (getClass() != obj.getClass()) return false;
      IterateSolvers other = (IterateSolvers) obj;
      if (!getOuterType().equals(other.getOuterType())) return false;
      if (query == null) {
        if (other.query != null) return false;
      } else if (!query.equals(other.query)) return false;
      if (invokeExpr == null) {
        if (other.invokeExpr != null) return false;
      } else if (!invokeExpr.equals(other.invokeExpr)) return false;
      return true;
    }

    private BoomerangResolver getOuterType() {
      // TODO why is this type of importance?
      return BoomerangResolver.this;
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy