/* * ControlFlowReconstruction.java - This file is part of the Jakstab project. * Copyright 2007-2015 Johannes Kinder <jk@jakstab.org> * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, see <http://www.gnu.org/licenses/>. */ package org.jakstab.analysis; import java.util.*; import org.jakstab.AnalysisManager; import org.jakstab.AnalysisProperties; import org.jakstab.Options; import org.jakstab.Program; import org.jakstab.Algorithm; import org.jakstab.analysis.composite.CompositeProgramAnalysis; import org.jakstab.analysis.composite.DualCompositeAnalysis; import org.jakstab.analysis.location.LocationAnalysis; import org.jakstab.analysis.tracereplay.TraceReplayAnalysis; import org.jakstab.asm.*; import org.jakstab.asm.x86.X86Instruction; import org.jakstab.cfa.*; import org.jakstab.rtl.statements.BasicBlock; import org.jakstab.util.*; /** * The control flow reconstruction algorithm in the CPA framework. * * @author Johannes Kinder */ public class ControlFlowReconstruction implements Algorithm { private class PriorityWorklist implements Worklist<AbstractState> { private FastSet<AbstractState> worklist = new FastSet<AbstractState>(); private FastSet<AbstractState> priorityList = new FastSet<AbstractState>(); @Override public AbstractState pick() { if (!priorityList.isEmpty()) { AbstractState p = priorityList.pick(); //logger.info("Returning prioritized state at " + p.getLocation()); return p; } else { return worklist.pick(); } } @Override public boolean add(AbstractState a) { if (!program.containsLabel((RTLLabel)a.getLocation())) { return priorityList.add(a); } else { if (priorityList.contains(a)) return false; else return worklist.add(a); } } public boolean isEmpty() { return priorityList.isEmpty() && worklist.isEmpty(); } @Override public boolean remove(AbstractState a) { // Single | is intended, run remove on both return priorityList.remove(a) | worklist.remove(a); } @Override public int size() { return priorityList.size() + worklist.size(); } @Override public String toString() { return size() + " elements. " + "Prio: " + priorityList.toString() + " Std: " + worklist.toString(); } } @SuppressWarnings("unused") private class RandomizedWorklist implements Worklist<AbstractState> { private ArrayList<AbstractState> list = new ArrayList<AbstractState>(10); private Random rand = new Random(); @Override public boolean add(AbstractState element) { return list.add(element); } @Override public boolean isEmpty() { return list.isEmpty(); } @Override public AbstractState pick() { return list.get(rand.nextInt(list.size())); } @Override public boolean remove(AbstractState element) { return list.remove(element); } @Override public int size() { return list.size(); } } private static final Logger logger = Logger.getLogger(ControlFlowReconstruction.class); private Program program; private ResolvingTransformerFactory transformerFactory; private CPAAlgorithm cpaAlgorithm; private String status; public ControlFlowReconstruction(Program program) { logger.info("Initializing control flow reconstruction."); this.program = program; // Init CPAs List<ConfigurableProgramAnalysis> cpas = new LinkedList<ConfigurableProgramAnalysis>(); boolean addedExplicitAnalysis = false; boolean addedUnderApproximation = false; AnalysisManager mgr = AnalysisManager.getInstance(); for (int i=0; i<Options.cpas.getValue().length(); i++) { char shortHand = Options.cpas.getValue().charAt(i); // Special handling for trace replay analysis that really creates multiple CPAs if (shortHand == 't') { logger.info("--- Using trace replay analysis."); for (String fileName : TraceReplayAnalysis.traceFiles.getValue().split(",")) { cpas.add(new TraceReplayAnalysis(fileName)); } addedExplicitAnalysis = true; addedUnderApproximation = true; continue; } ConfigurableProgramAnalysis cpa = mgr.createAnalysis(shortHand); if (cpa != null) { AnalysisProperties p = mgr.getProperties(cpa); logger.info("--- Using " + p.getName()); addedExplicitAnalysis |= p.isExplicit(); cpas.add(cpa); } else { logger.fatal("No analysis corresponds to letter \"" + shortHand + "\"!"); System.exit(1); } } if (!addedExplicitAnalysis) { logger.fatal("You need to specify at least one explicit value analysis: c, b, x or i"); System.exit(1); } ConfigurableProgramAnalysis cpa; if (!addedUnderApproximation) { cpa = new CompositeProgramAnalysis(new LocationAnalysis(), cpas.toArray(new ConfigurableProgramAnalysis[cpas.size()])); } else { cpa = new DualCompositeAnalysis(new LocationAnalysis(), cpas.toArray(new ConfigurableProgramAnalysis[cpas.size()])); } // Init State transformer factory if (Options.basicBlocks.getValue()) { if (addedUnderApproximation) { logger.fatal("Currently, basic block summarization cannot be combined with under-approximations!"); System.exit(1); } transformerFactory = new PessimisticBasicBlockFactory(); } else if (addedUnderApproximation) { transformerFactory = new AlternatingStateTransformerFactory(); } else { switch (Options.procedureAbstraction.getValue()) { case 0: transformerFactory = new PessimisticStateTransformerFactory(); break; case 1: transformerFactory = new InterproceduralTransformerFactory(); break; case 2: transformerFactory = new OptimisticStateTransformerFactory(); break; default: throw new RuntimeException("Invalid procedure abstraction level: " + Options.procedureAbstraction); } } //Worklist<AbstractState> worklist = new RandomizedWorklist(); Worklist<AbstractState> worklist = new PriorityWorklist(); //Worklist<AbstractState> worklist = new FastSet<AbstractState>(); cpaAlgorithm = new CPAAlgorithm(cpa, transformerFactory, worklist, Options.failFast.getValue()); } public ReachedSet getReachedStates() { return cpaAlgorithm.getReachedStates(); } public long getNumberOfStatesVisited() { return cpaAlgorithm.getNumberOfStatesVisited(); } public AbstractReachabilityTree getART() { return cpaAlgorithm.getART(); } public boolean isCompleted() { return cpaAlgorithm.isCompleted(); } public boolean isSound() { return !Options.ignoreWeakUpdates.getValue() && transformerFactory.isSound(); } public void run() { logger.info("Starting control flow reconstruction."); try { cpaAlgorithm.run(); status = cpaAlgorithm.isCompleted() ? "OK" : "interrupted"; } catch (StateException e) { logger.warn(e.getMessage()); status = e.getClass().getSimpleName(); Deque<AbstractState> trace = new LinkedList<AbstractState>(); if (cpaAlgorithm.getART() != null) { if (Options.errorTrace.getValue()) { AbstractState s = e.getState(); while (s != null) { trace.addFirst(s); s = cpaAlgorithm.getART().getParent(s); } AbstractState last = null; logger.warn("==== Error trace ===="); for (AbstractState state : trace) { // If we use basic blocks, don't attempt to print last edge (is probably a split block) if (transformerFactory instanceof PessimisticBasicBlockFactory && state == e.getState()) break; if (last != null) { for (CFAEdge edge : transformerFactory.getExistingOutEdges((RTLLabel)last.getLocation())) { if (edge.getTarget().equals(state.getLocation())) { logger.warn(edge.getTransformer()); break; } } } logger.warn(""); logger.warn(state); logger.warn(""); last = state; } // Replay basic block up to the error state location if (transformerFactory instanceof PessimisticBasicBlockFactory) { for (CFAEdge edge : transformerFactory.getExistingOutEdges((RTLLabel)last.getLocation())) { BasicBlock bb = (BasicBlock)edge.getTransformer(); if (bb.containsLocation((RTLLabel)e.getState().getLocation())) { logger.warn(bb.toStringUntil((RTLLabel)e.getState().getLocation())); break; } } logger.warn("State before last statement in block:"); logger.warn(e.getState()); } // No basic block, just output all transformers where post possibly failed else { logger.warn("Edges from error state: "); for (CFAEdge edge : transformerFactory .getExistingOutEdges((RTLLabel)e.getState().getLocation())) logger.warn(edge.getTransformer()); } //logger.warn("Error state:"); //logger.warn(s); /* AbstractState p = cpaAlgorithm.getART().getParent(s); while (p != null) { //logger.warn(program.getStatement((Location)s.getLocation())); //logger.warn(s); //s = cpaAlgorithm.getART().getParent(s); for (CFAEdge edge : transformerFactory.getExistingOutEdges(p.getLocation())) { if (edge.getTarget().equals(s.getLocation())) { logger.warn(edge.getTransformer()); break; } } logger.warn(p); s = p; p = cpaAlgorithm.getART().getParent(s); } */ } if (Options.asmTrace.getValue()) { logger.warn("==== Error trace (ASM) ===="); AbsoluteAddress lastAddr = null; AbsoluteAddress addr = null; AbstractState s = e.getState(); while (s != null) { lastAddr = addr; addr = s.getLocation().getAddress(); if (!addr.equals(lastAddr) && program.getModule(addr) != null) { StringBuilder sb = new StringBuilder(); SymbolFinder symFinder = program.getModule(addr).getSymbolFinder(); sb.append(symFinder.getSymbolFor(addr)); sb.append(":\t"); Instruction instr = program.getInstruction(addr); if (instr != null) { if (instr instanceof X86Instruction && ((X86Instruction)instr).hasPrefixLOCK() && ((X86Instruction)instr).hasPrefixREPZ()) { sb.append(program.getStatement((RTLLabel)s.getLocation())); } else { sb.append(instr.toString(addr.getValue(), symFinder)); } } if (symFinder.hasSymbolFor(addr)) sb.append(Characters.NEWLINE); logger.warn(sb.toString()); } s = cpaAlgorithm.getART().getParent(s); } } } else { logger.warn("No ART has been built, cannot show backtrace!"); } } catch (RuntimeException e) { // For other runtime exceptions (bugs in Jakstab), set the status to the name of the exception status = e.toString(); throw e; } finally { program.setCFA(transformerFactory.getCFA()); program.setUnresolvedBranches(transformerFactory.getUnresolvedBranches()); } } public void stop() { cpaAlgorithm.stop(); } public String getStatus() { return status; } }