CallEventRef<> CallEventManager::getCaller(const StackFrameContext *CalleeCtx, ProgramStateRef State) { const LocationContext *ParentCtx = CalleeCtx->getParent(); const LocationContext *CallerCtx = ParentCtx->getCurrentStackFrame(); assert(CallerCtx && "This should not be used for top-level stack frames"); const Stmt *CallSite = CalleeCtx->getCallSite(); if (CallSite) { if (const CallExpr *CE = dyn_cast<CallExpr>(CallSite)) return getSimpleCall(CE, State, CallerCtx); switch (CallSite->getStmtClass()) { case Stmt::CXXConstructExprClass: case Stmt::CXXTemporaryObjectExprClass: { SValBuilder &SVB = State->getStateManager().getSValBuilder(); const CXXMethodDecl *Ctor = cast<CXXMethodDecl>(CalleeCtx->getDecl()); Loc ThisPtr = SVB.getCXXThis(Ctor, CalleeCtx); SVal ThisVal = State->getSVal(ThisPtr); return getCXXConstructorCall(cast<CXXConstructExpr>(CallSite), ThisVal.getAsRegion(), State, CallerCtx); } case Stmt::CXXNewExprClass: return getCXXAllocatorCall(cast<CXXNewExpr>(CallSite), State, CallerCtx); case Stmt::ObjCMessageExprClass: return getObjCMethodCall(cast<ObjCMessageExpr>(CallSite), State, CallerCtx); default: llvm_unreachable("This is not an inlineable statement."); } } // Fall back to the CFG. The only thing we haven't handled yet is // destructors, though this could change in the future. const CFGBlock *B = CalleeCtx->getCallSiteBlock(); CFGElement E = (*B)[CalleeCtx->getIndex()]; assert(E.getAs<CFGImplicitDtor>() && "All other CFG elements should have exprs"); assert(!E.getAs<CFGTemporaryDtor>() && "We don't handle temporaries yet"); SValBuilder &SVB = State->getStateManager().getSValBuilder(); const CXXDestructorDecl *Dtor = cast<CXXDestructorDecl>(CalleeCtx->getDecl()); Loc ThisPtr = SVB.getCXXThis(Dtor, CalleeCtx); SVal ThisVal = State->getSVal(ThisPtr); const Stmt *Trigger; if (Optional<CFGAutomaticObjDtor> AutoDtor = E.getAs<CFGAutomaticObjDtor>()) Trigger = AutoDtor->getTriggerStmt(); else if (Optional<CFGDeleteDtor> DeleteDtor = E.getAs<CFGDeleteDtor>()) Trigger = cast<Stmt>(DeleteDtor->getDeleteExpr()); else Trigger = Dtor->getBody(); return getCXXDestructorCall(Dtor, Trigger, ThisVal.getAsRegion(), E.getAs<CFGBaseDtor>().hasValue(), State, CallerCtx); }
// Registers every VarDecl inside a Stmt with a last store visitor. void FindLastStoreBRVisitor::registerStatementVarDecls(BugReport &BR, const Stmt *S) { const ExplodedNode *N = BR.getErrorNode(); std::deque<const Stmt *> WorkList; WorkList.push_back(S); while (!WorkList.empty()) { const Stmt *Head = WorkList.front(); WorkList.pop_front(); ProgramStateRef state = N->getState(); ProgramStateManager &StateMgr = state->getStateManager(); if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Head)) { if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { const VarRegion *R = StateMgr.getRegionManager().getVarRegion(VD, N->getLocationContext()); // What did we load? SVal V = state->getSVal(S, N->getLocationContext()); if (V.getAs<loc::ConcreteInt>() || V.getAs<nonloc::ConcreteInt>()) { // Register a new visitor with the BugReport. BR.addVisitor(new FindLastStoreBRVisitor(V.castAs<KnownSVal>(), R)); } } } for (Stmt::const_child_iterator I = Head->child_begin(); I != Head->child_end(); ++I) WorkList.push_back(*I); } }
/// Remove the Value requiring a release from the tracked set for /// Instance and return the resultant state. ProgramStateRef ObjCDeallocChecker::removeValueRequiringRelease( ProgramStateRef State, SymbolRef Instance, SymbolRef Value) const { assert(Instance); assert(Value); const ObjCIvarRegion *RemovedRegion = getIvarRegionForIvarSymbol(Value); if (!RemovedRegion) return State; const SymbolSet *Unreleased = State->get<UnreleasedIvarMap>(Instance); if (!Unreleased) return State; // Mark the value as no longer requiring a release. SymbolSet::Factory &F = State->getStateManager().get_context<SymbolSet>(); SymbolSet NewUnreleased = *Unreleased; for (auto &Sym : *Unreleased) { const ObjCIvarRegion *UnreleasedRegion = getIvarRegionForIvarSymbol(Sym); assert(UnreleasedRegion); if (RemovedRegion->getDecl() == UnreleasedRegion->getDecl()) { NewUnreleased = F.remove(NewUnreleased, Sym); } } if (NewUnreleased.isEmpty()) { return State->remove<UnreleasedIvarMap>(Instance); } return State->set<UnreleasedIvarMap>(Instance, NewUnreleased); }
ProgramStateRef taint::addTaint(ProgramStateRef State, SVal V, TaintTagType Kind) { SymbolRef Sym = V.getAsSymbol(); if (Sym) return addTaint(State, Sym, Kind); // If the SVal represents a structure, try to mass-taint all values within the // structure. For now it only works efficiently on lazy compound values that // were conjured during a conservative evaluation of a function - either as // return values of functions that return structures or arrays by value, or as // values of structures or arrays passed into the function by reference, // directly or through pointer aliasing. Such lazy compound values are // characterized by having exactly one binding in their captured store within // their parent region, which is a conjured symbol default-bound to the base // region of the parent region. if (auto LCV = V.getAs<nonloc::LazyCompoundVal>()) { if (Optional<SVal> binding = State->getStateManager().getStoreManager() .getDefaultBinding(*LCV)) { if (SymbolRef Sym = binding->getAsSymbol()) return addPartialTaint(State, Sym, LCV->getRegion(), Kind); } } const MemRegion *R = V.getAsRegion(); return addTaint(State, R, Kind); }
void ChrootChecker::Chdir(CheckerContext &C, const CallExpr *CE) const { ProgramStateRef state = C.getState(); ProgramStateManager &Mgr = state->getStateManager(); // If there are no jail state in the GDM, just return. const void *k = state->FindGDM(ChrootChecker::getTag()); if (!k) return; // After chdir("/"), enter the jail, set the enum value JAIL_ENTERED. const Expr *ArgExpr = CE->getArg(0); SVal ArgVal = state->getSVal(ArgExpr, C.getLocationContext()); if (const MemRegion *R = ArgVal.getAsRegion()) { R = R->StripCasts(); if (const StringRegion* StrRegion= dyn_cast<StringRegion>(R)) { const StringLiteral* Str = StrRegion->getStringLiteral(); if (Str->getString() == "/") state = Mgr.addGDM(state, ChrootChecker::getTag(), (void*) JAIL_ENTERED); } } C.addTransition(state); }
void ChrootChecker::Chroot(CheckerContext &C, const CallExpr *CE) const { ProgramStateRef state = C.getState(); ProgramStateManager &Mgr = state->getStateManager(); // Once encouter a chroot(), set the enum value ROOT_CHANGED directly in // the GDM. state = Mgr.addGDM(state, ChrootChecker::getTag(), (void*) ROOT_CHANGED); C.addTransition(state); }
/// If this is the beginning of -dealloc, mark the values initially stored in /// instance variables that must be released by the end of -dealloc /// as unreleased in the state. void ObjCDeallocChecker::checkBeginFunction( CheckerContext &C) const { initIdentifierInfoAndSelectors(C.getASTContext()); // Only do this if the current method is -dealloc. SVal SelfVal; if (!isInInstanceDealloc(C, SelfVal)) return; SymbolRef SelfSymbol = SelfVal.getAsSymbol(); const LocationContext *LCtx = C.getLocationContext(); ProgramStateRef InitialState = C.getState(); ProgramStateRef State = InitialState; SymbolSet::Factory &F = State->getStateManager().get_context<SymbolSet>(); // Symbols that must be released by the end of the -dealloc; SymbolSet RequiredReleases = F.getEmptySet(); // If we're an inlined -dealloc, we should add our symbols to the existing // set from our subclass. if (const SymbolSet *CurrSet = State->get<UnreleasedIvarMap>(SelfSymbol)) RequiredReleases = *CurrSet; for (auto *PropImpl : getContainingObjCImpl(LCtx)->property_impls()) { ReleaseRequirement Requirement = getDeallocReleaseRequirement(PropImpl); if (Requirement != ReleaseRequirement::MustRelease) continue; SVal LVal = State->getLValue(PropImpl->getPropertyIvarDecl(), SelfVal); Optional<Loc> LValLoc = LVal.getAs<Loc>(); if (!LValLoc) continue; SVal InitialVal = State->getSVal(LValLoc.getValue()); SymbolRef Symbol = InitialVal.getAsSymbol(); if (!Symbol || !isa<SymbolRegionValue>(Symbol)) continue; // Mark the value as requiring a release. RequiredReleases = F.add(RequiredReleases, Symbol); } if (!RequiredReleases.isEmpty()) { State = State->set<UnreleasedIvarMap>(SelfSymbol, RequiredReleases); } if (State != InitialState) { C.addTransition(State); } }
/// Returns a region representing the first element of a (possibly /// multi-dimensional) array. /// /// On return, \p Ty will be set to the base type of the array. /// /// If the type is not an array type at all, the original value is returned. static SVal makeZeroElementRegion(ProgramStateRef State, SVal LValue, QualType &Ty) { SValBuilder &SVB = State->getStateManager().getSValBuilder(); ASTContext &Ctx = SVB.getContext(); while (const ArrayType *AT = Ctx.getAsArrayType(Ty)) { Ty = AT->getElementType(); LValue = State->getLValue(Ty, SVB.makeZeroArrayIndex(), LValue); } return LValue; }
ProgramStateRef SimpleConstraintManager::assume(ProgramStateRef State, DefinedSVal Cond, bool Assumption) { // If we have a Loc value, cast it to a bool NonLoc first. if (Optional<Loc> LV = Cond.getAs<Loc>()) { SValBuilder &SVB = State->getStateManager().getSValBuilder(); QualType T; const MemRegion *MR = LV->getAsRegion(); if (const TypedRegion *TR = dyn_cast_or_null<TypedRegion>(MR)) T = TR->getLocationType(); else T = SVB.getContext().VoidPtrTy; Cond = SVB.evalCast(*LV, SVB.getContext().BoolTy, T).castAs<DefinedSVal>(); } return assume(State, Cond.castAs<NonLoc>(), Assumption); }
void InnerPointerChecker::checkPostCall(const CallEvent &Call, CheckerContext &C) const { ProgramStateRef State = C.getState(); if (const auto *ICall = dyn_cast<CXXInstanceCall>(&Call)) { // TODO: Do we need these to be typed? const auto *ObjRegion = dyn_cast_or_null<TypedValueRegion>( ICall->getCXXThisVal().getAsRegion()); if (!ObjRegion) return; if (Call.isCalled(CStrFn) || Call.isCalled(DataFn)) { SVal RawPtr = Call.getReturnValue(); if (SymbolRef Sym = RawPtr.getAsSymbol(/*IncludeBaseRegions=*/true)) { // Start tracking this raw pointer by adding it to the set of symbols // associated with this container object in the program state map. PtrSet::Factory &F = State->getStateManager().get_context<PtrSet>(); const PtrSet *SetPtr = State->get<RawPtrMap>(ObjRegion); PtrSet Set = SetPtr ? *SetPtr : F.getEmptySet(); assert(C.wasInlined || !Set.contains(Sym)); Set = F.add(Set, Sym); State = State->set<RawPtrMap>(ObjRegion, Set); C.addTransition(State); } return; } // Check [string.require] / second point. if (isInvalidatingMemberFunction(Call)) { markPtrSymbolsReleased(Call, State, ObjRegion, C); return; } } // Check [string.require] / first point. checkFunctionArguments(Call, State, C); }
void InnerPointerChecker::checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const { ProgramStateRef State = C.getState(); PtrSet::Factory &F = State->getStateManager().get_context<PtrSet>(); RawPtrMapTy RPM = State->get<RawPtrMap>(); for (const auto Entry : RPM) { if (!SymReaper.isLiveRegion(Entry.first)) { // Due to incomplete destructor support, some dead regions might // remain in the program state map. Clean them up. State = State->remove<RawPtrMap>(Entry.first); } if (const PtrSet *OldSet = State->get<RawPtrMap>(Entry.first)) { PtrSet CleanedUpSet = *OldSet; for (const auto Symbol : Entry.second) { if (!SymReaper.isLive(Symbol)) CleanedUpSet = F.remove(CleanedUpSet, Symbol); } State = CleanedUpSet.isEmpty() ? State->remove<RawPtrMap>(Entry.first) : State->set<RawPtrMap>(Entry.first, CleanedUpSet); } } C.addTransition(State); }
static void generateDiagnosticsForCallLike(ProgramStateRef CurrSt, const LocationContext *LCtx, const RefVal &CurrV, SymbolRef &Sym, const Stmt *S, llvm::raw_string_ostream &os) { CallEventManager &Mgr = CurrSt->getStateManager().getCallEventManager(); if (const CallExpr *CE = dyn_cast<CallExpr>(S)) { // Get the name of the callee (if it is available) // from the tracked SVal. SVal X = CurrSt->getSValAsScalarOrLoc(CE->getCallee(), LCtx); const FunctionDecl *FD = X.getAsFunctionDecl(); // If failed, try to get it from AST. if (!FD) FD = dyn_cast<FunctionDecl>(CE->getCalleeDecl()); if (const auto *MD = dyn_cast<CXXMethodDecl>(CE->getCalleeDecl())) { os << "Call to method '" << MD->getQualifiedNameAsString() << '\''; } else if (FD) { os << "Call to function '" << FD->getQualifiedNameAsString() << '\''; } else { os << "function call"; } } else if (isa<CXXNewExpr>(S)) { os << "Operator 'new'"; } else { assert(isa<ObjCMessageExpr>(S)); CallEventRef<ObjCMethodCall> Call = Mgr.getObjCMethodCall(cast<ObjCMessageExpr>(S), CurrSt, LCtx); switch (Call->getMessageKind()) { case OCM_Message: os << "Method"; break; case OCM_PropertyAccess: os << "Property"; break; case OCM_Subscript: os << "Subscript"; break; } } Optional<CallEventRef<>> CE = Mgr.getCall(S, CurrSt, LCtx); auto Idx = findArgIdxOfSymbol(CurrSt, LCtx, Sym, CE); // If index is not found, we assume that the symbol was returned. if (!Idx) { os << " returns "; } else { os << " writes "; } if (CurrV.getObjKind() == ObjKind::CF) { os << "a Core Foundation object of type '" << Sym->getType().getAsString() << "' with a "; } else if (CurrV.getObjKind() == ObjKind::OS) { os << "an OSObject of type '" << getPrettyTypeName(Sym->getType()) << "' with a "; } else if (CurrV.getObjKind() == ObjKind::Generalized) { os << "an object of type '" << Sym->getType().getAsString() << "' with a "; } else { assert(CurrV.getObjKind() == ObjKind::ObjC); QualType T = Sym->getType(); if (!isa<ObjCObjectPointerType>(T)) { os << "an Objective-C object with a "; } else { const ObjCObjectPointerType *PT = cast<ObjCObjectPointerType>(T); os << "an instance of " << PT->getPointeeType().getAsString() << " with a "; } } if (CurrV.isOwned()) { os << "+1 retain count"; } else { assert(CurrV.isNotOwned()); os << "+0 retain count"; } if (Idx) { os << " into an out parameter '"; const ParmVarDecl *PVD = (*CE)->parameters()[*Idx]; PVD->getNameForDiagnostic(os, PVD->getASTContext().getPrintingPolicy(), /*Qualified=*/false); os << "'"; QualType RT = (*CE)->getResultType(); if (!RT.isNull() && !RT->isVoidType()) { SVal RV = (*CE)->getReturnValue(); if (CurrSt->isNull(RV).isConstrainedTrue()) { os << " (assuming the call returns zero)"; } else if (CurrSt->isNonNull(RV).isConstrainedTrue()) { os << " (assuming the call returns non-zero)"; } } } }
void StackAddrEscapeChecker::checkEndPath(CheckerContext &Ctx) const { ProgramStateRef state = Ctx.getState(); // Iterate over all bindings to global variables and see if it contains // a memory region in the stack space. class CallBack : public StoreManager::BindingsHandler { private: CheckerContext &Ctx; const StackFrameContext *CurSFC; public: SmallVector<std::pair<const MemRegion*, const MemRegion*>, 10> V; CallBack(CheckerContext &CC) : Ctx(CC), CurSFC(CC.getLocationContext()->getCurrentStackFrame()) {} bool HandleBinding(StoreManager &SMgr, Store store, const MemRegion *region, SVal val) { if (!isa<GlobalsSpaceRegion>(region->getMemorySpace())) return true; const MemRegion *vR = val.getAsRegion(); if (!vR) return true; // Under automated retain release, it is okay to assign a block // directly to a global variable. if (Ctx.getASTContext().getLangOpts().ObjCAutoRefCount && isa<BlockDataRegion>(vR)) return true; if (const StackSpaceRegion *SSR = dyn_cast<StackSpaceRegion>(vR->getMemorySpace())) { // If the global variable holds a location in the current stack frame, // record the binding to emit a warning. if (SSR->getStackFrame() == CurSFC) V.push_back(std::make_pair(region, vR)); } return true; } }; CallBack cb(Ctx); state->getStateManager().getStoreManager().iterBindings(state->getStore(),cb); if (cb.V.empty()) return; // Generate an error node. ExplodedNode *N = Ctx.addTransition(state); if (!N) return; if (!BT_stackleak) BT_stackleak.reset( new BuiltinBug("Stack address stored into global variable", "Stack address was saved into a global variable. " "This is dangerous because the address will become " "invalid after returning from the function")); for (unsigned i = 0, e = cb.V.size(); i != e; ++i) { // Generate a report for this bug. SmallString<512> buf; llvm::raw_svector_ostream os(buf); SourceRange range = GenName(os, cb.V[i].second, Ctx.getSourceManager()); os << " is still referred to by the global variable '"; const VarRegion *VR = cast<VarRegion>(cb.V[i].first->getBaseRegion()); os << *VR->getDecl() << "' upon returning to the caller. This will be a dangling reference"; BugReport *report = new BugReport(*BT_stackleak, os.str(), N); if (range.isValid()) report->addRange(range); Ctx.EmitReport(report); } }
Simplifier(ProgramStateRef State) : State(State), SVB(State->getStateManager().getSValBuilder()) {}
/// Report any unreleased instance variables for the current instance being /// dealloced. void ObjCDeallocChecker::diagnoseMissingReleases(CheckerContext &C) const { ProgramStateRef State = C.getState(); SVal SelfVal; if (!isInInstanceDealloc(C, SelfVal)) return; const MemRegion *SelfRegion = SelfVal.castAs<loc::MemRegionVal>().getRegion(); const LocationContext *LCtx = C.getLocationContext(); ExplodedNode *ErrNode = nullptr; SymbolRef SelfSym = SelfVal.getAsSymbol(); if (!SelfSym) return; const SymbolSet *OldUnreleased = State->get<UnreleasedIvarMap>(SelfSym); if (!OldUnreleased) return; SymbolSet NewUnreleased = *OldUnreleased; SymbolSet::Factory &F = State->getStateManager().get_context<SymbolSet>(); ProgramStateRef InitialState = State; for (auto *IvarSymbol : *OldUnreleased) { const TypedValueRegion *TVR = cast<SymbolRegionValue>(IvarSymbol)->getRegion(); const ObjCIvarRegion *IvarRegion = cast<ObjCIvarRegion>(TVR); // Don't warn if the ivar is not for this instance. if (SelfRegion != IvarRegion->getSuperRegion()) continue; const ObjCIvarDecl *IvarDecl = IvarRegion->getDecl(); // Prevent an inlined call to -dealloc in a super class from warning // about the values the subclass's -dealloc should release. if (IvarDecl->getContainingInterface() != cast<ObjCMethodDecl>(LCtx->getDecl())->getClassInterface()) continue; // Prevents diagnosing multiple times for the same instance variable // at, for example, both a return and at the end of the function. NewUnreleased = F.remove(NewUnreleased, IvarSymbol); if (State->getStateManager() .getConstraintManager() .isNull(State, IvarSymbol) .isConstrainedTrue()) { continue; } // A missing release manifests as a leak, so treat as a non-fatal error. if (!ErrNode) ErrNode = C.generateNonFatalErrorNode(); // If we've already reached this node on another path, return without // diagnosing. if (!ErrNode) return; std::string Buf; llvm::raw_string_ostream OS(Buf); const ObjCInterfaceDecl *Interface = IvarDecl->getContainingInterface(); // If the class is known to have a lifecycle with teardown that is // separate from -dealloc, do not warn about missing releases. We // suppress here (rather than not tracking for instance variables in // such classes) because these classes are rare. if (classHasSeparateTeardown(Interface)) return; ObjCImplDecl *ImplDecl = Interface->getImplementation(); const ObjCPropertyImplDecl *PropImpl = ImplDecl->FindPropertyImplIvarDecl(IvarDecl->getIdentifier()); const ObjCPropertyDecl *PropDecl = PropImpl->getPropertyDecl(); assert(PropDecl->getSetterKind() == ObjCPropertyDecl::Copy || PropDecl->getSetterKind() == ObjCPropertyDecl::Retain); OS << "The '" << *IvarDecl << "' ivar in '" << *ImplDecl << "' was "; if (PropDecl->getSetterKind() == ObjCPropertyDecl::Retain) OS << "retained"; else OS << "copied"; OS << " by a synthesized property but not released" " before '[super dealloc]'"; std::unique_ptr<BugReport> BR( new BugReport(*MissingReleaseBugType, OS.str(), ErrNode)); C.emitReport(std::move(BR)); } if (NewUnreleased.isEmpty()) { State = State->remove<UnreleasedIvarMap>(SelfSym); } else { State = State->set<UnreleasedIvarMap>(SelfSym, NewUnreleased); } if (ErrNode) { C.addTransition(State, ErrNode); } else if (State != InitialState) { C.addTransition(State); } // Make sure that after checking in the top-most frame the list of // tracked ivars is empty. This is intended to detect accidental leaks in // the UnreleasedIvarMap program state. assert(!LCtx->inTopFrame() || State->get<UnreleasedIvarMap>().isEmpty()); }
void StackAddrEscapeChecker::checkEndFunction(CheckerContext &Ctx) const { if (!ChecksEnabled[CK_StackAddrEscapeChecker]) return; ProgramStateRef State = Ctx.getState(); // Iterate over all bindings to global variables and see if it contains // a memory region in the stack space. class CallBack : public StoreManager::BindingsHandler { private: CheckerContext &Ctx; const StackFrameContext *CurSFC; public: SmallVector<std::pair<const MemRegion *, const MemRegion *>, 10> V; CallBack(CheckerContext &CC) : Ctx(CC), CurSFC(CC.getLocationContext()->getCurrentStackFrame()) {} bool HandleBinding(StoreManager &SMgr, Store S, const MemRegion *Region, SVal Val) override { if (!isa<GlobalsSpaceRegion>(Region->getMemorySpace())) return true; const MemRegion *VR = Val.getAsRegion(); if (VR && isa<StackSpaceRegion>(VR->getMemorySpace()) && !isArcManagedBlock(VR, Ctx) && !isNotInCurrentFrame(VR, Ctx)) V.emplace_back(Region, VR); return true; } }; CallBack Cb(Ctx); State->getStateManager().getStoreManager().iterBindings(State->getStore(), Cb); if (Cb.V.empty()) return; // Generate an error node. ExplodedNode *N = Ctx.generateNonFatalErrorNode(State); if (!N) return; if (!BT_stackleak) BT_stackleak = llvm::make_unique<BuiltinBug>( this, "Stack address stored into global variable", "Stack address was saved into a global variable. " "This is dangerous because the address will become " "invalid after returning from the function"); for (const auto &P : Cb.V) { // Generate a report for this bug. SmallString<128> Buf; llvm::raw_svector_ostream Out(Buf); SourceRange Range = genName(Out, P.second, Ctx.getASTContext()); Out << " is still referred to by the "; if (isa<StaticGlobalSpaceRegion>(P.first->getMemorySpace())) Out << "static"; else Out << "global"; Out << " variable '"; const VarRegion *VR = cast<VarRegion>(P.first->getBaseRegion()); Out << *VR->getDecl() << "' upon returning to the caller. This will be a dangling reference"; auto Report = llvm::make_unique<BugReport>(*BT_stackleak, Out.str(), N); if (Range.isValid()) Report->addRange(Range); Ctx.emitReport(std::move(Report)); } }
bool bugreporter::trackNullOrUndefValue(const ExplodedNode *ErrorNode, const Stmt *S, BugReport &report, bool IsArg) { if (!S || !ErrorNode) return false; if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(S)) S = OVE->getSourceExpr(); const ExplodedNode *N = ErrorNode; const Expr *Inner = 0; if (const Expr *Ex = dyn_cast<Expr>(S)) { Ex = Ex->IgnoreParenCasts(); if (ExplodedGraph::isInterestingLValueExpr(Ex) || CallEvent::isCallStmt(Ex)) Inner = Ex; } if (IsArg) { assert(N->getLocation().getAs<CallEnter>() && "Tracking arg but not at call"); } else { // Walk through nodes until we get one that matches the statement exactly. // Alternately, if we hit a known lvalue for the statement, we know we've // gone too far (though we can likely track the lvalue better anyway). do { const ProgramPoint &pp = N->getLocation(); if (Optional<PostStmt> ps = pp.getAs<PostStmt>()) { if (ps->getStmt() == S || ps->getStmt() == Inner) break; } else if (Optional<CallExitEnd> CEE = pp.getAs<CallExitEnd>()) { if (CEE->getCalleeContext()->getCallSite() == S || CEE->getCalleeContext()->getCallSite() == Inner) break; } N = N->getFirstPred(); } while (N); if (!N) return false; } ProgramStateRef state = N->getState(); // See if the expression we're interested refers to a variable. // If so, we can track both its contents and constraints on its value. if (Inner && ExplodedGraph::isInterestingLValueExpr(Inner)) { const MemRegion *R = 0; // First check if this is a DeclRefExpr for a C++ reference type. // For those, we want the location of the reference. if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Inner)) { if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { if (VD->getType()->isReferenceType()) { ProgramStateManager &StateMgr = state->getStateManager(); MemRegionManager &MRMgr = StateMgr.getRegionManager(); R = MRMgr.getVarRegion(VD, N->getLocationContext()); } } } // For all other cases, find the location by scouring the ExplodedGraph. if (!R) { // Find the ExplodedNode where the lvalue (the value of 'Ex') // was computed. We need this for getting the location value. const ExplodedNode *LVNode = N; while (LVNode) { if (Optional<PostStmt> P = LVNode->getLocation().getAs<PostStmt>()) { if (P->getStmt() == Inner) break; } LVNode = LVNode->getFirstPred(); } assert(LVNode && "Unable to find the lvalue node."); ProgramStateRef LVState = LVNode->getState(); R = LVState->getSVal(Inner, LVNode->getLocationContext()).getAsRegion(); } if (R) { // Mark both the variable region and its contents as interesting. SVal V = state->getRawSVal(loc::MemRegionVal(R)); // If the value matches the default for the variable region, that // might mean that it's been cleared out of the state. Fall back to // the full argument expression (with casts and such intact). if (IsArg) { bool UseArgValue = V.isUnknownOrUndef() || V.isZeroConstant(); if (!UseArgValue) { const SymbolRegionValue *SRV = dyn_cast_or_null<SymbolRegionValue>(V.getAsLocSymbol()); if (SRV) UseArgValue = (SRV->getRegion() == R); } if (UseArgValue) V = state->getSValAsScalarOrLoc(S, N->getLocationContext()); } report.markInteresting(R); report.markInteresting(V); report.addVisitor(new UndefOrNullArgVisitor(R)); if (isa<SymbolicRegion>(R)) { TrackConstraintBRVisitor *VI = new TrackConstraintBRVisitor(loc::MemRegionVal(R), false); report.addVisitor(VI); } // If the contents are symbolic, find out when they became null. if (V.getAsLocSymbol()) { BugReporterVisitor *ConstraintTracker = new TrackConstraintBRVisitor(V.castAs<DefinedSVal>(), false); report.addVisitor(ConstraintTracker); // Add visitor, which will suppress inline defensive checks. if (ErrorNode->getState()->isNull(V).isConstrainedTrue()) { BugReporterVisitor *IDCSuppressor = new SuppressInlineDefensiveChecksVisitor(V.castAs<DefinedSVal>(), ErrorNode); report.addVisitor(IDCSuppressor); } } if (Optional<KnownSVal> KV = V.getAs<KnownSVal>()) report.addVisitor(new FindLastStoreBRVisitor(*KV, R)); return true; } } // If the expression is not an "lvalue expression", we can still // track the constraints on its contents. SVal V = state->getSValAsScalarOrLoc(S, N->getLocationContext()); // If the value came from an inlined function call, we should at least make // sure that function isn't pruned in our output. if (const Expr *E = dyn_cast<Expr>(S)) S = E->IgnoreParenCasts(); ReturnVisitor::addVisitorIfNecessary(N, S, report); // Uncomment this to find cases where we aren't properly getting the // base value that was dereferenced. // assert(!V.isUnknownOrUndef()); // Is it a symbolic value? if (Optional<loc::MemRegionVal> L = V.getAs<loc::MemRegionVal>()) { // At this point we are dealing with the region's LValue. // However, if the rvalue is a symbolic region, we should track it as well. SVal RVal = state->getSVal(L->getRegion()); const MemRegion *RegionRVal = RVal.getAsRegion(); report.addVisitor(new UndefOrNullArgVisitor(L->getRegion())); if (RegionRVal && isa<SymbolicRegion>(RegionRVal)) { report.markInteresting(RegionRVal); report.addVisitor(new TrackConstraintBRVisitor( loc::MemRegionVal(RegionRVal), false)); } } return true; }
bool bugreporter::trackNullOrUndefValue(const ExplodedNode *N, const Stmt *S, BugReport &report, bool IsArg) { if (!S || !N) return false; if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(S)) S = OVE->getSourceExpr(); if (IsArg) { assert(isa<CallEnter>(N->getLocation()) && "Tracking arg but not at call"); } else { // Walk through nodes until we get one that matches the statement exactly. do { const ProgramPoint &pp = N->getLocation(); if (const PostStmt *ps = dyn_cast<PostStmt>(&pp)) { if (ps->getStmt() == S) break; } else if (const CallExitEnd *CEE = dyn_cast<CallExitEnd>(&pp)) { if (CEE->getCalleeContext()->getCallSite() == S) break; } N = N->getFirstPred(); } while (N); if (!N) return false; } ProgramStateRef state = N->getState(); // See if the expression we're interested refers to a variable. // If so, we can track both its contents and constraints on its value. if (const Expr *Ex = dyn_cast<Expr>(S)) { // Strip off parens and casts. Note that this will never have issues with // C++ user-defined implicit conversions, because those have a constructor // or function call inside. Ex = Ex->IgnoreParenCasts(); if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Ex)) { // FIXME: Right now we only track VarDecls because it's non-trivial to // get a MemRegion for any other DeclRefExprs. <rdar://problem/12114812> if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { ProgramStateManager &StateMgr = state->getStateManager(); MemRegionManager &MRMgr = StateMgr.getRegionManager(); const VarRegion *R = MRMgr.getVarRegion(VD, N->getLocationContext()); // Mark both the variable region and its contents as interesting. SVal V = state->getRawSVal(loc::MemRegionVal(R)); // If the value matches the default for the variable region, that // might mean that it's been cleared out of the state. Fall back to // the full argument expression (with casts and such intact). if (IsArg) { bool UseArgValue = V.isUnknownOrUndef() || V.isZeroConstant(); if (!UseArgValue) { const SymbolRegionValue *SRV = dyn_cast_or_null<SymbolRegionValue>(V.getAsLocSymbol()); if (SRV) UseArgValue = (SRV->getRegion() == R); } if (UseArgValue) V = state->getSValAsScalarOrLoc(S, N->getLocationContext()); } report.markInteresting(R); report.markInteresting(V); report.addVisitor(new UndefOrNullArgVisitor(R)); // If the contents are symbolic, find out when they became null. if (V.getAsLocSymbol()) { BugReporterVisitor *ConstraintTracker = new TrackConstraintBRVisitor(cast<DefinedSVal>(V), false); report.addVisitor(ConstraintTracker); } report.addVisitor(new FindLastStoreBRVisitor(V, R)); return true; } } } // If the expression does NOT refer to a variable, we can still track // constraints on its contents. SVal V = state->getSValAsScalarOrLoc(S, N->getLocationContext()); // Uncomment this to find cases where we aren't properly getting the // base value that was dereferenced. // assert(!V.isUnknownOrUndef()); // Is it a symbolic value? if (loc::MemRegionVal *L = dyn_cast<loc::MemRegionVal>(&V)) { // At this point we are dealing with the region's LValue. // However, if the rvalue is a symbolic region, we should track it as well. SVal RVal = state->getSVal(L->getRegion()); const MemRegion *RegionRVal = RVal.getAsRegion(); report.addVisitor(new UndefOrNullArgVisitor(L->getRegion())); if (RegionRVal && isa<SymbolicRegion>(RegionRVal)) { report.markInteresting(RegionRVal); report.addVisitor(new TrackConstraintBRVisitor( loc::MemRegionVal(RegionRVal), false)); } } else { // Otherwise, if the value came from an inlined function call, // we should at least make sure that function isn't pruned in our output. if (const Expr *E = dyn_cast<Expr>(S)) S = E->IgnoreParenCasts(); ReturnVisitor::addVisitorIfNecessary(N, S, report); } return true; }
static DefinedSVal getLocFromSymbol(const ProgramStateRef &State, SymbolRef Sym) { const MemRegion *R = State->getStateManager().getRegionManager().getSymbolicRegion(Sym); return loc::MemRegionVal(R); }