688 lines
24 KiB
Java
688 lines
24 KiB
Java
/*
|
|
* This program and the accompanying materials
|
|
* are made available under the terms of the Eclipse Public License v1.0
|
|
* which accompanies this distribution, and is available at
|
|
* http://www.eclipse.org/legal/epl-v10.html.
|
|
*
|
|
* This file is a derivative of code released under the terms listed below.
|
|
*
|
|
*/
|
|
/**
|
|
*
|
|
* Copyright (c) 2009-2012,
|
|
*
|
|
* Adam Fuchs <afuchs@cs.umd.edu>
|
|
* Avik Chaudhuri <avik@cs.umd.edu>
|
|
* Steve Suh <suhsteve@gmail.com>
|
|
*
|
|
* Galois, Inc. (Aaron Tomb <atomb@galois.com>, Rogan Creswick <creswick@galois.com>, Adam Foltzer <acfoltzer@galois.com>)
|
|
*
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
*
|
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
* and/or other materials provided with the distribution.
|
|
*
|
|
* 3. The names of the contributors may not be used to endorse or promote
|
|
* products derived from this software without specific prior written
|
|
* permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
*
|
|
*/
|
|
|
|
package org.scandroid.flow;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.Collection;
|
|
import java.util.HashSet;
|
|
import java.util.Iterator;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Set;
|
|
|
|
import org.scandroid.domain.DomainElement;
|
|
import org.scandroid.domain.IFDSTaintDomain;
|
|
import org.scandroid.domain.InstanceKeyElement;
|
|
import org.scandroid.domain.LocalElement;
|
|
import org.scandroid.domain.ReturnElement;
|
|
import org.scandroid.flow.types.FlowType;
|
|
import org.scandroid.flow.types.ParameterFlow;
|
|
import org.scandroid.flow.types.ReturnFlow;
|
|
import org.scandroid.spec.CallArgSinkSpec;
|
|
import org.scandroid.spec.EntryArgSinkSpec;
|
|
import org.scandroid.spec.EntryRetSinkSpec;
|
|
import org.scandroid.spec.ISpecs;
|
|
import org.scandroid.spec.SinkSpec;
|
|
import org.scandroid.spec.StaticFieldSinkSpec;
|
|
import org.scandroid.util.CGAnalysisContext;
|
|
import org.slf4j.Logger;
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
import com.ibm.wala.classLoader.IMethod;
|
|
import com.ibm.wala.dataflow.IFDS.ICFGSupergraph;
|
|
import com.ibm.wala.dataflow.IFDS.ISupergraph;
|
|
import com.ibm.wala.dataflow.IFDS.TabulationResult;
|
|
import com.ibm.wala.ipa.callgraph.CGNode;
|
|
import com.ibm.wala.ipa.callgraph.CallGraph;
|
|
import com.ibm.wala.ipa.callgraph.impl.Everywhere;
|
|
import com.ibm.wala.ipa.callgraph.propagation.InstanceKey;
|
|
import com.ibm.wala.ipa.callgraph.propagation.LocalPointerKey;
|
|
import com.ibm.wala.ipa.callgraph.propagation.PointerAnalysis;
|
|
import com.ibm.wala.ipa.callgraph.propagation.PointerKey;
|
|
import com.ibm.wala.ipa.cfg.BasicBlockInContext;
|
|
import com.ibm.wala.ipa.cha.ClassHierarchy;
|
|
import com.ibm.wala.ssa.SSAInstruction;
|
|
import com.ibm.wala.ssa.SSAInstruction.Visitor;
|
|
import com.ibm.wala.ssa.SSAInvokeInstruction;
|
|
import com.ibm.wala.ssa.SSAReturnInstruction;
|
|
import com.ibm.wala.ssa.analysis.IExplodedBasicBlock;
|
|
import com.ibm.wala.types.MethodReference;
|
|
import com.ibm.wala.util.collections.HashMapFactory;
|
|
import com.ibm.wala.util.collections.HashSetFactory;
|
|
import com.ibm.wala.util.collections.IteratorUtil;
|
|
import com.ibm.wala.util.debug.UnimplementedError;
|
|
import com.ibm.wala.util.intset.IntSet;
|
|
|
|
/**
|
|
* @author acfoltzer
|
|
*
|
|
*/
|
|
public class OutflowAnalysis {
|
|
private static final Logger logger = LoggerFactory
|
|
.getLogger(OutflowAnalysis.class);
|
|
|
|
private final CGAnalysisContext<IExplodedBasicBlock> ctx;
|
|
private final CallGraph cg;
|
|
private final ClassHierarchy cha;
|
|
private final PointerAnalysis<InstanceKey> pa;
|
|
private final ICFGSupergraph graph;
|
|
private final ISpecs specs;
|
|
|
|
public OutflowAnalysis(CGAnalysisContext<IExplodedBasicBlock> ctx,
|
|
ISpecs specs) {
|
|
this.ctx = ctx;
|
|
this.cg = ctx.cg;
|
|
this.cha = ctx.getClassHierarchy();
|
|
this.pa = ctx.pa;
|
|
this.graph = (ICFGSupergraph) ctx.graph;
|
|
this.specs = specs;
|
|
}
|
|
|
|
private void addEdge(
|
|
Map<FlowType<IExplodedBasicBlock>, Set<FlowType<IExplodedBasicBlock>>> graph,
|
|
FlowType<IExplodedBasicBlock> source,
|
|
FlowType<IExplodedBasicBlock> dest) {
|
|
Set<FlowType<IExplodedBasicBlock>> dests = graph.get(source);
|
|
if (dests == null) {
|
|
dests = new HashSet<FlowType<IExplodedBasicBlock>>();
|
|
graph.put(source, dests);
|
|
}
|
|
dests.add(dest);
|
|
logger.debug("added edge from {} to {}", source, dest);
|
|
}
|
|
|
|
@SuppressWarnings({ "unused", "unchecked" })
|
|
private void processArgSinks(
|
|
TabulationResult<BasicBlockInContext<IExplodedBasicBlock>, CGNode, DomainElement> flowResult,
|
|
IFDSTaintDomain<IExplodedBasicBlock> domain,
|
|
Map<FlowType<IExplodedBasicBlock>, Set<FlowType<IExplodedBasicBlock>>> flowGraph,
|
|
List<SinkSpec> sinkSpecs) {
|
|
List<Collection<IMethod>> targetList = new ArrayList<Collection<IMethod>>();
|
|
|
|
for (int i = 0; i < sinkSpecs.size(); i++) {
|
|
Collection<IMethod> tempList = sinkSpecs.get(i).getNamePattern()
|
|
.getPossibleTargets(cha);
|
|
targetList.add(tempList);
|
|
}
|
|
|
|
// look for all uses of query function and taint the results with the
|
|
// Uri used in those functions
|
|
Iterator<BasicBlockInContext<IExplodedBasicBlock>> graphIt = graph
|
|
.iterator();
|
|
while (graphIt.hasNext()) {
|
|
BasicBlockInContext<IExplodedBasicBlock> block = graphIt.next();
|
|
|
|
Iterator<SSAInvokeInstruction> invokeInstrs = IteratorUtil.filter(
|
|
block.iterator(), SSAInvokeInstruction.class);
|
|
|
|
while (invokeInstrs.hasNext()) {
|
|
SSAInvokeInstruction invInst = invokeInstrs.next();
|
|
|
|
for (IMethod target : cha.getPossibleTargets(invInst
|
|
.getDeclaredTarget())) {
|
|
|
|
for (int i = 0; i < targetList.size(); i++) {
|
|
if (!targetList.get(i).contains(target)) {
|
|
continue;
|
|
}
|
|
logger.debug("Found target: " + target);
|
|
int[] argNums = sinkSpecs.get(i).getArgNums();
|
|
|
|
if (null == argNums) {
|
|
int staticIndex = 0;
|
|
if (target.isStatic()) {
|
|
staticIndex = 1;
|
|
}
|
|
|
|
int targetParamCount = target
|
|
.getNumberOfParameters() - staticIndex;
|
|
argNums = SinkSpec.getNewArgNums(targetParamCount);
|
|
}
|
|
|
|
CGNode node = block.getNode();
|
|
|
|
IntSet resultSet = flowResult.getResult(block);
|
|
for (int j = 0; j < argNums.length; j++) {
|
|
logger.debug("Looping over arg[" + j + "] of "
|
|
+ argNums.length);
|
|
|
|
// The set of flow types we're looking for:
|
|
Set<FlowType<IExplodedBasicBlock>> taintTypeSet = HashSetFactory.make();
|
|
|
|
LocalElement le = new LocalElement(
|
|
invInst.getUse(argNums[j]));
|
|
Set<DomainElement> elements = domain
|
|
.getPossibleElements(le);
|
|
if (elements != null) {
|
|
for (DomainElement de : elements) {
|
|
if (resultSet.contains(domain
|
|
.getMappedIndex(de))) {
|
|
logger.debug("added to taintTypeSpecs: "
|
|
+ de.taintSource);
|
|
taintTypeSet.add(de.taintSource);
|
|
}
|
|
}
|
|
}
|
|
|
|
LocalPointerKey lpkey = new LocalPointerKey(node,
|
|
invInst.getUse(argNums[j]));
|
|
for (InstanceKey ik : pa.getPointsToSet(lpkey)) {
|
|
for (DomainElement de : domain
|
|
.getPossibleElements(new InstanceKeyElement(
|
|
ik))) {
|
|
if (resultSet.contains(domain
|
|
.getMappedIndex(de))) {
|
|
logger.debug("added to taintTypeSpecs: "
|
|
+ de.taintSource);
|
|
taintTypeSet.add(de.taintSource);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (FlowType<IExplodedBasicBlock> dest : sinkSpecs
|
|
.get(i).getFlowType(block)) {
|
|
for (FlowType<IExplodedBasicBlock> source : taintTypeSet) {
|
|
logger.debug("added edge: " + source
|
|
+ " \n \tto \n\t" + dest);
|
|
// flow taint into uriIK
|
|
addEdge(flowGraph, source, dest);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@SuppressWarnings({ "unused", "unchecked" })
|
|
private void processEntryArgs(
|
|
TabulationResult<BasicBlockInContext<IExplodedBasicBlock>, CGNode, DomainElement> flowResult,
|
|
IFDSTaintDomain<IExplodedBasicBlock> domain,
|
|
Map<FlowType<IExplodedBasicBlock>, Set<FlowType<IExplodedBasicBlock>>> flowGraph,
|
|
SinkSpec ss) {
|
|
|
|
int[] newArgNums;
|
|
for (IMethod im : ss.getNamePattern().getPossibleTargets(cha)) {
|
|
// look for a tainted reply
|
|
|
|
CGNode node = cg.getNode(im, Everywhere.EVERYWHERE);
|
|
if (node == null) {
|
|
logger.warn("null CGNode for {}", im.getSignature());
|
|
continue;
|
|
}
|
|
|
|
BasicBlockInContext<IExplodedBasicBlock>[] entriesForProcedure = graph
|
|
.getEntriesForProcedure(node);
|
|
if (entriesForProcedure == null || 0 == entriesForProcedure.length) {
|
|
logger.warn("procedure without entries {}", im.getSignature());
|
|
continue;
|
|
}
|
|
if (1 != entriesForProcedure.length) {
|
|
logger.error("More than one procedure entry. (Are you sure you're using an ICFGSupergraph?)");
|
|
}
|
|
BasicBlockInContext<IExplodedBasicBlock> entryBlock = entriesForProcedure[0];
|
|
|
|
newArgNums = ss.getArgNums();
|
|
if (null == newArgNums) {
|
|
int staticIndex = 1;
|
|
if (im.isStatic()) {
|
|
staticIndex = 0;
|
|
}
|
|
int targetParamCount = im.getNumberOfParameters() - staticIndex;
|
|
|
|
newArgNums = SinkSpec.getNewArgNums(targetParamCount);
|
|
}
|
|
// for (BasicBlockInContext<E> block:
|
|
// graph.getExitsForProcedure(node) ) {
|
|
// IntIterator itr = flowResult.getResult(block).intIterator();
|
|
// while (itr.hasNext()) {
|
|
// int i = itr.next();
|
|
// logger.debug("domain element at exit: "+domain.getMappedObject(i));
|
|
//
|
|
//
|
|
// }
|
|
// }
|
|
for (int i = 0; i < newArgNums.length; i++) {
|
|
|
|
// see if anything flowed into the args as sinks:
|
|
for (DomainElement de : domain
|
|
.getPossibleElements(new LocalElement(node.getIR()
|
|
.getParameter(newArgNums[i])))) {
|
|
|
|
for (BasicBlockInContext<IExplodedBasicBlock> block : graph
|
|
.getExitsForProcedure(node)) {
|
|
|
|
int mappedIndex = domain.getMappedIndex(de);
|
|
if (flowResult.getResult(block).contains(mappedIndex)) {
|
|
addEdge(flowGraph, de.taintSource,
|
|
new ParameterFlow<IExplodedBasicBlock>(
|
|
entryBlock, newArgNums[i], false));
|
|
}
|
|
}
|
|
|
|
int mappedIndex = domain.getMappedIndex(de);
|
|
if (flowResult.getResult(entryBlock).contains(mappedIndex)) {
|
|
addEdge(flowGraph, de.taintSource,
|
|
new ParameterFlow<IExplodedBasicBlock>(
|
|
entryBlock, newArgNums[i], false));
|
|
}
|
|
|
|
}
|
|
for (InstanceKey ik : pa.getPointsToSet(new LocalPointerKey(
|
|
node, node.getIR().getParameter(newArgNums[i])))) {
|
|
for (DomainElement de : domain
|
|
.getPossibleElements(new InstanceKeyElement(ik))) {
|
|
if (flowResult.getResult(entryBlock).contains(
|
|
domain.getMappedIndex(de))) {
|
|
logger.trace("found outflow in second EntryArgSink loop");
|
|
addEdge(flowGraph, de.taintSource,
|
|
new ParameterFlow<IExplodedBasicBlock>(
|
|
entryBlock, newArgNums[i], false));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@SuppressWarnings({ "unused", "unchecked" })
|
|
private void processEntryRets(
|
|
TabulationResult<BasicBlockInContext<IExplodedBasicBlock>, CGNode, DomainElement> flowResult,
|
|
IFDSTaintDomain<IExplodedBasicBlock> domain,
|
|
Map<FlowType<IExplodedBasicBlock>, Set<FlowType<IExplodedBasicBlock>>> flowGraph,
|
|
SinkSpec ss) {
|
|
|
|
for (IMethod im : ss.getNamePattern().getPossibleTargets(cha)) {
|
|
// look for a tainted reply
|
|
|
|
CGNode node = cg.getNode(im, Everywhere.EVERYWHERE);
|
|
|
|
if (node == null) {
|
|
logger.warn("could not find CGNode for SinkSpec {}", ss);
|
|
continue;
|
|
}
|
|
|
|
BasicBlockInContext<IExplodedBasicBlock>[] exitsForProcedure = graph
|
|
.getExitsForProcedure(node);
|
|
if (exitsForProcedure == null || 0 == exitsForProcedure.length) {
|
|
logger.warn("could not find exit blocks for SinkSpec {}", ss);
|
|
continue;
|
|
}
|
|
|
|
final Set<DomainElement> possibleElements = domain
|
|
.getPossibleElements(new ReturnElement());
|
|
logger.debug("{} possible elements found for ReturnElement",
|
|
possibleElements.size());
|
|
for (DomainElement de : possibleElements) {
|
|
logger.debug("processing domain element {}", de);
|
|
for (BasicBlockInContext<IExplodedBasicBlock> block : exitsForProcedure) {
|
|
logger.debug("{} instructions in block",
|
|
block.getLastInstructionIndex());
|
|
if (flowResult.getResult(block).contains(
|
|
domain.getMappedIndex(de))) {
|
|
logger.debug("original block has edge");
|
|
addEdge(flowGraph, de.taintSource,
|
|
new ReturnFlow<IExplodedBasicBlock>(block,
|
|
false));
|
|
}
|
|
// Iterator<BasicBlockInContext<E>> it =
|
|
// graph.getPredNodes(block);
|
|
// while (it.hasNext()) {
|
|
// BasicBlockInContext<E> realBlock = it.next();
|
|
// if (realBlock.isExitBlock()) {
|
|
// logger.warn("found edge to exit");
|
|
// // addEdge(flowGraph,de.taintSource, new
|
|
// ReturnFlow<E>(realBlock, false));
|
|
// }
|
|
// if(flowResult.getResult(realBlock).contains(domain.getMappedIndex(de)))
|
|
// {
|
|
// logger.debug("adding edge from {} to ReturnFlow",
|
|
// de.taintSource);
|
|
// addEdge(flowGraph,de.taintSource, new
|
|
// ReturnFlow<E>(realBlock, false));
|
|
// } else {
|
|
// logger.debug("no edge from block {} for {}", realBlock,
|
|
// de);
|
|
// }
|
|
}
|
|
}
|
|
|
|
for (BasicBlockInContext<IExplodedBasicBlock> block : exitsForProcedure) {
|
|
Iterator<BasicBlockInContext<IExplodedBasicBlock>> it = graph
|
|
.getPredNodes(block);
|
|
while (it.hasNext()) {
|
|
BasicBlockInContext<IExplodedBasicBlock> realBlock = it
|
|
.next();
|
|
final SSAInstruction inst = realBlock.getLastInstruction();
|
|
if (null != inst && inst instanceof SSAReturnInstruction) {
|
|
PointerKey pk = new LocalPointerKey(node,
|
|
inst.getUse(0));
|
|
for (InstanceKey ik : pa.getPointsToSet(pk)) {
|
|
for (DomainElement ikElement : domain
|
|
.getPossibleElements(new InstanceKeyElement(
|
|
ik))) {
|
|
if (flowResult.getResult(realBlock).contains(
|
|
domain.getMappedIndex(ikElement))) {
|
|
addEdge(flowGraph,
|
|
ikElement.taintSource,
|
|
new ReturnFlow<IExplodedBasicBlock>(
|
|
realBlock, false));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public Map<FlowType<IExplodedBasicBlock>, Set<FlowType<IExplodedBasicBlock>>> analyze(
|
|
TabulationResult<BasicBlockInContext<IExplodedBasicBlock>, CGNode, DomainElement> flowResult,
|
|
IFDSTaintDomain<IExplodedBasicBlock> domain) {
|
|
return analyze(ctx.cg, ctx.getClassHierarchy(), ctx.graph, ctx.pa,
|
|
flowResult, domain, specs);
|
|
}
|
|
|
|
public Map<FlowType<IExplodedBasicBlock>, Set<FlowType<IExplodedBasicBlock>>> analyze(
|
|
CallGraph cg,
|
|
ClassHierarchy cha,
|
|
ISupergraph<BasicBlockInContext<IExplodedBasicBlock>, CGNode> graph,
|
|
PointerAnalysis<InstanceKey> pa,
|
|
TabulationResult<BasicBlockInContext<IExplodedBasicBlock>, CGNode, DomainElement> flowResult,
|
|
IFDSTaintDomain<IExplodedBasicBlock> domain, ISpecs s) {
|
|
|
|
logger.debug("****************************");
|
|
logger.debug("* Running outflow analysis *");
|
|
logger.debug("****************************");
|
|
|
|
Map<FlowType<IExplodedBasicBlock>, Set<FlowType<IExplodedBasicBlock>>> taintFlow = HashMapFactory.make();
|
|
|
|
SinkSpec[] ss = s.getSinkSpecs();
|
|
logger.debug(ss.length + " sink Specs. ");
|
|
|
|
for (int i = 0; i < ss.length; i++) {
|
|
if (ss[i] instanceof EntryArgSinkSpec)
|
|
processSinkSpec(flowResult, domain, taintFlow, ss[i]);
|
|
else if (ss[i] instanceof CallArgSinkSpec)
|
|
processSinkSpec(flowResult, domain, taintFlow, ss[i]);
|
|
else if (ss[i] instanceof EntryRetSinkSpec)
|
|
processSinkSpec(flowResult, domain, taintFlow, ss[i]);
|
|
else if (ss[i] instanceof StaticFieldSinkSpec)
|
|
processSinkSpec(flowResult, domain, taintFlow, ss[i]);
|
|
else
|
|
throw new UnsupportedOperationException(
|
|
"SinkSpec not yet Implemented");
|
|
}
|
|
|
|
logger.info("************");
|
|
logger.info("* Results: *");
|
|
logger.info("************");
|
|
|
|
logger.debug("{}", taintFlow.toString());
|
|
|
|
/* TODO: re-enable this soon! */
|
|
/*
|
|
* for(Entry<FlowType,Set<FlowType>> e: taintFlow.entrySet()) {
|
|
* WalaGraphToJGraphT walaJgraphT = new WalaGraphToJGraphT(flowResult,
|
|
* domain, e.getKey(), graph, cg); logger.debug("Source: " +
|
|
* e.getKey()); for(FlowType target:e.getValue()) {
|
|
* logger.debug("\t=> Sink: " + target); //logger.debug("SourceNode: "+
|
|
* e.getKey().getRelevantNode() +
|
|
* "\nSinkNode: "+target.getRelevantNode());
|
|
* walaJgraphT.calcPath(e.getKey().getRelevantNode(),
|
|
* target.getRelevantNode()); Iterator<DefaultEdge> edgeI =
|
|
* walaJgraphT.getPath().getEdgeList().iterator(); if (edgeI.hasNext())
|
|
* logger.debug("\t::Method Trace::"); int counter = 1; while
|
|
* (edgeI.hasNext()) { DefaultEdge edge = edgeI.next();
|
|
* logger.debug("\t\t#"+counter+": " +
|
|
* walaJgraphT.getJGraphT().getEdgeSource
|
|
* (edge).getMethod().getSignature() + " ==> " +
|
|
* walaJgraphT.getJGraphT()
|
|
* .getEdgeTarget(edge).getMethod().getSignature()); }
|
|
*
|
|
* } }
|
|
*/
|
|
|
|
return taintFlow;
|
|
}
|
|
|
|
private void processSinkSpec(
|
|
TabulationResult<BasicBlockInContext<IExplodedBasicBlock>, CGNode, DomainElement> flowResult,
|
|
IFDSTaintDomain<IExplodedBasicBlock> domain,
|
|
Map<FlowType<IExplodedBasicBlock>, Set<FlowType<IExplodedBasicBlock>>> flowGraph,
|
|
SinkSpec ss) {
|
|
Set<ISinkPoint> sinkPoints = calculateSinkPoints(ss);
|
|
if (!(ss instanceof StaticFieldSinkSpec)) {
|
|
logger.debug("for {}, sinkPoints={}", ss, sinkPoints);
|
|
}
|
|
for (ISinkPoint sinkPoint : sinkPoints) {
|
|
for (FlowType<IExplodedBasicBlock> source : sinkPoint.findSources(
|
|
ctx, flowResult, domain)) {
|
|
addEdge(flowGraph, source, sinkPoint.getFlow());
|
|
}
|
|
}
|
|
}
|
|
|
|
private Set<ISinkPoint> calculateSinkPoints(SinkSpec sinkSpec) {
|
|
if (sinkSpec instanceof EntryArgSinkSpec) {
|
|
return calculateSinkPoints((EntryArgSinkSpec) sinkSpec);
|
|
}
|
|
if (sinkSpec instanceof CallArgSinkSpec) {
|
|
return calculateSinkPoints((CallArgSinkSpec) sinkSpec);
|
|
}
|
|
if (sinkSpec instanceof EntryRetSinkSpec) {
|
|
return calculateSinkPoints((EntryRetSinkSpec) sinkSpec);
|
|
}
|
|
if (sinkSpec instanceof StaticFieldSinkSpec) {
|
|
return calculateSinkPoints((StaticFieldSinkSpec) sinkSpec);
|
|
}
|
|
throw new UnimplementedError();
|
|
}
|
|
|
|
private Set<ISinkPoint> calculateSinkPoints(EntryArgSinkSpec sinkSpec) {
|
|
Set<ISinkPoint> points = HashSetFactory.make();
|
|
|
|
Collection<IMethod> methods = sinkSpec.getNamePattern()
|
|
.getPossibleTargets(cha);
|
|
if (null == methods) {
|
|
logger.warn("no methods found for sink spec {}", sinkSpec);
|
|
}
|
|
|
|
for (IMethod method : methods) {
|
|
for (CGNode node : cg.getNodes(method.getReference())) {
|
|
BasicBlockInContext<IExplodedBasicBlock> entryBlock = graph
|
|
.getICFG().getEntry(node);
|
|
BasicBlockInContext<IExplodedBasicBlock> exitBlock = graph
|
|
.getICFG().getExit(node);
|
|
for (int argNum : sinkSpec.getArgNums()) {
|
|
final int ssaVal = node.getIR().getParameter(argNum);
|
|
final ParameterFlow<IExplodedBasicBlock> sinkFlow = new ParameterFlow<IExplodedBasicBlock>(
|
|
entryBlock, argNum, false);
|
|
final LocalSinkPoint sinkPoint = new LocalSinkPoint(
|
|
exitBlock, ssaVal, sinkFlow);
|
|
points.add(sinkPoint);
|
|
}
|
|
}
|
|
}
|
|
return points;
|
|
}
|
|
|
|
private Set<ISinkPoint> calculateSinkPoints(final CallArgSinkSpec sinkSpec) {
|
|
final Set<ISinkPoint> points = HashSetFactory.make();
|
|
|
|
Collection<IMethod> methods = sinkSpec.getNamePattern()
|
|
.getPossibleTargets(cha);
|
|
if (null == methods) {
|
|
logger.warn("no methods found for sink spec {}", sinkSpec);
|
|
}
|
|
|
|
Set<CGNode> callees = HashSetFactory.make();
|
|
final Set<MethodReference> calleeRefs = HashSetFactory.make();
|
|
for (IMethod method : methods) {
|
|
callees.addAll(cg.getNodes(method.getReference()));
|
|
calleeRefs.add(method.getReference());
|
|
}
|
|
logger.debug("callee nodes {}", callees);
|
|
logger.debug("callee refs {}", calleeRefs);
|
|
|
|
// for each possible callee
|
|
for (CGNode callee : callees) {
|
|
Iterator<CGNode> callers = cg.getPredNodes(callee);
|
|
// for each possible caller of that callee
|
|
while (callers.hasNext()) {
|
|
final CGNode caller = callers.next();
|
|
// look for invoke instructions
|
|
caller.getIR().visitAllInstructions(new Visitor() {
|
|
@Override
|
|
public void visitInvoke(SSAInvokeInstruction invokeInst) {
|
|
// if the invoke instruction targets a possible callee
|
|
if (calleeRefs.contains(invokeInst.getDeclaredTarget())) {
|
|
// look up the instruction's block in context
|
|
// (surely there's a more straightforward way to do
|
|
// this!)
|
|
final SSAInstruction[] insts = graph.getICFG()
|
|
.getCFG(caller).getInstructions();
|
|
int invokeIndex = -1;
|
|
for (int i = 0; i < insts.length; i++) {
|
|
if (insts[i] instanceof SSAInvokeInstruction) {
|
|
SSAInvokeInstruction invokeInst2 = (SSAInvokeInstruction) insts[i];
|
|
if (invokeInst.getDeclaredTarget().equals(invokeInst2.getDeclaredTarget())) {
|
|
invokeIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (invokeIndex == -1) {
|
|
logger.error("couldn't find invoke instruction in caller node");
|
|
}
|
|
final IExplodedBasicBlock block = graph.getICFG()
|
|
.getCFG(caller)
|
|
.getBlockForInstruction(invokeIndex);
|
|
BasicBlockInContext<IExplodedBasicBlock> callBlock = new BasicBlockInContext<IExplodedBasicBlock>(
|
|
caller, block);
|
|
|
|
for (int argNum : sinkSpec.getArgNums()) {
|
|
// and add a sink point for each arg num
|
|
final int ssaVal = invokeInst.getUse(argNum);
|
|
final ParameterFlow<IExplodedBasicBlock> sinkFlow = new ParameterFlow<IExplodedBasicBlock>(
|
|
callBlock, argNum, false);
|
|
final LocalSinkPoint sinkPoint = new LocalSinkPoint(
|
|
callBlock, ssaVal, sinkFlow);
|
|
points.add(sinkPoint);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
return points;
|
|
}
|
|
|
|
private Set<ISinkPoint> calculateSinkPoints(EntryRetSinkSpec sinkSpec) {
|
|
Set<ISinkPoint> points = HashSetFactory.make();
|
|
|
|
Collection<IMethod> methods = sinkSpec.getNamePattern()
|
|
.getPossibleTargets(cha);
|
|
if (null == methods) {
|
|
logger.warn("no methods found for sink spec {}", sinkSpec);
|
|
}
|
|
|
|
// for all possible returning methods
|
|
for (IMethod method : methods) {
|
|
// for all possible CGNodes of that method
|
|
for (CGNode node : cg.getNodes(method.getReference())) {
|
|
// get the unique (null) exit block
|
|
BasicBlockInContext<IExplodedBasicBlock> nullExitBlock = graph
|
|
.getICFG().getExit(node);
|
|
// and for each predecessor to the exit block
|
|
Iterator<BasicBlockInContext<IExplodedBasicBlock>> exitBlocks = graph
|
|
.getPredNodes(nullExitBlock);
|
|
while (exitBlocks.hasNext()) {
|
|
// if that predecessor is a return instruction
|
|
BasicBlockInContext<IExplodedBasicBlock> exitBlock = exitBlocks
|
|
.next();
|
|
final SSAInstruction inst = exitBlock.getDelegate()
|
|
.getInstruction();
|
|
if (inst instanceof SSAReturnInstruction) {
|
|
// add a sink point for the instruction
|
|
SSAReturnInstruction returnInst = (SSAReturnInstruction) inst;
|
|
if (!returnInst.returnsVoid()) {
|
|
final int ssaVal = returnInst.getResult();
|
|
final ReturnFlow<IExplodedBasicBlock> sinkFlow = new ReturnFlow<IExplodedBasicBlock>(
|
|
exitBlock, false);
|
|
final LocalSinkPoint sinkPoint = new LocalSinkPoint(
|
|
exitBlock, ssaVal, sinkFlow);
|
|
points.add(sinkPoint);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return points;
|
|
}
|
|
|
|
private Set<ISinkPoint> calculateSinkPoints(StaticFieldSinkSpec sinkSpec) {
|
|
Set<ISinkPoint> points = HashSetFactory.make();
|
|
|
|
ICFGSupergraph graph = (ICFGSupergraph) ctx.graph;
|
|
for (CGNode node : ctx.cg.getNodes(sinkSpec.getMethod().getReference())) {
|
|
points.add(new StaticFieldSinkPoint(sinkSpec, graph.getICFG()
|
|
.getExit(node)));
|
|
}
|
|
|
|
return points;
|
|
}
|
|
|
|
}
|