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

org.scandroid.flow.functions.TaintTransferFunctions Maven / Gradle / Ivy

There is a newer version: 1.6.8
Show newest version
/*
 * 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, IUnaryFlowFunction>() { @Override public IUnaryFlowFunction load(BlockPair key) { return makeCallFlowFunction(key.fst); } }); this.normalFlowFunctions = CacheBuilder.newBuilder() .maximumSize(10000) .expireAfterWrite(10, TimeUnit.MINUTES) .build( new CacheLoader, IUnaryFlowFunction>() { @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() {} } */ }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy