void GetSufficientConditions(CheckerPropagate *propagate, Bit *safe_bit,
                             Vector<CheckerPropagate*> *propagate_list)
{
  CheckerState *state = propagate->m_frame->State();
  Solver *solver = state->GetSolver();

  if (!solver->IsSatisfiable())
    return;

  SufficientTester tester(propagate, propagate_list);

  if (tester.verbose)
    logout << "SUFFICIENT: " << propagate->m_frame
           << ": Finding candidates: " << safe_bit << endl;

  // get any potential sufficient conditions from the frame's impliciations.
  Vector<Bit*> imply_list;
  GetImplySufficient(propagate->m_frame, &imply_list);

  // try to follow possible equalities to get a sufficient condition.
  GetEqualitySufficient(&tester, safe_bit, imply_list);

  for (size_t ind = 0; ind < imply_list.Size(); ind++)
    tester.TestBit(imply_list[ind]);

  // try to mark the bit itself as a sufficient condition.
  tester.TestBit(safe_bit);
}
Example #2
0
// returns whether the error condition is satisfiable within frame.
bool TestErrorSatisfiable(CheckerState *state, CheckerFrame *frame, Bit *bit)
{
  BlockMemory *mcfg = frame->Memory();
  Solver *solver = state->GetSolver();

  if (!solver->IsSatisfiable()) {
    if (checker_verbose.IsSpecified())
      logout << "CHECK: " << frame << ": Guard unsatisfiable: " << bit
             << " [" << bit->Hash() << "]" << endl;
    return false;
  }

  state->PushContext();
  state->AssertBaseBits();

  if (!solver->IsSatisfiable()) {
    if (checker_verbose.IsSpecified())
      logout << "CHECK: " << frame << ": Error unsatisfiable: " << bit
             << " [" << bit->Hash() << "]" << endl;
    state->PopContext();
    return false;
  }

  if (!frame->m_checked_assertions) {
    frame->m_checked_assertions = true;

    // check to see if the error is contradicted by previous assertions
    // in this frame. assert the previous assertions, but don't keep
    // them around past this function to avoid polluting the solver
    // with worthless extra checks.

    BlockSummary *sum = GetBlockSummary(mcfg->GetId());

    const Vector<AssertInfo> *asserts = sum->GetAsserts();
    size_t assert_count = VectorSize<AssertInfo>(asserts);

    for (size_t ind = 0; ind < assert_count; ind++) {
      const AssertInfo &info = asserts->At(ind);

      // only use the same kind of assertion to check for redundancy.
      if (info.kind != state->GetAssertKind())
        continue;

      if (info.cls != ASC_Check)
        continue;

      if (info.point < frame->EndPoint()) {
        // get the asserted condition relative to block entry.

        Bit *assert_value;
        mcfg->TranslateBit(TRK_Point, info.point, info.bit, &assert_value);
        assert_value->MoveRef(&assert_value, NULL);

        Bit *point_guard = mcfg->GetGuard(info.point);
        point_guard->IncRef();

        Bit *imply_assert =
          Bit::MakeImply(point_guard, assert_value);

        solver->AddConstraint(frame->Id(), imply_assert);
      }
    }

    sum->DecRef();

    if (!solver->IsSatisfiable()) {
      if (checker_verbose.IsSpecified())
        logout << "CHECK: " << frame
               << ": Unsatisfiable from assertions" << endl;

      state->PopContext();
      return false;
    }
  }

  state->PopContext();
  return true;
}
Example #3
0
// check propagation for each point bit in the specified frame. this is called
// both for the initial and intermediate checks of the assertion. assert_safe
// indicates that this is an initial check or an intermediate check of a heap
// invariant, and should be marked as a base bit/frame in the state.
bool CheckFrameList(CheckerState *state, CheckerFrame *frame,
                    PPoint point, bool allow_point, bool assert_safe,
                    Bit *base_bit, const GuardBitVector &point_list)
{
  // check if we are ignoring this function outright.
  BlockId *id = frame->CFG()->GetId();
  if (id->Kind() != B_Initializer && IgnoreFunction(id->BaseVar())) {
    if (checker_verbose.IsSpecified())
      logout << "CHECK: " << frame << ": Ignoring function" << endl;
    return false;
  }

  Solver *solver = state->GetSolver();

  if (!solver->IsSatisfiable()) {
    if (checker_verbose.IsSpecified())
      logout << "CHECK: " << frame << ": List unsatisfiable" << endl;
    return false;
  }

  for (size_t ind = 0; ind < point_list.Size(); ind++) {
    const GuardBit &gb = point_list[ind];

    state->PushContext();

    // the guard for the paths this safe bit takes are an extra assumed bit.
    frame->PushAssumedBit(gb.guard);

    // add side conditions and pending information from the bit.
    solver->AddSideConditions(frame->Id(), gb.bit);

    if (assert_safe)
      state->PushBaseBit(gb.bit, frame);

    if (TestErrorSatisfiable(state, frame, gb.bit)) {
      // error is feasible along these paths, construct a propagation
      // for the safe bit and continue exploration.
      CheckerPropagate propagate(frame, point, allow_point);
      propagate.m_id = state->GetPropagateId();

      propagate.FindTest(base_bit, gb.bit);

      state->m_stack.PushBack(&propagate);

      // check the frame against this propagation.
      if (CheckFrame(state, frame, &propagate))
        return true;

      // check if there was a soft timeout while we were finished
      // exploring this path. when the timeout occurs all satisfiable
      // queries become false so we will end up here.
      if (TimerAlarm::ActiveExpired()) {
        logout << "Timeout: ";
        PrintTime(TimerAlarm::ActiveElapsed());
        logout << endl;

        state->SetReport(RK_Timeout);
        return true;
      }

      state->m_stack.PopBack();
    }

    // no error along these paths, unwind the changes we made beforehand.
    if (assert_safe)
      state->PopBaseBit();
    frame->PopAssumedBit();
    state->PopContext();
  }

  return false;
}
// mark the trivial/redundant assertions in the specified list.
void MarkRedundantAssertions(BlockMemory *mcfg, Vector<AssertInfo> &asserts)
{
  BlockCFG *cfg = mcfg->GetCFG();

  // assertions are marked redundant in two passes:
  // 1. for each path reaching the assertion, the validity of the assertion is
  //    implied by one or more prior or future assertions.
  //    this pass also picks up assertions which trivially hold, where the
  //    assertion is valid due to the conditions along the paths themselves.
  // 2. there is an isomorphic assertion within an inner loop. it is
  //    sufficient to check just the inner assertion.

  // implication works differently for invariants vs. other assertions,
  // since the invariant condition will be asserted at block exit.

  // for regular assertions, an bit is redundant if (guard ==> bit)
  // is implied by the (oguard ==> obit) for other assertions:

  // VALID((oguard ==> obit) ==> (guard ==> bit))
  // !SAT(!((oguard ==> obit) ==> (guard ==> bit)))
  // !SAT(!(!(oguard ==> obit) || (guard ==> bit)))
  // !SAT((oguard ==> obit) && !(guard ==> bit))
  // !SAT((oguard ==> obit) && !(!guard || bit))
  // !SAT((oguard ==> obit) && guard && !bit)

  // for invariants, a bit is redundant if guard implies the oguard
  // for other invariants with the same asserted bit:

  // VALID(guard ==> oguard)
  // !SAT(!(guard ==> oguard))
  // !SAT(!(!guard || oguard))
  // !SAT(guard && !oguard)

  Solver *solver = new Solver("redundant");

  for (size_t ind = 0; ind < asserts.Size(); ind++) {
    AssertInfo &info = asserts[ind];
    solver->PushContext();

    Assert(info.cls == ASC_Check);

    // assert guard.
    Bit *guard = mcfg->GetGuard(info.point);
    solver->AddAssert(0, guard);

    if (info.kind != ASK_Invariant) {
      // assert !bit.
      Bit *not_bit = Bit::MakeNot(info.bit);

      Bit *result_not_bit;
      mcfg->TranslateBit(TRK_Point, info.point, not_bit, &result_not_bit);
      solver->AddAssert(0, result_not_bit);
    }

    if (!solver->IsSatisfiable()) {
      // the assert is tautological or is proved by the guard, thus trivial.
      info.cls = ASC_Trivial;
      solver->PopContext();
      continue;
    }

    // assert the remaining assertions in the summary hold.
    for (size_t aind = 0; aind < asserts.Size(); aind++) {
      const AssertInfo &oinfo = asserts[aind];

      // skip this assertion itself.
      if (info.point == oinfo.point && info.bit == oinfo.bit)
        continue;

      // skip assertions already marked as trivial or redundant.
      if (oinfo.cls != ASC_Check)
        continue;

      // skip assertions for a different kind than the original.
      // this avoids interference between the different kinds of assertions,
      // though it is unlikely to affect whether we actually mark an
      // assert as redundant.
      if (oinfo.kind != info.kind)
        continue;

      Bit *oguard = mcfg->GetGuard(oinfo.point);

      if (info.kind == ASK_Invariant) {
        // only compare with other invariants for the same bit.
        if (oinfo.bit != info.bit)
          continue;

        // assert !oguard
        Bit *not_oguard = Bit::MakeNot(oguard);
        solver->AddAssert(0, not_oguard);
      }
      else {
        // assert (oguard ==> obit).
        Bit *result_obit;
        mcfg->TranslateBit(TRK_Point, oinfo.point, oinfo.bit, &result_obit);

        Bit *imply_bit = Bit::MakeImply(oguard, result_obit);
        solver->AddAssert(0, imply_bit);
      }
    }

    if (!solver->IsSatisfiable()) {
      // the assert is implied by the remaining assertions, thus redundant.
      info.cls = ASC_Redundant;
    }

    solver->PopContext();
  }

  solver->Clear();
  delete solver;

  for (size_t ind = 0; ind < cfg->GetEdgeCount(); ind++) {
    PEdgeLoop *loop_edge = cfg->GetEdge(ind)->IfLoop();
    if (!loop_edge)
      continue;

    for (size_t aind = 0; aind < asserts.Size(); aind++) {
      AssertInfo &info = asserts[aind];

      if (info.cls != ASC_Check)
        continue;

      if (cfg->IsLoopIsomorphic(info.point)) {
        // this assertion's point is isomorphic to a point within the
        // loop body, so there will be an equivalent loop assertion.
        info.cls = ASC_Redundant;
      }
    }
  }
}
  // returns true if bit is a useful condition for testing as sufficient
  // (no loop-modified terms, preserves reachability of assert, etc.)
  // regardless of whether it is actually sufficient.
  bool TestBit(Bit *bit)
  {
    Vector<Bit*> &tested_list = propagate->m_sufficient_tested_list;
    Vector<Bit*> &possible_list = propagate->m_sufficient_possible_list;
    Vector<Bit*> &sufficient_list = propagate->m_sufficient_list;

    Solver *solver = state->GetSolver();

    if (tested_list.Contains(bit)) {
      if (possible_list.Contains(bit))
        return true;
      return false;
    }

    if (verbose)
      logout << "SUFFICIENT: " << frame
             << ": Testing " << bit << " [" << bit->Hash() << "]" << endl;

    tested_list.PushBack(bit);

    // don't test for sufficient conditions if a timeout has occurred.
    if (TimerAlarm::ActiveExpired()) {
      if (verbose)
        logout << "SUFFICIENT: " << frame << ": Alarm expired" << endl;
      return false;
    }

    // check that the sufficient condition does not render the point
    // of the assertion unreachable: the solver is still satisfiable after
    // asserting the sufficient condition. this also takes care of
    // unsatisfiable sufficient conditions.

    state->PushContext();

    // ignore bits we can't do any propagation from.

    CheckerPropagate *test_propagate =
      new CheckerPropagate(frame, propagate->m_point,
                           propagate->m_allow_point);
    test_propagate->SetTest(bit);

    // propagations can trigger new solver side conditions. TODO: should figure
    // out what's going on here.
    if (!solver->IsSatisfiable()) {
      state->PopContext();
      return false;
    }

    if (test_propagate->m_where->IsNone()) {
      if (verbose)
        logout << "SUFFICIENT: " << frame << ": Failed propagate: "
               << test_propagate->m_where << endl;
      state->PopContext();
      delete test_propagate;
      return false;
    }

    // assert the tested sufficient holds in the frame.
    frame->AddAssert(bit);

    if (!solver->IsSatisfiable()) {
      // the sufficient condition rendered the assertion point unreachable.
      if (verbose)
        logout << "SUFFICIENT: " << frame
               << ": Renders point unreachable" << endl;

      state->PopContext();
      delete test_propagate;
      return false;
    }

    // this is a good potential sufficient condition, remember it.
    possible_list.PushBack(bit);

    // check whether the bit is actually a sufficient condition.
    // just assert the original negated safe bit, and if it is unsatisfiable
    // it cannot occur under this sufficient condition.

    state->AssertBaseBits();

    bool satisfiable = solver->IsSatisfiable();

    if (verbose) {
      if (satisfiable) {
        logout << "SUFFICIENT: " << frame
               << ": Not a sufficient condition:" << endl;
        solver->PinAssign();
        solver->PrintRawAssignment();
        solver->UnpinAssign();
      }
      else {
        logout << "SUFFICIENT: " << frame << ": Success!" << endl;
      }
    }

    if (!satisfiable) {
      sufficient_list.PushBack(bit);
      propagate_list->PushBack(test_propagate);
    }
    else {
      delete test_propagate;
    }

    state->PopContext();
    return true;
  }