// 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; }
void GetMatchingHeapWrites(const EscapeAccess &heap_write, Vector<HeapWriteInfo> *writes) { BlockId *id = heap_write.where.id; BlockMemory *mcfg = GetBlockMemory(id); if (mcfg == NULL) { logout << "WARNING: Missing memory: '" << id << "'" << endl; return; } BlockCFG *cfg = mcfg->GetCFG(); // for incremental analysis, make sure the write CFG uses the right version. // as for checking callers, if the CFG has changed but the new one still // has a matching write, we will see an escape access for the new CFG. if (cfg->GetVersion() != heap_write.where.version) { if (checker_verbose.IsSpecified()) logout << "CHECK: Write is an older version: " << heap_write.where.id << ": " << heap_write.where.version << endl; mcfg->DecRef(); return; } PPoint point = heap_write.where.point; PPoint exit_point = mcfg->GetCFG()->GetExitPoint(); // find a point-relative lvalue written at the write point with // the sanitized representation from the heap_write trace. // TODO: we only match against direct assignments in the CFG for now, // ignoring structural copies (which are simple recursive writes). PEdge *edge = cfg->GetSingleOutgoingEdge(point); Exp *point_lval = NULL; if (PEdgeAssign *nedge = edge->IfAssign()) point_lval = nedge->GetLeftSide(); else if (PEdgeCall *nedge = edge->IfCall()) point_lval = nedge->GetReturnValue(); bool lval_matches = false; if (point_lval) { if (Exp *new_point_lval = Trace::SanitizeExp(point_lval)) { lval_matches = (new_point_lval == heap_write.target->GetValue()); new_point_lval->DecRef(); } } if (!lval_matches) { mcfg->DecRef(); return; } // it would be nice to remove Val() expressions from this list, but we can't // do that as lvalues in memory assignments can contain Val and we want to // see the effects of those assignments. TODO: fix. GuardExpVector lval_res; mcfg->TranslateExp(TRK_Point, point, point_lval, &lval_res); for (size_t ind = 0; ind < lval_res.Size(); ind++) { const GuardExp &lv = lval_res[ind]; HeapWriteInfo info; info.mcfg = mcfg; info.lval = lv.exp; info.base_lval = point_lval; // look for a condition where the lvalue is not written. GuardExpVector exit_vals; info.mcfg->GetValComplete(info.lval, NULL, exit_point, &exit_vals); for (size_t ind = 0; ind < exit_vals.Size(); ind++) { const GuardExp &val = exit_vals[ind]; // exclude cases where the lvalue refers to its value at block entry. if (ExpDrf *nval = val.exp->IfDrf()) { if (nval->GetTarget() == info.lval) info.exclude.PushBack(val.guard); } } if (!writes->Contains(info)) { info.mcfg->IncRef(writes); info.lval->IncRef(writes); info.base_lval->IncRef(writes); IncRefVector<Bit>(info.exclude, writes); writes->PushBack(info); } } mcfg->DecRef(); }
void BlockModset::ComputeModset(BlockMemory *mcfg, bool indirect) { static BaseTimer compute_timer("modset_compute"); Timer _timer(&compute_timer); // get any indirect callees for this function, provided they have been // computed and stored in the callee database (indirect is set). CallEdgeSet *indirect_callees = NULL; if (indirect) indirect_callees = CalleeCache.Lookup(m_id->BaseVar()); BlockCFG *cfg = mcfg->GetCFG(); for (size_t eind = 0; eind < cfg->GetEdgeCount(); eind++) { PEdge *edge = cfg->GetEdge(eind); PPoint point = edge->GetSource(); if (edge->IsAssign() || edge->IsCall()) { // process direct assignments along this edge. const Vector<GuardAssign>* assigns = mcfg->GetAssigns(point); if (assigns) { for (size_t aind = 0; aind < assigns->Size(); aind++) { const GuardAssign &gasn = assigns->At(aind); ProcessUpdatedLval(mcfg, gasn.left, NULL, true, false); Exp *use_lval = NULL; Exp *kind = mcfg->GetTerminateAssign(point, gasn.left, gasn.right, &use_lval); if (kind) { ProcessUpdatedLval(mcfg, use_lval, kind, false, false); kind->DecRef(); } } } } // pull in modsets from the direct and indirect callees of the edge. if (BlockId *callee = edge->GetDirectCallee()) { ComputeModsetCall(mcfg, edge, callee, NULL); callee->DecRef(); } else if (edge->IsCall() && indirect_callees) { for (size_t ind = 0; ind < indirect_callees->GetEdgeCount(); ind++) { const CallEdge &cedge = indirect_callees->GetEdge(ind); // when comparing watch out for the case that this is a temporary // modset and does not share the same block kind as the edge point. if (cedge.where.version == cfg->GetVersion() && cedge.where.point == point && cedge.where.id->Function() == m_id->Function() && cedge.where.id->Loop() == m_id->Loop()) { cedge.callee->IncRef(); BlockId *callee = BlockId::Make(B_Function, cedge.callee); ComputeModsetCall(mcfg, edge, callee, cedge.rfld_chain); callee->DecRef(); } } } } // sort the modset exps to ensure a consistent representation. if (m_modset_list) SortVector<PointValue,compare_PointValue>(m_modset_list); if (m_assign_list) SortVector<GuardAssign,compare_GuardAssign>(m_assign_list); if (indirect) CalleeCache.Release(m_id->BaseVar()); }