diff options
Diffstat (limited to 'lib/StaticAnalyzer')
-rw-r--r-- | lib/StaticAnalyzer/Checkers/CStringChecker.cpp | 1 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp | 188 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Checkers/IteratorChecker.cpp | 25 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Checkers/MallocChecker.cpp | 43 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp | 2 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Checkers/RunLoopAutoreleaseLeakChecker.cpp | 18 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp | 10 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/BugReporterVisitors.cpp | 37 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/CallEvent.cpp | 63 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/ExprEngine.cpp | 150 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/ExprEngineC.cpp | 2 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/ExprEngineCXX.cpp | 43 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/IssueHash.cpp | 2 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/ProgramState.cpp | 5 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp | 46 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/Store.cpp | 4 |
16 files changed, 395 insertions, 244 deletions
diff --git a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp index 278452ec994a6..12a576e5d80d5 100644 --- a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp @@ -552,6 +552,7 @@ void CStringChecker::emitNullArgBug(CheckerContext &C, ProgramStateRef State, BuiltinBug *BT = static_cast<BuiltinBug *>(BT_Null.get()); auto Report = llvm::make_unique<BugReport>(*BT, WarningMsg, N); + Report->addRange(S->getSourceRange()); bugreporter::trackNullOrUndefValue(N, S, *Report); C.emitReport(std::move(Report)); } diff --git a/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp b/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp index ed877ab345189..29677f737f5cf 100644 --- a/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp @@ -91,37 +91,53 @@ public: ReserveFn("reserve"), ResizeFn("resize"), ShrinkToFitFn("shrink_to_fit"), SwapFn("swap") {} - /// Check whether the function called on the container object is a - /// member function that potentially invalidates pointers referring - /// to the objects's internal buffer. - bool mayInvalidateBuffer(const CallEvent &Call) const; - - /// Record the connection between the symbol returned by c_str() and the - /// corresponding string object region in the ProgramState. Mark the symbol - /// released if the string object is destroyed. + /// Check if the object of this member function call is a `basic_string`. + bool isCalledOnStringObject(const CXXInstanceCall *ICall) const; + + /// Check whether the called member function potentially invalidates + /// pointers referring to the container object's inner buffer. + bool isInvalidatingMemberFunction(const CallEvent &Call) const; + + /// Mark pointer symbols associated with the given memory region released + /// in the program state. + void markPtrSymbolsReleased(const CallEvent &Call, ProgramStateRef State, + const MemRegion *ObjRegion, + CheckerContext &C) const; + + /// Standard library functions that take a non-const `basic_string` argument by + /// reference may invalidate its inner pointers. Check for these cases and + /// mark the pointers released. + void checkFunctionArguments(const CallEvent &Call, ProgramStateRef State, + CheckerContext &C) const; + + /// Record the connection between raw pointers referring to a container + /// object's inner buffer and the object's memory region in the program state. + /// Mark potentially invalidated pointers released. void checkPostCall(const CallEvent &Call, CheckerContext &C) const; - /// Clean up the ProgramState map. + /// Clean up the program state map. void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; }; } // end anonymous namespace -// [string.require] -// -// "References, pointers, and iterators referring to the elements of a -// basic_string sequence may be invalidated by the following uses of that -// basic_string object: -// -// -- TODO: As an argument to any standard library function taking a reference -// to non-const basic_string as an argument. For example, as an argument to -// non-member functions swap(), operator>>(), and getline(), or as an argument -// to basic_string::swap(). -// -// -- Calling non-const member functions, except operator[], at, front, back, -// begin, rbegin, end, and rend." -// -bool InnerPointerChecker::mayInvalidateBuffer(const CallEvent &Call) const { +bool InnerPointerChecker::isCalledOnStringObject( + const CXXInstanceCall *ICall) const { + const auto *ObjRegion = + dyn_cast_or_null<TypedValueRegion>(ICall->getCXXThisVal().getAsRegion()); + if (!ObjRegion) + return false; + + QualType ObjTy = ObjRegion->getValueType(); + if (ObjTy.isNull() || + ObjTy->getAsCXXRecordDecl()->getName() != "basic_string") + return false; + + return true; +} + +bool InnerPointerChecker::isInvalidatingMemberFunction( + const CallEvent &Call) const { if (const auto *MemOpCall = dyn_cast<CXXMemberOperatorCall>(&Call)) { OverloadedOperatorKind Opc = MemOpCall->getOriginExpr()->getOperator(); if (Opc == OO_Equal || Opc == OO_PlusEqual) @@ -137,53 +153,104 @@ bool InnerPointerChecker::mayInvalidateBuffer(const CallEvent &Call) const { Call.isCalled(SwapFn)); } -void InnerPointerChecker::checkPostCall(const CallEvent &Call, - CheckerContext &C) const { - const auto *ICall = dyn_cast<CXXInstanceCall>(&Call); - if (!ICall) +void InnerPointerChecker::markPtrSymbolsReleased(const CallEvent &Call, + ProgramStateRef State, + const MemRegion *MR, + CheckerContext &C) const { + if (const PtrSet *PS = State->get<RawPtrMap>(MR)) { + const Expr *Origin = Call.getOriginExpr(); + for (const auto Symbol : *PS) { + // NOTE: `Origin` may be null, and will be stored so in the symbol's + // `RefState` in MallocChecker's `RegionState` program state map. + State = allocation_state::markReleased(State, Symbol, Origin); + } + State = State->remove<RawPtrMap>(MR); + C.addTransition(State); return; + } +} - SVal Obj = ICall->getCXXThisVal(); - const auto *ObjRegion = dyn_cast_or_null<TypedValueRegion>(Obj.getAsRegion()); - if (!ObjRegion) - return; +void InnerPointerChecker::checkFunctionArguments(const CallEvent &Call, + ProgramStateRef State, + CheckerContext &C) const { + if (const auto *FC = dyn_cast<AnyFunctionCall>(&Call)) { + const FunctionDecl *FD = FC->getDecl(); + if (!FD || !FD->isInStdNamespace()) + return; - auto *TypeDecl = ObjRegion->getValueType()->getAsCXXRecordDecl(); - if (TypeDecl->getName() != "basic_string") - return; + for (unsigned I = 0, E = FD->getNumParams(); I != E; ++I) { + QualType ParamTy = FD->getParamDecl(I)->getType(); + if (!ParamTy->isReferenceType() || + ParamTy->getPointeeType().isConstQualified()) + continue; - ProgramStateRef State = C.getState(); + // In case of member operator calls, `this` is counted as an + // argument but not as a parameter. + bool isaMemberOpCall = isa<CXXMemberOperatorCall>(FC); + unsigned ArgI = isaMemberOpCall ? I+1 : I; - 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); + SVal Arg = FC->getArgSVal(ArgI); + const auto *ArgRegion = + dyn_cast_or_null<TypedValueRegion>(Arg.getAsRegion()); + if (!ArgRegion) + continue; + + markPtrSymbolsReleased(Call, State, ArgRegion, C); } - return; } +} - if (mayInvalidateBuffer(Call)) { - if (const PtrSet *PS = State->get<RawPtrMap>(ObjRegion)) { - // Mark all pointer symbols associated with the deleted object released. - const Expr *Origin = Call.getOriginExpr(); - for (const auto Symbol : *PS) { - // NOTE: `Origin` may be null, and will be stored so in the symbol's - // `RefState` in MallocChecker's `RegionState` program state map. - State = allocation_state::markReleased(State, Symbol, Origin); +// [string.require] +// +// "References, pointers, and iterators referring to the elements of a +// basic_string sequence may be invalidated by the following uses of that +// basic_string object: +// +// -- As an argument to any standard library function taking a reference +// to non-const basic_string as an argument. For example, as an argument to +// non-member functions swap(), operator>>(), and getline(), or as an argument +// to basic_string::swap(). +// +// -- Calling non-const member functions, except operator[], at, front, back, +// begin, rbegin, end, and rend." + +void InnerPointerChecker::checkPostCall(const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + + if (const auto *ICall = dyn_cast<CXXInstanceCall>(&Call)) { + if (isCalledOnStringObject(ICall)) { + const auto *ObjRegion = dyn_cast_or_null<TypedValueRegion>( + ICall->getCXXThisVal().getAsRegion()); + + 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; } - State = State->remove<RawPtrMap>(ObjRegion); - C.addTransition(State); - return; } } + + // Check [string.require] / first point. + checkFunctionArguments(Call, State, C); } void InnerPointerChecker::checkDeadSymbols(SymbolReaper &SymReaper, @@ -216,7 +283,6 @@ InnerPointerChecker::InnerPointerBRVisitor::VisitNode(const ExplodedNode *N, const ExplodedNode *PrevN, BugReporterContext &BRC, BugReport &BR) { - if (!isSymbolTracked(N->getState(), PtrToBuf) || isSymbolTracked(PrevN->getState(), PtrToBuf)) return nullptr; diff --git a/lib/StaticAnalyzer/Checkers/IteratorChecker.cpp b/lib/StaticAnalyzer/Checkers/IteratorChecker.cpp index 56c250cd16783..520c32e1c7703 100644 --- a/lib/StaticAnalyzer/Checkers/IteratorChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/IteratorChecker.cpp @@ -291,6 +291,7 @@ const ContainerData *getContainerData(ProgramStateRef State, const MemRegion *Cont); ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont, const ContainerData &CData); +bool hasLiveIterators(ProgramStateRef State, const MemRegion *Cont); bool isOutOfRange(ProgramStateRef State, const IteratorPosition &Pos); bool isZero(ProgramStateRef State, const NonLoc &Val); } // namespace @@ -536,7 +537,11 @@ void IteratorChecker::checkDeadSymbols(SymbolReaper &SR, auto ContMap = State->get<ContainerMap>(); for (const auto Cont : ContMap) { if (!SR.isLiveRegion(Cont.first)) { - State = State->remove<ContainerMap>(Cont.first); + // We must keep the container data while it has live iterators to be able + // to compare them to the begin and the end of the container. + if (!hasLiveIterators(State, Cont.first)) { + State = State->remove<ContainerMap>(Cont.first); + } } } @@ -546,6 +551,8 @@ void IteratorChecker::checkDeadSymbols(SymbolReaper &SR, State = State->remove<IteratorComparisonMap>(Comp.first); } } + + C.addTransition(State); } ProgramStateRef IteratorChecker::evalAssume(ProgramStateRef State, SVal Cond, @@ -1188,6 +1195,22 @@ ProgramStateRef relateIteratorPositions(ProgramStateRef State, return NewState; } +bool hasLiveIterators(ProgramStateRef State, const MemRegion *Cont) { + auto RegionMap = State->get<IteratorRegionMap>(); + for (const auto Reg : RegionMap) { + if (Reg.second.getContainer() == Cont) + return true; + } + + auto SymbolMap = State->get<IteratorSymbolMap>(); + for (const auto Sym : SymbolMap) { + if (Sym.second.getContainer() == Cont) + return true; + } + + return false; +} + bool isZero(ProgramStateRef State, const NonLoc &Val) { auto &BVF = State->getBasicVals(); return compare(State, Val, diff --git a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp index 8f07f413e81f6..ebaf79a780c07 100644 --- a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -179,11 +179,11 @@ public: II_strdup(nullptr), II_win_strdup(nullptr), II_kmalloc(nullptr), II_if_nameindex(nullptr), II_if_freenameindex(nullptr), II_wcsdup(nullptr), II_win_wcsdup(nullptr), II_g_malloc(nullptr), - II_g_malloc0(nullptr), II_g_realloc(nullptr), II_g_try_malloc(nullptr), - II_g_try_malloc0(nullptr), II_g_try_realloc(nullptr), - II_g_free(nullptr), II_g_memdup(nullptr), II_g_malloc_n(nullptr), - II_g_malloc0_n(nullptr), II_g_realloc_n(nullptr), - II_g_try_malloc_n(nullptr), II_g_try_malloc0_n(nullptr), + II_g_malloc0(nullptr), II_g_realloc(nullptr), II_g_try_malloc(nullptr), + II_g_try_malloc0(nullptr), II_g_try_realloc(nullptr), + II_g_free(nullptr), II_g_memdup(nullptr), II_g_malloc_n(nullptr), + II_g_malloc0_n(nullptr), II_g_realloc_n(nullptr), + II_g_try_malloc_n(nullptr), II_g_try_malloc0_n(nullptr), II_g_try_realloc_n(nullptr) {} /// In pessimistic mode, the checker assumes that it does not know which @@ -248,11 +248,11 @@ private: *II_realloc, *II_calloc, *II_valloc, *II_reallocf, *II_strndup, *II_strdup, *II_win_strdup, *II_kmalloc, *II_if_nameindex, *II_if_freenameindex, *II_wcsdup, - *II_win_wcsdup, *II_g_malloc, *II_g_malloc0, - *II_g_realloc, *II_g_try_malloc, *II_g_try_malloc0, - *II_g_try_realloc, *II_g_free, *II_g_memdup, - *II_g_malloc_n, *II_g_malloc0_n, *II_g_realloc_n, - *II_g_try_malloc_n, *II_g_try_malloc0_n, + *II_win_wcsdup, *II_g_malloc, *II_g_malloc0, + *II_g_realloc, *II_g_try_malloc, *II_g_try_malloc0, + *II_g_try_realloc, *II_g_free, *II_g_memdup, + *II_g_malloc_n, *II_g_malloc0_n, *II_g_realloc_n, + *II_g_try_malloc_n, *II_g_try_malloc0_n, *II_g_try_realloc_n; mutable Optional<uint64_t> KernelZeroFlagVal; @@ -346,7 +346,7 @@ private: ProgramStateRef ReallocMemAux(CheckerContext &C, const CallExpr *CE, bool FreesMemOnFailure, - ProgramStateRef State, + ProgramStateRef State, bool SuffixWithN = false) const; static SVal evalMulForBufferSize(CheckerContext &C, const Expr *Blocks, const Expr *BlockBytes); @@ -652,7 +652,7 @@ bool MallocChecker::isCMemFunction(const FunctionDecl *FD, initIdentifierInfo(C); if (Family == AF_Malloc && CheckFree) { - if (FunI == II_free || FunI == II_realloc || FunI == II_reallocf || + if (FunI == II_free || FunI == II_realloc || FunI == II_reallocf || FunI == II_g_free) return true; } @@ -662,12 +662,12 @@ bool MallocChecker::isCMemFunction(const FunctionDecl *FD, FunI == II_calloc || FunI == II_valloc || FunI == II_strdup || FunI == II_win_strdup || FunI == II_strndup || FunI == II_wcsdup || FunI == II_win_wcsdup || FunI == II_kmalloc || - FunI == II_g_malloc || FunI == II_g_malloc0 || - FunI == II_g_realloc || FunI == II_g_try_malloc || + FunI == II_g_malloc || FunI == II_g_malloc0 || + FunI == II_g_realloc || FunI == II_g_try_malloc || FunI == II_g_try_malloc0 || FunI == II_g_try_realloc || - FunI == II_g_memdup || FunI == II_g_malloc_n || - FunI == II_g_malloc0_n || FunI == II_g_realloc_n || - FunI == II_g_try_malloc_n || FunI == II_g_try_malloc0_n || + FunI == II_g_memdup || FunI == II_g_malloc_n || + FunI == II_g_malloc0_n || FunI == II_g_realloc_n || + FunI == II_g_try_malloc_n || FunI == II_g_try_malloc0_n || FunI == II_g_try_realloc_n) return true; } @@ -873,7 +873,7 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const { return; State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State); State = ProcessZeroAllocation(C, CE, 0, State); - } else if (FunI == II_realloc || FunI == II_g_realloc || + } else if (FunI == II_realloc || FunI == II_g_realloc || FunI == II_g_try_realloc) { State = ReallocMemAux(C, CE, false, State); State = ProcessZeroAllocation(C, CE, 1, State); @@ -936,7 +936,7 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const { return; State = MallocMemAux(C, CE, CE->getArg(1), UndefinedVal(), State); State = ProcessZeroAllocation(C, CE, 1, State); - } else if (FunI == II_g_malloc_n || FunI == II_g_try_malloc_n || + } else if (FunI == II_g_malloc_n || FunI == II_g_try_malloc_n || FunI == II_g_malloc0_n || FunI == II_g_try_malloc0_n) { if (CE->getNumArgs() < 2) return; @@ -2930,6 +2930,11 @@ std::shared_ptr<PathDiagnosticPiece> MallocChecker::MallocBugVisitor::VisitNode( OS << MemCallE->getMethodDecl()->getNameAsString(); } else if (const auto *OpCallE = dyn_cast<CXXOperatorCallExpr>(S)) { OS << OpCallE->getDirectCallee()->getNameAsString(); + } else if (const auto *CallE = dyn_cast<CallExpr>(S)) { + auto &CEMgr = BRC.getStateManager().getCallEventManager(); + CallEventRef<> Call = CEMgr.getSimpleCall(CallE, state, CurrentLC); + const auto *D = dyn_cast_or_null<NamedDecl>(Call->getDecl()); + OS << (D ? D->getNameAsString() : "unknown"); } OS << "'"; } diff --git a/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp b/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp index 2c1e139330d68..9c85c09837239 100644 --- a/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp @@ -4000,7 +4000,7 @@ void RetainCountChecker::checkEndFunction(const ReturnStmt *RS, // Don't process anything within synthesized bodies. const LocationContext *LCtx = Pred->getLocationContext(); if (LCtx->getAnalysisDeclContext()->isBodyAutosynthesized()) { - assert(!LCtx->inTopFrame()); + assert(!LCtx->inTopFrame()); return; } diff --git a/lib/StaticAnalyzer/Checkers/RunLoopAutoreleaseLeakChecker.cpp b/lib/StaticAnalyzer/Checkers/RunLoopAutoreleaseLeakChecker.cpp index 64b61a0213d28..55516a34d1a78 100644 --- a/lib/StaticAnalyzer/Checkers/RunLoopAutoreleaseLeakChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/RunLoopAutoreleaseLeakChecker.cpp @@ -45,9 +45,9 @@ const char * RunLoopBind = "NSRunLoopM"; const char * RunLoopRunBind = "RunLoopRunM"; const char * OtherMsgBind = "OtherMessageSentM"; const char * AutoreleasePoolBind = "AutoreleasePoolM"; +const char * OtherStmtAutoreleasePoolBind = "OtherAutoreleasePoolM"; -class RunLoopAutoreleaseLeakChecker : public Checker< - check::ASTCodeBody> { +class RunLoopAutoreleaseLeakChecker : public Checker<check::ASTCodeBody> { public: void checkASTCodeBody(const Decl *D, @@ -66,6 +66,8 @@ static TriBoolTy seenBeforeRec(const Stmt *Parent, const Stmt *A, const Stmt *B, MemoizationMapTy &Memoization) { for (const Stmt *C : Parent->children()) { + if (!C) continue; + if (C == A) return true; @@ -110,17 +112,20 @@ static void emitDiagnostics(BoundNodes &Match, const auto *AP = Match.getNodeAs<ObjCAutoreleasePoolStmt>(AutoreleasePoolBind); + const auto *OAP = + Match.getNodeAs<ObjCAutoreleasePoolStmt>(OtherStmtAutoreleasePoolBind); bool HasAutoreleasePool = (AP != nullptr); const auto *RL = Match.getNodeAs<ObjCMessageExpr>(RunLoopBind); const auto *RLR = Match.getNodeAs<Stmt>(RunLoopRunBind); assert(RLR && "Run loop launch not found"); - assert(ME != RLR); - if (HasAutoreleasePool && seenBefore(AP, RLR, ME)) + + // Launch of run loop occurs before the message-sent expression is seen. + if (seenBefore(DeclBody, RLR, ME)) return; - if (!HasAutoreleasePool && seenBefore(DeclBody, RLR, ME)) + if (HasAutoreleasePool && (OAP != AP)) return; PathDiagnosticLocation Location = PathDiagnosticLocation::createBegin( @@ -169,7 +174,8 @@ static void checkTempObjectsInSamePool(const Decl *D, AnalysisManager &AM, BugReporter &BR, const RunLoopAutoreleaseLeakChecker *Chkr) { StatementMatcher RunLoopRunM = getRunLoopRunM(); - StatementMatcher OtherMessageSentM = getOtherMessageSentM(); + StatementMatcher OtherMessageSentM = getOtherMessageSentM( + hasAncestor(autoreleasePoolStmt().bind(OtherStmtAutoreleasePoolBind))); StatementMatcher RunLoopInAutorelease = autoreleasePoolStmt( diff --git a/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp b/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp index feae9e59b343a..e7a20fa03a4ae 100644 --- a/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp @@ -129,7 +129,7 @@ bool StackAddrEscapeChecker::isSemaphoreCaptured(const BlockDecl &B) const { for (const auto &C : B.captures()) { const auto *T = C.getVariable()->getType()->getAs<TypedefType>(); if (T && T->getDecl()->getIdentifier() == dispatch_semaphore_tII) - return true; + return true; } return false; } @@ -175,9 +175,9 @@ void StackAddrEscapeChecker::checkAsyncExecutedBlockCaptures( // There is a not-too-uncommon idiom // where a block passed to dispatch_async captures a semaphore // and then the thread (which called dispatch_async) is blocked on waiting - // for the completion of the execution of the block - // via dispatch_semaphore_wait. To avoid false-positives (for now) - // we ignore all the blocks which have captured + // for the completion of the execution of the block + // via dispatch_semaphore_wait. To avoid false-positives (for now) + // we ignore all the blocks which have captured // a variable of the type "dispatch_semaphore_t". if (isSemaphoreCaptured(*B.getDecl())) return; @@ -263,7 +263,7 @@ void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS, if (const BlockDataRegion *B = dyn_cast<BlockDataRegion>(R)) checkReturnedBlockCaptures(*B, C); - if (!isa<StackSpaceRegion>(R->getMemorySpace()) || + if (!isa<StackSpaceRegion>(R->getMemorySpace()) || isNotInCurrentFrame(R, C) || isArcManagedBlock(R, C)) return; diff --git a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp index c87bc685d8b96..d4d33c1746ced 100644 --- a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp +++ b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp @@ -269,6 +269,8 @@ namespace { /// pointer dereference outside. class NoStoreFuncVisitor final : public BugReporterVisitor { const SubRegion *RegionOfInterest; + const SourceManager &SM; + const PrintingPolicy &PP; static constexpr const char *DiagnosticsMsg = "Returning without writing to '"; @@ -284,7 +286,10 @@ class NoStoreFuncVisitor final : public BugReporterVisitor { llvm::SmallPtrSet<const StackFrameContext *, 32> FramesModifyingCalculated; public: - NoStoreFuncVisitor(const SubRegion *R) : RegionOfInterest(R) {} + NoStoreFuncVisitor(const SubRegion *R) + : RegionOfInterest(R), + SM(R->getMemRegionManager()->getContext().getSourceManager()), + PP(R->getMemRegionManager()->getContext().getPrintingPolicy()) {} void Profile(llvm::FoldingSetNodeID &ID) const override { static int Tag = 0; @@ -307,8 +312,6 @@ public: CallEventRef<> Call = BRC.getStateManager().getCallEventManager().getCaller(SCtx, State); - const PrintingPolicy &PP = BRC.getASTContext().getPrintingPolicy(); - const SourceManager &SM = BRC.getSourceManager(); // Region of interest corresponds to an IVar, exiting a method // which could have written into that IVar, but did not. @@ -318,16 +321,14 @@ public: IvarR->getDecl()) && !isRegionOfInterestModifiedInFrame(N)) return notModifiedMemberDiagnostics( - Ctx, SM, PP, *CallExitLoc, Call, - MC->getReceiverSVal().getAsRegion()); + Ctx, *CallExitLoc, Call, MC->getReceiverSVal().getAsRegion()); if (const auto *CCall = dyn_cast<CXXConstructorCall>(Call)) { - const MemRegion *ThisRegion = CCall->getCXXThisVal().getAsRegion(); - if (RegionOfInterest->isSubRegionOf(ThisRegion) + const MemRegion *ThisR = CCall->getCXXThisVal().getAsRegion(); + if (RegionOfInterest->isSubRegionOf(ThisR) && !CCall->getDecl()->isImplicit() && !isRegionOfInterestModifiedInFrame(N)) - return notModifiedMemberDiagnostics(Ctx, SM, PP, *CallExitLoc, - CCall, ThisRegion); + return notModifiedMemberDiagnostics(Ctx, *CallExitLoc, Call, ThisR); } ArrayRef<ParmVarDecl *> parameters = getCallParameters(Call); @@ -344,7 +345,7 @@ public: return nullptr; return notModifiedParameterDiagnostics( - Ctx, SM, PP, *CallExitLoc, Call, PVD, R, IndirectionLevel); + Ctx, *CallExitLoc, Call, PVD, R, IndirectionLevel); } QualType PT = T->getPointeeType(); if (PT.isNull() || PT->isVoidType()) break; @@ -446,8 +447,6 @@ private: /// in a given function. std::shared_ptr<PathDiagnosticPiece> notModifiedMemberDiagnostics( const LocationContext *Ctx, - const SourceManager &SM, - const PrintingPolicy &PP, CallExitBegin &CallExitLoc, CallEventRef<> Call, const MemRegion *ArgRegion) { @@ -474,8 +473,6 @@ private: /// before we get to the super region of \c RegionOfInterest std::shared_ptr<PathDiagnosticPiece> notModifiedParameterDiagnostics(const LocationContext *Ctx, - const SourceManager &SM, - const PrintingPolicy &PP, CallExitBegin &CallExitLoc, CallEventRef<> Call, const ParmVarDecl *PVD, @@ -612,8 +609,7 @@ public: const ExplodedNode *N, const MemRegion *R, bool EnableNullFPSuppression, BugReport &BR, const SVal V) { - AnalyzerOptions &Options = N->getState()->getStateManager() - .getOwningEngine()->getAnalysisManager().options; + AnalyzerOptions &Options = N->getState()->getAnalysisManager().options; if (EnableNullFPSuppression && Options.shouldSuppressNullReturnPaths() && V.getAs<Loc>()) BR.addVisitor(llvm::make_unique<MacroNullReturnSuppressionVisitor>( @@ -740,9 +736,7 @@ public: RetVal = State->getSVal(*LValue); // See if the return value is NULL. If so, suppress the report. - SubEngine *Eng = State->getStateManager().getOwningEngine(); - assert(Eng && "Cannot file a bug report without an owning engine"); - AnalyzerOptions &Options = Eng->getAnalysisManager().options; + AnalyzerOptions &Options = State->getAnalysisManager().options; bool EnableNullFPSuppression = false; if (InEnableNullFPSuppression && Options.shouldSuppressNullReturnPaths()) @@ -1321,9 +1315,7 @@ SuppressInlineDefensiveChecksVisitor:: SuppressInlineDefensiveChecksVisitor(DefinedSVal Value, const ExplodedNode *N) : V(Value) { // Check if the visitor is disabled. - SubEngine *Eng = N->getState()->getStateManager().getOwningEngine(); - assert(Eng && "Cannot file a bug report without an owning engine"); - AnalyzerOptions &Options = Eng->getAnalysisManager().options; + AnalyzerOptions &Options = N->getState()->getAnalysisManager().options; if (!Options.shouldSuppressInlinedDefensiveChecks()) IsSatisfied = true; @@ -1603,6 +1595,7 @@ bool bugreporter::trackNullOrUndefValue(const ExplodedNode *N, LVNode->getSVal(Inner).getAsRegion(); if (R) { + // Mark both the variable region and its contents as interesting. SVal V = LVState->getRawSVal(loc::MemRegionVal(R)); report.addVisitor( diff --git a/lib/StaticAnalyzer/Core/CallEvent.cpp b/lib/StaticAnalyzer/Core/CallEvent.cpp index 8db7b06f186da..fe9260e32dd8b 100644 --- a/lib/StaticAnalyzer/Core/CallEvent.cpp +++ b/lib/StaticAnalyzer/Core/CallEvent.cpp @@ -27,6 +27,7 @@ #include "clang/AST/Type.h" #include "clang/Analysis/AnalysisDeclContext.h" #include "clang/Analysis/CFG.h" +#include "clang/Analysis/CFGStmtMap.h" #include "clang/Analysis/ProgramPoint.h" #include "clang/CrossTU/CrossTranslationUnit.h" #include "clang/Basic/IdentifierTable.h" @@ -166,6 +167,68 @@ bool CallEvent::isGlobalCFunction(StringRef FunctionName) const { return CheckerContext::isCLibraryFunction(FD, FunctionName); } +AnalysisDeclContext *CallEvent::getCalleeAnalysisDeclContext() const { + const Decl *D = getDecl(); + + // If the callee is completely unknown, we cannot construct the stack frame. + if (!D) + return nullptr; + + // FIXME: Skip virtual functions for now. There's no easy procedure to foresee + // the exact decl that should be used, especially when it's not a definition. + if (const Decl *RD = getRuntimeDefinition().getDecl()) + if (RD != D) + return nullptr; + + return LCtx->getAnalysisDeclContext()->getManager()->getContext(D); +} + +const StackFrameContext *CallEvent::getCalleeStackFrame() const { + AnalysisDeclContext *ADC = getCalleeAnalysisDeclContext(); + if (!ADC) + return nullptr; + + const Expr *E = getOriginExpr(); + if (!E) + return nullptr; + + // Recover CFG block via reverse lookup. + // TODO: If we were to keep CFG element information as part of the CallEvent + // instead of doing this reverse lookup, we would be able to build the stack + // frame for non-expression-based calls, and also we wouldn't need the reverse + // lookup. + CFGStmtMap *Map = LCtx->getAnalysisDeclContext()->getCFGStmtMap(); + const CFGBlock *B = Map->getBlock(E); + assert(B); + + // Also recover CFG index by scanning the CFG block. + unsigned Idx = 0, Sz = B->size(); + for (; Idx < Sz; ++Idx) + if (auto StmtElem = (*B)[Idx].getAs<CFGStmt>()) + if (StmtElem->getStmt() == E) + break; + assert(Idx < Sz); + + return ADC->getManager()->getStackFrame(ADC, LCtx, E, B, Idx); +} + +const VarRegion *CallEvent::getParameterLocation(unsigned Index) const { + const StackFrameContext *SFC = getCalleeStackFrame(); + // We cannot construct a VarRegion without a stack frame. + if (!SFC) + return nullptr; + + const ParmVarDecl *PVD = parameters()[Index]; + const VarRegion *VR = + State->getStateManager().getRegionManager().getVarRegion(PVD, SFC); + + // This sanity check would fail if our parameter declaration doesn't + // correspond to the stack frame's function declaration. + assert(VR->getStackFrame() == SFC); + + return VR; +} + /// Returns true if a type is a pointer-to-const or reference-to-const /// with no further indirection. static bool isPointerToConst(QualType Ty) { diff --git a/lib/StaticAnalyzer/Core/ExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngine.cpp index 188316c096e38..2b4bdd754fdbc 100644 --- a/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -117,56 +117,42 @@ STATISTIC(NumTimesRetriedWithoutInlining, /// the construction context was present and contained references to these /// AST nodes. class ConstructedObjectKey { - typedef std::pair< - llvm::PointerUnion<const Stmt *, const CXXCtorInitializer *>, - const LocationContext *> ConstructedObjectKeyImpl; + typedef std::pair<ConstructionContextItem, const LocationContext *> + ConstructedObjectKeyImpl; - ConstructedObjectKeyImpl Impl; + const ConstructedObjectKeyImpl Impl; const void *getAnyASTNodePtr() const { - if (const Stmt *S = getStmt()) + if (const Stmt *S = getItem().getStmtOrNull()) return S; else - return getCXXCtorInitializer(); + return getItem().getCXXCtorInitializer(); } public: - ConstructedObjectKey( - llvm::PointerUnion<const Stmt *, const CXXCtorInitializer *> P, - const LocationContext *LC) - : Impl(P, LC) { - // This is the full list of statements that require additional actions when - // encountered. This list may be expanded when new actions are implemented. - assert(getCXXCtorInitializer() || isa<DeclStmt>(getStmt()) || - isa<CXXNewExpr>(getStmt()) || isa<CXXBindTemporaryExpr>(getStmt()) || - isa<MaterializeTemporaryExpr>(getStmt()) || - isa<CXXConstructExpr>(getStmt())); - } - - const Stmt *getStmt() const { - return Impl.first.dyn_cast<const Stmt *>(); - } + explicit ConstructedObjectKey(const ConstructionContextItem &Item, + const LocationContext *LC) + : Impl(Item, LC) {} - const CXXCtorInitializer *getCXXCtorInitializer() const { - return Impl.first.dyn_cast<const CXXCtorInitializer *>(); - } - - const LocationContext *getLocationContext() const { - return Impl.second; - } + const ConstructionContextItem &getItem() const { return Impl.first; } + const LocationContext *getLocationContext() const { return Impl.second; } void print(llvm::raw_ostream &OS, PrinterHelper *Helper, PrintingPolicy &PP) { - OS << '(' << getLocationContext() << ',' << getAnyASTNodePtr() << ") "; - if (const Stmt *S = getStmt()) { + OS << '(' << getLocationContext() << ',' << getAnyASTNodePtr() << ',' + << getItem().getKindAsString(); + if (getItem().getKind() == ConstructionContextItem::ArgumentKind) + OS << " #" << getItem().getIndex(); + OS << ") "; + if (const Stmt *S = getItem().getStmtOrNull()) { S->printPretty(OS, Helper, PP); } else { - const CXXCtorInitializer *I = getCXXCtorInitializer(); + const CXXCtorInitializer *I = getItem().getCXXCtorInitializer(); OS << I->getAnyMember()->getNameAsString(); } } void Profile(llvm::FoldingSetNodeID &ID) const { - ID.AddPointer(Impl.first.getOpaqueValue()); + ID.Add(Impl.first); ID.AddPointer(Impl.second); } @@ -184,15 +170,6 @@ typedef llvm::ImmutableMap<ConstructedObjectKey, SVal> REGISTER_TRAIT_WITH_PROGRAMSTATE(ObjectsUnderConstruction, ObjectsUnderConstructionMap) -// Additionally, track a set of destructors that correspond to elided -// constructors when copy elision occurs. -typedef std::pair<const CXXBindTemporaryExpr *, const LocationContext *> - ElidedDestructorItem; -typedef llvm::ImmutableSet<ElidedDestructorItem> - ElidedDestructorSet; -REGISTER_TRAIT_WITH_PROGRAMSTATE(ElidedDestructors, - ElidedDestructorSet) - //===----------------------------------------------------------------------===// // Engine construction and deletion. //===----------------------------------------------------------------------===// @@ -449,31 +426,32 @@ ExprEngine::createTemporaryRegionIfNeeded(ProgramStateRef State, return State; } -ProgramStateRef ExprEngine::addObjectUnderConstruction( - ProgramStateRef State, - llvm::PointerUnion<const Stmt *, const CXXCtorInitializer *> P, - const LocationContext *LC, SVal V) { - ConstructedObjectKey Key(P, LC->getStackFrame()); +ProgramStateRef +ExprEngine::addObjectUnderConstruction(ProgramStateRef State, + const ConstructionContextItem &Item, + const LocationContext *LC, SVal V) { + ConstructedObjectKey Key(Item, LC->getStackFrame()); // FIXME: Currently the state might already contain the marker due to // incorrect handling of temporaries bound to default parameters. assert(!State->get<ObjectsUnderConstruction>(Key) || - isa<CXXBindTemporaryExpr>(Key.getStmt())); + Key.getItem().getKind() == + ConstructionContextItem::TemporaryDestructorKind); return State->set<ObjectsUnderConstruction>(Key, V); } -Optional<SVal> ExprEngine::getObjectUnderConstruction( - ProgramStateRef State, - llvm::PointerUnion<const Stmt *, const CXXCtorInitializer *> P, - const LocationContext *LC) { - ConstructedObjectKey Key(P, LC->getStackFrame()); +Optional<SVal> +ExprEngine::getObjectUnderConstruction(ProgramStateRef State, + const ConstructionContextItem &Item, + const LocationContext *LC) { + ConstructedObjectKey Key(Item, LC->getStackFrame()); return Optional<SVal>::create(State->get<ObjectsUnderConstruction>(Key)); } -ProgramStateRef ExprEngine::finishObjectConstruction( - ProgramStateRef State, - llvm::PointerUnion<const Stmt *, const CXXCtorInitializer *> P, - const LocationContext *LC) { - ConstructedObjectKey Key(P, LC->getStackFrame()); +ProgramStateRef +ExprEngine::finishObjectConstruction(ProgramStateRef State, + const ConstructionContextItem &Item, + const LocationContext *LC) { + ConstructedObjectKey Key(Item, LC->getStackFrame()); assert(State->contains<ObjectsUnderConstruction>(Key)); return State->remove<ObjectsUnderConstruction>(Key); } @@ -481,25 +459,26 @@ ProgramStateRef ExprEngine::finishObjectConstruction( ProgramStateRef ExprEngine::elideDestructor(ProgramStateRef State, const CXXBindTemporaryExpr *BTE, const LocationContext *LC) { - ElidedDestructorItem I(BTE, LC); - assert(!State->contains<ElidedDestructors>(I)); - return State->add<ElidedDestructors>(I); + ConstructedObjectKey Key({BTE, /*IsElided=*/true}, LC); + // FIXME: Currently the state might already contain the marker due to + // incorrect handling of temporaries bound to default parameters. + return State->set<ObjectsUnderConstruction>(Key, UnknownVal()); } ProgramStateRef ExprEngine::cleanupElidedDestructor(ProgramStateRef State, const CXXBindTemporaryExpr *BTE, const LocationContext *LC) { - ElidedDestructorItem I(BTE, LC); - assert(State->contains<ElidedDestructors>(I)); - return State->remove<ElidedDestructors>(I); + ConstructedObjectKey Key({BTE, /*IsElided=*/true}, LC); + assert(State->contains<ObjectsUnderConstruction>(Key)); + return State->remove<ObjectsUnderConstruction>(Key); } bool ExprEngine::isDestructorElided(ProgramStateRef State, const CXXBindTemporaryExpr *BTE, const LocationContext *LC) { - ElidedDestructorItem I(BTE, LC); - return State->contains<ElidedDestructors>(I); + ConstructedObjectKey Key({BTE, /*IsElided=*/true}, LC); + return State->contains<ObjectsUnderConstruction>(Key); } bool ExprEngine::areAllObjectsFullyConstructed(ProgramStateRef State, @@ -512,10 +491,6 @@ bool ExprEngine::areAllObjectsFullyConstructed(ProgramStateRef State, if (I.first.getLocationContext() == LC) return false; - for (auto I: State->get<ElidedDestructors>()) - if (I.second == LC) - return false; - LC = LC->getParent(); } return true; @@ -560,14 +535,6 @@ static void printObjectsUnderConstructionForContext(raw_ostream &Out, Key.print(Out, nullptr, PP); Out << " : " << Value << NL; } - - for (auto I : State->get<ElidedDestructors>()) { - if (I.second != LC) - continue; - Out << '(' << I.second << ',' << (const void *)I.first << ") "; - I.first->printPretty(Out, nullptr, PP); - Out << " : (constructor elided)" << NL; - } } void ExprEngine::printState(raw_ostream &Out, ProgramStateRef State, @@ -2252,25 +2219,14 @@ void ExprEngine::processEndOfFunction(NodeBuilderContext& BC, for (auto I : State->get<ObjectsUnderConstruction>()) if (I.first.getLocationContext() == LC) { // The comment above only pardons us for not cleaning up a - // CXXBindTemporaryExpr. If any other statements are found here, + // temporary destructor. If any other statements are found here, // it must be a separate problem. - assert(isa<CXXBindTemporaryExpr>(I.first.getStmt())); + assert(I.first.getItem().getKind() == + ConstructionContextItem::TemporaryDestructorKind || + I.first.getItem().getKind() == + ConstructionContextItem::ElidedDestructorKind); State = State->remove<ObjectsUnderConstruction>(I.first); - // Also cleanup the elided destructor info. - ElidedDestructorItem Item( - cast<CXXBindTemporaryExpr>(I.first.getStmt()), - I.first.getLocationContext()); - State = State->remove<ElidedDestructors>(Item); } - - // Also suppress the assertion for elided destructors when temporary - // destructors are not provided at all by the CFG, because there's no - // good place to clean them up. - if (!AMgr.getAnalyzerOptions().includeTemporaryDtorsInCFG()) - for (auto I : State->get<ElidedDestructors>()) - if (I.second == LC) - State = State->remove<ElidedDestructors>(I); - LC = LC->getParent(); } if (State != Pred->getState()) { @@ -2338,7 +2294,7 @@ void ExprEngine::processSwitch(SwitchNodeBuilder& builder) { // Evaluate the LHS of the case value. llvm::APSInt V1 = Case->getLHS()->EvaluateKnownConstInt(getContext()); assert(V1.getBitWidth() == getContext().getIntWidth(CondE->getType())); - + // Get the RHS of the case, if it exists. llvm::APSInt V2; if (const Expr *E = Case->getRHS()) @@ -2538,12 +2494,12 @@ void ExprEngine::VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred, ExplodedNodeSet CheckedSet; getCheckerManager().runCheckersForPreStmt(CheckedSet, Pred, M, *this); - ExplodedNodeSet EvalSet; - ValueDecl *Member = M->getMemberDecl(); + ExplodedNodeSet EvalSet; + ValueDecl *Member = M->getMemberDecl(); // Handle static member variables and enum constants accessed via // member syntax. - if (isa<VarDecl>(Member) || isa<EnumConstantDecl>(Member)) { + if (isa<VarDecl>(Member) || isa<EnumConstantDecl>(Member)) { for (const auto I : CheckedSet) VisitCommonDeclRefExpr(M, Member, I, EvalSet); } else { diff --git a/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/lib/StaticAnalyzer/Core/ExprEngineC.cpp index c7b1a9ac82f0b..61b7a290e42a9 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineC.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineC.cpp @@ -684,7 +684,7 @@ void ExprEngine::VisitLogicalExpr(const BinaryOperator* B, ExplodedNode *Pred, // known to be false, 1 if the value is known to be true and a new symbol // when the assumption is unknown. nonloc::ConcreteInt Zero(getBasicVals().getValue(0, B->getType())); - X = evalBinOp(N->getState(), BO_NE, + X = evalBinOp(N->getState(), BO_NE, svalBuilder.evalCast(RHSVal, B->getType(), RHS->getType()), Zero, B->getType()); } diff --git a/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp index dc124fc3ff2d2..4f1766a813c6d 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -221,25 +221,42 @@ std::pair<ProgramStateRef, SVal> ExprEngine::prepareForObjectConstruction( // and construct directly into the final object. This call // also sets the CallOpts flags for us. SVal V; + // If the elided copy/move constructor is not supported, there's still + // benefit in trying to model the non-elided constructor. + // Stash our state before trying to elide, as it'll get overwritten. + ProgramStateRef PreElideState = State; + EvalCallOptions PreElideCallOpts = CallOpts; + std::tie(State, V) = prepareForObjectConstruction( CE, State, LCtx, TCC->getConstructionContextAfterElision(), CallOpts); - // Remember that we've elided the constructor. - State = addObjectUnderConstruction(State, CE, LCtx, V); + // FIXME: This definition of "copy elision has not failed" is unreliable. + // It doesn't indicate that the constructor will actually be inlined + // later; it is still up to evalCall() to decide. + if (!CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion) { + // Remember that we've elided the constructor. + State = addObjectUnderConstruction(State, CE, LCtx, V); - // Remember that we've elided the destructor. - if (BTE) - State = elideDestructor(State, BTE, LCtx); + // Remember that we've elided the destructor. + if (BTE) + State = elideDestructor(State, BTE, LCtx); - // Instead of materialization, shamelessly return - // the final object destination. - if (MTE) - State = addObjectUnderConstruction(State, MTE, LCtx, V); + // Instead of materialization, shamelessly return + // the final object destination. + if (MTE) + State = addObjectUnderConstruction(State, MTE, LCtx, V); - return std::make_pair(State, V); + return std::make_pair(State, V); + } else { + // Copy elision failed. Revert the changes and proceed as if we have + // a simple temporary. + State = PreElideState; + CallOpts = PreElideCallOpts; + } + LLVM_FALLTHROUGH; } case ConstructionContext::SimpleTemporaryObjectKind: { - const auto *TCC = cast<SimpleTemporaryObjectConstructionContext>(CC); + const auto *TCC = cast<TemporaryObjectConstructionContext>(CC); const CXXBindTemporaryExpr *BTE = TCC->getCXXBindTemporaryExpr(); const MaterializeTemporaryExpr *MTE = TCC->getMaterializedTemporaryExpr(); SVal V = UnknownVal(); @@ -274,6 +291,10 @@ std::pair<ProgramStateRef, SVal> ExprEngine::prepareForObjectConstruction( CallOpts.IsTemporaryCtorOrDtor = true; return std::make_pair(State, V); } + case ConstructionContext::ArgumentKind: { + // Function argument constructors. Not implemented yet. + break; + } } } // If we couldn't find an existing region to construct into, assume we're diff --git a/lib/StaticAnalyzer/Core/IssueHash.cpp b/lib/StaticAnalyzer/Core/IssueHash.cpp index 274ebe7a941bc..6c55c61dd399a 100644 --- a/lib/StaticAnalyzer/Core/IssueHash.cpp +++ b/lib/StaticAnalyzer/Core/IssueHash.cpp @@ -26,7 +26,7 @@ using namespace clang; -// Get a string representation of the parts of the signature that can be +// Get a string representation of the parts of the signature that can be // overloaded on. static std::string GetSignature(const FunctionDecl *Target) { if (!Target) diff --git a/lib/StaticAnalyzer/Core/ProgramState.cpp b/lib/StaticAnalyzer/Core/ProgramState.cpp index 2b401607293b7..94e2e00d8bbc5 100644 --- a/lib/StaticAnalyzer/Core/ProgramState.cpp +++ b/lib/StaticAnalyzer/Core/ProgramState.cpp @@ -11,6 +11,7 @@ // //===----------------------------------------------------------------------===// +#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/Analysis/CFG.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" @@ -494,6 +495,10 @@ void ProgramState::dumpTaint() const { printTaint(llvm::errs()); } +AnalysisManager& ProgramState::getAnalysisManager() const { + return stateMgr->getOwningEngine()->getAnalysisManager(); +} + //===----------------------------------------------------------------------===// // Generic Data Map. //===----------------------------------------------------------------------===// diff --git a/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp b/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp index beae0dfae2896..62c54fc956a96 100644 --- a/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp +++ b/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp @@ -440,7 +440,7 @@ static bool shouldRearrange(ProgramStateRef State, BinaryOperator::Opcode Op, SymbolRef Sym, llvm::APSInt Int, QualType Ty) { return Sym->getType() == Ty && (!BinaryOperator::isComparisonOp(Op) || - (isWithinConstantOverflowBounds(Sym, State) && + (isWithinConstantOverflowBounds(Sym, State) && isWithinConstantOverflowBounds(Int))); } @@ -1236,11 +1236,21 @@ SVal SimpleSValBuilder::simplifySVal(ProgramStateRef State, SVal V) { return Sym == Val.getAsSymbol(); } + SVal cache(SymbolRef Sym, SVal V) { + Cached[Sym] = V; + return V; + } + + SVal skip(SymbolRef Sym) { + return cache(Sym, SVB.makeSymbolVal(Sym)); + } + public: Simplifier(ProgramStateRef State) : State(State), SVB(State->getStateManager().getSValBuilder()) {} SVal VisitSymbolData(const SymbolData *S) { + // No cache here. if (const llvm::APSInt *I = SVB.getKnownValue(State, SVB.makeSymbolVal(S))) return Loc::isLocType(S->getType()) ? (SVal)SVB.makeIntLocVal(*I) @@ -1257,11 +1267,9 @@ SVal SimpleSValBuilder::simplifySVal(ProgramStateRef State, SVal V) { return I->second; SVal LHS = Visit(S->getLHS()); - if (isUnchanged(S->getLHS(), LHS)) { - SVal V = SVB.makeSymbolVal(S); - Cached[S] = V; - return V; - } + if (isUnchanged(S->getLHS(), LHS)) + return skip(S); + SVal RHS; // By looking at the APSInt in the right-hand side of S, we cannot // figure out if it should be treated as a Loc or as a NonLoc. @@ -1281,9 +1289,8 @@ SVal SimpleSValBuilder::simplifySVal(ProgramStateRef State, SVal V) { RHS = SVB.makeIntVal(S->getRHS()); } - SVal V = SVB.evalBinOp(State, S->getOpcode(), LHS, RHS, S->getType()); - Cached[S] = V; - return V; + return cache( + S, SVB.evalBinOp(State, S->getOpcode(), LHS, RHS, S->getType())); } SVal VisitSymSymExpr(const SymSymExpr *S) { @@ -1291,16 +1298,21 @@ SVal SimpleSValBuilder::simplifySVal(ProgramStateRef State, SVal V) { if (I != Cached.end()) return I->second; + // For now don't try to simplify mixed Loc/NonLoc expressions + // because they often appear from LocAsInteger operations + // and we don't know how to combine a LocAsInteger + // with a concrete value. + if (Loc::isLocType(S->getLHS()->getType()) != + Loc::isLocType(S->getRHS()->getType())) + return skip(S); + SVal LHS = Visit(S->getLHS()); SVal RHS = Visit(S->getRHS()); - if (isUnchanged(S->getLHS(), LHS) && isUnchanged(S->getRHS(), RHS)) { - SVal V = SVB.makeSymbolVal(S); - Cached[S] = V; - return V; - } - SVal V = SVB.evalBinOp(State, S->getOpcode(), LHS, RHS, S->getType()); - Cached[S] = V; - return V; + if (isUnchanged(S->getLHS(), LHS) && isUnchanged(S->getRHS(), RHS)) + return skip(S); + + return cache( + S, SVB.evalBinOp(State, S->getOpcode(), LHS, RHS, S->getType())); } SVal VisitSymExpr(SymbolRef S) { return nonloc::SymbolVal(S); } diff --git a/lib/StaticAnalyzer/Core/Store.cpp b/lib/StaticAnalyzer/Core/Store.cpp index 5ab5c082269ba..94188a9ef698f 100644 --- a/lib/StaticAnalyzer/Core/Store.cpp +++ b/lib/StaticAnalyzer/Core/Store.cpp @@ -448,10 +448,10 @@ SVal StoreManager::getLValueElement(QualType elementType, NonLoc Offset, // value. See also the similar FIXME in getLValueFieldOrIvar(). if (Base.isUnknownOrUndef() || Base.getAs<loc::ConcreteInt>()) return Base; - + if (Base.getAs<loc::GotoLabel>()) return UnknownVal(); - + const SubRegion *BaseRegion = Base.castAs<loc::MemRegionVal>().getRegionAs<SubRegion>(); |