WALA/com.ibm.wala.scandroid/source/org/scandroid/flow/functions/IFDSTaintFlowFunctionProvid...

609 lines
21 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>
*
* 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 java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
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.flow.types.FlowType;
import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.classLoader.IField;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.dataflow.IFDS.IFlowFunction;
import com.ibm.wala.dataflow.IFDS.IFlowFunctionMap;
import com.ibm.wala.dataflow.IFDS.ISupergraph;
import com.ibm.wala.dataflow.IFDS.IUnaryFlowFunction;
import com.ibm.wala.ipa.callgraph.CGNode;
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.callgraph.propagation.StaticFieldKey;
import com.ibm.wala.ipa.cfg.BasicBlockInContext;
import com.ibm.wala.ipa.cha.IClassHierarchy;
import com.ibm.wala.ssa.ISSABasicBlock;
import com.ibm.wala.ssa.SSAArrayLoadInstruction;
import com.ibm.wala.ssa.SSAArrayStoreInstruction;
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.MethodReference;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.collections.HashMapFactory;
import com.ibm.wala.util.collections.HashSetFactory;
import com.ibm.wala.util.intset.BitVectorIntSet;
import com.ibm.wala.util.intset.IntSet;
import com.ibm.wala.util.intset.MutableIntSet;
import com.ibm.wala.util.intset.MutableSparseIntSet;
import com.ibm.wala.util.intset.OrdinalSet;
/**
* @deprecated Replaced by TaintTransferFunctions.
*/
@Deprecated
public class IFDSTaintFlowFunctionProvider<E extends ISSABasicBlock>
implements IFlowFunctionMap<BasicBlockInContext<E>> {
private final IFDSTaintDomain<E> domain;
private final ISupergraph<BasicBlockInContext<E>,CGNode> graph;
private final PointerAnalysis<InstanceKey> pa;
public IFDSTaintFlowFunctionProvider(IFDSTaintDomain<E> domain,
ISupergraph<BasicBlockInContext<E>, CGNode> graph,
PointerAnalysis<InstanceKey> pa)
{
this.domain = domain;
this.graph = graph;
this.pa = pa;
}
// instruction has a valid def set
private static boolean inFlow(SSAInstruction instruction) {
return (instruction instanceof SSAArrayLoadInstruction) ||
(instruction instanceof SSAGetInstruction);
}
// instruction's def is getUse(0)
private static boolean outFlow(SSAInstruction instruction) {
return (instruction instanceof SSAArrayStoreInstruction) ||
(instruction instanceof SSAPutInstruction) ||
(instruction instanceof SSAInvokeInstruction);
}
// instruction is a return instruction
private static boolean returnFlow(SSAInstruction instruction) {
return (instruction instanceof SSAReturnInstruction);
}
private static class UseDefSetPair
{
public Set<CodeElement> uses = HashSetFactory.make();
public Set<CodeElement> defs = HashSetFactory.make();
}
private class DefUse implements IUnaryFlowFunction {
private final List<UseDefSetPair> useToDefList = new ArrayList<>();
private final BasicBlockInContext<E> bb;
public DefUse(final BasicBlockInContext<E> inBlock) {
this.bb = inBlock;
for (SSAInstruction instruction : bb) {
handleInstruction(instruction);
}
}
private void handleInstruction(SSAInstruction instruction) {
// System.out.println("handle instruction: "+instruction);
UseDefSetPair p = new UseDefSetPair();
boolean thisToResult = false;
if(instruction instanceof SSAInvokeInstruction)
{
thisToResult = handleInvokeInstruction(instruction, p);
}
if (thisToResult) {
useToDefList.add(p);
p = new UseDefSetPair();
}
IClassHierarchy ch = bb.getNode().getClassHierarchy();
if (inFlow(instruction)) {
handleInflowInstruction(instruction, p, ch);
}
else if (outFlow(instruction)) {
handleOutflowInstruction(instruction, p, ch);
}
else if(returnFlow(instruction))
{
handleReturnFlowInstruction(instruction, p);
}
else
{
for (int i = 0; i < instruction.getNumberOfUses(); i++) {
p.uses.addAll(CodeElement.valueElements(instruction.getUse(i)));
}
for (int j = 0; j < instruction.getNumberOfDefs(); j++) {
p.defs.addAll(CodeElement.valueElements(instruction.getDef(j)));
}
}
useToDefList.add(p);
}
private void handleReturnFlowInstruction(SSAInstruction instruction,
UseDefSetPair p) {
SSAReturnInstruction retInst = (SSAReturnInstruction)instruction;
if(retInst.getNumberOfUses() > 0)
{
/* TODO: why not add instance keys, too? */
for(int i = 0; i < instruction.getNumberOfUses(); i++)
{
//p.uses.add(new LocalElement(instruction.getUse(i)));
p.uses.addAll(
CodeElement.valueElements(instruction.getUse(i)));
}
p.defs.add(new ReturnElement());
}
}
private boolean handleInvokeInstruction(SSAInstruction instruction,
UseDefSetPair p) {
boolean thisToResult;
SSAInvokeInstruction invInst = (SSAInvokeInstruction)instruction;
if(!invInst.isSpecial() && !invInst.isStatic() && instruction.getNumberOfDefs() > 0)
{
//System.out.println("adding receiver flow in "+this+" for "+invInst);
//System.out.println("\tadding local element "+invInst.getReceiver());
//getReceiver() == getUse(0) == param[0] == this
p.uses.addAll(CodeElement.valueElements(invInst.getReceiver()));
for(int i = 0; i < invInst.getNumberOfDefs(); i++)
{
//System.out.println("\tadding def local element "+invInst.getDef(i));
//return valuenumber of invoke instruction
p.defs.addAll(CodeElement.valueElements(invInst.getDef(i)));
}
}
thisToResult = true;
return thisToResult;
}
private void handleInflowInstruction(SSAInstruction instruction,
UseDefSetPair p, IClassHierarchy ch) {
if (instruction instanceof SSAGetInstruction) {
handleInflowGetInstruction(instruction, p, ch);
}
else if (instruction instanceof SSAArrayLoadInstruction){
handleInflowArrayLoadInstruction(instruction, p);
}
}
private void handleOutflowInstruction(SSAInstruction instruction,
UseDefSetPair p, IClassHierarchy ch) {
if (instruction instanceof SSAPutInstruction) {
handleOutflowPutInstruction(instruction, p, ch);
}
else if (instruction instanceof SSAArrayStoreInstruction){
handleOutflowArrayStoreInstruction(instruction, p);
}
else if (instruction instanceof SSAInvokeInstruction){
handleOutflowInvokeInstruction(instruction, p);
}
}
private void handleOutflowInvokeInstruction(SSAInstruction instruction,
UseDefSetPair p) {
MethodReference targetMethod = ((SSAInvokeInstruction) instruction).getCallSite().getDeclaredTarget();
if (methodExcluded(targetMethod)) {
// TODO make all parameters flow into all other
// parameters, which could happen in the static case as well.
if (!((SSAInvokeInstruction) instruction).isStatic()) {
// These loops cause all parameters flow into the
// 'this' param (due to instruction.getUse(0))
for (int i = 1; i < instruction.getNumberOfUses(); i++) {
p.uses.addAll(CodeElement.valueElements(instruction.getUse(i)));
}
if (instruction.getNumberOfUses() > 0) {
p.defs.addAll(CodeElement.valueElements(instruction.getUse(0)));
}
}
}
}
private void handleOutflowArrayStoreInstruction(
SSAInstruction instruction, UseDefSetPair p) {
p.uses.addAll(CodeElement.valueElements(instruction.getUse(2)));
p.defs.addAll(CodeElement.valueElements(instruction.getUse(0)));
}
private void handleOutflowPutInstruction(SSAInstruction instruction,
UseDefSetPair p, IClassHierarchy ch) {
SSAPutInstruction pi = (SSAPutInstruction)instruction;
PointerKey pk;
Set<CodeElement> elements = HashSetFactory.make();
if (pi.isStatic()) {
p.uses.addAll(CodeElement.valueElements(instruction.getUse(0)));
FieldReference declaredField = pi.getDeclaredField();
IField staticField = getStaticIField(ch, declaredField);
if (staticField == null) {
pk = null;
} else {
pk = new StaticFieldKey(staticField);
}
} else {
p.uses.addAll(
CodeElement.valueElements(instruction.getUse(1)));
// this value number seems to be the object referenced in this instruction (?)
int valueNumber = instruction.getUse(0);
pk = new LocalPointerKey(bb.getNode(), valueNumber);
//MyLogger.log(LogLevel.DEBUG, " instruction: "+instruction);
// add the object that holds the field that was modified
// to the list of things tainted by this flow:
p.defs.addAll(CodeElement.valueElements(valueNumber));
}
// now add the field keys to the defs list so that they
// are also tainted:
if (pk!=null) {
OrdinalSet<InstanceKey> m = pa.getPointsToSet(pk);
if (m != null) {
for (InstanceKey instanceKey : m) {
elements.add(new FieldElement(instanceKey, pi.getDeclaredField()));
elements.add(new InstanceKeyElement(instanceKey));
}
}
p.defs.addAll(elements);
}
}
private void handleInflowArrayLoadInstruction(
SSAInstruction instruction, UseDefSetPair p) {
p.uses.addAll(CodeElement.valueElements(instruction.getUse(0)));
p.defs.addAll(CodeElement.valueElements(instruction.getDef()));
}
private void handleInflowGetInstruction(SSAInstruction instruction,
UseDefSetPair p, IClassHierarchy ch) {
SSAGetInstruction gi = (SSAGetInstruction)instruction;
PointerKey pk;
FieldReference declaredField = gi.getDeclaredField();
if ( gi.isStatic()) {
IField staticField =
getStaticIField(ch, declaredField);
if (staticField == null) {
pk = null;
} else {
pk = new StaticFieldKey(staticField);
}
} else {
int valueNumber = instruction.getUse(0);
pk = new LocalPointerKey(bb.getNode(), valueNumber);
}
if (pk!=null) {
Set<CodeElement> elements = HashSetFactory.make();
OrdinalSet<InstanceKey> m = pa.getPointsToSet(pk);
if(m != null) {
for (InstanceKey instanceKey : m) {
elements.add(new FieldElement(instanceKey, declaredField));
elements.add(new InstanceKeyElement(instanceKey));
}
}
p.uses.addAll(elements);
//getinstruction only has 1 def
p.defs.add(new LocalElement(instruction.getDef(0)));
}
}
/**
* Determines if the provide method is in the exclusions by checking the supergraph.
* @param method
* @return True if the method can not be found in the supergraph.
*/
private boolean methodExcluded(MethodReference method) {
Collection<IMethod> iMethods = pa.getClassHierarchy().getPossibleTargets(method);
return 0 == iMethods.size();
}
private IField getStaticIField(IClassHierarchy ch,
FieldReference declaredField) {
TypeReference staticTypeRef = declaredField.getDeclaringClass();
IClass staticClass = ch.lookupClass(staticTypeRef);
//referring to a static field which we don't have loaded in the class hierarchy
//possibly ignored in the exclusions file or just not included in the scope
if (staticClass == null)
return null;
IField staticField =
staticClass.getField(declaredField.getName());
return staticField;
}
private void addTargets(CodeElement d1, MutableIntSet set, FlowType<E> taintType)
{
//System.out.println(this.toString()+".addTargets("+d1+"...)");
for(UseDefSetPair p: useToDefList)
{
if(p.uses.contains(d1))
{
//System.out.println("\t\tfound pair that uses "+d1);
for(CodeElement i:p.defs)
{
//System.out.println("\t\tadding outflow "+i);
set.add(domain.getMappedIndex(new DomainElement(i,taintType)));
}
}
}
}
@Override
@SuppressWarnings("unchecked")
public IntSet getTargets(int d1) {
//System.out.println(this.toString()+".getTargets("+d1+") "+bb);
//BitVectorIntSet set = new BitVectorIntSet();
MutableSparseIntSet set = MutableSparseIntSet.makeEmpty();
set.add(d1);
DomainElement de = domain.getMappedObject(d1);
if (de != null) {
addTargets(de.codeElement, set, de.taintSource);
}
return set;
}
}
@Override
public IUnaryFlowFunction getCallFlowFunction(
BasicBlockInContext<E> src,
BasicBlockInContext<E> dest,
BasicBlockInContext<E> ret) {
assert graph.isCall(src);
final SSAInvokeInstruction instruction = (SSAInvokeInstruction) src.getLastInstruction();
// String signature = dest.getMethod().getSignature();
// if ( dest.getMethod().isWalaSynthetic() ) {
// System.out.println("Synthetic: "+signature);
// } else {
// System.err.println(signature);
// }
// if ( LoaderUtils.fromLoader(src.getNode(), ClassLoaderReference.Application)
// && LoaderUtils.fromLoader(dest.getNode(), ClassLoaderReference.Primordial)) {
// System.out.println("Call to system: "+signature);
// }
// if (! dest.getMethod().isWalaSynthetic()
// && LoaderUtils.fromLoader(dest.getNode(), ClassLoaderReference.Primordial)) {
//
// MyLogger.log(DEBUG,"Primordial and No Summary! (getCallFlowFunction) - " + dest.getMethod().getReference());
// }
final Map<CodeElement,CodeElement> parameterMap = HashMapFactory.make();
for (int i = 0; i < instruction.getNumberOfPositionalParameters(); i++) {
Set<CodeElement> elements = CodeElement.valueElements(instruction.getUse(i));
for(CodeElement e: elements) {
parameterMap.put(e, new LocalElement(i+1));
}
}
return d1 -> {
BitVectorIntSet set = new BitVectorIntSet();
if(d1 == 0 || !(domain.getMappedObject(d1).codeElement instanceof LocalElement)) {
set.add(d1);
}
DomainElement de = domain.getMappedObject(d1);
if(de!=null && parameterMap.containsKey(de.codeElement))
set.add(domain.getMappedIndex(new DomainElement(parameterMap.get(de.codeElement),de.taintSource)));
return set;
};
}
@Override
public IUnaryFlowFunction getCallNoneToReturnFlowFunction(
BasicBlockInContext<E> src,
BasicBlockInContext<E> dest) {
//I Believe this method is called only if there are no callees of src in the supergraph
//if supergraph included all primordials, this method can still be called if it calls a
//method that wasn't included in the scope
//Assertions.UNREACHABLE();
// TODO: Look up summary for this method, or warn if it doesn't exist.
assert (src.getNode().equals(dest.getNode()));
// final SSAInvokeInstruction instruction = (SSAInvokeInstruction) src.getLastInstruction();
// System.out.println("call to return(no callee) method inside call graph: " + src.getNode()+"--" + instruction.getDeclaredTarget());
// System.out.println("call to system: " + instruction.getDeclaredTarget());
return new DefUse(dest);
}
@Override
public IUnaryFlowFunction getCallToReturnFlowFunction(
BasicBlockInContext<E> src,
BasicBlockInContext<E> dest) {
assert (src.getNode().equals(dest.getNode()));
//final SSAInvokeInstruction instruction = (SSAInvokeInstruction) src.getLastInstruction();
//System.out.println("call to return method inside call graph: " + instruction.getDeclaredTarget());
return new DefUse(dest);
}
@Override
public IUnaryFlowFunction getNormalFlowFunction(
BasicBlockInContext<E> src,
BasicBlockInContext<E> dest) {
assert (src.getNode().equals(dest.getNode()));
//System.out.println("getNormalFlowFuntion");
//System.out.println("\tSrc " + src.getLastInstruction());
//System.out.println("\tDest " + dest.getLastInstruction());
return new DefUse(dest);
}
public class ReturnDefUse extends DefUse
{
CodeElement callSet;
Set<CodeElement> receivers = new HashSet<>();
public ReturnDefUse(BasicBlockInContext<E> dest,
BasicBlockInContext<E> call) {
super(dest);
// TODO: look into exception handling through getDef(1)
if(call.getLastInstruction() instanceof SSAInvokeInstruction) {
SSAInvokeInstruction invInst = (SSAInvokeInstruction) call.getLastInstruction();
if(!invInst.isSpecial()) {// && !invInst.isStatic()) {
// for (int i = 0; i < invInst.getNumberOfReturnValues(); i++) {
//
// }
if (invInst.hasDef()) {
callSet = new LocalElement(invInst.getReturnValue(0));
if ( !invInst.isStatic() ) {
//used to be invInst.getReceiver(), but I believe that was incorrect.
receivers.addAll(CodeElement.valueElements(invInst.getReceiver()));
//receivers.addAll(CodeElement.valueElements(pa, call.getNode(), invInst.getReturnValue(0)));
}
}
}
}
else {
callSet = null;
}
// // TODO: look into exception handling through getDef(1)
// if(call.getLastInstruction().getNumberOfDefs() == 1)
// {
// //System.out.println("\treturn defines something: "+call.getLastInstruction());
// callSet = new LocalElement(call.getLastInstruction().getDef(0));
// if(call.getLastInstruction() instanceof SSAInvokeInstruction)
// {
// SSAInvokeInstruction invInst = (SSAInvokeInstruction) call.getLastInstruction();
// if(!invInst.isSpecial() && !invInst.isStatic()) {
// receivers.addAll(CodeElement.valueElements(pa, call.getNode(), invInst.getReceiver()));
// }
// }
// }
// else
// callSet = null;
}
@Override
public IntSet getTargets(int d1)
{
if(d1 != 0 && domain.getMappedObject(d1).codeElement instanceof ReturnElement)
{
BitVectorIntSet set = new BitVectorIntSet();
if(callSet != null) {
// System.out.println("callset: " + callSet);
set.add(domain.getMappedIndex(new DomainElement(callSet,domain.getMappedObject(d1).taintSource)));
}
return set;
}
else if(d1 != 0 && domain.getMappedObject(d1).codeElement instanceof LocalElement)
{
return new BitVectorIntSet();
}
else if(d1 != 0 && receivers.contains(domain.getMappedObject(d1).codeElement))
{
BitVectorIntSet set = new BitVectorIntSet();
if(callSet != null)
set.add(domain.getMappedIndex(new DomainElement(callSet,domain.getMappedObject(d1).taintSource)));
set.addAll(super.getTargets(d1));
return set;
}
else
{
return super.getTargets(d1);
}
}
}
@Override
public IFlowFunction getReturnFlowFunction(BasicBlockInContext<E> call,
BasicBlockInContext<E> src,
BasicBlockInContext<E> dest) {
assert (graph.isCall(call) && graph.isReturn(dest) && call.getNode().equals(dest.getNode()));
//final SSAInvokeInstruction instruction = (SSAInvokeInstruction) call.getLastInstruction();
//System.out.println("Return from call to method inside call graph: " + instruction.getDeclaredTarget());
return new ReturnDefUse(dest,call);
}
}