/* * SubstitutionState.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.substitution; import java.util.*; import org.jakstab.analysis.*; import org.jakstab.cfa.Location; import org.jakstab.cfa.StateTransformer; import org.jakstab.rtl.*; import org.jakstab.rtl.expressions.*; import org.jakstab.rtl.statements.*; import org.jakstab.util.*; /** * The abstract state for constant propagation. There are two explicit states * TOP and BOT, all other states use two collections of CPValue elements to * assign values to variables and memory. Elements not present in the * collections are implicitly set to a TOP value. BOT elements cannot be * present, since a single BOT element makes the whole state turn to BOT. * * @author Johannes Kinder */ public final class SubstitutionState implements AbstractState { @SuppressWarnings("unused") private static final Logger logger = Logger.getLogger(SubstitutionState.class); private static long maxStateId = 0; public static final SubstitutionState TOP = new SubstitutionState(); public static final SubstitutionState BOT = new SubstitutionState(); private Map<Writable,SubstitutionElement> aVarVal; private final long stateId; private SubstitutionState() { this (new HashMap<Writable, SubstitutionElement>()); } private SubstitutionState(Map<Writable,SubstitutionElement> aVarVal) { stateId = ++maxStateId; this.aVarVal = aVarVal; } /** * Copy constructor * * @param proto */ private SubstitutionState(SubstitutionState proto) { this(); aVarVal.putAll(proto.aVarVal); } /** * Evaluates an expression in the context of this abstract state to * an abstract value. * * @param e the expression to be evaluated. * @return the abstract value of the expression in the abstract state. */ protected SubstitutionElement abstractEval(RTLExpression e) { ExpressionVisitor<SubstitutionElement> visitor = new ExpressionVisitor<SubstitutionElement>() { @Override public SubstitutionElement visit(RTLBitRange e) { SubstitutionElement aFirstBit = e.getFirstBitIndex().accept(this); SubstitutionElement aLastBit = e.getLastBitIndex().accept(this); SubstitutionElement aOperand = e.getOperand().accept(this); return new SubstitutionElement( ExpressionFactory.createBitRange(aOperand.getExpression(), aFirstBit.getExpression(), aLastBit.getExpression()) ); } @Override public SubstitutionElement visit(RTLConditionalExpression e) { SubstitutionElement aCondition = e.getCondition().accept(this); SubstitutionElement aTrue = e.getTrueExpression().accept(this); SubstitutionElement aFalse = e.getFalseExpression().accept(this); return new SubstitutionElement( ExpressionFactory.createConditionalExpression(aCondition.getExpression(), aTrue.getExpression(), aFalse.getExpression()) ); } @Override public SubstitutionElement visit(RTLMemoryLocation m) { SubstitutionElement aAddress = m.getAddress().accept(this); if (!aAddress.isTop()) { m = ExpressionFactory.createMemoryLocation(m.getSegmentRegister(), aAddress.getExpression(), m.getBitWidth()); } SubstitutionElement s = getValue(m); if (s.isTop()) { return new SubstitutionElement(m); } else { return s; } } @Override public SubstitutionElement visit(RTLNondet e) { return new SubstitutionElement(e); } @Override public SubstitutionElement visit(RTLNumber e) { return new SubstitutionElement(e); } @Override public SubstitutionElement visit(RTLOperation e) { RTLExpression[] aOperands = new RTLExpression[e.getOperandCount()]; for (int i=0; i<e.getOperandCount(); i++) { aOperands[i] = e.getOperands()[i].accept(this).getExpression(); } return new SubstitutionElement( ExpressionFactory.createOperation(e.getOperator(), aOperands).evaluate(new Context()) ); } @Override public SubstitutionElement visit(RTLSpecialExpression e) { RTLExpression[] aOperands = new RTLExpression[e.getOperandCount()]; for (int i=0; i<e.getOperandCount(); i++) { aOperands[i] = e.getOperands()[i].accept(this).getExpression(); } return new SubstitutionElement( ExpressionFactory.createSpecialExpression(e.getOperator(), aOperands) ); } @Override public SubstitutionElement visit(RTLVariable e) { SubstitutionElement s = getValue(e); if (s.isTop()) { return new SubstitutionElement(e); } else { return s; } } }; SubstitutionElement result = e.accept(visitor); RTLExpression simplified = ExpressionSimplifier.getInstance().simplify(result.getExpression()); if (simplified != result.getExpression()) result = new SubstitutionElement(simplified); return result; } public AbstractState abstractPost(StateTransformer transformer, Precision precision) { if (isBot()) return BOT; final RTLStatement statement = (RTLStatement)transformer; return statement.accept(new DefaultStatementVisitor<SubstitutionState>() { private SubstitutionState clobber(Writable x) { SubstitutionState post = new SubstitutionState(SubstitutionState.this); // Remove existing substitutions for the pointer post.aVarVal.remove(x); // Remove substituted expressions that contain the pointer for (Iterator<Map.Entry<Writable, SubstitutionElement>> iter = post.aVarVal.entrySet().iterator(); iter.hasNext();) { Map.Entry<Writable, SubstitutionElement> e = iter.next(); if (e.getValue().getExpression().getUsedVariables().contains(x)) { iter.remove(); } } if (post.aVarVal.isEmpty()) return TOP; if (post.equals(SubstitutionState.this)) return SubstitutionState.this; return post; } @Override protected SubstitutionState visitDefault(RTLStatement stmt) { return SubstitutionState.this; } @Override public SubstitutionState visit(RTLVariableAssignment stmt) { // Copy old state to new state SubstitutionState post = new SubstitutionState(SubstitutionState.this); Writable lhs = stmt.getLeftHandSide(); RTLExpression rhs = stmt.getRightHandSide(); // Evaluate righthandside rhs = abstractEval(rhs).getExpression(); // Remove existing substitution for the LHS post.aVarVal.remove(lhs); // If RHS is a pure variable, assign RHS to LHS as substitution if (!containsNondet(rhs)) { post.setValue(lhs, new SubstitutionElement(rhs)); } // If any expression in the map uses the LHS variable, it is now invalid, so remove it // Note: This also removes substitutions that were just added such // as esp = esp - 4 List<RTLVariable> aliasing = new LinkedList<RTLVariable>(); aliasing.add((RTLVariable)lhs); // Remove mappings of all aliasing registers aliasing.addAll(ExpressionFactory.coveredRegisters((RTLVariable)lhs)); aliasing.addAll(ExpressionFactory.coveringRegisters((RTLVariable)lhs)); for (RTLVariable v : aliasing) { for (Iterator<Map.Entry<Writable, SubstitutionElement>> iter = post.aVarVal.entrySet().iterator(); iter.hasNext();) { Map.Entry<Writable, SubstitutionElement> e = iter.next(); if (e.getKey().getUsedVariablesOnWrite().contains(v) || e.getValue().getExpression().getUsedVariables().contains(v)) { iter.remove(); } } } if (post.aVarVal.isEmpty()) return TOP; if (post.equals(SubstitutionState.this)) return SubstitutionState.this; //logger.info("Post: " + post); return post; } @Override public SubstitutionState visit(RTLMemoryAssignment stmt) { // Copy old state to new state SubstitutionState post = new SubstitutionState(SubstitutionState.this); RTLMemoryLocation lhs = stmt.getLeftHandSide(); RTLExpression rhs = stmt.getRightHandSide(); // Evaluate righthandside rhs = abstractEval(rhs).getExpression(); // Substitute address elements in a memory location LHS SubstitutionElement aAddress = abstractEval(lhs.getAddress()); if (!aAddress.isTop()) lhs = ExpressionFactory.createMemoryLocation(lhs.getSegmentRegister(), aAddress.getExpression(), lhs.getBitWidth()); // Remove existing substitution for the LHS post.aVarVal.remove(lhs); // If RHS is a pure memory expression, assign RHS to LHS as substitution if (!containsNondet(rhs)) { post.setValue(lhs, new SubstitutionElement(rhs)); } // If any expression in the map uses the LHS variable, it is now invalid, so remove it // Note: This also removes substitutions that were just added such // as esp = esp - 4 // Remove all substitutions that might alias with it // (trivial implementation of memory aliasing, always yes) for (Iterator<Map.Entry<Writable, SubstitutionElement>> iter = post.aVarVal.entrySet().iterator(); iter.hasNext();) { Map.Entry<Writable, SubstitutionElement> e = iter.next(); if (e.getKey() instanceof RTLMemoryLocation || !e.getValue().getExpression().getUsedMemoryLocations().isEmpty()) { iter.remove(); } } if (post.aVarVal.isEmpty()) return TOP; if (post.equals(SubstitutionState.this)) return SubstitutionState.this; //logger.info("Post: " + post); return post; } @Override public SubstitutionState visit(RTLAlloc stmt) { return clobber(stmt.getPointer()); } @Override public SubstitutionState visit(RTLUnknownProcedureCall stmt) { // Remove all substitutions return TOP; } @Override public SubstitutionState visit(RTLHavoc stmt) { return clobber(stmt.getVariable()); } }); } @Override public boolean isTop() { return this == TOP; } @Override public boolean isBot() { return this == BOT; } private void setValue(Writable w, SubstitutionElement v) { if (v.isTop()) { aVarVal.remove(w); } else { aVarVal.put(w, v); } } public SubstitutionElement getValue(Writable v) { if (isTop()) return SubstitutionElement.TOP; if (isBot()) return SubstitutionElement.BOT; if (aVarVal.containsKey(v)) return aVarVal.get(v); else return SubstitutionElement.TOP; } /* * @see org.jakstab.analysis.AbstractState#join(org.jakstab.analysis.AbstractState) */ @Override public SubstitutionState join(LatticeElement l) { SubstitutionState other = (SubstitutionState)l; if (isTop() || other.isBot()) return this; if (isBot() || other.isTop()) return other; SubstitutionState result = new SubstitutionState(); // Join variable valuations for (Map.Entry<Writable,SubstitutionElement> entry : aVarVal.entrySet()) { Writable w = entry.getKey(); SubstitutionElement v = entry.getValue(); result.setValue(w, v.join(other.getValue(w))); } if (result.aVarVal.isEmpty()) return TOP; return result; } @Override public boolean lessOrEqual(LatticeElement l) { SubstitutionState other = (SubstitutionState)l; if (other.isTop() || isBot()) return true; if (isTop() || other.isBot()) return false; // Check for every element in "other" if its value in "this" is less or equal // than the value in "other". The elements not stored in the valuation maps // of "other" are implicitly TOP and thus every value is less or equal than them. for (Map.Entry<Writable,SubstitutionElement> entry : other.aVarVal.entrySet()) { Writable w = entry.getKey(); SubstitutionElement v = entry.getValue(); if (!getValue(w).lessOrEqual(v)) { //logger.info(w + ": " + getValue(w) + " is not less or equal to " + v); return false; } } return true; } @Override public String getIdentifier() { return Long.toString(stateId); } private boolean containsNondet(RTLExpression rhs) { return rhs.accept(new ExpressionVisitor<Boolean>() { @Override public Boolean visit(RTLBitRange e) { return e.getFirstBitIndex().accept(this) || e.getLastBitIndex().accept(this) || e.getOperand().accept(this); } @Override public Boolean visit(RTLConditionalExpression e) { return e.getCondition().accept(this) || e.getTrueExpression().accept(this) || e.getFalseExpression().accept(this); } @Override public Boolean visit(RTLMemoryLocation e) { return e.getAddress().accept(this); } @Override public Boolean visit(RTLNondet e) { return true; } @Override public Boolean visit(RTLNumber e) { return false; } @Override public Boolean visit(RTLOperation e) { for (RTLExpression op : e.getOperands()) { if (op.accept(this)) return true; } return false; } @Override public Boolean visit(RTLSpecialExpression e) { for (RTLExpression op : e.getOperands()) { if (op.accept(this)) return true; } return false; } @Override public Boolean visit(RTLVariable e) { return false; } }); } @Override public String toString() { if (isTop()) return Characters.TOP; else if (isBot()) return Characters.BOT; else return stateId + ": " + aVarVal.toString(); } @Override public Set<Tuple<RTLNumber>> projectionFromConcretization( RTLExpression... expressions) { Tuple<Set<RTLNumber>> cValues = new Tuple<Set<RTLNumber>>(expressions.length); for (int i=0; i<expressions.length; i++) { SubstitutionElement aValue = abstractEval(expressions[i]); cValues.set(i, aValue.concretize()); } return Sets.crossProduct(cValues); } @Override public Location getLocation() { throw new UnsupportedOperationException(this.getClass().getSimpleName() + " does not contain location information."); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((aVarVal == null) ? 0 : aVarVal.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; SubstitutionState other = (SubstitutionState) obj; if (other.isTop()) return this.isTop(); if (other.isBot()) return this.isBot(); if (aVarVal == null) { if (other.aVarVal != null) return false; } else if (!aVarVal.equals(other.aVarVal)) return false; return true; } }