632 lines
22 KiB
Java
632 lines
22 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,
|
||
|
*
|
||
|
* Galois, Inc. (Aaron Tomb <atomb@galois.com>,
|
||
|
* Rogan Creswick <creswick@galois.com>,
|
||
|
* Adam Foltzer <acfoltzer@galois.com>)
|
||
|
* 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.Arrays;
|
||
|
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;
|
||
|
import org.slf4j.Logger;
|
||
|
import org.slf4j.LoggerFactory;
|
||
|
|
||
|
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.ISupergraph;
|
||
|
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.IntSetAction;
|
||
|
import com.ibm.wala.util.intset.MutableSparseIntSet;
|
||
|
import com.ibm.wala.util.intset.OrdinalSet;
|
||
|
import com.ibm.wala.util.intset.SparseIntSet;
|
||
|
|
||
|
public class TaintTransferFunctions<E extends ISSABasicBlock> implements
|
||
|
IFlowFunctionMap<BasicBlockInContext<E>> {
|
||
|
private static final Logger logger = LoggerFactory
|
||
|
.getLogger(TaintTransferFunctions.class);
|
||
|
|
||
|
// Java, you need type aliases.
|
||
|
private static class BlockPair<E extends ISSABasicBlock> extends
|
||
|
Pair<BasicBlockInContext<E>, BasicBlockInContext<E>> {
|
||
|
protected BlockPair(BasicBlockInContext<E> fst,
|
||
|
BasicBlockInContext<E> snd) {
|
||
|
super(fst, snd);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private final IFDSTaintDomain<E> domain;
|
||
|
private final PointerAnalysis<InstanceKey> pa;
|
||
|
private final boolean taintStaticFields;
|
||
|
private final IUnaryFlowFunction globalId;
|
||
|
private final IUnaryFlowFunction callToReturn;
|
||
|
private final LoadingCache<BlockPair<E>, IUnaryFlowFunction> callFlowFunctions;
|
||
|
private final LoadingCache<BlockPair<E>, 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<E> domain,
|
||
|
ISupergraph<BasicBlockInContext<E>, CGNode> graph,
|
||
|
PointerAnalysis<InstanceKey> pa) {
|
||
|
this(domain, graph, pa, false);
|
||
|
}
|
||
|
|
||
|
public TaintTransferFunctions(IFDSTaintDomain<E> domain,
|
||
|
ISupergraph<BasicBlockInContext<E>, CGNode> graph,
|
||
|
PointerAnalysis<InstanceKey> pa, boolean taintStaticFields) {
|
||
|
this.domain = domain;
|
||
|
this.pa = pa;
|
||
|
this.globalId = new GlobalIdentityFunction<E>(domain);
|
||
|
this.callToReturn = new CallToReturnFunction<E>(domain);
|
||
|
this.callFlowFunctions = CacheBuilder.newBuilder().maximumSize(10000)
|
||
|
.expireAfterWrite(10, TimeUnit.MINUTES)
|
||
|
.build(new CacheLoader<BlockPair<E>, IUnaryFlowFunction>() {
|
||
|
@Override
|
||
|
public IUnaryFlowFunction load(BlockPair<E> key)
|
||
|
throws Exception {
|
||
|
return makeCallFlowFunction(key.fst, key.snd, null);
|
||
|
}
|
||
|
});
|
||
|
this.normalFlowFunctions = CacheBuilder.newBuilder().maximumSize(10000)
|
||
|
.expireAfterWrite(10, TimeUnit.MINUTES)
|
||
|
.build(new CacheLoader<BlockPair<E>, IUnaryFlowFunction>() {
|
||
|
@Override
|
||
|
public IUnaryFlowFunction load(BlockPair<E> key)
|
||
|
throws Exception {
|
||
|
return makeNormalFlowFunction(key.fst, key.snd);
|
||
|
}
|
||
|
});
|
||
|
this.taintStaticFields = taintStaticFields;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public IUnaryFlowFunction getCallFlowFunction(BasicBlockInContext<E> src,
|
||
|
BasicBlockInContext<E> dest, BasicBlockInContext<E> ret) {
|
||
|
try {
|
||
|
return callFlowFunctions.get(new BlockPair<E>(src, dest));
|
||
|
} catch (ExecutionException e) {
|
||
|
logger.error("Exception accessing callFlowFunctions {}", e);
|
||
|
throw new RuntimeException(e);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private IUnaryFlowFunction makeCallFlowFunction(BasicBlockInContext<E> src,
|
||
|
BasicBlockInContext<E> dest, BasicBlockInContext<E> ret) {
|
||
|
logger.trace("getCallFlowFunction");
|
||
|
SSAInstruction srcInst = src.getLastInstruction();
|
||
|
if (null == srcInst) {
|
||
|
logger.warn("null source for a call");
|
||
|
return IDENTITY_FN;
|
||
|
}
|
||
|
|
||
|
if (srcInst instanceof SSAInvokeInstruction) {
|
||
|
// build list of actual parameter code elements, and return a
|
||
|
// function
|
||
|
final int numParams = ((SSAInvokeInstruction) srcInst)
|
||
|
.getNumberOfParameters();
|
||
|
List<CodeElement> actualParams = new ArrayList<CodeElement>(numParams);
|
||
|
for (int i = 0; i < numParams; i++) {
|
||
|
actualParams.add(i, new LocalElement(srcInst.getUse(i)));
|
||
|
}
|
||
|
logger.trace("actual param list length: {}", actualParams);
|
||
|
// return new TracingFlowFunction<E>(domain, union(new
|
||
|
// GlobalIdentityFunction<E>(domain),
|
||
|
// new CallFlowFunction<E>(domain, actualParams)));
|
||
|
return union(globalId,
|
||
|
new CallFlowFunction<E>(domain, actualParams));
|
||
|
} else {
|
||
|
throw new RuntimeException("src block not an invoke instruction");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public IUnaryFlowFunction getCallNoneToReturnFlowFunction(
|
||
|
BasicBlockInContext<E> src, BasicBlockInContext<E> dest) {
|
||
|
if (logger.isTraceEnabled()) {
|
||
|
logger.trace("getNoneToReturnFunction");
|
||
|
logger.trace("callee signature: {}", ((SSAInvokeInstruction) src
|
||
|
.getLastInstruction()).getDeclaredTarget().getSignature());
|
||
|
}
|
||
|
// 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<E> src, BasicBlockInContext<E> dest) {
|
||
|
if (logger.isTraceEnabled()) {
|
||
|
logger.trace("getCallToReturnFunction\n\t{}\n\t-> {}", src
|
||
|
.getMethod().getSignature(), dest.getMethod()
|
||
|
.getSignature());
|
||
|
}
|
||
|
// return new TracingFlowFunction<E>(domain, new
|
||
|
// CallToReturnFunction<E>(domain));
|
||
|
return callToReturn;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public IUnaryFlowFunction getNormalFlowFunction(BasicBlockInContext<E> src,
|
||
|
BasicBlockInContext<E> dest) {
|
||
|
try {
|
||
|
return normalFlowFunctions.get(new BlockPair<E>(src, dest));
|
||
|
} catch (ExecutionException e) {
|
||
|
logger.error("Exception accessing normalFlowFunctions {}", e);
|
||
|
throw new RuntimeException(e);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private IUnaryFlowFunction makeNormalFlowFunction(
|
||
|
BasicBlockInContext<E> src, BasicBlockInContext<E> dest) {
|
||
|
List<UseDefPair> pairs = new ArrayList<UseDefPair>();
|
||
|
|
||
|
if (logger.isTraceEnabled()) {
|
||
|
logger.trace("getNormalFlowFunction {}", dest.getMethod()
|
||
|
.getSignature());
|
||
|
}
|
||
|
|
||
|
// we first try to process the destination instruction
|
||
|
SSAInstruction inst = dest.getLastInstruction();
|
||
|
CGNode node = dest.getNode();
|
||
|
|
||
|
if (null == inst) {
|
||
|
logger.trace("Using identity fn. for normal flow (dest instruction null)");
|
||
|
return IDENTITY_FN;
|
||
|
}
|
||
|
|
||
|
logger.trace("\tinstruction: {}", inst);
|
||
|
|
||
|
Iterable<CodeElement> inCodeElts = getInCodeElts(node, inst);
|
||
|
Iterable<CodeElement> outCodeElts = getOutCodeElts(node, inst);
|
||
|
if (!inCodeElts.iterator().hasNext()) {
|
||
|
logger.trace("no input elements for {}", inst);
|
||
|
}
|
||
|
if (!outCodeElts.iterator().hasNext()) {
|
||
|
logger.trace("no output elements for {}", inst);
|
||
|
}
|
||
|
|
||
|
// 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<E> flowFunction = new PairBasedFlowFunction<E>(
|
||
|
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, node, flowFunction);
|
||
|
}
|
||
|
|
||
|
return flowFunction;
|
||
|
}
|
||
|
|
||
|
public IUnaryFlowFunction makeStaticFieldTaints(
|
||
|
BasicBlockInContext<E> dest, SSAInstruction inst, CGNode node,
|
||
|
final PairBasedFlowFunction<E> flowFunction) {
|
||
|
final Set<DomainElement> elts = HashSetFactory.make();
|
||
|
for (CodeElement ce : getStaticFieldAccessCodeElts(node,
|
||
|
(SSAGetInstruction) inst)) {
|
||
|
StaticFieldElement sfe = (StaticFieldElement) ce;
|
||
|
IField field = pa.getClassHierarchy().resolveField(sfe.getRef());
|
||
|
if (field.isFinal()) {
|
||
|
continue;
|
||
|
}
|
||
|
final StaticFieldFlow<E> taintSource = new StaticFieldFlow<E>(dest,
|
||
|
field, true);
|
||
|
elts.add(new DomainElement(ce, taintSource));
|
||
|
}
|
||
|
IUnaryFlowFunction newTaints = new ConstantFlowFunction<E>(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<E> call,
|
||
|
BasicBlockInContext<E> src, BasicBlockInContext<E> dest) {
|
||
|
if (logger.isTraceEnabled()) {
|
||
|
logger.trace("getReturnFlowFunction\n\t{}\n\t-> {}\n\t-> {}", call
|
||
|
.getNode().getMethod().getSignature(), src.getNode()
|
||
|
.getMethod().getSignature(), dest.getNode().getMethod()
|
||
|
.getSignature());
|
||
|
logger.trace("\t{} -> {} -> {}", call.getLastInstruction(),
|
||
|
src.getLastInstruction(), dest.getLastInstruction());
|
||
|
}
|
||
|
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
|
||
|
logger.warn("call block null or not an invoke instruction");
|
||
|
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<E>(domain, compose (flowFromDest,
|
||
|
// new GlobalIdentityFunction<E>(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<E>(domain, invoke.getDef()));
|
||
|
|
||
|
// return new TracingFlowFunction<E>(domain, compose(flowFromDest,
|
||
|
// flowToDest));
|
||
|
return compose(flowFromDest, flowToDest);
|
||
|
}
|
||
|
|
||
|
private Iterable<CodeElement> getOutCodeElts(CGNode node,
|
||
|
SSAInstruction inst) {
|
||
|
int defNo = inst.getNumberOfDefs();
|
||
|
Set<CodeElement> elts = HashSetFactory.make();
|
||
|
|
||
|
if (inst instanceof SSAReturnInstruction) {
|
||
|
// only one possible element for returns
|
||
|
if (logger.isTraceEnabled()) {
|
||
|
logger.trace("making a return element for {}", node.getMethod()
|
||
|
.getSignature());
|
||
|
}
|
||
|
elts.add(new ReturnElement());
|
||
|
return elts;
|
||
|
}
|
||
|
|
||
|
if (inst instanceof SSAPutInstruction) {
|
||
|
final Set<CodeElement> fieldAccessCodeElts = getFieldAccessCodeElts(
|
||
|
node, (SSAPutInstruction) inst);
|
||
|
if (logger.isTraceEnabled()) {
|
||
|
logger.trace("put outelts: {}",
|
||
|
Arrays.toString(fieldAccessCodeElts.toArray()));
|
||
|
}
|
||
|
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(pa, node, valNo));
|
||
|
}
|
||
|
|
||
|
return elts;
|
||
|
}
|
||
|
|
||
|
private Iterable<CodeElement> getInCodeElts(CGNode node, SSAInstruction inst) {
|
||
|
int useNo = inst.getNumberOfUses();
|
||
|
Set<CodeElement> 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(pa, node, valNo));
|
||
|
} catch (IllegalArgumentException e) {
|
||
|
logger.error("Exception working on node: " + node);
|
||
|
logger.error("Node is in method: " + node.getMethod());
|
||
|
throw e;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return elts;
|
||
|
}
|
||
|
|
||
|
// private Iterable<CodeElement> getOutCodeElts(final CGNode node, final
|
||
|
// SSAInstruction inst) {
|
||
|
// return new Iterable<CodeElement>() {
|
||
|
// @Override
|
||
|
// public Iterator<CodeElement> iterator() {
|
||
|
// return new DefEltIterator(node, inst);
|
||
|
// }
|
||
|
// };
|
||
|
// }
|
||
|
//
|
||
|
// private Iterable<CodeElement> getInCodeElts(final CGNode node, final
|
||
|
// SSAInstruction inst) {
|
||
|
// return new Iterable<CodeElement>() {
|
||
|
// @Override
|
||
|
// public Iterator<CodeElement> iterator() {
|
||
|
// return new UseEltIterator(node, inst);
|
||
|
// }
|
||
|
// };
|
||
|
// }
|
||
|
|
||
|
private Set<CodeElement> getFieldAccessCodeElts(CGNode node,
|
||
|
SSAFieldAccessInstruction inst) {
|
||
|
if (inst.isStatic()) {
|
||
|
return getStaticFieldAccessCodeElts(node, inst);
|
||
|
}
|
||
|
|
||
|
Set<CodeElement> 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<InstanceKey> pointsToSet = pa.getPointsToSet(pk);
|
||
|
if (pointsToSet.isEmpty()) {
|
||
|
logger.debug(
|
||
|
"pointsToSet empty for ref of {}, creating InstanceKey manually",
|
||
|
inst);
|
||
|
InstanceKey ik = new ConcreteTypeKey(field.getDeclaringClass());
|
||
|
elts.add(new FieldElement(ik, fieldRef));
|
||
|
elts.add(new InstanceKeyElement(ik));
|
||
|
} else {
|
||
|
for (InstanceKey ik : pointsToSet) {
|
||
|
if (logger.isTraceEnabled()) {
|
||
|
logger.trace("adding elements for field {} on {}",
|
||
|
field.getName(), ik.getConcreteType().getName());
|
||
|
}
|
||
|
elts.add(new FieldElement(ik, fieldRef));
|
||
|
elts.add(new InstanceKeyElement(ik));
|
||
|
}
|
||
|
}
|
||
|
return elts;
|
||
|
}
|
||
|
|
||
|
private Set<CodeElement> getStaticFieldAccessCodeElts(CGNode node,
|
||
|
SSAFieldAccessInstruction inst) {
|
||
|
Set<CodeElement> elts = HashSetFactory.make();
|
||
|
final FieldReference fieldRef = inst.getDeclaredField();
|
||
|
elts.add(new StaticFieldElement(fieldRef));
|
||
|
// TODO: what about tainting the declaring class?
|
||
|
|
||
|
return elts;
|
||
|
}
|
||
|
|
||
|
private Set<CodeElement> getArrayRefCodeElts(CGNode node,
|
||
|
SSAArrayReferenceInstruction inst) {
|
||
|
Set<CodeElement> elts = HashSetFactory.make();
|
||
|
final PointerKey pk = pa.getHeapModel().getPointerKeyForLocal(node,
|
||
|
inst.getArrayRef());
|
||
|
final OrdinalSet<InstanceKey> pointsToSet = pa.getPointsToSet(pk);
|
||
|
if (pointsToSet.isEmpty()) {
|
||
|
logger.debug(
|
||
|
"pointsToSet empty for ref of {}, creating InstanceKey manually",
|
||
|
inst);
|
||
|
TypeReference arrayType = TypeReference.findOrCreateArrayOf(inst
|
||
|
.getElementType());
|
||
|
InstanceKey ik = new ConcreteTypeKey(pa.getClassHierarchy()
|
||
|
.lookupClass(arrayType));
|
||
|
elts.add(new InstanceKeyElement(ik));
|
||
|
} else {
|
||
|
for (InstanceKey ik : pointsToSet) {
|
||
|
if (logger.isTraceEnabled()) {
|
||
|
logger.trace("adding element for array store in {}", ik
|
||
|
.getConcreteType().getName());
|
||
|
}
|
||
|
elts.add(new InstanceKeyElement(ik));
|
||
|
}
|
||
|
}
|
||
|
return elts;
|
||
|
}
|
||
|
|
||
|
private IUnaryFlowFunction union(final IUnaryFlowFunction g,
|
||
|
final IUnaryFlowFunction h) {
|
||
|
return new IUnaryFlowFunction() {
|
||
|
@Override
|
||
|
public IntSet getTargets(int d1) {
|
||
|
return g.getTargets(d1).union(h.getTargets(d1));
|
||
|
}
|
||
|
};
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Flow function composition
|
||
|
*
|
||
|
* @param f
|
||
|
* @param g
|
||
|
* @return { (x, z) | (x, y) \in g, (y, z) \in f }
|
||
|
*/
|
||
|
private IUnaryFlowFunction compose(final IUnaryFlowFunction f,
|
||
|
final IUnaryFlowFunction g) {
|
||
|
return new IUnaryFlowFunction() {
|
||
|
|
||
|
@Override
|
||
|
public IntSet getTargets(int d1) {
|
||
|
final MutableSparseIntSet set = MutableSparseIntSet.makeEmpty();
|
||
|
g.getTargets(d1).foreach(new IntSetAction() {
|
||
|
|
||
|
@Override
|
||
|
public void act(int x) {
|
||
|
set.addAll(f.getTargets(x));
|
||
|
}
|
||
|
});
|
||
|
return set;
|
||
|
}
|
||
|
};
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* private class UseEltIterator implements Iterator<CodeElement> { private
|
||
|
* int idx = 0; private Iterator<CodeElement> 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<CodeElement> 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<CodeElement> { private
|
||
|
* int idx = 0; private Iterator<CodeElement> 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<CodeElement> 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() {} }
|
||
|
*/
|
||
|
}
|