/* * CallStackState.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.callstack; import java.util.*; import org.jakstab.Program; import org.jakstab.analysis.*; import org.jakstab.asm.*; import org.jakstab.cfa.Location; import org.jakstab.cfa.RTLLabel; import org.jakstab.cfa.StateTransformer; import org.jakstab.rtl.expressions.*; import org.jakstab.rtl.statements.*; import org.jakstab.util.Logger; import org.jakstab.util.Tuple; /** * @author Johannes Kinder */ public class CallStackState implements AbstractState { private static final Logger logger = Logger.getLogger(CallStackState.class); public static CallStackState TOP = new CallStackState(null); public static CallStackState BOT = new CallStackState(null); private final Deque<RTLLabel> callStack; public CallStackState() { this(new LinkedList<RTLLabel>()); } public CallStackState(Deque<RTLLabel> callStack) { this.callStack = callStack; } public AbstractState abstractPost(StateTransformer transformer, Precision precision) { if (isBot()) return BOT; if (isTop()) return TOP; final RTLStatement statement = (RTLStatement)transformer; return statement.accept(new DefaultStatementVisitor<CallStackState>() { @Override protected CallStackState visitDefault(RTLStatement stmt) { return CallStackState.this; } @Override public CallStackState visit(RTLAssume stmt) { Instruction instr = Program.getProgram().getAssemblyMap().get(stmt.getAddress()); Deque<RTLLabel> postStack; RTLGoto gotoStmt = stmt.getSource(); long addressValue = stmt.getAddress().getValue(); // in the stub area there are only returns (but no real asm instructions) // in the prologue there is only a single call // Return if (gotoStmt.getType() == RTLGoto.Type.RETURN) { postStack = new LinkedList<RTLLabel>(callStack); if (postStack.isEmpty()) { logger.warn("Return instruction on empty call stack!"); } else { RTLLabel target = postStack.pop(); logger.debug("Call stack: Return to " + target + ". Remaining stack " + postStack); } } // Prologue Call else if (Program.getProgram().getHarness().contains(stmt.getAddress())) { postStack = new LinkedList<RTLLabel>(callStack); postStack.push(new RTLLabel(Program.getProgram().getHarness().getFallthroughAddress(stmt.getAddress()))); } // Call else if (gotoStmt.getType() == RTLGoto.Type.CALL) { RTLLabel returnLabel; if (instr == null) { // Happens in import stubs containing a call logger.info("No instruction at address " + stmt.getLabel()); returnLabel = gotoStmt.getNextLabel(); } else { returnLabel = new RTLLabel(new AbsoluteAddress(addressValue + instr.getSize())); } postStack = new LinkedList<RTLLabel>(); for (Iterator<RTLLabel> iter = callStack.descendingIterator(); iter.hasNext();) { RTLLabel exRetLoc = iter.next(); if (exRetLoc.equals(returnLabel)) { logger.verbose("Recursion detected in call at " + stmt.getAddress()); break; } else { postStack.push(exRetLoc); } } logger.debug("Call stack: Pushing " + returnLabel); postStack.push(returnLabel); } // Something else else { return CallStackState.this; } return new CallStackState(postStack); } }); } @Override public String getIdentifier() { return Integer.toString(hashCode()); } @Override public Location getLocation() { throw new UnsupportedOperationException(this.getClass().getSimpleName() + " does not contain location information."); } @Override public AbstractState join(LatticeElement l) { CallStackState other = (CallStackState)l; if (this.isBot()) return other; if (other.isBot() || this.equals(other)) return this; return TOP; } @Override public Set<Tuple<RTLNumber>> projectionFromConcretization( RTLExpression... expressions) { if (!isBot() && !isTop() && expressions.length == 2 && expressions[0].equals(ExpressionFactory.TRUE) && expressions[1].equals(Program.getProgram().getArchitecture().returnAddressVariable())) { if (callStack.isEmpty()) { logger.error("Trying to read return target from empty callstack!"); return null; } logger.debug("Concretizing callstack element: " + callStack.peek()); return Collections.singleton(Tuple.create( ExpressionFactory.TRUE, ExpressionFactory.createNumber(callStack.peek().getAddress().getValue(), 32)) ); } else { Tuple<RTLNumber> result = new Tuple<RTLNumber>(expressions.length); for (int i=0; i<expressions.length; i++) { result.set(i, null); } return Collections.singleton(result); } } @Override public boolean isBot() { return this == BOT; } @Override public boolean isTop() { return this == TOP; } @Override public boolean lessOrEqual(LatticeElement l) { CallStackState other = (CallStackState)l; if (this.isBot() || other.isTop()) return true; if (other.equals(this)) return true; else return false; } @Override public String toString() { return "Call stack: " + callStack; } @Override public int hashCode() { if (isTop()) return 842192; if (isBot()) return 189487; return callStack.hashCode(); } @Override public boolean equals(Object obj) { if (this == obj) return true; CallStackState other = (CallStackState)obj; if (callStack == null) { // callStack is only null for singletons TOP and BOT assert isTop() || isBot(); return false; } return callStack.equals(other.callStack); } }