/*
* IntervalAnalysis.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.intervals;
import java.util.*;
import org.jakstab.AnalysisProperties;
import org.jakstab.Program;
import org.jakstab.analysis.*;
import org.jakstab.analysis.explicit.BasedNumberElement;
import org.jakstab.analysis.explicit.BasedNumberValuation;
import org.jakstab.cfa.CFAEdge;
import org.jakstab.cfa.Location;
import org.jakstab.cfa.StateTransformer;
import org.jakstab.rtl.expressions.*;
import org.jakstab.rtl.statements.*;
import org.jakstab.util.*;
import org.jakstab.util.MapMap.EntryIterator;
/**
* A reduced interval congruence analysis with regioned memory. Inspired by
* Codesurfer's VSA domain.
*
* @author Johannes Kinder
*/
public class IntervalAnalysis implements ConfigurableProgramAnalysis {
public static void register(AnalysisProperties p) {
p.setShortHand('i');
p.setName("Interval analysis");
p.setDescription("Compute strided intervals with region information.");
p.setExplicit(true);
}
private static final Logger logger = Logger.getLogger(IntervalAnalysis.class);
private AbstractValueFactory<IntervalElement> valueFactory;
public IntervalAnalysis() {
valueFactory = new IntervalElementFactory();
}
@Override
public Precision initPrecision(Location location, StateTransformer transformer) {
return new IntervalPrecision();
}
/*
* @see org.jakstab.analysis.ConfigurableProgramAnalysis#initStartState(org.jakstab.cfa.Location)
*/
@Override
public AbstractState initStartState(Location location) {
//IntervalState init = new IntervalState();
/*init.setValue(Program.getProgram().getArchitecture().stackPointer(),
new IntervalElement(MemoryRegion.STACK, 0, 0, 0, 32));*/
//return init;
return new ValuationState(valueFactory);
}
/*
* @see org.jakstab.analysis.ConfigurableProgramAnalysis#merge(org.jakstab.analysis.AbstractState, org.jakstab.analysis.AbstractState)
*/
@Override
public AbstractState merge(AbstractState s1, AbstractState s2, Precision precision) {
// Widen s2 towards s1.
//return ((IntervalState)s2).widen((IntervalState)s1);
if (s2.isTop() || s1.isBot()) return s2;
if (s1.isTop()) return s1;
ValuationState current = (ValuationState)s2;
ValuationState towards = (ValuationState)s1;
ValuationState widenedState = new ValuationState(valueFactory);
// Widen variable valuations
for (Iterator<Map.Entry<RTLVariable,AbstractDomainElement>> entryIt = current.variableIterator(); entryIt.hasNext();) {
Map.Entry<RTLVariable,AbstractDomainElement> entry = entryIt.next();
RTLVariable var = entry.getKey();
IntervalElement v = (IntervalElement)entry.getValue();
widenedState.setVariableValue(var, v.widen((IntervalElement)towards.getVariableValue(var)));
}
// Widen memory
for (EntryIterator<MemoryRegion, Long, AbstractDomainElement> entryIt = current.storeIterator(); entryIt.hasEntry(); entryIt.next()) {
MemoryRegion region = entryIt.getLeftKey();
Long offset = entryIt.getRightKey();
IntervalElement v = (IntervalElement)entryIt.getValue();
int bitWidth = v.getBitWidth();
widenedState.setMemoryValue(region, offset, bitWidth,
v.widen((IntervalElement)towards.getMemoryValue(region, offset, bitWidth)));
}
return widenedState;
}
/*
* @see org.jakstab.analysis.ConfigurableProgramAnalysis#post(org.jakstab.analysis.AbstractState, org.jakstab.analysis.StateTransformer, org.jakstab.analysis.Precision)
*/
@Override
public Set<AbstractState> post(final AbstractState state, CFAEdge cfaEdge, Precision precision) {
final RTLStatement statement = (RTLStatement)cfaEdge.getTransformer();
final ValuationState iState = (ValuationState)state;
return Collections.singleton(statement.accept(new DefaultStatementVisitor<AbstractState>() {
@Override
protected AbstractState visitDefault(RTLStatement stmt) {
return state;
}
@Override
public AbstractState visit(RTLVariableAssignment stmt) {
ValuationState post = new ValuationState(iState);
Writable lhs = stmt.getLeftHandSide();
RTLExpression rhs = stmt.getRightHandSide();
AbstractDomainElement evaledRhs = iState.abstractEval(rhs);
// Check for stackpointer alignment assignments (workaround for gcc compiled files)
RTLVariable sp = Program.getProgram().getArchitecture().stackPointer();
if (lhs.equals(sp) && rhs instanceof RTLOperation) {
RTLOperation op = (RTLOperation)rhs;
if (op.getOperator().equals(Operator.AND) &&
op.getOperands()[0].equals(sp) &&
op.getOperands()[1] instanceof RTLNumber) {
evaledRhs = iState.getVariableValue(sp);
logger.warn("Ignoring stackpointer alignment at " + stmt.getAddress());
}
}
post.setVariableValue((RTLVariable)lhs, evaledRhs);
return post;
}
@Override
public AbstractState visit(RTLMemoryAssignment stmt) {
ValuationState post = new ValuationState(iState);
RTLMemoryLocation m = stmt.getLeftHandSide();
RTLExpression rhs = stmt.getRightHandSide();
AbstractDomainElement evaledRhs = iState.abstractEval(rhs);
AbstractDomainElement evaledAddress = iState.abstractEval(m.getAddress());
post.setMemoryValue(evaledAddress, m.getBitWidth(), evaledRhs);
return post;
}
@Override
public AbstractState visit(RTLAssume stmt) {
ValuationState post = new ValuationState(iState);
RTLExpression assumption = stmt.getAssumption();
// TODO: implement assume
if (assumption instanceof RTLOperation) {
RTLOperation op = (RTLOperation)assumption;
switch (op.getOperator()) {
case UNSIGNED_LESS_OR_EQUAL:
RTLExpression lhs = op.getOperands()[0];
RTLExpression rhs = op.getOperands()[1];
IntervalElement evaledLhs = (IntervalElement)iState.abstractEval(lhs);
IntervalElement evaledRhs = (IntervalElement)iState.abstractEval(rhs);
if (evaledRhs.getLeft() >= 0) {
IntervalElement uLessInt = new IntervalElement(
evaledRhs.getRegion(), 0,
evaledRhs.getRight(), 1, evaledLhs.getBitWidth()
);
// TODO: Implement meet for interval elements for optimal result
// uLessInt = uLessInt.meet(evaledLhs);
// if uLessInt.isBot() return Collections.emptySet();
// cheap but sound solution for now: only use new interval if it has less elements
if (uLessInt.size() < evaledLhs.size()) {
if (lhs instanceof RTLVariable) {
post.setVariableValue((RTLVariable)lhs, uLessInt);
} else if (lhs instanceof RTLMemoryLocation) {
RTLMemoryLocation m = (RTLMemoryLocation)lhs;
AbstractDomainElement evaledAddress = iState.abstractEval(m.getAddress());
post.setMemoryValue(evaledAddress, m.getBitWidth(), uLessInt);
}
}
}
break;
default: // nothing
}
}
return post;
}
@Override
public AbstractState visit(RTLAlloc stmt) {
ValuationState post = new ValuationState(iState);
Writable lhs = stmt.getPointer();
MemoryRegion newRegion;
if (stmt.getAllocationName() != null) {
newRegion = MemoryRegion.create(stmt.getAllocationName());
} else {
// TODO: Detect whether this allocation is unique to allow strong updates
newRegion = MemoryRegion.createAsSummary("alloc" + stmt.getLabel());
}
IntervalElement basePointer = new IntervalElement(newRegion,
ExpressionFactory.createNumber(0, 32));
if (lhs instanceof RTLVariable) {
post.setVariableValue((RTLVariable)lhs, basePointer);
} else {
RTLMemoryLocation m = (RTLMemoryLocation)lhs;
AbstractDomainElement evaledAddress = iState.abstractEval(m.getAddress());
post.setMemoryValue(evaledAddress, m.getBitWidth(), basePointer);
}
return post;
}
@Override
public AbstractState visit(RTLHavoc stmt) {
ValuationState post = new ValuationState(iState);
// Only create a single state with the havoc range, since this analysis
// is not path sensitive
post.setVariableValue(stmt.getVariable(),
//new IntervalElement(ExpressionFactory.getInstance().createNumber(0, stmt.getVariable().getBitWidth()),
//(RTLNumber)stmt.getMaximum()));
new IntervalElement(MemoryRegion.GLOBAL, 0, ((RTLNumber)stmt.getMaximum()).longValue(), 1,
stmt.getVariable().getBitWidth())
);
return post;
}
@Override
public AbstractState visit(RTLUnknownProcedureCall stmt) {
ValuationState post = new ValuationState(iState);
for (RTLVariable var : stmt.getDefinedVariables()) {
post.setVariableValue(var, IntervalElement.getTop(var.getBitWidth()));
}
post.setMemoryValue(IntervalElement.getTop(Program.getProgram().getArchitecture().getAddressBitWidth()),
32, IntervalElement.getTop(32));
return post;
}
}));
}
@Override
public AbstractState strengthen(AbstractState s, Iterable<AbstractState> otherStates,
CFAEdge cfaEdge, Precision precision) {
ValuationState state = (ValuationState)s;
ValuationState strengthenedState = null;
for (AbstractState t : otherStates) {
// TODO: Does not work correctly if BoundedAddressTracking returns more than
// one successor state.
if (t instanceof BasedNumberValuation) {
BasedNumberValuation exState = (BasedNumberValuation)t;
for (Map.Entry<RTLVariable, BasedNumberElement> entry :
exState.getVariableValuation()) {
RTLVariable var = entry.getKey();
BasedNumberElement exVal = entry.getValue();
if (exVal.isTop() || exVal.isNumberTop())
continue;
if (state.getVariableValue(var).isTop()) {
if (strengthenedState == null) {
strengthenedState = new ValuationState(state);
}
strengthenedState.setVariableValue(var,
new IntervalElement(exVal.getRegion(),
exVal.getNumber()));
//logger.debug("Strengthened state " + state.getIdentifier() +
// " by setting " + var + " to " + state.getValue(var));
}
}
}
}
return strengthenedState == null ? state : strengthenedState;
}
@Override
public Pair<AbstractState, Precision> prec(AbstractState s,
Precision precision, ReachedSet reached) {
return Pair.create(s, precision);
}
@Override
public boolean stop(AbstractState s, ReachedSet reached, Precision precision) {
return CPAOperators.stopJoin(s, reached, precision);
}
private RTLExpression addClause(RTLExpression formula, RTLExpression clause) {
if (formula != null) {
return ExpressionFactory.createAnd(formula, clause);
} else {
return clause;
}
}
public RTLExpression getStateFormula(ValuationState state) {
RTLExpression result = null;
for (Iterator<Map.Entry<RTLVariable,AbstractDomainElement>> entryIt = state.variableIterator(); entryIt.hasNext();) {
Map.Entry<RTLVariable,AbstractDomainElement> entry = entryIt.next();
RTLVariable var = entry.getKey();
IntervalElement interval = (IntervalElement)entry.getValue();
if (interval.size() == 1) {
result = addClause(result, ExpressionFactory.createEqual(
var, ExpressionFactory.createNumber(interval.getLeft(), var.getBitWidth())));
} else {
if (!interval.leftOpen()) {
result = addClause(result, ExpressionFactory.createLessOrEqual(
ExpressionFactory.createNumber(interval.getLeft(), var.getBitWidth()), var));
}
if (!interval.rightOpen()) {
result = addClause(result, ExpressionFactory.createLessOrEqual(
var, ExpressionFactory.createNumber(interval.getRight(), var.getBitWidth())));
}
}
}
if (result == null) {
result = ExpressionFactory.TRUE;
}
return result;
}
}