/* * BoundedAddressTracking.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.explicit; import java.util.*; import org.jakstab.AnalysisProperties; import org.jakstab.JOption; import org.jakstab.Program; import org.jakstab.analysis.*; import org.jakstab.asm.AbsoluteAddress; import org.jakstab.asm.x86.X86Instruction; import org.jakstab.cfa.CFAEdge; import org.jakstab.cfa.Location; import org.jakstab.cfa.StateTransformer; import org.jakstab.rtl.expressions.ExpressionFactory; import org.jakstab.rtl.expressions.RTLVariable; import org.jakstab.rtl.statements.RTLStatement; import org.jakstab.util.*; import org.jakstab.util.MapMap.EntryIterator; import com.google.common.collect.*; /** * @author Johannes Kinder */ public class BoundedAddressTracking implements ConfigurableProgramAnalysis { private final static Logger logger = Logger.getLogger(BoundedAddressTracking.class); public static void register(AnalysisProperties p) { p.setShortHand('x'); p.setName("Bounded Address Tracking"); p.setDescription("Enumerate region-based addresses up to a bound per variable per location."); p.setExplicit(true); } public static JOption<Integer> varThreshold = JOption.create("explicit-threshold", "k", 5, "Set the maximum number of values tracked per variable per location."); public static JOption<Integer> heapThreshold = JOption.create("heap-threshold", "k", 5, "Explicit threshold for data stored on the heap."); public static JOption<Boolean> repPrecBoost = JOption.create("rep-prec-boost", "Increase precision for rep-prefixed instructions."); public static JOption<Boolean> keepDeadStack = JOption.create("keep-dead-stack", "Do not discard stack contents below current stack pointer."); public BoundedAddressTracking() { } @Override public AbstractState merge(AbstractState s1, AbstractState s2, Precision precision) { // Reduces states, but makes it harder to reconstruct the trace that lead to a certain state if (s2.lessOrEqual(s1)) return s1; return CPAOperators.mergeSep(s1, s2, precision); } @Override public boolean stop(AbstractState s, ReachedSet reached, Precision precision) { return CPAOperators.stopSep(s, reached, precision); } @Override public Set<AbstractState> post(AbstractState state, CFAEdge cfaEdge, Precision precision) { return ((BasedNumberValuation)state).abstractPost((RTLStatement)cfaEdge.getTransformer(), precision); } @Override public AbstractState strengthen(AbstractState s, Iterable<AbstractState> otherStates, CFAEdge cfaEdge, Precision precision) { return s; } @Override public Pair<AbstractState, Precision> prec(AbstractState s, Precision precision, ReachedSet reached) { // This method uses the fact that there is only 1 precision per location ExplicitPrecision eprec = (ExplicitPrecision)precision; BasedNumberValuation widenedState = (BasedNumberValuation)s; // Only check value counts if we have at least enough states to reach it if (reached.size() > Math.min(varThreshold.getValue(), heapThreshold.getValue())) { boolean changed = false; // Check value counts for variables for (RTLVariable v : eprec.varMap.keySet()) { //BasedNumberElement currentValue = ((BasedNumberValuation)s).getValue(v); Set<BasedNumberElement> existingValues = eprec.varMap.get(v); int threshold = eprec.getThreshold(v); if (existingValues.size() > threshold) { // Lower precisions and widen the value in this state, too. // This avoids values accumulating at join points (where they are not // intercepted by the precision-aware setValue) if (countRegions(existingValues) > threshold) { eprec.stopTracking(v); if (!changed) { widenedState = new BasedNumberValuation(widenedState); changed = true; } widenedState.setValue(v, BasedNumberElement.getTop(v.getBitWidth())); } else { eprec.trackRegionOnly(v); if (!changed) { widenedState = new BasedNumberValuation(widenedState); changed = true; } logger.debug("Only tracking region of " + v + ", values were " + existingValues); widenedState.setValue(v, new BasedNumberElement( widenedState.getValue(v).getRegion(), NumberElement.getTop(v.getBitWidth()))); } } } // Check value counts for store PartitionedMemory<BasedNumberElement> sStore = ((BasedNumberValuation)s).getStore(); for (EntryIterator<MemoryRegion, Long, BasedNumberElement> entryIt = sStore.entryIterator(); entryIt.hasEntry(); entryIt.next()) { MemoryRegion region = entryIt.getLeftKey(); Long offset = entryIt.getRightKey(); BasedNumberElement value = entryIt.getValue(); SetMultimap<Long, BasedNumberElement> memoryMap = eprec.regionMaps.get(region); if (memoryMap == null) continue; //BasedNumberElement currentValue = entry.getValue(); Set<BasedNumberElement> existingValues = memoryMap.get(offset); int threshold = eprec.getStoreThreshold(region, offset); if (existingValues.size() > threshold) { if (countRegions(existingValues) > 5*threshold) { eprec.stopTracking(region, offset); if (!changed) { widenedState = new BasedNumberValuation(widenedState); changed = true; } widenedState.getStore().set(region, offset, value.getBitWidth(), BasedNumberElement.getTop(value.getBitWidth())); } else { eprec.trackRegionOnly(region, offset); if (!changed) { widenedState = new BasedNumberValuation(widenedState); changed = true; } widenedState.getStore().set(region, offset, value.getBitWidth(), new BasedNumberElement(value.getRegion(), NumberElement.getTop(value.getBitWidth()))); } } } } // Collect all values for all variables for (Map.Entry<RTLVariable, BasedNumberElement> entry : widenedState.getVariableValuation()) { eprec.varMap.put(entry.getKey(), entry.getValue()); } // Collect all values for all memory areas PartitionedMemory<BasedNumberElement> store = widenedState.getStore(); for (EntryIterator<MemoryRegion, Long, BasedNumberElement> entryIt = store.entryIterator(); entryIt.hasEntry(); entryIt.next()) { SetMultimap<Long, BasedNumberElement> memoryMap = eprec.regionMaps.get(entryIt.getLeftKey()); if (memoryMap == null) { memoryMap = HashMultimap.create(); eprec.regionMaps.put(entryIt.getLeftKey(), memoryMap); } memoryMap.put(entryIt.getRightKey(), entryIt.getValue()); } // If it was changed, widenedState is now a new state return Pair.create((AbstractState)widenedState, precision); } @Override public AbstractState initStartState(Location location) { return BasedNumberValuation.createInitialState(); } @Override public Precision initPrecision(Location location, StateTransformer transformer) { ExplicitPrecision p = new ExplicitPrecision(varThreshold.getValue()); // Increase precision of ecx, esi, edi for REP prefixed instructions Program program = Program.getProgram(); if (BoundedAddressTracking.repPrecBoost.getValue()) { AbsoluteAddress addr = location.getAddress(); X86Instruction instr = (X86Instruction)program.getInstruction(addr); if (instr != null && (instr.hasPrefixREPZ() || instr.hasPrefixREPNZ())) { logger.debug("boost-rep: REP instruction at " + location + ", increasing precision of loop registers."); p.setThreshold(ExpressionFactory.createVariable("%ecx"), 1000); p.setThreshold(ExpressionFactory.createVariable("%esi"), 1000); p.setThreshold(ExpressionFactory.createVariable("%edi"), 1000); } } return p; } private int countRegions(Set<BasedNumberElement> values) { Set<MemoryRegion> regions = new HashSet<MemoryRegion>(); for (BasedNumberElement e : values) regions.add(e.getRegion()); return regions.size(); } }