/** * */ package bixie.checker.inconsistency_checker; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import bixie.checker.report.Report; import bixie.checker.transition_relation.TransitionRelation; import bixie.prover.Prover; import bixie.prover.ProverExpr; import bixie.prover.ProverResult; import bixie.prover.princess.PrincessProver; import boogie.controlflow.AbstractControlFlowFactory; import boogie.controlflow.BasicBlock; import boogie.controlflow.CfgProcedure; /** * @author schaef * * Inconsistent code detection algorithm based on greedy Cfg covering. * It uses the Cfg-theory plugin of Princess. * * The algorithm is described in the papers: - Infeasible code detection * (VSTTE'12) - A theory for control-flow graph exploration (ATVA'13) * */ public class GreedyCfgChecker extends AbstractChecker { /** * @param cff * @param p */ public GreedyCfgChecker(AbstractControlFlowFactory cff, CfgProcedure p) { super(cff, p); } /* * (non-Javadoc) * * @see bixie.checker.checker.AbstractChecker#checkSat(bixie.prover.Prover, * boogie.controlflow.AbstractControlFlowFactory, * boogie.controlflow.CfgProcedure) */ @Override public Report runAnalysis(Prover prover) { TransitionRelation tr = new TransitionRelation(this.procedure, this.cff, prover); // Statistics.HACK_effectualSetSize = tr.getEffectualSet().size(); LinkedHashMap<ProverExpr, ProverExpr> ineffFlags = new LinkedHashMap<ProverExpr, ProverExpr>(); for (BasicBlock block : tr.getEffectualSet()) { ProverExpr v = tr.getReachabilityVariables().get(block); ineffFlags.put(v, prover.mkVariable("" + v + "_flag", prover.getBooleanType())); } /* * Assert the transition relation of the procedure. */ prover.push(); this.pushTransitionRelation(prover, tr); prover.addAssertion(tr.getEnsures()); // construct the inverted reachabilityVariables which is used later // to keep track of what has been covered so far. Map<ProverExpr, BasicBlock> blocksToCover = createdInvertedReachabilityVariableMap( tr, new HashSet<BasicBlock>(tr.getReachabilityVariables() .keySet())); /* * ===== main algorithm ==== Two steps: In the first step, push the * assertion flag and check which blocks have feasible executions. Then * pop the flag to disable all assertions and check what blocks can now * be reached. * * Step 1: */ prover.push(); prover.addAssertion(prover.mkNot(tr.assertionFlag)); HashSet<BasicBlock> coveredBlocks = new HashSet<BasicBlock>(); coveredBlocks.addAll(coverBlocks(blocksToCover, tr, ineffFlags)); // coverBlocks returns the set of all feasible blocks. this.feasibleBlocks = new HashSet<BasicBlock>(coveredBlocks); /* * Step 2: Pop the tr.assertionFlag. An re-run coverBlocks to cover * everything that has a feasible execution if assertions are ignored. */ prover.pop(); coveredBlocks.addAll(coverBlocks(blocksToCover, tr, ineffFlags)); /* Pop the transition relation. */ prover.pop(); /* * ===== End of the main algorithm ==== everything that was not covered * in either of the iterations is clearly unreachable. */ HashSet<BasicBlock> unreachable = new HashSet<BasicBlock>( blocksToCover.values()); /* * All blocks that are covered in the second round - that is, the blocks * that are in coveredBlocks but not in feasibleBlocks - are potentially * dangerous, because their inconsistency contains an assertion. */ HashSet<BasicBlock> dangerous = new HashSet<BasicBlock>(coveredBlocks); dangerous.removeAll(this.feasibleBlocks); Report report = new Report(tr); report.reportInconsistentCode(0, dangerous); report.reportInconsistentCode(1, unreachable); return report; } /** * Tries to cover elements in blocks by covering all blocks in the effectual * set of the CFG using Princess' cfg-theory. The map ineffFlags contains * one helper variable for each block in the effectual set. * * @param blocks * Map from SMT variables to BasicBlocks. * @param tr * Transition relation of the analyzed procedure. * @param ineffFlags * Map from SMT variables in blocks to helper variables for * Princess. * @return The set of blocks that could be covered. */ protected Set<BasicBlock> coverBlocks(Map<ProverExpr, BasicBlock> blocks, TransitionRelation tr, LinkedHashMap<ProverExpr, ProverExpr> ineffFlags) { Set<BasicBlock> coveredBlocks = new HashSet<BasicBlock>(); int threshold = ineffFlags.size(); // hint for the greedy cover algorithm about // how many blocks could be covered in one query. if (threshold > 1) threshold = threshold / 2; while (threshold >= 1 && !ineffFlags.isEmpty()) { prover.push(); coveredBlocks.addAll(coverBlocksWithThreshold(blocks, tr, ineffFlags, threshold)); prover.pop(); if (threshold == 1 || ineffFlags.isEmpty()) break; do { threshold = (int) Math.ceil((double) threshold / 2.0); } while (threshold > ineffFlags.size()); } return coveredBlocks; } /** * Sub-step of coverBlocks. Finds all paths that contain at least * 'threshold' previously uncovered blocks. Setting the threshold is more * efficient looking for arbitrary new paths. * * @param blocks * Map from SMT variables to BasicBlocks. * @param tr * Transition relation of the analyzed procedure. * @param ineffFlags * Map from SMT variables in blocks to helper variables for * Princess. * @param threshold * lower bound for the number of new blocks that have to be * covered per path. * @param timeLimit * the time limit for the prover. If the limit is reached, the * analysis stops and returns the current set of covered blocks. * If timeLimit is 0, the solver is not timed out. * @return The set of blocks that could be covered for the given threshold. */ protected Set<BasicBlock> coverBlocksWithThreshold( Map<ProverExpr, BasicBlock> blocks, TransitionRelation tr, LinkedHashMap<ProverExpr, ProverExpr> ineffFlags, int threshold) { // setup the CFG module LinkedList<ProverExpr> remainingBlockVars = new LinkedList<ProverExpr>(); LinkedList<ProverExpr> remainingIneffFlags = new LinkedList<ProverExpr>(); for (Entry<ProverExpr, ProverExpr> entry : ineffFlags.entrySet()) { remainingBlockVars.add(entry.getKey()); remainingIneffFlags.add(entry.getValue()); } ((PrincessProver) prover).setupCFGPlugin(tr.getProverDAG(), remainingBlockVars, remainingIneffFlags, threshold); Set<BasicBlock> coveredBlocks = new HashSet<BasicBlock>(); ProverResult res = prover.checkSat(true); while (res == ProverResult.Sat) { LinkedList<ProverExpr> trueInModel = new LinkedList<ProverExpr>(); LinkedList<ProverExpr> flagsToAssert = new LinkedList<ProverExpr>(); for (Entry<ProverExpr, BasicBlock> entry : blocks.entrySet()) { final ProverExpr pe = entry.getKey(); if (prover.evaluate(pe).getBooleanLiteralValue()) { trueInModel.add(pe); ProverExpr flag = ineffFlags.get(pe); if (flag != null) { flagsToAssert.add(flag); } ineffFlags.remove(pe); } } for (ProverExpr e : trueInModel) { coveredBlocks.add(blocks.get(e)); blocks.remove(e); } prover.addAssertion(prover.mkAnd(flagsToAssert .toArray(new ProverExpr[flagsToAssert.size()]))); res = prover.checkSat(true); } return coveredBlocks; } }