diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2017-04-16 16:02:28 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2017-04-16 16:02:28 +0000 |
commit | 7442d6faa2719e4e7d33a7021c406c5a4facd74d (patch) | |
tree | c72b9241553fc9966179aba84f90f17bfa9235c3 /lib/StaticAnalyzer | |
parent | b52119637f743680a99710ce5fdb6646da2772af (diff) | |
download | src-7442d6faa2719e4e7d33a7021c406c5a4facd74d.tar.gz src-7442d6faa2719e4e7d33a7021c406c5a4facd74d.zip |
Vendor import of clang trunk r300422:vendor/clang/clang-trunk-r300422
Notes
Notes:
svn path=/vendor/clang/dist/; revision=317019
svn path=/vendor/clang/clang-trunk-r300422/; revision=317020; tag=vendor/clang/clang-trunk-r300422
Diffstat (limited to 'lib/StaticAnalyzer')
46 files changed, 3393 insertions, 696 deletions
diff --git a/lib/StaticAnalyzer/Checkers/AnalysisOrderChecker.cpp b/lib/StaticAnalyzer/Checkers/AnalysisOrderChecker.cpp index e6592a285e47..90d5c0e36a47 100644 --- a/lib/StaticAnalyzer/Checkers/AnalysisOrderChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/AnalysisOrderChecker.cpp @@ -24,16 +24,29 @@ using namespace ento; namespace { -class AnalysisOrderChecker : public Checker< check::PreStmt<CastExpr>, - check::PostStmt<CastExpr>, - check::PreStmt<ArraySubscriptExpr>, - check::PostStmt<ArraySubscriptExpr>> { - bool isCallbackEnabled(CheckerContext &C, StringRef CallbackName) const { - AnalyzerOptions &Opts = C.getAnalysisManager().getAnalyzerOptions(); +class AnalysisOrderChecker + : public Checker<check::PreStmt<CastExpr>, + check::PostStmt<CastExpr>, + check::PreStmt<ArraySubscriptExpr>, + check::PostStmt<ArraySubscriptExpr>, + check::Bind, + check::RegionChanges> { + bool isCallbackEnabled(AnalyzerOptions &Opts, StringRef CallbackName) const { return Opts.getBooleanOption("*", false, this) || Opts.getBooleanOption(CallbackName, false, this); } + bool isCallbackEnabled(CheckerContext &C, StringRef CallbackName) const { + AnalyzerOptions &Opts = C.getAnalysisManager().getAnalyzerOptions(); + return isCallbackEnabled(Opts, CallbackName); + } + + bool isCallbackEnabled(ProgramStateRef State, StringRef CallbackName) const { + AnalyzerOptions &Opts = State->getStateManager().getOwningEngine() + ->getAnalysisManager().getAnalyzerOptions(); + return isCallbackEnabled(Opts, CallbackName); + } + public: void checkPreStmt(const CastExpr *CE, CheckerContext &C) const { if (isCallbackEnabled(C, "PreStmtCastExpr")) @@ -47,17 +60,35 @@ public: << ")\n"; } - void checkPreStmt(const ArraySubscriptExpr *SubExpr, CheckerContext &C) const { + void checkPreStmt(const ArraySubscriptExpr *SubExpr, + CheckerContext &C) const { if (isCallbackEnabled(C, "PreStmtArraySubscriptExpr")) llvm::errs() << "PreStmt<ArraySubscriptExpr>\n"; } - void checkPostStmt(const ArraySubscriptExpr *SubExpr, CheckerContext &C) const { + void checkPostStmt(const ArraySubscriptExpr *SubExpr, + CheckerContext &C) const { if (isCallbackEnabled(C, "PostStmtArraySubscriptExpr")) llvm::errs() << "PostStmt<ArraySubscriptExpr>\n"; } + + void checkBind(SVal Loc, SVal Val, const Stmt *S, CheckerContext &C) const { + if (isCallbackEnabled(C, "Bind")) + llvm::errs() << "Bind\n"; + } + + ProgramStateRef + checkRegionChanges(ProgramStateRef State, + const InvalidatedSymbols *Invalidated, + ArrayRef<const MemRegion *> ExplicitRegions, + ArrayRef<const MemRegion *> Regions, + const LocationContext *LCtx, const CallEvent *Call) const { + if (isCallbackEnabled(State, "RegionChanges")) + llvm::errs() << "RegionChanges\n"; + return State; + } }; -} +} // end anonymous namespace //===----------------------------------------------------------------------===// // Registration. diff --git a/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp b/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp index 082a4873217b..d19630eeef77 100644 --- a/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp @@ -29,7 +29,9 @@ namespace { class BlockInCriticalSectionChecker : public Checker<check::PostCall, check::PreCall> { - CallDescription LockFn, UnlockFn, SleepFn, GetcFn, FgetsFn, ReadFn, RecvFn; + CallDescription LockFn, UnlockFn, SleepFn, GetcFn, FgetsFn, ReadFn, RecvFn, + PthreadLockFn, PthreadTryLockFn, PthreadUnlockFn, + MtxLock, MtxTimedLock, MtxTryLock, MtxUnlock; std::unique_ptr<BugType> BlockInCritSectionBugType; @@ -40,6 +42,10 @@ class BlockInCriticalSectionChecker : public Checker<check::PostCall, public: BlockInCriticalSectionChecker(); + bool isBlockingFunction(const CallEvent &Call) const; + bool isLockFunction(const CallEvent &Call) const; + bool isUnlockFunction(const CallEvent &Call) const; + void checkPreCall(const CallEvent &Call, CheckerContext &C) const; /// Process unlock. @@ -55,34 +61,69 @@ REGISTER_TRAIT_WITH_PROGRAMSTATE(MutexCounter, unsigned) BlockInCriticalSectionChecker::BlockInCriticalSectionChecker() : LockFn("lock"), UnlockFn("unlock"), SleepFn("sleep"), GetcFn("getc"), - FgetsFn("fgets"), ReadFn("read"), RecvFn("recv") { + FgetsFn("fgets"), ReadFn("read"), RecvFn("recv"), + PthreadLockFn("pthread_mutex_lock"), + PthreadTryLockFn("pthread_mutex_trylock"), + PthreadUnlockFn("pthread_mutex_unlock"), + MtxLock("mtx_lock"), + MtxTimedLock("mtx_timedlock"), + MtxTryLock("mtx_trylock"), + MtxUnlock("mtx_unlock") { // Initialize the bug type. BlockInCritSectionBugType.reset( new BugType(this, "Call to blocking function in critical section", "Blocking Error")); } +bool BlockInCriticalSectionChecker::isBlockingFunction(const CallEvent &Call) const { + if (Call.isCalled(SleepFn) + || Call.isCalled(GetcFn) + || Call.isCalled(FgetsFn) + || Call.isCalled(ReadFn) + || Call.isCalled(RecvFn)) { + return true; + } + return false; +} + +bool BlockInCriticalSectionChecker::isLockFunction(const CallEvent &Call) const { + if (Call.isCalled(LockFn) + || Call.isCalled(PthreadLockFn) + || Call.isCalled(PthreadTryLockFn) + || Call.isCalled(MtxLock) + || Call.isCalled(MtxTimedLock) + || Call.isCalled(MtxTryLock)) { + return true; + } + return false; +} + +bool BlockInCriticalSectionChecker::isUnlockFunction(const CallEvent &Call) const { + if (Call.isCalled(UnlockFn) + || Call.isCalled(PthreadUnlockFn) + || Call.isCalled(MtxUnlock)) { + return true; + } + return false; +} + void BlockInCriticalSectionChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { } void BlockInCriticalSectionChecker::checkPostCall(const CallEvent &Call, CheckerContext &C) const { - if (!Call.isCalled(LockFn) - && !Call.isCalled(SleepFn) - && !Call.isCalled(GetcFn) - && !Call.isCalled(FgetsFn) - && !Call.isCalled(ReadFn) - && !Call.isCalled(RecvFn) - && !Call.isCalled(UnlockFn)) + if (!isBlockingFunction(Call) + && !isLockFunction(Call) + && !isUnlockFunction(Call)) return; ProgramStateRef State = C.getState(); unsigned mutexCount = State->get<MutexCounter>(); - if (Call.isCalled(UnlockFn) && mutexCount > 0) { + if (isUnlockFunction(Call) && mutexCount > 0) { State = State->set<MutexCounter>(--mutexCount); C.addTransition(State); - } else if (Call.isCalled(LockFn)) { + } else if (isLockFunction(Call)) { State = State->set<MutexCounter>(++mutexCount); C.addTransition(State); } else if (mutexCount > 0) { @@ -97,8 +138,11 @@ void BlockInCriticalSectionChecker::reportBlockInCritSection( if (!ErrNode) return; - auto R = llvm::make_unique<BugReport>(*BlockInCritSectionBugType, - "A blocking function %s is called inside a critical section.", ErrNode); + std::string msg; + llvm::raw_string_ostream os(msg); + os << "Call to blocking function '" << Call.getCalleeIdentifier()->getName() + << "' inside of critical section"; + auto R = llvm::make_unique<BugReport>(*BlockInCritSectionBugType, os.str(), ErrNode); R->addRange(Call.getSourceRange()); R->markInteresting(BlockDescSym); C.emitReport(std::move(R)); diff --git a/lib/StaticAnalyzer/Checkers/CMakeLists.txt b/lib/StaticAnalyzer/Checkers/CMakeLists.txt index 05505ec38600..60d60bc074eb 100644 --- a/lib/StaticAnalyzer/Checkers/CMakeLists.txt +++ b/lib/StaticAnalyzer/Checkers/CMakeLists.txt @@ -48,6 +48,7 @@ add_clang_library(clangStaticAnalyzerCheckers MallocChecker.cpp MallocOverflowSecurityChecker.cpp MallocSizeofChecker.cpp + MisusedMovedObjectChecker.cpp MPI-Checker/MPIBugReporter.cpp MPI-Checker/MPIChecker.cpp MPI-Checker/MPIFunctionClassifier.cpp diff --git a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp index 238032c895f6..32e3ce9270aa 100644 --- a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp @@ -68,6 +68,7 @@ public: const InvalidatedSymbols *, ArrayRef<const MemRegion *> ExplicitRegions, ArrayRef<const MemRegion *> Regions, + const LocationContext *LCtx, const CallEvent *Call) const; typedef void (CStringChecker::*FnCheck)(CheckerContext &, @@ -1943,8 +1944,12 @@ void CStringChecker::evalStrsep(CheckerContext &C, const CallExpr *CE) const { // Overwrite the search string pointer. The new value is either an address // further along in the same string, or NULL if there are no more tokens. State = State->bindLoc(*SearchStrLoc, - SVB.conjureSymbolVal(getTag(), CE, LCtx, CharPtrTy, - C.blockCount())); + SVB.conjureSymbolVal(getTag(), + CE, + LCtx, + CharPtrTy, + C.blockCount()), + LCtx); } else { assert(SearchStrVal.isUnknown()); // Conjure a symbolic value. It's the best we can do. @@ -2116,6 +2121,7 @@ CStringChecker::checkRegionChanges(ProgramStateRef state, const InvalidatedSymbols *, ArrayRef<const MemRegion *> ExplicitRegions, ArrayRef<const MemRegion *> Regions, + const LocationContext *LCtx, const CallEvent *Call) const { CStringLengthTy Entries = state->get<CStringLength>(); if (Entries.isEmpty()) diff --git a/lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp b/lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp index 3db19946a300..391b843ff3db 100644 --- a/lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp @@ -36,25 +36,24 @@ class WalkAST: public StmtVisitor<WalkAST> { AnalysisDeclContext* AC; /// Check if two expressions refer to the same declaration. - inline bool sameDecl(const Expr *A1, const Expr *A2) { - if (const DeclRefExpr *D1 = dyn_cast<DeclRefExpr>(A1->IgnoreParenCasts())) - if (const DeclRefExpr *D2 = dyn_cast<DeclRefExpr>(A2->IgnoreParenCasts())) + bool sameDecl(const Expr *A1, const Expr *A2) { + if (const auto *D1 = dyn_cast<DeclRefExpr>(A1->IgnoreParenCasts())) + if (const auto *D2 = dyn_cast<DeclRefExpr>(A2->IgnoreParenCasts())) return D1->getDecl() == D2->getDecl(); return false; } /// Check if the expression E is a sizeof(WithArg). - inline bool isSizeof(const Expr *E, const Expr *WithArg) { - if (const UnaryExprOrTypeTraitExpr *UE = - dyn_cast<UnaryExprOrTypeTraitExpr>(E)) - if (UE->getKind() == UETT_SizeOf) + bool isSizeof(const Expr *E, const Expr *WithArg) { + if (const auto *UE = dyn_cast<UnaryExprOrTypeTraitExpr>(E)) + if (UE->getKind() == UETT_SizeOf && !UE->isArgumentType()) return sameDecl(UE->getArgumentExpr(), WithArg); return false; } /// Check if the expression E is a strlen(WithArg). - inline bool isStrlen(const Expr *E, const Expr *WithArg) { - if (const CallExpr *CE = dyn_cast<CallExpr>(E)) { + bool isStrlen(const Expr *E, const Expr *WithArg) { + if (const auto *CE = dyn_cast<CallExpr>(E)) { const FunctionDecl *FD = CE->getDirectCallee(); if (!FD) return false; @@ -65,14 +64,14 @@ class WalkAST: public StmtVisitor<WalkAST> { } /// Check if the expression is an integer literal with value 1. - inline bool isOne(const Expr *E) { - if (const IntegerLiteral *IL = dyn_cast<IntegerLiteral>(E)) + bool isOne(const Expr *E) { + if (const auto *IL = dyn_cast<IntegerLiteral>(E)) return (IL->getValue().isIntN(1)); return false; } - inline StringRef getPrintableName(const Expr *E) { - if (const DeclRefExpr *D = dyn_cast<DeclRefExpr>(E->IgnoreParenCasts())) + StringRef getPrintableName(const Expr *E) { + if (const auto *D = dyn_cast<DeclRefExpr>(E->IgnoreParenCasts())) return D->getDecl()->getName(); return StringRef(); } @@ -82,8 +81,8 @@ class WalkAST: public StmtVisitor<WalkAST> { bool containsBadStrncatPattern(const CallExpr *CE); public: - WalkAST(const CheckerBase *checker, BugReporter &br, AnalysisDeclContext *ac) - : Checker(checker), BR(br), AC(ac) {} + WalkAST(const CheckerBase *Checker, BugReporter &BR, AnalysisDeclContext *AC) + : Checker(Checker), BR(BR), AC(AC) {} // Statement visitor methods. void VisitChildren(Stmt *S); @@ -108,8 +107,7 @@ bool WalkAST::containsBadStrncatPattern(const CallExpr *CE) { const Expr *LenArg = CE->getArg(2); // Identify wrong size expressions, which are commonly used instead. - if (const BinaryOperator *BE = - dyn_cast<BinaryOperator>(LenArg->IgnoreParenCasts())) { + if (const auto *BE = dyn_cast<BinaryOperator>(LenArg->IgnoreParenCasts())) { // - sizeof(dst) - strlen(dst) if (BE->getOpcode() == BO_Sub) { const Expr *L = BE->getLHS(); diff --git a/lib/StaticAnalyzer/Checkers/CXXSelfAssignmentChecker.cpp b/lib/StaticAnalyzer/Checkers/CXXSelfAssignmentChecker.cpp index 7631322d255b..668e772fe1b3 100644 --- a/lib/StaticAnalyzer/Checkers/CXXSelfAssignmentChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/CXXSelfAssignmentChecker.cpp @@ -51,9 +51,9 @@ void CXXSelfAssignmentChecker::checkBeginFunction(CheckerContext &C) const { State->getSVal(SVB.getCXXThis(MD, LCtx->getCurrentStackFrame())); auto Param = SVB.makeLoc(State->getRegion(MD->getParamDecl(0), LCtx)); auto ParamVal = State->getSVal(Param); - ProgramStateRef SelfAssignState = State->bindLoc(Param, ThisVal); + ProgramStateRef SelfAssignState = State->bindLoc(Param, ThisVal, LCtx); C.addTransition(SelfAssignState); - ProgramStateRef NonSelfAssignState = State->bindLoc(Param, ParamVal); + ProgramStateRef NonSelfAssignState = State->bindLoc(Param, ParamVal, LCtx); C.addTransition(NonSelfAssignState); } diff --git a/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp b/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp index f474857a1bf4..07285d27ed9e 100644 --- a/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp @@ -21,6 +21,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringExtras.h" #include "llvm/Support/raw_ostream.h" using namespace clang; @@ -71,7 +72,7 @@ public: private: bool PreVisitProcessArg(CheckerContext &C, SVal V, SourceRange ArgRange, - const Expr *ArgEx, bool IsFirstArgument, + const Expr *ArgEx, int ArgumentNumber, bool CheckUninitFields, const CallEvent &Call, std::unique_ptr<BugType> &BT, const ParmVarDecl *ParamDecl) const; @@ -89,9 +90,10 @@ private: BT.reset(new BuiltinBug(this, desc)); } bool uninitRefOrPointer(CheckerContext &C, const SVal &V, - SourceRange ArgRange, - const Expr *ArgEx, std::unique_ptr<BugType> &BT, - const ParmVarDecl *ParamDecl, const char *BD) const; + SourceRange ArgRange, const Expr *ArgEx, + std::unique_ptr<BugType> &BT, + const ParmVarDecl *ParamDecl, const char *BD, + int ArgumentNumber) const; }; } // end anonymous namespace @@ -111,38 +113,45 @@ void CallAndMessageChecker::emitBadCall(BugType *BT, CheckerContext &C, C.emitReport(std::move(R)); } -static StringRef describeUninitializedArgumentInCall(const CallEvent &Call, - bool IsFirstArgument) { +static void describeUninitializedArgumentInCall(const CallEvent &Call, + int ArgumentNumber, + llvm::raw_svector_ostream &Os) { switch (Call.getKind()) { case CE_ObjCMessage: { const ObjCMethodCall &Msg = cast<ObjCMethodCall>(Call); switch (Msg.getMessageKind()) { case OCM_Message: - return "Argument in message expression is an uninitialized value"; + Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(ArgumentNumber + 1) + << " argument in message expression is an uninitialized value"; + return; case OCM_PropertyAccess: assert(Msg.isSetter() && "Getters have no args"); - return "Argument for property setter is an uninitialized value"; + Os << "Argument for property setter is an uninitialized value"; + return; case OCM_Subscript: - if (Msg.isSetter() && IsFirstArgument) - return "Argument for subscript setter is an uninitialized value"; - return "Subscript index is an uninitialized value"; + if (Msg.isSetter() && (ArgumentNumber == 0)) + Os << "Argument for subscript setter is an uninitialized value"; + else + Os << "Subscript index is an uninitialized value"; + return; } llvm_unreachable("Unknown message kind."); } case CE_Block: - return "Block call argument is an uninitialized value"; + Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(ArgumentNumber + 1) + << " block call argument is an uninitialized value"; + return; default: - return "Function call argument is an uninitialized value"; + Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(ArgumentNumber + 1) + << " function call argument is an uninitialized value"; + return; } } -bool CallAndMessageChecker::uninitRefOrPointer(CheckerContext &C, - const SVal &V, - SourceRange ArgRange, - const Expr *ArgEx, - std::unique_ptr<BugType> &BT, - const ParmVarDecl *ParamDecl, - const char *BD) const { +bool CallAndMessageChecker::uninitRefOrPointer( + CheckerContext &C, const SVal &V, SourceRange ArgRange, const Expr *ArgEx, + std::unique_ptr<BugType> &BT, const ParmVarDecl *ParamDecl, const char *BD, + int ArgumentNumber) const { if (!Filter.Check_CallAndMessageUnInitRefArg) return false; @@ -153,12 +162,15 @@ bool CallAndMessageChecker::uninitRefOrPointer(CheckerContext &C, // If parameter is declared as pointer to const in function declaration, // then check if corresponding argument in function call is // pointing to undefined symbol value (uninitialized memory). - StringRef Message; + SmallString<200> Buf; + llvm::raw_svector_ostream Os(Buf); if (ParamDecl->getType()->isPointerType()) { - Message = "Function call argument is a pointer to uninitialized value"; + Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(ArgumentNumber + 1) + << " function call argument is a pointer to uninitialized value"; } else if (ParamDecl->getType()->isReferenceType()) { - Message = "Function call argument is an uninitialized value"; + Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(ArgumentNumber + 1) + << " function call argument is an uninitialized value"; } else return false; @@ -171,7 +183,7 @@ bool CallAndMessageChecker::uninitRefOrPointer(CheckerContext &C, if (PSV.isUndef()) { if (ExplodedNode *N = C.generateErrorNode()) { LazyInit_BT(BD, BT); - auto R = llvm::make_unique<BugReport>(*BT, Message, N); + auto R = llvm::make_unique<BugReport>(*BT, Os.str(), N); R->addRange(ArgRange); if (ArgEx) { bugreporter::trackNullOrUndefValue(N, ArgEx, *R); @@ -188,7 +200,7 @@ bool CallAndMessageChecker::PreVisitProcessArg(CheckerContext &C, SVal V, SourceRange ArgRange, const Expr *ArgEx, - bool IsFirstArgument, + int ArgumentNumber, bool CheckUninitFields, const CallEvent &Call, std::unique_ptr<BugType> &BT, @@ -196,17 +208,19 @@ bool CallAndMessageChecker::PreVisitProcessArg(CheckerContext &C, ) const { const char *BD = "Uninitialized argument value"; - if (uninitRefOrPointer(C, V, ArgRange, ArgEx, BT, ParamDecl, BD)) + if (uninitRefOrPointer(C, V, ArgRange, ArgEx, BT, ParamDecl, BD, + ArgumentNumber)) return true; if (V.isUndef()) { if (ExplodedNode *N = C.generateErrorNode()) { LazyInit_BT(BD, BT); - // Generate a report for this bug. - StringRef Desc = - describeUninitializedArgumentInCall(Call, IsFirstArgument); - auto R = llvm::make_unique<BugReport>(*BT, Desc, N); + SmallString<200> Buf; + llvm::raw_svector_ostream Os(Buf); + describeUninitializedArgumentInCall(Call, ArgumentNumber, Os); + auto R = llvm::make_unique<BugReport>(*BT, Os.str(), N); + R->addRange(ArgRange); if (ArgEx) bugreporter::trackNullOrUndefValue(N, ArgEx, *R); @@ -435,7 +449,7 @@ void CallAndMessageChecker::checkPreCall(const CallEvent &Call, if(FD && i < FD->getNumParams()) ParamDecl = FD->getParamDecl(i); if (PreVisitProcessArg(C, Call.getArgSVal(i), Call.getArgSourceRange(i), - Call.getArgExpr(i), /*IsFirstArgument=*/i == 0, + Call.getArgExpr(i), i, checkUninitFields, Call, *BT, ParamDecl)) return; } diff --git a/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp b/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp index 16a475ae9dd2..65e81315f095 100644 --- a/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp @@ -84,6 +84,10 @@ bool CastToStructVisitor::VisitCastExpr(const CastExpr *CE) { if (!VD || VD->getType()->isReferenceType()) return true; + if (ToPointeeTy->isIncompleteType() || + OrigPointeeTy->isIncompleteType()) + return true; + // Warn when there is widening cast. unsigned ToWidth = Ctx.getTypeInfo(ToPointeeTy).Width; unsigned OrigWidth = Ctx.getTypeInfo(OrigPointeeTy).Width; diff --git a/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp b/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp index 86764c939dcd..95b6c4d3775d 100644 --- a/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp +++ b/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp @@ -231,14 +231,6 @@ public: /// check::LiveSymbols void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const {} - /// \brief Called to determine if the checker currently needs to know if when - /// contents of any regions change. - /// - /// Since it is not necessarily cheap to compute which regions are being - /// changed, this allows the analyzer core to skip the more expensive - /// #checkRegionChanges when no checkers are tracking any state. - bool wantsRegionChangeUpdate(ProgramStateRef St) const { return true; } - /// \brief Called when the contents of one or more regions change. /// /// This can occur in many different ways: an explicit bind, a blanket @@ -255,18 +247,18 @@ public: /// by this change. For a simple bind, this list will be the same as /// \p ExplicitRegions, since a bind does not affect the contents of /// anything accessible through the base region. + /// \param LCtx LocationContext that is useful for getting various contextual + /// info, like callstack, CFG etc. /// \param Call The opaque call triggering this invalidation. Will be 0 if the /// change was not triggered by a call. /// - /// Note that this callback will not be invoked unless - /// #wantsRegionChangeUpdate returns \c true. - /// /// check::RegionChanges ProgramStateRef checkRegionChanges(ProgramStateRef State, const InvalidatedSymbols *Invalidated, ArrayRef<const MemRegion *> ExplicitRegions, ArrayRef<const MemRegion *> Regions, + const LocationContext *LCtx, const CallEvent *Call) const { return State; } diff --git a/lib/StaticAnalyzer/Checkers/CloneChecker.cpp b/lib/StaticAnalyzer/Checkers/CloneChecker.cpp index 6fa5732d10cb..1885b0e39203 100644 --- a/lib/StaticAnalyzer/Checkers/CloneChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/CloneChecker.cpp @@ -38,14 +38,15 @@ public: void checkEndOfTranslationUnit(const TranslationUnitDecl *TU, AnalysisManager &Mgr, BugReporter &BR) const; - /// \brief Reports all clones to the user. + /// Reports all clones to the user. void reportClones(BugReporter &BR, AnalysisManager &Mgr, - int MinComplexity) const; + std::vector<CloneDetector::CloneGroup> &CloneGroups) const; - /// \brief Reports only suspicious clones to the user along with informaton - /// that explain why they are suspicious. - void reportSuspiciousClones(BugReporter &BR, AnalysisManager &Mgr, - int MinComplexity) const; + /// Reports only suspicious clones to the user along with informaton + /// that explain why they are suspicious. + void reportSuspiciousClones( + BugReporter &BR, AnalysisManager &Mgr, + std::vector<CloneDetector::CloneGroup> &CloneGroups) const; }; } // end anonymous namespace @@ -72,11 +73,30 @@ void CloneChecker::checkEndOfTranslationUnit(const TranslationUnitDecl *TU, bool ReportNormalClones = Mgr.getAnalyzerOptions().getBooleanOption( "ReportNormalClones", true, this); + // Let the CloneDetector create a list of clones from all the analyzed + // statements. We don't filter for matching variable patterns at this point + // because reportSuspiciousClones() wants to search them for errors. + std::vector<CloneDetector::CloneGroup> AllCloneGroups; + + Detector.findClones(AllCloneGroups, RecursiveCloneTypeIIConstraint(), + MinComplexityConstraint(MinComplexity), + MinGroupSizeConstraint(2), OnlyLargestCloneConstraint()); + if (ReportSuspiciousClones) - reportSuspiciousClones(BR, Mgr, MinComplexity); + reportSuspiciousClones(BR, Mgr, AllCloneGroups); + + // We are done for this translation unit unless we also need to report normal + // clones. + if (!ReportNormalClones) + return; - if (ReportNormalClones) - reportClones(BR, Mgr, MinComplexity); + // Now that the suspicious clone detector has checked for pattern errors, + // we also filter all clones who don't have matching patterns + CloneDetector::constrainClones(AllCloneGroups, + MatchingVariablePatternConstraint(), + MinGroupSizeConstraint(2)); + + reportClones(BR, Mgr, AllCloneGroups); } static PathDiagnosticLocation makeLocation(const StmtSequence &S, @@ -87,37 +107,55 @@ static PathDiagnosticLocation makeLocation(const StmtSequence &S, Mgr.getAnalysisDeclContext(ACtx.getTranslationUnitDecl())); } -void CloneChecker::reportClones(BugReporter &BR, AnalysisManager &Mgr, - int MinComplexity) const { - - std::vector<CloneDetector::CloneGroup> CloneGroups; - Detector.findClones(CloneGroups, MinComplexity); +void CloneChecker::reportClones( + BugReporter &BR, AnalysisManager &Mgr, + std::vector<CloneDetector::CloneGroup> &CloneGroups) const { if (!BT_Exact) BT_Exact.reset(new BugType(this, "Exact code clone", "Code clone")); - for (CloneDetector::CloneGroup &Group : CloneGroups) { + for (const CloneDetector::CloneGroup &Group : CloneGroups) { // We group the clones by printing the first as a warning and all others // as a note. - auto R = llvm::make_unique<BugReport>( - *BT_Exact, "Duplicate code detected", - makeLocation(Group.Sequences.front(), Mgr)); - R->addRange(Group.Sequences.front().getSourceRange()); - - for (unsigned i = 1; i < Group.Sequences.size(); ++i) - R->addNote("Similar code here", - makeLocation(Group.Sequences[i], Mgr), - Group.Sequences[i].getSourceRange()); + auto R = llvm::make_unique<BugReport>(*BT_Exact, "Duplicate code detected", + makeLocation(Group.front(), Mgr)); + R->addRange(Group.front().getSourceRange()); + + for (unsigned i = 1; i < Group.size(); ++i) + R->addNote("Similar code here", makeLocation(Group[i], Mgr), + Group[i].getSourceRange()); BR.emitReport(std::move(R)); } } -void CloneChecker::reportSuspiciousClones(BugReporter &BR, - AnalysisManager &Mgr, - int MinComplexity) const { - - std::vector<CloneDetector::SuspiciousClonePair> Clones; - Detector.findSuspiciousClones(Clones, MinComplexity); +void CloneChecker::reportSuspiciousClones( + BugReporter &BR, AnalysisManager &Mgr, + std::vector<CloneDetector::CloneGroup> &CloneGroups) const { + std::vector<VariablePattern::SuspiciousClonePair> Pairs; + + for (const CloneDetector::CloneGroup &Group : CloneGroups) { + for (unsigned i = 0; i < Group.size(); ++i) { + VariablePattern PatternA(Group[i]); + + for (unsigned j = i + 1; j < Group.size(); ++j) { + VariablePattern PatternB(Group[j]); + + VariablePattern::SuspiciousClonePair ClonePair; + // For now, we only report clones which break the variable pattern just + // once because multiple differences in a pattern are an indicator that + // those differences are maybe intended (e.g. because it's actually a + // different algorithm). + // FIXME: In very big clones even multiple variables can be unintended, + // so replacing this number with a percentage could better handle such + // cases. On the other hand it could increase the false-positive rate + // for all clones if the percentage is too high. + if (PatternA.countPatternDifferences(PatternB, &ClonePair) == 1) { + Pairs.push_back(ClonePair); + break; + } + } + } + } if (!BT_Suspicious) BT_Suspicious.reset( @@ -128,7 +166,7 @@ void CloneChecker::reportSuspiciousClones(BugReporter &BR, AnalysisDeclContext *ADC = Mgr.getAnalysisDeclContext(ACtx.getTranslationUnitDecl()); - for (CloneDetector::SuspiciousClonePair &Pair : Clones) { + for (VariablePattern::SuspiciousClonePair &Pair : Pairs) { // FIXME: We are ignoring the suggestions currently, because they are // only 50% accurate (even if the second suggestion is unavailable), // which may confuse the user. diff --git a/lib/StaticAnalyzer/Checkers/ConversionChecker.cpp b/lib/StaticAnalyzer/Checkers/ConversionChecker.cpp index 2bb9e858731c..ea894c81011c 100644 --- a/lib/StaticAnalyzer/Checkers/ConversionChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ConversionChecker.cpp @@ -41,7 +41,8 @@ private: mutable std::unique_ptr<BuiltinBug> BT; // Is there loss of precision - bool isLossOfPrecision(const ImplicitCastExpr *Cast, CheckerContext &C) const; + bool isLossOfPrecision(const ImplicitCastExpr *Cast, QualType DestType, + CheckerContext &C) const; // Is there loss of sign bool isLossOfSign(const ImplicitCastExpr *Cast, CheckerContext &C) const; @@ -73,16 +74,30 @@ void ConversionChecker::checkPreStmt(const ImplicitCastExpr *Cast, // Loss of sign/precision in binary operation. if (const auto *B = dyn_cast<BinaryOperator>(Parent)) { BinaryOperator::Opcode Opc = B->getOpcode(); - if (Opc == BO_Assign || Opc == BO_AddAssign || Opc == BO_SubAssign || - Opc == BO_MulAssign) { + if (Opc == BO_Assign) { LossOfSign = isLossOfSign(Cast, C); - LossOfPrecision = isLossOfPrecision(Cast, C); + LossOfPrecision = isLossOfPrecision(Cast, Cast->getType(), C); + } else if (Opc == BO_AddAssign || Opc == BO_SubAssign) { + // No loss of sign. + LossOfPrecision = isLossOfPrecision(Cast, B->getLHS()->getType(), C); + } else if (Opc == BO_MulAssign) { + LossOfSign = isLossOfSign(Cast, C); + LossOfPrecision = isLossOfPrecision(Cast, B->getLHS()->getType(), C); + } else if (Opc == BO_DivAssign || Opc == BO_RemAssign) { + LossOfSign = isLossOfSign(Cast, C); + // No loss of precision. + } else if (Opc == BO_AndAssign) { + LossOfSign = isLossOfSign(Cast, C); + // No loss of precision. + } else if (Opc == BO_OrAssign || Opc == BO_XorAssign) { + LossOfSign = isLossOfSign(Cast, C); + LossOfPrecision = isLossOfPrecision(Cast, B->getLHS()->getType(), C); } else if (B->isRelationalOp() || B->isMultiplicativeOp()) { LossOfSign = isLossOfSign(Cast, C); } } else if (isa<DeclStmt>(Parent)) { LossOfSign = isLossOfSign(Cast, C); - LossOfPrecision = isLossOfPrecision(Cast, C); + LossOfPrecision = isLossOfPrecision(Cast, Cast->getType(), C); } if (LossOfSign || LossOfPrecision) { @@ -113,6 +128,13 @@ static bool isGreaterEqual(CheckerContext &C, const Expr *E, unsigned long long Val) { ProgramStateRef State = C.getState(); SVal EVal = C.getSVal(E); + if (EVal.isUnknownOrUndef()) + return false; + if (!EVal.getAs<NonLoc>() && EVal.getAs<Loc>()) { + ProgramStateManager &Mgr = C.getStateManager(); + EVal = + Mgr.getStoreManager().getBinding(State->getStore(), EVal.castAs<Loc>()); + } if (EVal.isUnknownOrUndef() || !EVal.getAs<NonLoc>()) return false; @@ -153,22 +175,22 @@ static bool isNegative(CheckerContext &C, const Expr *E) { } bool ConversionChecker::isLossOfPrecision(const ImplicitCastExpr *Cast, - CheckerContext &C) const { + QualType DestType, + CheckerContext &C) const { // Don't warn about explicit loss of precision. if (Cast->isEvaluatable(C.getASTContext())) return false; - QualType CastType = Cast->getType(); QualType SubType = Cast->IgnoreParenImpCasts()->getType(); - if (!CastType->isIntegerType() || !SubType->isIntegerType()) + if (!DestType->isIntegerType() || !SubType->isIntegerType()) return false; - if (C.getASTContext().getIntWidth(CastType) >= + if (C.getASTContext().getIntWidth(DestType) >= C.getASTContext().getIntWidth(SubType)) return false; - unsigned W = C.getASTContext().getIntWidth(CastType); + unsigned W = C.getASTContext().getIntWidth(DestType); if (W == 1 || W >= 64U) return false; diff --git a/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp b/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp index 2d5cb60edf7d..32040e71163d 100644 --- a/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp @@ -13,6 +13,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Checkers/SValExplainer.h" #include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/ScopedPrinter.h" using namespace clang; using namespace ento; @@ -71,8 +72,8 @@ bool ExprInspectionChecker::evalCall(const CallExpr *CE, &ExprInspectionChecker::analyzerWarnIfReached) .Case("clang_analyzer_warnOnDeadSymbol", &ExprInspectionChecker::analyzerWarnOnDeadSymbol) - .Case("clang_analyzer_explain", &ExprInspectionChecker::analyzerExplain) - .Case("clang_analyzer_dump", &ExprInspectionChecker::analyzerDump) + .StartsWith("clang_analyzer_explain", &ExprInspectionChecker::analyzerExplain) + .StartsWith("clang_analyzer_dump", &ExprInspectionChecker::analyzerDump) .Case("clang_analyzer_getExtent", &ExprInspectionChecker::analyzerGetExtent) .Case("clang_analyzer_printState", &ExprInspectionChecker::analyzerPrintState) @@ -269,7 +270,7 @@ void ExprInspectionChecker::checkEndAnalysis(ExplodedGraph &G, BugReporter &BR, unsigned NumTimesReached = Item.second.NumTimesReached; ExplodedNode *N = Item.second.ExampleNode; - reportBug(std::to_string(NumTimesReached), BR, N); + reportBug(llvm::to_string(NumTimesReached), BR, N); } } diff --git a/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp b/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp index 8c8acc637f1f..b1a54e77951b 100644 --- a/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp @@ -65,6 +65,18 @@ private: /// and thus, is tainted. static bool isStdin(const Expr *E, CheckerContext &C); + /// This is called from getPointedToSymbol() to resolve symbol references for + /// the region underlying a LazyCompoundVal. This is the default binding + /// for the LCV, which could be a conjured symbol from a function call that + /// initialized the region. It only returns the conjured symbol if the LCV + /// covers the entire region, e.g. we avoid false positives by not returning + /// a default bindingc for an entire struct if the symbol for only a single + /// field or element within it is requested. + // TODO: Return an appropriate symbol for sub-fields/elements of an LCV so + // that they are also appropriately tainted. + static SymbolRef getLCVSymbol(CheckerContext &C, + nonloc::LazyCompoundVal &LCV); + /// \brief Given a pointer argument, get the symbol of the value it contains /// (points to). static SymbolRef getPointedToSymbol(CheckerContext &C, const Expr *Arg); @@ -101,6 +113,22 @@ private: bool generateReportIfTainted(const Expr *E, const char Msg[], CheckerContext &C) const; + /// The bug visitor prints a diagnostic message at the location where a given + /// variable was tainted. + class TaintBugVisitor + : public BugReporterVisitorImpl<TaintBugVisitor> { + private: + const SVal V; + + public: + TaintBugVisitor(const SVal V) : V(V) {} + void Profile(llvm::FoldingSetNodeID &ID) const override { ID.Add(V); } + + std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, + const ExplodedNode *PrevN, + BugReporterContext &BRC, + BugReport &BR) override; + }; typedef SmallVector<unsigned, 2> ArgVector; @@ -194,6 +222,28 @@ const char GenericTaintChecker::MsgTaintedBufferSize[] = /// points to data, which should be tainted on return. REGISTER_SET_WITH_PROGRAMSTATE(TaintArgsOnPostVisit, unsigned) +std::shared_ptr<PathDiagnosticPiece> +GenericTaintChecker::TaintBugVisitor::VisitNode(const ExplodedNode *N, + const ExplodedNode *PrevN, BugReporterContext &BRC, BugReport &BR) { + + // Find the ExplodedNode where the taint was first introduced + if (!N->getState()->isTainted(V) || PrevN->getState()->isTainted(V)) + return nullptr; + + const Stmt *S = PathDiagnosticLocation::getStmt(N); + if (!S) + return nullptr; + + const LocationContext *NCtx = N->getLocationContext(); + PathDiagnosticLocation L = + PathDiagnosticLocation::createBegin(S, BRC.getSourceManager(), NCtx); + if (!L.isValid() || !L.asLocation().isValid()) + return nullptr; + + return std::make_shared<PathDiagnosticEventPiece>( + L, "Taint originated here"); +} + GenericTaintChecker::TaintPropagationRule GenericTaintChecker::TaintPropagationRule::getTaintPropagationRule( const FunctionDecl *FDecl, @@ -423,6 +473,27 @@ bool GenericTaintChecker::checkPre(const CallExpr *CE, CheckerContext &C) const{ return false; } +SymbolRef GenericTaintChecker::getLCVSymbol(CheckerContext &C, + nonloc::LazyCompoundVal &LCV) { + StoreManager &StoreMgr = C.getStoreManager(); + + // getLCVSymbol() is reached in a PostStmt so we can always expect a default + // binding to exist if one is present. + if (Optional<SVal> binding = StoreMgr.getDefaultBinding(LCV)) { + SymbolRef Sym = binding->getAsSymbol(); + if (!Sym) + return nullptr; + + // If the LCV covers an entire base region return the default conjured symbol. + if (LCV.getRegion() == LCV.getRegion()->getBaseRegion()) + return Sym; + } + + // Otherwise, return a nullptr as there's not yet a functional way to taint + // sub-regions of LCVs. + return nullptr; +} + SymbolRef GenericTaintChecker::getPointedToSymbol(CheckerContext &C, const Expr* Arg) { ProgramStateRef State = C.getState(); @@ -438,6 +509,10 @@ SymbolRef GenericTaintChecker::getPointedToSymbol(CheckerContext &C, dyn_cast<PointerType>(Arg->getType().getCanonicalType().getTypePtr()); SVal Val = State->getSVal(*AddrLoc, ArgTy ? ArgTy->getPointeeType(): QualType()); + + if (auto LCV = Val.getAs<nonloc::LazyCompoundVal>()) + return getLCVSymbol(C, *LCV); + return Val.getAsSymbol(); } @@ -635,8 +710,13 @@ bool GenericTaintChecker::generateReportIfTainted(const Expr *E, // Check for taint. ProgramStateRef State = C.getState(); - if (!State->isTainted(getPointedToSymbol(C, E)) && - !State->isTainted(E, C.getLocationContext())) + const SymbolRef PointedToSym = getPointedToSymbol(C, E); + SVal TaintedSVal; + if (State->isTainted(PointedToSym)) + TaintedSVal = nonloc::SymbolVal(PointedToSym); + else if (State->isTainted(E, C.getLocationContext())) + TaintedSVal = C.getSVal(E); + else return false; // Generate diagnostic. @@ -644,6 +724,7 @@ bool GenericTaintChecker::generateReportIfTainted(const Expr *E, initBugType(); auto report = llvm::make_unique<BugReport>(*BT, Msg, N); report->addRange(E->getSourceRange()); + report->addVisitor(llvm::make_unique<TaintBugVisitor>(TaintedSVal)); C.emitReport(std::move(report)); return true; } diff --git a/lib/StaticAnalyzer/Checkers/IteratorPastEndChecker.cpp b/lib/StaticAnalyzer/Checkers/IteratorPastEndChecker.cpp index 531054aa7887..f0f7c98c9640 100644 --- a/lib/StaticAnalyzer/Checkers/IteratorPastEndChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/IteratorPastEndChecker.cpp @@ -415,8 +415,6 @@ bool IteratorPastEndChecker::evalCall(const CallExpr *CE, return evalFindFirstOf(C, CE); } else if (FD->getIdentifier() == II_find_if) { return evalFindIf(C, CE); - } else if (FD->getIdentifier() == II_find_if) { - return evalFindIf(C, CE); } else if (FD->getIdentifier() == II_find_if_not) { return evalFindIfNot(C, CE); } else if (FD->getIdentifier() == II_upper_bound) { diff --git a/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.cpp b/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.cpp index c667b9e67d4b..696cf39473d5 100644 --- a/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.cpp @@ -153,9 +153,9 @@ void MPIChecker::allRegionsUsedByWait( MemRegionManager *const RegionManager = MR->getMemRegionManager(); if (FuncClassifier->isMPI_Waitall(CE.getCalleeIdentifier())) { - const MemRegion *SuperRegion{nullptr}; + const SubRegion *SuperRegion{nullptr}; if (const ElementRegion *const ER = MR->getAs<ElementRegion>()) { - SuperRegion = ER->getSuperRegion(); + SuperRegion = cast<SubRegion>(ER->getSuperRegion()); } // A single request is passed to MPI_Waitall. diff --git a/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp b/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp index f1aa16391db1..f8473dbd7647 100644 --- a/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp @@ -28,7 +28,8 @@ using namespace ento; namespace { class MacOSKeychainAPIChecker : public Checker<check::PreStmt<CallExpr>, check::PostStmt<CallExpr>, - check::DeadSymbols> { + check::DeadSymbols, + eval::Assume> { mutable std::unique_ptr<BugType> BT; public: @@ -57,6 +58,10 @@ public: void checkPreStmt(const CallExpr *S, CheckerContext &C) const; void checkPostStmt(const CallExpr *S, CheckerContext &C) const; void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const; + ProgramStateRef evalAssume(ProgramStateRef state, SVal Cond, + bool Assumption) const; + void printState(raw_ostream &Out, ProgramStateRef State, + const char *NL, const char *Sep) const; private: typedef std::pair<SymbolRef, const AllocationState*> AllocationPair; @@ -106,19 +111,6 @@ private: std::unique_ptr<BugReport> generateAllocatedDataNotReleasedReport( const AllocationPair &AP, ExplodedNode *N, CheckerContext &C) const; - /// Check if RetSym evaluates to an error value in the current state. - bool definitelyReturnedError(SymbolRef RetSym, - ProgramStateRef State, - SValBuilder &Builder, - bool noError = false) const; - - /// Check if RetSym evaluates to a NoErr value in the current state. - bool definitelyDidnotReturnError(SymbolRef RetSym, - ProgramStateRef State, - SValBuilder &Builder) const { - return definitelyReturnedError(RetSym, State, Builder, true); - } - /// Mark an AllocationPair interesting for diagnostic reporting. void markInteresting(BugReport *R, const AllocationPair &AP) const { R->markInteresting(AP.first); @@ -221,24 +213,6 @@ static SymbolRef getAsPointeeSymbol(const Expr *Expr, return nullptr; } -// When checking for error code, we need to consider the following cases: -// 1) noErr / [0] -// 2) someErr / [1, inf] -// 3) unknown -// If noError, returns true iff (1). -// If !noError, returns true iff (2). -bool MacOSKeychainAPIChecker::definitelyReturnedError(SymbolRef RetSym, - ProgramStateRef State, - SValBuilder &Builder, - bool noError) const { - DefinedOrUnknownSVal NoErrVal = Builder.makeIntVal(NoErr, - Builder.getSymbolManager().getType(RetSym)); - DefinedOrUnknownSVal NoErr = Builder.evalEQ(State, NoErrVal, - nonloc::SymbolVal(RetSym)); - ProgramStateRef ErrState = State->assume(NoErr, noError); - return ErrState == State; -} - // Report deallocator mismatch. Remove the region from tracking - reporting a // missing free error after this one is redundant. void MacOSKeychainAPIChecker:: @@ -289,27 +263,25 @@ void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE, const Expr *ArgExpr = CE->getArg(paramIdx); if (SymbolRef V = getAsPointeeSymbol(ArgExpr, C)) if (const AllocationState *AS = State->get<AllocatedData>(V)) { - if (!definitelyReturnedError(AS->Region, State, C.getSValBuilder())) { - // Remove the value from the state. The new symbol will be added for - // tracking when the second allocator is processed in checkPostStmt(). - State = State->remove<AllocatedData>(V); - ExplodedNode *N = C.generateNonFatalErrorNode(State); - if (!N) - return; - initBugType(); - SmallString<128> sbuf; - llvm::raw_svector_ostream os(sbuf); - unsigned int DIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx; - os << "Allocated data should be released before another call to " - << "the allocator: missing a call to '" - << FunctionsToTrack[DIdx].Name - << "'."; - auto Report = llvm::make_unique<BugReport>(*BT, os.str(), N); - Report->addVisitor(llvm::make_unique<SecKeychainBugVisitor>(V)); - Report->addRange(ArgExpr->getSourceRange()); - Report->markInteresting(AS->Region); - C.emitReport(std::move(Report)); - } + // Remove the value from the state. The new symbol will be added for + // tracking when the second allocator is processed in checkPostStmt(). + State = State->remove<AllocatedData>(V); + ExplodedNode *N = C.generateNonFatalErrorNode(State); + if (!N) + return; + initBugType(); + SmallString<128> sbuf; + llvm::raw_svector_ostream os(sbuf); + unsigned int DIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx; + os << "Allocated data should be released before another call to " + << "the allocator: missing a call to '" + << FunctionsToTrack[DIdx].Name + << "'."; + auto Report = llvm::make_unique<BugReport>(*BT, os.str(), N); + Report->addVisitor(llvm::make_unique<SecKeychainBugVisitor>(V)); + Report->addRange(ArgExpr->getSourceRange()); + Report->markInteresting(AS->Region); + C.emitReport(std::move(Report)); } return; } @@ -344,13 +316,12 @@ void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE, // Is the argument to the call being tracked? const AllocationState *AS = State->get<AllocatedData>(ArgSM); - if (!AS && FunctionsToTrack[idx].Kind != ValidAPI) { + if (!AS) return; - } - // If trying to free data which has not been allocated yet, report as a bug. - // TODO: We might want a more precise diagnostic for double free + + // TODO: We might want to report double free here. // (that would involve tracking all the freed symbols in the checker state). - if (!AS || RegionArgIsBad) { + if (RegionArgIsBad) { // It is possible that this is a false positive - the argument might // have entered as an enclosing function parameter. if (isEnclosingFunctionParam(ArgExpr)) @@ -418,23 +389,6 @@ void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE, return; } - // If the buffer can be null and the return status can be an error, - // report a bad call to free. - if (State->assume(ArgSVal.castAs<DefinedSVal>(), false) && - !definitelyDidnotReturnError(AS->Region, State, C.getSValBuilder())) { - ExplodedNode *N = C.generateNonFatalErrorNode(State); - if (!N) - return; - initBugType(); - auto Report = llvm::make_unique<BugReport>( - *BT, "Only call free if a valid (non-NULL) buffer was returned.", N); - Report->addVisitor(llvm::make_unique<SecKeychainBugVisitor>(ArgSM)); - Report->addRange(ArgExpr->getSourceRange()); - Report->markInteresting(AS->Region); - C.emitReport(std::move(Report)); - return; - } - C.addTransition(State); } @@ -540,27 +494,63 @@ MacOSKeychainAPIChecker::generateAllocatedDataNotReleasedReport( return Report; } +/// If the return symbol is assumed to be error, remove the allocated info +/// from consideration. +ProgramStateRef MacOSKeychainAPIChecker::evalAssume(ProgramStateRef State, + SVal Cond, + bool Assumption) const { + AllocatedDataTy AMap = State->get<AllocatedData>(); + if (AMap.isEmpty()) + return State; + + auto *CondBSE = dyn_cast_or_null<BinarySymExpr>(Cond.getAsSymExpr()); + if (!CondBSE) + return State; + BinaryOperator::Opcode OpCode = CondBSE->getOpcode(); + if (OpCode != BO_EQ && OpCode != BO_NE) + return State; + + // Match for a restricted set of patterns for cmparison of error codes. + // Note, the comparisons of type '0 == st' are transformed into SymIntExpr. + SymbolRef ReturnSymbol = nullptr; + if (auto *SIE = dyn_cast<SymIntExpr>(CondBSE)) { + const llvm::APInt &RHS = SIE->getRHS(); + bool ErrorIsReturned = (OpCode == BO_EQ && RHS != NoErr) || + (OpCode == BO_NE && RHS == NoErr); + if (!Assumption) + ErrorIsReturned = !ErrorIsReturned; + if (ErrorIsReturned) + ReturnSymbol = SIE->getLHS(); + } + + if (ReturnSymbol) + for (auto I = AMap.begin(), E = AMap.end(); I != E; ++I) { + if (ReturnSymbol == I->second.Region) + State = State->remove<AllocatedData>(I->first); + } + + return State; +} + void MacOSKeychainAPIChecker::checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const { ProgramStateRef State = C.getState(); - AllocatedDataTy ASet = State->get<AllocatedData>(); - if (ASet.isEmpty()) + AllocatedDataTy AMap = State->get<AllocatedData>(); + if (AMap.isEmpty()) return; bool Changed = false; AllocationPairVec Errors; - for (AllocatedDataTy::iterator I = ASet.begin(), E = ASet.end(); I != E; ++I) { - if (SR.isLive(I->first)) + for (auto I = AMap.begin(), E = AMap.end(); I != E; ++I) { + if (!SR.isDead(I->first)) continue; Changed = true; State = State->remove<AllocatedData>(I->first); - // If the allocated symbol is null or if the allocation call might have - // returned an error, do not report. + // If the allocated symbol is null do not report. ConstraintManager &CMgr = State->getConstraintManager(); ConditionTruthVal AllocFailed = CMgr.isNull(State, I.getKey()); - if (AllocFailed.isConstrainedTrue() || - definitelyReturnedError(I->second.Region, State, C.getSValBuilder())) + if (AllocFailed.isConstrainedTrue()) continue; Errors.push_back(std::make_pair(I->first, &I->second)); } @@ -612,6 +602,22 @@ MacOSKeychainAPIChecker::SecKeychainBugVisitor::VisitNode( "Data is allocated here."); } +void MacOSKeychainAPIChecker::printState(raw_ostream &Out, + ProgramStateRef State, + const char *NL, + const char *Sep) const { + + AllocatedDataTy AMap = State->get<AllocatedData>(); + + if (!AMap.isEmpty()) { + Out << Sep << "KeychainAPIChecker :" << NL; + for (auto I = AMap.begin(), E = AMap.end(); I != E; ++I) { + I.getKey()->dumpToStream(Out); + } + } +} + + void ento::registerMacOSKeychainAPIChecker(CheckerManager &mgr) { mgr.registerChecker<MacOSKeychainAPIChecker>(); } diff --git a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp index 8e839a1d28fd..6e9b7fefa3d0 100644 --- a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -174,7 +174,10 @@ public: II_valloc(nullptr), II_reallocf(nullptr), II_strndup(nullptr), 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_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) {} /// In pessimistic mode, the checker assumes that it does not know which /// functions might free the memory. @@ -236,7 +239,9 @@ 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_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; mutable Optional<uint64_t> KernelZeroFlagVal; void initIdentifierInfo(ASTContext &C) const; @@ -554,6 +559,16 @@ void MallocChecker::initIdentifierInfo(ASTContext &Ctx) const { II_win_strdup = &Ctx.Idents.get("_strdup"); II_win_wcsdup = &Ctx.Idents.get("_wcsdup"); II_win_alloca = &Ctx.Idents.get("_alloca"); + + // Glib + II_g_malloc = &Ctx.Idents.get("g_malloc"); + II_g_malloc0 = &Ctx.Idents.get("g_malloc0"); + II_g_realloc = &Ctx.Idents.get("g_realloc"); + II_g_try_malloc = &Ctx.Idents.get("g_try_malloc"); + II_g_try_malloc0 = &Ctx.Idents.get("g_try_malloc0"); + II_g_try_realloc = &Ctx.Idents.get("g_try_realloc"); + II_g_free = &Ctx.Idents.get("g_free"); + II_g_memdup = &Ctx.Idents.get("g_memdup"); } bool MallocChecker::isMemFunction(const FunctionDecl *FD, ASTContext &C) const { @@ -589,7 +604,8 @@ 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; } @@ -597,7 +613,11 @@ bool MallocChecker::isCMemFunction(const FunctionDecl *FD, if (FunI == II_malloc || FunI == II_realloc || FunI == II_reallocf || 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_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_try_malloc0 || FunI == II_g_try_realloc || + FunI == II_g_memdup) return true; } @@ -762,7 +782,7 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const { initIdentifierInfo(C.getASTContext()); IdentifierInfo *FunI = FD->getIdentifier(); - if (FunI == II_malloc) { + if (FunI == II_malloc || FunI == II_g_malloc || FunI == II_g_try_malloc) { if (CE->getNumArgs() < 1) return; if (CE->getNumArgs() < 3) { @@ -791,7 +811,8 @@ 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) { + } else if (FunI == II_realloc || FunI == II_g_realloc || + FunI == II_g_try_realloc) { State = ReallocMem(C, CE, false, State); State = ProcessZeroAllocation(C, CE, 1, State); } else if (FunI == II_reallocf) { @@ -801,7 +822,7 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const { State = CallocMem(C, CE, State); State = ProcessZeroAllocation(C, CE, 0, State); State = ProcessZeroAllocation(C, CE, 1, State); - } else if (FunI == II_free) { + } else if (FunI == II_free || FunI == II_g_free) { State = FreeMemAux(C, CE, State, 0, false, ReleasedAllocatedMemory); } else if (FunI == II_strdup || FunI == II_win_strdup || FunI == II_wcsdup || FunI == II_win_wcsdup) { @@ -841,6 +862,18 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const { AF_IfNameIndex); } else if (FunI == II_if_freenameindex) { State = FreeMemAux(C, CE, State, 0, false, ReleasedAllocatedMemory); + } else if (FunI == II_g_malloc0 || FunI == II_g_try_malloc0) { + if (CE->getNumArgs() < 1) + return; + SValBuilder &svalBuilder = C.getSValBuilder(); + SVal zeroVal = svalBuilder.makeZeroVal(svalBuilder.getContext().CharTy); + State = MallocMemAux(C, CE, CE->getArg(0), zeroVal, State); + State = ProcessZeroAllocation(C, CE, 0, State); + } else if (FunI == II_g_memdup) { + if (CE->getNumArgs() < 2) + return; + State = MallocMemAux(C, CE, CE->getArg(1), UndefinedVal(), State); + State = ProcessZeroAllocation(C, CE, 1, State); } } @@ -1154,7 +1187,7 @@ ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C, State = State->BindExpr(CE, C.getLocationContext(), RetVal); // Fill the region with the initialization value. - State = State->bindDefault(RetVal, Init); + State = State->bindDefault(RetVal, Init, LCtx); // Set the region's extent equal to the Size parameter. const SymbolicRegion *R = diff --git a/lib/StaticAnalyzer/Checkers/MisusedMovedObjectChecker.cpp b/lib/StaticAnalyzer/Checkers/MisusedMovedObjectChecker.cpp new file mode 100644 index 000000000000..decc552e1213 --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/MisusedMovedObjectChecker.cpp @@ -0,0 +1,481 @@ +// MisusedMovedObjectChecker.cpp - Check use of moved-from objects. - C++ -===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines checker which checks for potential misuses of a moved-from +// object. That means method calls on the object or copying it in moved-from +// state. +// +//===----------------------------------------------------------------------===// + +#include "ClangSACheckers.h" +#include "clang/AST/ExprCXX.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" + +using namespace clang; +using namespace ento; + +namespace { + +struct RegionState { +private: + enum Kind { Moved, Reported } K; + RegionState(Kind InK) : K(InK) {} + +public: + bool isReported() const { return K == Reported; } + bool isMoved() const { return K == Moved; } + + static RegionState getReported() { return RegionState(Reported); } + static RegionState getMoved() { return RegionState(Moved); } + + bool operator==(const RegionState &X) const { return K == X.K; } + void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(K); } +}; + +class MisusedMovedObjectChecker + : public Checker<check::PreCall, check::PostCall, check::EndFunction, + check::DeadSymbols, check::RegionChanges> { +public: + void checkEndFunction(CheckerContext &C) const; + void checkPreCall(const CallEvent &MC, CheckerContext &C) const; + void checkPostCall(const CallEvent &MC, CheckerContext &C) const; + void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const; + ProgramStateRef + checkRegionChanges(ProgramStateRef State, + const InvalidatedSymbols *Invalidated, + ArrayRef<const MemRegion *> ExplicitRegions, + ArrayRef<const MemRegion *> Regions, + const LocationContext *LCtx, const CallEvent *Call) const; + +private: + class MovedBugVisitor : public BugReporterVisitorImpl<MovedBugVisitor> { + public: + MovedBugVisitor(const MemRegion *R) : Region(R), Found(false) {} + + void Profile(llvm::FoldingSetNodeID &ID) const override { + static int X = 0; + ID.AddPointer(&X); + ID.AddPointer(Region); + } + + std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, + const ExplodedNode *PrevN, + BugReporterContext &BRC, + BugReport &BR) override; + + private: + // The tracked region. + const MemRegion *Region; + bool Found; + }; + + mutable std::unique_ptr<BugType> BT; + ExplodedNode *reportBug(const MemRegion *Region, const CallEvent &Call, + CheckerContext &C, bool isCopy) const; + bool isInMoveSafeContext(const LocationContext *LC) const; + bool isStateResetMethod(const CXXMethodDecl *MethodDec) const; + bool isMoveSafeMethod(const CXXMethodDecl *MethodDec) const; + const ExplodedNode *getMoveLocation(const ExplodedNode *N, + const MemRegion *Region, + CheckerContext &C) const; +}; +} // end anonymous namespace + +REGISTER_MAP_WITH_PROGRAMSTATE(TrackedRegionMap, const MemRegion *, RegionState) + +// If a region is removed all of the subregions needs to be removed too. +static ProgramStateRef removeFromState(ProgramStateRef State, + const MemRegion *Region) { + if (!Region) + return State; + // Note: The isSubRegionOf function is not reflexive. + State = State->remove<TrackedRegionMap>(Region); + for (auto &E : State->get<TrackedRegionMap>()) { + if (E.first->isSubRegionOf(Region)) + State = State->remove<TrackedRegionMap>(E.first); + } + return State; +} + +static bool isAnyBaseRegionReported(ProgramStateRef State, + const MemRegion *Region) { + for (auto &E : State->get<TrackedRegionMap>()) { + if (Region->isSubRegionOf(E.first) && E.second.isReported()) + return true; + } + return false; +} + +std::shared_ptr<PathDiagnosticPiece> +MisusedMovedObjectChecker::MovedBugVisitor::VisitNode(const ExplodedNode *N, + const ExplodedNode *PrevN, + BugReporterContext &BRC, + BugReport &BR) { + // We need only the last move of the reported object's region. + // The visitor walks the ExplodedGraph backwards. + if (Found) + return nullptr; + ProgramStateRef State = N->getState(); + ProgramStateRef StatePrev = PrevN->getState(); + const RegionState *TrackedObject = State->get<TrackedRegionMap>(Region); + const RegionState *TrackedObjectPrev = + StatePrev->get<TrackedRegionMap>(Region); + if (!TrackedObject) + return nullptr; + if (TrackedObjectPrev && TrackedObject) + return nullptr; + + // Retrieve the associated statement. + const Stmt *S = PathDiagnosticLocation::getStmt(N); + if (!S) + return nullptr; + Found = true; + + std::string ObjectName; + if (const auto DecReg = Region->getAs<DeclRegion>()) { + const auto *RegionDecl = dyn_cast<NamedDecl>(DecReg->getDecl()); + ObjectName = RegionDecl->getNameAsString(); + } + std::string InfoText; + if (ObjectName != "") + InfoText = "'" + ObjectName + "' became 'moved-from' here"; + else + InfoText = "Became 'moved-from' here"; + + // Generate the extra diagnostic. + PathDiagnosticLocation Pos(S, BRC.getSourceManager(), + N->getLocationContext()); + return std::make_shared<PathDiagnosticEventPiece>(Pos, InfoText, true); +} + +const ExplodedNode *MisusedMovedObjectChecker::getMoveLocation( + const ExplodedNode *N, const MemRegion *Region, CheckerContext &C) const { + // Walk the ExplodedGraph backwards and find the first node that referred to + // the tracked region. + const ExplodedNode *MoveNode = N; + + while (N) { + ProgramStateRef State = N->getState(); + if (!State->get<TrackedRegionMap>(Region)) + break; + MoveNode = N; + N = N->pred_empty() ? nullptr : *(N->pred_begin()); + } + return MoveNode; +} + +ExplodedNode *MisusedMovedObjectChecker::reportBug(const MemRegion *Region, + const CallEvent &Call, + CheckerContext &C, + bool isCopy = false) const { + if (ExplodedNode *N = C.generateNonFatalErrorNode()) { + if (!BT) + BT.reset(new BugType(this, "Usage of a 'moved-from' object", + "C++ move semantics")); + + // Uniqueing report to the same object. + PathDiagnosticLocation LocUsedForUniqueing; + const ExplodedNode *MoveNode = getMoveLocation(N, Region, C); + + if (const Stmt *MoveStmt = PathDiagnosticLocation::getStmt(MoveNode)) + LocUsedForUniqueing = PathDiagnosticLocation::createBegin( + MoveStmt, C.getSourceManager(), MoveNode->getLocationContext()); + + // Creating the error message. + std::string ErrorMessage; + if (isCopy) + ErrorMessage = "Copying a 'moved-from' object"; + else + ErrorMessage = "Method call on a 'moved-from' object"; + if (const auto DecReg = Region->getAs<DeclRegion>()) { + const auto *RegionDecl = dyn_cast<NamedDecl>(DecReg->getDecl()); + ErrorMessage += " '" + RegionDecl->getNameAsString() + "'"; + } + + auto R = + llvm::make_unique<BugReport>(*BT, ErrorMessage, N, LocUsedForUniqueing, + MoveNode->getLocationContext()->getDecl()); + R->addVisitor(llvm::make_unique<MovedBugVisitor>(Region)); + C.emitReport(std::move(R)); + return N; + } + return nullptr; +} + +// Removing the function parameters' MemRegion from the state. This is needed +// for PODs where the trivial destructor does not even created nor executed. +void MisusedMovedObjectChecker::checkEndFunction(CheckerContext &C) const { + auto State = C.getState(); + TrackedRegionMapTy Objects = State->get<TrackedRegionMap>(); + if (Objects.isEmpty()) + return; + + auto LC = C.getLocationContext(); + + const auto LD = dyn_cast_or_null<FunctionDecl>(LC->getDecl()); + if (!LD) + return; + llvm::SmallSet<const MemRegion *, 8> InvalidRegions; + + for (auto Param : LD->parameters()) { + auto Type = Param->getType().getTypePtrOrNull(); + if (!Type) + continue; + if (!Type->isPointerType() && !Type->isReferenceType()) { + InvalidRegions.insert(State->getLValue(Param, LC).getAsRegion()); + } + } + + if (InvalidRegions.empty()) + return; + + for (const auto &E : State->get<TrackedRegionMap>()) { + if (InvalidRegions.count(E.first->getBaseRegion())) + State = State->remove<TrackedRegionMap>(E.first); + } + + C.addTransition(State); +} + +void MisusedMovedObjectChecker::checkPostCall(const CallEvent &Call, + CheckerContext &C) const { + const auto *AFC = dyn_cast<AnyFunctionCall>(&Call); + if (!AFC) + return; + + ProgramStateRef State = C.getState(); + const auto MethodDecl = dyn_cast_or_null<CXXMethodDecl>(AFC->getDecl()); + if (!MethodDecl) + return; + + const auto *ConstructorDecl = dyn_cast<CXXConstructorDecl>(MethodDecl); + + const auto *CC = dyn_cast_or_null<CXXConstructorCall>(&Call); + // Check if an object became moved-from. + // Object can become moved from after a call to move assignment operator or + // move constructor . + if (ConstructorDecl && !ConstructorDecl->isMoveConstructor()) + return; + + if (!ConstructorDecl && !MethodDecl->isMoveAssignmentOperator()) + return; + + const auto ArgRegion = AFC->getArgSVal(0).getAsRegion(); + if (!ArgRegion) + return; + + // Skip moving the object to itself. + if (CC && CC->getCXXThisVal().getAsRegion() == ArgRegion) + return; + if (const auto *IC = dyn_cast<CXXInstanceCall>(AFC)) + if (IC->getCXXThisVal().getAsRegion() == ArgRegion) + return; + + const MemRegion *BaseRegion = ArgRegion->getBaseRegion(); + // Skip temp objects because of their short lifetime. + if (BaseRegion->getAs<CXXTempObjectRegion>() || + AFC->getArgExpr(0)->isRValue()) + return; + // If it has already been reported do not need to modify the state. + + if (State->get<TrackedRegionMap>(ArgRegion)) + return; + // Mark object as moved-from. + State = State->set<TrackedRegionMap>(ArgRegion, RegionState::getMoved()); + C.addTransition(State); +} + +bool MisusedMovedObjectChecker::isMoveSafeMethod( + const CXXMethodDecl *MethodDec) const { + // We abandon the cases where bool/void/void* conversion happens. + if (const auto *ConversionDec = + dyn_cast_or_null<CXXConversionDecl>(MethodDec)) { + const Type *Tp = ConversionDec->getConversionType().getTypePtrOrNull(); + if (!Tp) + return false; + if (Tp->isBooleanType() || Tp->isVoidType() || Tp->isVoidPointerType()) + return true; + } + // Function call `empty` can be skipped. + if (MethodDec && MethodDec->getDeclName().isIdentifier() && + (MethodDec->getName().lower() == "empty" || + MethodDec->getName().lower() == "isempty")) + return true; + + return false; +} + +bool MisusedMovedObjectChecker::isStateResetMethod( + const CXXMethodDecl *MethodDec) const { + if (MethodDec && MethodDec->getDeclName().isIdentifier()) { + std::string MethodName = MethodDec->getName().lower(); + if (MethodName == "reset" || MethodName == "clear" || + MethodName == "destroy") + return true; + } + return false; +} + +// Don't report an error inside a move related operation. +// We assume that the programmer knows what she does. +bool MisusedMovedObjectChecker::isInMoveSafeContext( + const LocationContext *LC) const { + do { + const auto *CtxDec = LC->getDecl(); + auto *CtorDec = dyn_cast_or_null<CXXConstructorDecl>(CtxDec); + auto *DtorDec = dyn_cast_or_null<CXXDestructorDecl>(CtxDec); + auto *MethodDec = dyn_cast_or_null<CXXMethodDecl>(CtxDec); + if (DtorDec || (CtorDec && CtorDec->isCopyOrMoveConstructor()) || + (MethodDec && MethodDec->isOverloadedOperator() && + MethodDec->getOverloadedOperator() == OO_Equal) || + isStateResetMethod(MethodDec) || isMoveSafeMethod(MethodDec)) + return true; + } while ((LC = LC->getParent())); + return false; +} + +void MisusedMovedObjectChecker::checkPreCall(const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + const LocationContext *LC = C.getLocationContext(); + ExplodedNode *N = nullptr; + + // Remove the MemRegions from the map on which a ctor/dtor call or assignement + // happened. + + // Checking constructor calls. + if (const auto *CC = dyn_cast<CXXConstructorCall>(&Call)) { + State = removeFromState(State, CC->getCXXThisVal().getAsRegion()); + auto CtorDec = CC->getDecl(); + // Check for copying a moved-from object and report the bug. + if (CtorDec && CtorDec->isCopyOrMoveConstructor()) { + const MemRegion *ArgRegion = CC->getArgSVal(0).getAsRegion(); + const RegionState *ArgState = State->get<TrackedRegionMap>(ArgRegion); + if (ArgState && ArgState->isMoved()) { + if (!isInMoveSafeContext(LC)) { + N = reportBug(ArgRegion, Call, C, /*isCopy=*/true); + State = State->set<TrackedRegionMap>(ArgRegion, + RegionState::getReported()); + } + } + } + C.addTransition(State, N); + return; + } + + const auto IC = dyn_cast<CXXInstanceCall>(&Call); + if (!IC) + return; + // In case of destructor call we do not track the object anymore. + const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion(); + if (dyn_cast_or_null<CXXDestructorDecl>(Call.getDecl())) { + State = removeFromState(State, IC->getCXXThisVal().getAsRegion()); + C.addTransition(State); + return; + } + + const auto MethodDecl = dyn_cast_or_null<CXXMethodDecl>(IC->getDecl()); + if (!MethodDecl) + return; + // Checking assignment operators. + bool OperatorEq = MethodDecl->isOverloadedOperator() && + MethodDecl->getOverloadedOperator() == OO_Equal; + // Remove the tracked object for every assignment operator, but report bug + // only for move or copy assignment's argument. + if (OperatorEq) { + State = removeFromState(State, ThisRegion); + if (MethodDecl->isCopyAssignmentOperator() || + MethodDecl->isMoveAssignmentOperator()) { + const RegionState *ArgState = + State->get<TrackedRegionMap>(IC->getArgSVal(0).getAsRegion()); + if (ArgState && ArgState->isMoved() && !isInMoveSafeContext(LC)) { + const MemRegion *ArgRegion = IC->getArgSVal(0).getAsRegion(); + N = reportBug(ArgRegion, Call, C, /*isCopy=*/true); + State = + State->set<TrackedRegionMap>(ArgRegion, RegionState::getReported()); + } + } + C.addTransition(State, N); + return; + } + + // The remaining part is check only for method call on a moved-from object. + if (isMoveSafeMethod(MethodDecl)) + return; + + if (isStateResetMethod(MethodDecl)) { + State = State->remove<TrackedRegionMap>(ThisRegion); + C.addTransition(State); + return; + } + + // If it is already reported then we dont report the bug again. + const RegionState *ThisState = State->get<TrackedRegionMap>(ThisRegion); + if (!(ThisState && ThisState->isMoved())) + return; + + // Dont report it in case if any base region is already reported + if (isAnyBaseRegionReported(State, ThisRegion)) + return; + + if (isInMoveSafeContext(LC)) + return; + + N = reportBug(ThisRegion, Call, C); + State = State->set<TrackedRegionMap>(ThisRegion, RegionState::getReported()); + C.addTransition(State, N); +} + +void MisusedMovedObjectChecker::checkDeadSymbols(SymbolReaper &SymReaper, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>(); + for (TrackedRegionMapTy::value_type E : TrackedRegions) { + const MemRegion *Region = E.first; + bool IsRegDead = !SymReaper.isLiveRegion(Region); + + // Remove the dead regions from the region map. + if (IsRegDead) { + State = State->remove<TrackedRegionMap>(Region); + } + } + C.addTransition(State); +} + +ProgramStateRef MisusedMovedObjectChecker::checkRegionChanges( + ProgramStateRef State, const InvalidatedSymbols *Invalidated, + ArrayRef<const MemRegion *> ExplicitRegions, + ArrayRef<const MemRegion *> Regions, const LocationContext *LCtx, + const CallEvent *Call) const { + // In case of an InstanceCall don't remove the ThisRegion from the GDM since + // it is handled in checkPreCall and checkPostCall. + const MemRegion *ThisRegion = nullptr; + if (const auto *IC = dyn_cast_or_null<CXXInstanceCall>(Call)) { + ThisRegion = IC->getCXXThisVal().getAsRegion(); + } + + for (ArrayRef<const MemRegion *>::iterator I = ExplicitRegions.begin(), + E = ExplicitRegions.end(); + I != E; ++I) { + const auto *Region = *I; + if (ThisRegion != Region) { + State = removeFromState(State, Region); + } + } + + return State; +} + +void ento::registerMisusedMovedObjectChecker(CheckerManager &mgr) { + mgr.registerChecker<MisusedMovedObjectChecker>(); +} diff --git a/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp b/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp index 1f82ab94af82..6d05159e51b0 100644 --- a/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp @@ -73,7 +73,7 @@ void NonNullParamChecker::checkPreCall(const CallEvent &Call, for (unsigned idx = 0; idx < NumArgs; ++idx) { // Check if the parameter is a reference. We want to report when reference - // to a null pointer is passed as a paramter. + // to a null pointer is passed as a parameter. bool haveRefTypeParam = false; if (TyI != TyE) { haveRefTypeParam = (*TyI)->isReferenceType(); diff --git a/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp b/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp index c14a87c9d2a4..21527d8c347a 100644 --- a/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp @@ -49,7 +49,7 @@ namespace { enum class Nullability : char { Contradicted, // Tracked nullability is contradicted by an explicit cast. Do // not report any nullability related issue for this symbol. - // This nullability is propagated agressively to avoid false + // This nullability is propagated aggressively to avoid false // positive results. See the comment on getMostNullable method. Nullable, Unspecified, @@ -57,7 +57,7 @@ enum class Nullability : char { }; /// Returns the most nullable nullability. This is used for message expressions -/// like [reciever method], where the nullability of this expression is either +/// like [receiver method], where the nullability of this expression is either /// the nullability of the receiver or the nullability of the return type of the /// method, depending on which is more nullable. Contradicted is considered to /// be the most nullable, to avoid false positive results. diff --git a/lib/StaticAnalyzer/Checkers/ObjCPropertyChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCPropertyChecker.cpp index b9857e51f3ea..dfd2c9afe7fb 100644 --- a/lib/StaticAnalyzer/Checkers/ObjCPropertyChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ObjCPropertyChecker.cpp @@ -58,8 +58,7 @@ void ObjCPropertyChecker::checkCopyMutable(const ObjCPropertyDecl *D, if (const ObjCInterfaceDecl *IntD = dyn_cast<ObjCInterfaceDecl>(D->getDeclContext())) { ImplD = IntD->getImplementation(); - } else { - const ObjCCategoryDecl *CatD = cast<ObjCCategoryDecl>(D->getDeclContext()); + } else if (auto *CatD = dyn_cast<ObjCCategoryDecl>(D->getDeclContext())) { ImplD = CatD->getClassInterface()->getImplementation(); } diff --git a/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp b/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp index eb101e12af25..3f6ae6222ce0 100644 --- a/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp @@ -2661,6 +2661,7 @@ public: const InvalidatedSymbols *invalidated, ArrayRef<const MemRegion *> ExplicitRegions, ArrayRef<const MemRegion *> Regions, + const LocationContext* LCtx, const CallEvent *Call) const; void checkPreStmt(const ReturnStmt *S, CheckerContext &C) const; @@ -3647,7 +3648,7 @@ void RetainCountChecker::checkBind(SVal loc, SVal val, const Stmt *S, // same state. SVal StoredVal = state->getSVal(regionLoc->getRegion()); if (StoredVal != val) - escapes = (state == (state->bindLoc(*regionLoc, val))); + escapes = (state == (state->bindLoc(*regionLoc, val, C.getLocationContext()))); } if (!escapes) { // Case 4: We do not currently model what happens when a symbol is @@ -3714,10 +3715,11 @@ ProgramStateRef RetainCountChecker::evalAssume(ProgramStateRef state, ProgramStateRef RetainCountChecker::checkRegionChanges(ProgramStateRef state, - const InvalidatedSymbols *invalidated, - ArrayRef<const MemRegion *> ExplicitRegions, - ArrayRef<const MemRegion *> Regions, - const CallEvent *Call) const { + const InvalidatedSymbols *invalidated, + ArrayRef<const MemRegion *> ExplicitRegions, + ArrayRef<const MemRegion *> Regions, + const LocationContext *LCtx, + const CallEvent *Call) const { if (!invalidated) return state; diff --git a/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp b/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp index 38d2aa6d8f9d..f3c2ffc58662 100644 --- a/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp @@ -35,6 +35,30 @@ public: }; } // end anonymous namespace +static bool isArrayIndexOutOfBounds(CheckerContext &C, const Expr *Ex) { + ProgramStateRef state = C.getState(); + const LocationContext *LCtx = C.getLocationContext(); + + if (!isa<ArraySubscriptExpr>(Ex)) + return false; + + SVal Loc = state->getSVal(Ex, LCtx); + if (!Loc.isValid()) + return false; + + const MemRegion *MR = Loc.castAs<loc::MemRegionVal>().getRegion(); + const ElementRegion *ER = dyn_cast<ElementRegion>(MR); + if (!ER) + return false; + + DefinedOrUnknownSVal Idx = ER->getIndex().castAs<DefinedOrUnknownSVal>(); + DefinedOrUnknownSVal NumElements = C.getStoreManager().getSizeInElements( + state, ER->getSuperRegion(), ER->getValueType()); + ProgramStateRef StInBound = state->assumeInBound(Idx, NumElements, true); + ProgramStateRef StOutBound = state->assumeInBound(Idx, NumElements, false); + return StOutBound && !StInBound; +} + void UndefResultChecker::checkPostStmt(const BinaryOperator *B, CheckerContext &C) const { ProgramStateRef state = C.getState(); @@ -77,6 +101,8 @@ void UndefResultChecker::checkPostStmt(const BinaryOperator *B, << " operand of '" << BinaryOperator::getOpcodeStr(B->getOpcode()) << "' is a garbage value"; + if (isArrayIndexOutOfBounds(C, Ex)) + OS << " due to array index out of bounds"; } else { // Neither operand was undefined, but the result is undefined. diff --git a/lib/StaticAnalyzer/Checkers/ValistChecker.cpp b/lib/StaticAnalyzer/Checkers/ValistChecker.cpp index 0b7a4865ddc2..d12ba6258073 100644 --- a/lib/StaticAnalyzer/Checkers/ValistChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ValistChecker.cpp @@ -54,11 +54,11 @@ public: void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const; private: - const MemRegion *getVAListAsRegion(SVal SV, CheckerContext &C) const; + const MemRegion *getVAListAsRegion(SVal SV, const Expr *VAExpr, + bool &IsSymbolic, CheckerContext &C) const; StringRef getVariableNameFromRegion(const MemRegion *Reg) const; const ExplodedNode *getStartCallSite(const ExplodedNode *N, - const MemRegion *Reg, - CheckerContext &C) const; + const MemRegion *Reg) const; void reportUninitializedAccess(const MemRegion *VAList, StringRef Msg, CheckerContext &C) const; @@ -138,14 +138,21 @@ void ValistChecker::checkPreCall(const CallEvent &Call, for (auto FuncInfo : VAListAccepters) { if (!Call.isCalled(FuncInfo.Func)) continue; + bool Symbolic; const MemRegion *VAList = - getVAListAsRegion(Call.getArgSVal(FuncInfo.VAListPos), C); + getVAListAsRegion(Call.getArgSVal(FuncInfo.VAListPos), + Call.getArgExpr(FuncInfo.VAListPos), Symbolic, C); if (!VAList) return; if (C.getState()->contains<InitializedVALists>(VAList)) return; + // We did not see va_start call, but the source of the region is unknown. + // Be conservative and assume the best. + if (Symbolic) + return; + SmallString<80> Errmsg("Function '"); Errmsg += FuncInfo.Func.getFunctionName(); Errmsg += "' is called with an uninitialized va_list argument"; @@ -155,13 +162,41 @@ void ValistChecker::checkPreCall(const CallEvent &Call, } } +const MemRegion *ValistChecker::getVAListAsRegion(SVal SV, const Expr *E, + bool &IsSymbolic, + CheckerContext &C) const { + const MemRegion *Reg = SV.getAsRegion(); + if (!Reg) + return nullptr; + // TODO: In the future this should be abstracted away by the analyzer. + bool VaListModelledAsArray = false; + if (const auto *Cast = dyn_cast<CastExpr>(E)) { + QualType Ty = Cast->getType(); + VaListModelledAsArray = + Ty->isPointerType() && Ty->getPointeeType()->isRecordType(); + } + if (const auto *DeclReg = Reg->getAs<DeclRegion>()) { + if (isa<ParmVarDecl>(DeclReg->getDecl())) + Reg = C.getState()->getSVal(SV.castAs<Loc>()).getAsRegion(); + } + IsSymbolic = Reg && Reg->getAs<SymbolicRegion>(); + // Some VarRegion based VA lists reach here as ElementRegions. + const auto *EReg = dyn_cast_or_null<ElementRegion>(Reg); + return (EReg && VaListModelledAsArray) ? EReg->getSuperRegion() : Reg; +} + void ValistChecker::checkPreStmt(const VAArgExpr *VAA, CheckerContext &C) const { ProgramStateRef State = C.getState(); - SVal VAListSVal = State->getSVal(VAA->getSubExpr(), C.getLocationContext()); - const MemRegion *VAList = getVAListAsRegion(VAListSVal, C); + const Expr *VASubExpr = VAA->getSubExpr(); + SVal VAListSVal = State->getSVal(VASubExpr, C.getLocationContext()); + bool Symbolic; + const MemRegion *VAList = + getVAListAsRegion(VAListSVal, VASubExpr, Symbolic, C); if (!VAList) return; + if (Symbolic) + return; if (!State->contains<InitializedVALists>(VAList)) reportUninitializedAccess( VAList, "va_arg() is called on an uninitialized va_list", C); @@ -183,22 +218,13 @@ void ValistChecker::checkDeadSymbols(SymbolReaper &SR, N); } -const MemRegion *ValistChecker::getVAListAsRegion(SVal SV, - CheckerContext &C) const { - const MemRegion *Reg = SV.getAsRegion(); - const auto *TReg = dyn_cast_or_null<TypedValueRegion>(Reg); - // Some VarRegion based VLAs reach here as ElementRegions. - const auto *EReg = dyn_cast_or_null<ElementRegion>(TReg); - return EReg ? EReg->getSuperRegion() : TReg; -} - // This function traverses the exploded graph backwards and finds the node where // the va_list is initialized. That node is used for uniquing the bug paths. // It is not likely that there are several different va_lists that belongs to // different stack frames, so that case is not yet handled. -const ExplodedNode *ValistChecker::getStartCallSite(const ExplodedNode *N, - const MemRegion *Reg, - CheckerContext &C) const { +const ExplodedNode * +ValistChecker::getStartCallSite(const ExplodedNode *N, + const MemRegion *Reg) const { const LocationContext *LeakContext = N->getLocationContext(); const ExplodedNode *StartCallNode = N; @@ -252,7 +278,7 @@ void ValistChecker::reportLeakedVALists(const RegionVector &LeakedVALists, BT_leakedvalist->setSuppressOnSink(true); } - const ExplodedNode *StartNode = getStartCallSite(N, Reg, C); + const ExplodedNode *StartNode = getStartCallSite(N, Reg); PathDiagnosticLocation LocUsedForUniqueing; if (const Stmt *StartCallStmt = PathDiagnosticLocation::getStmt(StartNode)) @@ -278,13 +304,17 @@ void ValistChecker::reportLeakedVALists(const RegionVector &LeakedVALists, void ValistChecker::checkVAListStartCall(const CallEvent &Call, CheckerContext &C, bool IsCopy) const { - const MemRegion *VAList = getVAListAsRegion(Call.getArgSVal(0), C); - ProgramStateRef State = C.getState(); + bool Symbolic; + const MemRegion *VAList = + getVAListAsRegion(Call.getArgSVal(0), Call.getArgExpr(0), Symbolic, C); if (!VAList) return; + ProgramStateRef State = C.getState(); + if (IsCopy) { - const MemRegion *Arg2 = getVAListAsRegion(Call.getArgSVal(1), C); + const MemRegion *Arg2 = + getVAListAsRegion(Call.getArgSVal(1), Call.getArgExpr(1), Symbolic, C); if (Arg2) { if (ChecksEnabled[CK_CopyToSelf] && VAList == Arg2) { RegionVector LeakedVALists{VAList}; @@ -292,7 +322,7 @@ void ValistChecker::checkVAListStartCall(const CallEvent &Call, reportLeakedVALists(LeakedVALists, "va_list", " is copied onto itself", C, N, true); return; - } else if (!State->contains<InitializedVALists>(Arg2)) { + } else if (!State->contains<InitializedVALists>(Arg2) && !Symbolic) { if (State->contains<InitializedVALists>(VAList)) { State = State->remove<InitializedVALists>(VAList); RegionVector LeakedVALists{VAList}; @@ -321,10 +351,17 @@ void ValistChecker::checkVAListStartCall(const CallEvent &Call, void ValistChecker::checkVAListEndCall(const CallEvent &Call, CheckerContext &C) const { - const MemRegion *VAList = getVAListAsRegion(Call.getArgSVal(0), C); + bool Symbolic; + const MemRegion *VAList = + getVAListAsRegion(Call.getArgSVal(0), Call.getArgExpr(0), Symbolic, C); if (!VAList) return; + // We did not see va_start call, but the source of the region is unknown. + // Be conservative and assume the best. + if (Symbolic) + return; + if (!C.getState()->contains<InitializedVALists>(VAList)) { reportUninitializedAccess( VAList, "va_end() is called on an uninitialized va_list", C); diff --git a/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp b/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp index 15422633ba33..45ef612ee1d5 100644 --- a/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp +++ b/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp @@ -230,7 +230,7 @@ bool AnalyzerOptions::shouldSuppressInlinedDefensiveChecks() { bool AnalyzerOptions::shouldSuppressFromCXXStandardLibrary() { return getBooleanOption(SuppressFromCXXStandardLibrary, "suppress-c++-stdlib", - /* Default = */ false); + /* Default = */ true); } bool AnalyzerOptions::shouldReportIssuesInMainSourceFile() { diff --git a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp index c3c3f2ff76ec..7309741d22e3 100644 --- a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp +++ b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp @@ -1027,7 +1027,7 @@ bool bugreporter::trackNullOrUndefValue(const ExplodedNode *N, R = LVState->getSVal(Inner, LVNode->getLocationContext()).getAsRegion(); // If this is a C++ reference to a null pointer, we are tracking the - // pointer. In additon, we should find the store at which the reference + // pointer. In addition, we should find the store at which the reference // got initialized. if (const MemRegion *RR = getLocationRegionIfReference(Inner, N)) { if (Optional<KnownSVal> KV = LVal.getAs<KnownSVal>()) @@ -1290,7 +1290,7 @@ std::shared_ptr<PathDiagnosticPiece> ConditionBRVisitor::VisitTerminator( break; case Stmt::BinaryOperatorClass: // When we encounter a logical operator (&& or ||) as a CFG terminator, - // then the condition is actually its LHS; otheriwse, we'd encounter + // then the condition is actually its LHS; otherwise, we'd encounter // the parent, such as if-statement, as a terminator. const auto *BO = cast<BinaryOperator>(Term); assert(BO->isLogicalOp() && @@ -1659,7 +1659,7 @@ LikelyFalsePositiveSuppressionBRVisitor::getEndPath(BugReporterContext &BRC, // The analyzer issues a false use-after-free when std::list::pop_front // or std::list::pop_back are called multiple times because we cannot - // reason about the internal invariants of the datastructure. + // reason about the internal invariants of the data structure. if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(D)) { const CXXRecordDecl *CD = MD->getParent(); if (CD->getName() == "list") { @@ -1690,7 +1690,7 @@ LikelyFalsePositiveSuppressionBRVisitor::getEndPath(BugReporterContext &BRC, // and // std::u16string s; s += u'a'; // because we cannot reason about the internal invariants of the - // datastructure. + // data structure. if (CD->getName() == "basic_string") { BR.markInvalid(getTag(), nullptr); return nullptr; diff --git a/lib/StaticAnalyzer/Core/CMakeLists.txt b/lib/StaticAnalyzer/Core/CMakeLists.txt index aaffb0b82ce0..85878f5e96ee 100644 --- a/lib/StaticAnalyzer/Core/CMakeLists.txt +++ b/lib/StaticAnalyzer/Core/CMakeLists.txt @@ -1,5 +1,12 @@ set(LLVM_LINK_COMPONENTS support) +# Link Z3 if the user wants to build it. +if(CLANG_ANALYZER_WITH_Z3) + set(Z3_LINK_FILES ${Z3_LIBRARIES}) +else() + set(Z3_LINK_FILES "") +endif() + add_clang_library(clangStaticAnalyzerCore APSIntType.cpp AnalysisManager.cpp @@ -34,6 +41,7 @@ add_clang_library(clangStaticAnalyzerCore PlistDiagnostics.cpp ProgramState.cpp RangeConstraintManager.cpp + RangedConstraintManager.cpp RegionStore.cpp SValBuilder.cpp SVals.cpp @@ -42,6 +50,7 @@ add_clang_library(clangStaticAnalyzerCore Store.cpp SubEngine.cpp SymbolManager.cpp + Z3ConstraintManager.cpp LINK_LIBS clangAST @@ -49,4 +58,12 @@ add_clang_library(clangStaticAnalyzerCore clangBasic clangLex clangRewrite + ${Z3_LINK_FILES} ) + +if(CLANG_ANALYZER_WITH_Z3) + target_include_directories(clangStaticAnalyzerCore SYSTEM + PRIVATE + ${Z3_INCLUDE_DIR} + ) +endif() diff --git a/lib/StaticAnalyzer/Core/CallEvent.cpp b/lib/StaticAnalyzer/Core/CallEvent.cpp index 420e2a6b5c8c..ee761689f479 100644 --- a/lib/StaticAnalyzer/Core/CallEvent.cpp +++ b/lib/StaticAnalyzer/Core/CallEvent.cpp @@ -212,9 +212,12 @@ ProgramPoint CallEvent::getProgramPoint(bool IsPreVisit, bool CallEvent::isCalled(const CallDescription &CD) const { assert(getKind() != CE_ObjCMessage && "Obj-C methods are not supported"); - if (!CD.II) + if (!CD.IsLookupDone) { + CD.IsLookupDone = true; CD.II = &getState()->getStateManager().getContext().Idents.get(CD.FuncName); - if (getCalleeIdentifier() != CD.II) + } + const IdentifierInfo *II = getCalleeIdentifier(); + if (!II || II != CD.II) return false; return (CD.RequiredArgs == CallDescription::NoArgRequirement || CD.RequiredArgs == getNumArgs()); @@ -692,13 +695,15 @@ void ObjCMethodCall::getExtraInvalidatedValues( if (const ObjCPropertyDecl *PropDecl = getAccessedProperty()) { if (const ObjCIvarDecl *PropIvar = PropDecl->getPropertyIvarDecl()) { SVal IvarLVal = getState()->getLValue(PropIvar, getReceiverSVal()); - const MemRegion *IvarRegion = IvarLVal.getAsRegion(); - ETraits->setTrait( + if (const MemRegion *IvarRegion = IvarLVal.getAsRegion()) { + ETraits->setTrait( IvarRegion, RegionAndSymbolInvalidationTraits::TK_DoNotInvalidateSuperRegion); - ETraits->setTrait(IvarRegion, - RegionAndSymbolInvalidationTraits::TK_SuppressEscape); - Values.push_back(IvarLVal); + ETraits->setTrait( + IvarRegion, + RegionAndSymbolInvalidationTraits::TK_SuppressEscape); + Values.push_back(IvarLVal); + } return; } } @@ -896,6 +901,38 @@ bool ObjCMethodCall::canBeOverridenInSubclass(ObjCInterfaceDecl *IDecl, llvm_unreachable("The while loop should always terminate."); } +static const ObjCMethodDecl *findDefiningRedecl(const ObjCMethodDecl *MD) { + if (!MD) + return MD; + + // Find the redeclaration that defines the method. + if (!MD->hasBody()) { + for (auto I : MD->redecls()) + if (I->hasBody()) + MD = cast<ObjCMethodDecl>(I); + } + return MD; +} + +static bool isCallToSelfClass(const ObjCMessageExpr *ME) { + const Expr* InstRec = ME->getInstanceReceiver(); + if (!InstRec) + return false; + const auto *InstRecIg = dyn_cast<DeclRefExpr>(InstRec->IgnoreParenImpCasts()); + + // Check that receiver is called 'self'. + if (!InstRecIg || !InstRecIg->getFoundDecl() || + !InstRecIg->getFoundDecl()->getName().equals("self")) + return false; + + // Check that the method name is 'class'. + if (ME->getSelector().getNumArgs() != 0 || + !ME->getSelector().getNameForSlot(0).equals("class")) + return false; + + return true; +} + RuntimeDefinition ObjCMethodCall::getRuntimeDefinition() const { const ObjCMessageExpr *E = getOriginExpr(); assert(E); @@ -910,6 +947,7 @@ RuntimeDefinition ObjCMethodCall::getRuntimeDefinition() const { const MemRegion *Receiver = nullptr; if (!SupersType.isNull()) { + // The receiver is guaranteed to be 'super' in this case. // Super always means the type of immediate predecessor to the method // where the call occurs. ReceiverT = cast<ObjCObjectPointerType>(SupersType); @@ -921,7 +959,7 @@ RuntimeDefinition ObjCMethodCall::getRuntimeDefinition() const { DynamicTypeInfo DTI = getDynamicTypeInfo(getState(), Receiver); QualType DynType = DTI.getType(); CanBeSubClassed = DTI.canBeASubClass(); - ReceiverT = dyn_cast<ObjCObjectPointerType>(DynType); + ReceiverT = dyn_cast<ObjCObjectPointerType>(DynType.getCanonicalType()); if (ReceiverT && CanBeSubClassed) if (ObjCInterfaceDecl *IDecl = ReceiverT->getInterfaceDecl()) @@ -929,7 +967,32 @@ RuntimeDefinition ObjCMethodCall::getRuntimeDefinition() const { CanBeSubClassed = false; } - // Lookup the method implementation. + // Handle special cases of '[self classMethod]' and + // '[[self class] classMethod]', which are treated by the compiler as + // instance (not class) messages. We will statically dispatch to those. + if (auto *PT = dyn_cast_or_null<ObjCObjectPointerType>(ReceiverT)) { + // For [self classMethod], return the compiler visible declaration. + if (PT->getObjectType()->isObjCClass() && + Receiver == getSelfSVal().getAsRegion()) + return RuntimeDefinition(findDefiningRedecl(E->getMethodDecl())); + + // Similarly, handle [[self class] classMethod]. + // TODO: We are currently doing a syntactic match for this pattern with is + // limiting as the test cases in Analysis/inlining/InlineObjCClassMethod.m + // shows. A better way would be to associate the meta type with the symbol + // using the dynamic type info tracking and use it here. We can add a new + // SVal for ObjC 'Class' values that know what interface declaration they + // come from. Then 'self' in a class method would be filled in with + // something meaningful in ObjCMethodCall::getReceiverSVal() and we could + // do proper dynamic dispatch for class methods just like we do for + // instance methods now. + if (E->getInstanceReceiver()) + if (const auto *M = dyn_cast<ObjCMessageExpr>(E->getInstanceReceiver())) + if (isCallToSelfClass(M)) + return RuntimeDefinition(findDefiningRedecl(E->getMethodDecl())); + } + + // Lookup the instance method implementation. if (ReceiverT) if (ObjCInterfaceDecl *IDecl = ReceiverT->getInterfaceDecl()) { // Repeatedly calling lookupPrivateMethod() is expensive, especially diff --git a/lib/StaticAnalyzer/Core/CheckerManager.cpp b/lib/StaticAnalyzer/Core/CheckerManager.cpp index 79e204cdafec..49f3edef2a2d 100644 --- a/lib/StaticAnalyzer/Core/CheckerManager.cpp +++ b/lib/StaticAnalyzer/Core/CheckerManager.cpp @@ -521,17 +521,19 @@ void CheckerManager::runCheckersForDeadSymbols(ExplodedNodeSet &Dst, /// \brief Run checkers for region changes. ProgramStateRef CheckerManager::runCheckersForRegionChanges(ProgramStateRef state, - const InvalidatedSymbols *invalidated, - ArrayRef<const MemRegion *> ExplicitRegions, - ArrayRef<const MemRegion *> Regions, - const CallEvent *Call) { + const InvalidatedSymbols *invalidated, + ArrayRef<const MemRegion *> ExplicitRegions, + ArrayRef<const MemRegion *> Regions, + const LocationContext *LCtx, + const CallEvent *Call) { for (unsigned i = 0, e = RegionChangesCheckers.size(); i != e; ++i) { // If any checker declares the state infeasible (or if it starts that way), // bail out. if (!state) return nullptr; state = RegionChangesCheckers[i](state, invalidated, - ExplicitRegions, Regions, Call); + ExplicitRegions, Regions, + LCtx, Call); } return state; } diff --git a/lib/StaticAnalyzer/Core/ConstraintManager.cpp b/lib/StaticAnalyzer/Core/ConstraintManager.cpp index b7db8333aaac..8de2b0e8d271 100644 --- a/lib/StaticAnalyzer/Core/ConstraintManager.cpp +++ b/lib/StaticAnalyzer/Core/ConstraintManager.cpp @@ -20,8 +20,8 @@ ConstraintManager::~ConstraintManager() {} static DefinedSVal getLocFromSymbol(const ProgramStateRef &State, SymbolRef Sym) { - const MemRegion *R = State->getStateManager().getRegionManager() - .getSymbolicRegion(Sym); + const MemRegion *R = + State->getStateManager().getRegionManager().getSymbolicRegion(Sym); return loc::MemRegionVal(R); } diff --git a/lib/StaticAnalyzer/Core/DynamicTypeMap.cpp b/lib/StaticAnalyzer/Core/DynamicTypeMap.cpp index fd35b664a912..a01ff36a8aae 100644 --- a/lib/StaticAnalyzer/Core/DynamicTypeMap.cpp +++ b/lib/StaticAnalyzer/Core/DynamicTypeMap.cpp @@ -8,7 +8,7 @@ //===----------------------------------------------------------------------===// // // This file defines APIs that track and query dynamic type information. This -// information can be used to devirtualize calls during the symbolic exection +// information can be used to devirtualize calls during the symbolic execution // or do type checking. // //===----------------------------------------------------------------------===// diff --git a/lib/StaticAnalyzer/Core/ExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngine.cpp index d563f8e9eac0..9e6ec09010e9 100644 --- a/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -182,19 +182,25 @@ ProgramStateRef ExprEngine::getInitialState(const LocationContext *InitLoc) { ProgramStateRef ExprEngine::createTemporaryRegionIfNeeded(ProgramStateRef State, const LocationContext *LC, - const Expr *Ex, + const Expr *InitWithAdjustments, const Expr *Result) { - SVal V = State->getSVal(Ex, LC); + // FIXME: This function is a hack that works around the quirky AST + // we're often having with respect to C++ temporaries. If only we modelled + // the actual execution order of statements properly in the CFG, + // all the hassle with adjustments would not be necessary, + // and perhaps the whole function would be removed. + SVal InitValWithAdjustments = State->getSVal(InitWithAdjustments, LC); if (!Result) { // If we don't have an explicit result expression, we're in "if needed" // mode. Only create a region if the current value is a NonLoc. - if (!V.getAs<NonLoc>()) + if (!InitValWithAdjustments.getAs<NonLoc>()) return State; - Result = Ex; + Result = InitWithAdjustments; } else { // We need to create a region no matter what. For sanity, make sure we don't // try to stuff a Loc into a non-pointer temporary region. - assert(!V.getAs<Loc>() || Loc::isLocType(Result->getType()) || + assert(!InitValWithAdjustments.getAs<Loc>() || + Loc::isLocType(Result->getType()) || Result->getType()->isMemberPointerType()); } @@ -226,7 +232,8 @@ ExprEngine::createTemporaryRegionIfNeeded(ProgramStateRef State, SmallVector<const Expr *, 2> CommaLHSs; SmallVector<SubobjectAdjustment, 2> Adjustments; - const Expr *Init = Ex->skipRValueSubobjectAdjustments(CommaLHSs, Adjustments); + const Expr *Init = InitWithAdjustments->skipRValueSubobjectAdjustments( + CommaLHSs, Adjustments); const TypedValueRegion *TR = nullptr; if (const MaterializeTemporaryExpr *MT = @@ -241,6 +248,7 @@ ExprEngine::createTemporaryRegionIfNeeded(ProgramStateRef State, TR = MRMgr.getCXXTempObjectRegion(Init, LC); SVal Reg = loc::MemRegionVal(TR); + SVal BaseReg = Reg; // Make the necessary adjustments to obtain the sub-object. for (auto I = Adjustments.rbegin(), E = Adjustments.rend(); I != E; ++I) { @@ -254,19 +262,47 @@ ExprEngine::createTemporaryRegionIfNeeded(ProgramStateRef State, break; case SubobjectAdjustment::MemberPointerAdjustment: // FIXME: Unimplemented. - State->bindDefault(Reg, UnknownVal()); + State = State->bindDefault(Reg, UnknownVal(), LC); return State; } } - // Try to recover some path sensitivity in case we couldn't compute the value. - if (V.isUnknown()) - V = getSValBuilder().conjureSymbolVal(Result, LC, TR->getValueType(), - currBldrCtx->blockCount()); - // Bind the value of the expression to the sub-object region, and then bind - // the sub-object region to our expression. - State = State->bindLoc(Reg, V); + // What remains is to copy the value of the object to the new region. + // FIXME: In other words, what we should always do is copy value of the + // Init expression (which corresponds to the bigger object) to the whole + // temporary region TR. However, this value is often no longer present + // in the Environment. If it has disappeared, we instead invalidate TR. + // Still, what we can do is assign the value of expression Ex (which + // corresponds to the sub-object) to the TR's sub-region Reg. At least, + // values inside Reg would be correct. + SVal InitVal = State->getSVal(Init, LC); + if (InitVal.isUnknown()) { + InitVal = getSValBuilder().conjureSymbolVal(Result, LC, Init->getType(), + currBldrCtx->blockCount()); + State = State->bindLoc(BaseReg.castAs<Loc>(), InitVal, LC, false); + + // Then we'd need to take the value that certainly exists and bind it over. + if (InitValWithAdjustments.isUnknown()) { + // Try to recover some path sensitivity in case we couldn't + // compute the value. + InitValWithAdjustments = getSValBuilder().conjureSymbolVal( + Result, LC, InitWithAdjustments->getType(), + currBldrCtx->blockCount()); + } + State = + State->bindLoc(Reg.castAs<Loc>(), InitValWithAdjustments, LC, false); + } else { + State = State->bindLoc(BaseReg.castAs<Loc>(), InitVal, LC, false); + } + + // The result expression would now point to the correct sub-region of the + // newly created temporary region. Do this last in order to getSVal of Init + // correctly in case (Result == Init). State = State->BindExpr(Result, LC, Reg); + + // Notify checkers once for two bindLoc()s. + State = processRegionChange(State, TR, LC); + return State; } @@ -286,9 +322,11 @@ ExprEngine::processRegionChanges(ProgramStateRef state, const InvalidatedSymbols *invalidated, ArrayRef<const MemRegion *> Explicits, ArrayRef<const MemRegion *> Regions, + const LocationContext *LCtx, const CallEvent *Call) { return getCheckerManager().runCheckersForRegionChanges(state, invalidated, - Explicits, Regions, Call); + Explicits, Regions, + LCtx, Call); } void ExprEngine::printState(raw_ostream &Out, ProgramStateRef State, @@ -613,7 +651,15 @@ void ExprEngine::ProcessAutomaticObjDtor(const CFGAutomaticObjDtor Dtor, const MemRegion *Region = dest.castAs<loc::MemRegionVal>().getRegion(); if (varType->isReferenceType()) { - Region = state->getSVal(Region).getAsRegion()->getBaseRegion(); + const MemRegion *ValueRegion = state->getSVal(Region).getAsRegion(); + if (!ValueRegion) { + // FIXME: This should not happen. The language guarantees a presence + // of a valid initializer here, so the reference shall not be undefined. + // It seems that we're calling destructors over variables that + // were not initialized yet. + return; + } + Region = ValueRegion->getBaseRegion(); varType = cast<TypedValueRegion>(Region)->getValueType(); } @@ -767,7 +813,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, assert(!isa<Expr>(S) || S == cast<Expr>(S)->IgnoreParens()); switch (S->getStmtClass()) { - // C++ and ARC stuff we don't support yet. + // C++, OpenMP and ARC stuff we don't support yet. case Expr::ObjCIndirectCopyRestoreExprClass: case Stmt::CXXDependentScopeMemberExprClass: case Stmt::CXXInheritedCtorInitExprClass: @@ -790,41 +836,13 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::FunctionParmPackExprClass: case Stmt::CoroutineBodyStmtClass: case Stmt::CoawaitExprClass: + case Stmt::DependentCoawaitExprClass: case Stmt::CoreturnStmtClass: case Stmt::CoyieldExprClass: case Stmt::SEHTryStmtClass: case Stmt::SEHExceptStmtClass: case Stmt::SEHLeaveStmtClass: - case Stmt::SEHFinallyStmtClass: { - const ExplodedNode *node = Bldr.generateSink(S, Pred, Pred->getState()); - Engine.addAbortedBlock(node, currBldrCtx->getBlock()); - break; - } - - case Stmt::ParenExprClass: - llvm_unreachable("ParenExprs already handled."); - case Stmt::GenericSelectionExprClass: - llvm_unreachable("GenericSelectionExprs already handled."); - // Cases that should never be evaluated simply because they shouldn't - // appear in the CFG. - case Stmt::BreakStmtClass: - case Stmt::CaseStmtClass: - case Stmt::CompoundStmtClass: - case Stmt::ContinueStmtClass: - case Stmt::CXXForRangeStmtClass: - case Stmt::DefaultStmtClass: - case Stmt::DoStmtClass: - case Stmt::ForStmtClass: - case Stmt::GotoStmtClass: - case Stmt::IfStmtClass: - case Stmt::IndirectGotoStmtClass: - case Stmt::LabelStmtClass: - case Stmt::NoStmtClass: - case Stmt::NullStmtClass: - case Stmt::SwitchStmtClass: - case Stmt::WhileStmtClass: - case Expr::MSDependentExistsStmtClass: - case Stmt::CapturedStmtClass: + case Stmt::SEHFinallyStmtClass: case Stmt::OMPParallelDirectiveClass: case Stmt::OMPSimdDirectiveClass: case Stmt::OMPForDirectiveClass: @@ -872,6 +890,36 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::OMPTargetTeamsDistributeParallelForDirectiveClass: case Stmt::OMPTargetTeamsDistributeParallelForSimdDirectiveClass: case Stmt::OMPTargetTeamsDistributeSimdDirectiveClass: + case Stmt::CapturedStmtClass: + { + const ExplodedNode *node = Bldr.generateSink(S, Pred, Pred->getState()); + Engine.addAbortedBlock(node, currBldrCtx->getBlock()); + break; + } + + case Stmt::ParenExprClass: + llvm_unreachable("ParenExprs already handled."); + case Stmt::GenericSelectionExprClass: + llvm_unreachable("GenericSelectionExprs already handled."); + // Cases that should never be evaluated simply because they shouldn't + // appear in the CFG. + case Stmt::BreakStmtClass: + case Stmt::CaseStmtClass: + case Stmt::CompoundStmtClass: + case Stmt::ContinueStmtClass: + case Stmt::CXXForRangeStmtClass: + case Stmt::DefaultStmtClass: + case Stmt::DoStmtClass: + case Stmt::ForStmtClass: + case Stmt::GotoStmtClass: + case Stmt::IfStmtClass: + case Stmt::IndirectGotoStmtClass: + case Stmt::LabelStmtClass: + case Stmt::NoStmtClass: + case Stmt::NullStmtClass: + case Stmt::SwitchStmtClass: + case Stmt::WhileStmtClass: + case Expr::MSDependentExistsStmtClass: llvm_unreachable("Stmt should not be in analyzer evaluation loop"); case Stmt::ObjCSubscriptRefExprClass: @@ -2165,7 +2213,9 @@ public: // (3) We are binding to a MemRegion with stack storage that the store // does not understand. ProgramStateRef ExprEngine::processPointerEscapedOnBind(ProgramStateRef State, - SVal Loc, SVal Val) { + SVal Loc, + SVal Val, + const LocationContext *LCtx) { // Are we storing to something that causes the value to "escape"? bool escapes = true; @@ -2181,7 +2231,7 @@ ProgramStateRef ExprEngine::processPointerEscapedOnBind(ProgramStateRef State, // same state. SVal StoredVal = State->getSVal(regionLoc->getRegion()); if (StoredVal != Val) - escapes = (State == (State->bindLoc(*regionLoc, Val))); + escapes = (State == (State->bindLoc(*regionLoc, Val, LCtx))); } } @@ -2278,7 +2328,7 @@ void ExprEngine::evalBind(ExplodedNodeSet &Dst, const Stmt *StoreE, const ProgramPoint L = PostStore(StoreE, LC, /*Loc*/nullptr, /*tag*/nullptr); ProgramStateRef state = Pred->getState(); - state = processPointerEscapedOnBind(state, location, Val); + state = processPointerEscapedOnBind(state, location, Val, LC); Bldr.generateNode(L, state, Pred); return; } @@ -2288,13 +2338,13 @@ void ExprEngine::evalBind(ExplodedNodeSet &Dst, const Stmt *StoreE, ExplodedNode *PredI = *I; ProgramStateRef state = PredI->getState(); - state = processPointerEscapedOnBind(state, location, Val); + state = processPointerEscapedOnBind(state, location, Val, LC); // When binding the value, pass on the hint that this is a initialization. // For initializations, we do not need to inform clients of region // changes. state = state->bindLoc(location.castAs<Loc>(), - Val, /* notifyChanges = */ !atDeclInit); + Val, LC, /* notifyChanges = */ !atDeclInit); const MemRegion *LocReg = nullptr; if (Optional<loc::MemRegionVal> LocRegVal = @@ -2520,7 +2570,7 @@ void ExprEngine::VisitGCCAsmStmt(const GCCAsmStmt *A, ExplodedNode *Pred, assert (!X.getAs<NonLoc>()); // Should be an Lval, or unknown, undef. if (Optional<Loc> LV = X.getAs<Loc>()) - state = state->bindLoc(*LV, UnknownVal()); + state = state->bindLoc(*LV, UnknownVal(), Pred->getLocationContext()); } Bldr.generateNode(A, Pred, state); diff --git a/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/lib/StaticAnalyzer/Core/ExprEngineC.cpp index 89fab1d56af0..8f720a2067b1 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineC.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineC.cpp @@ -227,12 +227,13 @@ void ExprEngine::VisitBlockExpr(const BlockExpr *BE, ExplodedNode *Pred, if (capturedR != originalR) { SVal originalV; + const LocationContext *LCtx = Pred->getLocationContext(); if (copyExpr) { - originalV = State->getSVal(copyExpr, Pred->getLocationContext()); + originalV = State->getSVal(copyExpr, LCtx); } else { originalV = State->getSVal(loc::MemRegionVal(originalR)); } - State = State->bindLoc(loc::MemRegionVal(capturedR), originalV); + State = State->bindLoc(loc::MemRegionVal(capturedR), originalV, LCtx); } } } @@ -534,7 +535,7 @@ void ExprEngine::VisitCompoundLiteralExpr(const CompoundLiteralExpr *CL, } else { assert(isa<InitListExpr>(Init)); Loc CLLoc = State->getLValue(CL, LCtx); - State = State->bindLoc(CLLoc, V); + State = State->bindLoc(CLLoc, V, LCtx); if (CL->isGLValue()) V = CLLoc; @@ -1053,7 +1054,7 @@ void ExprEngine::VisitIncrementDecrementOperator(const UnaryOperator* U, // Conjure a new symbol if necessary to recover precision. if (Result.isUnknown()){ DefinedOrUnknownSVal SymVal = - svalBuilder.conjureSymbolVal(nullptr, Ex, LCtx, + svalBuilder.conjureSymbolVal(nullptr, U, LCtx, currBldrCtx->blockCount()); Result = SymVal; diff --git a/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp index 7e9b2033ca37..03e0095d0e83 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -317,7 +317,7 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE, // actually make things worse. Placement new makes this tricky as well, // since it's then possible to be initializing one part of a multi- // dimensional array. - State = State->bindDefault(loc::MemRegionVal(Target), ZeroVal); + State = State->bindDefault(loc::MemRegionVal(Target), ZeroVal, LCtx); Bldr.generateNode(CE, *I, State, /*tag=*/nullptr, ProgramPoint::PreStmtKind); } @@ -512,7 +512,8 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred, if (CNE->isArray()) { // FIXME: allocating an array requires simulating the constructors. // For now, just return a symbolicated region. - const MemRegion *NewReg = symVal.castAs<loc::MemRegionVal>().getRegion(); + const SubRegion *NewReg = + symVal.castAs<loc::MemRegionVal>().getRegionAs<SubRegion>(); QualType ObjTy = CNE->getType()->getAs<PointerType>()->getPointeeType(); const ElementRegion *EleReg = getStoreManager().GetElementZeroRegion(NewReg, ObjTy); @@ -572,7 +573,7 @@ void ExprEngine::VisitCXXCatchStmt(const CXXCatchStmt *CS, SVal V = svalBuilder.conjureSymbolVal(CS, LCtx, VD->getType(), currBldrCtx->blockCount()); ProgramStateRef state = Pred->getState(); - state = state->bindLoc(state->getLValue(VD, LCtx), V); + state = state->bindLoc(state->getLValue(VD, LCtx), V, LCtx); StmtNodeBuilder Bldr(Pred, Dst, *currBldrCtx); Bldr.generateNode(CS, Pred, state); @@ -627,7 +628,7 @@ void ExprEngine::VisitLambdaExpr(const LambdaExpr *LE, ExplodedNode *Pred, InitVal = State->getSVal(SizeExpr, LocCtxt); } - State = State->bindLoc(FieldLoc, InitVal); + State = State->bindLoc(FieldLoc, InitVal, LocCtxt); } // Decay the Loc into an RValue, because there might be a diff --git a/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp b/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp index 92c5fe6b6f1a..f5e64f4a5a8c 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp @@ -115,11 +115,11 @@ void ExprEngine::VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S, SymbolRef Sym = SymMgr.conjureSymbol(elem, LCtx, T, currBldrCtx->blockCount()); SVal V = svalBuilder.makeLoc(Sym); - hasElems = hasElems->bindLoc(elementV, V); + hasElems = hasElems->bindLoc(elementV, V, LCtx); // Bind the location to 'nil' on the false branch. SVal nilV = svalBuilder.makeIntVal(0, T); - noElems = noElems->bindLoc(elementV, nilV); + noElems = noElems->bindLoc(elementV, nilV, LCtx); } // Create the new nodes. diff --git a/lib/StaticAnalyzer/Core/MemRegion.cpp b/lib/StaticAnalyzer/Core/MemRegion.cpp index d6e8fe5b51b3..7bc186d5b994 100644 --- a/lib/StaticAnalyzer/Core/MemRegion.cpp +++ b/lib/StaticAnalyzer/Core/MemRegion.cpp @@ -31,54 +31,56 @@ using namespace ento; // MemRegion Construction. //===----------------------------------------------------------------------===// -template <typename RegionTy, typename A1> -RegionTy* MemRegionManager::getSubRegion(const A1 a1, - const MemRegion *superRegion) { +template <typename RegionTy, typename SuperTy, typename Arg1Ty> +RegionTy* MemRegionManager::getSubRegion(const Arg1Ty arg1, + const SuperTy *superRegion) { llvm::FoldingSetNodeID ID; - RegionTy::ProfileRegion(ID, a1, superRegion); + RegionTy::ProfileRegion(ID, arg1, superRegion); void *InsertPos; RegionTy* R = cast_or_null<RegionTy>(Regions.FindNodeOrInsertPos(ID, InsertPos)); if (!R) { R = A.Allocate<RegionTy>(); - new (R) RegionTy(a1, superRegion); + new (R) RegionTy(arg1, superRegion); Regions.InsertNode(R, InsertPos); } return R; } -template <typename RegionTy, typename A1, typename A2> -RegionTy* MemRegionManager::getSubRegion(const A1 a1, const A2 a2, - const MemRegion *superRegion) { +template <typename RegionTy, typename SuperTy, typename Arg1Ty, typename Arg2Ty> +RegionTy* MemRegionManager::getSubRegion(const Arg1Ty arg1, const Arg2Ty arg2, + const SuperTy *superRegion) { llvm::FoldingSetNodeID ID; - RegionTy::ProfileRegion(ID, a1, a2, superRegion); + RegionTy::ProfileRegion(ID, arg1, arg2, superRegion); void *InsertPos; RegionTy* R = cast_or_null<RegionTy>(Regions.FindNodeOrInsertPos(ID, InsertPos)); if (!R) { R = A.Allocate<RegionTy>(); - new (R) RegionTy(a1, a2, superRegion); + new (R) RegionTy(arg1, arg2, superRegion); Regions.InsertNode(R, InsertPos); } return R; } -template <typename RegionTy, typename A1, typename A2, typename A3> -RegionTy* MemRegionManager::getSubRegion(const A1 a1, const A2 a2, const A3 a3, - const MemRegion *superRegion) { +template <typename RegionTy, typename SuperTy, + typename Arg1Ty, typename Arg2Ty, typename Arg3Ty> +RegionTy* MemRegionManager::getSubRegion(const Arg1Ty arg1, const Arg2Ty arg2, + const Arg3Ty arg3, + const SuperTy *superRegion) { llvm::FoldingSetNodeID ID; - RegionTy::ProfileRegion(ID, a1, a2, a3, superRegion); + RegionTy::ProfileRegion(ID, arg1, arg2, arg3, superRegion); void *InsertPos; RegionTy* R = cast_or_null<RegionTy>(Regions.FindNodeOrInsertPos(ID, InsertPos)); if (!R) { R = A.Allocate<RegionTy>(); - new (R) RegionTy(a1, a2, a3, superRegion); + new (R) RegionTy(arg1, arg2, arg3, superRegion); Regions.InsertNode(R, InsertPos); } @@ -180,8 +182,8 @@ DefinedOrUnknownSVal StringRegion::getExtent(SValBuilder &svalBuilder) const { svalBuilder.getArrayIndexType()); } -ObjCIvarRegion::ObjCIvarRegion(const ObjCIvarDecl *ivd, const MemRegion* sReg) - : DeclRegion(ivd, sReg, ObjCIvarRegionKind) {} +ObjCIvarRegion::ObjCIvarRegion(const ObjCIvarDecl *ivd, const SubRegion *sReg) + : DeclRegion(ivd, sReg, ObjCIvarRegionKind) {} const ObjCIvarDecl *ObjCIvarRegion::getDecl() const { return cast<ObjCIvarDecl>(D); @@ -379,10 +381,8 @@ void CXXBaseObjectRegion::Profile(llvm::FoldingSetNodeID &ID) const { //===----------------------------------------------------------------------===// void GlobalsSpaceRegion::anchor() { } -void HeapSpaceRegion::anchor() { } -void UnknownSpaceRegion::anchor() { } -void StackLocalsSpaceRegion::anchor() { } -void StackArgumentsSpaceRegion::anchor() { } +void NonStaticGlobalSpaceRegion::anchor() { } +void StackSpaceRegion::anchor() { } void TypedRegion::anchor() { } void TypedValueRegion::anchor() { } void CodeTextRegion::anchor() { } @@ -737,12 +737,14 @@ const CodeSpaceRegion *MemRegionManager::getCodeRegion() { // Constructing regions. //===----------------------------------------------------------------------===// const StringRegion* MemRegionManager::getStringRegion(const StringLiteral* Str){ - return getSubRegion<StringRegion>(Str, getGlobalsRegion()); + return getSubRegion<StringRegion>( + Str, cast<GlobalInternalSpaceRegion>(getGlobalsRegion())); } const ObjCStringRegion * MemRegionManager::getObjCStringRegion(const ObjCStringLiteral* Str){ - return getSubRegion<ObjCStringRegion>(Str, getGlobalsRegion()); + return getSubRegion<ObjCStringRegion>( + Str, cast<GlobalInternalSpaceRegion>(getGlobalsRegion())); } /// Look through a chain of LocationContexts to either find the @@ -871,7 +873,7 @@ const BlockDataRegion * MemRegionManager::getBlockDataRegion(const BlockCodeRegion *BC, const LocationContext *LC, unsigned blockCount) { - const MemRegion *sReg = nullptr; + const MemSpaceRegion *sReg = nullptr; const BlockDecl *BD = BC->getDecl(); if (!BD->hasCaptures()) { // This handles 'static' blocks. @@ -904,7 +906,7 @@ MemRegionManager::getCXXStaticTempObjectRegion(const Expr *Ex) { const CompoundLiteralRegion* MemRegionManager::getCompoundLiteralRegion(const CompoundLiteralExpr *CL, const LocationContext *LC) { - const MemRegion *sReg = nullptr; + const MemSpaceRegion *sReg = nullptr; if (CL->isFileScope()) sReg = getGlobalsRegion(); @@ -919,7 +921,7 @@ MemRegionManager::getCompoundLiteralRegion(const CompoundLiteralExpr *CL, const ElementRegion* MemRegionManager::getElementRegion(QualType elementType, NonLoc Idx, - const MemRegion* superRegion, + const SubRegion* superRegion, ASTContext &Ctx){ QualType T = Ctx.getCanonicalType(elementType).getUnqualifiedType(); @@ -962,13 +964,13 @@ const SymbolicRegion *MemRegionManager::getSymbolicHeapRegion(SymbolRef Sym) { const FieldRegion* MemRegionManager::getFieldRegion(const FieldDecl *d, - const MemRegion* superRegion){ + const SubRegion* superRegion){ return getSubRegion<FieldRegion>(d, superRegion); } const ObjCIvarRegion* MemRegionManager::getObjCIvarRegion(const ObjCIvarDecl *d, - const MemRegion* superRegion) { + const SubRegion* superRegion) { return getSubRegion<ObjCIvarRegion>(d, superRegion); } @@ -1004,7 +1006,7 @@ static bool isValidBaseClass(const CXXRecordDecl *BaseClass, const CXXBaseObjectRegion * MemRegionManager::getCXXBaseObjectRegion(const CXXRecordDecl *RD, - const MemRegion *Super, + const SubRegion *Super, bool IsVirtual) { if (isa<TypedValueRegion>(Super)) { assert(isValidBaseClass(RD, dyn_cast<TypedValueRegion>(Super), IsVirtual)); @@ -1015,7 +1017,7 @@ MemRegionManager::getCXXBaseObjectRegion(const CXXRecordDecl *RD, // are different. while (const CXXBaseObjectRegion *Base = dyn_cast<CXXBaseObjectRegion>(Super)) { - Super = Base->getSuperRegion(); + Super = cast<SubRegion>(Base->getSuperRegion()); } assert(Super && !isa<MemSpaceRegion>(Super)); } diff --git a/lib/StaticAnalyzer/Core/ProgramState.cpp b/lib/StaticAnalyzer/Core/ProgramState.cpp index 03ace35965cb..31556c792fc5 100644 --- a/lib/StaticAnalyzer/Core/ProgramState.cpp +++ b/lib/StaticAnalyzer/Core/ProgramState.cpp @@ -111,24 +111,29 @@ ProgramStateManager::removeDeadBindings(ProgramStateRef state, return ConstraintMgr->removeDeadBindings(Result, SymReaper); } -ProgramStateRef ProgramState::bindLoc(Loc LV, SVal V, bool notifyChanges) const { +ProgramStateRef ProgramState::bindLoc(Loc LV, + SVal V, + const LocationContext *LCtx, + bool notifyChanges) const { ProgramStateManager &Mgr = getStateManager(); ProgramStateRef newState = makeWithStore(Mgr.StoreMgr->Bind(getStore(), LV, V)); const MemRegion *MR = LV.getAsRegion(); if (MR && Mgr.getOwningEngine() && notifyChanges) - return Mgr.getOwningEngine()->processRegionChange(newState, MR); + return Mgr.getOwningEngine()->processRegionChange(newState, MR, LCtx); return newState; } -ProgramStateRef ProgramState::bindDefault(SVal loc, SVal V) const { +ProgramStateRef ProgramState::bindDefault(SVal loc, + SVal V, + const LocationContext *LCtx) const { ProgramStateManager &Mgr = getStateManager(); const MemRegion *R = loc.castAs<loc::MemRegionVal>().getRegion(); const StoreRef &newStore = Mgr.StoreMgr->BindDefault(getStore(), R, V); ProgramStateRef new_state = makeWithStore(newStore); return Mgr.getOwningEngine() ? - Mgr.getOwningEngine()->processRegionChange(new_state, R) : + Mgr.getOwningEngine()->processRegionChange(new_state, R, LCtx) : new_state; } @@ -202,7 +207,7 @@ ProgramState::invalidateRegionsImpl(ValueList Values, } return Eng->processRegionChanges(newState, IS, TopLevelInvalidated, - Invalidated, Call); + Invalidated, LCtx, Call); } const StoreRef &newStore = diff --git a/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp index 15073bb82b36..e0ad2d8ad45c 100644 --- a/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp +++ b/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp @@ -12,7 +12,7 @@ // //===----------------------------------------------------------------------===// -#include "SimpleConstraintManager.h" +#include "RangedConstraintManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" @@ -282,12 +282,31 @@ REGISTER_TRAIT_WITH_PROGRAMSTATE(ConstraintRange, RangeSet)) namespace { -class RangeConstraintManager : public SimpleConstraintManager { - RangeSet getRange(ProgramStateRef State, SymbolRef Sym); - +class RangeConstraintManager : public RangedConstraintManager { public: RangeConstraintManager(SubEngine *SE, SValBuilder &SVB) - : SimpleConstraintManager(SE, SVB) {} + : RangedConstraintManager(SE, SVB) {} + + //===------------------------------------------------------------------===// + // Implementation for interface from ConstraintManager. + //===------------------------------------------------------------------===// + + bool canReasonAbout(SVal X) const override; + + ConditionTruthVal checkNull(ProgramStateRef State, SymbolRef Sym) override; + + const llvm::APSInt *getSymVal(ProgramStateRef State, + SymbolRef Sym) const override; + + ProgramStateRef removeDeadBindings(ProgramStateRef State, + SymbolReaper &SymReaper) override; + + void print(ProgramStateRef State, raw_ostream &Out, const char *nl, + const char *sep) override; + + //===------------------------------------------------------------------===// + // Implementation for interface from RangedConstraintManager. + //===------------------------------------------------------------------===// ProgramStateRef assumeSymNE(ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &V, @@ -313,26 +332,19 @@ public: const llvm::APSInt &V, const llvm::APSInt &Adjustment) override; - ProgramStateRef assumeSymbolWithinInclusiveRange( + ProgramStateRef assumeSymWithinInclusiveRange( ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &From, const llvm::APSInt &To, const llvm::APSInt &Adjustment) override; - ProgramStateRef assumeSymbolOutOfInclusiveRange( + ProgramStateRef assumeSymOutsideInclusiveRange( ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &From, const llvm::APSInt &To, const llvm::APSInt &Adjustment) override; - const llvm::APSInt *getSymVal(ProgramStateRef St, - SymbolRef Sym) const override; - ConditionTruthVal checkNull(ProgramStateRef State, SymbolRef Sym) override; - - ProgramStateRef removeDeadBindings(ProgramStateRef St, - SymbolReaper &SymReaper) override; - - void print(ProgramStateRef St, raw_ostream &Out, const char *nl, - const char *sep) override; - private: RangeSet::Factory F; + + RangeSet getRange(ProgramStateRef State, SymbolRef Sym); + RangeSet getSymLTRange(ProgramStateRef St, SymbolRef Sym, const llvm::APSInt &Int, const llvm::APSInt &Adjustment); @@ -356,10 +368,46 @@ ento::CreateRangeConstraintManager(ProgramStateManager &StMgr, SubEngine *Eng) { return llvm::make_unique<RangeConstraintManager>(Eng, StMgr.getSValBuilder()); } -const llvm::APSInt *RangeConstraintManager::getSymVal(ProgramStateRef St, - SymbolRef Sym) const { - const ConstraintRangeTy::data_type *T = St->get<ConstraintRange>(Sym); - return T ? T->getConcreteValue() : nullptr; +bool RangeConstraintManager::canReasonAbout(SVal X) const { + Optional<nonloc::SymbolVal> SymVal = X.getAs<nonloc::SymbolVal>(); + if (SymVal && SymVal->isExpression()) { + const SymExpr *SE = SymVal->getSymbol(); + + if (const SymIntExpr *SIE = dyn_cast<SymIntExpr>(SE)) { + switch (SIE->getOpcode()) { + // We don't reason yet about bitwise-constraints on symbolic values. + case BO_And: + case BO_Or: + case BO_Xor: + return false; + // We don't reason yet about these arithmetic constraints on + // symbolic values. + case BO_Mul: + case BO_Div: + case BO_Rem: + case BO_Shl: + case BO_Shr: + return false; + // All other cases. + default: + return true; + } + } + + if (const SymSymExpr *SSE = dyn_cast<SymSymExpr>(SE)) { + if (BinaryOperator::isComparisonOp(SSE->getOpcode())) { + // We handle Loc <> Loc comparisons, but not (yet) NonLoc <> NonLoc. + if (Loc::isLocType(SSE->getLHS()->getType())) { + assert(Loc::isLocType(SSE->getRHS()->getType())); + return true; + } + } + } + + return false; + } + + return true; } ConditionTruthVal RangeConstraintManager::checkNull(ProgramStateRef State, @@ -386,6 +434,12 @@ ConditionTruthVal RangeConstraintManager::checkNull(ProgramStateRef State, return ConditionTruthVal(); } +const llvm::APSInt *RangeConstraintManager::getSymVal(ProgramStateRef St, + SymbolRef Sym) const { + const ConstraintRangeTy::data_type *T = St->get<ConstraintRange>(Sym); + return T ? T->getConcreteValue() : nullptr; +} + /// Scan all symbols referenced by the constraints. If the symbol is not alive /// as marked in LSymbols, mark it as dead in DSymbols. ProgramStateRef @@ -429,7 +483,7 @@ RangeSet RangeConstraintManager::getRange(ProgramStateRef State, } //===------------------------------------------------------------------------=== -// assumeSymX methods: public interface for RangeConstraintManager. +// assumeSymX methods: protected interface for RangeConstraintManager. //===------------------------------------------------------------------------===/ // The syntax for ranges below is mathematical, using [x, y] for closed ranges @@ -646,7 +700,7 @@ RangeConstraintManager::assumeSymLE(ProgramStateRef St, SymbolRef Sym, return New.isEmpty() ? nullptr : St->set<ConstraintRange>(Sym, New); } -ProgramStateRef RangeConstraintManager::assumeSymbolWithinInclusiveRange( +ProgramStateRef RangeConstraintManager::assumeSymWithinInclusiveRange( ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &From, const llvm::APSInt &To, const llvm::APSInt &Adjustment) { RangeSet New = getSymGERange(State, Sym, From, Adjustment); @@ -656,7 +710,7 @@ ProgramStateRef RangeConstraintManager::assumeSymbolWithinInclusiveRange( return New.isEmpty() ? nullptr : State->set<ConstraintRange>(Sym, New); } -ProgramStateRef RangeConstraintManager::assumeSymbolOutOfInclusiveRange( +ProgramStateRef RangeConstraintManager::assumeSymOutsideInclusiveRange( ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &From, const llvm::APSInt &To, const llvm::APSInt &Adjustment) { RangeSet RangeLT = getSymLTRange(State, Sym, From, Adjustment); diff --git a/lib/StaticAnalyzer/Core/RangedConstraintManager.cpp b/lib/StaticAnalyzer/Core/RangedConstraintManager.cpp new file mode 100644 index 000000000000..1304116f4974 --- /dev/null +++ b/lib/StaticAnalyzer/Core/RangedConstraintManager.cpp @@ -0,0 +1,204 @@ +//== RangedConstraintManager.cpp --------------------------------*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines RangedConstraintManager, a class that provides a +// range-based constraint manager interface. +// +//===----------------------------------------------------------------------===// + +#include "RangedConstraintManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" + +namespace clang { + +namespace ento { + +RangedConstraintManager::~RangedConstraintManager() {} + +ProgramStateRef RangedConstraintManager::assumeSym(ProgramStateRef State, + SymbolRef Sym, + bool Assumption) { + // Handle SymbolData. + if (isa<SymbolData>(Sym)) { + return assumeSymUnsupported(State, Sym, Assumption); + + // Handle symbolic expression. + } else if (const SymIntExpr *SIE = dyn_cast<SymIntExpr>(Sym)) { + // We can only simplify expressions whose RHS is an integer. + + BinaryOperator::Opcode op = SIE->getOpcode(); + if (BinaryOperator::isComparisonOp(op)) { + if (!Assumption) + op = BinaryOperator::negateComparisonOp(op); + + return assumeSymRel(State, SIE->getLHS(), op, SIE->getRHS()); + } + + } else if (const SymSymExpr *SSE = dyn_cast<SymSymExpr>(Sym)) { + // Translate "a != b" to "(b - a) != 0". + // We invert the order of the operands as a heuristic for how loop + // conditions are usually written ("begin != end") as compared to length + // calculations ("end - begin"). The more correct thing to do would be to + // canonicalize "a - b" and "b - a", which would allow us to treat + // "a != b" and "b != a" the same. + SymbolManager &SymMgr = getSymbolManager(); + BinaryOperator::Opcode Op = SSE->getOpcode(); + assert(BinaryOperator::isComparisonOp(Op)); + + // For now, we only support comparing pointers. + assert(Loc::isLocType(SSE->getLHS()->getType())); + assert(Loc::isLocType(SSE->getRHS()->getType())); + QualType DiffTy = SymMgr.getContext().getPointerDiffType(); + SymbolRef Subtraction = + SymMgr.getSymSymExpr(SSE->getRHS(), BO_Sub, SSE->getLHS(), DiffTy); + + const llvm::APSInt &Zero = getBasicVals().getValue(0, DiffTy); + Op = BinaryOperator::reverseComparisonOp(Op); + if (!Assumption) + Op = BinaryOperator::negateComparisonOp(Op); + return assumeSymRel(State, Subtraction, Op, Zero); + } + + // If we get here, there's nothing else we can do but treat the symbol as + // opaque. + return assumeSymUnsupported(State, Sym, Assumption); +} + +ProgramStateRef RangedConstraintManager::assumeSymInclusiveRange( + ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &From, + const llvm::APSInt &To, bool InRange) { + // Get the type used for calculating wraparound. + BasicValueFactory &BVF = getBasicVals(); + APSIntType WraparoundType = BVF.getAPSIntType(Sym->getType()); + + llvm::APSInt Adjustment = WraparoundType.getZeroValue(); + SymbolRef AdjustedSym = Sym; + computeAdjustment(AdjustedSym, Adjustment); + + // Convert the right-hand side integer as necessary. + APSIntType ComparisonType = std::max(WraparoundType, APSIntType(From)); + llvm::APSInt ConvertedFrom = ComparisonType.convert(From); + llvm::APSInt ConvertedTo = ComparisonType.convert(To); + + // Prefer unsigned comparisons. + if (ComparisonType.getBitWidth() == WraparoundType.getBitWidth() && + ComparisonType.isUnsigned() && !WraparoundType.isUnsigned()) + Adjustment.setIsSigned(false); + + if (InRange) + return assumeSymWithinInclusiveRange(State, AdjustedSym, ConvertedFrom, + ConvertedTo, Adjustment); + return assumeSymOutsideInclusiveRange(State, AdjustedSym, ConvertedFrom, + ConvertedTo, Adjustment); +} + +ProgramStateRef +RangedConstraintManager::assumeSymUnsupported(ProgramStateRef State, + SymbolRef Sym, bool Assumption) { + BasicValueFactory &BVF = getBasicVals(); + QualType T = Sym->getType(); + + // Non-integer types are not supported. + if (!T->isIntegralOrEnumerationType()) + return State; + + // Reverse the operation and add directly to state. + const llvm::APSInt &Zero = BVF.getValue(0, T); + if (Assumption) + return assumeSymNE(State, Sym, Zero, Zero); + else + return assumeSymEQ(State, Sym, Zero, Zero); +} + +ProgramStateRef RangedConstraintManager::assumeSymRel(ProgramStateRef State, + SymbolRef Sym, + BinaryOperator::Opcode Op, + const llvm::APSInt &Int) { + assert(BinaryOperator::isComparisonOp(Op) && + "Non-comparison ops should be rewritten as comparisons to zero."); + + // Simplification: translate an assume of a constraint of the form + // "(exp comparison_op expr) != 0" to true into an assume of + // "exp comparison_op expr" to true. (And similarly, an assume of the form + // "(exp comparison_op expr) == 0" to true into an assume of + // "exp comparison_op expr" to false.) + if (Int == 0 && (Op == BO_EQ || Op == BO_NE)) { + if (const BinarySymExpr *SE = dyn_cast<BinarySymExpr>(Sym)) + if (BinaryOperator::isComparisonOp(SE->getOpcode())) + return assumeSym(State, Sym, (Op == BO_NE ? true : false)); + } + + // Get the type used for calculating wraparound. + BasicValueFactory &BVF = getBasicVals(); + APSIntType WraparoundType = BVF.getAPSIntType(Sym->getType()); + + // We only handle simple comparisons of the form "$sym == constant" + // or "($sym+constant1) == constant2". + // The adjustment is "constant1" in the above expression. It's used to + // "slide" the solution range around for modular arithmetic. For example, + // x < 4 has the solution [0, 3]. x+2 < 4 has the solution [0-2, 3-2], which + // in modular arithmetic is [0, 1] U [UINT_MAX-1, UINT_MAX]. It's up to + // the subclasses of SimpleConstraintManager to handle the adjustment. + llvm::APSInt Adjustment = WraparoundType.getZeroValue(); + computeAdjustment(Sym, Adjustment); + + // Convert the right-hand side integer as necessary. + APSIntType ComparisonType = std::max(WraparoundType, APSIntType(Int)); + llvm::APSInt ConvertedInt = ComparisonType.convert(Int); + + // Prefer unsigned comparisons. + if (ComparisonType.getBitWidth() == WraparoundType.getBitWidth() && + ComparisonType.isUnsigned() && !WraparoundType.isUnsigned()) + Adjustment.setIsSigned(false); + + switch (Op) { + default: + llvm_unreachable("invalid operation not caught by assertion above"); + + case BO_EQ: + return assumeSymEQ(State, Sym, ConvertedInt, Adjustment); + + case BO_NE: + return assumeSymNE(State, Sym, ConvertedInt, Adjustment); + + case BO_GT: + return assumeSymGT(State, Sym, ConvertedInt, Adjustment); + + case BO_GE: + return assumeSymGE(State, Sym, ConvertedInt, Adjustment); + + case BO_LT: + return assumeSymLT(State, Sym, ConvertedInt, Adjustment); + + case BO_LE: + return assumeSymLE(State, Sym, ConvertedInt, Adjustment); + } // end switch +} + +void RangedConstraintManager::computeAdjustment(SymbolRef &Sym, + llvm::APSInt &Adjustment) { + // Is it a "($sym+constant1)" expression? + if (const SymIntExpr *SE = dyn_cast<SymIntExpr>(Sym)) { + BinaryOperator::Opcode Op = SE->getOpcode(); + if (Op == BO_Add || Op == BO_Sub) { + Sym = SE->getLHS(); + Adjustment = APSIntType(Adjustment).convert(SE->getRHS()); + + // Don't forget to negate the adjustment if it's being subtracted. + // This should happen /after/ promotion, in case the value being + // subtracted is, say, CHAR_MIN, and the promoted type is 'int'. + if (Op == BO_Sub) + Adjustment = -Adjustment; + } + } +} + +} // end of namespace ento + +} // end of namespace clang diff --git a/lib/StaticAnalyzer/Core/SimpleConstraintManager.h b/lib/StaticAnalyzer/Core/RangedConstraintManager.h index 1128e775b320..a4e6062a4f57 100644 --- a/lib/StaticAnalyzer/Core/SimpleConstraintManager.h +++ b/lib/StaticAnalyzer/Core/RangedConstraintManager.h @@ -1,4 +1,4 @@ -//== SimpleConstraintManager.h ----------------------------------*- C++ -*--==// +//== RangedConstraintManager.h ----------------------------------*- C++ -*--==// // // The LLVM Compiler Infrastructure // @@ -7,59 +7,55 @@ // //===----------------------------------------------------------------------===// // -// Code shared between BasicConstraintManager and RangeConstraintManager. +// Ranged constraint manager, built on SimpleConstraintManager. // //===----------------------------------------------------------------------===// -#ifndef LLVM_CLANG_LIB_STATICANALYZER_CORE_SIMPLECONSTRAINTMANAGER_H -#define LLVM_CLANG_LIB_STATICANALYZER_CORE_SIMPLECONSTRAINTMANAGER_H +#ifndef LLVM_CLANG_LIB_STATICANALYZER_CORE_RANGEDCONSTRAINTMANAGER_H +#define LLVM_CLANG_LIB_STATICANALYZER_CORE_RANGEDCONSTRAINTMANAGER_H -#include "clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SimpleConstraintManager.h" namespace clang { namespace ento { -class SimpleConstraintManager : public ConstraintManager { - SubEngine *SU; - SValBuilder &SVB; - +class RangedConstraintManager : public SimpleConstraintManager { public: - SimpleConstraintManager(SubEngine *SE, SValBuilder &SB) : SU(SE), SVB(SB) {} - ~SimpleConstraintManager() override; + RangedConstraintManager(SubEngine *SE, SValBuilder &SB) + : SimpleConstraintManager(SE, SB) {} + + ~RangedConstraintManager() override; //===------------------------------------------------------------------===// - // Common implementation for the interface provided by ConstraintManager. + // Implementation for interface from SimpleConstraintManager. //===------------------------------------------------------------------===// - ProgramStateRef assume(ProgramStateRef State, DefinedSVal Cond, - bool Assumption) override; + ProgramStateRef assumeSym(ProgramStateRef State, SymbolRef Sym, + bool Assumption) override; - ProgramStateRef assume(ProgramStateRef State, NonLoc Cond, bool Assumption); + ProgramStateRef assumeSymInclusiveRange(ProgramStateRef State, SymbolRef Sym, + const llvm::APSInt &From, + const llvm::APSInt &To, + bool InRange) override; - ProgramStateRef assumeInclusiveRange(ProgramStateRef State, NonLoc Value, - const llvm::APSInt &From, - const llvm::APSInt &To, - bool InRange) override; + ProgramStateRef assumeSymUnsupported(ProgramStateRef State, SymbolRef Sym, + bool Assumption) override; - ProgramStateRef assumeSymRel(ProgramStateRef State, const SymExpr *LHS, - BinaryOperator::Opcode Op, +protected: + /// Assume a constraint between a symbolic expression and a concrete integer. + virtual ProgramStateRef assumeSymRel(ProgramStateRef State, SymbolRef Sym, + BinaryOperator::Opcode op, const llvm::APSInt &Int); - ProgramStateRef assumeSymWithinInclusiveRange(ProgramStateRef State, - SymbolRef Sym, - const llvm::APSInt &From, - const llvm::APSInt &To, - bool InRange); - -protected: //===------------------------------------------------------------------===// // Interface that subclasses must implement. //===------------------------------------------------------------------===// // Each of these is of the form "$Sym+Adj <> V", where "<>" is the comparison // operation for the method being invoked. + virtual ProgramStateRef assumeSymNE(ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &V, const llvm::APSInt &Adjustment) = 0; @@ -84,28 +80,19 @@ protected: const llvm::APSInt &V, const llvm::APSInt &Adjustment) = 0; - virtual ProgramStateRef assumeSymbolWithinInclusiveRange( + virtual ProgramStateRef assumeSymWithinInclusiveRange( ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &From, const llvm::APSInt &To, const llvm::APSInt &Adjustment) = 0; - virtual ProgramStateRef assumeSymbolOutOfInclusiveRange( + virtual ProgramStateRef assumeSymOutsideInclusiveRange( ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &From, const llvm::APSInt &To, const llvm::APSInt &Adjustment) = 0; //===------------------------------------------------------------------===// // Internal implementation. //===------------------------------------------------------------------===// - - BasicValueFactory &getBasicVals() const { return SVB.getBasicValueFactory(); } - SymbolManager &getSymbolManager() const { return SVB.getSymbolManager(); } - - bool canReasonAbout(SVal X) const override; - - ProgramStateRef assumeAux(ProgramStateRef State, NonLoc Cond, - bool Assumption); - - ProgramStateRef assumeAuxForSymbol(ProgramStateRef State, SymbolRef Sym, - bool Assumption); +private: + static void computeAdjustment(SymbolRef &Sym, llvm::APSInt &Adjustment); }; } // end GR namespace diff --git a/lib/StaticAnalyzer/Core/RegionStore.cpp b/lib/StaticAnalyzer/Core/RegionStore.cpp index 934cc5cd3ac4..dd7e9dd11781 100644 --- a/lib/StaticAnalyzer/Core/RegionStore.cpp +++ b/lib/StaticAnalyzer/Core/RegionStore.cpp @@ -494,6 +494,11 @@ public: // Part of public interface to class. return getBinding(getRegionBindings(S), L, T); } + Optional<SVal> getDefaultBinding(Store S, const MemRegion *R) override { + RegionBindingsRef B = getRegionBindings(S); + return B.getDefaultBinding(R); + } + SVal getBinding(RegionBindingsConstRef B, Loc L, QualType T = QualType()); SVal getBindingForElement(RegionBindingsConstRef B, const ElementRegion *R); @@ -1336,7 +1341,8 @@ SVal RegionStoreManager::ArrayToPointer(Loc Array, QualType T) { if (!Array.getAs<loc::MemRegionVal>()) return UnknownVal(); - const MemRegion* R = Array.castAs<loc::MemRegionVal>().getRegion(); + const SubRegion *R = + cast<SubRegion>(Array.castAs<loc::MemRegionVal>().getRegion()); NonLoc ZeroIdx = svalBuilder.makeZeroArrayIndex(); return loc::MemRegionVal(MRMgr.getElementRegion(T, ZeroIdx, R, Ctx)); } @@ -1379,7 +1385,7 @@ SVal RegionStoreManager::getBinding(RegionBindingsConstRef B, Loc L, QualType T) T = SR->getSymbol()->getType(); } } - MR = GetElementZeroRegion(MR, T); + MR = GetElementZeroRegion(cast<SubRegion>(MR), T); } // FIXME: Perhaps this method should just take a 'const MemRegion*' argument diff --git a/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp b/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp index 0e512ff80861..adb40178f5b1 100644 --- a/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp +++ b/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp @@ -7,12 +7,12 @@ // //===----------------------------------------------------------------------===// // -// This file defines SimpleConstraintManager, a class that holds code shared -// between BasicConstraintManager and RangeConstraintManager. +// This file defines SimpleConstraintManager, a class that provides a +// simplified constraint manager interface, compared to ConstraintManager. // //===----------------------------------------------------------------------===// -#include "SimpleConstraintManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SimpleConstraintManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" @@ -23,48 +23,6 @@ namespace ento { SimpleConstraintManager::~SimpleConstraintManager() {} -bool SimpleConstraintManager::canReasonAbout(SVal X) const { - Optional<nonloc::SymbolVal> SymVal = X.getAs<nonloc::SymbolVal>(); - if (SymVal && SymVal->isExpression()) { - const SymExpr *SE = SymVal->getSymbol(); - - if (const SymIntExpr *SIE = dyn_cast<SymIntExpr>(SE)) { - switch (SIE->getOpcode()) { - // We don't reason yet about bitwise-constraints on symbolic values. - case BO_And: - case BO_Or: - case BO_Xor: - return false; - // We don't reason yet about these arithmetic constraints on - // symbolic values. - case BO_Mul: - case BO_Div: - case BO_Rem: - case BO_Shl: - case BO_Shr: - return false; - // All other cases. - default: - return true; - } - } - - if (const SymSymExpr *SSE = dyn_cast<SymSymExpr>(SE)) { - if (BinaryOperator::isComparisonOp(SSE->getOpcode())) { - // We handle Loc <> Loc comparisons, but not (yet) NonLoc <> NonLoc. - if (Loc::isLocType(SSE->getLHS()->getType())) { - assert(Loc::isLocType(SSE->getRHS()->getType())); - return true; - } - } - } - - return false; - } - - return true; -} - ProgramStateRef SimpleConstraintManager::assume(ProgramStateRef State, DefinedSVal Cond, bool Assumption) { @@ -92,23 +50,6 @@ ProgramStateRef SimpleConstraintManager::assume(ProgramStateRef State, return State; } -ProgramStateRef -SimpleConstraintManager::assumeAuxForSymbol(ProgramStateRef State, - SymbolRef Sym, bool Assumption) { - BasicValueFactory &BVF = getBasicVals(); - QualType T = Sym->getType(); - - // None of the constraint solvers currently support non-integer types. - if (!T->isIntegralOrEnumerationType()) - return State; - - const llvm::APSInt &zero = BVF.getValue(0, T); - if (Assumption) - return assumeSymNE(State, Sym, zero, zero); - else - return assumeSymEQ(State, Sym, zero, zero); -} - ProgramStateRef SimpleConstraintManager::assumeAux(ProgramStateRef State, NonLoc Cond, bool Assumption) { @@ -118,7 +59,8 @@ ProgramStateRef SimpleConstraintManager::assumeAux(ProgramStateRef State, if (!canReasonAbout(Cond)) { // Just add the constraint to the expression without trying to simplify. SymbolRef Sym = Cond.getAsSymExpr(); - return assumeAuxForSymbol(State, Sym, Assumption); + assert(Sym); + return assumeSymUnsupported(State, Sym, Assumption); } switch (Cond.getSubKind()) { @@ -129,51 +71,7 @@ ProgramStateRef SimpleConstraintManager::assumeAux(ProgramStateRef State, nonloc::SymbolVal SV = Cond.castAs<nonloc::SymbolVal>(); SymbolRef Sym = SV.getSymbol(); assert(Sym); - - // Handle SymbolData. - if (!SV.isExpression()) { - return assumeAuxForSymbol(State, Sym, Assumption); - - // Handle symbolic expression. - } else if (const SymIntExpr *SE = dyn_cast<SymIntExpr>(Sym)) { - // We can only simplify expressions whose RHS is an integer. - - BinaryOperator::Opcode Op = SE->getOpcode(); - if (BinaryOperator::isComparisonOp(Op)) { - if (!Assumption) - Op = BinaryOperator::negateComparisonOp(Op); - - return assumeSymRel(State, SE->getLHS(), Op, SE->getRHS()); - } - - } else if (const SymSymExpr *SSE = dyn_cast<SymSymExpr>(Sym)) { - // Translate "a != b" to "(b - a) != 0". - // We invert the order of the operands as a heuristic for how loop - // conditions are usually written ("begin != end") as compared to length - // calculations ("end - begin"). The more correct thing to do would be to - // canonicalize "a - b" and "b - a", which would allow us to treat - // "a != b" and "b != a" the same. - SymbolManager &SymMgr = getSymbolManager(); - BinaryOperator::Opcode Op = SSE->getOpcode(); - assert(BinaryOperator::isComparisonOp(Op)); - - // For now, we only support comparing pointers. - assert(Loc::isLocType(SSE->getLHS()->getType())); - assert(Loc::isLocType(SSE->getRHS()->getType())); - QualType DiffTy = SymMgr.getContext().getPointerDiffType(); - SymbolRef Subtraction = - SymMgr.getSymSymExpr(SSE->getRHS(), BO_Sub, SSE->getLHS(), DiffTy); - - const llvm::APSInt &Zero = getBasicVals().getValue(0, DiffTy); - Op = BinaryOperator::reverseComparisonOp(Op); - if (!Assumption) - Op = BinaryOperator::negateComparisonOp(Op); - return assumeSymRel(State, Subtraction, Op, Zero); - } - - // If we get here, there's nothing else we can do but treat the symbol as - // opaque. - return assumeAuxForSymbol(State, Sym, Assumption); + return assumeSym(State, Sym, Assumption); } case nonloc::ConcreteIntKind: { @@ -206,7 +104,7 @@ ProgramStateRef SimpleConstraintManager::assumeInclusiveRange( // Just add the constraint to the expression without trying to simplify. SymbolRef Sym = Value.getAsSymExpr(); assert(Sym); - return assumeSymWithinInclusiveRange(State, Sym, From, To, InRange); + return assumeSymInclusiveRange(State, Sym, From, To, InRange); } switch (Value.getSubKind()) { @@ -217,7 +115,7 @@ ProgramStateRef SimpleConstraintManager::assumeInclusiveRange( case nonloc::LocAsIntegerKind: case nonloc::SymbolValKind: { if (SymbolRef Sym = Value.getAsSymbol()) - return assumeSymWithinInclusiveRange(State, Sym, From, To, InRange); + return assumeSymInclusiveRange(State, Sym, From, To, InRange); return State; } // end switch @@ -230,118 +128,6 @@ ProgramStateRef SimpleConstraintManager::assumeInclusiveRange( } // end switch } -static void computeAdjustment(SymbolRef &Sym, llvm::APSInt &Adjustment) { - // Is it a "($sym+constant1)" expression? - if (const SymIntExpr *SE = dyn_cast<SymIntExpr>(Sym)) { - BinaryOperator::Opcode Op = SE->getOpcode(); - if (Op == BO_Add || Op == BO_Sub) { - Sym = SE->getLHS(); - Adjustment = APSIntType(Adjustment).convert(SE->getRHS()); - - // Don't forget to negate the adjustment if it's being subtracted. - // This should happen /after/ promotion, in case the value being - // subtracted is, say, CHAR_MIN, and the promoted type is 'int'. - if (Op == BO_Sub) - Adjustment = -Adjustment; - } - } -} - -ProgramStateRef SimpleConstraintManager::assumeSymRel(ProgramStateRef State, - const SymExpr *LHS, - BinaryOperator::Opcode Op, - const llvm::APSInt &Int) { - assert(BinaryOperator::isComparisonOp(Op) && - "Non-comparison ops should be rewritten as comparisons to zero."); - - SymbolRef Sym = LHS; - - // Simplification: translate an assume of a constraint of the form - // "(exp comparison_op expr) != 0" to true into an assume of - // "exp comparison_op expr" to true. (And similarly, an assume of the form - // "(exp comparison_op expr) == 0" to true into an assume of - // "exp comparison_op expr" to false.) - if (Int == 0 && (Op == BO_EQ || Op == BO_NE)) { - if (const BinarySymExpr *SE = dyn_cast<BinarySymExpr>(Sym)) - if (BinaryOperator::isComparisonOp(SE->getOpcode())) - return assume(State, nonloc::SymbolVal(Sym), (Op == BO_NE ? true : false)); - } - - // Get the type used for calculating wraparound. - BasicValueFactory &BVF = getBasicVals(); - APSIntType WraparoundType = BVF.getAPSIntType(LHS->getType()); - - // We only handle simple comparisons of the form "$sym == constant" - // or "($sym+constant1) == constant2". - // The adjustment is "constant1" in the above expression. It's used to - // "slide" the solution range around for modular arithmetic. For example, - // x < 4 has the solution [0, 3]. x+2 < 4 has the solution [0-2, 3-2], which - // in modular arithmetic is [0, 1] U [UINT_MAX-1, UINT_MAX]. It's up to - // the subclasses of SimpleConstraintManager to handle the adjustment. - llvm::APSInt Adjustment = WraparoundType.getZeroValue(); - computeAdjustment(Sym, Adjustment); - - // Convert the right-hand side integer as necessary. - APSIntType ComparisonType = std::max(WraparoundType, APSIntType(Int)); - llvm::APSInt ConvertedInt = ComparisonType.convert(Int); - - // Prefer unsigned comparisons. - if (ComparisonType.getBitWidth() == WraparoundType.getBitWidth() && - ComparisonType.isUnsigned() && !WraparoundType.isUnsigned()) - Adjustment.setIsSigned(false); - - switch (Op) { - default: - llvm_unreachable("invalid operation not caught by assertion above"); - - case BO_EQ: - return assumeSymEQ(State, Sym, ConvertedInt, Adjustment); - - case BO_NE: - return assumeSymNE(State, Sym, ConvertedInt, Adjustment); - - case BO_GT: - return assumeSymGT(State, Sym, ConvertedInt, Adjustment); - - case BO_GE: - return assumeSymGE(State, Sym, ConvertedInt, Adjustment); - - case BO_LT: - return assumeSymLT(State, Sym, ConvertedInt, Adjustment); - - case BO_LE: - return assumeSymLE(State, Sym, ConvertedInt, Adjustment); - } // end switch -} - -ProgramStateRef SimpleConstraintManager::assumeSymWithinInclusiveRange( - ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &From, - const llvm::APSInt &To, bool InRange) { - // Get the type used for calculating wraparound. - BasicValueFactory &BVF = getBasicVals(); - APSIntType WraparoundType = BVF.getAPSIntType(Sym->getType()); - - llvm::APSInt Adjustment = WraparoundType.getZeroValue(); - SymbolRef AdjustedSym = Sym; - computeAdjustment(AdjustedSym, Adjustment); - - // Convert the right-hand side integer as necessary. - APSIntType ComparisonType = std::max(WraparoundType, APSIntType(From)); - llvm::APSInt ConvertedFrom = ComparisonType.convert(From); - llvm::APSInt ConvertedTo = ComparisonType.convert(To); - - // Prefer unsigned comparisons. - if (ComparisonType.getBitWidth() == WraparoundType.getBitWidth() && - ComparisonType.isUnsigned() && !WraparoundType.isUnsigned()) - Adjustment.setIsSigned(false); - - if (InRange) - return assumeSymbolWithinInclusiveRange(State, AdjustedSym, ConvertedFrom, - ConvertedTo, Adjustment); - return assumeSymbolOutOfInclusiveRange(State, AdjustedSym, ConvertedFrom, - ConvertedTo, Adjustment); -} - } // end of namespace ento } // end of namespace clang diff --git a/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp b/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp index 28b43dd566d5..82ce8b45fe78 100644 --- a/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp +++ b/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp @@ -14,6 +14,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h" #include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SValVisitor.h" using namespace clang; using namespace ento; @@ -44,6 +45,10 @@ public: /// (integer) value, that value is returned. Otherwise, returns NULL. const llvm::APSInt *getKnownValue(ProgramStateRef state, SVal V) override; + /// Recursively descends into symbolic expressions and replaces symbols + /// with their known values (in the sense of the getKnownValue() method). + SVal simplifySVal(ProgramStateRef State, SVal V) override; + SVal MakeSymIntVal(const SymExpr *LHS, BinaryOperator::Opcode op, const llvm::APSInt &RHS, QualType resultTy); }; @@ -362,6 +367,9 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state, resultTy); case nonloc::ConcreteIntKind: { // Transform the integer into a location and compare. + // FIXME: This only makes sense for comparisons. If we want to, say, + // add 1 to a LocAsInteger, we'd better unpack the Loc and add to it, + // then pack it back into a LocAsInteger. llvm::APSInt i = rhs.castAs<nonloc::ConcreteInt>().getValue(); BasicVals.getAPSIntType(Context.VoidPtrTy).apply(i); return evalBinOpLL(state, op, lhsL, makeLoc(i), resultTy); @@ -534,11 +542,12 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state, // Does the symbolic expression simplify to a constant? // If so, "fold" the constant by setting 'lhs' to a ConcreteInt // and try again. - ConstraintManager &CMgr = state->getConstraintManager(); - if (const llvm::APSInt *Constant = CMgr.getSymVal(state, Sym)) { - lhs = nonloc::ConcreteInt(*Constant); - continue; - } + SVal simplifiedLhs = simplifySVal(state, lhs); + if (simplifiedLhs != lhs) + if (auto simplifiedLhsAsNonLoc = simplifiedLhs.getAs<NonLoc>()) { + lhs = *simplifiedLhsAsNonLoc; + continue; + } // Is the RHS a constant? if (const llvm::APSInt *RHSValue = getKnownValue(state, rhs)) @@ -941,20 +950,26 @@ SVal SimpleSValBuilder::evalBinOpLN(ProgramStateRef state, if (const MemRegion *region = lhs.getAsRegion()) { rhs = convertToArrayIndex(rhs).castAs<NonLoc>(); SVal index = UnknownVal(); - const MemRegion *superR = nullptr; + const SubRegion *superR = nullptr; + // We need to know the type of the pointer in order to add an integer to it. + // Depending on the type, different amount of bytes is added. QualType elementType; if (const ElementRegion *elemReg = dyn_cast<ElementRegion>(region)) { assert(op == BO_Add || op == BO_Sub); index = evalBinOpNN(state, op, elemReg->getIndex(), rhs, getArrayIndexType()); - superR = elemReg->getSuperRegion(); + superR = cast<SubRegion>(elemReg->getSuperRegion()); elementType = elemReg->getElementType(); } else if (isa<SubRegion>(region)) { assert(op == BO_Add || op == BO_Sub); index = (op == BO_Add) ? rhs : evalMinus(rhs); - superR = region; + superR = cast<SubRegion>(region); + // TODO: Is this actually reliable? Maybe improving our MemRegion + // hierarchy to provide typed regions for all non-void pointers would be + // better. For instance, we cannot extend this towards LocAsInteger + // operations, where result type of the expression is integer. if (resultTy->isAnyPointerType()) elementType = resultTy->getPointeeType(); } @@ -984,3 +999,74 @@ const llvm::APSInt *SimpleSValBuilder::getKnownValue(ProgramStateRef state, // FIXME: Add support for SymExprs. return nullptr; } + +SVal SimpleSValBuilder::simplifySVal(ProgramStateRef State, SVal V) { + // For now, this function tries to constant-fold symbols inside a + // nonloc::SymbolVal, and does nothing else. More simplifications should + // be possible, such as constant-folding an index in an ElementRegion. + + class Simplifier : public FullSValVisitor<Simplifier, SVal> { + ProgramStateRef State; + SValBuilder &SVB; + + public: + Simplifier(ProgramStateRef State) + : State(State), SVB(State->getStateManager().getSValBuilder()) {} + + SVal VisitSymbolData(const SymbolData *S) { + if (const llvm::APSInt *I = + SVB.getKnownValue(State, nonloc::SymbolVal(S))) + return Loc::isLocType(S->getType()) ? (SVal)SVB.makeIntLocVal(*I) + : (SVal)SVB.makeIntVal(*I); + return nonloc::SymbolVal(S); + } + + // TODO: Support SymbolCast. Support IntSymExpr when/if we actually + // start producing them. + + SVal VisitSymIntExpr(const SymIntExpr *S) { + SVal LHS = Visit(S->getLHS()); + 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. + // So make our guess by recalling that we cannot multiply pointers + // or compare a pointer to an integer. + if (Loc::isLocType(S->getLHS()->getType()) && + BinaryOperator::isComparisonOp(S->getOpcode())) { + // The usual conversion of $sym to &SymRegion{$sym}, as they have + // the same meaning for Loc-type symbols, but the latter form + // is preferred in SVal computations for being Loc itself. + if (SymbolRef Sym = LHS.getAsSymbol()) { + assert(Loc::isLocType(Sym->getType())); + LHS = SVB.makeLoc(Sym); + } + RHS = SVB.makeIntLocVal(S->getRHS()); + } else { + RHS = SVB.makeIntVal(S->getRHS()); + } + return SVB.evalBinOp(State, S->getOpcode(), LHS, RHS, S->getType()); + } + + SVal VisitSymSymExpr(const SymSymExpr *S) { + SVal LHS = Visit(S->getLHS()); + SVal RHS = Visit(S->getRHS()); + return SVB.evalBinOp(State, S->getOpcode(), LHS, RHS, S->getType()); + } + + SVal VisitSymExpr(SymbolRef S) { return nonloc::SymbolVal(S); } + + SVal VisitMemRegion(const MemRegion *R) { return loc::MemRegionVal(R); } + + SVal VisitNonLocSymbolVal(nonloc::SymbolVal V) { + // Simplification is much more costly than computing complexity. + // For high complexity, it may be not worth it. + if (V.getSymbol()->computeComplexity() > 100) + return V; + return Visit(V.getSymbol()); + } + + SVal VisitSVal(SVal V) { return V; } + }; + + return Simplifier(State).Visit(V); +} diff --git a/lib/StaticAnalyzer/Core/Store.cpp b/lib/StaticAnalyzer/Core/Store.cpp index aca6e3b6255b..ba48a60d5a1c 100644 --- a/lib/StaticAnalyzer/Core/Store.cpp +++ b/lib/StaticAnalyzer/Core/Store.cpp @@ -42,8 +42,9 @@ StoreRef StoreManager::enterStackFrame(Store OldStore, return Store; } -const MemRegion *StoreManager::MakeElementRegion(const MemRegion *Base, - QualType EleTy, uint64_t index) { +const ElementRegion *StoreManager::MakeElementRegion(const SubRegion *Base, + QualType EleTy, + uint64_t index) { NonLoc idx = svalBuilder.makeArrayIndex(index); return MRMgr.getElementRegion(EleTy, idx, Base, svalBuilder.getContext()); } @@ -52,7 +53,7 @@ StoreRef StoreManager::BindDefault(Store store, const MemRegion *R, SVal V) { return StoreRef(store, *this); } -const ElementRegion *StoreManager::GetElementZeroRegion(const MemRegion *R, +const ElementRegion *StoreManager::GetElementZeroRegion(const SubRegion *R, QualType T) { NonLoc idx = svalBuilder.makeZeroArrayIndex(); assert(!T.isNull()); @@ -126,7 +127,7 @@ const MemRegion *StoreManager::castRegion(const MemRegion *R, QualType CastToTy) case MemRegion::VarRegionKind: case MemRegion::CXXTempObjectRegionKind: case MemRegion::CXXBaseObjectRegionKind: - return MakeElementRegion(R, PointeeTy); + return MakeElementRegion(cast<SubRegion>(R), PointeeTy); case MemRegion::ElementRegionKind: { // If we are casting from an ElementRegion to another type, the @@ -171,7 +172,7 @@ const MemRegion *StoreManager::castRegion(const MemRegion *R, QualType CastToTy) } // Otherwise, create a new ElementRegion at offset 0. - return MakeElementRegion(baseR, PointeeTy); + return MakeElementRegion(cast<SubRegion>(baseR), PointeeTy); } // We have a non-zero offset from the base region. We want to determine @@ -202,10 +203,11 @@ const MemRegion *StoreManager::castRegion(const MemRegion *R, QualType CastToTy) if (!newSuperR) { // Create an intermediate ElementRegion to represent the raw byte. // This will be the super region of the final ElementRegion. - newSuperR = MakeElementRegion(baseR, Ctx.CharTy, off.getQuantity()); + newSuperR = MakeElementRegion(cast<SubRegion>(baseR), Ctx.CharTy, + off.getQuantity()); } - return MakeElementRegion(newSuperR, PointeeTy, newIndex); + return MakeElementRegion(cast<SubRegion>(newSuperR), PointeeTy, newIndex); } } @@ -271,9 +273,8 @@ SVal StoreManager::evalDerivedToBase(SVal Derived, QualType BaseType, BaseDecl = BaseType->getAsCXXRecordDecl(); assert(BaseDecl && "not a C++ object?"); - const MemRegion *BaseReg = - MRMgr.getCXXBaseObjectRegion(BaseDecl, DerivedRegVal->getRegion(), - IsVirtual); + const MemRegion *BaseReg = MRMgr.getCXXBaseObjectRegion( + BaseDecl, cast<SubRegion>(DerivedRegVal->getRegion()), IsVirtual); return loc::MemRegionVal(BaseReg); } @@ -390,11 +391,11 @@ SVal StoreManager::getLValueFieldOrIvar(const Decl *D, SVal Base) { return Base; Loc BaseL = Base.castAs<Loc>(); - const MemRegion* BaseR = nullptr; + const SubRegion* BaseR = nullptr; switch (BaseL.getSubKind()) { case loc::MemRegionValKind: - BaseR = BaseL.castAs<loc::MemRegionVal>().getRegion(); + BaseR = cast<SubRegion>(BaseL.castAs<loc::MemRegionVal>().getRegion()); break; case loc::GotoLabelKind: @@ -434,7 +435,8 @@ SVal StoreManager::getLValueElement(QualType elementType, NonLoc Offset, if (Base.isUnknownOrUndef() || Base.getAs<loc::ConcreteInt>()) return Base; - const MemRegion* BaseRegion = Base.castAs<loc::MemRegionVal>().getRegion(); + const SubRegion *BaseRegion = + Base.castAs<loc::MemRegionVal>().getRegionAs<SubRegion>(); // Pointer of any type can be cast and used as array base. const ElementRegion *ElemR = dyn_cast<ElementRegion>(BaseRegion); @@ -471,9 +473,8 @@ SVal StoreManager::getLValueElement(QualType elementType, NonLoc Offset, if (isa<ElementRegion>(BaseRegion->StripCasts())) return UnknownVal(); - return loc::MemRegionVal(MRMgr.getElementRegion(elementType, Offset, - ElemR->getSuperRegion(), - Ctx)); + return loc::MemRegionVal(MRMgr.getElementRegion( + elementType, Offset, cast<SubRegion>(ElemR->getSuperRegion()), Ctx)); } const llvm::APSInt& OffI = Offset.castAs<nonloc::ConcreteInt>().getValue(); @@ -484,7 +485,7 @@ SVal StoreManager::getLValueElement(QualType elementType, NonLoc Offset, OffI)); // Construct the new ElementRegion. - const MemRegion *ArrayR = ElemR->getSuperRegion(); + const SubRegion *ArrayR = cast<SubRegion>(ElemR->getSuperRegion()); return loc::MemRegionVal(MRMgr.getElementRegion(elementType, NewIdx, ArrayR, Ctx)); } diff --git a/lib/StaticAnalyzer/Core/Z3ConstraintManager.cpp b/lib/StaticAnalyzer/Core/Z3ConstraintManager.cpp new file mode 100644 index 000000000000..f9f9057a89cd --- /dev/null +++ b/lib/StaticAnalyzer/Core/Z3ConstraintManager.cpp @@ -0,0 +1,1618 @@ +//== Z3ConstraintManager.cpp --------------------------------*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Basic/TargetInfo.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SimpleConstraintManager.h" + +#include "clang/Config/config.h" + +using namespace clang; +using namespace ento; + +#if CLANG_ANALYZER_WITH_Z3 + +#include <z3.h> + +// Forward declarations +namespace { +class Z3Expr; +class ConstraintZ3 {}; +} // end anonymous namespace + +typedef llvm::ImmutableSet<std::pair<SymbolRef, Z3Expr>> ConstraintZ3Ty; + +// Expansion of REGISTER_TRAIT_WITH_PROGRAMSTATE(ConstraintZ3, Z3SetPair) +namespace clang { +namespace ento { +template <> +struct ProgramStateTrait<ConstraintZ3> + : public ProgramStatePartialTrait<ConstraintZ3Ty> { + static void *GDMIndex() { + static int Index; + return &Index; + } +}; +} // end namespace ento +} // end namespace clang + +namespace { + +class Z3Config { + friend class Z3Context; + + Z3_config Config; + +public: + Z3Config() : Config(Z3_mk_config()) { + // Enable model finding + Z3_set_param_value(Config, "model", "true"); + // Disable proof generation + Z3_set_param_value(Config, "proof", "false"); + // Set timeout to 15000ms = 15s + Z3_set_param_value(Config, "timeout", "15000"); + } + + ~Z3Config() { Z3_del_config(Config); } +}; // end class Z3Config + +class Z3Context { + Z3_context ZC_P; + +public: + static Z3_context ZC; + + Z3Context() : ZC_P(Z3_mk_context_rc(Z3Config().Config)) { ZC = ZC_P; } + + ~Z3Context() { + Z3_del_context(ZC); + Z3_finalize_memory(); + ZC_P = nullptr; + } +}; // end class Z3Context + +class Z3Sort { + friend class Z3Expr; + + Z3_sort Sort; + + Z3Sort() : Sort(nullptr) {} + Z3Sort(Z3_sort ZS) : Sort(ZS) { + Z3_inc_ref(Z3Context::ZC, reinterpret_cast<Z3_ast>(Sort)); + } + +public: + /// Override implicit copy constructor for correct reference counting. + Z3Sort(const Z3Sort &Copy) : Sort(Copy.Sort) { + Z3_inc_ref(Z3Context::ZC, reinterpret_cast<Z3_ast>(Sort)); + } + + /// Provide move constructor + Z3Sort(Z3Sort &&Move) : Sort(nullptr) { *this = std::move(Move); } + + /// Provide move assignment constructor + Z3Sort &operator=(Z3Sort &&Move) { + if (this != &Move) { + if (Sort) + Z3_dec_ref(Z3Context::ZC, reinterpret_cast<Z3_ast>(Sort)); + Sort = Move.Sort; + Move.Sort = nullptr; + } + return *this; + } + + ~Z3Sort() { + if (Sort) + Z3_dec_ref(Z3Context::ZC, reinterpret_cast<Z3_ast>(Sort)); + } + + // Return a boolean sort. + static Z3Sort getBoolSort() { return Z3Sort(Z3_mk_bool_sort(Z3Context::ZC)); } + + // Return an appropriate bitvector sort for the given bitwidth. + static Z3Sort getBitvectorSort(unsigned BitWidth) { + return Z3Sort(Z3_mk_bv_sort(Z3Context::ZC, BitWidth)); + } + + // Return an appropriate floating-point sort for the given bitwidth. + static Z3Sort getFloatSort(unsigned BitWidth) { + Z3_sort Sort; + + switch (BitWidth) { + default: + llvm_unreachable("Unsupported floating-point bitwidth!"); + break; + case 16: + Sort = Z3_mk_fpa_sort_16(Z3Context::ZC); + break; + case 32: + Sort = Z3_mk_fpa_sort_32(Z3Context::ZC); + break; + case 64: + Sort = Z3_mk_fpa_sort_64(Z3Context::ZC); + break; + case 128: + Sort = Z3_mk_fpa_sort_128(Z3Context::ZC); + break; + } + return Z3Sort(Sort); + } + + // Return an appropriate sort for the given AST. + static Z3Sort getSort(Z3_ast AST) { + return Z3Sort(Z3_get_sort(Z3Context::ZC, AST)); + } + + Z3_sort_kind getSortKind() const { + return Z3_get_sort_kind(Z3Context::ZC, Sort); + } + + unsigned getBitvectorSortSize() const { + assert(getSortKind() == Z3_BV_SORT && "Not a bitvector sort!"); + return Z3_get_bv_sort_size(Z3Context::ZC, Sort); + } + + unsigned getFloatSortSize() const { + assert(getSortKind() == Z3_FLOATING_POINT_SORT && + "Not a floating-point sort!"); + return Z3_fpa_get_ebits(Z3Context::ZC, Sort) + + Z3_fpa_get_sbits(Z3Context::ZC, Sort); + } + + bool operator==(const Z3Sort &Other) const { + return Z3_is_eq_sort(Z3Context::ZC, Sort, Other.Sort); + } + + Z3Sort &operator=(const Z3Sort &Move) { + Z3_inc_ref(Z3Context::ZC, reinterpret_cast<Z3_ast>(Move.Sort)); + Z3_dec_ref(Z3Context::ZC, reinterpret_cast<Z3_ast>(Sort)); + Sort = Move.Sort; + return *this; + } + + void print(raw_ostream &OS) const { + OS << Z3_sort_to_string(Z3Context::ZC, Sort); + } + + LLVM_DUMP_METHOD void dump() const { print(llvm::errs()); } +}; // end class Z3Sort + +class Z3Expr { + friend class Z3Model; + friend class Z3Solver; + + Z3_ast AST; + + Z3Expr(Z3_ast ZA) : AST(ZA) { Z3_inc_ref(Z3Context::ZC, AST); } + + // Return an appropriate floating-point rounding mode. + static Z3Expr getFloatRoundingMode() { + // TODO: Don't assume nearest ties to even rounding mode + return Z3Expr(Z3_mk_fpa_rne(Z3Context::ZC)); + } + + // Determine whether two float semantics are equivalent + static bool areEquivalent(const llvm::fltSemantics &LHS, + const llvm::fltSemantics &RHS) { + return (llvm::APFloat::semanticsPrecision(LHS) == + llvm::APFloat::semanticsPrecision(RHS)) && + (llvm::APFloat::semanticsMinExponent(LHS) == + llvm::APFloat::semanticsMinExponent(RHS)) && + (llvm::APFloat::semanticsMaxExponent(LHS) == + llvm::APFloat::semanticsMaxExponent(RHS)) && + (llvm::APFloat::semanticsSizeInBits(LHS) == + llvm::APFloat::semanticsSizeInBits(RHS)); + } + +public: + /// Override implicit copy constructor for correct reference counting. + Z3Expr(const Z3Expr &Copy) : AST(Copy.AST) { Z3_inc_ref(Z3Context::ZC, AST); } + + /// Provide move constructor + Z3Expr(Z3Expr &&Move) : AST(nullptr) { *this = std::move(Move); } + + /// Provide move assignment constructor + Z3Expr &operator=(Z3Expr &&Move) { + if (this != &Move) { + if (AST) + Z3_dec_ref(Z3Context::ZC, AST); + AST = Move.AST; + Move.AST = nullptr; + } + return *this; + } + + ~Z3Expr() { + if (AST) + Z3_dec_ref(Z3Context::ZC, AST); + } + + /// Get the corresponding IEEE floating-point type for a given bitwidth. + static const llvm::fltSemantics &getFloatSemantics(unsigned BitWidth) { + switch (BitWidth) { + default: + llvm_unreachable("Unsupported floating-point semantics!"); + break; + case 16: + return llvm::APFloat::IEEEhalf(); + case 32: + return llvm::APFloat::IEEEsingle(); + case 64: + return llvm::APFloat::IEEEdouble(); + case 128: + return llvm::APFloat::IEEEquad(); + } + } + + /// Construct a Z3Expr from a unary operator, given a Z3_context. + static Z3Expr fromUnOp(const UnaryOperator::Opcode Op, const Z3Expr &Exp) { + Z3_ast AST; + + switch (Op) { + default: + llvm_unreachable("Unimplemented opcode"); + break; + + case UO_Minus: + AST = Z3_mk_bvneg(Z3Context::ZC, Exp.AST); + break; + + case UO_Not: + AST = Z3_mk_bvnot(Z3Context::ZC, Exp.AST); + break; + + case UO_LNot: + AST = Z3_mk_not(Z3Context::ZC, Exp.AST); + break; + } + + return Z3Expr(AST); + } + + /// Construct a Z3Expr from a floating-point unary operator, given a + /// Z3_context. + static Z3Expr fromFloatUnOp(const UnaryOperator::Opcode Op, + const Z3Expr &Exp) { + Z3_ast AST; + + switch (Op) { + default: + llvm_unreachable("Unimplemented opcode"); + break; + + case UO_Minus: + AST = Z3_mk_fpa_neg(Z3Context::ZC, Exp.AST); + break; + + case UO_LNot: + return Z3Expr::fromUnOp(Op, Exp); + } + + return Z3Expr(AST); + } + + /// Construct a Z3Expr from a n-ary binary operator. + static Z3Expr fromNBinOp(const BinaryOperator::Opcode Op, + const std::vector<Z3_ast> &ASTs) { + Z3_ast AST; + + switch (Op) { + default: + llvm_unreachable("Unimplemented opcode"); + break; + + case BO_LAnd: + AST = Z3_mk_and(Z3Context::ZC, ASTs.size(), ASTs.data()); + break; + + case BO_LOr: + AST = Z3_mk_or(Z3Context::ZC, ASTs.size(), ASTs.data()); + break; + } + + return Z3Expr(AST); + } + + /// Construct a Z3Expr from a binary operator, given a Z3_context. + static Z3Expr fromBinOp(const Z3Expr &LHS, const BinaryOperator::Opcode Op, + const Z3Expr &RHS, bool isSigned) { + Z3_ast AST; + + assert(Z3Sort::getSort(LHS.AST) == Z3Sort::getSort(RHS.AST) && + "AST's must have the same sort!"); + + switch (Op) { + default: + llvm_unreachable("Unimplemented opcode"); + break; + + // Multiplicative operators + case BO_Mul: + AST = Z3_mk_bvmul(Z3Context::ZC, LHS.AST, RHS.AST); + break; + case BO_Div: + AST = isSigned ? Z3_mk_bvsdiv(Z3Context::ZC, LHS.AST, RHS.AST) + : Z3_mk_bvudiv(Z3Context::ZC, LHS.AST, RHS.AST); + break; + case BO_Rem: + AST = isSigned ? Z3_mk_bvsrem(Z3Context::ZC, LHS.AST, RHS.AST) + : Z3_mk_bvurem(Z3Context::ZC, LHS.AST, RHS.AST); + break; + + // Additive operators + case BO_Add: + AST = Z3_mk_bvadd(Z3Context::ZC, LHS.AST, RHS.AST); + break; + case BO_Sub: + AST = Z3_mk_bvsub(Z3Context::ZC, LHS.AST, RHS.AST); + break; + + // Bitwise shift operators + case BO_Shl: + AST = Z3_mk_bvshl(Z3Context::ZC, LHS.AST, RHS.AST); + break; + case BO_Shr: + AST = isSigned ? Z3_mk_bvashr(Z3Context::ZC, LHS.AST, RHS.AST) + : Z3_mk_bvlshr(Z3Context::ZC, LHS.AST, RHS.AST); + break; + + // Relational operators + case BO_LT: + AST = isSigned ? Z3_mk_bvslt(Z3Context::ZC, LHS.AST, RHS.AST) + : Z3_mk_bvult(Z3Context::ZC, LHS.AST, RHS.AST); + break; + case BO_GT: + AST = isSigned ? Z3_mk_bvsgt(Z3Context::ZC, LHS.AST, RHS.AST) + : Z3_mk_bvugt(Z3Context::ZC, LHS.AST, RHS.AST); + break; + case BO_LE: + AST = isSigned ? Z3_mk_bvsle(Z3Context::ZC, LHS.AST, RHS.AST) + : Z3_mk_bvule(Z3Context::ZC, LHS.AST, RHS.AST); + break; + case BO_GE: + AST = isSigned ? Z3_mk_bvsge(Z3Context::ZC, LHS.AST, RHS.AST) + : Z3_mk_bvuge(Z3Context::ZC, LHS.AST, RHS.AST); + break; + + // Equality operators + case BO_EQ: + AST = Z3_mk_eq(Z3Context::ZC, LHS.AST, RHS.AST); + break; + case BO_NE: + return Z3Expr::fromUnOp(UO_LNot, + Z3Expr::fromBinOp(LHS, BO_EQ, RHS, isSigned)); + break; + + // Bitwise operators + case BO_And: + AST = Z3_mk_bvand(Z3Context::ZC, LHS.AST, RHS.AST); + break; + case BO_Xor: + AST = Z3_mk_bvxor(Z3Context::ZC, LHS.AST, RHS.AST); + break; + case BO_Or: + AST = Z3_mk_bvor(Z3Context::ZC, LHS.AST, RHS.AST); + break; + + // Logical operators + case BO_LAnd: + case BO_LOr: { + std::vector<Z3_ast> Args = {LHS.AST, RHS.AST}; + return Z3Expr::fromNBinOp(Op, Args); + } + } + + return Z3Expr(AST); + } + + /// Construct a Z3Expr from a special floating-point binary operator, given + /// a Z3_context. + static Z3Expr fromFloatSpecialBinOp(const Z3Expr &LHS, + const BinaryOperator::Opcode Op, + const llvm::APFloat::fltCategory &RHS) { + Z3_ast AST; + + switch (Op) { + default: + llvm_unreachable("Unimplemented opcode"); + break; + + // Equality operators + case BO_EQ: + switch (RHS) { + case llvm::APFloat::fcInfinity: + AST = Z3_mk_fpa_is_infinite(Z3Context::ZC, LHS.AST); + break; + case llvm::APFloat::fcNaN: + AST = Z3_mk_fpa_is_nan(Z3Context::ZC, LHS.AST); + break; + case llvm::APFloat::fcNormal: + AST = Z3_mk_fpa_is_normal(Z3Context::ZC, LHS.AST); + break; + case llvm::APFloat::fcZero: + AST = Z3_mk_fpa_is_zero(Z3Context::ZC, LHS.AST); + break; + } + break; + case BO_NE: + return Z3Expr::fromFloatUnOp( + UO_LNot, Z3Expr::fromFloatSpecialBinOp(LHS, BO_EQ, RHS)); + break; + } + + return Z3Expr(AST); + } + + /// Construct a Z3Expr from a floating-point binary operator, given a + /// Z3_context. + static Z3Expr fromFloatBinOp(const Z3Expr &LHS, + const BinaryOperator::Opcode Op, + const Z3Expr &RHS) { + Z3_ast AST; + + assert(Z3Sort::getSort(LHS.AST) == Z3Sort::getSort(RHS.AST) && + "AST's must have the same sort!"); + + switch (Op) { + default: + llvm_unreachable("Unimplemented opcode"); + break; + + // Multiplicative operators + case BO_Mul: { + Z3Expr RoundingMode = Z3Expr::getFloatRoundingMode(); + AST = Z3_mk_fpa_mul(Z3Context::ZC, RoundingMode.AST, LHS.AST, RHS.AST); + break; + } + case BO_Div: { + Z3Expr RoundingMode = Z3Expr::getFloatRoundingMode(); + AST = Z3_mk_fpa_div(Z3Context::ZC, RoundingMode.AST, LHS.AST, RHS.AST); + break; + } + case BO_Rem: + AST = Z3_mk_fpa_rem(Z3Context::ZC, LHS.AST, RHS.AST); + break; + + // Additive operators + case BO_Add: { + Z3Expr RoundingMode = Z3Expr::getFloatRoundingMode(); + AST = Z3_mk_fpa_add(Z3Context::ZC, RoundingMode.AST, LHS.AST, RHS.AST); + break; + } + case BO_Sub: { + Z3Expr RoundingMode = Z3Expr::getFloatRoundingMode(); + AST = Z3_mk_fpa_sub(Z3Context::ZC, RoundingMode.AST, LHS.AST, RHS.AST); + break; + } + + // Relational operators + case BO_LT: + AST = Z3_mk_fpa_lt(Z3Context::ZC, LHS.AST, RHS.AST); + break; + case BO_GT: + AST = Z3_mk_fpa_gt(Z3Context::ZC, LHS.AST, RHS.AST); + break; + case BO_LE: + AST = Z3_mk_fpa_leq(Z3Context::ZC, LHS.AST, RHS.AST); + break; + case BO_GE: + AST = Z3_mk_fpa_geq(Z3Context::ZC, LHS.AST, RHS.AST); + break; + + // Equality operators + case BO_EQ: + AST = Z3_mk_fpa_eq(Z3Context::ZC, LHS.AST, RHS.AST); + break; + case BO_NE: + return Z3Expr::fromFloatUnOp(UO_LNot, + Z3Expr::fromFloatBinOp(LHS, BO_EQ, RHS)); + break; + + // Logical operators + case BO_LAnd: + case BO_LOr: + return Z3Expr::fromBinOp(LHS, Op, RHS, false); + } + + return Z3Expr(AST); + } + + /// Construct a Z3Expr from a SymbolData, given a Z3_context. + static Z3Expr fromData(const SymbolID ID, bool isBool, bool isFloat, + uint64_t BitWidth) { + llvm::Twine Name = "$" + llvm::Twine(ID); + + Z3Sort Sort; + if (isBool) + Sort = Z3Sort::getBoolSort(); + else if (isFloat) + Sort = Z3Sort::getFloatSort(BitWidth); + else + Sort = Z3Sort::getBitvectorSort(BitWidth); + + Z3_symbol Symbol = Z3_mk_string_symbol(Z3Context::ZC, Name.str().c_str()); + Z3_ast AST = Z3_mk_const(Z3Context::ZC, Symbol, Sort.Sort); + return Z3Expr(AST); + } + + /// Construct a Z3Expr from a SymbolCast, given a Z3_context. + static Z3Expr fromCast(const Z3Expr &Exp, QualType ToTy, uint64_t ToBitWidth, + QualType FromTy, uint64_t FromBitWidth) { + Z3_ast AST; + + if ((FromTy->isIntegralOrEnumerationType() && + ToTy->isIntegralOrEnumerationType()) || + (FromTy->isAnyPointerType() ^ ToTy->isAnyPointerType()) || + (FromTy->isBlockPointerType() ^ ToTy->isBlockPointerType()) || + (FromTy->isReferenceType() ^ ToTy->isReferenceType())) { + // Special case: Z3 boolean type is distinct from bitvector type, so + // must use if-then-else expression instead of direct cast + if (FromTy->isBooleanType()) { + assert(ToBitWidth > 0 && "BitWidth must be positive!"); + Z3Expr Zero = Z3Expr::fromInt("0", ToBitWidth); + Z3Expr One = Z3Expr::fromInt("1", ToBitWidth); + AST = Z3_mk_ite(Z3Context::ZC, Exp.AST, One.AST, Zero.AST); + } else if (ToBitWidth > FromBitWidth) { + AST = FromTy->isSignedIntegerOrEnumerationType() + ? Z3_mk_sign_ext(Z3Context::ZC, ToBitWidth - FromBitWidth, + Exp.AST) + : Z3_mk_zero_ext(Z3Context::ZC, ToBitWidth - FromBitWidth, + Exp.AST); + } else if (ToBitWidth < FromBitWidth) { + AST = Z3_mk_extract(Z3Context::ZC, ToBitWidth - 1, 0, Exp.AST); + } else { + // Both are bitvectors with the same width, ignore the type cast + return Exp; + } + } else if (FromTy->isRealFloatingType() && ToTy->isRealFloatingType()) { + if (ToBitWidth != FromBitWidth) { + Z3Expr RoundingMode = Z3Expr::getFloatRoundingMode(); + Z3Sort Sort = Z3Sort::getFloatSort(ToBitWidth); + AST = Z3_mk_fpa_to_fp_float(Z3Context::ZC, RoundingMode.AST, Exp.AST, + Sort.Sort); + } else { + return Exp; + } + } else if (FromTy->isIntegralOrEnumerationType() && + ToTy->isRealFloatingType()) { + Z3Expr RoundingMode = Z3Expr::getFloatRoundingMode(); + Z3Sort Sort = Z3Sort::getFloatSort(ToBitWidth); + AST = FromTy->isSignedIntegerOrEnumerationType() + ? Z3_mk_fpa_to_fp_signed(Z3Context::ZC, RoundingMode.AST, + Exp.AST, Sort.Sort) + : Z3_mk_fpa_to_fp_unsigned(Z3Context::ZC, RoundingMode.AST, + Exp.AST, Sort.Sort); + } else if (FromTy->isRealFloatingType() && + ToTy->isIntegralOrEnumerationType()) { + Z3Expr RoundingMode = Z3Expr::getFloatRoundingMode(); + AST = ToTy->isSignedIntegerOrEnumerationType() + ? Z3_mk_fpa_to_sbv(Z3Context::ZC, RoundingMode.AST, Exp.AST, + ToBitWidth) + : Z3_mk_fpa_to_ubv(Z3Context::ZC, RoundingMode.AST, Exp.AST, + ToBitWidth); + } else { + llvm_unreachable("Unsupported explicit type cast!"); + } + + return Z3Expr(AST); + } + + /// Construct a Z3Expr from a boolean, given a Z3_context. + static Z3Expr fromBoolean(const bool Bool) { + Z3_ast AST = Bool ? Z3_mk_true(Z3Context::ZC) : Z3_mk_false(Z3Context::ZC); + return Z3Expr(AST); + } + + /// Construct a Z3Expr from a finite APFloat, given a Z3_context. + static Z3Expr fromAPFloat(const llvm::APFloat &Float) { + Z3_ast AST; + Z3Sort Sort = Z3Sort::getFloatSort( + llvm::APFloat::semanticsSizeInBits(Float.getSemantics())); + + llvm::APSInt Int = llvm::APSInt(Float.bitcastToAPInt(), true); + Z3Expr Z3Int = Z3Expr::fromAPSInt(Int); + AST = Z3_mk_fpa_to_fp_bv(Z3Context::ZC, Z3Int.AST, Sort.Sort); + + return Z3Expr(AST); + } + + /// Construct a Z3Expr from an APSInt, given a Z3_context. + static Z3Expr fromAPSInt(const llvm::APSInt &Int) { + Z3Sort Sort = Z3Sort::getBitvectorSort(Int.getBitWidth()); + Z3_ast AST = + Z3_mk_numeral(Z3Context::ZC, Int.toString(10).c_str(), Sort.Sort); + return Z3Expr(AST); + } + + /// Construct a Z3Expr from an integer, given a Z3_context. + static Z3Expr fromInt(const char *Int, uint64_t BitWidth) { + Z3Sort Sort = Z3Sort::getBitvectorSort(BitWidth); + Z3_ast AST = Z3_mk_numeral(Z3Context::ZC, Int, Sort.Sort); + return Z3Expr(AST); + } + + /// Construct an APFloat from a Z3Expr, given the AST representation + static bool toAPFloat(const Z3Sort &Sort, const Z3_ast &AST, + llvm::APFloat &Float, bool useSemantics = true) { + assert(Sort.getSortKind() == Z3_FLOATING_POINT_SORT && + "Unsupported sort to floating-point!"); + + llvm::APSInt Int(Sort.getFloatSortSize(), true); + const llvm::fltSemantics &Semantics = + Z3Expr::getFloatSemantics(Sort.getFloatSortSize()); + Z3Sort BVSort = Z3Sort::getBitvectorSort(Sort.getFloatSortSize()); + if (!Z3Expr::toAPSInt(BVSort, AST, Int, true)) { + return false; + } + + if (useSemantics && + !Z3Expr::areEquivalent(Float.getSemantics(), Semantics)) { + assert(false && "Floating-point types don't match!"); + return false; + } + + Float = llvm::APFloat(Semantics, Int); + return true; + } + + /// Construct an APSInt from a Z3Expr, given the AST representation + static bool toAPSInt(const Z3Sort &Sort, const Z3_ast &AST, llvm::APSInt &Int, + bool useSemantics = true) { + switch (Sort.getSortKind()) { + default: + llvm_unreachable("Unsupported sort to integer!"); + case Z3_BV_SORT: { + if (useSemantics && Int.getBitWidth() != Sort.getBitvectorSortSize()) { + assert(false && "Bitvector types don't match!"); + return false; + } + + uint64_t Value[2]; + // Force cast because Z3 defines __uint64 to be a unsigned long long + // type, which isn't compatible with a unsigned long type, even if they + // are the same size. + Z3_get_numeral_uint64(Z3Context::ZC, AST, + reinterpret_cast<__uint64 *>(&Value[0])); + if (Sort.getBitvectorSortSize() <= 64) { + Int = llvm::APSInt(llvm::APInt(Int.getBitWidth(), Value[0]), true); + } else if (Sort.getBitvectorSortSize() == 128) { + Z3Expr ASTHigh = Z3Expr(Z3_mk_extract(Z3Context::ZC, 127, 64, AST)); + Z3_get_numeral_uint64(Z3Context::ZC, AST, + reinterpret_cast<__uint64 *>(&Value[1])); + Int = llvm::APSInt(llvm::APInt(Int.getBitWidth(), Value), true); + } else { + assert(false && "Bitwidth not supported!"); + return false; + } + return true; + } + case Z3_BOOL_SORT: + if (useSemantics && Int.getBitWidth() < 1) { + assert(false && "Boolean type doesn't match!"); + return false; + } + Int = llvm::APSInt( + llvm::APInt(Int.getBitWidth(), + Z3_get_bool_value(Z3Context::ZC, AST) == Z3_L_TRUE ? 1 + : 0), + true); + return true; + } + } + + void Profile(llvm::FoldingSetNodeID &ID) const { + ID.AddInteger(Z3_get_ast_hash(Z3Context::ZC, AST)); + } + + bool operator<(const Z3Expr &Other) const { + llvm::FoldingSetNodeID ID1, ID2; + Profile(ID1); + Other.Profile(ID2); + return ID1 < ID2; + } + + /// Comparison of AST equality, not model equivalence. + bool operator==(const Z3Expr &Other) const { + assert(Z3_is_eq_sort(Z3Context::ZC, Z3_get_sort(Z3Context::ZC, AST), + Z3_get_sort(Z3Context::ZC, Other.AST)) && + "AST's must have the same sort"); + return Z3_is_eq_ast(Z3Context::ZC, AST, Other.AST); + } + + /// Override implicit move constructor for correct reference counting. + Z3Expr &operator=(const Z3Expr &Move) { + Z3_inc_ref(Z3Context::ZC, Move.AST); + Z3_dec_ref(Z3Context::ZC, AST); + AST = Move.AST; + return *this; + } + + void print(raw_ostream &OS) const { + OS << Z3_ast_to_string(Z3Context::ZC, AST); + } + + LLVM_DUMP_METHOD void dump() const { print(llvm::errs()); } +}; // end class Z3Expr + +class Z3Model { + Z3_model Model; + +public: + Z3Model(Z3_model ZM) : Model(ZM) { Z3_model_inc_ref(Z3Context::ZC, Model); } + + /// Override implicit copy constructor for correct reference counting. + Z3Model(const Z3Model &Copy) : Model(Copy.Model) { + Z3_model_inc_ref(Z3Context::ZC, Model); + } + + /// Provide move constructor + Z3Model(Z3Model &&Move) : Model(nullptr) { *this = std::move(Move); } + + /// Provide move assignment constructor + Z3Model &operator=(Z3Model &&Move) { + if (this != &Move) { + if (Model) + Z3_model_dec_ref(Z3Context::ZC, Model); + Model = Move.Model; + Move.Model = nullptr; + } + return *this; + } + + ~Z3Model() { + if (Model) + Z3_model_dec_ref(Z3Context::ZC, Model); + } + + /// Given an expression, extract the value of this operand in the model. + bool getInterpretation(const Z3Expr &Exp, llvm::APSInt &Int) const { + Z3_func_decl Func = + Z3_get_app_decl(Z3Context::ZC, Z3_to_app(Z3Context::ZC, Exp.AST)); + if (Z3_model_has_interp(Z3Context::ZC, Model, Func) != Z3_L_TRUE) + return false; + + Z3_ast Assign = Z3_model_get_const_interp(Z3Context::ZC, Model, Func); + Z3Sort Sort = Z3Sort::getSort(Assign); + return Z3Expr::toAPSInt(Sort, Assign, Int, true); + } + + /// Given an expression, extract the value of this operand in the model. + bool getInterpretation(const Z3Expr &Exp, llvm::APFloat &Float) const { + Z3_func_decl Func = + Z3_get_app_decl(Z3Context::ZC, Z3_to_app(Z3Context::ZC, Exp.AST)); + if (Z3_model_has_interp(Z3Context::ZC, Model, Func) != Z3_L_TRUE) + return false; + + Z3_ast Assign = Z3_model_get_const_interp(Z3Context::ZC, Model, Func); + Z3Sort Sort = Z3Sort::getSort(Assign); + return Z3Expr::toAPFloat(Sort, Assign, Float, true); + } + + void print(raw_ostream &OS) const { + OS << Z3_model_to_string(Z3Context::ZC, Model); + } + + LLVM_DUMP_METHOD void dump() const { print(llvm::errs()); } +}; // end class Z3Model + +class Z3Solver { + friend class Z3ConstraintManager; + + Z3_solver Solver; + + Z3Solver(Z3_solver ZS) : Solver(ZS) { + Z3_solver_inc_ref(Z3Context::ZC, Solver); + } + +public: + /// Override implicit copy constructor for correct reference counting. + Z3Solver(const Z3Solver &Copy) : Solver(Copy.Solver) { + Z3_solver_inc_ref(Z3Context::ZC, Solver); + } + + /// Provide move constructor + Z3Solver(Z3Solver &&Move) : Solver(nullptr) { *this = std::move(Move); } + + /// Provide move assignment constructor + Z3Solver &operator=(Z3Solver &&Move) { + if (this != &Move) { + if (Solver) + Z3_solver_dec_ref(Z3Context::ZC, Solver); + Solver = Move.Solver; + Move.Solver = nullptr; + } + return *this; + } + + ~Z3Solver() { + if (Solver) + Z3_solver_dec_ref(Z3Context::ZC, Solver); + } + + /// Given a constraint, add it to the solver + void addConstraint(const Z3Expr &Exp) { + Z3_solver_assert(Z3Context::ZC, Solver, Exp.AST); + } + + /// Given a program state, construct the logical conjunction and add it to + /// the solver + void addStateConstraints(ProgramStateRef State) { + // TODO: Don't add all the constraints, only the relevant ones + ConstraintZ3Ty CZ = State->get<ConstraintZ3>(); + ConstraintZ3Ty::iterator I = CZ.begin(), IE = CZ.end(); + + // Construct the logical AND of all the constraints + if (I != IE) { + std::vector<Z3_ast> ASTs; + + while (I != IE) + ASTs.push_back(I++->second.AST); + + Z3Expr Conj = Z3Expr::fromNBinOp(BO_LAnd, ASTs); + addConstraint(Conj); + } + } + + /// Check if the constraints are satisfiable + Z3_lbool check() { return Z3_solver_check(Z3Context::ZC, Solver); } + + /// Push the current solver state + void push() { return Z3_solver_push(Z3Context::ZC, Solver); } + + /// Pop the previous solver state + void pop(unsigned NumStates = 1) { + assert(Z3_solver_get_num_scopes(Z3Context::ZC, Solver) >= NumStates); + return Z3_solver_pop(Z3Context::ZC, Solver, NumStates); + } + + /// Get a model from the solver. Caller should check the model is + /// satisfiable. + Z3Model getModel() { + return Z3Model(Z3_solver_get_model(Z3Context::ZC, Solver)); + } + + /// Reset the solver and remove all constraints. + void reset() { Z3_solver_reset(Z3Context::ZC, Solver); } +}; // end class Z3Solver + +void Z3ErrorHandler(Z3_context Context, Z3_error_code Error) { + llvm::report_fatal_error("Z3 error: " + + llvm::Twine(Z3_get_error_msg_ex(Context, Error))); +} + +class Z3ConstraintManager : public SimpleConstraintManager { + Z3Context Context; + mutable Z3Solver Solver; + +public: + Z3ConstraintManager(SubEngine *SE, SValBuilder &SB) + : SimpleConstraintManager(SE, SB), + Solver(Z3_mk_simple_solver(Z3Context::ZC)) { + Z3_set_error_handler(Z3Context::ZC, Z3ErrorHandler); + } + + //===------------------------------------------------------------------===// + // Implementation for interface from ConstraintManager. + //===------------------------------------------------------------------===// + + bool canReasonAbout(SVal X) const override; + + ConditionTruthVal checkNull(ProgramStateRef State, SymbolRef Sym) override; + + const llvm::APSInt *getSymVal(ProgramStateRef State, + SymbolRef Sym) const override; + + ProgramStateRef removeDeadBindings(ProgramStateRef St, + SymbolReaper &SymReaper) override; + + void print(ProgramStateRef St, raw_ostream &Out, const char *nl, + const char *sep) override; + + //===------------------------------------------------------------------===// + // Implementation for interface from SimpleConstraintManager. + //===------------------------------------------------------------------===// + + ProgramStateRef assumeSym(ProgramStateRef state, SymbolRef Sym, + bool Assumption) override; + + ProgramStateRef assumeSymInclusiveRange(ProgramStateRef State, SymbolRef Sym, + const llvm::APSInt &From, + const llvm::APSInt &To, + bool InRange) override; + + ProgramStateRef assumeSymUnsupported(ProgramStateRef State, SymbolRef Sym, + bool Assumption) override; + +private: + //===------------------------------------------------------------------===// + // Internal implementation. + //===------------------------------------------------------------------===// + + // Check whether a new model is satisfiable, and update the program state. + ProgramStateRef assumeZ3Expr(ProgramStateRef State, SymbolRef Sym, + const Z3Expr &Exp); + + // Generate and check a Z3 model, using the given constraint. + Z3_lbool checkZ3Model(ProgramStateRef State, const Z3Expr &Exp) const; + + // Generate a Z3Expr that represents the given symbolic expression. + // Sets the hasComparison parameter if the expression has a comparison + // operator. + // Sets the RetTy parameter to the final return type after promotions and + // casts. + Z3Expr getZ3Expr(SymbolRef Sym, QualType *RetTy = nullptr, + bool *hasComparison = nullptr) const; + + // Generate a Z3Expr that takes the logical not of an expression. + Z3Expr getZ3NotExpr(const Z3Expr &Exp) const; + + // Generate a Z3Expr that compares the expression to zero. + Z3Expr getZ3ZeroExpr(const Z3Expr &Exp, QualType RetTy, + bool Assumption) const; + + // Recursive implementation to unpack and generate symbolic expression. + // Sets the hasComparison and RetTy parameters. See getZ3Expr(). + Z3Expr getZ3SymExpr(SymbolRef Sym, QualType *RetTy, + bool *hasComparison) const; + + // Wrapper to generate Z3Expr from SymbolData. + Z3Expr getZ3DataExpr(const SymbolID ID, QualType Ty) const; + + // Wrapper to generate Z3Expr from SymbolCast. + Z3Expr getZ3CastExpr(const Z3Expr &Exp, QualType FromTy, QualType Ty) const; + + // Wrapper to generate Z3Expr from BinarySymExpr. + // Sets the hasComparison and RetTy parameters. See getZ3Expr(). + Z3Expr getZ3SymBinExpr(const BinarySymExpr *BSE, bool *hasComparison, + QualType *RetTy) const; + + // Wrapper to generate Z3Expr from unpacked binary symbolic expression. + // Sets the RetTy parameter. See getZ3Expr(). + Z3Expr getZ3BinExpr(const Z3Expr &LHS, QualType LTy, + BinaryOperator::Opcode Op, const Z3Expr &RHS, + QualType RTy, QualType *RetTy) const; + + //===------------------------------------------------------------------===// + // Helper functions. + //===------------------------------------------------------------------===// + + // Recover the QualType of an APSInt. + // TODO: Refactor to put elsewhere + QualType getAPSIntType(const llvm::APSInt &Int) const; + + // Perform implicit type conversion on binary symbolic expressions. + // May modify all input parameters. + // TODO: Refactor to use built-in conversion functions + void doTypeConversion(Z3Expr &LHS, Z3Expr &RHS, QualType <y, + QualType &RTy) const; + + // Perform implicit integer type conversion. + // May modify all input parameters. + // TODO: Refactor to use Sema::handleIntegerConversion() + template <typename T, + T(doCast)(const T &, QualType, uint64_t, QualType, uint64_t)> + void doIntTypeConversion(T &LHS, QualType <y, T &RHS, QualType &RTy) const; + + // Perform implicit floating-point type conversion. + // May modify all input parameters. + // TODO: Refactor to use Sema::handleFloatConversion() + template <typename T, + T(doCast)(const T &, QualType, uint64_t, QualType, uint64_t)> + void doFloatTypeConversion(T &LHS, QualType <y, T &RHS, + QualType &RTy) const; + + // Callback function for doCast parameter on APSInt type. + static llvm::APSInt castAPSInt(const llvm::APSInt &V, QualType ToTy, + uint64_t ToWidth, QualType FromTy, + uint64_t FromWidth); +}; // end class Z3ConstraintManager + +Z3_context Z3Context::ZC; + +} // end anonymous namespace + +ProgramStateRef Z3ConstraintManager::assumeSym(ProgramStateRef State, + SymbolRef Sym, bool Assumption) { + QualType RetTy; + bool hasComparison; + + Z3Expr Exp = getZ3Expr(Sym, &RetTy, &hasComparison); + // Create zero comparison for implicit boolean cast, with reversed assumption + if (!hasComparison && !RetTy->isBooleanType()) + return assumeZ3Expr(State, Sym, getZ3ZeroExpr(Exp, RetTy, !Assumption)); + + return assumeZ3Expr(State, Sym, Assumption ? Exp : getZ3NotExpr(Exp)); +} + +ProgramStateRef Z3ConstraintManager::assumeSymInclusiveRange( + ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &From, + const llvm::APSInt &To, bool InRange) { + QualType RetTy; + // The expression may be casted, so we cannot call getZ3DataExpr() directly + Z3Expr Exp = getZ3Expr(Sym, &RetTy); + + assert((getAPSIntType(From) == getAPSIntType(To)) && + "Range values have different types!"); + QualType RTy = getAPSIntType(From); + bool isSignedTy = RetTy->isSignedIntegerOrEnumerationType(); + Z3Expr FromExp = Z3Expr::fromAPSInt(From); + Z3Expr ToExp = Z3Expr::fromAPSInt(To); + + // Construct single (in)equality + if (From == To) + return assumeZ3Expr(State, Sym, + getZ3BinExpr(Exp, RetTy, InRange ? BO_EQ : BO_NE, + FromExp, RTy, nullptr)); + + // Construct two (in)equalities, and a logical and/or + Z3Expr LHS = + getZ3BinExpr(Exp, RetTy, InRange ? BO_GE : BO_LT, FromExp, RTy, nullptr); + Z3Expr RHS = + getZ3BinExpr(Exp, RetTy, InRange ? BO_LE : BO_GT, ToExp, RTy, nullptr); + return assumeZ3Expr( + State, Sym, + Z3Expr::fromBinOp(LHS, InRange ? BO_LAnd : BO_LOr, RHS, isSignedTy)); +} + +ProgramStateRef Z3ConstraintManager::assumeSymUnsupported(ProgramStateRef State, + SymbolRef Sym, + bool Assumption) { + // Skip anything that is unsupported + return State; +} + +bool Z3ConstraintManager::canReasonAbout(SVal X) const { + const TargetInfo &TI = getBasicVals().getContext().getTargetInfo(); + + Optional<nonloc::SymbolVal> SymVal = X.getAs<nonloc::SymbolVal>(); + if (!SymVal) + return true; + + const SymExpr *Sym = SymVal->getSymbol(); + do { + QualType Ty = Sym->getType(); + + // Complex types are not modeled + if (Ty->isComplexType() || Ty->isComplexIntegerType()) + return false; + + // Non-IEEE 754 floating-point types are not modeled + if ((Ty->isSpecificBuiltinType(BuiltinType::LongDouble) && + (&TI.getLongDoubleFormat() == &llvm::APFloat::x87DoubleExtended() || + &TI.getLongDoubleFormat() == &llvm::APFloat::PPCDoubleDouble()))) + return false; + + if (isa<SymbolData>(Sym)) { + break; + } else if (const SymbolCast *SC = dyn_cast<SymbolCast>(Sym)) { + Sym = SC->getOperand(); + } else if (const BinarySymExpr *BSE = dyn_cast<BinarySymExpr>(Sym)) { + if (const SymIntExpr *SIE = dyn_cast<SymIntExpr>(BSE)) { + Sym = SIE->getLHS(); + } else if (const IntSymExpr *ISE = dyn_cast<IntSymExpr>(BSE)) { + Sym = ISE->getRHS(); + } else if (const SymSymExpr *SSM = dyn_cast<SymSymExpr>(BSE)) { + return canReasonAbout(nonloc::SymbolVal(SSM->getLHS())) && + canReasonAbout(nonloc::SymbolVal(SSM->getRHS())); + } else { + llvm_unreachable("Unsupported binary expression to reason about!"); + } + } else { + llvm_unreachable("Unsupported expression to reason about!"); + } + } while (Sym); + + return true; +} + +ConditionTruthVal Z3ConstraintManager::checkNull(ProgramStateRef State, + SymbolRef Sym) { + QualType RetTy; + // The expression may be casted, so we cannot call getZ3DataExpr() directly + Z3Expr VarExp = getZ3Expr(Sym, &RetTy); + Z3Expr Exp = getZ3ZeroExpr(VarExp, RetTy, true); + // Negate the constraint + Z3Expr NotExp = getZ3ZeroExpr(VarExp, RetTy, false); + + Solver.reset(); + Solver.addStateConstraints(State); + + Solver.push(); + Solver.addConstraint(Exp); + Z3_lbool isSat = Solver.check(); + + Solver.pop(); + Solver.addConstraint(NotExp); + Z3_lbool isNotSat = Solver.check(); + + // Zero is the only possible solution + if (isSat == Z3_L_TRUE && isNotSat == Z3_L_FALSE) + return true; + // Zero is not a solution + else if (isSat == Z3_L_FALSE && isNotSat == Z3_L_TRUE) + return false; + + // Zero may be a solution + return ConditionTruthVal(); +} + +const llvm::APSInt *Z3ConstraintManager::getSymVal(ProgramStateRef State, + SymbolRef Sym) const { + BasicValueFactory &BV = getBasicVals(); + ASTContext &Ctx = BV.getContext(); + + if (const SymbolData *SD = dyn_cast<SymbolData>(Sym)) { + QualType Ty = Sym->getType(); + assert(!Ty->isRealFloatingType()); + llvm::APSInt Value(Ctx.getTypeSize(Ty), + !Ty->isSignedIntegerOrEnumerationType()); + + Z3Expr Exp = getZ3DataExpr(SD->getSymbolID(), Ty); + + Solver.reset(); + Solver.addStateConstraints(State); + + // Constraints are unsatisfiable + if (Solver.check() != Z3_L_TRUE) + return nullptr; + + Z3Model Model = Solver.getModel(); + // Model does not assign interpretation + if (!Model.getInterpretation(Exp, Value)) + return nullptr; + + // A value has been obtained, check if it is the only value + Z3Expr NotExp = Z3Expr::fromBinOp( + Exp, BO_NE, + Ty->isBooleanType() ? Z3Expr::fromBoolean(Value.getBoolValue()) + : Z3Expr::fromAPSInt(Value), + false); + + Solver.addConstraint(NotExp); + if (Solver.check() == Z3_L_TRUE) + return nullptr; + + // This is the only solution, store it + return &BV.getValue(Value); + } else if (const SymbolCast *SC = dyn_cast<SymbolCast>(Sym)) { + SymbolRef CastSym = SC->getOperand(); + QualType CastTy = SC->getType(); + // Skip the void type + if (CastTy->isVoidType()) + return nullptr; + + const llvm::APSInt *Value; + if (!(Value = getSymVal(State, CastSym))) + return nullptr; + return &BV.Convert(SC->getType(), *Value); + } else if (const BinarySymExpr *BSE = dyn_cast<BinarySymExpr>(Sym)) { + const llvm::APSInt *LHS, *RHS; + if (const SymIntExpr *SIE = dyn_cast<SymIntExpr>(BSE)) { + LHS = getSymVal(State, SIE->getLHS()); + RHS = &SIE->getRHS(); + } else if (const IntSymExpr *ISE = dyn_cast<IntSymExpr>(BSE)) { + LHS = &ISE->getLHS(); + RHS = getSymVal(State, ISE->getRHS()); + } else if (const SymSymExpr *SSM = dyn_cast<SymSymExpr>(BSE)) { + // Early termination to avoid expensive call + LHS = getSymVal(State, SSM->getLHS()); + RHS = LHS ? getSymVal(State, SSM->getRHS()) : nullptr; + } else { + llvm_unreachable("Unsupported binary expression to get symbol value!"); + } + + if (!LHS || !RHS) + return nullptr; + + llvm::APSInt ConvertedLHS = *LHS, ConvertedRHS = *RHS; + QualType LTy = getAPSIntType(*LHS), RTy = getAPSIntType(*RHS); + doIntTypeConversion<llvm::APSInt, Z3ConstraintManager::castAPSInt>( + ConvertedLHS, LTy, ConvertedRHS, RTy); + return BV.evalAPSInt(BSE->getOpcode(), ConvertedLHS, ConvertedRHS); + } + + llvm_unreachable("Unsupported expression to get symbol value!"); +} + +ProgramStateRef +Z3ConstraintManager::removeDeadBindings(ProgramStateRef State, + SymbolReaper &SymReaper) { + ConstraintZ3Ty CZ = State->get<ConstraintZ3>(); + ConstraintZ3Ty::Factory &CZFactory = State->get_context<ConstraintZ3>(); + + for (ConstraintZ3Ty::iterator I = CZ.begin(), E = CZ.end(); I != E; ++I) { + if (SymReaper.maybeDead(I->first)) + CZ = CZFactory.remove(CZ, *I); + } + + return State->set<ConstraintZ3>(CZ); +} + +//===------------------------------------------------------------------===// +// Internal implementation. +//===------------------------------------------------------------------===// + +ProgramStateRef Z3ConstraintManager::assumeZ3Expr(ProgramStateRef State, + SymbolRef Sym, + const Z3Expr &Exp) { + // Check the model, avoid simplifying AST to save time + if (checkZ3Model(State, Exp) == Z3_L_TRUE) + return State->add<ConstraintZ3>(std::make_pair(Sym, Exp)); + + return nullptr; +} + +Z3_lbool Z3ConstraintManager::checkZ3Model(ProgramStateRef State, + const Z3Expr &Exp) const { + Solver.reset(); + Solver.addConstraint(Exp); + Solver.addStateConstraints(State); + return Solver.check(); +} + +Z3Expr Z3ConstraintManager::getZ3Expr(SymbolRef Sym, QualType *RetTy, + bool *hasComparison) const { + if (hasComparison) { + *hasComparison = false; + } + + return getZ3SymExpr(Sym, RetTy, hasComparison); +} + +Z3Expr Z3ConstraintManager::getZ3NotExpr(const Z3Expr &Exp) const { + return Z3Expr::fromUnOp(UO_LNot, Exp); +} + +Z3Expr Z3ConstraintManager::getZ3ZeroExpr(const Z3Expr &Exp, QualType Ty, + bool Assumption) const { + ASTContext &Ctx = getBasicVals().getContext(); + if (Ty->isRealFloatingType()) { + llvm::APFloat Zero = llvm::APFloat::getZero(Ctx.getFloatTypeSemantics(Ty)); + return Z3Expr::fromFloatBinOp(Exp, Assumption ? BO_EQ : BO_NE, + Z3Expr::fromAPFloat(Zero)); + } else if (Ty->isIntegralOrEnumerationType() || Ty->isAnyPointerType() || + Ty->isBlockPointerType() || Ty->isReferenceType()) { + bool isSigned = Ty->isSignedIntegerOrEnumerationType(); + // Skip explicit comparison for boolean types + if (Ty->isBooleanType()) + return Assumption ? getZ3NotExpr(Exp) : Exp; + return Z3Expr::fromBinOp(Exp, Assumption ? BO_EQ : BO_NE, + Z3Expr::fromInt("0", Ctx.getTypeSize(Ty)), + isSigned); + } + + llvm_unreachable("Unsupported type for zero value!"); +} + +Z3Expr Z3ConstraintManager::getZ3SymExpr(SymbolRef Sym, QualType *RetTy, + bool *hasComparison) const { + if (const SymbolData *SD = dyn_cast<SymbolData>(Sym)) { + if (RetTy) + *RetTy = Sym->getType(); + + return getZ3DataExpr(SD->getSymbolID(), Sym->getType()); + } else if (const SymbolCast *SC = dyn_cast<SymbolCast>(Sym)) { + if (RetTy) + *RetTy = Sym->getType(); + + QualType FromTy; + Z3Expr Exp = getZ3SymExpr(SC->getOperand(), &FromTy, hasComparison); + // Casting an expression with a comparison invalidates it. Note that this + // must occur after the recursive call above. + // e.g. (signed char) (x > 0) + if (hasComparison) + *hasComparison = false; + return getZ3CastExpr(Exp, FromTy, Sym->getType()); + } else if (const BinarySymExpr *BSE = dyn_cast<BinarySymExpr>(Sym)) { + Z3Expr Exp = getZ3SymBinExpr(BSE, hasComparison, RetTy); + // Set the hasComparison parameter, in post-order traversal order. + if (hasComparison) + *hasComparison = BinaryOperator::isComparisonOp(BSE->getOpcode()); + return Exp; + } + + llvm_unreachable("Unsupported SymbolRef type!"); +} + +Z3Expr Z3ConstraintManager::getZ3DataExpr(const SymbolID ID, + QualType Ty) const { + ASTContext &Ctx = getBasicVals().getContext(); + return Z3Expr::fromData(ID, Ty->isBooleanType(), Ty->isRealFloatingType(), + Ctx.getTypeSize(Ty)); +} + +Z3Expr Z3ConstraintManager::getZ3CastExpr(const Z3Expr &Exp, QualType FromTy, + QualType ToTy) const { + ASTContext &Ctx = getBasicVals().getContext(); + return Z3Expr::fromCast(Exp, ToTy, Ctx.getTypeSize(ToTy), FromTy, + Ctx.getTypeSize(FromTy)); +} + +Z3Expr Z3ConstraintManager::getZ3SymBinExpr(const BinarySymExpr *BSE, + bool *hasComparison, + QualType *RetTy) const { + QualType LTy, RTy; + BinaryOperator::Opcode Op = BSE->getOpcode(); + + if (const SymIntExpr *SIE = dyn_cast<SymIntExpr>(BSE)) { + RTy = getAPSIntType(SIE->getRHS()); + Z3Expr LHS = getZ3SymExpr(SIE->getLHS(), <y, hasComparison); + Z3Expr RHS = Z3Expr::fromAPSInt(SIE->getRHS()); + return getZ3BinExpr(LHS, LTy, Op, RHS, RTy, RetTy); + } else if (const IntSymExpr *ISE = dyn_cast<IntSymExpr>(BSE)) { + LTy = getAPSIntType(ISE->getLHS()); + Z3Expr LHS = Z3Expr::fromAPSInt(ISE->getLHS()); + Z3Expr RHS = getZ3SymExpr(ISE->getRHS(), &RTy, hasComparison); + return getZ3BinExpr(LHS, LTy, Op, RHS, RTy, RetTy); + } else if (const SymSymExpr *SSM = dyn_cast<SymSymExpr>(BSE)) { + Z3Expr LHS = getZ3SymExpr(SSM->getLHS(), <y, hasComparison); + Z3Expr RHS = getZ3SymExpr(SSM->getRHS(), &RTy, hasComparison); + return getZ3BinExpr(LHS, LTy, Op, RHS, RTy, RetTy); + } else { + llvm_unreachable("Unsupported BinarySymExpr type!"); + } +} + +Z3Expr Z3ConstraintManager::getZ3BinExpr(const Z3Expr &LHS, QualType LTy, + BinaryOperator::Opcode Op, + const Z3Expr &RHS, QualType RTy, + QualType *RetTy) const { + Z3Expr NewLHS = LHS; + Z3Expr NewRHS = RHS; + doTypeConversion(NewLHS, NewRHS, LTy, RTy); + // Update the return type parameter if the output type has changed. + if (RetTy) { + // A boolean result can be represented as an integer type in C/C++, but at + // this point we only care about the Z3 type. Set it as a boolean type to + // avoid subsequent Z3 errors. + if (BinaryOperator::isComparisonOp(Op) || BinaryOperator::isLogicalOp(Op)) { + ASTContext &Ctx = getBasicVals().getContext(); + *RetTy = Ctx.BoolTy; + } else { + *RetTy = LTy; + } + + // If the two operands are pointers and the operation is a subtraction, the + // result is of type ptrdiff_t, which is signed + if (LTy->isAnyPointerType() && LTy == RTy && Op == BO_Sub) { + ASTContext &Ctx = getBasicVals().getContext(); + *RetTy = Ctx.getIntTypeForBitwidth(Ctx.getTypeSize(LTy), true); + } + } + + return LTy->isRealFloatingType() + ? Z3Expr::fromFloatBinOp(NewLHS, Op, NewRHS) + : Z3Expr::fromBinOp(NewLHS, Op, NewRHS, + LTy->isSignedIntegerOrEnumerationType()); +} + +//===------------------------------------------------------------------===// +// Helper functions. +//===------------------------------------------------------------------===// + +QualType Z3ConstraintManager::getAPSIntType(const llvm::APSInt &Int) const { + ASTContext &Ctx = getBasicVals().getContext(); + return Ctx.getIntTypeForBitwidth(Int.getBitWidth(), Int.isSigned()); +} + +void Z3ConstraintManager::doTypeConversion(Z3Expr &LHS, Z3Expr &RHS, + QualType <y, QualType &RTy) const { + ASTContext &Ctx = getBasicVals().getContext(); + + // Perform type conversion + if (LTy->isIntegralOrEnumerationType() && + RTy->isIntegralOrEnumerationType()) { + if (LTy->isArithmeticType() && RTy->isArithmeticType()) + return doIntTypeConversion<Z3Expr, Z3Expr::fromCast>(LHS, LTy, RHS, RTy); + } else if (LTy->isRealFloatingType() || RTy->isRealFloatingType()) { + return doFloatTypeConversion<Z3Expr, Z3Expr::fromCast>(LHS, LTy, RHS, RTy); + } else if ((LTy->isAnyPointerType() || RTy->isAnyPointerType()) || + (LTy->isBlockPointerType() || RTy->isBlockPointerType()) || + (LTy->isReferenceType() || RTy->isReferenceType())) { + // TODO: Refactor to Sema::FindCompositePointerType(), and + // Sema::CheckCompareOperands(). + + uint64_t LBitWidth = Ctx.getTypeSize(LTy); + uint64_t RBitWidth = Ctx.getTypeSize(RTy); + + // Cast the non-pointer type to the pointer type. + // TODO: Be more strict about this. + if ((LTy->isAnyPointerType() ^ RTy->isAnyPointerType()) || + (LTy->isBlockPointerType() ^ RTy->isBlockPointerType()) || + (LTy->isReferenceType() ^ RTy->isReferenceType())) { + if (LTy->isNullPtrType() || LTy->isBlockPointerType() || + LTy->isReferenceType()) { + LHS = Z3Expr::fromCast(LHS, RTy, RBitWidth, LTy, LBitWidth); + LTy = RTy; + } else { + RHS = Z3Expr::fromCast(RHS, LTy, LBitWidth, RTy, RBitWidth); + RTy = LTy; + } + } + + // Cast the void pointer type to the non-void pointer type. + // For void types, this assumes that the casted value is equal to the value + // of the original pointer, and does not account for alignment requirements. + if (LTy->isVoidPointerType() ^ RTy->isVoidPointerType()) { + assert((Ctx.getTypeSize(LTy) == Ctx.getTypeSize(RTy)) && + "Pointer types have different bitwidths!"); + if (RTy->isVoidPointerType()) + RTy = LTy; + else + LTy = RTy; + } + + if (LTy == RTy) + return; + } + + // Fallback: for the solver, assume that these types don't really matter + if ((LTy.getCanonicalType() == RTy.getCanonicalType()) || + (LTy->isObjCObjectPointerType() && RTy->isObjCObjectPointerType())) { + LTy = RTy; + return; + } + + // TODO: Refine behavior for invalid type casts +} + +template <typename T, + T(doCast)(const T &, QualType, uint64_t, QualType, uint64_t)> +void Z3ConstraintManager::doIntTypeConversion(T &LHS, QualType <y, T &RHS, + QualType &RTy) const { + ASTContext &Ctx = getBasicVals().getContext(); + + uint64_t LBitWidth = Ctx.getTypeSize(LTy); + uint64_t RBitWidth = Ctx.getTypeSize(RTy); + + // Always perform integer promotion before checking type equality. + // Otherwise, e.g. (bool) a + (bool) b could trigger a backend assertion + if (LTy->isPromotableIntegerType()) { + QualType NewTy = Ctx.getPromotedIntegerType(LTy); + uint64_t NewBitWidth = Ctx.getTypeSize(NewTy); + LHS = (*doCast)(LHS, NewTy, NewBitWidth, LTy, LBitWidth); + LTy = NewTy; + LBitWidth = NewBitWidth; + } + if (RTy->isPromotableIntegerType()) { + QualType NewTy = Ctx.getPromotedIntegerType(RTy); + uint64_t NewBitWidth = Ctx.getTypeSize(NewTy); + RHS = (*doCast)(RHS, NewTy, NewBitWidth, RTy, RBitWidth); + RTy = NewTy; + RBitWidth = NewBitWidth; + } + + if (LTy == RTy) + return; + + // Perform integer type conversion + // Note: Safe to skip updating bitwidth because this must terminate + bool isLSignedTy = LTy->isSignedIntegerOrEnumerationType(); + bool isRSignedTy = RTy->isSignedIntegerOrEnumerationType(); + + int order = Ctx.getIntegerTypeOrder(LTy, RTy); + if (isLSignedTy == isRSignedTy) { + // Same signedness; use the higher-ranked type + if (order == 1) { + RHS = (*doCast)(RHS, LTy, LBitWidth, RTy, RBitWidth); + RTy = LTy; + } else { + LHS = (*doCast)(LHS, RTy, RBitWidth, LTy, LBitWidth); + LTy = RTy; + } + } else if (order != (isLSignedTy ? 1 : -1)) { + // The unsigned type has greater than or equal rank to the + // signed type, so use the unsigned type + if (isRSignedTy) { + RHS = (*doCast)(RHS, LTy, LBitWidth, RTy, RBitWidth); + RTy = LTy; + } else { + LHS = (*doCast)(LHS, RTy, RBitWidth, LTy, LBitWidth); + LTy = RTy; + } + } else if (LBitWidth != RBitWidth) { + // The two types are different widths; if we are here, that + // means the signed type is larger than the unsigned type, so + // use the signed type. + if (isLSignedTy) { + RHS = (*doCast)(RHS, LTy, LBitWidth, RTy, RBitWidth); + RTy = LTy; + } else { + LHS = (*doCast)(LHS, RTy, RBitWidth, LTy, LBitWidth); + LTy = RTy; + } + } else { + // The signed type is higher-ranked than the unsigned type, + // but isn't actually any bigger (like unsigned int and long + // on most 32-bit systems). Use the unsigned type corresponding + // to the signed type. + QualType NewTy = Ctx.getCorrespondingUnsignedType(isLSignedTy ? LTy : RTy); + RHS = (*doCast)(RHS, LTy, LBitWidth, RTy, RBitWidth); + RTy = NewTy; + LHS = (*doCast)(LHS, RTy, RBitWidth, LTy, LBitWidth); + LTy = NewTy; + } +} + +template <typename T, + T(doCast)(const T &, QualType, uint64_t, QualType, uint64_t)> +void Z3ConstraintManager::doFloatTypeConversion(T &LHS, QualType <y, T &RHS, + QualType &RTy) const { + ASTContext &Ctx = getBasicVals().getContext(); + + uint64_t LBitWidth = Ctx.getTypeSize(LTy); + uint64_t RBitWidth = Ctx.getTypeSize(RTy); + + // Perform float-point type promotion + if (!LTy->isRealFloatingType()) { + LHS = (*doCast)(LHS, RTy, RBitWidth, LTy, LBitWidth); + LTy = RTy; + LBitWidth = RBitWidth; + } + if (!RTy->isRealFloatingType()) { + RHS = (*doCast)(RHS, LTy, LBitWidth, RTy, RBitWidth); + RTy = LTy; + RBitWidth = LBitWidth; + } + + if (LTy == RTy) + return; + + // If we have two real floating types, convert the smaller operand to the + // bigger result + // Note: Safe to skip updating bitwidth because this must terminate + int order = Ctx.getFloatingTypeOrder(LTy, RTy); + if (order > 0) { + RHS = Z3Expr::fromCast(RHS, LTy, LBitWidth, RTy, RBitWidth); + RTy = LTy; + } else if (order == 0) { + LHS = Z3Expr::fromCast(LHS, RTy, RBitWidth, LTy, LBitWidth); + LTy = RTy; + } else { + llvm_unreachable("Unsupported floating-point type cast!"); + } +} + +llvm::APSInt Z3ConstraintManager::castAPSInt(const llvm::APSInt &V, + QualType ToTy, uint64_t ToWidth, + QualType FromTy, + uint64_t FromWidth) { + APSIntType TargetType(ToWidth, !ToTy->isSignedIntegerOrEnumerationType()); + return TargetType.convert(V); +} + +//==------------------------------------------------------------------------==/ +// Pretty-printing. +//==------------------------------------------------------------------------==/ + +void Z3ConstraintManager::print(ProgramStateRef St, raw_ostream &OS, + const char *nl, const char *sep) { + + ConstraintZ3Ty CZ = St->get<ConstraintZ3>(); + + OS << nl << sep << "Constraints:"; + for (ConstraintZ3Ty::iterator I = CZ.begin(), E = CZ.end(); I != E; ++I) { + OS << nl << ' ' << I->first << " : "; + I->second.print(OS); + } + OS << nl; +} + +#endif + +std::unique_ptr<ConstraintManager> +ento::CreateZ3ConstraintManager(ProgramStateManager &StMgr, SubEngine *Eng) { +#if CLANG_ANALYZER_WITH_Z3 + return llvm::make_unique<Z3ConstraintManager>(Eng, StMgr.getSValBuilder()); +#else + llvm::report_fatal_error("Clang was not compiled with Z3 support!", false); + return nullptr; +#endif +} diff --git a/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp b/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp index b3e287ebf815..0fe0f3a6ed58 100644 --- a/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp +++ b/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp @@ -615,8 +615,8 @@ std::string AnalysisConsumer::getFunctionName(const Decl *D) { << OC->getIdentifier()->getNameStart() << ')'; } } else if (const auto *OCD = dyn_cast<ObjCCategoryImplDecl>(DC)) { - OS << ((const NamedDecl *)OCD)->getIdentifier()->getNameStart() << '(' - << OCD->getIdentifier()->getNameStart() << ')'; + OS << OCD->getClassInterface()->getName() << '(' + << OCD->getName() << ')'; } else if (isa<ObjCProtocolDecl>(DC)) { // We can extract the type of the class from the self pointer. if (ImplicitParamDecl *SelfDecl = OMD->getSelfDecl()) { |