void WherePostcondition::PrintHook(OutStream &out) const { BlockId *id = m_frame->CFG()->GetId(); Variable *func_var = id->BaseVar(); PEdge *edge = m_frame->CFG()->GetSingleOutgoingEdge(m_point); if (edge->IsLoop()) { PEdgeLoop *nedge = edge->AsLoop(); out << nedge->GetLoopId()->Loop()->Value() << " " << func_var->GetName()->Value(); } else { PEdgeCall *nedge = edge->AsCall(); if (Variable *callee = nedge->GetDirectFunction()) { // direct call, just one hook function. out << "post " << callee->GetName()->Value(); } else { // indirect call, one hook function for each callee. CallEdgeSet *callees = CalleeCache.Lookup(func_var); bool found_callee = false; if (callees) { for (size_t eind = 0; eind < callees->GetEdgeCount(); eind++) { const CallEdge &edge = callees->GetEdge(eind); if (edge.where.id == id && edge.where.point == m_point) { if (found_callee) out << "$"; // add the separator found_callee = true; out << "post " << edge.callee->GetName()->Value(); } } } CalleeCache.Release(func_var); } } }
bool CheckFrame(CheckerState *state, CheckerFrame *frame, CheckerPropagate *propagate) { Assert(!state->GetReportKind()); BlockMemory *mcfg = frame->Memory(); BlockCFG *cfg = mcfg->GetCFG(); BlockId *id = cfg->GetId(); if (checker_verbose.IsSpecified()) { logout << "CHECK: " << frame << ": Entering " << id << endl; if (propagate) propagate->Print(); } Where *where = propagate ? propagate->m_where : NULL; // check if we should terminate the search at this point (with or without // generating a report). if (where && where->IsNone()) { WhereNone *nwhere = where->AsNone(); ReportKind kind = nwhere->GetReportKind(); if (kind == RK_None) { if (checker_verbose.IsSpecified()) logout << "CHECK: " << frame << ": Ignoring" << endl; return false; } else { if (checker_verbose.IsSpecified()) logout << "CHECK: " << frame << ": Propagation failed" << endl; state->SetReport(kind); return true; } } // check for other propagations on the stack with frames for the same block, // and block the recursion if we exceed the checker's depth. we assume that // if we're ever going to terminate in the presence of recursion, we will // do so quickly. if (propagate) { if (uint32_t depth = checker_depth.UIntValue()) { Vector<CheckerFrame*> recurse_frames; for (size_t ind = 0; ind < state->m_stack.Size(); ind++) { CheckerFrame *other_frame = state->m_stack[ind]->m_frame; if (other_frame != frame && other_frame->Memory() == mcfg && !recurse_frames.Contains(other_frame)) recurse_frames.PushBack(other_frame); } if (recurse_frames.Size() >= depth) { state->SetReport(RK_Recursion); return true; } } } // check if we are propagating into some callee. if (where && where->IsPostcondition()) { WherePostcondition *nwhere = where->AsPostcondition(); // expand the callee at the specified point. PPoint point = nwhere->GetPoint(); PEdge *edge = cfg->GetSingleOutgoingEdge(point); if (edge->IsLoop()) { // expanding data from a loop. first try the case that the loop // does not execute at all. if (checker_verbose.IsSpecified()) logout << "CHECK: " << frame << ": Trying to skip loop at " << point << endl; state->PushContext(); if (CheckSkipLoop(state, frame, point, nwhere)) return true; state->PopContext(); } if (BlockId *callee = edge->GetDirectCallee()) { // easy case, there is only a single callee. if (checker_verbose.IsSpecified()) logout << "CHECK: " << frame << ": Expanding single callee at " << point << ": " << callee << endl; state->PushContext(); if (CheckSingleCallee(state, frame, point, nwhere, callee, true)) return true; state->PopContext(); } else { // iterate through all the possible callees Variable *function = id->BaseVar(); CallEdgeSet *callees = CalleeCache.Lookup(function); Vector<Variable*> callee_vars; if (callees) { for (size_t eind = 0; eind < callees->GetEdgeCount(); eind++) { const CallEdge &edge = callees->GetEdge(eind); if (edge.where.id == id && edge.where.point == point) callee_vars.PushBack(edge.callee); } } SortVector<Variable*,Variable>(&callee_vars); for (size_t cind = 0; cind < callee_vars.Size(); cind++) { Variable *callee = callee_vars[cind]; if (checker_verbose.IsSpecified()) logout << "CHECK: " << frame << ": Expanding indirect callee at " << point << ": " << callee << endl; callee->IncRef(); BlockId *callee_id = BlockId::Make(B_Function, callee); state->PushContext(); if (CheckSingleCallee(state, frame, point, nwhere, callee_id, false)) { CalleeCache.Release(function); return true; } state->PopContext(); } if (callee_vars.Empty()) { if (checker_verbose.IsSpecified()) logout << "CHECK: " << frame << ": No callees to expand at " << point << endl; } CalleeCache.Release(function); } return false; } // any precondition we have to propagate up to the callers. WherePrecondition *precondition = NULL; if (where) precondition = where->IfPrecondition(); // whether we will be reconnecting to the caller without any // propagation information. bool reconnect_caller = false; if (precondition) { Bit *bit = precondition->GetBit(); WherePrecondition *dupe_precondition = new WherePrecondition(mcfg, bit); state->m_precondition_list.PushBack(dupe_precondition); } else { // we will propagate to the caller regardless if there is already a caller // hooked up or if we are inside a loop body. if (frame->GetCaller().id != NULL) reconnect_caller = true; if (frame->Kind() == B_Loop) reconnect_caller = true; } if (propagate && reconnect_caller) { // check to see if we are delaying any heap propagation. if (where->IsInvariant()) { Assert(state->m_delayed_propagate_heap == NULL); state->m_delayed_propagate_heap = propagate; } } else if (!precondition && !reconnect_caller) { // check to see if we are performing heap propagation. if (state->m_delayed_propagate_heap) { Assert(propagate == NULL); CheckerPropagate *heap_propagate = state->m_delayed_propagate_heap; state->m_delayed_propagate_heap = NULL; WhereInvariant *invariant = heap_propagate->m_where->AsInvariant(); if (CheckHeapWrites(state, frame, heap_propagate->m_frame, invariant)) return true; state->m_delayed_propagate_heap = heap_propagate; return false; } else if (where && where->IsInvariant()) { return CheckHeapWrites(state, frame, frame, where->AsInvariant()); } Assert(propagate); // don't need to expand the callers or anything else. // we can finally terminate propagation with an error report. if (checker_verbose.IsSpecified()) logout << "CHECK: " << frame << ": Nothing to expand, finishing" << endl; state->SetReport(RK_Finished); return true; } if (frame->GetCaller().id != NULL) { // just propagate to the existing caller. if (checker_verbose.IsSpecified()) logout << "CHECK: " << frame << ": Returning to caller" << endl; state->PushContext(); if (CheckSingleCaller(state, frame, precondition, frame->GetCaller())) return true; state->PopContext(); } else if (id->Kind() == B_Function) { // propagate to all callers to the function. Variable *function = id->BaseVar(); CallEdgeSet *callers = CallerCache.Lookup(function); Vector<BlockPPoint> caller_points; for (size_t eind = 0; callers && eind < callers->GetEdgeCount(); eind++) { const CallEdge &edge = callers->GetEdge(eind); Assert(edge.callee == function); caller_points.PushBack(edge.where); } SortVector<BlockPPoint,BlockPPoint>(&caller_points); for (size_t cind = 0; cind < caller_points.Size(); cind++) { BlockPPoint caller = caller_points[cind]; if (checker_verbose.IsSpecified()) logout << "CHECK: " << frame << ": Checking caller: " << caller << endl; state->PushContext(); if (CheckSingleCaller(state, frame, precondition, caller)) { CallerCache.Release(function); return true; } state->PopContext(); } if (caller_points.Empty()) { if (checker_verbose.IsSpecified()) logout << "CHECK: " << frame << ": No callers to expand" << endl; } CallerCache.Release(function); } else if (id->Kind() == B_Loop) { // check all possible callers of the loop. unroll an iteration before // checking the parents so that if we can't figure out a sufficient // condition for the loop we will stop exploration quickly. // unroll another iteration of the loop. if (checker_verbose.IsSpecified()) logout << "CHECK: " << frame << ": Unrolling loop iteration" << endl; state->PushContext(); BlockPPoint recursive_caller(id, cfg->GetExitPoint()); if (CheckSingleCaller(state, frame, precondition, recursive_caller)) return true; state->PopContext(); // check the parents which can initially invoke this loop. if (frame->GetLoopParent().id != NULL) { if (checker_verbose.IsSpecified()) logout << "CHECK: " << frame << ": Checking existing loop parent: " << frame->GetLoopParent() << endl; state->PushContext(); if (CheckSingleCaller(state, frame, precondition, frame->GetLoopParent())) return true; state->PopContext(); } else { for (size_t pind = 0; pind < cfg->GetLoopParentCount(); pind++) { BlockPPoint where = cfg->GetLoopParent(pind); if (checker_verbose.IsSpecified()) logout << "CHECK: " << frame << ": Checking loop parent: " << where << endl; state->PushContext(); if (CheckSingleCaller(state, frame, precondition, where)) return true; state->PopContext(); } } } else if (id->Kind() == B_Initializer) { // initializers don't have callers, can just ignore this. // TODO: should address why this code is being reached in the first place. if (checker_verbose.IsSpecified()) logout << "CHECK: " << frame << ": Initializer has no callers" << endl; return false; } else { // unknown type of block. Assert(false); } // if we set the state's delayed heap propagation then unset it. if (propagate && state->m_delayed_propagate_heap == propagate) state->m_delayed_propagate_heap = NULL; return false; }
// 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; }
void BlockSummary::GetAssumedBits(BlockMemory *mcfg, PPoint end_point, Vector<AssumeInfo> *assume_list) { BlockId *id = mcfg->GetId(); BlockCFG *cfg = mcfg->GetCFG(); BlockSummary *sum = GetBlockSummary(id); const Vector<Bit*> *assumes = sum->GetAssumes(); size_t assume_count = VectorSize<Bit*>(assumes); // pull in assumptions from the summary for mcfg. in some cases these // assumptions won't be useful, e.g. describing the state at exit // for functions. for now we're just adding all of them though. TODO: fix. for (size_t ind = 0; ind < assume_count; ind++) { Bit *bit = assumes->At(ind); bit->IncRef(assume_list); AssumeInfo info; info.bit = bit; assume_list->PushBack(info); } sum->DecRef(); Vector<BlockCFG*> *annot_list = BodyAnnotCache.Lookup(id->Function()); // add assumes at function entry for any preconditions. if (id->Kind() == B_Function) { for (size_t ind = 0; annot_list && ind < annot_list->Size(); ind++) { BlockCFG *annot_cfg = annot_list->At(ind); if (annot_cfg->GetAnnotationKind() != AK_Precondition && annot_cfg->GetAnnotationKind() != AK_PreconditionAssume) continue; Bit *bit = BlockMemory::GetAnnotationBit(annot_cfg); if (!bit) continue; annot_cfg->IncRef(assume_list); bit->IncRef(assume_list); AssumeInfo info; info.annot = annot_cfg; info.bit = bit; assume_list->PushBack(info); } } // add assumptions from points within the block. for (size_t pind = 0; pind < cfg->GetPointAnnotationCount(); pind++) { PointAnnotation pann = cfg->GetPointAnnotation(pind); if (end_point && pann.point >= end_point) continue; BlockCFG *annot_cfg = GetAnnotationCFG(pann.annot); if (!annot_cfg) continue; Assert(annot_cfg->GetAnnotationKind() != AK_AssertRuntime); if (Bit *bit = BlockMemory::GetAnnotationBit(annot_cfg)) { // get the annotation bit in terms of block entry. Bit *point_bit = NULL; mcfg->TranslateBit(TRK_Point, pann.point, bit, &point_bit); point_bit->MoveRef(&point_bit, assume_list); annot_cfg->IncRef(assume_list); AssumeInfo info; info.annot = annot_cfg; info.point = pann.point; info.bit = point_bit; assume_list->PushBack(info); } annot_cfg->DecRef(); } // add assumptions from annotation edges within the block, invariants // on values accessed by the block, and from the summaries of any callees. for (size_t eind = 0; eind < cfg->GetEdgeCount(); eind++) { PEdge *edge = cfg->GetEdge(eind); PPoint point = edge->GetSource(); if (end_point && point >= end_point) continue; InvariantAssumeVisitor visitor(mcfg, point, assume_list); edge->DoVisit(&visitor); if (PEdgeAnnotation *nedge = edge->IfAnnotation()) { // add an assumption for this annotation. BlockCFG *annot_cfg = GetAnnotationCFG(nedge->GetAnnotationId()); if (!annot_cfg) continue; Bit *bit = BlockMemory::GetAnnotationBit(annot_cfg); // don't incorporate AssertRuntimes, these are not assumed. if (bit && annot_cfg->GetAnnotationKind() != AK_AssertRuntime) { // get the annotation bit in terms of block entry. Bit *point_bit = NULL; mcfg->TranslateBit(TRK_Point, point, bit, &point_bit); point_bit->MoveRef(&point_bit, assume_list); annot_cfg->IncRef(assume_list); AssumeInfo info; info.annot = annot_cfg; info.point = point; info.bit = point_bit; assume_list->PushBack(info); } annot_cfg->DecRef(); } if (BlockId *callee = edge->GetDirectCallee()) { GetCallAssumedBits(mcfg, edge, callee, false, assume_list); callee->DecRef(); } else if (edge->IsCall()) { // add conditional assumes for the indirect targets of the call. // this is most useful for baked information and annotations, where // we sometimes need to attach information at indirect calls. CallEdgeSet *callees = CalleeCache.Lookup(id->BaseVar()); size_t old_count = assume_list->Size(); if (callees) { for (size_t cind = 0; cind < callees->GetEdgeCount(); cind++) { const CallEdge &cedge = callees->GetEdge(cind); if (cedge.where.id == id && cedge.where.point == point) { cedge.callee->IncRef(); BlockId *callee = BlockId::Make(B_Function, cedge.callee); GetCallAssumedBits(mcfg, edge, callee, true, assume_list); callee->DecRef(); } } } if (assume_list->Size() != old_count) { // we managed to do something at this indirect call site. // add another assumption restricting the possible callees to // only those identified by our callgraph. GuardExpVector receiver_list; mcfg->TranslateReceiver(point, &receiver_list); for (size_t rind = 0; rind < receiver_list.Size(); rind++) { const GuardExp &gs = receiver_list[rind]; gs.guard->IncRef(); // make a bit: !when || rcv == callee0 || rcv == callee1 || ... Bit *extra_bit = Bit::MakeNot(gs.guard); for (size_t cind = 0; cind < callees->GetEdgeCount(); cind++) { const CallEdge &cedge = callees->GetEdge(cind); if (cedge.where.id == id && cedge.where.point == point) { Variable *callee_var = cedge.callee; callee_var->IncRef(); Exp *callee_exp = Exp::MakeVar(callee_var); gs.exp->IncRef(); Bit *equal = Exp::MakeCompareBit(B_Equal, callee_exp, gs.exp); extra_bit = Bit::MakeOr(extra_bit, equal); } } extra_bit->MoveRef(NULL, assume_list); AssumeInfo info; info.bit = extra_bit; assume_list->PushBack(info); } } CalleeCache.Release(id->BaseVar()); } } BodyAnnotCache.Release(id->Function()); // add assumptions from heap invariants describing values mentioned // in added assumptions. we could keep doing this transitively but don't, // to ensure termination. size_t count = assume_list->Size(); for (size_t ind = 0; ind < count; ind++) { InvariantAssumeVisitor visitor(NULL, 0, assume_list); assume_list->At(ind).bit->DoVisit(&visitor); } CombineAssumeList(assume_list); }