org.scandroid.flow.functions.TaintTransferFunctions Maven / Gradle / Ivy
Show all versions of com.ibm.wala.scandroid Show documentation
/*
* 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,
*
* Galois, Inc. (Aaron Tomb , Rogan Creswick , Adam
* Foltzer ) Steve Suh
*
* 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.functions;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.ibm.wala.classLoader.IField;
import com.ibm.wala.dataflow.IFDS.IFlowFunction;
import com.ibm.wala.dataflow.IFDS.IFlowFunctionMap;
import com.ibm.wala.dataflow.IFDS.IReversibleFlowFunction;
import com.ibm.wala.dataflow.IFDS.IUnaryFlowFunction;
import com.ibm.wala.dataflow.IFDS.IdentityFlowFunction;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.callgraph.propagation.ConcreteTypeKey;
import com.ibm.wala.ipa.callgraph.propagation.InstanceKey;
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.ssa.ISSABasicBlock;
import com.ibm.wala.ssa.SSAArrayLoadInstruction;
import com.ibm.wala.ssa.SSAArrayReferenceInstruction;
import com.ibm.wala.ssa.SSAArrayStoreInstruction;
import com.ibm.wala.ssa.SSAFieldAccessInstruction;
import com.ibm.wala.ssa.SSAGetInstruction;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSAInvokeInstruction;
import com.ibm.wala.ssa.SSAPutInstruction;
import com.ibm.wala.ssa.SSAReturnInstruction;
import com.ibm.wala.types.FieldReference;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.collections.HashSetFactory;
import com.ibm.wala.util.collections.Pair;
import com.ibm.wala.util.intset.IntSet;
import com.ibm.wala.util.intset.MutableSparseIntSet;
import com.ibm.wala.util.intset.OrdinalSet;
import com.ibm.wala.util.intset.SparseIntSet;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import org.scandroid.domain.CodeElement;
import org.scandroid.domain.DomainElement;
import org.scandroid.domain.FieldElement;
import org.scandroid.domain.IFDSTaintDomain;
import org.scandroid.domain.InstanceKeyElement;
import org.scandroid.domain.LocalElement;
import org.scandroid.domain.ReturnElement;
import org.scandroid.domain.StaticFieldElement;
import org.scandroid.flow.types.StaticFieldFlow;
public class TaintTransferFunctions
implements IFlowFunctionMap> {
// Java, you need type aliases.
private static class BlockPair
extends Pair, BasicBlockInContext> {
private static final long serialVersionUID = 6838579950051954781L;
protected BlockPair(BasicBlockInContext fst, BasicBlockInContext snd) {
super(fst, snd);
}
}
private final IFDSTaintDomain domain;
private final PointerAnalysis pa;
private final boolean taintStaticFields;
private final IUnaryFlowFunction globalId;
private final IUnaryFlowFunction callToReturn;
private final LoadingCache, IUnaryFlowFunction> callFlowFunctions;
private final LoadingCache, IUnaryFlowFunction> normalFlowFunctions;
public static final IntSet EMPTY_SET = new SparseIntSet();
public static final IntSet ZERO_SET = SparseIntSet.singleton(0);
private static final IReversibleFlowFunction IDENTITY_FN = new IdentityFlowFunction();
public TaintTransferFunctions(IFDSTaintDomain domain, PointerAnalysis pa) {
this(domain, pa, false);
}
public TaintTransferFunctions(
IFDSTaintDomain domain, PointerAnalysis pa, boolean taintStaticFields) {
this.domain = domain;
this.pa = pa;
this.globalId = new GlobalIdentityFunction<>(domain);
this.callToReturn = new CallToReturnFunction<>(domain);
this.callFlowFunctions =
CacheBuilder.newBuilder()
.maximumSize(10000)
.expireAfterWrite(10, TimeUnit.MINUTES)
.build(
new CacheLoader<>() {
@Override
public IUnaryFlowFunction load(BlockPair key) {
return makeCallFlowFunction(key.fst);
}
});
this.normalFlowFunctions =
CacheBuilder.newBuilder()
.maximumSize(10000)
.expireAfterWrite(10, TimeUnit.MINUTES)
.build(
new CacheLoader<>() {
@Override
public IUnaryFlowFunction load(BlockPair key) {
return makeNormalFlowFunction(key.snd);
}
});
this.taintStaticFields = taintStaticFields;
}
@Override
public IUnaryFlowFunction getCallFlowFunction(
BasicBlockInContext src, BasicBlockInContext dest, BasicBlockInContext ret) {
try {
return callFlowFunctions.get(new BlockPair<>(src, dest));
} catch (ExecutionException e) {
throw new RuntimeException(e);
}
}
private IUnaryFlowFunction makeCallFlowFunction(BasicBlockInContext src) {
SSAInstruction srcInst = src.getLastInstruction();
if (null == srcInst) {
return IDENTITY_FN;
}
if (srcInst instanceof SSAInvokeInstruction) {
// build list of actual parameter code elements, and return a
// function
final int numParams = ((SSAInvokeInstruction) srcInst).getNumberOfPositionalParameters();
List actualParams = new ArrayList<>(numParams);
for (int i = 0; i < numParams; i++) {
actualParams.add(i, new LocalElement(srcInst.getUse(i)));
}
// return new TracingFlowFunction(domain, union(new
// GlobalIdentityFunction(domain),
// new CallFlowFunction(domain, actualParams)));
return union(globalId, new CallFlowFunction<>(domain, actualParams));
} else {
throw new RuntimeException("src block not an invoke instruction");
}
}
@Override
public IUnaryFlowFunction getCallNoneToReturnFlowFunction(
BasicBlockInContext src, BasicBlockInContext dest) {
// return callNoneToReturn;
/*
* TODO: is this right?
*
* The original callNoneToReturn impl just adds taints to absolutely
* everything in the domain. This seems like the wrong approach, but
* it's unclear what would be correct...
*
* Switching this to the identity for now improves performance
* drastically.
*/
return IDENTITY_FN;
}
@Override
public IUnaryFlowFunction getCallToReturnFlowFunction(
BasicBlockInContext src, BasicBlockInContext dest) {
// return new TracingFlowFunction(domain, new
// CallToReturnFunction(domain));
return callToReturn;
}
@Override
public IUnaryFlowFunction getNormalFlowFunction(
BasicBlockInContext src, BasicBlockInContext dest) {
try {
return normalFlowFunctions.get(new BlockPair<>(src, dest));
} catch (ExecutionException e) {
throw new RuntimeException(e);
}
}
private IUnaryFlowFunction makeNormalFlowFunction(BasicBlockInContext dest) {
List pairs = new ArrayList<>();
// we first try to process the destination instruction
SSAInstruction inst = dest.getLastInstruction();
CGNode node = dest.getNode();
if (null == inst) {
return IDENTITY_FN;
}
Iterable inCodeElts = getInCodeElts(node, inst);
Iterable outCodeElts = getOutCodeElts(node, inst);
if (!inCodeElts.iterator().hasNext()) {}
if (!outCodeElts.iterator().hasNext()) {}
// for now, take the Cartesian product of the inputs and outputs:
// TODO specialize this on a per-instruction basis to improve precision.
for (CodeElement use : inCodeElts) {
for (CodeElement def : outCodeElts) {
pairs.add(new UseDefPair(use, def));
}
}
// globals may be redefined here, so we can't union with the globals ID
// flow function, as we often do elsewhere.
final PairBasedFlowFunction flowFunction = new PairBasedFlowFunction<>(domain, pairs);
// special case for static field gets so we can introduce new taints for
// them
if (taintStaticFields
&& inst instanceof SSAGetInstruction
&& ((SSAGetInstruction) inst).isStatic()) {
return makeStaticFieldTaints(dest, inst, flowFunction);
}
return flowFunction;
}
public IUnaryFlowFunction makeStaticFieldTaints(
BasicBlockInContext dest,
SSAInstruction inst,
final PairBasedFlowFunction flowFunction) {
final Set elts = HashSetFactory.make();
for (CodeElement ce : getStaticFieldAccessCodeElts((SSAGetInstruction) inst)) {
StaticFieldElement sfe = (StaticFieldElement) ce;
IField field = pa.getClassHierarchy().resolveField(sfe.getRef());
if (field.isFinal()) {
continue;
}
final StaticFieldFlow taintSource = new StaticFieldFlow<>(dest, field, true);
elts.add(new DomainElement(ce, taintSource));
}
IUnaryFlowFunction newTaints = new ConstantFlowFunction<>(domain, elts);
return compose(flowFunction, newTaints);
}
/*
* The usual arguments:
*
* call: the invoke instruction that took us into this method
*
* src: a block that's the postdominator of this method, usually with no
* instructions
*
* dest: whatever instruction followed the invoke instruction in call
*
* What we want to accomplish:
*
* 1. Map taints from the value being returned to a LocalElement in the
* caller's context
*
* 2. Pass through any global information that the callee may have changed
*
* 3. Process ins/outs of dest block as well (it will never be the dest of a
* NormalFlowFunction)
*
* @see
* com.ibm.wala.dataflow.IFDS.IFlowFunctionMap#getReturnFlowFunction(java
* .lang.Object, java.lang.Object, java.lang.Object)
*/
@Override
public IFlowFunction getReturnFlowFunction(
BasicBlockInContext call, BasicBlockInContext src, BasicBlockInContext dest) {
final SSAInstruction inst = call.getLastInstruction();
if (null == inst || !(inst instanceof SSAInvokeInstruction)) {
// if we don't have an invoke, just punt and hope the necessary
// information is already in global elements
return globalId;
}
// we always need to process the destination instruction
final IUnaryFlowFunction flowFromDest = getNormalFlowFunction(null, dest);
final SSAInvokeInstruction invoke = (SSAInvokeInstruction) inst;
if (invoke.getNumberOfReturnValues() == 0) {
// no return values, just propagate global information
// return new TracingFlowFunction(domain, compose (flowFromDest,
// new GlobalIdentityFunction(domain)));
return compose(flowFromDest, globalId);
}
// we have a return value, so we need to map any return elements onto
// the local element corresponding to the invoke's def
final IUnaryFlowFunction flowToDest =
union(globalId, new ReturnFlowFunction<>(domain, invoke.getDef()));
// return new TracingFlowFunction(domain, compose(flowFromDest,
// flowToDest));
return compose(flowFromDest, flowToDest);
}
private Iterable getOutCodeElts(CGNode node, SSAInstruction inst) {
int defNo = inst.getNumberOfDefs();
Set elts = HashSetFactory.make();
if (inst instanceof SSAReturnInstruction) {
// only one possible element for returns
elts.add(new ReturnElement());
return elts;
}
if (inst instanceof SSAPutInstruction) {
final Set fieldAccessCodeElts =
getFieldAccessCodeElts(node, (SSAPutInstruction) inst);
elts.addAll(fieldAccessCodeElts);
}
if (inst instanceof SSAArrayStoreInstruction) {
elts.addAll(getArrayRefCodeElts(node, (SSAArrayStoreInstruction) inst));
}
for (int i = 0; i < defNo; i++) {
int valNo = inst.getDef(i);
elts.addAll(CodeElement.valueElements(valNo));
}
return elts;
}
private Iterable getInCodeElts(CGNode node, SSAInstruction inst) {
int useNo = inst.getNumberOfUses();
Set elts = HashSetFactory.make();
if (inst instanceof SSAGetInstruction) {
elts.addAll(getFieldAccessCodeElts(node, (SSAGetInstruction) inst));
}
if (inst instanceof SSAArrayLoadInstruction) {
elts.addAll(getArrayRefCodeElts(node, (SSAArrayLoadInstruction) inst));
}
for (int i = 0; i < useNo; i++) {
int valNo = inst.getUse(i);
// Constants have valuenumber 0, which is otherwise, illegal.
// these need to be skipped:
if (0 == valNo) {
continue;
}
try {
elts.addAll(CodeElement.valueElements(valNo));
} catch (IllegalArgumentException e) {
throw e;
}
}
return elts;
}
// private Iterable getOutCodeElts(final CGNode node, final
// SSAInstruction inst) {
// return new Iterable() {
// @Override
// public Iterator iterator() {
// return new DefEltIterator(node, inst);
// }
// };
// }
//
// private Iterable getInCodeElts(final CGNode node, final
// SSAInstruction inst) {
// return new Iterable() {
// @Override
// public Iterator iterator() {
// return new UseEltIterator(node, inst);
// }
// };
// }
private Set getFieldAccessCodeElts(CGNode node, SSAFieldAccessInstruction inst) {
if (inst.isStatic()) {
return getStaticFieldAccessCodeElts(inst);
}
Set elts = HashSetFactory.make();
final FieldReference fieldRef = inst.getDeclaredField();
final IField field = node.getClassHierarchy().resolveField(fieldRef);
PointerKey pk = pa.getHeapModel().getPointerKeyForLocal(node, inst.getRef());
final OrdinalSet pointsToSet = pa.getPointsToSet(pk);
if (pointsToSet.isEmpty()) {
InstanceKey ik = new ConcreteTypeKey(field.getDeclaringClass());
elts.add(new FieldElement(ik, fieldRef));
elts.add(new InstanceKeyElement(ik));
} else {
for (InstanceKey ik : pointsToSet) {
elts.add(new FieldElement(ik, fieldRef));
elts.add(new InstanceKeyElement(ik));
}
}
return elts;
}
private static Set getStaticFieldAccessCodeElts(SSAFieldAccessInstruction inst) {
Set elts = HashSetFactory.make();
final FieldReference fieldRef = inst.getDeclaredField();
elts.add(new StaticFieldElement(fieldRef));
// TODO: what about tainting the declaring class?
return elts;
}
private Set getArrayRefCodeElts(CGNode node, SSAArrayReferenceInstruction inst) {
Set elts = HashSetFactory.make();
final PointerKey pk = pa.getHeapModel().getPointerKeyForLocal(node, inst.getArrayRef());
final OrdinalSet pointsToSet = pa.getPointsToSet(pk);
if (pointsToSet.isEmpty()) {
TypeReference arrayType = TypeReference.findOrCreateArrayOf(inst.getElementType());
InstanceKey ik = new ConcreteTypeKey(pa.getClassHierarchy().lookupClass(arrayType));
elts.add(new InstanceKeyElement(ik));
} else {
for (InstanceKey ik : pointsToSet) {
elts.add(new InstanceKeyElement(ik));
}
}
return elts;
}
private static IUnaryFlowFunction union(final IUnaryFlowFunction g, final IUnaryFlowFunction h) {
return d1 -> g.getTargets(d1).union(h.getTargets(d1));
}
/**
* Flow function composition
*
* @return { (x, z) | (x, y) \in g, (y, z) \in f }
*/
private static IUnaryFlowFunction compose(
final IUnaryFlowFunction f, final IUnaryFlowFunction g) {
return d1 -> {
final MutableSparseIntSet set = MutableSparseIntSet.makeEmpty();
g.getTargets(d1).foreach(x -> set.addAll(f.getTargets(x)));
return set;
};
}
/*
* private class UseEltIterator implements Iterator { private
* int idx = 0; private Iterator subIt; private final CGNode
* node; private final SSAInstruction inst; private final int count;
*
* public UseEltIterator(CGNode node, SSAInstruction inst) { this.node =
* node; this.inst = inst; count = inst.getNumberOfUses();
* updateIterator(node, inst); }
*
* private void updateIterator(final CGNode node, final SSAInstruction inst)
* { int valNo = inst.getUse(idx); idx++; Set elements =
* CodeElement.valueElements(pa, node, valNo); subIt = elements.iterator();
* }
*
* @Override public boolean hasNext() { if (subIt.hasNext()) { return true;
* } else if (idx < count) { updateIterator(node, inst); return hasNext(); }
* else { return false; } }
*
* @Override public CodeElement next() { return subIt.next(); }
*
* @Override public void remove() {} }
*
* private class DefEltIterator implements Iterator { private
* int idx = 0; private Iterator subIt; private final CGNode
* node; private final SSAInstruction inst; private final int count;
*
* public DefEltIterator(CGNode node, SSAInstruction inst) { this.node =
* node; this.inst = inst; count = inst.getNumberOfDefs();
* updateIterator(node, inst); }
*
* private void updateIterator(final CGNode node, final SSAInstruction inst)
* { int valNo = inst.getDef(idx); idx++; Set elements =
* CodeElement.valueElements(pa, node, valNo); subIt = elements.iterator();
* }
*
* @Override public boolean hasNext() { if (subIt.hasNext()) { return true;
* } else if (idx < count) { updateIterator(node, inst); return hasNext(); }
* else { return false; } }
*
* @Override public CodeElement next() { return subIt.next(); }
*
* @Override public void remove() {} }
*/
}