diff options
| author | Dimitry Andric <dim@FreeBSD.org> | 2022-07-27 19:50:45 +0000 |
|---|---|---|
| committer | Dimitry Andric <dim@FreeBSD.org> | 2022-07-27 19:50:54 +0000 |
| commit | 08e8dd7b9db7bb4a9de26d44c1cbfd24e869c014 (patch) | |
| tree | 041e72e32710b1e742516d8c9f1575bf0116d3e3 /clang/lib/StaticAnalyzer | |
| parent | 4b4fe385e49bd883fd183b5f21c1ea486c722e61 (diff) | |
vendor/llvm-project/llvmorg-15-init-17827-gd77882e66779vendor/llvm-project/llvmorg-15-init-17826-g1f8ae9d7e7e4
Diffstat (limited to 'clang/lib/StaticAnalyzer')
12 files changed, 373 insertions, 127 deletions
diff --git a/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp index 987cf65d6fec..9a6c013bcf66 100644 --- a/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp @@ -26,9 +26,11 @@ #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/raw_ostream.h" +#include <functional> using namespace clang; using namespace ento; +using namespace std::placeholders; namespace { struct AnyArgExpr { @@ -118,10 +120,14 @@ public: const LocationContext *LCtx, const CallEvent *Call) const; - typedef void (CStringChecker::*FnCheck)(CheckerContext &, - const CallExpr *) const; + using FnCheck = std::function<void(const CStringChecker *, CheckerContext &, + const CallExpr *)>; + CallDescriptionMap<FnCheck> Callbacks = { - {{CDF_MaybeBuiltin, "memcpy", 3}, &CStringChecker::evalMemcpy}, + {{CDF_MaybeBuiltin, "memcpy", 3}, + std::bind(&CStringChecker::evalMemcpy, _1, _2, _3, false)}, + {{CDF_MaybeBuiltin, "wmemcpy", 3}, + std::bind(&CStringChecker::evalMemcpy, _1, _2, _3, true)}, {{CDF_MaybeBuiltin, "mempcpy", 3}, &CStringChecker::evalMempcpy}, {{CDF_MaybeBuiltin, "memcmp", 3}, &CStringChecker::evalMemcmp}, {{CDF_MaybeBuiltin, "memmove", 3}, &CStringChecker::evalMemmove}, @@ -135,7 +141,9 @@ public: {{CDF_MaybeBuiltin, "strncat", 3}, &CStringChecker::evalStrncat}, {{CDF_MaybeBuiltin, "strlcat", 3}, &CStringChecker::evalStrlcat}, {{CDF_MaybeBuiltin, "strlen", 1}, &CStringChecker::evalstrLength}, + {{CDF_MaybeBuiltin, "wcslen", 1}, &CStringChecker::evalstrLength}, {{CDF_MaybeBuiltin, "strnlen", 2}, &CStringChecker::evalstrnLength}, + {{CDF_MaybeBuiltin, "wcsnlen", 2}, &CStringChecker::evalstrnLength}, {{CDF_MaybeBuiltin, "strcmp", 2}, &CStringChecker::evalStrcmp}, {{CDF_MaybeBuiltin, "strncmp", 3}, &CStringChecker::evalStrncmp}, {{CDF_MaybeBuiltin, "strcasecmp", 2}, &CStringChecker::evalStrcasecmp}, @@ -152,14 +160,14 @@ public: StdCopyBackward{{"std", "copy_backward"}, 3}; FnCheck identifyCall(const CallEvent &Call, CheckerContext &C) const; - void evalMemcpy(CheckerContext &C, const CallExpr *CE) const; + void evalMemcpy(CheckerContext &C, const CallExpr *CE, bool IsWide) const; void evalMempcpy(CheckerContext &C, const CallExpr *CE) const; void evalMemmove(CheckerContext &C, const CallExpr *CE) const; void evalBcopy(CheckerContext &C, const CallExpr *CE) const; void evalCopyCommon(CheckerContext &C, const CallExpr *CE, ProgramStateRef state, SizeArgExpr Size, DestinationArgExpr Dest, SourceArgExpr Source, - bool Restricted, bool IsMempcpy) const; + bool Restricted, bool IsMempcpy, bool IsWide) const; void evalMemcmp(CheckerContext &C, const CallExpr *CE) const; @@ -240,13 +248,14 @@ public: AnyArgExpr Arg, SVal l) const; ProgramStateRef CheckLocation(CheckerContext &C, ProgramStateRef state, AnyArgExpr Buffer, SVal Element, - AccessKind Access) const; + AccessKind Access, bool IsWide = false) const; ProgramStateRef CheckBufferAccess(CheckerContext &C, ProgramStateRef State, AnyArgExpr Buffer, SizeArgExpr Size, - AccessKind Access) const; + AccessKind Access, + bool IsWide = false) const; ProgramStateRef CheckOverlap(CheckerContext &C, ProgramStateRef state, SizeArgExpr Size, AnyArgExpr First, - AnyArgExpr Second) const; + AnyArgExpr Second, bool IsWide = false) const; void emitOverlapBug(CheckerContext &C, ProgramStateRef state, const Stmt *First, @@ -329,7 +338,8 @@ ProgramStateRef CStringChecker::checkNonNull(CheckerContext &C, ProgramStateRef CStringChecker::CheckLocation(CheckerContext &C, ProgramStateRef state, AnyArgExpr Buffer, SVal Element, - AccessKind Access) const { + AccessKind Access, + bool IsWide) const { // If a previous check has failed, propagate the failure. if (!state) @@ -344,17 +354,36 @@ ProgramStateRef CStringChecker::CheckLocation(CheckerContext &C, if (!ER) return state; - if (ER->getValueType() != C.getASTContext().CharTy) - return state; + SValBuilder &svalBuilder = C.getSValBuilder(); + ASTContext &Ctx = svalBuilder.getContext(); + + // Get the index of the accessed element. + NonLoc Idx = ER->getIndex(); + + if (!IsWide) { + if (ER->getValueType() != Ctx.CharTy) + return state; + } else { + if (ER->getValueType() != Ctx.WideCharTy) + return state; + + QualType SizeTy = Ctx.getSizeType(); + NonLoc WideSize = + svalBuilder + .makeIntVal(Ctx.getTypeSizeInChars(Ctx.WideCharTy).getQuantity(), + SizeTy) + .castAs<NonLoc>(); + SVal Offset = svalBuilder.evalBinOpNN(state, BO_Mul, Idx, WideSize, SizeTy); + if (Offset.isUnknown()) + return state; + Idx = Offset.castAs<NonLoc>(); + } // Get the size of the array. const auto *superReg = cast<SubRegion>(ER->getSuperRegion()); DefinedOrUnknownSVal Size = getDynamicExtent(state, superReg, C.getSValBuilder()); - // Get the index of the accessed element. - DefinedOrUnknownSVal Idx = ER->getIndex().castAs<DefinedOrUnknownSVal>(); - ProgramStateRef StInBound, StOutBound; std::tie(StInBound, StOutBound) = state->assumeInBoundDual(Idx, Size); if (StOutBound && !StInBound) { @@ -385,11 +414,10 @@ ProgramStateRef CStringChecker::CheckLocation(CheckerContext &C, return StInBound; } -ProgramStateRef CStringChecker::CheckBufferAccess(CheckerContext &C, - ProgramStateRef State, - AnyArgExpr Buffer, - SizeArgExpr Size, - AccessKind Access) const { +ProgramStateRef +CStringChecker::CheckBufferAccess(CheckerContext &C, ProgramStateRef State, + AnyArgExpr Buffer, SizeArgExpr Size, + AccessKind Access, bool IsWide) const { // If a previous check has failed, propagate the failure. if (!State) return nullptr; @@ -398,7 +426,7 @@ ProgramStateRef CStringChecker::CheckBufferAccess(CheckerContext &C, ASTContext &Ctx = svalBuilder.getContext(); QualType SizeTy = Size.Expression->getType(); - QualType PtrTy = Ctx.getPointerType(Ctx.CharTy); + QualType PtrTy = Ctx.getPointerType(IsWide ? Ctx.WideCharTy : Ctx.CharTy); // Check that the first buffer is non-null. SVal BufVal = C.getSVal(Buffer.Expression); @@ -432,7 +460,7 @@ ProgramStateRef CStringChecker::CheckBufferAccess(CheckerContext &C, SVal BufEnd = svalBuilder.evalBinOpLN(State, BO_Add, *BufLoc, LastOffset, PtrTy); - State = CheckLocation(C, State, Buffer, BufEnd, Access); + State = CheckLocation(C, State, Buffer, BufEnd, Access, IsWide); // If the buffer isn't large enough, abort. if (!State) @@ -446,7 +474,8 @@ ProgramStateRef CStringChecker::CheckBufferAccess(CheckerContext &C, ProgramStateRef CStringChecker::CheckOverlap(CheckerContext &C, ProgramStateRef state, SizeArgExpr Size, AnyArgExpr First, - AnyArgExpr Second) const { + AnyArgExpr Second, + bool IsWide) const { if (!Filter.CheckCStringBufferOverlap) return state; @@ -525,7 +554,7 @@ ProgramStateRef CStringChecker::CheckOverlap(CheckerContext &C, // Convert the first buffer's start address to char*. // Bail out if the cast fails. ASTContext &Ctx = svalBuilder.getContext(); - QualType CharPtrTy = Ctx.getPointerType(Ctx.CharTy); + QualType CharPtrTy = Ctx.getPointerType(IsWide ? Ctx.WideCharTy : Ctx.CharTy); SVal FirstStart = svalBuilder.evalCast(*firstLoc, CharPtrTy, First.Expression->getType()); Optional<Loc> FirstStartLoc = FirstStart.getAs<Loc>(); @@ -1161,7 +1190,7 @@ void CStringChecker::evalCopyCommon(CheckerContext &C, const CallExpr *CE, ProgramStateRef state, SizeArgExpr Size, DestinationArgExpr Dest, SourceArgExpr Source, bool Restricted, - bool IsMempcpy) const { + bool IsMempcpy, bool IsWide) const { CurrentFunctionDescription = "memory copy function"; // See if the size argument is zero. @@ -1204,11 +1233,11 @@ void CStringChecker::evalCopyCommon(CheckerContext &C, const CallExpr *CE, return; // Ensure the accesses are valid and that the buffers do not overlap. - state = CheckBufferAccess(C, state, Dest, Size, AccessKind::write); - state = CheckBufferAccess(C, state, Source, Size, AccessKind::read); + state = CheckBufferAccess(C, state, Dest, Size, AccessKind::write, IsWide); + state = CheckBufferAccess(C, state, Source, Size, AccessKind::read, IsWide); if (Restricted) - state = CheckOverlap(C, state, Size, Dest, Source); + state = CheckOverlap(C, state, Size, Dest, Source, IsWide); if (!state) return; @@ -1258,7 +1287,8 @@ void CStringChecker::evalCopyCommon(CheckerContext &C, const CallExpr *CE, } } -void CStringChecker::evalMemcpy(CheckerContext &C, const CallExpr *CE) const { +void CStringChecker::evalMemcpy(CheckerContext &C, const CallExpr *CE, + bool IsWide) const { // void *memcpy(void *restrict dst, const void *restrict src, size_t n); // The return value is the address of the destination buffer. DestinationArgExpr Dest = {CE->getArg(0), 0}; @@ -1269,7 +1299,8 @@ void CStringChecker::evalMemcpy(CheckerContext &C, const CallExpr *CE) const { constexpr bool IsRestricted = true; constexpr bool IsMempcpy = false; - evalCopyCommon(C, CE, State, Size, Dest, Src, IsRestricted, IsMempcpy); + evalCopyCommon(C, CE, State, Size, Dest, Src, IsRestricted, IsMempcpy, + IsWide); } void CStringChecker::evalMempcpy(CheckerContext &C, const CallExpr *CE) const { @@ -1281,7 +1312,8 @@ void CStringChecker::evalMempcpy(CheckerContext &C, const CallExpr *CE) const { constexpr bool IsRestricted = true; constexpr bool IsMempcpy = true; - evalCopyCommon(C, CE, C.getState(), Size, Dest, Src, IsRestricted, IsMempcpy); + evalCopyCommon(C, CE, C.getState(), Size, Dest, Src, IsRestricted, IsMempcpy, + false); } void CStringChecker::evalMemmove(CheckerContext &C, const CallExpr *CE) const { @@ -1293,7 +1325,8 @@ void CStringChecker::evalMemmove(CheckerContext &C, const CallExpr *CE) const { constexpr bool IsRestricted = false; constexpr bool IsMempcpy = false; - evalCopyCommon(C, CE, C.getState(), Size, Dest, Src, IsRestricted, IsMempcpy); + evalCopyCommon(C, CE, C.getState(), Size, Dest, Src, IsRestricted, IsMempcpy, + false); } void CStringChecker::evalBcopy(CheckerContext &C, const CallExpr *CE) const { @@ -1304,7 +1337,8 @@ void CStringChecker::evalBcopy(CheckerContext &C, const CallExpr *CE) const { constexpr bool IsRestricted = false; constexpr bool IsMempcpy = false; - evalCopyCommon(C, CE, C.getState(), Size, Dest, Src, IsRestricted, IsMempcpy); + evalCopyCommon(C, CE, C.getState(), Size, Dest, Src, IsRestricted, IsMempcpy, + false); } void CStringChecker::evalMemcmp(CheckerContext &C, const CallExpr *CE) const { @@ -2336,7 +2370,7 @@ bool CStringChecker::evalCall(const CallEvent &Call, CheckerContext &C) const { // Check and evaluate the call. const auto *CE = cast<CallExpr>(Call.getOriginExpr()); - (this->*Callback)(C, CE); + Callback(this, C, CE); // If the evaluate call resulted in no change, chain to the next eval call // handler. diff --git a/clang/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp index 895212d134b8..b673b51c4623 100644 --- a/clang/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp @@ -55,9 +55,9 @@ public: ID.AddPointer(getTag()); } - virtual PathDiagnosticPieceRef - VisitNode(const ExplodedNode *N, BugReporterContext &BRC, - PathSensitiveBugReport &BR) override; + PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + PathSensitiveBugReport &BR) override; // FIXME: Scan the map once in the visitor's constructor and do a direct // lookup by region. diff --git a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp index 36464707d06a..adedc9c30fad 100644 --- a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -852,9 +852,8 @@ protected: return false; } - virtual bool - wasModifiedInFunction(const ExplodedNode *CallEnterN, - const ExplodedNode *CallExitEndN) override { + bool wasModifiedInFunction(const ExplodedNode *CallEnterN, + const ExplodedNode *CallExitEndN) override { if (!doesFnIntendToHandleOwnership( CallExitEndN->getFirstPred()->getLocationContext()->getDecl(), CallExitEndN->getState()->getAnalysisManager().getASTContext())) @@ -885,7 +884,7 @@ protected: "later deallocation"); } - virtual PathDiagnosticPieceRef + PathDiagnosticPieceRef maybeEmitNoteForObjCSelf(PathSensitiveBugReport &R, const ObjCMethodCall &Call, const ExplodedNode *N) override { @@ -893,7 +892,7 @@ protected: return nullptr; } - virtual PathDiagnosticPieceRef + PathDiagnosticPieceRef maybeEmitNoteForCXXThis(PathSensitiveBugReport &R, const CXXConstructorCall &Call, const ExplodedNode *N) override { @@ -901,7 +900,7 @@ protected: return nullptr; } - virtual PathDiagnosticPieceRef + PathDiagnosticPieceRef maybeEmitNoteForParameters(PathSensitiveBugReport &R, const CallEvent &Call, const ExplodedNode *N) override { // TODO: Factor the logic of "what constitutes as an entity being passed diff --git a/clang/lib/StaticAnalyzer/Checkers/PaddingChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/PaddingChecker.cpp index cddf206728b1..27fd40a441fa 100644 --- a/clang/lib/StaticAnalyzer/Checkers/PaddingChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/PaddingChecker.cpp @@ -182,7 +182,7 @@ public: return false; }; - if (std::any_of(RD->field_begin(), RD->field_end(), IsTrickyField)) + if (llvm::any_of(RD->fields(), IsTrickyField)) return true; return false; } diff --git a/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp index ef673ae41a3d..5897e5096461 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp @@ -240,7 +240,7 @@ class StdLibraryFunctionsChecker ArgNo OtherArgN; public: - virtual StringRef getName() const override { return "Comparison"; }; + StringRef getName() const override { return "Comparison"; }; ComparisonConstraint(ArgNo ArgN, BinaryOperator::Opcode Opcode, ArgNo OtherArgN) : ValueConstraint(ArgN), Opcode(Opcode), OtherArgN(OtherArgN) {} diff --git a/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp index 38e69e81d800..cd91fa9b090c 100644 --- a/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp @@ -57,19 +57,17 @@ class RegularField final : public FieldNode { public: RegularField(const FieldRegion *FR) : FieldNode(FR) {} - virtual void printNoteMsg(llvm::raw_ostream &Out) const override { + void printNoteMsg(llvm::raw_ostream &Out) const override { Out << "uninitialized field "; } - virtual void printPrefix(llvm::raw_ostream &Out) const override {} + void printPrefix(llvm::raw_ostream &Out) const override {} - virtual void printNode(llvm::raw_ostream &Out) const override { + void printNode(llvm::raw_ostream &Out) const override { Out << getVariableName(getDecl()); } - virtual void printSeparator(llvm::raw_ostream &Out) const override { - Out << '.'; - } + void printSeparator(llvm::raw_ostream &Out) const override { Out << '.'; } }; /// Represents that the FieldNode that comes after this is declared in a base @@ -85,20 +83,20 @@ public: assert(T->getAsCXXRecordDecl()); } - virtual void printNoteMsg(llvm::raw_ostream &Out) const override { + void printNoteMsg(llvm::raw_ostream &Out) const override { llvm_unreachable("This node can never be the final node in the " "fieldchain!"); } - virtual void printPrefix(llvm::raw_ostream &Out) const override {} + void printPrefix(llvm::raw_ostream &Out) const override {} - virtual void printNode(llvm::raw_ostream &Out) const override { + void printNode(llvm::raw_ostream &Out) const override { Out << BaseClassT->getAsCXXRecordDecl()->getName() << "::"; } - virtual void printSeparator(llvm::raw_ostream &Out) const override {} + void printSeparator(llvm::raw_ostream &Out) const override {} - virtual bool isBase() const override { return true; } + bool isBase() const override { return true; } }; } // end of anonymous namespace diff --git a/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp b/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp index a6e81b3657a2..f5bd765ff679 100644 --- a/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp @@ -34,20 +34,20 @@ public: LocField(const FieldRegion *FR, const bool IsDereferenced = true) : FieldNode(FR), IsDereferenced(IsDereferenced) {} - virtual void printNoteMsg(llvm::raw_ostream &Out) const override { + void printNoteMsg(llvm::raw_ostream &Out) const override { if (IsDereferenced) Out << "uninitialized pointee "; else Out << "uninitialized pointer "; } - virtual void printPrefix(llvm::raw_ostream &Out) const override {} + void printPrefix(llvm::raw_ostream &Out) const override {} - virtual void printNode(llvm::raw_ostream &Out) const override { + void printNode(llvm::raw_ostream &Out) const override { Out << getVariableName(getDecl()); } - virtual void printSeparator(llvm::raw_ostream &Out) const override { + void printSeparator(llvm::raw_ostream &Out) const override { if (getDecl()->getType()->isPointerType()) Out << "->"; else @@ -64,11 +64,11 @@ public: NeedsCastLocField(const FieldRegion *FR, const QualType &T) : FieldNode(FR), CastBackType(T) {} - virtual void printNoteMsg(llvm::raw_ostream &Out) const override { + void printNoteMsg(llvm::raw_ostream &Out) const override { Out << "uninitialized pointee "; } - virtual void printPrefix(llvm::raw_ostream &Out) const override { + void printPrefix(llvm::raw_ostream &Out) const override { // If this object is a nonloc::LocAsInteger. if (getDecl()->getType()->isIntegerType()) Out << "reinterpret_cast"; @@ -78,13 +78,11 @@ public: Out << '<' << CastBackType.getAsString() << ">("; } - virtual void printNode(llvm::raw_ostream &Out) const override { + void printNode(llvm::raw_ostream &Out) const override { Out << getVariableName(getDecl()) << ')'; } - virtual void printSeparator(llvm::raw_ostream &Out) const override { - Out << "->"; - } + void printSeparator(llvm::raw_ostream &Out) const override { Out << "->"; } }; /// Represents a Loc field that points to itself. @@ -93,17 +91,17 @@ class CyclicLocField final : public FieldNode { public: CyclicLocField(const FieldRegion *FR) : FieldNode(FR) {} - virtual void printNoteMsg(llvm::raw_ostream &Out) const override { + void printNoteMsg(llvm::raw_ostream &Out) const override { Out << "object references itself "; } - virtual void printPrefix(llvm::raw_ostream &Out) const override {} + void printPrefix(llvm::raw_ostream &Out) const override {} - virtual void printNode(llvm::raw_ostream &Out) const override { + void printNode(llvm::raw_ostream &Out) const override { Out << getVariableName(getDecl()); } - virtual void printSeparator(llvm::raw_ostream &Out) const override { + void printSeparator(llvm::raw_ostream &Out) const override { llvm_unreachable("CyclicLocField objects must be the last node of the " "fieldchain!"); } diff --git a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp index 2caa5bbc16df..3a90c37a36da 100644 --- a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp +++ b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp @@ -530,9 +530,8 @@ public: private: /// \return Whether \c RegionOfInterest was modified at \p CurrN compared to /// the value it holds in \p CallExitBeginN. - virtual bool - wasModifiedBeforeCallExit(const ExplodedNode *CurrN, - const ExplodedNode *CallExitBeginN) override; + bool wasModifiedBeforeCallExit(const ExplodedNode *CurrN, + const ExplodedNode *CallExitBeginN) override; /// Attempts to find the region of interest in a given record decl, /// by either following the base classes or fields. @@ -547,19 +546,17 @@ private: // Region of interest corresponds to an IVar, exiting a method // which could have written into that IVar, but did not. - virtual PathDiagnosticPieceRef - maybeEmitNoteForObjCSelf(PathSensitiveBugReport &R, - const ObjCMethodCall &Call, - const ExplodedNode *N) override final; + PathDiagnosticPieceRef maybeEmitNoteForObjCSelf(PathSensitiveBugReport &R, + const ObjCMethodCall &Call, + const ExplodedNode *N) final; - virtual PathDiagnosticPieceRef - maybeEmitNoteForCXXThis(PathSensitiveBugReport &R, - const CXXConstructorCall &Call, - const ExplodedNode *N) override final; + PathDiagnosticPieceRef maybeEmitNoteForCXXThis(PathSensitiveBugReport &R, + const CXXConstructorCall &Call, + const ExplodedNode *N) final; - virtual PathDiagnosticPieceRef + PathDiagnosticPieceRef maybeEmitNoteForParameters(PathSensitiveBugReport &R, const CallEvent &Call, - const ExplodedNode *N) override final; + const ExplodedNode *N) final; /// Consume the information on the no-store stack frame in order to /// either emit a note or suppress the report enirely. diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index d8f56f2f8cff..19149d079822 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -196,6 +196,14 @@ typedef llvm::ImmutableMap< IndexOfElementToConstructMap; REGISTER_TRAIT_WITH_PROGRAMSTATE(IndexOfElementToConstruct, IndexOfElementToConstructMap) + +// This trait is responsible for holding our pending ArrayInitLoopExprs. +// It pairs the LocationContext and the initializer CXXConstructExpr with +// the size of the array that's being copy initialized. +typedef llvm::ImmutableMap< + std::pair<const CXXConstructExpr *, const LocationContext *>, unsigned> + PendingInitLoopMap; +REGISTER_TRAIT_WITH_PROGRAMSTATE(PendingInitLoop, PendingInitLoopMap) //===----------------------------------------------------------------------===// // Engine construction and deletion. //===----------------------------------------------------------------------===// @@ -462,6 +470,34 @@ ProgramStateRef ExprEngine::setIndexOfElementToConstruct( return State->set<IndexOfElementToConstruct>(Key, Idx); } +Optional<unsigned> ExprEngine::getPendingInitLoop(ProgramStateRef State, + const CXXConstructExpr *E, + const LocationContext *LCtx) { + + return Optional<unsigned>::create( + State->get<PendingInitLoop>({E, LCtx->getStackFrame()})); +} + +ProgramStateRef ExprEngine::removePendingInitLoop(ProgramStateRef State, + const CXXConstructExpr *E, + const LocationContext *LCtx) { + auto Key = std::make_pair(E, LCtx->getStackFrame()); + + assert(E && State->contains<PendingInitLoop>(Key)); + return State->remove<PendingInitLoop>(Key); +} + +ProgramStateRef ExprEngine::setPendingInitLoop(ProgramStateRef State, + const CXXConstructExpr *E, + const LocationContext *LCtx, + unsigned Size) { + auto Key = std::make_pair(E, LCtx->getStackFrame()); + + assert(!State->contains<PendingInitLoop>(Key) && Size > 0); + + return State->set<PendingInitLoop>(Key, Size); +} + Optional<unsigned> ExprEngine::getIndexOfElementToConstruct(ProgramStateRef State, const CXXConstructExpr *E, @@ -487,17 +523,23 @@ ExprEngine::addObjectUnderConstruction(ProgramStateRef State, const LocationContext *LC, SVal V) { ConstructedObjectKey Key(Item, LC->getStackFrame()); - const CXXConstructExpr *E = nullptr; + const Expr *Init = nullptr; if (auto DS = dyn_cast_or_null<DeclStmt>(Item.getStmtOrNull())) { if (auto VD = dyn_cast_or_null<VarDecl>(DS->getSingleDecl())) - E = dyn_cast<CXXConstructExpr>(VD->getInit()); + Init = VD->getInit(); } - if (!E && !Item.getStmtOrNull()) { - auto CtorInit = Item.getCXXCtorInitializer(); - E = dyn_cast<CXXConstructExpr>(CtorInit->getInit()); - } + if (auto LE = dyn_cast_or_null<LambdaExpr>(Item.getStmtOrNull())) + Init = *(LE->capture_init_begin() + Item.getIndex()); + + if (!Init && !Item.getStmtOrNull()) + Init = Item.getCXXCtorInitializer()->getInit(); + + // In an ArrayInitLoopExpr the real initializer is returned by + // getSubExpr(). + if (const auto *AILE = dyn_cast_or_null<ArrayInitLoopExpr>(Init)) + Init = AILE->getSubExpr(); // FIXME: Currently the state might already contain the marker due to // incorrect handling of temporaries bound to default parameters. @@ -508,7 +550,8 @@ ExprEngine::addObjectUnderConstruction(ProgramStateRef State, assert((!State->get<ObjectsUnderConstruction>(Key) || Key.getItem().getKind() == ConstructionContextItem::TemporaryDestructorKind || - State->contains<IndexOfElementToConstruct>({E, LC})) && + State->contains<IndexOfElementToConstruct>( + {dyn_cast_or_null<CXXConstructExpr>(Init), LC})) && "The object is already marked as `UnderConstruction`, when it's not " "supposed to!"); return State->set<ObjectsUnderConstruction>(Key, V); @@ -2744,7 +2787,10 @@ void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D, SVal Base = state->getLValue(DD, LCtx); if (DD->getType()->isReferenceType()) { - Base = state->getSVal(Base.getAsRegion()); + if (const MemRegion *R = Base.getAsRegion()) + Base = state->getSVal(R); + else + Base = UnknownVal(); } SVal V = UnknownVal(); @@ -2765,15 +2811,27 @@ void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D, V = state->getLValue(BD->getType(), Idx, Base); } - // Handle binding to tuple-like strcutures - else if (BD->getHoldingVar()) { - // FIXME: handle tuples - return; + // Handle binding to tuple-like structures + else if (const auto *HV = BD->getHoldingVar()) { + V = state->getLValue(HV, LCtx); + + if (HV->getType()->isReferenceType()) { + if (const MemRegion *R = V.getAsRegion()) + V = state->getSVal(R); + else + V = UnknownVal(); + } } else llvm_unreachable("An unknown case of structured binding encountered!"); - if (BD->getType()->isReferenceType()) - V = state->getSVal(V.getAsRegion()); + // In case of tuple-like types the references are already handled, so we + // don't want to handle them again. + if (BD->getType()->isReferenceType() && !BD->getHoldingVar()) { + if (const MemRegion *R = V.getAsRegion()) + V = state->getSVal(R); + else + V = UnknownVal(); + } Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V), nullptr, ProgramPoint::PostLValueKind); @@ -2797,6 +2855,11 @@ void ExprEngine::VisitArrayInitLoopExpr(const ArrayInitLoopExpr *Ex, const Expr *Arr = Ex->getCommonExpr()->getSourceExpr(); for (auto *Node : CheckerPreStmt) { + + // The constructor visitior has already taken care of everything. + if (auto *CE = dyn_cast<CXXConstructExpr>(Ex->getSubExpr())) + break; + const LocationContext *LCtx = Node->getLocationContext(); ProgramStateRef state = Node->getState(); diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp index 08fac9fb2e69..04e00274b2a7 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -290,6 +290,23 @@ SVal ExprEngine::computeObjectUnderConstruction( return loc::MemRegionVal(MRMgr.getCXXTempObjectRegion(E, LCtx)); } + case ConstructionContext::LambdaCaptureKind: { + CallOpts.IsTemporaryCtorOrDtor = true; + + const auto *LCC = cast<LambdaCaptureConstructionContext>(CC); + + SVal Base = loc::MemRegionVal( + MRMgr.getCXXTempObjectRegion(LCC->getInitializer(), LCtx)); + + const auto *CE = dyn_cast_or_null<CXXConstructExpr>(E); + if (getIndexOfElementToConstruct(State, CE, LCtx)) { + CallOpts.IsArrayCtorOrDtor = true; + Base = State->getLValue(E->getType(), svalBuilder.makeArrayIndex(Idx), + Base); + } + + return Base; + } case ConstructionContext::ArgumentKind: { // Arguments are technically temporaries. CallOpts.IsTemporaryCtorOrDtor = true; @@ -450,6 +467,17 @@ ProgramStateRef ExprEngine::updateObjectsUnderConstruction( return State; } + case ConstructionContext::LambdaCaptureKind: { + const auto *LCC = cast<LambdaCaptureConstructionContext>(CC); + + // If we capture and array, we want to store the super region, not a + // sub-region. + if (const auto *EL = dyn_cast_or_null<ElementRegion>(V.getAsRegion())) + V = loc::MemRegionVal(EL->getSuperRegion()); + + return addObjectUnderConstruction( + State, {LCC->getLambdaExpr(), LCC->getIndex()}, LCtx, V); + } case ConstructionContext::ArgumentKind: { const auto *ACC = cast<ArgumentConstructionContext>(CC); if (const auto *BTE = ACC->getCXXBindTemporaryExpr()) @@ -462,6 +490,59 @@ ProgramStateRef ExprEngine::updateObjectsUnderConstruction( llvm_unreachable("Unhandled construction context!"); } +static ProgramStateRef +bindRequiredArrayElementToEnvironment(ProgramStateRef State, + const ArrayInitLoopExpr *AILE, + const LocationContext *LCtx, SVal Idx) { + // The ctor in this case is guaranteed to be a copy ctor, otherwise we hit a + // compile time error. + // + // -ArrayInitLoopExpr <-- we're here + // |-OpaqueValueExpr + // | `-DeclRefExpr <-- match this + // `-CXXConstructExpr + // `-ImplicitCastExpr + // `-ArraySubscriptExpr + // |-ImplicitCastExpr + // | `-OpaqueValueExpr + // | `-DeclRefExpr + // `-ArrayInitIndexExpr + // + // The resulting expression might look like the one below in an implicit + // copy/move ctor. + // + // ArrayInitLoopExpr <-- we're here + // |-OpaqueValueExpr + // | `-MemberExpr <-- match this + // | (`-CXXStaticCastExpr) <-- move ctor only + // | `-DeclRefExpr + // `-CXXConstructExpr + // `-ArraySubscriptExpr + // |-ImplicitCastExpr + // | `-OpaqueValueExpr + // | `-MemberExpr + // | `-DeclRefExpr + // `-ArrayInitIndexExpr + // + // HACK: There is no way we can put the index of the array element into the + // CFG unless we unroll the loop, so we manually select and bind the required + // parameter to the environment. + const auto *CE = cast<CXXConstructExpr>(AILE->getSubExpr()); + const auto *OVESrc = AILE->getCommonExpr()->getSourceExpr(); + + SVal Base = UnknownVal(); + if (const auto *ME = dyn_cast<MemberExpr>(OVESrc)) + Base = State->getSVal(ME, LCtx); + else if (const auto *DRE = cast<DeclRefExpr>(OVESrc)) + Base = State->getLValue(cast<VarDecl>(DRE->getDecl()), LCtx); + else + llvm_unreachable("ArrayInitLoopExpr contains unexpected source expression"); + + SVal NthElem = State->getLValue(CE->getType(), Idx, Base); + + return State->BindExpr(CE->getArg(0), LCtx, NthElem); +} + void ExprEngine::handleConstructor(const Expr *E, ExplodedNode *Pred, ExplodedNodeSet &destNodes) { @@ -502,12 +583,26 @@ void ExprEngine::handleConstructor(const Expr *E, // Inherited constructors are always base class constructors. assert(CE && !CIE && "A complete constructor is inherited?!"); + // If the ctor is part of an ArrayInitLoopExpr, we want to handle it + // differently. + auto *AILE = CC ? CC->getArrayInitLoop() : nullptr; + unsigned Idx = 0; - if (CE->getType()->isArrayType()) { + if (CE->getType()->isArrayType() || AILE) { Idx = getIndexOfElementToConstruct(State, CE, LCtx).value_or(0u); State = setIndexOfElementToConstruct(State, CE, LCtx, Idx + 1); } + if (AILE) { + // Only set this once even though we loop through it multiple times. + if (!getPendingInitLoop(State, CE, LCtx)) + State = setPendingInitLoop(State, CE, LCtx, + AILE->getArraySize().getLimitedValue()); + + State = bindRequiredArrayElementToEnvironment( + State, AILE, LCtx, svalBuilder.makeArrayIndex(Idx)); + } + // The target region is found from construction context. std::tie(State, Target) = handleConstructionContext(CE, State, LCtx, CC, CallOpts, Idx); @@ -908,7 +1003,7 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred, // values are properly placed inside the required region, however if an // initializer list is used, this doesn't happen automatically. auto *Init = CNE->getInitializer(); - bool isInitList = dyn_cast_or_null<InitListExpr>(Init); + bool isInitList = isa_and_nonnull<InitListExpr>(Init); QualType ObjTy = isInitList ? Init->getType() : CNE->getType()->getPointeeType(); @@ -1038,19 +1133,40 @@ void ExprEngine::VisitLambdaExpr(const LambdaExpr *LE, ExplodedNode *Pred, // If we created a new MemRegion for the lambda, we should explicitly bind // the captures. + unsigned Idx = 0; CXXRecordDecl::field_iterator CurField = LE->getLambdaClass()->field_begin(); for (LambdaExpr::const_capture_init_iterator i = LE->capture_init_begin(), e = LE->capture_init_end(); - i != e; ++i, ++CurField) { + i != e; ++i, ++CurField, ++Idx) { FieldDecl *FieldForCapture = *CurField; SVal FieldLoc = State->getLValue(FieldForCapture, V); SVal InitVal; if (!FieldForCapture->hasCapturedVLAType()) { Expr *InitExpr = *i; + + if (const auto AILE = dyn_cast<ArrayInitLoopExpr>(InitExpr)) { + // If the AILE initializes a POD array, we need to keep it as the + // InitExpr. + if (dyn_cast<CXXConstructExpr>(AILE->getSubExpr())) + InitExpr = AILE->getSubExpr(); + } + assert(InitExpr && "Capture missing initialization expression"); - InitVal = State->getSVal(InitExpr, LocCtxt); + + if (dyn_cast<CXXConstructExpr>(InitExpr)) { + InitVal = *getObjectUnderConstruction(State, {LE, Idx}, LocCtxt); + InitVal = State->getSVal(InitVal.getAsRegion()); + + State = finishObjectConstruction(State, {LE, Idx}, LocCtxt); + } else + InitVal = State->getSVal(InitExpr, LocCtxt); + } else { + + assert(!getObjectUnderConstruction(State, {LE, Idx}, LocCtxt) && + "VLA capture by value is a compile time error!"); + // The field stores the length of a captured variable-length array. // These captures don't have initialization expressions; instead we // get the length from the VLAType size expression. diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp index ebcca92a3e4e..8fb2ce9cd18f 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -265,9 +265,13 @@ void ExprEngine::processCallExit(ExplodedNode *CEBNode) { ShouldRepeatCall = shouldRepeatCtorCall(state, CCE, callerCtx); - if (!ShouldRepeatCall && - getIndexOfElementToConstruct(state, CCE, callerCtx)) - state = removeIndexOfElementToConstruct(state, CCE, callerCtx); + if (!ShouldRepeatCall) { + if (getIndexOfElementToConstruct(state, CCE, callerCtx)) + state = removeIndexOfElementToConstruct(state, CCE, callerCtx); + + if (getPendingInitLoop(state, CCE, callerCtx)) + state = removePendingInitLoop(state, CCE, callerCtx); + } } if (const auto *CNE = dyn_cast<CXXNewExpr>(CE)) { @@ -815,8 +819,7 @@ ExprEngine::mayInlineCallKind(const CallEvent &Call, const ExplodedNode *Pred, // We still allow construction into ElementRegion targets when they don't // represent array elements. if (CallOpts.IsArrayCtorOrDtor) { - if (!shouldInlineArrayConstruction( - dyn_cast<ArrayType>(CtorExpr->getType()))) + if (!shouldInlineArrayConstruction(Pred->getState(), CtorExpr, CurLC)) return CIP_DisallowedOnce; } @@ -1082,10 +1085,14 @@ bool ExprEngine::shouldInlineCall(const CallEvent &Call, const Decl *D, return true; } -bool ExprEngine::shouldInlineArrayConstruction(const ArrayType *Type) { - if (!Type) +bool ExprEngine::shouldInlineArrayConstruction(const ProgramStateRef State, + const CXXConstructExpr *CE, + const LocationContext *LCtx) { + if (!CE) return false; + auto Type = CE->getType(); + // FIXME: Handle other arrays types. if (const auto *CAT = dyn_cast<ConstantArrayType>(Type)) { unsigned Size = getContext().getConstantArrayElementCount(CAT); @@ -1093,6 +1100,10 @@ bool ExprEngine::shouldInlineArrayConstruction(const ArrayType *Type) { return Size <= AMgr.options.maxBlockVisitOnPath; } + // Check if we're inside an ArrayInitLoopExpr, and it's sufficiently small. + if (auto Size = getPendingInitLoop(State, CE, LCtx)) + return *Size <= AMgr.options.maxBlockVisitOnPath; + return false; } @@ -1111,6 +1122,9 @@ bool ExprEngine::shouldRepeatCtorCall(ProgramStateRef State, return Size > getIndexOfElementToConstruct(State, E, LCtx); } + if (auto Size = getPendingInitLoop(State, E, LCtx)) + return Size > getIndexOfElementToConstruct(State, E, LCtx); + return false; } diff --git a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp index 5e946483a93d..d8ece9f39a25 100644 --- a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp +++ b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp @@ -1888,6 +1888,30 @@ SVal RegionStoreManager::getSValFromStringLiteral(const StringLiteral *SL, return svalBuilder.makeIntVal(Code, ElemT); } +static Optional<SVal> getDerivedSymbolForBinding( + RegionBindingsConstRef B, const TypedValueRegion *BaseRegion, + const TypedValueRegion *SubReg, const ASTContext &Ctx, SValBuilder &SVB) { + assert(BaseRegion); + QualType BaseTy = BaseRegion->getValueType(); + QualType Ty = SubReg->getValueType(); + if (BaseTy->isScalarType() && Ty->isScalarType()) { + if (Ctx.getTypeSizeInChars(BaseTy) >= Ctx.getTypeSizeInChars(Ty)) { + if (const Optional<SVal> &ParentValue = B.getDirectBinding(BaseRegion)) { + if (SymbolRef ParentValueAsSym = ParentValue->getAsSymbol()) + return SVB.getDerivedRegionValueSymbolVal(ParentValueAsSym, SubReg); + + if (ParentValue->isUndef()) + return UndefinedVal(); + + // Other cases: give up. We are indexing into a larger object + // that has some value, but we don't know how to handle that yet. + return UnknownVal(); + } + } + } + return None; +} + SVal RegionStoreManager::getBindingForElement(RegionBindingsConstRef B, const ElementRegion* R) { // Check if the region has a binding. @@ -1932,27 +1956,10 @@ SVal RegionStoreManager::getBindingForElement(RegionBindingsConstRef B, if (!O.getRegion()) return UnknownVal(); - if (const TypedValueRegion *baseR = - dyn_cast_or_null<TypedValueRegion>(O.getRegion())) { - QualType baseT = baseR->getValueType(); - if (baseT->isScalarType()) { - QualType elemT = R->getElementType(); - if (elemT->isScalarType()) { - if (Ctx.getTypeSizeInChars(baseT) >= Ctx.getTypeSizeInChars(elemT)) { - if (const Optional<SVal> &V = B.getDirectBinding(superR)) { - if (SymbolRef parentSym = V->getAsSymbol()) - return svalBuilder.getDerivedRegionValueSymbolVal(parentSym, R); + if (const TypedValueRegion *baseR = dyn_cast<TypedValueRegion>(O.getRegion())) + if (auto V = getDerivedSymbolForBinding(B, baseR, R, Ctx, svalBuilder)) + return *V; - if (V->isUnknownOrUndef()) - return *V; - // Other cases: give up. We are indexing into a larger object - // that has some value, but we don't know how to handle that yet. - return UnknownVal(); - } - } - } - } - } return getBindingForFieldOrElementCommon(B, R, R->getElementType()); } @@ -1988,6 +1995,26 @@ SVal RegionStoreManager::getBindingForField(RegionBindingsConstRef B, } } + // Handle the case where we are accessing into a larger scalar object. + // For example, this handles: + // struct header { + // unsigned a : 1; + // unsigned b : 1; + // }; + // struct parse_t { + // unsigned bits0 : 1; + // unsigned bits2 : 2; // <-- header + // unsigned bits4 : 4; + // }; + // int parse(parse_t *p) { + // unsigned copy = p->bits2; + // header *bits = (header *)© + // return bits->b; <-- here + // } + if (const auto *Base = dyn_cast<TypedValueRegion>(R->getBaseRegion())) + if (auto V = getDerivedSymbolForBinding(B, Base, R, Ctx, svalBuilder)) + return *V; + return getBindingForFieldOrElementCommon(B, R, Ty); } |
