Ejemplo n.º 1
0
// check a single caller to the specified frame at site 'where'.
// return value is whether an error path was found.
bool CheckSingleCaller(CheckerState *state, CheckerFrame *frame,
                       WherePrecondition *precondition, BlockPPoint where)
{
  CheckerFrame *caller_frame = frame->GetCallerFrame();
  CheckerFrame *loop_parent_frame = frame->GetLoopParentFrame();

  bool has_caller = (caller_frame != NULL);
  bool has_loop_parent = (loop_parent_frame != NULL);

  // whether we should keep the caller and callee frames disconnected
  // (unless they already are connected). this is done during loop propagation
  // if there is either a sufficient condition on the propagation or
  // no propagation at all (connecting frames for heap propagation),
  // ensuring we do not have to unroll the loop ad infinitum.
  bool keep_disconnected = false;

  if (frame->Kind() == B_Loop) {
    if (!precondition || precondition->IsIgnoreUnroll())
      keep_disconnected = true;
  }

  // special handling for loop unroll cases.
  if (frame->Kind() == B_Loop && frame->Memory()->GetId() == where.id) {

    // filter out unrolled loop iterations if the caller is disconnected.
    if (keep_disconnected) {
      if (checker_verbose.IsSpecified())
        logout << "CHECK: " << frame << ": Skipping loop unroll" << endl;
      return false;
    }
  }

  // whether we had to create the caller frame.
  bool created_frame = false;

  // whether we need to connect this frame to the caller frame.
  bool connect_frame = false;

  if (has_caller) {
    // we should already have ensured this is a compatible caller.
    Assert(frame->GetCaller() == where);
    Assert(caller_frame);
  }
  else if (where == frame->GetLoopParent()) {
    // connecting to the loop parent of this frame.
    caller_frame = loop_parent_frame;
    Assert(caller_frame);

    connect_frame = true;
  }
  else {
    // no caller to connect to, we will have to make one.
    caller_frame = state->MakeFrame(where.id);

    if (caller_frame == NULL) {
      logout << "WARNING: Missing memory: '" << where.id << "'" << endl;
      return false;
    }

    BlockCFG *cfg = caller_frame->CFG();

    // check for call sites at loop isomorphic points. we will also see the
    // corresponding call site within the loop body, and handling that site
    // will also cover this one.
    if (cfg->IsLoopIsomorphic(where.point)) {
      if (checker_verbose.IsSpecified())
        logout << "CHECK: " << frame << ": Caller is loop isomorphic" << endl;
      state->DeleteFrame(caller_frame);
      return false;
    }

    // for incremental analysis, make sure the call edge uses the current
    // version of the caller CFG. if there is a version mismatch then this
    // call site is for an older version of the CFG; if the current version
    // of the CFG still calls the function then there will be a separate
    // call edge for the updated call site.
    if (cfg->GetVersion() != where.version) {
      if (checker_verbose.IsSpecified())
        logout << "CHECK: " << frame << ": Caller is an older version" << endl;
      state->DeleteFrame(caller_frame);
      return false;
    }

    caller_frame->AssertPointGuard(where.point, true);

    // check for a recursive function/loop call. if there is an existing
    // propagate for this function then just assert its safe/sufficient
    // bit within this caller_frame. TODO: this is unsound as we are not
    // accounting for the *other* parameters to the function which might
    // differ along the recursive calls.
    if (precondition && frame->Memory() == caller_frame->Memory()) {
      Bit *entry_bit = precondition->GetBit();
      caller_frame->AddAssert(entry_bit);
    }

    connect_frame = true;
    created_frame = true;
  }

  // if we are skipping from a loop iteration to the parent, make sure there
  // is a loop child relationship between the two.
  if (keep_disconnected) {
    Assert(frame->Kind() == B_Loop);
    Assert(caller_frame->Memory() != frame->Memory());

    // connect the caller frame as the loop parent if there isn't already one.
    if (loop_parent_frame) {
      Assert(loop_parent_frame == caller_frame);
    }
    else {
      caller_frame->ConnectLoopChild(frame, where.point);
    }
  }

  if (keep_disconnected)
    connect_frame = false;

  if (connect_frame) {
    caller_frame->ConnectCallee(frame, where.point, true);
    frame->PushCallerLoopParent(caller_frame);
  }

  // add any equalities we can between the frame and its caller.
  frame->ExpandPendingExps();

  if (precondition) {
    // get the safe bits for the caller frame.
    Bit *caller_base_bit;
    GuardBitVector caller_safe_list;
    precondition->GetCallerBits(caller_frame, where.point,
                                &caller_base_bit, &caller_safe_list);

    if (CheckFrameList(state, caller_frame, where.point, false, false,
                       caller_base_bit, caller_safe_list)) {
      caller_base_bit->DecRef();
      return true;
    }

    caller_base_bit->DecRef();
  }
  else {
    // continue the checker without any active propagation.
    if (CheckFrameSingle(state, caller_frame, where.point))
      return true;
  }

  if (!has_caller && frame->GetCaller().id != NULL)
    caller_frame->DisconnectCallee(frame, where.point);

  if (!has_loop_parent && frame->GetLoopParent().id != NULL)
    caller_frame->DisconnectLoopChild(frame, where.point);

  if (created_frame)
    state->DeleteFrame(caller_frame);

  return false;
}
Ejemplo n.º 2
0
// 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;
      }
    }
  }
}