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

boomerang.solver.AbstractBoomerangSolver Maven / Gradle / Ivy

There is a newer version: 3.2.2
Show newest version
/**
 * ***************************************************************************** Copyright (c) 2018
 * Fraunhofer IEM, Paderborn, Germany. This program and the accompanying materials are made
 * available under the terms of the Eclipse Public License 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * 

SPDX-License-Identifier: EPL-2.0 * *

Contributors: Johannes Spaeth - initial API and implementation * ***************************************************************************** */ package boomerang.solver; import boomerang.BoomerangOptions; import boomerang.Query; import boomerang.callgraph.BackwardsObservableICFG; import boomerang.callgraph.CallerListener; import boomerang.callgraph.ObservableICFG; import boomerang.controlflowgraph.ObservableControlFlowGraph; import boomerang.scene.AllocVal; import boomerang.scene.ControlFlowGraph; import boomerang.scene.ControlFlowGraph.Edge; import boomerang.scene.DataFlowScope; import boomerang.scene.Field; import boomerang.scene.Method; import boomerang.scene.Statement; import boomerang.scene.Type; import boomerang.scene.Val; import boomerang.util.RegExAccessPath; import com.google.common.base.Stopwatch; import com.google.common.collect.HashBasedTable; import com.google.common.collect.HashMultimap; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Multimap; import com.google.common.collect.Sets; import com.google.common.collect.Table; import java.util.AbstractMap; import java.util.Collection; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.slf4j.LoggerFactory; import pathexpression.IRegEx; import sync.pds.solver.EmptyStackWitnessListener; import sync.pds.solver.SyncPDSSolver; import sync.pds.solver.WitnessListener; import sync.pds.solver.nodes.GeneratedState; import sync.pds.solver.nodes.INode; import sync.pds.solver.nodes.Node; import sync.pds.solver.nodes.SingleNode; import wpds.impl.NestedWeightedPAutomatons; import wpds.impl.NormalRule; import wpds.impl.Rule; import wpds.impl.Transition; import wpds.impl.Weight; import wpds.impl.WeightedPAutomaton; import wpds.impl.WeightedPushdownSystem; import wpds.interfaces.State; import wpds.interfaces.WPAUpdateListener; public abstract class AbstractBoomerangSolver extends SyncPDSSolver { private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(AbstractBoomerangSolver.class); protected final ObservableICFG icfg; protected final ObservableControlFlowGraph cfg; protected boolean INTERPROCEDURAL = true; protected final Map< Entry>, Field>, INode>> generatedFieldState; private Multimap>>> perMethodFieldTransitions = HashMultimap.create(); private Multimap> perMethodFieldTransitionsListener = HashMultimap.create(); protected Multimap< ControlFlowGraph.Edge, Transition>>> perStatementFieldTransitions = HashMultimap.create(); private Multimap> perStatementFieldTransitionsListener = HashMultimap.create(); private HashBasedTable>, W> perStatementCallTransitions = HashBasedTable.create(); private Multimap> perStatementCallTransitionsListener = HashMultimap.create(); private Multimap> unbalancedDataFlows = HashMultimap.create(); private Multimap unbalancedDataFlowListeners = HashMultimap.create(); protected final DataFlowScope dataFlowScope; protected final BoomerangOptions options; protected final Type type; public AbstractBoomerangSolver( ObservableICFG icfg, ObservableControlFlowGraph cfg, Map>, Field>, INode>> genField, BoomerangOptions options, NestedWeightedPAutomatons, W> callSummaries, NestedWeightedPAutomatons>, W> fieldSummaries, DataFlowScope scope, Type propagationType) { super( icfg instanceof BackwardsObservableICFG ? false : options.callSummaries(), callSummaries, options.fieldSummaries(), fieldSummaries, options.maxCallDepth(), options.maxFieldDepth(), options.maxUnbalancedCallDepth()); this.options = options; this.icfg = icfg; this.cfg = cfg; this.dataFlowScope = scope; this.type = propagationType; this.fieldAutomaton.registerListener( (t, w, aut) -> { addTransitionToMethod(t.getStart().fact().stmt().getStart().getMethod(), t); addTransitionToMethod(t.getTarget().fact().stmt().getStart().getMethod(), t); addTransitionToStatement(t.getStart().fact().stmt(), t); }); this.callAutomaton.registerListener( (t, w, aut) -> { addCallTransitionToStatement(t.getLabel(), t, w); }); this.callAutomaton.registerListener(new UnbalancedListener()); this.generatedFieldState = genField; } public boolean reachesNodeWithEmptyField(Node node) { for (Transition>> t : getFieldAutomaton().getTransitions()) { if (t.getStart() instanceof GeneratedState) { continue; } if (t.getStart().fact().equals(node) && t.getLabel().equals(Field.empty())) { return true; } } return false; } private class UnbalancedListener implements WPAUpdateListener, W> { @Override public void onWeightAdded( Transition> t, W w, WeightedPAutomaton, W> aut) { if (t.getLabel().equals(new Edge(Statement.epsilon(), Statement.epsilon()))) return; if (icfg.isExitStmt( (AbstractBoomerangSolver.this instanceof ForwardBoomerangSolver ? t.getLabel().getTarget() : t.getLabel().getStart()))) { Statement exitStmt = t.getLabel().getTarget(); Method callee = exitStmt.getMethod(); if (callAutomaton.getInitialStates().contains(t.getTarget())) { addPotentialUnbalancedFlow(callee, t, w); } } } } public INode> createQueryNodeField(Query query) { return new SingleNode( /* TODO Replace by new designated type */ new Node<>( query.cfgEdge(), query.asNode().fact().asUnbalanced(query.cfgEdge()))); } public void synchedEmptyStackReachable( final Node sourceNode, final EmptyStackWitnessListener listener) { synchedReachable( sourceNode, new WitnessListener() { Multimap> potentialFieldCandidate = HashMultimap.create(); Set potentialCallCandidate = Sets.newHashSet(); @Override public void fieldWitness(Transition>> t) { if (t.getTarget() instanceof GeneratedState) return; if (!t.getLabel().equals(emptyField())) return; Node targetFact = new Node<>( t.getTarget().fact().stmt(), t.getTarget().fact().fact().asUnbalanced(null)); if (!potentialFieldCandidate.put(targetFact.fact(), targetFact)) return; if (potentialCallCandidate.contains(targetFact.fact())) { listener.witnessFound(targetFact); } } @Override public void callWitness(Transition> t) { Val targetFact = t.getTarget().fact(); if (targetFact instanceof AllocVal) { targetFact = ((AllocVal) targetFact).getDelegate(); if (!potentialCallCandidate.add(targetFact)) return; if (potentialFieldCandidate.containsKey(targetFact)) { for (Node w : potentialFieldCandidate.get(targetFact)) { listener.witnessFound(w); } } } } }); } public void synchedReachable( final Node sourceNode, final WitnessListener listener) { registerListener( reachableNode -> { if (!reachableNode.equals(sourceNode)) return; fieldAutomaton.registerListener( (t, w, aut) -> { if (t.getStart() instanceof GeneratedState) return; if (!t.getStart().fact().equals(sourceNode)) return; listener.fieldWitness(t); }); callAutomaton.registerListener( (t, w, aut) -> { if (t.getStart() instanceof GeneratedState) return; if (!t.getStart().fact().equals(sourceNode.fact())) return; if (!t.getLabel().equals(sourceNode.stmt())) return; if (callAutomaton.isUnbalancedState(t.getTarget())) { listener.callWitness(t); } }); }); } public Table asStatementValWeightTable() { final Table results = HashBasedTable.create(); Stopwatch sw = Stopwatch.createStarted(); WeightedPAutomaton, W> callAut = getCallAutomaton(); for (Entry>, W> e : callAut.getTransitionsToFinalWeights().entrySet()) { Transition> t = e.getKey(); W w = e.getValue(); if (t.getLabel().equals(new Edge(Statement.epsilon(), Statement.epsilon()))) continue; if (t.getStart().fact().isLocal() && !t.getLabel().getMethod().equals(t.getStart().fact().m())) continue; results.put(t.getLabel(), t.getStart().fact(), w); } return results; } protected void addPotentialUnbalancedFlow( Method callee, Transition> trans, W weight) { if (unbalancedDataFlows.put(callee, new UnbalancedDataFlow<>(callee, trans))) { Collection existingListeners = Lists.newArrayList(unbalancedDataFlowListeners.get(callee)); for (UnbalancedDataFlowListener l : existingListeners) { propagateUnbalancedToCallSite(l.getCallSiteEdge(), trans); } } if (forceUnbalanced(trans.getTarget(), callAutomaton.getUnbalancedStartOf(trans.getTarget()))) { icfg.addCallerListener( new CallerListener() { @Override public Method getObservedCallee() { return callee; } @Override public void onCallerAdded(Statement n, Method m) { propagateUnbalancedToCallSite(n, trans); } }); } } protected boolean forceUnbalanced(INode iNode, Collection> collection) { return false; } public void allowUnbalanced(Method callee, Statement callSite) { if (dataFlowScope.isExcluded(callee)) { return; } UnbalancedDataFlowListener l = new UnbalancedDataFlowListener(callee, callSite); if (unbalancedDataFlowListeners.put(callee, l)) { LOGGER.trace("Allowing unbalanced propagation from {} to {} of {}", callee, callSite, this); for (UnbalancedDataFlow e : Lists.newArrayList(unbalancedDataFlows.get(callee))) { propagateUnbalancedToCallSite(callSite, e.getReturningTransition()); } } } protected boolean isMatchingCallSiteCalleePair(Statement callSite, Method method) { Set callsitesOfCall = Sets.newHashSet(); icfg.addCallerListener( new CallerListener() { @Override public Method getObservedCallee() { return method; } @Override public void onCallerAdded(Statement statement, Method method) { callsitesOfCall.add(statement); } }); return callsitesOfCall.contains(callSite); } protected abstract void propagateUnbalancedToCallSite( Statement callSiteEdge, Transition> transInCallee); @Override protected boolean preventCallTransitionAdd( Transition> t, W weight) { return false; } @Override public void addCallRule(final Rule, W> rule) { if (rule instanceof NormalRule) { if (rule.getL1().equals(rule.getL2()) && rule.getS1().equals(rule.getS2())) return; } super.addCallRule(rule); } @Override public void addFieldRule(final Rule>, W> rule) { if (rule instanceof NormalRule) { if (rule.getL1().equals(rule.getL2()) && rule.getS1().equals(rule.getS2())) return; } super.addFieldRule(rule); } private void addTransitionToMethod(Method method, Transition>> t) { if (perMethodFieldTransitions.put(method, t)) { for (MethodBasedFieldTransitionListener l : Lists.newArrayList(perMethodFieldTransitionsListener.get(method))) { l.onAddedTransition(t); } } } public void registerFieldTransitionListener(MethodBasedFieldTransitionListener l) { if (perMethodFieldTransitionsListener.put(l.getMethod(), l)) { for (Transition>> t : Lists.newArrayList(perMethodFieldTransitions.get(l.getMethod()))) { l.onAddedTransition(t); } } } private void addTransitionToStatement( ControlFlowGraph.Edge s, Transition>> t) { if (perStatementFieldTransitions.put(s, t)) { for (ControlFlowEdgeBasedFieldTransitionListener l : Lists.newArrayList(perStatementFieldTransitionsListener.get(s))) { l.onAddedTransition(t); } } } public void registerStatementFieldTransitionListener( ControlFlowEdgeBasedFieldTransitionListener l) { if (perStatementFieldTransitionsListener.put(l.getCfgEdge(), l)) { for (Transition>> t : Lists.newArrayList(perStatementFieldTransitions.get(l.getCfgEdge()))) { l.onAddedTransition(t); } } } private void addCallTransitionToStatement(Edge s, Transition> t, W w) { W put = perStatementCallTransitions.get(s, t); if (put != null) { W combineWith = (W) put.combineWith(w); if (!combineWith.equals(put)) { perStatementCallTransitions.put(s, t, combineWith); for (ControlFlowEdgeBasedCallTransitionListener l : Lists.newArrayList(perStatementCallTransitionsListener.get(s))) { l.onAddedTransition(t, w); } } } else { perStatementCallTransitions.put(s, t, w); for (ControlFlowEdgeBasedCallTransitionListener l : Lists.newArrayList(perStatementCallTransitionsListener.get(s))) { l.onAddedTransition(t, w); } } } public void registerStatementCallTransitionListener( ControlFlowEdgeBasedCallTransitionListener l) { if (perStatementCallTransitionsListener.put(l.getControlFlowEdge(), l)) { Map>, W> row = perStatementCallTransitions.row(l.getControlFlowEdge()); for (Entry>, W> t : Lists.newArrayList(row.entrySet())) { l.onAddedTransition(t.getKey(), t.getValue()); } } } public INode> generateFieldState( final INode> d, final Field loc) { Entry>, Field> e = new AbstractMap.SimpleEntry<>(d, loc); if (!generatedFieldState.containsKey(e)) { generatedFieldState.put(e, new GeneratedState<>(d, loc)); } return generatedFieldState.get(e); } private boolean isBackward() { return this instanceof BackwardBoomerangSolver; } protected abstract Collection computeReturnFlow( Method method, Statement curr, Val value); protected void returnFlow(Method method, Node currNode) { Val value = currNode.fact(); Collection outFlow = computeReturnFlow(method, currNode.stmt().getTarget(), value); for (State s : outFlow) { propagate(currNode, s); } } protected abstract Collection computeNormalFlow(Method method, Edge currEdge, Val value); @Override public Field epsilonField() { return Field.epsilon(); } @Override public Field emptyField() { return Field.empty(); } @Override public ControlFlowGraph.Edge epsilonStmt() { return new Edge(Statement.epsilon(), Statement.epsilon()); } @Override public Field fieldWildCard() { return Field.wildcard(); } @Override public Field exclusionFieldWildCard(Field exclusion) { return Field.exclusionWildcard(exclusion); } public WeightedPAutomaton>, W> getFieldAutomaton() { return fieldAutomaton; } public WeightedPAutomaton, W> getCallAutomaton() { return callAutomaton; } public WeightedPushdownSystem, W> getCallPDS() { return callingPDS; } public WeightedPushdownSystem>, W> getFieldPDS() { return fieldPDS; } public int getNumberOfRules() { return callingPDS.getAllRules().size() + fieldPDS.getAllRules().size(); } @Override protected boolean preventFieldTransitionAdd( Transition>> t, W weight) { if (t.getStart().equals(t.getTarget()) && t.getLabel().equals(Field.empty())) { LOGGER.warn("Prevented illegal edge addition of {}", t); return true; } if (!t.getLabel().equals(Field.empty()) || !options.typeCheck()) { return false; } if (t.getTarget() instanceof GeneratedState || t.getStart() instanceof GeneratedState) { return false; } Val target = t.getTarget().fact().fact(); Val source = t.getStart().fact().fact(); if (source.isStatic()) { return false; } Type sourceVal = source.getType(); Type targetVal = (isBackward() ? target.getType() : type); if (sourceVal == null) { return true; } if (sourceVal.equals(targetVal)) { return false; } if (!(targetVal.isRefType()) || !(sourceVal.isRefType())) { if (options.killNullAtCast() && targetVal.isNullType() && isCastNode(t.getStart().fact())) { // A null pointer cannot be cast to any object return true; } return false; // !allocVal.value().getType().equals(varVal.value().getType()); } return sourceVal.doesCastFail(targetVal, target); } private boolean isCastNode(Node node) { boolean isCast = node.stmt().getStart().isCast(); if (isCast) { Val rightOp = node.stmt().getStart().getRightOp(); if (rightOp.isCast()) { if (rightOp.getCastOp().equals(node.fact())) { return true; } } } return false; } public Map getResultsAt(final Statement stmt) { final Map results = Maps.newHashMap(); fieldAutomaton.registerListener( (t, w, aut) -> { if (t.getStart() instanceof GeneratedState) { return; } if (t.getStart().fact().stmt().equals(stmt)) { for (INode> initState : fieldAutomaton.getInitialStates()) { IRegEx regEx = fieldAutomaton.toRegEx(t.getStart(), initState); results.put(new RegExAccessPath(t.getStart().fact().fact(), regEx), w); } } }); return results; } public Table getResults(Method m) { final Table results = HashBasedTable.create(); LOGGER.debug("Start extracting results from {}", this); fieldAutomaton.registerListener( (t, w, aut) -> { if (t.getStart() instanceof GeneratedState) { return; } if (t.getStart().fact().stmt().getStart().getMethod().equals(m)) { for (INode> initState : fieldAutomaton.getInitialStates()) { IRegEx regEx = fieldAutomaton.toRegEx(t.getStart(), initState); AbstractBoomerangSolver.this.callAutomaton.registerListener( (callT, w1, aut1) -> { if (callT.getStart().fact().equals(t.getStart().fact().fact()) && callT.getLabel().equals(t.getStart().fact().stmt())) { results.put( t.getStart().fact().stmt(), new RegExAccessPath(t.getStart().fact().fact(), regEx), w1); } }); } } }); LOGGER.debug("End extracted results from {}", this); return results; } public void debugFieldAutomaton(final Statement stmt) { fieldAutomaton.registerListener( (t, w, aut) -> { if (t.getStart() instanceof GeneratedState) { return; } if (t.getStart().fact().stmt().equals(stmt)) { for (INode> initState : fieldAutomaton.getInitialStates()) { IRegEx regEx = fieldAutomaton.toRegEx(t.getStart(), initState); LOGGER.debug(t.getStart().fact().fact() + " " + regEx); } } }); } public void unregisterAllListeners() { this.callAutomaton.unregisterAllListeners(); this.fieldAutomaton.unregisterAllListeners(); this.perMethodFieldTransitionsListener.clear(); this.perStatementCallTransitionsListener.clear(); this.perStatementFieldTransitionsListener.clear(); this.unbalancedDataFlowListeners.clear(); this.unbalancedDataFlows.clear(); this.callingPDS.unregisterAllListeners(); this.fieldPDS.unregisterAllListeners(); } private static class UnbalancedDataFlow { private final Method callee; private Transition> trans; public UnbalancedDataFlow(Method callee, Transition> trans) { this.callee = callee; this.trans = trans; } public Transition> getReturningTransition() { return trans; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((callee == null) ? 0 : callee.hashCode()); result = prime * result + ((trans == null) ? 0 : trans.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; UnbalancedDataFlow other = (UnbalancedDataFlow) obj; if (callee == null) { if (other.callee != null) return false; } else if (!callee.equals(other.callee)) return false; if (trans == null) { if (other.trans != null) return false; } else if (!trans.equals(other.trans)) return false; return true; } } private static class UnbalancedDataFlowListener { private Method callee; private Statement callSite; public UnbalancedDataFlowListener(Method callee, Statement callSite) { this.callee = callee; this.callSite = callSite; } public Statement getCallSiteEdge() { return callSite; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((callee == null) ? 0 : callee.hashCode()); result = prime * result + ((callSite == null) ? 0 : callSite.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; UnbalancedDataFlowListener other = (UnbalancedDataFlowListener) obj; if (callee == null) { if (other.callee != null) return false; } else if (!callee.equals(other.callee)) return false; if (callSite == null) { if (other.callSite != null) return false; } else if (!callSite.equals(other.callSite)) return false; return true; } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy