diff options
Diffstat (limited to 'clang/lib/StaticAnalyzer/Checkers')
72 files changed, 1794 insertions, 1067 deletions
diff --git a/clang/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp index 6154eeb3419c..45783729e142 100644 --- a/clang/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp @@ -19,6 +19,7 @@ #include "llvm/ADT/SmallString.h" #include "llvm/ADT/Statistic.h" #include "llvm/Support/raw_ostream.h" +#include <optional> using namespace clang; using namespace ento; @@ -59,7 +60,7 @@ void AnalyzerStatsChecker::checkEndAnalysis(ExplodedGraph &G, if (D != P.getLocationContext()->getDecl()) continue; - if (Optional<BlockEntrance> BE = P.getAs<BlockEntrance>()) { + if (std::optional<BlockEntrance> BE = P.getAs<BlockEntrance>()) { const CFGBlock *CB = BE->getBlock(); reachable.insert(CB); } @@ -123,7 +124,7 @@ void AnalyzerStatsChecker::checkEndAnalysis(ExplodedGraph &G, if (Exit->empty()) continue; const CFGElement &CE = Exit->front(); - if (Optional<CFGStmt> CS = CE.getAs<CFGStmt>()) { + if (std::optional<CFGStmt> CS = CE.getAs<CFGStmt>()) { SmallString<128> bufI; llvm::raw_svector_ostream outputI(bufI); outputI << "(" << NameOfRootFunction << ")" << diff --git a/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp b/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp index 5be5bcde4d6e..986b0add93df 100644 --- a/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp @@ -23,6 +23,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/raw_ostream.h" +#include <optional> using namespace clang; using namespace ento; @@ -84,7 +85,7 @@ static SVal computeExtentBegin(SValBuilder &svalBuilder, static std::pair<NonLoc, nonloc::ConcreteInt> getSimplifiedOffsets(NonLoc offset, nonloc::ConcreteInt extent, SValBuilder &svalBuilder) { - Optional<nonloc::SymbolVal> SymVal = offset.getAs<nonloc::SymbolVal>(); + std::optional<nonloc::SymbolVal> SymVal = offset.getAs<nonloc::SymbolVal>(); if (SymVal && SymVal->isExpression()) { if (const SymIntExpr *SIE = dyn_cast<SymIntExpr>(SymVal->getSymbol())) { llvm::APSInt constant = @@ -143,7 +144,7 @@ void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad, SVal extentBegin = computeExtentBegin(svalBuilder, rawOffset.getRegion()); - if (Optional<NonLoc> NV = extentBegin.getAs<NonLoc>()) { + if (std::optional<NonLoc> NV = extentBegin.getAs<NonLoc>()) { if (auto ConcreteNV = NV->getAs<nonloc::ConcreteInt>()) { std::pair<NonLoc, nonloc::ConcreteInt> simplifiedOffsets = getSimplifiedOffsets(rawOffset.getByteOffset(), *ConcreteNV, @@ -155,7 +156,7 @@ void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad, SVal lowerBound = svalBuilder.evalBinOpNN(state, BO_LT, rawOffsetVal, *NV, svalBuilder.getConditionType()); - Optional<NonLoc> lowerBoundToCheck = lowerBound.getAs<NonLoc>(); + std::optional<NonLoc> lowerBoundToCheck = lowerBound.getAs<NonLoc>(); if (!lowerBoundToCheck) return; @@ -194,7 +195,7 @@ void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad, Size.castAs<NonLoc>(), svalBuilder.getConditionType()); - Optional<NonLoc> upperboundToCheck = upperbound.getAs<NonLoc>(); + std::optional<NonLoc> upperboundToCheck = upperbound.getAs<NonLoc>(); if (!upperboundToCheck) break; diff --git a/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp b/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp index ca76e2d83381..44166aaf5b85 100644 --- a/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp @@ -33,6 +33,7 @@ #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringMap.h" #include "llvm/Support/raw_ostream.h" +#include <optional> using namespace clang; using namespace ento; @@ -369,7 +370,7 @@ enum CFNumberType { kCFNumberCGFloatType = 16 }; -static Optional<uint64_t> GetCFNumberSize(ASTContext &Ctx, uint64_t i) { +static std::optional<uint64_t> GetCFNumberSize(ASTContext &Ctx, uint64_t i) { static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 }; if (i < kCFNumberCharType) @@ -390,7 +391,7 @@ static Optional<uint64_t> GetCFNumberSize(ASTContext &Ctx, uint64_t i) { case kCFNumberCGFloatType: // FIXME: We need a way to map from names to Type*. default: - return None; + return std::nullopt; } return Ctx.getTypeSize(T); @@ -442,12 +443,13 @@ void CFNumberChecker::checkPreStmt(const CallExpr *CE, // FIXME: We really should allow ranges of valid theType values, and // bifurcate the state appropriately. - Optional<nonloc::ConcreteInt> V = dyn_cast<nonloc::ConcreteInt>(TheTypeVal); + std::optional<nonloc::ConcreteInt> V = + dyn_cast<nonloc::ConcreteInt>(TheTypeVal); if (!V) return; uint64_t NumberKind = V->getValue().getLimitedValue(); - Optional<uint64_t> OptCFNumberSize = GetCFNumberSize(Ctx, NumberKind); + std::optional<uint64_t> OptCFNumberSize = GetCFNumberSize(Ctx, NumberKind); // FIXME: In some cases we can emit an error. if (!OptCFNumberSize) @@ -462,7 +464,7 @@ void CFNumberChecker::checkPreStmt(const CallExpr *CE, // FIXME: Eventually we should handle arbitrary locations. We can do this // by having an enhanced memory model that does low-level typing. - Optional<loc::MemRegionVal> LV = TheValueExpr.getAs<loc::MemRegionVal>(); + std::optional<loc::MemRegionVal> LV = TheValueExpr.getAs<loc::MemRegionVal>(); if (!LV) return; @@ -531,10 +533,10 @@ namespace { class CFRetainReleaseChecker : public Checker<check::PreCall> { mutable APIMisuse BT{this, "null passed to CF memory management function"}; const CallDescriptionSet ModelledCalls = { - {"CFRetain", 1}, - {"CFRelease", 1}, - {"CFMakeCollectable", 1}, - {"CFAutorelease", 1}, + {{"CFRetain"}, 1}, + {{"CFRelease"}, 1}, + {{"CFMakeCollectable"}, 1}, + {{"CFAutorelease"}, 1}, }; public: @@ -554,7 +556,7 @@ void CFRetainReleaseChecker::checkPreCall(const CallEvent &Call, // Get the argument's value. SVal ArgVal = Call.getArgSVal(0); - Optional<DefinedSVal> DefArgVal = ArgVal.getAs<DefinedSVal>(); + std::optional<DefinedSVal> DefArgVal = ArgVal.getAs<DefinedSVal>(); if (!DefArgVal) return; @@ -742,7 +744,7 @@ void VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &msg, return; // Verify that all arguments have Objective-C types. - Optional<ExplodedNode*> errorNode; + std::optional<ExplodedNode *> errorNode; for (unsigned I = variadicArgsBegin; I != variadicArgsEnd; ++I) { QualType ArgTy = msg.getArgExpr(I)->getType(); @@ -769,7 +771,7 @@ void VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &msg, if (!errorNode) errorNode = C.generateNonFatalErrorNode(); - if (!errorNode.value()) + if (!*errorNode) continue; SmallString<128> sbuf; @@ -786,8 +788,8 @@ void VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &msg, ArgTy.print(os, C.getLangOpts()); os << "'"; - auto R = std::make_unique<PathSensitiveBugReport>(*BT, os.str(), - errorNode.value()); + auto R = + std::make_unique<PathSensitiveBugReport>(*BT, os.str(), *errorNode); R->addRange(msg.getArgSourceRange(I)); C.emitReport(std::move(R)); } @@ -857,7 +859,8 @@ static ProgramStateRef checkCollectionNonNil(CheckerContext &C, return nullptr; SVal CollectionVal = C.getSVal(FCS->getCollection()); - Optional<DefinedSVal> KnownCollection = CollectionVal.getAs<DefinedSVal>(); + std::optional<DefinedSVal> KnownCollection = + CollectionVal.getAs<DefinedSVal>(); if (!KnownCollection) return State; @@ -889,7 +892,7 @@ static ProgramStateRef checkElementNonNil(CheckerContext &C, const Stmt *Element = FCS->getElement(); // FIXME: Copied from ExprEngineObjC. - Optional<Loc> ElementLoc; + std::optional<Loc> ElementLoc; if (const DeclStmt *DS = dyn_cast<DeclStmt>(Element)) { const VarDecl *ElemDecl = cast<VarDecl>(DS->getSingleDecl()); assert(ElemDecl->getInit() == nullptr); @@ -928,8 +931,8 @@ assumeCollectionNonEmpty(CheckerContext &C, ProgramStateRef State, nonloc::SymbolVal(*CountS), SvalBuilder.makeIntVal(0, (*CountS)->getType()), SvalBuilder.getConditionType()); - Optional<DefinedSVal> CountGreaterThanZero = - CountGreaterThanZeroVal.getAs<DefinedSVal>(); + std::optional<DefinedSVal> CountGreaterThanZero = + CountGreaterThanZeroVal.getAs<DefinedSVal>(); if (!CountGreaterThanZero) { // The SValBuilder cannot construct a valid SVal for this condition. // This means we cannot properly reason about it. @@ -957,7 +960,7 @@ static bool alreadyExecutedAtLeastOneLoopIteration(const ExplodedNode *N, return false; ProgramPoint P = N->getLocation(); - if (Optional<BlockEdge> BE = P.getAs<BlockEdge>()) { + if (std::optional<BlockEdge> BE = P.getAs<BlockEdge>()) { return BE->getSrc()->getLoopTarget() == FCS; } @@ -1092,7 +1095,7 @@ ObjCLoopChecker::checkPointerEscape(ProgramStateRef State, PointerEscapeKind Kind) const { SymbolRef ImmutableReceiver = getMethodReceiverIfKnownImmutable(Call); - // Remove the invalidated symbols form the collection count map. + // Remove the invalidated symbols from the collection count map. for (InvalidatedSymbols::const_iterator I = Escaped.begin(), E = Escaped.end(); I != E; ++I) { @@ -1174,7 +1177,8 @@ ObjCNonNilReturnValueChecker::assumeExprIsNonNull(const Expr *NonNullExpr, ProgramStateRef State, CheckerContext &C) const { SVal Val = C.getSVal(NonNullExpr); - if (Optional<DefinedOrUnknownSVal> DV = Val.getAs<DefinedOrUnknownSVal>()) + if (std::optional<DefinedOrUnknownSVal> DV = + Val.getAs<DefinedOrUnknownSVal>()) return State->assume(*DV, true); return State; } diff --git a/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp index 8416ab39e194..76f091562cd5 100644 --- a/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp @@ -64,19 +64,15 @@ public: REGISTER_TRAIT_WITH_PROGRAMSTATE(MutexCounter, unsigned) BlockInCriticalSectionChecker::BlockInCriticalSectionChecker() - : IILockGuard(nullptr), IIUniqueLock(nullptr), - LockFn("lock"), UnlockFn("unlock"), SleepFn("sleep"), GetcFn("getc"), - 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"), - ClassLockGuard("lock_guard"), - ClassUniqueLock("unique_lock"), - IdentifierInfoInitialized(false) { + : IILockGuard(nullptr), IIUniqueLock(nullptr), LockFn({"lock"}), + UnlockFn({"unlock"}), SleepFn({"sleep"}), GetcFn({"getc"}), + 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"}), ClassLockGuard("lock_guard"), + ClassUniqueLock("unique_lock"), IdentifierInfoInitialized(false) { // Initialize the bug type. BlockInCritSectionBugType.reset( new BugType(this, "Call to blocking function in critical section", diff --git a/clang/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp index dad25d6f853b..2d20e394681d 100644 --- a/clang/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp @@ -17,6 +17,7 @@ #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include <optional> using namespace clang; using namespace ento; @@ -74,7 +75,7 @@ void BoolAssignmentChecker::checkBind(SVal loc, SVal val, const Stmt *S, // Get the value of the right-hand side. We only care about values // that are defined (UnknownVals and UndefinedVals are handled by other // checkers). - Optional<NonLoc> NV = val.getAs<NonLoc>(); + std::optional<NonLoc> NV = val.getAs<NonLoc>(); if (!NV) return; diff --git a/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp index 9a6c013bcf66..12b948a65261 100644 --- a/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp @@ -27,6 +27,7 @@ #include "llvm/ADT/StringExtras.h" #include "llvm/Support/raw_ostream.h" #include <functional> +#include <optional> using namespace clang; using namespace ento; @@ -75,6 +76,16 @@ static ErrorMessage createOutOfBoundErrorMsg(StringRef FunctionDescription, } enum class ConcatFnKind { none = 0, strcat = 1, strlcat = 2 }; + +enum class CharKind { Regular = 0, Wide }; +constexpr CharKind CK_Regular = CharKind::Regular; +constexpr CharKind CK_Wide = CharKind::Wide; + +static QualType getCharPtrType(ASTContext &Ctx, CharKind CK) { + return Ctx.getPointerType(CK == CharKind::Regular ? Ctx.CharTy + : Ctx.WideCharTy); +} + class CStringChecker : public Checker< eval::Call, check::PreStmt<DeclStmt>, check::LiveSymbols, @@ -124,35 +135,46 @@ public: const CallExpr *)>; CallDescriptionMap<FnCheck> Callbacks = { - {{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}, - {{CDF_MaybeBuiltin, "memset", 3}, &CStringChecker::evalMemset}, - {{CDF_MaybeBuiltin, "explicit_memset", 3}, &CStringChecker::evalMemset}, - {{CDF_MaybeBuiltin, "strcpy", 2}, &CStringChecker::evalStrcpy}, - {{CDF_MaybeBuiltin, "strncpy", 3}, &CStringChecker::evalStrncpy}, - {{CDF_MaybeBuiltin, "stpcpy", 2}, &CStringChecker::evalStpcpy}, - {{CDF_MaybeBuiltin, "strlcpy", 3}, &CStringChecker::evalStrlcpy}, - {{CDF_MaybeBuiltin, "strcat", 2}, &CStringChecker::evalStrcat}, - {{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}, - {{CDF_MaybeBuiltin, "strncasecmp", 3}, &CStringChecker::evalStrncasecmp}, - {{CDF_MaybeBuiltin, "strsep", 2}, &CStringChecker::evalStrsep}, - {{CDF_MaybeBuiltin, "bcopy", 3}, &CStringChecker::evalBcopy}, - {{CDF_MaybeBuiltin, "bcmp", 3}, &CStringChecker::evalMemcmp}, - {{CDF_MaybeBuiltin, "bzero", 2}, &CStringChecker::evalBzero}, - {{CDF_MaybeBuiltin, "explicit_bzero", 2}, &CStringChecker::evalBzero}, + {{CDF_MaybeBuiltin, {"memcpy"}, 3}, + std::bind(&CStringChecker::evalMemcpy, _1, _2, _3, CK_Regular)}, + {{CDF_MaybeBuiltin, {"wmemcpy"}, 3}, + std::bind(&CStringChecker::evalMemcpy, _1, _2, _3, CK_Wide)}, + {{CDF_MaybeBuiltin, {"mempcpy"}, 3}, + std::bind(&CStringChecker::evalMempcpy, _1, _2, _3, CK_Regular)}, + {{CDF_None, {"wmempcpy"}, 3}, + std::bind(&CStringChecker::evalMempcpy, _1, _2, _3, CK_Wide)}, + {{CDF_MaybeBuiltin, {"memcmp"}, 3}, + std::bind(&CStringChecker::evalMemcmp, _1, _2, _3, CK_Regular)}, + {{CDF_MaybeBuiltin, {"wmemcmp"}, 3}, + std::bind(&CStringChecker::evalMemcmp, _1, _2, _3, CK_Wide)}, + {{CDF_MaybeBuiltin, {"memmove"}, 3}, + std::bind(&CStringChecker::evalMemmove, _1, _2, _3, CK_Regular)}, + {{CDF_MaybeBuiltin, {"wmemmove"}, 3}, + std::bind(&CStringChecker::evalMemmove, _1, _2, _3, CK_Wide)}, + {{CDF_MaybeBuiltin, {"memset"}, 3}, &CStringChecker::evalMemset}, + {{CDF_MaybeBuiltin, {"explicit_memset"}, 3}, &CStringChecker::evalMemset}, + {{CDF_MaybeBuiltin, {"strcpy"}, 2}, &CStringChecker::evalStrcpy}, + {{CDF_MaybeBuiltin, {"strncpy"}, 3}, &CStringChecker::evalStrncpy}, + {{CDF_MaybeBuiltin, {"stpcpy"}, 2}, &CStringChecker::evalStpcpy}, + {{CDF_MaybeBuiltin, {"strlcpy"}, 3}, &CStringChecker::evalStrlcpy}, + {{CDF_MaybeBuiltin, {"strcat"}, 2}, &CStringChecker::evalStrcat}, + {{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}, + {{CDF_MaybeBuiltin, {"strncasecmp"}, 3}, + &CStringChecker::evalStrncasecmp}, + {{CDF_MaybeBuiltin, {"strsep"}, 2}, &CStringChecker::evalStrsep}, + {{CDF_MaybeBuiltin, {"bcopy"}, 3}, &CStringChecker::evalBcopy}, + {{CDF_MaybeBuiltin, {"bcmp"}, 3}, + std::bind(&CStringChecker::evalMemcmp, _1, _2, _3, CK_Regular)}, + {{CDF_MaybeBuiltin, {"bzero"}, 2}, &CStringChecker::evalBzero}, + {{CDF_MaybeBuiltin, {"explicit_bzero"}, 2}, &CStringChecker::evalBzero}, }; // These require a bit of special handling. @@ -160,16 +182,16 @@ public: StdCopyBackward{{"std", "copy_backward"}, 3}; FnCheck identifyCall(const CallEvent &Call, CheckerContext &C) 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 evalMemcpy(CheckerContext &C, const CallExpr *CE, CharKind CK) const; + void evalMempcpy(CheckerContext &C, const CallExpr *CE, CharKind CK) const; + void evalMemmove(CheckerContext &C, const CallExpr *CE, CharKind CK) 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, bool IsWide) const; + bool Restricted, bool IsMempcpy, CharKind CK) const; - void evalMemcmp(CheckerContext &C, const CallExpr *CE) const; + void evalMemcmp(CheckerContext &C, const CallExpr *CE, CharKind CK) const; void evalstrLength(CheckerContext &C, const CallExpr *CE) const; void evalstrnLength(CheckerContext &C, const CallExpr *CE) const; @@ -248,14 +270,16 @@ public: AnyArgExpr Arg, SVal l) const; ProgramStateRef CheckLocation(CheckerContext &C, ProgramStateRef state, AnyArgExpr Buffer, SVal Element, - AccessKind Access, bool IsWide = false) const; + AccessKind Access, + CharKind CK = CharKind::Regular) const; ProgramStateRef CheckBufferAccess(CheckerContext &C, ProgramStateRef State, AnyArgExpr Buffer, SizeArgExpr Size, AccessKind Access, - bool IsWide = false) const; + CharKind CK = CharKind::Regular) const; ProgramStateRef CheckOverlap(CheckerContext &C, ProgramStateRef state, SizeArgExpr Size, AnyArgExpr First, - AnyArgExpr Second, bool IsWide = false) const; + AnyArgExpr Second, + CharKind CK = CharKind::Regular) const; void emitOverlapBug(CheckerContext &C, ProgramStateRef state, const Stmt *First, @@ -295,7 +319,7 @@ REGISTER_MAP_WITH_PROGRAMSTATE(CStringLength, const MemRegion *, SVal) std::pair<ProgramStateRef , ProgramStateRef > CStringChecker::assumeZero(CheckerContext &C, ProgramStateRef state, SVal V, QualType Ty) { - Optional<DefinedSVal> val = V.getAs<DefinedSVal>(); + std::optional<DefinedSVal> val = V.getAs<DefinedSVal>(); if (!val) return std::pair<ProgramStateRef , ProgramStateRef >(state, state); @@ -339,7 +363,7 @@ ProgramStateRef CStringChecker::CheckLocation(CheckerContext &C, ProgramStateRef state, AnyArgExpr Buffer, SVal Element, AccessKind Access, - bool IsWide) const { + CharKind CK) const { // If a previous check has failed, propagate the failure. if (!state) @@ -360,7 +384,7 @@ ProgramStateRef CStringChecker::CheckLocation(CheckerContext &C, // Get the index of the accessed element. NonLoc Idx = ER->getIndex(); - if (!IsWide) { + if (CK == CharKind::Regular) { if (ER->getValueType() != Ctx.CharTy) return state; } else { @@ -417,7 +441,7 @@ ProgramStateRef CStringChecker::CheckLocation(CheckerContext &C, ProgramStateRef CStringChecker::CheckBufferAccess(CheckerContext &C, ProgramStateRef State, AnyArgExpr Buffer, SizeArgExpr Size, - AccessKind Access, bool IsWide) const { + AccessKind Access, CharKind CK) const { // If a previous check has failed, propagate the failure. if (!State) return nullptr; @@ -426,7 +450,7 @@ CStringChecker::CheckBufferAccess(CheckerContext &C, ProgramStateRef State, ASTContext &Ctx = svalBuilder.getContext(); QualType SizeTy = Size.Expression->getType(); - QualType PtrTy = Ctx.getPointerType(IsWide ? Ctx.WideCharTy : Ctx.CharTy); + QualType PtrTy = getCharPtrType(Ctx, CK); // Check that the first buffer is non-null. SVal BufVal = C.getSVal(Buffer.Expression); @@ -442,7 +466,7 @@ CStringChecker::CheckBufferAccess(CheckerContext &C, ProgramStateRef State, // FIXME: This assumes the caller has already checked that the access length // is positive. And that it's unsigned. SVal LengthVal = C.getSVal(Size.Expression); - Optional<NonLoc> Length = LengthVal.getAs<NonLoc>(); + std::optional<NonLoc> Length = LengthVal.getAs<NonLoc>(); if (!Length) return State; @@ -456,11 +480,11 @@ CStringChecker::CheckBufferAccess(CheckerContext &C, ProgramStateRef State, // Check that the first buffer is sufficiently long. SVal BufStart = svalBuilder.evalCast(BufVal, PtrTy, Buffer.Expression->getType()); - if (Optional<Loc> BufLoc = BufStart.getAs<Loc>()) { + if (std::optional<Loc> BufLoc = BufStart.getAs<Loc>()) { SVal BufEnd = svalBuilder.evalBinOpLN(State, BO_Add, *BufLoc, LastOffset, PtrTy); - State = CheckLocation(C, State, Buffer, BufEnd, Access, IsWide); + State = CheckLocation(C, State, Buffer, BufEnd, Access, CK); // If the buffer isn't large enough, abort. if (!State) @@ -475,7 +499,7 @@ ProgramStateRef CStringChecker::CheckOverlap(CheckerContext &C, ProgramStateRef state, SizeArgExpr Size, AnyArgExpr First, AnyArgExpr Second, - bool IsWide) const { + CharKind CK) const { if (!Filter.CheckCStringBufferOverlap) return state; @@ -499,11 +523,11 @@ ProgramStateRef CStringChecker::CheckOverlap(CheckerContext &C, SVal firstVal = state->getSVal(First.Expression, LCtx); SVal secondVal = state->getSVal(Second.Expression, LCtx); - Optional<Loc> firstLoc = firstVal.getAs<Loc>(); + std::optional<Loc> firstLoc = firstVal.getAs<Loc>(); if (!firstLoc) return state; - Optional<Loc> secondLoc = secondVal.getAs<Loc>(); + std::optional<Loc> secondLoc = secondVal.getAs<Loc>(); if (!secondLoc) return state; @@ -526,7 +550,7 @@ ProgramStateRef CStringChecker::CheckOverlap(CheckerContext &C, QualType cmpTy = svalBuilder.getConditionType(); SVal reverse = svalBuilder.evalBinOpLL(state, BO_GT, *firstLoc, *secondLoc, cmpTy); - Optional<DefinedOrUnknownSVal> reverseTest = + std::optional<DefinedOrUnknownSVal> reverseTest = reverse.getAs<DefinedOrUnknownSVal>(); if (!reverseTest) return state; @@ -547,31 +571,31 @@ ProgramStateRef CStringChecker::CheckOverlap(CheckerContext &C, // Get the length, and make sure it too is known. SVal LengthVal = state->getSVal(Size.Expression, LCtx); - Optional<NonLoc> Length = LengthVal.getAs<NonLoc>(); + std::optional<NonLoc> Length = LengthVal.getAs<NonLoc>(); if (!Length) return state; // Convert the first buffer's start address to char*. // Bail out if the cast fails. ASTContext &Ctx = svalBuilder.getContext(); - QualType CharPtrTy = Ctx.getPointerType(IsWide ? Ctx.WideCharTy : Ctx.CharTy); + QualType CharPtrTy = getCharPtrType(Ctx, CK); SVal FirstStart = svalBuilder.evalCast(*firstLoc, CharPtrTy, First.Expression->getType()); - Optional<Loc> FirstStartLoc = FirstStart.getAs<Loc>(); + std::optional<Loc> FirstStartLoc = FirstStart.getAs<Loc>(); if (!FirstStartLoc) return state; // Compute the end of the first buffer. Bail out if THAT fails. SVal FirstEnd = svalBuilder.evalBinOpLN(state, BO_Add, *FirstStartLoc, *Length, CharPtrTy); - Optional<Loc> FirstEndLoc = FirstEnd.getAs<Loc>(); + std::optional<Loc> FirstEndLoc = FirstEnd.getAs<Loc>(); if (!FirstEndLoc) return state; // Is the end of the first buffer past the start of the second buffer? SVal Overlap = svalBuilder.evalBinOpLL(state, BO_GT, *FirstEndLoc, *secondLoc, cmpTy); - Optional<DefinedOrUnknownSVal> OverlapTest = + std::optional<DefinedOrUnknownSVal> OverlapTest = Overlap.getAs<DefinedOrUnknownSVal>(); if (!OverlapTest) return state; @@ -736,7 +760,7 @@ ProgramStateRef CStringChecker::checkAdditionOverflow(CheckerContext &C, left = right; } - if (Optional<NonLoc> maxMinusRightNL = maxMinusRight.getAs<NonLoc>()) { + if (std::optional<NonLoc> maxMinusRightNL = maxMinusRight.getAs<NonLoc>()) { QualType cmpTy = svalBuilder.getConditionType(); // If left > max - right, we have an overflow. SVal willOverflow = svalBuilder.evalBinOpNN(state, BO_GT, left, @@ -822,7 +846,7 @@ SVal CStringChecker::getCStringLengthForRegion(CheckerContext &C, C.blockCount()); if (!hypothetical) { - if (Optional<NonLoc> strLn = strLength.getAs<NonLoc>()) { + if (std::optional<NonLoc> strLn = strLength.getAs<NonLoc>()) { // In case of unbounded calls strlen etc bound the range to SIZE_MAX/4 BasicValueFactory &BVF = svalBuilder.getBasicValueFactory(); const llvm::APSInt &maxValInt = BVF.getMaxValue(sizeTy); @@ -848,7 +872,7 @@ SVal CStringChecker::getCStringLength(CheckerContext &C, ProgramStateRef &state, // If we can't get a region, see if it's something we /know/ isn't a // C string. In the context of locations, the only time we can issue such // a warning is for labels. - if (Optional<loc::GotoLabel> Label = Buf.getAs<loc::GotoLabel>()) { + if (std::optional<loc::GotoLabel> Label = Buf.getAs<loc::GotoLabel>()) { if (Filter.CheckCStringNotNullTerm) { SmallString<120> buf; llvm::raw_svector_ostream os(buf); @@ -953,7 +977,7 @@ bool CStringChecker::IsFirstBufInBound(CheckerContext &C, SVal BufVal = state->getSVal(FirstBuf, LCtx); SVal LengthVal = state->getSVal(Size, LCtx); - Optional<NonLoc> Length = LengthVal.getAs<NonLoc>(); + std::optional<NonLoc> Length = LengthVal.getAs<NonLoc>(); if (!Length) return true; // cf top comment. @@ -966,7 +990,7 @@ bool CStringChecker::IsFirstBufInBound(CheckerContext &C, // Check that the first buffer is sufficiently long. SVal BufStart = svalBuilder.evalCast(BufVal, PtrTy, FirstBuf->getType()); - Optional<Loc> BufLoc = BufStart.getAs<Loc>(); + std::optional<Loc> BufLoc = BufStart.getAs<Loc>(); if (!BufLoc) return true; // cf top comment. @@ -1004,14 +1028,14 @@ ProgramStateRef CStringChecker::InvalidateBuffer(CheckerContext &C, const Expr *E, SVal V, bool IsSourceBuffer, const Expr *Size) { - Optional<Loc> L = V.getAs<Loc>(); + std::optional<Loc> L = V.getAs<Loc>(); if (!L) return state; // FIXME: This is a simplified version of what's in CFRefCount.cpp -- it makes // some assumptions about the value that CFRefCount can't. Even so, it should // probably be refactored. - if (Optional<loc::MemRegionVal> MR = L->getAs<loc::MemRegionVal>()) { + if (std::optional<loc::MemRegionVal> MR = L->getAs<loc::MemRegionVal>()) { const MemRegion *R = MR->getRegion()->StripCasts(); // Are we dealing with an ElementRegion? If so, we should be invalidating @@ -1111,7 +1135,7 @@ bool CStringChecker::memsetAux(const Expr *DstBuffer, SVal CharVal, RegionOffset Offset = MR->getAsOffset(); const MemRegion *BR = Offset.getRegion(); - Optional<NonLoc> SizeNL = SizeVal.getAs<NonLoc>(); + std::optional<NonLoc> SizeNL = SizeVal.getAs<NonLoc>(); if (!SizeNL) return false; @@ -1190,7 +1214,7 @@ void CStringChecker::evalCopyCommon(CheckerContext &C, const CallExpr *CE, ProgramStateRef state, SizeArgExpr Size, DestinationArgExpr Dest, SourceArgExpr Source, bool Restricted, - bool IsMempcpy, bool IsWide) const { + bool IsMempcpy, CharKind CK) const { CurrentFunctionDescription = "memory copy function"; // See if the size argument is zero. @@ -1233,11 +1257,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, IsWide); - state = CheckBufferAccess(C, state, Source, Size, AccessKind::read, IsWide); + state = CheckBufferAccess(C, state, Dest, Size, AccessKind::write, CK); + state = CheckBufferAccess(C, state, Source, Size, AccessKind::read, CK); if (Restricted) - state = CheckOverlap(C, state, Size, Dest, Source, IsWide); + state = CheckOverlap(C, state, Size, Dest, Source, CK); if (!state) return; @@ -1248,7 +1272,7 @@ void CStringChecker::evalCopyCommon(CheckerContext &C, const CallExpr *CE, // Get the byte after the last byte copied. SValBuilder &SvalBuilder = C.getSValBuilder(); ASTContext &Ctx = SvalBuilder.getContext(); - QualType CharPtrTy = Ctx.getPointerType(Ctx.CharTy); + QualType CharPtrTy = getCharPtrType(Ctx, CK); SVal DestRegCharVal = SvalBuilder.evalCast(destVal, CharPtrTy, Dest.Expression->getType()); SVal lastElement = C.getSValBuilder().evalBinOp( @@ -1288,7 +1312,7 @@ void CStringChecker::evalCopyCommon(CheckerContext &C, const CallExpr *CE, } void CStringChecker::evalMemcpy(CheckerContext &C, const CallExpr *CE, - bool IsWide) const { + CharKind CK) 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}; @@ -1299,11 +1323,11 @@ void CStringChecker::evalMemcpy(CheckerContext &C, const CallExpr *CE, constexpr bool IsRestricted = true; constexpr bool IsMempcpy = false; - evalCopyCommon(C, CE, State, Size, Dest, Src, IsRestricted, IsMempcpy, - IsWide); + evalCopyCommon(C, CE, State, Size, Dest, Src, IsRestricted, IsMempcpy, CK); } -void CStringChecker::evalMempcpy(CheckerContext &C, const CallExpr *CE) const { +void CStringChecker::evalMempcpy(CheckerContext &C, const CallExpr *CE, + CharKind CK) const { // void *mempcpy(void *restrict dst, const void *restrict src, size_t n); // The return value is a pointer to the byte following the last written byte. DestinationArgExpr Dest = {CE->getArg(0), 0}; @@ -1313,10 +1337,11 @@ 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, - false); + CK); } -void CStringChecker::evalMemmove(CheckerContext &C, const CallExpr *CE) const { +void CStringChecker::evalMemmove(CheckerContext &C, const CallExpr *CE, + CharKind CK) const { // void *memmove(void *dst, const void *src, size_t n); // The return value is the address of the destination buffer. DestinationArgExpr Dest = {CE->getArg(0), 0}; @@ -1326,7 +1351,7 @@ 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, - false); + CK); } void CStringChecker::evalBcopy(CheckerContext &C, const CallExpr *CE) const { @@ -1338,10 +1363,11 @@ 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, - false); + CharKind::Regular); } -void CStringChecker::evalMemcmp(CheckerContext &C, const CallExpr *CE) const { +void CStringChecker::evalMemcmp(CheckerContext &C, const CallExpr *CE, + CharKind CK) const { // int memcmp(const void *s1, const void *s2, size_t n); CurrentFunctionDescription = "memory comparison function"; @@ -1401,8 +1427,8 @@ void CStringChecker::evalMemcmp(CheckerContext &C, const CallExpr *CE) const { // If the two arguments might be different buffers, we have to check // the size of both of them. assert(NotSameBuffer); - State = CheckBufferAccess(C, State, Right, Size, AccessKind::read); - State = CheckBufferAccess(C, State, Left, Size, AccessKind::read); + State = CheckBufferAccess(C, State, Right, Size, AccessKind::read, CK); + State = CheckBufferAccess(C, State, Left, Size, AccessKind::read, CK); if (State) { // The return value is the comparison result, which we don't know. SVal CmpV = Builder.conjureSymbolVal(nullptr, CE, LCtx, C.blockCount()); @@ -1481,8 +1507,8 @@ void CStringChecker::evalstrLengthCommon(CheckerContext &C, const CallExpr *CE, const Expr *maxlenExpr = CE->getArg(1); SVal maxlenVal = state->getSVal(maxlenExpr, LCtx); - Optional<NonLoc> strLengthNL = strLength.getAs<NonLoc>(); - Optional<NonLoc> maxlenValNL = maxlenVal.getAs<NonLoc>(); + std::optional<NonLoc> strLengthNL = strLength.getAs<NonLoc>(); + std::optional<NonLoc> maxlenValNL = maxlenVal.getAs<NonLoc>(); if (strLengthNL && maxlenValNL) { ProgramStateRef stateStringTooLong, stateStringNotTooLong; @@ -1630,11 +1656,11 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, // Get the string length of the source. SVal strLength = getCStringLength(C, state, srcExpr.Expression, srcVal); - Optional<NonLoc> strLengthNL = strLength.getAs<NonLoc>(); + std::optional<NonLoc> strLengthNL = strLength.getAs<NonLoc>(); // Get the string length of the destination buffer. SVal dstStrLength = getCStringLength(C, state, Dst.Expression, DstVal); - Optional<NonLoc> dstStrLengthNL = dstStrLength.getAs<NonLoc>(); + std::optional<NonLoc> dstStrLengthNL = dstStrLength.getAs<NonLoc>(); // If the source isn't a valid C string, give up. if (strLength.isUndef()) @@ -1672,7 +1698,7 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, lenVal = svalBuilder.evalCast(lenVal, sizeTy, lenExpr.Expression->getType()); - Optional<NonLoc> lenValNL = lenVal.getAs<NonLoc>(); + std::optional<NonLoc> lenValNL = lenVal.getAs<NonLoc>(); // If we know both values, we might be able to figure out how much // we're copying. @@ -1714,7 +1740,7 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, freeSpace = svalBuilder.evalBinOp(state, BO_Sub, freeSpace, svalBuilder.makeIntVal(1, sizeTy), sizeTy); - Optional<NonLoc> freeSpaceNL = freeSpace.getAs<NonLoc>(); + std::optional<NonLoc> freeSpaceNL = freeSpace.getAs<NonLoc>(); // While unlikely, it is possible that the subtraction is // too complex to compute, let's check whether it succeeded. @@ -1839,7 +1865,7 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, *dstStrLengthNL, sizeTy); } - Optional<NonLoc> amountCopiedNL = amountCopied.getAs<NonLoc>(); + std::optional<NonLoc> amountCopiedNL = amountCopied.getAs<NonLoc>(); // If we know both string lengths, we might know the final string length. if (amountCopiedNL && dstStrLengthNL) { @@ -1860,7 +1886,8 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, finalStrLength = getCStringLength(C, state, CE, DstVal, true); assert(!finalStrLength.isUndef()); - if (Optional<NonLoc> finalStrLengthNL = finalStrLength.getAs<NonLoc>()) { + if (std::optional<NonLoc> finalStrLengthNL = + finalStrLength.getAs<NonLoc>()) { if (amountCopiedNL && appendK == ConcatFnKind::none) { // we overwrite dst string with the src // finalStrLength >= srcStrLength @@ -1911,13 +1938,13 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, // If the destination is a MemRegion, try to check for a buffer overflow and // record the new string length. - if (Optional<loc::MemRegionVal> dstRegVal = - DstVal.getAs<loc::MemRegionVal>()) { + if (std::optional<loc::MemRegionVal> dstRegVal = + DstVal.getAs<loc::MemRegionVal>()) { QualType ptrTy = Dst.Expression->getType(); // If we have an exact value on a bounded copy, use that to check for // overflows, rather than our estimate about how much is actually copied. - if (Optional<NonLoc> maxLastNL = maxLastElementIndex.getAs<NonLoc>()) { + if (std::optional<NonLoc> maxLastNL = maxLastElementIndex.getAs<NonLoc>()) { SVal maxLastElement = svalBuilder.evalBinOpLN(state, BO_Add, *dstRegVal, *maxLastNL, ptrTy); @@ -1927,7 +1954,7 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, } // Then, if the final length is known... - if (Optional<NonLoc> knownStrLength = finalStrLength.getAs<NonLoc>()) { + if (std::optional<NonLoc> knownStrLength = finalStrLength.getAs<NonLoc>()) { SVal lastElement = svalBuilder.evalBinOpLN(state, BO_Add, *dstRegVal, *knownStrLength, ptrTy); @@ -2119,7 +2146,7 @@ void CStringChecker::evalStrcmpCommon(CheckerContext &C, const CallExpr *CE, DefinedSVal zeroVal = svalBuilder.makeIntVal(0, CE->getType()); // Constrain strcmp's result range based on the result of StringRef's // comparison methods. - BinaryOperatorKind op = (compareRes == 1) ? BO_GT : BO_LT; + BinaryOperatorKind op = (compareRes > 0) ? BO_GT : BO_LT; SVal compareWithZero = svalBuilder.evalBinOp(state, op, resultVal, zeroVal, svalBuilder.getConditionType()); @@ -2165,7 +2192,7 @@ void CStringChecker::evalStrsep(CheckerContext &C, const CallExpr *CE) const { SValBuilder &SVB = C.getSValBuilder(); SVal Result; - if (Optional<Loc> SearchStrLoc = SearchStrVal.getAs<Loc>()) { + if (std::optional<Loc> SearchStrLoc = SearchStrVal.getAs<Loc>()) { // Get the current value of the search string pointer, as a char*. Result = State->getSVal(*SearchStrLoc, CharPtrTy); diff --git a/clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp index 45f9a82a9d0a..f02d20d45678 100644 --- a/clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp @@ -24,7 +24,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h" -#include "llvm/ADT/Optional.h" +#include <optional> #include <utility> using namespace clang; @@ -472,7 +472,7 @@ bool CastValueChecker::evalCall(const CallEvent &Call, const CastCheck &Check = Lookup->first; CallKind Kind = Lookup->second; - Optional<DefinedOrUnknownSVal> DV; + std::optional<DefinedOrUnknownSVal> DV; switch (Kind) { case CallKind::Function: { diff --git a/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp b/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp index 2d2ddcdf3890..3fcf6f435a43 100644 --- a/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp @@ -45,6 +45,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" #include "llvm/Support/raw_ostream.h" +#include <optional> using namespace clang; using namespace ento; @@ -282,7 +283,7 @@ void ObjCDeallocChecker::checkBeginFunction( continue; SVal LVal = State->getLValue(PropImpl->getPropertyIvarDecl(), SelfVal); - Optional<Loc> LValLoc = LVal.getAs<Loc>(); + std::optional<Loc> LValLoc = LVal.getAs<Loc>(); if (!LValLoc) continue; @@ -953,7 +954,7 @@ ObjCDeallocChecker::getValueReleasedByNillingOut(const ObjCMethodCall &M, ProgramStateRef State = C.getState(); SVal LVal = State->getLValue(PropIvarDecl, ReceiverVal); - Optional<Loc> LValLoc = LVal.getAs<Loc>(); + std::optional<Loc> LValLoc = LVal.getAs<Loc>(); if (!LValLoc) return nullptr; @@ -1004,7 +1005,7 @@ bool ObjCDeallocChecker::instanceDeallocIsOnStack(const CheckerContext &C, return false; } -/// Returns true if the ID is a class in which which is known to have +/// Returns true if the ID is a class in which is known to have /// a separate teardown lifecycle. In this case, -dealloc warnings /// about missing releases should be suppressed. bool ObjCDeallocChecker::classHasSeparateTeardown( diff --git a/clang/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp index ce8d6c879870..9ace1583eb53 100644 --- a/clang/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp @@ -43,7 +43,7 @@ class ChrootChecker : public Checker<eval::Call, check::PreCall> { // This bug refers to possibly break out of a chroot() jail. mutable std::unique_ptr<BuiltinBug> BT_BreakJail; - const CallDescription Chroot{"chroot", 1}, Chdir{"chdir", 1}; + const CallDescription Chroot{{"chroot"}, 1}, Chdir{{"chdir"}, 1}; public: ChrootChecker() {} diff --git a/clang/lib/StaticAnalyzer/Checkers/ContainerModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/ContainerModeling.cpp index 77a3218f55fb..67962f75f9bf 100644 --- a/clang/lib/StaticAnalyzer/Checkers/ContainerModeling.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/ContainerModeling.cpp @@ -72,26 +72,26 @@ public: SVal) const; CallDescriptionMap<NoItParamFn> NoIterParamFunctions = { - {{"clear", 0}, &ContainerModeling::handleClear}, - {{"assign", 2}, &ContainerModeling::handleAssign}, - {{"push_back", 1}, &ContainerModeling::handlePushBack}, - {{"emplace_back", 1}, &ContainerModeling::handlePushBack}, - {{"pop_back", 0}, &ContainerModeling::handlePopBack}, - {{"push_front", 1}, &ContainerModeling::handlePushFront}, - {{"emplace_front", 1}, &ContainerModeling::handlePushFront}, - {{"pop_front", 0}, &ContainerModeling::handlePopFront}, + {{{"clear"}, 0}, &ContainerModeling::handleClear}, + {{{"assign"}, 2}, &ContainerModeling::handleAssign}, + {{{"push_back"}, 1}, &ContainerModeling::handlePushBack}, + {{{"emplace_back"}, 1}, &ContainerModeling::handlePushBack}, + {{{"pop_back"}, 0}, &ContainerModeling::handlePopBack}, + {{{"push_front"}, 1}, &ContainerModeling::handlePushFront}, + {{{"emplace_front"}, 1}, &ContainerModeling::handlePushFront}, + {{{"pop_front"}, 0}, &ContainerModeling::handlePopFront}, }; CallDescriptionMap<OneItParamFn> OneIterParamFunctions = { - {{"insert", 2}, &ContainerModeling::handleInsert}, - {{"emplace", 2}, &ContainerModeling::handleInsert}, - {{"erase", 1}, &ContainerModeling::handleErase}, - {{"erase_after", 1}, &ContainerModeling::handleEraseAfter}, + {{{"insert"}, 2}, &ContainerModeling::handleInsert}, + {{{"emplace"}, 2}, &ContainerModeling::handleInsert}, + {{{"erase"}, 1}, &ContainerModeling::handleErase}, + {{{"erase_after"}, 1}, &ContainerModeling::handleEraseAfter}, }; CallDescriptionMap<TwoItParamFn> TwoIterParamFunctions = { - {{"erase", 2}, &ContainerModeling::handleErase}, - {{"erase_after", 2}, &ContainerModeling::handleEraseAfter}, + {{{"erase"}, 2}, &ContainerModeling::handleErase}, + {{{"erase_after"}, 2}, &ContainerModeling::handleEraseAfter}, }; }; diff --git a/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp index 2102f9233bc1..01b662064d7b 100644 --- a/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp @@ -38,17 +38,17 @@ public: llvm::DenseSet<const VarDecl *> &S; bool TraverseObjCAtFinallyStmt(ObjCAtFinallyStmt *S) { - SaveAndRestore<bool> inFinally(inEH, true); + SaveAndRestore inFinally(inEH, true); return ::RecursiveASTVisitor<EHCodeVisitor>::TraverseObjCAtFinallyStmt(S); } bool TraverseObjCAtCatchStmt(ObjCAtCatchStmt *S) { - SaveAndRestore<bool> inCatch(inEH, true); + SaveAndRestore inCatch(inEH, true); return ::RecursiveASTVisitor<EHCodeVisitor>::TraverseObjCAtCatchStmt(S); } bool TraverseCXXCatchStmt(CXXCatchStmt *S) { - SaveAndRestore<bool> inCatch(inEH, true); + SaveAndRestore inCatch(inEH, true); return TraverseStmt(S->getHandlerBlock()); } @@ -103,8 +103,8 @@ void ReachableCode::computeReachableBlocks() { static const Expr * LookThroughTransitiveAssignmentsAndCommaOperators(const Expr *Ex) { while (Ex) { - const BinaryOperator *BO = - dyn_cast<BinaryOperator>(Ex->IgnoreParenCasts()); + Ex = Ex->IgnoreParenCasts(); + const BinaryOperator *BO = dyn_cast<BinaryOperator>(Ex); if (!BO) break; BinaryOperatorKind Op = BO->getOpcode(); @@ -240,7 +240,7 @@ public: case DeadIncrement: BugType = "Dead increment"; - LLVM_FALLTHROUGH; + [[fallthrough]]; case Standard: if (!BugType) BugType = "Dead assignment"; os << "Value stored to '" << *V << "' is never read"; @@ -331,8 +331,7 @@ public: // Special case: check for assigning null to a pointer. // This is a common form of defensive programming. const Expr *RHS = - LookThroughTransitiveAssignmentsAndCommaOperators(B->getRHS()); - RHS = RHS->IgnoreParenCasts(); + LookThroughTransitiveAssignmentsAndCommaOperators(B->getRHS()); QualType T = VD->getType(); if (T.isVolatileQualified()) @@ -415,8 +414,7 @@ public: if (isConstant(E)) return; - if (const DeclRefExpr *DRE = - dyn_cast<DeclRefExpr>(E->IgnoreParenCasts())) + if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) if (const VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl())) { // Special case: check for initialization from constant // variables. @@ -438,7 +436,7 @@ public: PathDiagnosticLocation Loc = PathDiagnosticLocation::create(V, BR.getSourceManager()); - Report(V, DeadInit, Loc, E->getSourceRange()); + Report(V, DeadInit, Loc, V->getInit()->getSourceRange()); } } } @@ -450,8 +448,9 @@ private: bool isConstant(const InitListExpr *Candidate) const { // We consider init list to be constant if each member of the list can be // interpreted as constant. - return llvm::all_of(Candidate->inits(), - [this](const Expr *Init) { return isConstant(Init); }); + return llvm::all_of(Candidate->inits(), [this](const Expr *Init) { + return isConstant(Init->IgnoreParenCasts()); + }); } /// Return true if the given expression can be interpreted as constant @@ -461,7 +460,7 @@ private: return true; // We should also allow defensive initialization of structs, i.e. { 0 } - if (const auto *ILE = dyn_cast<InitListExpr>(E->IgnoreParenCasts())) { + if (const auto *ILE = dyn_cast<InitListExpr>(E)) { return isConstant(ILE); } @@ -504,7 +503,7 @@ public: // Treat local variables captured by reference in C++ lambdas as escaped. void findLambdaReferenceCaptures(const LambdaExpr *LE) { const CXXRecordDecl *LambdaClass = LE->getLambdaClass(); - llvm::DenseMap<const VarDecl *, FieldDecl *> CaptureFields; + llvm::DenseMap<const ValueDecl *, FieldDecl *> CaptureFields; FieldDecl *ThisCaptureField; LambdaClass->getCaptureFields(CaptureFields, ThisCaptureField); @@ -512,14 +511,14 @@ public: if (!C.capturesVariable()) continue; - VarDecl *VD = C.getCapturedVar(); + ValueDecl *VD = C.getCapturedVar(); const FieldDecl *FD = CaptureFields[VD]; - if (!FD) + if (!FD || !isa<VarDecl>(VD)) continue; // If the capture field is a reference type, it is capture-by-reference. if (FD->getType()->isReferenceType()) - Escaped.insert(VD); + Escaped.insert(cast<VarDecl>(VD)); } } }; diff --git a/clang/lib/StaticAnalyzer/Checkers/DebugContainerModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/DebugContainerModeling.cpp index 47fd57c7db9b..832bb78c4803 100644 --- a/clang/lib/StaticAnalyzer/Checkers/DebugContainerModeling.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/DebugContainerModeling.cpp @@ -41,9 +41,9 @@ class DebugContainerModeling CheckerContext &) const; CallDescriptionMap<FnCheck> Callbacks = { - {{"clang_analyzer_container_begin", 1}, + {{{"clang_analyzer_container_begin"}, 1}, &DebugContainerModeling::analyzerContainerBegin}, - {{"clang_analyzer_container_end", 1}, + {{{"clang_analyzer_container_end"}, 1}, &DebugContainerModeling::analyzerContainerEnd}, }; diff --git a/clang/lib/StaticAnalyzer/Checkers/DebugIteratorModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/DebugIteratorModeling.cpp index 6add9a007a87..d05298b42c55 100644 --- a/clang/lib/StaticAnalyzer/Checkers/DebugIteratorModeling.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/DebugIteratorModeling.cpp @@ -42,11 +42,11 @@ class DebugIteratorModeling CheckerContext &) const; CallDescriptionMap<FnCheck> Callbacks = { - {{"clang_analyzer_iterator_position", 1}, + {{{"clang_analyzer_iterator_position"}, 1}, &DebugIteratorModeling::analyzerIteratorPosition}, - {{"clang_analyzer_iterator_container", 1}, + {{{"clang_analyzer_iterator_container"}, 1}, &DebugIteratorModeling::analyzerIteratorContainer}, - {{"clang_analyzer_iterator_validity", 1}, + {{{"clang_analyzer_iterator_validity"}, 1}, &DebugIteratorModeling::analyzerIteratorValidity}, }; diff --git a/clang/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp index bfd1d4c162ec..cc01e97d3fa2 100644 --- a/clang/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp @@ -17,6 +17,7 @@ #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include <optional> using namespace clang; using namespace ento; @@ -67,7 +68,7 @@ void DivZeroChecker::checkPreStmt(const BinaryOperator *B, return; SVal Denom = C.getSVal(B->getRHS()); - Optional<DefinedSVal> DV = Denom.getAs<DefinedSVal>(); + std::optional<DefinedSVal> DV = Denom.getAs<DefinedSVal>(); // Divide-by-undefined handled in the generic checking for uses of // undefined values. diff --git a/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp b/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp index aaf8cca32b60..6f26842e62c7 100644 --- a/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp @@ -31,6 +31,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" +#include <optional> using namespace clang; using namespace ento; @@ -846,7 +847,7 @@ void DynamicTypePropagation::checkPreObjCMessage(const ObjCMethodCall &M, return; } - Optional<ArrayRef<QualType>> TypeArgs = + std::optional<ArrayRef<QualType>> TypeArgs = (*TrackedType)->getObjCSubstitutions(Method->getDeclContext()); // This case might happen when there is an unspecialized override of a // specialized method. @@ -979,7 +980,7 @@ void DynamicTypePropagation::checkPostObjCMessage(const ObjCMethodCall &M, if (!Method) return; - Optional<ArrayRef<QualType>> TypeArgs = + std::optional<ArrayRef<QualType>> TypeArgs = (*TrackedType)->getObjCSubstitutions(Method->getDeclContext()); if (!TypeArgs) return; diff --git a/clang/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp index e5088fb266bc..1077ceb6288e 100644 --- a/clang/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp @@ -22,6 +22,7 @@ #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include <optional> using namespace clang; using namespace ento; @@ -108,7 +109,7 @@ void EnumCastOutOfRangeChecker::checkPreStmt(const CastExpr *CE, } // Get the value of the expression to cast. - const llvm::Optional<DefinedOrUnknownSVal> ValueToCast = + const std::optional<DefinedOrUnknownSVal> ValueToCast = C.getSVal(CE->getSubExpr()).getAs<DefinedOrUnknownSVal>(); // If the value cannot be reasoned about (not even a DefinedOrUnknownSVal), diff --git a/clang/lib/StaticAnalyzer/Checkers/ErrnoChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/ErrnoChecker.cpp index d8c3d7a4a6a6..265185e64107 100644 --- a/clang/lib/StaticAnalyzer/Checkers/ErrnoChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/ErrnoChecker.cpp @@ -22,6 +22,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" #include "llvm/ADT/STLExtras.h" +#include <optional> using namespace clang; using namespace ento; @@ -132,7 +133,7 @@ void ErrnoChecker::generateErrnoNotCheckedBug( void ErrnoChecker::checkLocation(SVal Loc, bool IsLoad, const Stmt *S, CheckerContext &C) const { - Optional<ento::Loc> ErrnoLoc = getErrnoLoc(C.getState()); + std::optional<ento::Loc> ErrnoLoc = getErrnoLoc(C.getState()); if (!ErrnoLoc) return; @@ -206,7 +207,7 @@ void ErrnoChecker::checkPreCall(const CallEvent &Call, C.getSourceManager().isInSystemHeader(CallF->getLocation()) && !isErrno(CallF)) { if (getErrnoState(C.getState()) == MustBeChecked) { - Optional<ento::Loc> ErrnoLoc = getErrnoLoc(C.getState()); + std::optional<ento::Loc> ErrnoLoc = getErrnoLoc(C.getState()); assert(ErrnoLoc && "ErrnoLoc should exist if an errno state is set."); generateErrnoNotCheckedBug(C, setErrnoStateIrrelevant(C.getState()), ErrnoLoc->getAsRegion(), &Call); @@ -219,7 +220,7 @@ ProgramStateRef ErrnoChecker::checkRegionChanges( ArrayRef<const MemRegion *> ExplicitRegions, ArrayRef<const MemRegion *> Regions, const LocationContext *LCtx, const CallEvent *Call) const { - Optional<ento::Loc> ErrnoLoc = getErrnoLoc(State); + std::optional<ento::Loc> ErrnoLoc = getErrnoLoc(State); if (!ErrnoLoc) return State; const MemRegion *ErrnoRegion = ErrnoLoc->getAsRegion(); @@ -227,12 +228,12 @@ ProgramStateRef ErrnoChecker::checkRegionChanges( // If 'errno' is invalidated we can not know if it is checked or written into, // allow read and write without bug reports. if (llvm::is_contained(Regions, ErrnoRegion)) - return setErrnoStateIrrelevant(State); + return clearErrnoState(State); // Always reset errno state when the system memory space is invalidated. // The ErrnoRegion is not always found in the list in this case. if (llvm::is_contained(Regions, ErrnoRegion->getMemorySpace())) - return setErrnoStateIrrelevant(State); + return clearErrnoState(State); return State; } diff --git a/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.cpp index 618f7e97f6e8..51f39c606d5c 100644 --- a/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.cpp @@ -28,6 +28,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" #include "llvm/ADT/STLExtras.h" +#include <optional> using namespace clang; using namespace ento; @@ -57,11 +58,11 @@ public: private: // FIXME: Names from `ErrnoLocationFuncNames` are used to build this set. - CallDescriptionSet ErrnoLocationCalls{{"__errno_location", 0, 0}, - {"___errno", 0, 0}, - {"__errno", 0, 0}, - {"_errno", 0, 0}, - {"__error", 0, 0}}; + CallDescriptionSet ErrnoLocationCalls{{{"__errno_location"}, 0, 0}, + {{"___errno"}, 0, 0}, + {{"__errno"}, 0, 0}, + {{"_errno"}, 0, 0}, + {{"__error"}, 0, 0}}; }; } // namespace @@ -207,7 +208,7 @@ namespace clang { namespace ento { namespace errno_modeling { -Optional<SVal> getErrnoValue(ProgramStateRef State) { +std::optional<SVal> getErrnoValue(ProgramStateRef State) { const MemRegion *ErrnoR = State->get<ErrnoRegion>(); if (!ErrnoR) return {}; @@ -239,19 +240,23 @@ ProgramStateRef setErrnoValue(ProgramStateRef State, CheckerContext &C, return State->set<ErrnoState>(EState); } -Optional<Loc> getErrnoLoc(ProgramStateRef State) { +std::optional<Loc> getErrnoLoc(ProgramStateRef State) { const MemRegion *ErrnoR = State->get<ErrnoRegion>(); if (!ErrnoR) return {}; return loc::MemRegionVal{ErrnoR}; } +ErrnoCheckState getErrnoState(ProgramStateRef State) { + return State->get<ErrnoState>(); +} + ProgramStateRef setErrnoState(ProgramStateRef State, ErrnoCheckState EState) { return State->set<ErrnoState>(EState); } -ErrnoCheckState getErrnoState(ProgramStateRef State) { - return State->get<ErrnoState>(); +ProgramStateRef clearErrnoState(ProgramStateRef State) { + return setErrnoState(State, Irrelevant); } bool isErrno(const Decl *D) { @@ -264,6 +269,12 @@ bool isErrno(const Decl *D) { return false; } +const char *describeErrnoCheckState(ErrnoCheckState CS) { + assert(CS == errno_modeling::MustNotBeChecked && + "Errno description not applicable."); + return "may be undefined after the call and should not be used"; +} + const NoteTag *getErrnoNoteTag(CheckerContext &C, const std::string &Message) { return C.getNoteTag([Message](PathSensitiveBugReport &BR) -> std::string { const MemRegion *ErrnoR = BR.getErrorNode()->getState()->get<ErrnoRegion>(); @@ -275,6 +286,53 @@ const NoteTag *getErrnoNoteTag(CheckerContext &C, const std::string &Message) { }); } +ProgramStateRef setErrnoForStdSuccess(ProgramStateRef State, + CheckerContext &C) { + return setErrnoState(State, MustNotBeChecked); +} + +ProgramStateRef setErrnoForStdFailure(ProgramStateRef State, CheckerContext &C, + NonLoc ErrnoSym) { + SValBuilder &SVB = C.getSValBuilder(); + NonLoc ZeroVal = SVB.makeZeroVal(C.getASTContext().IntTy).castAs<NonLoc>(); + DefinedOrUnknownSVal Cond = + SVB.evalBinOp(State, BO_NE, ErrnoSym, ZeroVal, SVB.getConditionType()) + .castAs<DefinedOrUnknownSVal>(); + State = State->assume(Cond, true); + if (!State) + return nullptr; + return setErrnoValue(State, C.getLocationContext(), ErrnoSym, Irrelevant); +} + +ProgramStateRef setErrnoStdMustBeChecked(ProgramStateRef State, + CheckerContext &C, + const Expr *InvalE) { + const MemRegion *ErrnoR = State->get<ErrnoRegion>(); + if (!ErrnoR) + return State; + State = State->invalidateRegions(ErrnoR, InvalE, C.blockCount(), + C.getLocationContext(), false); + if (!State) + return nullptr; + return setErrnoState(State, MustBeChecked); +} + +const NoteTag *getNoteTagForStdSuccess(CheckerContext &C, llvm::StringRef Fn) { + return getErrnoNoteTag( + C, (Twine("Assuming that function '") + Twine(Fn) + + Twine("' is successful, in this case the value 'errno' ") + + Twine(describeErrnoCheckState(MustNotBeChecked))) + .str()); +} + +const NoteTag *getNoteTagForStdMustBeChecked(CheckerContext &C, + llvm::StringRef Fn) { + return getErrnoNoteTag( + C, (Twine("Function '") + Twine(Fn) + + Twine("' indicates failure only by setting of 'errno'")) + .str()); +} + } // namespace errno_modeling } // namespace ento } // namespace clang diff --git a/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.h b/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.h index 3757e25e1afe..2ca3979944e3 100644 --- a/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.h +++ b/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.h @@ -16,35 +16,43 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include <optional> namespace clang { namespace ento { namespace errno_modeling { +/// Describe how reads and writes of \c errno are handled by the checker. enum ErrnoCheckState : unsigned { /// We do not know anything about 'errno'. + /// Read and write is always allowed. Irrelevant = 0, /// Value of 'errno' should be checked to find out if a previous function call /// has failed. + /// When this state is set \c errno must be read by the program before a next + /// standard function call or other overwrite of \c errno follows, otherwise + /// a bug report is emitted. MustBeChecked = 1, /// Value of 'errno' is not allowed to be read, it can contain an unspecified /// value. + /// When this state is set \c errno is not allowed to be read by the program + /// until it is overwritten or invalidated. MustNotBeChecked = 2 }; /// Returns the value of 'errno', if 'errno' was found in the AST. -llvm::Optional<SVal> getErrnoValue(ProgramStateRef State); +std::optional<SVal> getErrnoValue(ProgramStateRef State); /// Returns the errno check state, \c Errno_Irrelevant if 'errno' was not found /// (this is not the only case for that value). ErrnoCheckState getErrnoState(ProgramStateRef State); /// Returns the location that points to the \c MemoryRegion where the 'errno' -/// value is stored. Returns \c None if 'errno' was not found. Otherwise it -/// always returns a valid memory region in the system global memory space. -llvm::Optional<Loc> getErrnoLoc(ProgramStateRef State); +/// value is stored. Returns \c std::nullopt if 'errno' was not found. Otherwise +/// it always returns a valid memory region in the system global memory space. +std::optional<Loc> getErrnoLoc(ProgramStateRef State); /// Set value of 'errno' to any SVal, if possible. /// The errno check state is set always when the 'errno' value is set. @@ -60,6 +68,9 @@ ProgramStateRef setErrnoValue(ProgramStateRef State, CheckerContext &C, /// Set the errno check state, do not modify the errno value. ProgramStateRef setErrnoState(ProgramStateRef State, ErrnoCheckState EState); +/// Clear state of errno (make it irrelevant). +ProgramStateRef clearErrnoState(ProgramStateRef State); + /// Determine if a `Decl` node related to 'errno'. /// This is true if the declaration is the errno variable or a function /// that returns a pointer to the 'errno' value (usually the 'errno' macro is @@ -67,10 +78,53 @@ ProgramStateRef setErrnoState(ProgramStateRef State, ErrnoCheckState EState); /// declaration. bool isErrno(const Decl *D); +/// Produce a textual description about how \c errno is allowed to be used +/// (in a \c ErrnoCheckState). +/// The returned string is insertable into a longer warning message in the form +/// "the value 'errno' <...>". +/// Currently only the \c errno_modeling::MustNotBeChecked state is supported, +/// others are not used by the clients. +const char *describeErrnoCheckState(ErrnoCheckState CS); + /// Create a NoteTag that displays the message if the 'errno' memory region is /// marked as interesting, and resets the interestingness. const NoteTag *getErrnoNoteTag(CheckerContext &C, const std::string &Message); +/// Set errno state for the common case when a standard function is successful. +/// Set \c ErrnoCheckState to \c MustNotBeChecked (the \c errno value is not +/// affected). At the state transition a note tag created by +/// \c getNoteTagForStdSuccess can be used. +ProgramStateRef setErrnoForStdSuccess(ProgramStateRef State, CheckerContext &C); + +/// Set errno state for the common case when a standard function fails. +/// Set \c errno value to be not equal to zero and \c ErrnoCheckState to +/// \c Irrelevant . The irrelevant errno state ensures that no related bug +/// report is emitted later and no note tag is needed. +/// \arg \c ErrnoSym Value to be used for \c errno and constrained to be +/// non-zero. +ProgramStateRef setErrnoForStdFailure(ProgramStateRef State, CheckerContext &C, + NonLoc ErrnoSym); + +/// Set errno state for the common case when a standard function indicates +/// failure only by \c errno. Sets \c ErrnoCheckState to \c MustBeChecked, and +/// invalidates the errno region (clear of previous value). +/// At the state transition a note tag created by +/// \c getNoteTagForStdMustBeChecked can be used. +/// \arg \c InvalE Expression that causes invalidation of \c errno. +ProgramStateRef setErrnoStdMustBeChecked(ProgramStateRef State, + CheckerContext &C, const Expr *InvalE); + +/// Generate the note tag that can be applied at the state generated by +/// \c setErrnoForStdSuccess . +/// \arg \c Fn Name of the (standard) function that is modeled. +const NoteTag *getNoteTagForStdSuccess(CheckerContext &C, llvm::StringRef Fn); + +/// Generate the note tag that can be applied at the state generated by +/// \c setErrnoStdMustBeChecked . +/// \arg \c Fn Name of the (standard) function that is modeled. +const NoteTag *getNoteTagForStdMustBeChecked(CheckerContext &C, + llvm::StringRef Fn); + } // namespace errno_modeling } // namespace ento } // namespace clang diff --git a/clang/lib/StaticAnalyzer/Checkers/ErrnoTesterChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/ErrnoTesterChecker.cpp index 924407ebe2d1..c46ebee0c94f 100644 --- a/clang/lib/StaticAnalyzer/Checkers/ErrnoTesterChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/ErrnoTesterChecker.cpp @@ -17,6 +17,7 @@ #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include <optional> using namespace clang; using namespace ento; @@ -69,13 +70,13 @@ private: using EvalFn = std::function<void(CheckerContext &, const CallEvent &)>; const CallDescriptionMap<EvalFn> TestCalls{ - {{"ErrnoTesterChecker_setErrno", 1}, &ErrnoTesterChecker::evalSetErrno}, - {{"ErrnoTesterChecker_getErrno", 0}, &ErrnoTesterChecker::evalGetErrno}, - {{"ErrnoTesterChecker_setErrnoIfError", 0}, + {{{"ErrnoTesterChecker_setErrno"}, 1}, &ErrnoTesterChecker::evalSetErrno}, + {{{"ErrnoTesterChecker_getErrno"}, 0}, &ErrnoTesterChecker::evalGetErrno}, + {{{"ErrnoTesterChecker_setErrnoIfError"}, 0}, &ErrnoTesterChecker::evalSetErrnoIfError}, - {{"ErrnoTesterChecker_setErrnoIfErrorRange", 0}, + {{{"ErrnoTesterChecker_setErrnoIfErrorRange"}, 0}, &ErrnoTesterChecker::evalSetErrnoIfErrorRange}, - {{"ErrnoTesterChecker_setErrnoCheckState", 0}, + {{{"ErrnoTesterChecker_setErrnoCheckState"}, 0}, &ErrnoTesterChecker::evalSetErrnoCheckState}}; }; @@ -91,7 +92,7 @@ void ErrnoTesterChecker::evalGetErrno(CheckerContext &C, const CallEvent &Call) { ProgramStateRef State = C.getState(); - Optional<SVal> ErrnoVal = getErrnoValue(State); + std::optional<SVal> ErrnoVal = getErrnoValue(State); assert(ErrnoVal && "Errno value should be available."); State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), *ErrnoVal); diff --git a/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp index ec1b0a70d7d3..355e9c2238a4 100644 --- a/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp @@ -17,6 +17,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/ScopedPrinter.h" +#include <optional> using namespace clang; using namespace ento; @@ -58,9 +59,9 @@ class ExprInspectionChecker // Optional parameter `ExprVal` for expression value to be marked interesting. ExplodedNode *reportBug(llvm::StringRef Msg, CheckerContext &C, - Optional<SVal> ExprVal = None) const; + std::optional<SVal> ExprVal = std::nullopt) const; ExplodedNode *reportBug(llvm::StringRef Msg, BugReporter &BR, ExplodedNode *N, - Optional<SVal> ExprVal = None) const; + std::optional<SVal> ExprVal = std::nullopt) const; template <typename T> void printAndReport(CheckerContext &C, T What) const; const Expr *getArgExpr(const CallExpr *CE, CheckerContext &C) const; @@ -115,7 +116,8 @@ bool ExprInspectionChecker::evalCall(const CallEvent &Call, .Case("clang_analyzer_hashDump", &ExprInspectionChecker::analyzerHashDump) .Case("clang_analyzer_denote", &ExprInspectionChecker::analyzerDenote) - .Case("clang_analyzer_express", + .Case("clang_analyzer_express", // This also marks the argument as + // interesting. &ExprInspectionChecker::analyzerExpress) .StartsWith("clang_analyzer_isTainted", &ExprInspectionChecker::analyzerIsTainted) @@ -160,17 +162,18 @@ static const char *getArgumentValueString(const CallExpr *CE, } } -ExplodedNode *ExprInspectionChecker::reportBug(llvm::StringRef Msg, - CheckerContext &C, - Optional<SVal> ExprVal) const { +ExplodedNode * +ExprInspectionChecker::reportBug(llvm::StringRef Msg, CheckerContext &C, + std::optional<SVal> ExprVal) const { ExplodedNode *N = C.generateNonFatalErrorNode(); reportBug(Msg, C.getBugReporter(), N, ExprVal); return N; } -ExplodedNode *ExprInspectionChecker::reportBug(llvm::StringRef Msg, - BugReporter &BR, ExplodedNode *N, - Optional<SVal> ExprVal) const { +ExplodedNode * +ExprInspectionChecker::reportBug(llvm::StringRef Msg, BugReporter &BR, + ExplodedNode *N, + std::optional<SVal> ExprVal) const { if (!N) return nullptr; @@ -354,8 +357,7 @@ void ExprInspectionChecker::analyzerDumpElementCount(const CallExpr *CE, if (const auto *TVR = MR->getAs<TypedValueRegion>()) { ElementTy = TVR->getValueType(); } else { - ElementTy = - MR->castAs<SymbolicRegion>()->getSymbol()->getType()->getPointeeType(); + ElementTy = MR->castAs<SymbolicRegion>()->getPointeeStaticType(); } assert(!ElementTy->isPointerType()); @@ -466,58 +468,60 @@ void ExprInspectionChecker::analyzerDenote(const CallExpr *CE, namespace { class SymbolExpressor - : public SymExprVisitor<SymbolExpressor, Optional<std::string>> { + : public SymExprVisitor<SymbolExpressor, std::optional<std::string>> { ProgramStateRef State; public: SymbolExpressor(ProgramStateRef State) : State(State) {} - Optional<std::string> lookup(const SymExpr *S) { + std::optional<std::string> lookup(const SymExpr *S) { if (const StringLiteral *const *SLPtr = State->get<DenotedSymbols>(S)) { const StringLiteral *SL = *SLPtr; return std::string(SL->getBytes()); } - return None; + return std::nullopt; } - Optional<std::string> VisitSymExpr(const SymExpr *S) { return lookup(S); } + std::optional<std::string> VisitSymExpr(const SymExpr *S) { + return lookup(S); + } - Optional<std::string> VisitSymIntExpr(const SymIntExpr *S) { - if (Optional<std::string> Str = lookup(S)) + std::optional<std::string> VisitSymIntExpr(const SymIntExpr *S) { + if (std::optional<std::string> Str = lookup(S)) return Str; - if (Optional<std::string> Str = Visit(S->getLHS())) + if (std::optional<std::string> Str = Visit(S->getLHS())) return (*Str + " " + BinaryOperator::getOpcodeStr(S->getOpcode()) + " " + std::to_string(S->getRHS().getLimitedValue()) + (S->getRHS().isUnsigned() ? "U" : "")) .str(); - return None; + return std::nullopt; } - Optional<std::string> VisitSymSymExpr(const SymSymExpr *S) { - if (Optional<std::string> Str = lookup(S)) + std::optional<std::string> VisitSymSymExpr(const SymSymExpr *S) { + if (std::optional<std::string> Str = lookup(S)) return Str; - if (Optional<std::string> Str1 = Visit(S->getLHS())) - if (Optional<std::string> Str2 = Visit(S->getRHS())) + if (std::optional<std::string> Str1 = Visit(S->getLHS())) + if (std::optional<std::string> Str2 = Visit(S->getRHS())) return (*Str1 + " " + BinaryOperator::getOpcodeStr(S->getOpcode()) + " " + *Str2) .str(); - return None; + return std::nullopt; } - Optional<std::string> VisitUnarySymExpr(const UnarySymExpr *S) { - if (Optional<std::string> Str = lookup(S)) + std::optional<std::string> VisitUnarySymExpr(const UnarySymExpr *S) { + if (std::optional<std::string> Str = lookup(S)) return Str; - if (Optional<std::string> Str = Visit(S->getOperand())) + if (std::optional<std::string> Str = Visit(S->getOperand())) return (UnaryOperator::getOpcodeStr(S->getOpcode()) + *Str).str(); - return None; + return std::nullopt; } - Optional<std::string> VisitSymbolCast(const SymbolCast *S) { - if (Optional<std::string> Str = lookup(S)) + std::optional<std::string> VisitSymbolCast(const SymbolCast *S) { + if (std::optional<std::string> Str = lookup(S)) return Str; - if (Optional<std::string> Str = Visit(S->getOperand())) + if (std::optional<std::string> Str = Visit(S->getOperand())) return (Twine("(") + S->getType().getAsString() + ")" + *Str).str(); - return None; + return std::nullopt; } }; } // namespace @@ -531,14 +535,14 @@ void ExprInspectionChecker::analyzerExpress(const CallExpr *CE, SVal ArgVal = C.getSVal(CE->getArg(0)); SymbolRef Sym = ArgVal.getAsSymbol(); if (!Sym) { - reportBug("Not a symbol", C); + reportBug("Not a symbol", C, ArgVal); return; } SymbolExpressor V(C.getState()); auto Str = V.Visit(Sym); if (!Str) { - reportBug("Unable to express", C); + reportBug("Unable to express", C, ArgVal); return; } diff --git a/clang/lib/StaticAnalyzer/Checkers/FuchsiaHandleChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/FuchsiaHandleChecker.cpp index eb3b89ee6c5c..65ff1be8ec05 100644 --- a/clang/lib/StaticAnalyzer/Checkers/FuchsiaHandleChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/FuchsiaHandleChecker.cpp @@ -101,6 +101,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" #include "llvm/ADT/StringExtras.h" +#include <optional> using namespace clang; using namespace ento; @@ -300,7 +301,7 @@ getFuchsiaHandleSymbols(QualType QT, SVal Arg, ProgramStateRef State) { } } else { assert(PtrToHandleLevel == 1); - if (Optional<Loc> ArgLoc = Arg.getAs<Loc>()) { + if (std::optional<Loc> ArgLoc = Arg.getAs<Loc>()) { SymbolRef Sym = State->getSVal(*ArgLoc).getAsSymbol(); if (Sym) { return {Sym}; diff --git a/clang/lib/StaticAnalyzer/Checkers/GTestChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/GTestChecker.cpp index dbfdff4d2a3b..43d2eeeb4b27 100644 --- a/clang/lib/StaticAnalyzer/Checkers/GTestChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/GTestChecker.cpp @@ -20,6 +20,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "llvm/Support/raw_ostream.h" +#include <optional> using namespace clang; using namespace ento; @@ -258,7 +259,7 @@ SVal GTestChecker::getAssertionResultSuccessFieldValue( if (!SuccessField) return UnknownVal(); - Optional<Loc> FieldLoc = + std::optional<Loc> FieldLoc = State->getLValue(SuccessField, Instance).getAs<Loc>(); if (!FieldLoc) return UnknownVal(); diff --git a/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp index f0a114801dda..f6e2f59d5697 100644 --- a/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp @@ -30,6 +30,7 @@ #include <limits> #include <memory> +#include <optional> #include <utility> #define DEBUG_TYPE "taint-checker" @@ -124,16 +125,17 @@ SVal getPointeeOf(const CheckerContext &C, Loc LValue) { } /// Given a pointer/reference argument, return the value it refers to. -Optional<SVal> getPointeeOf(const CheckerContext &C, SVal Arg) { +std::optional<SVal> getPointeeOf(const CheckerContext &C, SVal Arg) { if (auto LValue = Arg.getAs<Loc>()) return getPointeeOf(C, *LValue); - return None; + return std::nullopt; } /// Given a pointer, return the SVal of its pointee or if it is tainted, /// otherwise return the pointer's SVal if tainted. /// Also considers stdin as a taint source. -Optional<SVal> getTaintedPointeeOrPointer(const CheckerContext &C, SVal Arg) { +std::optional<SVal> getTaintedPointeeOrPointer(const CheckerContext &C, + SVal Arg) { const ProgramStateRef State = C.getState(); if (auto Pointee = getPointeeOf(C, Arg)) @@ -147,7 +149,7 @@ Optional<SVal> getTaintedPointeeOrPointer(const CheckerContext &C, SVal Arg) { if (isStdin(Arg, C.getASTContext())) return Arg; - return None; + return std::nullopt; } bool isTaintedOrPointsToTainted(const Expr *E, const ProgramStateRef &State, @@ -161,7 +163,8 @@ bool isTaintedOrPointsToTainted(const Expr *E, const ProgramStateRef &State, class ArgSet { public: ArgSet() = default; - ArgSet(ArgVecTy &&DiscreteArgs, Optional<ArgIdxTy> VariadicIndex = None) + ArgSet(ArgVecTy &&DiscreteArgs, + std::optional<ArgIdxTy> VariadicIndex = std::nullopt) : DiscreteArgs(std::move(DiscreteArgs)), VariadicIndex(std::move(VariadicIndex)) {} @@ -176,7 +179,7 @@ public: private: ArgVecTy DiscreteArgs; - Optional<ArgIdxTy> VariadicIndex; + std::optional<ArgIdxTy> VariadicIndex; }; /// A struct used to specify taint propagation rules for a function. @@ -197,12 +200,12 @@ class GenericTaintRule { ArgSet PropDstArgs; /// A message that explains why the call is sensitive to taint. - Optional<StringRef> SinkMsg; + std::optional<StringRef> SinkMsg; GenericTaintRule() = default; GenericTaintRule(ArgSet &&Sink, ArgSet &&Filter, ArgSet &&Src, ArgSet &&Dst, - Optional<StringRef> SinkMsg = None) + std::optional<StringRef> SinkMsg = std::nullopt) : SinkArgs(std::move(Sink)), FilterArgs(std::move(Filter)), PropSrcArgs(std::move(Src)), PropDstArgs(std::move(Dst)), SinkMsg(SinkMsg) {} @@ -211,7 +214,7 @@ public: /// Make a rule that reports a warning if taint reaches any of \p FilterArgs /// arguments. static GenericTaintRule Sink(ArgSet &&SinkArgs, - Optional<StringRef> Msg = None) { + std::optional<StringRef> Msg = std::nullopt) { return {std::move(SinkArgs), {}, {}, {}, Msg}; } @@ -232,9 +235,9 @@ public: } /// Make a rule that taints all PropDstArgs if any of PropSrcArgs is tainted. - static GenericTaintRule SinkProp(ArgSet &&SinkArgs, ArgSet &&SrcArgs, - ArgSet &&DstArgs, - Optional<StringRef> Msg = None) { + static GenericTaintRule + SinkProp(ArgSet &&SinkArgs, ArgSet &&SrcArgs, ArgSet &&DstArgs, + std::optional<StringRef> Msg = std::nullopt) { return { std::move(SinkArgs), {}, std::move(SrcArgs), std::move(DstArgs), Msg}; } @@ -302,7 +305,7 @@ struct GenericTaintRuleParser { TaintConfiguration &&Config) const; private: - using NamePartsTy = llvm::SmallVector<SmallString<32>, 2>; + using NamePartsTy = llvm::SmallVector<StringRef, 2>; /// Validate part of the configuration, which contains a list of argument /// indexes. @@ -359,8 +362,8 @@ private: // TODO: Remove separation to simplify matching logic once CallDescriptions // are more expressive. - mutable Optional<RuleLookupTy> StaticTaintRules; - mutable Optional<RuleLookupTy> DynamicTaintRules; + mutable std::optional<RuleLookupTy> StaticTaintRules; + mutable std::optional<RuleLookupTy> DynamicTaintRules; }; } // end of anonymous namespace @@ -442,10 +445,8 @@ GenericTaintRuleParser::parseNameParts(const Config &C) { if (!C.Scope.empty()) { // If the Scope argument contains multiple "::" parts, those are considered // namespace identifiers. - llvm::SmallVector<StringRef, 2> NSParts; - StringRef{C.Scope}.split(NSParts, "::", /*MaxSplit*/ -1, + StringRef{C.Scope}.split(NameParts, "::", /*MaxSplit*/ -1, /*KeepEmpty*/ false); - NameParts.append(NSParts.begin(), NSParts.end()); } NameParts.emplace_back(C.Name); return NameParts; @@ -456,10 +457,7 @@ void GenericTaintRuleParser::consumeRulesFromConfig(const Config &C, GenericTaintRule &&Rule, RulesContTy &Rules) { NamePartsTy NameParts = parseNameParts(C); - llvm::SmallVector<const char *, 2> CallDescParts{NameParts.size()}; - llvm::transform(NameParts, CallDescParts.begin(), - [](SmallString<32> &S) { return S.c_str(); }); - Rules.emplace_back(CallDescription(CallDescParts), std::move(Rule)); + Rules.emplace_back(CallDescription(NameParts), std::move(Rule)); } void GenericTaintRuleParser::parseConfig(const std::string &Option, @@ -485,10 +483,12 @@ void GenericTaintRuleParser::parseConfig(const std::string &Option, validateArgVector(Option, P.DstArgs); bool IsSrcVariadic = P.VarType == TaintConfiguration::VariadicType::Src; bool IsDstVariadic = P.VarType == TaintConfiguration::VariadicType::Dst; - Optional<ArgIdxTy> JustVarIndex = P.VarIndex; + std::optional<ArgIdxTy> JustVarIndex = P.VarIndex; - ArgSet SrcDesc(std::move(P.SrcArgs), IsSrcVariadic ? JustVarIndex : None); - ArgSet DstDesc(std::move(P.DstArgs), IsDstVariadic ? JustVarIndex : None); + ArgSet SrcDesc(std::move(P.SrcArgs), + IsSrcVariadic ? JustVarIndex : std::nullopt); + ArgSet DstDesc(std::move(P.DstArgs), + IsDstVariadic ? JustVarIndex : std::nullopt); consumeRulesFromConfig( P, GenericTaintRule::Prop(std::move(SrcDesc), std::move(DstDesc)), Rules); @@ -527,128 +527,128 @@ void GenericTaintChecker::initTaintRules(CheckerContext &C) const { RulesConstructionTy GlobalCRules{ // Sources - {{"fdopen"}, TR::Source({{ReturnValueIndex}})}, - {{"fopen"}, TR::Source({{ReturnValueIndex}})}, - {{"freopen"}, TR::Source({{ReturnValueIndex}})}, - {{"getch"}, TR::Source({{ReturnValueIndex}})}, - {{"getchar"}, TR::Source({{ReturnValueIndex}})}, - {{"getchar_unlocked"}, TR::Source({{ReturnValueIndex}})}, - {{"gets"}, TR::Source({{0}, ReturnValueIndex})}, - {{"gets_s"}, TR::Source({{0}, ReturnValueIndex})}, - {{"scanf"}, TR::Source({{}, 1})}, - {{"scanf_s"}, TR::Source({{}, {1}})}, - {{"wgetch"}, TR::Source({{}, ReturnValueIndex})}, + {{{"fdopen"}}, TR::Source({{ReturnValueIndex}})}, + {{{"fopen"}}, TR::Source({{ReturnValueIndex}})}, + {{{"freopen"}}, TR::Source({{ReturnValueIndex}})}, + {{{"getch"}}, TR::Source({{ReturnValueIndex}})}, + {{{"getchar"}}, TR::Source({{ReturnValueIndex}})}, + {{{"getchar_unlocked"}}, TR::Source({{ReturnValueIndex}})}, + {{{"gets"}}, TR::Source({{0}, ReturnValueIndex})}, + {{{"gets_s"}}, TR::Source({{0}, ReturnValueIndex})}, + {{{"scanf"}}, TR::Source({{}, 1})}, + {{{"scanf_s"}}, TR::Source({{}, {1}})}, + {{{"wgetch"}}, TR::Source({{}, ReturnValueIndex})}, // Sometimes the line between taint sources and propagators is blurry. // _IO_getc is choosen to be a source, but could also be a propagator. // This way it is simpler, as modeling it as a propagator would require // to model the possible sources of _IO_FILE * values, which the _IO_getc // function takes as parameters. - {{"_IO_getc"}, TR::Source({{ReturnValueIndex}})}, - {{"getcwd"}, TR::Source({{0, ReturnValueIndex}})}, - {{"getwd"}, TR::Source({{0, ReturnValueIndex}})}, - {{"readlink"}, TR::Source({{1, ReturnValueIndex}})}, - {{"readlinkat"}, TR::Source({{2, ReturnValueIndex}})}, - {{"get_current_dir_name"}, TR::Source({{ReturnValueIndex}})}, - {{"gethostname"}, TR::Source({{0}})}, - {{"getnameinfo"}, TR::Source({{2, 4}})}, - {{"getseuserbyname"}, TR::Source({{1, 2}})}, - {{"getgroups"}, TR::Source({{1, ReturnValueIndex}})}, - {{"getlogin"}, TR::Source({{ReturnValueIndex}})}, - {{"getlogin_r"}, TR::Source({{0}})}, + {{{"_IO_getc"}}, TR::Source({{ReturnValueIndex}})}, + {{{"getcwd"}}, TR::Source({{0, ReturnValueIndex}})}, + {{{"getwd"}}, TR::Source({{0, ReturnValueIndex}})}, + {{{"readlink"}}, TR::Source({{1, ReturnValueIndex}})}, + {{{"readlinkat"}}, TR::Source({{2, ReturnValueIndex}})}, + {{{"get_current_dir_name"}}, TR::Source({{ReturnValueIndex}})}, + {{{"gethostname"}}, TR::Source({{0}})}, + {{{"getnameinfo"}}, TR::Source({{2, 4}})}, + {{{"getseuserbyname"}}, TR::Source({{1, 2}})}, + {{{"getgroups"}}, TR::Source({{1, ReturnValueIndex}})}, + {{{"getlogin"}}, TR::Source({{ReturnValueIndex}})}, + {{{"getlogin_r"}}, TR::Source({{0}})}, // Props - {{"atoi"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"atol"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"atoll"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"fgetc"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"fgetln"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"fgets"}, TR::Prop({{2}}, {{0, ReturnValueIndex}})}, - {{"fscanf"}, TR::Prop({{0}}, {{}, 2})}, - {{"fscanf_s"}, TR::Prop({{0}}, {{}, {2}})}, - {{"sscanf"}, TR::Prop({{0}}, {{}, 2})}, - - {{"getc"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"getc_unlocked"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"getdelim"}, TR::Prop({{3}}, {{0}})}, - {{"getline"}, TR::Prop({{2}}, {{0}})}, - {{"getw"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"pread"}, TR::Prop({{0, 1, 2, 3}}, {{1, ReturnValueIndex}})}, - {{"read"}, TR::Prop({{0, 2}}, {{1, ReturnValueIndex}})}, - {{"strchr"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"strrchr"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"tolower"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"toupper"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"fread"}, TR::Prop({{3}}, {{0, ReturnValueIndex}})}, - {{"recv"}, TR::Prop({{0}}, {{1, ReturnValueIndex}})}, - {{"recvfrom"}, TR::Prop({{0}}, {{1, ReturnValueIndex}})}, - - {{"ttyname"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"ttyname_r"}, TR::Prop({{0}}, {{1, ReturnValueIndex}})}, - - {{"basename"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"dirname"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"fnmatch"}, TR::Prop({{1}}, {{ReturnValueIndex}})}, - {{"memchr"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"memrchr"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"rawmemchr"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - - {{"mbtowc"}, TR::Prop({{1}}, {{0, ReturnValueIndex}})}, - {{"wctomb"}, TR::Prop({{1}}, {{0, ReturnValueIndex}})}, - {{"wcwidth"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - - {{"memcmp"}, TR::Prop({{0, 1}}, {{ReturnValueIndex}})}, - {{"memcpy"}, TR::Prop({{1}}, {{0, ReturnValueIndex}})}, - {{"memmove"}, TR::Prop({{1}}, {{0, ReturnValueIndex}})}, + {{{"atoi"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"atol"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"atoll"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"fgetc"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"fgetln"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"fgets"}}, TR::Prop({{2}}, {{0, ReturnValueIndex}})}, + {{{"fscanf"}}, TR::Prop({{0}}, {{}, 2})}, + {{{"fscanf_s"}}, TR::Prop({{0}}, {{}, {2}})}, + {{{"sscanf"}}, TR::Prop({{0}}, {{}, 2})}, + + {{{"getc"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"getc_unlocked"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"getdelim"}}, TR::Prop({{3}}, {{0}})}, + {{{"getline"}}, TR::Prop({{2}}, {{0}})}, + {{{"getw"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"pread"}}, TR::Prop({{0, 1, 2, 3}}, {{1, ReturnValueIndex}})}, + {{{"read"}}, TR::Prop({{0, 2}}, {{1, ReturnValueIndex}})}, + {{{"strchr"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"strrchr"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"tolower"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"toupper"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"fread"}}, TR::Prop({{3}}, {{0, ReturnValueIndex}})}, + {{{"recv"}}, TR::Prop({{0}}, {{1, ReturnValueIndex}})}, + {{{"recvfrom"}}, TR::Prop({{0}}, {{1, ReturnValueIndex}})}, + + {{{"ttyname"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"ttyname_r"}}, TR::Prop({{0}}, {{1, ReturnValueIndex}})}, + + {{{"basename"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"dirname"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"fnmatch"}}, TR::Prop({{1}}, {{ReturnValueIndex}})}, + {{{"memchr"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"memrchr"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"rawmemchr"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + + {{{"mbtowc"}}, TR::Prop({{1}}, {{0, ReturnValueIndex}})}, + {{{"wctomb"}}, TR::Prop({{1}}, {{0, ReturnValueIndex}})}, + {{{"wcwidth"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + + {{{"memcmp"}}, TR::Prop({{0, 1}}, {{ReturnValueIndex}})}, + {{{"memcpy"}}, TR::Prop({{1}}, {{0, ReturnValueIndex}})}, + {{{"memmove"}}, TR::Prop({{1}}, {{0, ReturnValueIndex}})}, // If memmem was called with a tainted needle and the search was // successful, that would mean that the value pointed by the return value // has the same content as the needle. If we choose to go by the policy of // content equivalence implies taintedness equivalence, that would mean // haystack should be considered a propagation source argument. - {{"memmem"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"memmem"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, // The comment for memmem above also applies to strstr. - {{"strstr"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"strcasestr"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"strstr"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"strcasestr"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"strchrnul"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"strchrnul"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"index"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"rindex"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"index"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"rindex"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, // FIXME: In case of arrays, only the first element of the array gets // tainted. - {{"qsort"}, TR::Prop({{0}}, {{0}})}, - {{"qsort_r"}, TR::Prop({{0}}, {{0}})}, - - {{"strcmp"}, TR::Prop({{0, 1}}, {{ReturnValueIndex}})}, - {{"strcasecmp"}, TR::Prop({{0, 1}}, {{ReturnValueIndex}})}, - {{"strncmp"}, TR::Prop({{0, 1, 2}}, {{ReturnValueIndex}})}, - {{"strncasecmp"}, TR::Prop({{0, 1, 2}}, {{ReturnValueIndex}})}, - {{"strspn"}, TR::Prop({{0, 1}}, {{ReturnValueIndex}})}, - {{"strcspn"}, TR::Prop({{0, 1}}, {{ReturnValueIndex}})}, - {{"strpbrk"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"strndup"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"strndupa"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"strlen"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"strnlen"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"strtol"}, TR::Prop({{0}}, {{1, ReturnValueIndex}})}, - {{"strtoll"}, TR::Prop({{0}}, {{1, ReturnValueIndex}})}, - {{"strtoul"}, TR::Prop({{0}}, {{1, ReturnValueIndex}})}, - {{"strtoull"}, TR::Prop({{0}}, {{1, ReturnValueIndex}})}, - - {{"isalnum"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"isalpha"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"isascii"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"isblank"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"iscntrl"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"isdigit"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"isgraph"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"islower"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"isprint"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"ispunct"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"isspace"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"isupper"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{"isxdigit"}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"qsort"}}, TR::Prop({{0}}, {{0}})}, + {{{"qsort_r"}}, TR::Prop({{0}}, {{0}})}, + + {{{"strcmp"}}, TR::Prop({{0, 1}}, {{ReturnValueIndex}})}, + {{{"strcasecmp"}}, TR::Prop({{0, 1}}, {{ReturnValueIndex}})}, + {{{"strncmp"}}, TR::Prop({{0, 1, 2}}, {{ReturnValueIndex}})}, + {{{"strncasecmp"}}, TR::Prop({{0, 1, 2}}, {{ReturnValueIndex}})}, + {{{"strspn"}}, TR::Prop({{0, 1}}, {{ReturnValueIndex}})}, + {{{"strcspn"}}, TR::Prop({{0, 1}}, {{ReturnValueIndex}})}, + {{{"strpbrk"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"strndup"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"strndupa"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"strlen"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"strnlen"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"strtol"}}, TR::Prop({{0}}, {{1, ReturnValueIndex}})}, + {{{"strtoll"}}, TR::Prop({{0}}, {{1, ReturnValueIndex}})}, + {{{"strtoul"}}, TR::Prop({{0}}, {{1, ReturnValueIndex}})}, + {{{"strtoull"}}, TR::Prop({{0}}, {{1, ReturnValueIndex}})}, + + {{{"isalnum"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"isalpha"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"isascii"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"isblank"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"iscntrl"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"isdigit"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"isgraph"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"islower"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"isprint"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"ispunct"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"isspace"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"isupper"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"isxdigit"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, {{CDF_MaybeBuiltin, {BI.getName(Builtin::BIstrncat)}}, TR::Prop({{1, 2}}, {{0, ReturnValueIndex}})}, @@ -656,37 +656,40 @@ void GenericTaintChecker::initTaintRules(CheckerContext &C) const { TR::Prop({{1, 2}}, {{0}})}, {{CDF_MaybeBuiltin, {BI.getName(Builtin::BIstrlcat)}}, TR::Prop({{1, 2}}, {{0}})}, - {{CDF_MaybeBuiltin, {"snprintf"}}, + {{CDF_MaybeBuiltin, {{"snprintf"}}}, TR::Prop({{1}, 3}, {{0, ReturnValueIndex}})}, - {{CDF_MaybeBuiltin, {"sprintf"}}, + {{CDF_MaybeBuiltin, {{"sprintf"}}}, TR::Prop({{1}, 2}, {{0, ReturnValueIndex}})}, - {{CDF_MaybeBuiltin, {"strcpy"}}, + {{CDF_MaybeBuiltin, {{"strcpy"}}}, TR::Prop({{1}}, {{0, ReturnValueIndex}})}, - {{CDF_MaybeBuiltin, {"stpcpy"}}, + {{CDF_MaybeBuiltin, {{"stpcpy"}}}, TR::Prop({{1}}, {{0, ReturnValueIndex}})}, - {{CDF_MaybeBuiltin, {"strcat"}}, + {{CDF_MaybeBuiltin, {{"strcat"}}}, TR::Prop({{1}}, {{0, ReturnValueIndex}})}, - {{CDF_MaybeBuiltin, {"strdup"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{CDF_MaybeBuiltin, {"strdupa"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, - {{CDF_MaybeBuiltin, {"wcsdup"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{CDF_MaybeBuiltin, {{"strdup"}}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{CDF_MaybeBuiltin, {{"strdupa"}}}, + TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{CDF_MaybeBuiltin, {{"wcsdup"}}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, // Sinks - {{"system"}, TR::Sink({{0}}, MsgSanitizeSystemArgs)}, - {{"popen"}, TR::Sink({{0}}, MsgSanitizeSystemArgs)}, - {{"execl"}, TR::Sink({{0}}, MsgSanitizeSystemArgs)}, - {{"execle"}, TR::Sink({{0}}, MsgSanitizeSystemArgs)}, - {{"execlp"}, TR::Sink({{0}}, MsgSanitizeSystemArgs)}, - {{"execvp"}, TR::Sink({{0}}, MsgSanitizeSystemArgs)}, - {{"execvP"}, TR::Sink({{0}}, MsgSanitizeSystemArgs)}, - {{"execve"}, TR::Sink({{0}}, MsgSanitizeSystemArgs)}, - {{"dlopen"}, TR::Sink({{0}}, MsgSanitizeSystemArgs)}, - {{CDF_MaybeBuiltin, {"malloc"}}, TR::Sink({{0}}, MsgTaintedBufferSize)}, - {{CDF_MaybeBuiltin, {"calloc"}}, TR::Sink({{0}}, MsgTaintedBufferSize)}, - {{CDF_MaybeBuiltin, {"alloca"}}, TR::Sink({{0}}, MsgTaintedBufferSize)}, - {{CDF_MaybeBuiltin, {"memccpy"}}, TR::Sink({{3}}, MsgTaintedBufferSize)}, - {{CDF_MaybeBuiltin, {"realloc"}}, TR::Sink({{1}}, MsgTaintedBufferSize)}, - {{{"setproctitle"}}, TR::Sink({{0}, 1}, MsgUncontrolledFormatString)}, - {{{"setproctitle_fast"}}, + {{{"system"}}, TR::Sink({{0}}, MsgSanitizeSystemArgs)}, + {{{"popen"}}, TR::Sink({{0}}, MsgSanitizeSystemArgs)}, + {{{"execl"}}, TR::Sink({{0}}, MsgSanitizeSystemArgs)}, + {{{"execle"}}, TR::Sink({{0}}, MsgSanitizeSystemArgs)}, + {{{"execlp"}}, TR::Sink({{0}}, MsgSanitizeSystemArgs)}, + {{{"execvp"}}, TR::Sink({{0}}, MsgSanitizeSystemArgs)}, + {{{"execvP"}}, TR::Sink({{0}}, MsgSanitizeSystemArgs)}, + {{{"execve"}}, TR::Sink({{0}}, MsgSanitizeSystemArgs)}, + {{{"dlopen"}}, TR::Sink({{0}}, MsgSanitizeSystemArgs)}, + {{CDF_MaybeBuiltin, {{"malloc"}}}, TR::Sink({{0}}, MsgTaintedBufferSize)}, + {{CDF_MaybeBuiltin, {{"calloc"}}}, TR::Sink({{0}}, MsgTaintedBufferSize)}, + {{CDF_MaybeBuiltin, {{"alloca"}}}, TR::Sink({{0}}, MsgTaintedBufferSize)}, + {{CDF_MaybeBuiltin, {{"memccpy"}}}, + TR::Sink({{3}}, MsgTaintedBufferSize)}, + {{CDF_MaybeBuiltin, {{"realloc"}}}, + TR::Sink({{1}}, MsgTaintedBufferSize)}, + {{{{"setproctitle"}}}, TR::Sink({{0}, 1}, MsgUncontrolledFormatString)}, + {{{{"setproctitle_fast"}}}, TR::Sink({{0}, 1}, MsgUncontrolledFormatString)}, // SinkProps @@ -702,7 +705,7 @@ void GenericTaintChecker::initTaintRules(CheckerContext &C) const { {{CDF_MaybeBuiltin, {BI.getName(Builtin::BIstrndup)}}, TR::SinkProp({{1}}, {{0, 1}}, {{ReturnValueIndex}}, MsgTaintedBufferSize)}, - {{CDF_MaybeBuiltin, {"bcopy"}}, + {{CDF_MaybeBuiltin, {{"bcopy"}}}, TR::SinkProp({{2}}, {{0, 2}}, {{1}}, MsgTaintedBufferSize)}}; // `getenv` returns taint only in untrusted environments. @@ -710,7 +713,7 @@ void GenericTaintChecker::initTaintRules(CheckerContext &C) const { // void setproctitle_init(int argc, char *argv[], char *envp[]) GlobalCRules.push_back( {{{"setproctitle_init"}}, TR::Sink({{1, 2}}, MsgCustomSink)}); - GlobalCRules.push_back({{"getenv"}, TR::Source({{ReturnValueIndex}})}); + GlobalCRules.push_back({{{"getenv"}}, TR::Source({{ReturnValueIndex}})}); } StaticTaintRules.emplace(std::make_move_iterator(GlobalCRules.begin()), @@ -723,7 +726,7 @@ void GenericTaintChecker::initTaintRules(CheckerContext &C) const { std::string Option{"Config"}; StringRef ConfigFile = Mgr->getAnalyzerOptions().getCheckerStringOption(this, Option); - llvm::Optional<TaintConfiguration> Config = + std::optional<TaintConfiguration> Config = getConfiguration<TaintConfiguration>(*Mgr, this, Option, ConfigFile); if (!Config) { // We don't have external taint config, no parsing required. @@ -899,7 +902,7 @@ bool GenericTaintRule::UntrustedEnv(CheckerContext &C) { bool GenericTaintChecker::generateReportIfTainted(const Expr *E, StringRef Msg, CheckerContext &C) const { assert(E); - Optional<SVal> TaintedSVal{getTaintedPointeeOrPointer(C, C.getSVal(E))}; + std::optional<SVal> TaintedSVal{getTaintedPointeeOrPointer(C, C.getSVal(E))}; if (!TaintedSVal) return false; diff --git a/clang/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp index ce334b18386e..b3f2d7f4d268 100644 --- a/clang/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp @@ -379,8 +379,7 @@ visit(const ObjCImplementationDecl *ImplD) const { IvarToPropMapTy IvarToPopertyMap; ObjCInterfaceDecl::PropertyMap PropMap; - ObjCInterfaceDecl::PropertyDeclOrder PropOrder; - InterfaceD->collectPropertiesToImplement(PropMap, PropOrder); + InterfaceD->collectPropertiesToImplement(PropMap); for (ObjCInterfaceDecl::PropertyMap::iterator I = PropMap.begin(), E = PropMap.end(); I != E; ++I) { diff --git a/clang/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp index 9870746cbe0c..bca10ec96cea 100644 --- a/clang/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp @@ -29,6 +29,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" #include "llvm/Support/Unicode.h" +#include <optional> using namespace clang; using namespace ento; @@ -1004,7 +1005,7 @@ NonLocalizedStringBRVisitor::VisitNode(const ExplodedNode *Succ, if (Satisfied) return nullptr; - Optional<StmtPoint> Point = Succ->getLocation().getAs<StmtPoint>(); + std::optional<StmtPoint> Point = Succ->getLocation().getAs<StmtPoint>(); if (!Point) return nullptr; @@ -1141,7 +1142,7 @@ void EmptyLocalizationContextChecker::MethodCrawler::VisitObjCMessageExpr( SE = Mgr.getSourceManager().getSLocEntry(SLInfo.first); } - llvm::Optional<llvm::MemoryBufferRef> BF = + std::optional<llvm::MemoryBufferRef> BF = Mgr.getSourceManager().getBufferOrNone(SLInfo.first, SL); if (!BF) return; diff --git a/clang/lib/StaticAnalyzer/Checkers/MIGChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MIGChecker.cpp index 139bc0e99d78..153a0a51e980 100644 --- a/clang/lib/StaticAnalyzer/Checkers/MIGChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/MIGChecker.cpp @@ -30,6 +30,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include <optional> using namespace clang; using namespace ento; @@ -86,7 +87,7 @@ class MIGChecker : public Checker<check::PostCall, check::PreStmt<ReturnStmt>, #undef CALL }; - CallDescription OsRefRetain{"os_ref_retain", 1}; + CallDescription OsRefRetain{{"os_ref_retain"}, 1}; void checkReturnAux(const ReturnStmt *RS, CheckerContext &C) const; @@ -157,7 +158,7 @@ static bool isInMIGCall(CheckerContext &C) { const Decl *D = SFC->getDecl(); - if (Optional<AnyCall> AC = AnyCall::forDecl(D)) { + if (std::optional<AnyCall> AC = AnyCall::forDecl(D)) { // Even though there's a Sema warning when the return type of an annotated // function is not a kern_return_t, this warning isn't an error, so we need // an extra check here. diff --git a/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp index 635eb00e4ca9..c1b85ace3e2d 100644 --- a/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp @@ -21,6 +21,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/raw_ostream.h" +#include <optional> using namespace clang; using namespace ento; @@ -209,7 +210,7 @@ static SymbolRef getAsPointeeSymbol(const Expr *Expr, ProgramStateRef State = C.getState(); SVal ArgV = C.getSVal(Expr); - if (Optional<loc::MemRegionVal> X = ArgV.getAs<loc::MemRegionVal>()) { + if (std::optional<loc::MemRegionVal> X = ArgV.getAs<loc::MemRegionVal>()) { StoreManager& SM = C.getStoreManager(); SymbolRef sym = SM.getBinding(State->getStore(), *X).getAsLocSymbol(); if (sym) diff --git a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp index adedc9c30fad..f05cd9227b65 100644 --- a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -86,6 +86,7 @@ #include "llvm/Support/raw_ostream.h" #include <climits> #include <functional> +#include <optional> #include <utility> using namespace clang; @@ -220,10 +221,10 @@ static bool isReleased(SymbolRef Sym, CheckerContext &C); /// Update the RefState to reflect the new memory allocation. /// The optional \p RetVal parameter specifies the newly allocated pointer /// value; if unspecified, the value of expression \p E is used. -static ProgramStateRef MallocUpdateRefState(CheckerContext &C, const Expr *E, - ProgramStateRef State, - AllocationFamily Family, - Optional<SVal> RetVal = None); +static ProgramStateRef +MallocUpdateRefState(CheckerContext &C, const Expr *E, ProgramStateRef State, + AllocationFamily Family, + std::optional<SVal> RetVal = std::nullopt); //===----------------------------------------------------------------------===// // The modeling of memory reallocation. @@ -391,10 +392,10 @@ private: const CallEvent &Call, CheckerContext &C)>; const CallDescriptionMap<CheckFn> FreeingMemFnMap{ - {{"free", 1}, &MallocChecker::checkFree}, - {{"if_freenameindex", 1}, &MallocChecker::checkIfFreeNameIndex}, - {{"kfree", 1}, &MallocChecker::checkFree}, - {{"g_free", 1}, &MallocChecker::checkFree}, + {{{"free"}, 1}, &MallocChecker::checkFree}, + {{{"if_freenameindex"}, 1}, &MallocChecker::checkIfFreeNameIndex}, + {{{"kfree"}, 1}, &MallocChecker::checkFree}, + {{{"g_free"}, 1}, &MallocChecker::checkFree}, }; bool isFreeingCall(const CallEvent &Call) const; @@ -403,61 +404,60 @@ private: friend class NoOwnershipChangeVisitor; CallDescriptionMap<CheckFn> AllocatingMemFnMap{ - {{"alloca", 1}, &MallocChecker::checkAlloca}, - {{"_alloca", 1}, &MallocChecker::checkAlloca}, - {{"malloc", 1}, &MallocChecker::checkBasicAlloc}, - {{"malloc", 3}, &MallocChecker::checkKernelMalloc}, - {{"calloc", 2}, &MallocChecker::checkCalloc}, - {{"valloc", 1}, &MallocChecker::checkBasicAlloc}, - {{CDF_MaybeBuiltin, "strndup", 2}, &MallocChecker::checkStrdup}, - {{CDF_MaybeBuiltin, "strdup", 1}, &MallocChecker::checkStrdup}, - {{"_strdup", 1}, &MallocChecker::checkStrdup}, - {{"kmalloc", 2}, &MallocChecker::checkKernelMalloc}, - {{"if_nameindex", 1}, &MallocChecker::checkIfNameIndex}, - {{CDF_MaybeBuiltin, "wcsdup", 1}, &MallocChecker::checkStrdup}, - {{CDF_MaybeBuiltin, "_wcsdup", 1}, &MallocChecker::checkStrdup}, - {{"g_malloc", 1}, &MallocChecker::checkBasicAlloc}, - {{"g_malloc0", 1}, &MallocChecker::checkGMalloc0}, - {{"g_try_malloc", 1}, &MallocChecker::checkBasicAlloc}, - {{"g_try_malloc0", 1}, &MallocChecker::checkGMalloc0}, - {{"g_memdup", 2}, &MallocChecker::checkGMemdup}, - {{"g_malloc_n", 2}, &MallocChecker::checkGMallocN}, - {{"g_malloc0_n", 2}, &MallocChecker::checkGMallocN0}, - {{"g_try_malloc_n", 2}, &MallocChecker::checkGMallocN}, - {{"g_try_malloc0_n", 2}, &MallocChecker::checkGMallocN0}, + {{{"alloca"}, 1}, &MallocChecker::checkAlloca}, + {{{"_alloca"}, 1}, &MallocChecker::checkAlloca}, + {{{"malloc"}, 1}, &MallocChecker::checkBasicAlloc}, + {{{"malloc"}, 3}, &MallocChecker::checkKernelMalloc}, + {{{"calloc"}, 2}, &MallocChecker::checkCalloc}, + {{{"valloc"}, 1}, &MallocChecker::checkBasicAlloc}, + {{CDF_MaybeBuiltin, {"strndup"}, 2}, &MallocChecker::checkStrdup}, + {{CDF_MaybeBuiltin, {"strdup"}, 1}, &MallocChecker::checkStrdup}, + {{{"_strdup"}, 1}, &MallocChecker::checkStrdup}, + {{{"kmalloc"}, 2}, &MallocChecker::checkKernelMalloc}, + {{{"if_nameindex"}, 1}, &MallocChecker::checkIfNameIndex}, + {{CDF_MaybeBuiltin, {"wcsdup"}, 1}, &MallocChecker::checkStrdup}, + {{CDF_MaybeBuiltin, {"_wcsdup"}, 1}, &MallocChecker::checkStrdup}, + {{{"g_malloc"}, 1}, &MallocChecker::checkBasicAlloc}, + {{{"g_malloc0"}, 1}, &MallocChecker::checkGMalloc0}, + {{{"g_try_malloc"}, 1}, &MallocChecker::checkBasicAlloc}, + {{{"g_try_malloc0"}, 1}, &MallocChecker::checkGMalloc0}, + {{{"g_memdup"}, 2}, &MallocChecker::checkGMemdup}, + {{{"g_malloc_n"}, 2}, &MallocChecker::checkGMallocN}, + {{{"g_malloc0_n"}, 2}, &MallocChecker::checkGMallocN0}, + {{{"g_try_malloc_n"}, 2}, &MallocChecker::checkGMallocN}, + {{{"g_try_malloc0_n"}, 2}, &MallocChecker::checkGMallocN0}, }; CallDescriptionMap<CheckFn> ReallocatingMemFnMap{ - {{"realloc", 2}, + {{{"realloc"}, 2}, std::bind(&MallocChecker::checkRealloc, _1, _2, _3, false)}, - {{"reallocf", 2}, + {{{"reallocf"}, 2}, std::bind(&MallocChecker::checkRealloc, _1, _2, _3, true)}, - {{"g_realloc", 2}, + {{{"g_realloc"}, 2}, std::bind(&MallocChecker::checkRealloc, _1, _2, _3, false)}, - {{"g_try_realloc", 2}, + {{{"g_try_realloc"}, 2}, std::bind(&MallocChecker::checkRealloc, _1, _2, _3, false)}, - {{"g_realloc_n", 3}, &MallocChecker::checkReallocN}, - {{"g_try_realloc_n", 3}, &MallocChecker::checkReallocN}, + {{{"g_realloc_n"}, 3}, &MallocChecker::checkReallocN}, + {{{"g_try_realloc_n"}, 3}, &MallocChecker::checkReallocN}, }; bool isMemCall(const CallEvent &Call) const; // TODO: Remove mutable by moving the initializtaion to the registry function. - mutable Optional<uint64_t> KernelZeroFlagVal; + mutable std::optional<uint64_t> KernelZeroFlagVal; - using KernelZeroSizePtrValueTy = Optional<int>; + using KernelZeroSizePtrValueTy = std::optional<int>; /// Store the value of macro called `ZERO_SIZE_PTR`. /// The value is initialized at first use, before first use the outer /// Optional is empty, afterwards it contains another Optional that indicates /// if the macro value could be determined, and if yes the value itself. - mutable Optional<KernelZeroSizePtrValueTy> KernelZeroSizePtrValue; + mutable std::optional<KernelZeroSizePtrValueTy> KernelZeroSizePtrValue; /// Process C++ operator new()'s allocation, which is the part of C++ /// new-expression that goes before the constructor. - LLVM_NODISCARD - ProgramStateRef processNewAllocation(const CXXAllocatorCall &Call, - CheckerContext &C, - AllocationFamily Family) const; + [[nodiscard]] ProgramStateRef + processNewAllocation(const CXXAllocatorCall &Call, CheckerContext &C, + AllocationFamily Family) const; /// Perform a zero-allocation check. /// @@ -467,11 +467,10 @@ private: /// 0. /// \param [in] RetVal Specifies the newly allocated pointer value; /// if unspecified, the value of expression \p E is used. - LLVM_NODISCARD - static ProgramStateRef ProcessZeroAllocCheck(const CallEvent &Call, - const unsigned IndexOfSizeArg, - ProgramStateRef State, - Optional<SVal> RetVal = None); + [[nodiscard]] static ProgramStateRef + ProcessZeroAllocCheck(const CallEvent &Call, const unsigned IndexOfSizeArg, + ProgramStateRef State, + std::optional<SVal> RetVal = std::nullopt); /// Model functions with the ownership_returns attribute. /// @@ -489,10 +488,9 @@ private: /// \param [in] Att The ownership_returns attribute. /// \param [in] State The \c ProgramState right before allocation. /// \returns The ProgramState right after allocation. - LLVM_NODISCARD - ProgramStateRef MallocMemReturnsAttr(CheckerContext &C, const CallEvent &Call, - const OwnershipAttr *Att, - ProgramStateRef State) const; + [[nodiscard]] ProgramStateRef + MallocMemReturnsAttr(CheckerContext &C, const CallEvent &Call, + const OwnershipAttr *Att, ProgramStateRef State) const; /// Models memory allocation. /// @@ -503,11 +501,9 @@ private: /// malloc leaves it undefined. /// \param [in] State The \c ProgramState right before allocation. /// \returns The ProgramState right after allocation. - LLVM_NODISCARD - static ProgramStateRef MallocMemAux(CheckerContext &C, const CallEvent &Call, - const Expr *SizeEx, SVal Init, - ProgramStateRef State, - AllocationFamily Family); + [[nodiscard]] static ProgramStateRef + MallocMemAux(CheckerContext &C, const CallEvent &Call, const Expr *SizeEx, + SVal Init, ProgramStateRef State, AllocationFamily Family); /// Models memory allocation. /// @@ -518,16 +514,13 @@ private: /// malloc leaves it undefined. /// \param [in] State The \c ProgramState right before allocation. /// \returns The ProgramState right after allocation. - LLVM_NODISCARD - static ProgramStateRef MallocMemAux(CheckerContext &C, const CallEvent &Call, - SVal Size, SVal Init, - ProgramStateRef State, - AllocationFamily Family); + [[nodiscard]] static ProgramStateRef + MallocMemAux(CheckerContext &C, const CallEvent &Call, SVal Size, SVal Init, + ProgramStateRef State, AllocationFamily Family); // Check if this malloc() for special flags. At present that means M_ZERO or // __GFP_ZERO (in which case, treat it like calloc). - LLVM_NODISCARD - llvm::Optional<ProgramStateRef> + [[nodiscard]] std::optional<ProgramStateRef> performKernelMalloc(const CallEvent &Call, CheckerContext &C, const ProgramStateRef &State) const; @@ -548,10 +541,10 @@ private: /// \param [in] Att The ownership_takes or ownership_holds attribute. /// \param [in] State The \c ProgramState right before allocation. /// \returns The ProgramState right after deallocation. - LLVM_NODISCARD - ProgramStateRef FreeMemAttr(CheckerContext &C, const CallEvent &Call, - const OwnershipAttr *Att, - ProgramStateRef State) const; + [[nodiscard]] ProgramStateRef FreeMemAttr(CheckerContext &C, + const CallEvent &Call, + const OwnershipAttr *Att, + ProgramStateRef State) const; /// Models memory deallocation. /// @@ -572,12 +565,10 @@ private: /// \param [in] ReturnsNullOnFailure Whether the memory deallocation function /// we're modeling returns with Null on failure. /// \returns The ProgramState right after deallocation. - LLVM_NODISCARD - ProgramStateRef FreeMemAux(CheckerContext &C, const CallEvent &Call, - ProgramStateRef State, unsigned Num, bool Hold, - bool &IsKnownToBeAllocated, - AllocationFamily Family, - bool ReturnsNullOnFailure = false) const; + [[nodiscard]] ProgramStateRef + FreeMemAux(CheckerContext &C, const CallEvent &Call, ProgramStateRef State, + unsigned Num, bool Hold, bool &IsKnownToBeAllocated, + AllocationFamily Family, bool ReturnsNullOnFailure = false) const; /// Models memory deallocation. /// @@ -598,12 +589,10 @@ private: /// \param [in] ReturnsNullOnFailure Whether the memory deallocation function /// we're modeling returns with Null on failure. /// \returns The ProgramState right after deallocation. - LLVM_NODISCARD - ProgramStateRef FreeMemAux(CheckerContext &C, const Expr *ArgExpr, - const CallEvent &Call, ProgramStateRef State, - bool Hold, bool &IsKnownToBeAllocated, - AllocationFamily Family, - bool ReturnsNullOnFailure = false) const; + [[nodiscard]] ProgramStateRef + FreeMemAux(CheckerContext &C, const Expr *ArgExpr, const CallEvent &Call, + ProgramStateRef State, bool Hold, bool &IsKnownToBeAllocated, + AllocationFamily Family, bool ReturnsNullOnFailure = false) const; // TODO: Needs some refactoring, as all other deallocation modeling // functions are suffering from out parameters and messy code due to how @@ -618,29 +607,27 @@ private: /// \param [in] SuffixWithN Whether the reallocation function we're modeling /// has an '_n' suffix, such as g_realloc_n. /// \returns The ProgramState right after reallocation. - LLVM_NODISCARD - ProgramStateRef ReallocMemAux(CheckerContext &C, const CallEvent &Call, - bool ShouldFreeOnFail, ProgramStateRef State, - AllocationFamily Family, - bool SuffixWithN = false) const; + [[nodiscard]] ProgramStateRef + ReallocMemAux(CheckerContext &C, const CallEvent &Call, bool ShouldFreeOnFail, + ProgramStateRef State, AllocationFamily Family, + bool SuffixWithN = false) const; /// Evaluates the buffer size that needs to be allocated. /// /// \param [in] Blocks The amount of blocks that needs to be allocated. /// \param [in] BlockBytes The size of a block. /// \returns The symbolic value of \p Blocks * \p BlockBytes. - LLVM_NODISCARD - static SVal evalMulForBufferSize(CheckerContext &C, const Expr *Blocks, - const Expr *BlockBytes); + [[nodiscard]] static SVal evalMulForBufferSize(CheckerContext &C, + const Expr *Blocks, + const Expr *BlockBytes); /// Models zero initialized array allocation. /// /// \param [in] Call The expression that reallocated memory /// \param [in] State The \c ProgramState right before reallocation. /// \returns The ProgramState right after allocation. - LLVM_NODISCARD - static ProgramStateRef CallocMem(CheckerContext &C, const CallEvent &Call, - ProgramStateRef State); + [[nodiscard]] static ProgramStateRef + CallocMem(CheckerContext &C, const CallEvent &Call, ProgramStateRef State); /// See if deallocation happens in a suspicious context. If so, escape the /// pointers that otherwise would have been deallocated and return true. @@ -673,12 +660,11 @@ private: SymbolRef &EscapingSymbol) const; /// Implementation of the checkPointerEscape callbacks. - LLVM_NODISCARD - ProgramStateRef checkPointerEscapeAux(ProgramStateRef State, - const InvalidatedSymbols &Escaped, - const CallEvent *Call, - PointerEscapeKind Kind, - bool IsConstPointerEscape) const; + [[nodiscard]] ProgramStateRef + checkPointerEscapeAux(ProgramStateRef State, + const InvalidatedSymbols &Escaped, + const CallEvent *Call, PointerEscapeKind Kind, + bool IsConstPointerEscape) const; // Implementation of the checkPreStmt and checkEndFunction callbacks. void checkEscapeOnReturn(const ReturnStmt *S, CheckerContext &C) const; @@ -687,11 +673,11 @@ private: /// Tells if a given family/call/symbol is tracked by the current checker. /// Sets CheckKind to the kind of the checker responsible for this /// family/call/symbol. - Optional<CheckKind> getCheckIfTracked(AllocationFamily Family, - bool IsALeakCheck = false) const; + std::optional<CheckKind> getCheckIfTracked(AllocationFamily Family, + bool IsALeakCheck = false) const; - Optional<CheckKind> getCheckIfTracked(CheckerContext &C, SymbolRef Sym, - bool IsALeakCheck = false) const; + std::optional<CheckKind> getCheckIfTracked(CheckerContext &C, SymbolRef Sym, + bool IsALeakCheck = false) const; ///@} static bool SummarizeValue(raw_ostream &os, SVal V); static bool SummarizeRegion(raw_ostream &os, const MemRegion *MR); @@ -870,7 +856,7 @@ protected: // If a variable is dead (is not referenced directly or indirectly after // some point), it will be removed from the Store before the end of its // actual lifetime. - // This means that that if the ownership status didn't change, CurrOwners + // This means that if the ownership status didn't change, CurrOwners // must be a superset of, but not necessarily equal to ExitOwners. return !llvm::set_is_subset(ExitOwners, CurrOwners); } @@ -1132,7 +1118,7 @@ bool MallocChecker::isMemCall(const CallEvent &Call) const { return Func && Func->hasAttr<OwnershipAttr>(); } -llvm::Optional<ProgramStateRef> +std::optional<ProgramStateRef> MallocChecker::performKernelMalloc(const CallEvent &Call, CheckerContext &C, const ProgramStateRef &State) const { // 3-argument malloc(), as commonly used in {Free,Net,Open}BSD Kernels: @@ -1170,33 +1156,32 @@ MallocChecker::performKernelMalloc(const CallEvent &Call, CheckerContext &C, // Fall back to normal malloc behavior on platforms where we don't // know M_ZERO. - return None; + return std::nullopt; } // We treat the last argument as the flags argument, and callers fall-back to // normal malloc on a None return. This works for the FreeBSD kernel malloc // as well as Linux kmalloc. if (Call.getNumArgs() < 2) - return None; + return std::nullopt; const Expr *FlagsEx = Call.getArgExpr(Call.getNumArgs() - 1); const SVal V = C.getSVal(FlagsEx); if (!isa<NonLoc>(V)) { // The case where 'V' can be a location can only be due to a bad header, // so in this case bail out. - return None; + return std::nullopt; } NonLoc Flags = V.castAs<NonLoc>(); - NonLoc ZeroFlag = - C.getSValBuilder() - .makeIntVal(KernelZeroFlagVal.value(), FlagsEx->getType()) - .castAs<NonLoc>(); + NonLoc ZeroFlag = C.getSValBuilder() + .makeIntVal(*KernelZeroFlagVal, FlagsEx->getType()) + .castAs<NonLoc>(); SVal MaskedFlagsUC = C.getSValBuilder().evalBinOpNN(State, BO_And, Flags, ZeroFlag, FlagsEx->getType()); if (MaskedFlagsUC.isUnknownOrUndef()) - return None; + return std::nullopt; DefinedSVal MaskedFlags = MaskedFlagsUC.castAs<DefinedSVal>(); // Check if maskedFlags is non-zero. @@ -1210,7 +1195,7 @@ MallocChecker::performKernelMalloc(const CallEvent &Call, CheckerContext &C, AF_Malloc); } - return None; + return std::nullopt; } SVal MallocChecker::evalMulForBufferSize(CheckerContext &C, const Expr *Blocks, @@ -1236,10 +1221,10 @@ void MallocChecker::checkBasicAlloc(const CallEvent &Call, void MallocChecker::checkKernelMalloc(const CallEvent &Call, CheckerContext &C) const { ProgramStateRef State = C.getState(); - llvm::Optional<ProgramStateRef> MaybeState = + std::optional<ProgramStateRef> MaybeState = performKernelMalloc(Call, C, State); if (MaybeState) - State = MaybeState.value(); + State = *MaybeState; else State = MallocMemAux(C, Call, Call.getArgExpr(0), UndefinedVal(), State, AF_Malloc); @@ -1506,7 +1491,7 @@ void MallocChecker::checkPostCall(const CallEvent &Call, // Performs a 0-sized allocations check. ProgramStateRef MallocChecker::ProcessZeroAllocCheck( const CallEvent &Call, const unsigned IndexOfSizeArg, ProgramStateRef State, - Optional<SVal> RetVal) { + std::optional<SVal> RetVal) { if (!State) return nullptr; @@ -1658,7 +1643,7 @@ static bool isKnownDeallocObjCMethodName(const ObjCMethodCall &Call) { FirstSlot == "initWithCharactersNoCopy"; } -static Optional<bool> getFreeWhenDoneArg(const ObjCMethodCall &Call) { +static std::optional<bool> getFreeWhenDoneArg(const ObjCMethodCall &Call) { Selector S = Call.getSelector(); // FIXME: We should not rely on fully-constrained symbols being folded. @@ -1666,7 +1651,7 @@ static Optional<bool> getFreeWhenDoneArg(const ObjCMethodCall &Call) { if (S.getNameForSlot(i).equals("freeWhenDone")) return !Call.getArgSVal(i).isZeroConstant(); - return None; + return std::nullopt; } void MallocChecker::checkPostObjCMessage(const ObjCMethodCall &Call, @@ -1677,7 +1662,7 @@ void MallocChecker::checkPostObjCMessage(const ObjCMethodCall &Call, if (!isKnownDeallocObjCMethodName(Call)) return; - if (Optional<bool> FreeWhenDone = getFreeWhenDoneArg(Call)) + if (std::optional<bool> FreeWhenDone = getFreeWhenDoneArg(Call)) if (!*FreeWhenDone) return; @@ -1749,6 +1734,10 @@ ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C, // Fill the region with the initialization value. State = State->bindDefaultInitial(RetVal, Init, LCtx); + // If Size is somehow undefined at this point, this line prevents a crash. + if (Size.isUndef()) + Size = UnknownVal(); + // Set the region's extent. State = setDynamicExtent(State, RetVal.getAsRegion(), Size.castAs<DefinedOrUnknownSVal>(), svalBuilder); @@ -1759,7 +1748,7 @@ ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C, static ProgramStateRef MallocUpdateRefState(CheckerContext &C, const Expr *E, ProgramStateRef State, AllocationFamily Family, - Optional<SVal> RetVal) { + std::optional<SVal> RetVal) { if (!State) return nullptr; @@ -2074,7 +2063,7 @@ ProgramStateRef MallocChecker::FreeMemAux( RefState::getReleased(Family, ParentExpr)); } -Optional<MallocChecker::CheckKind> +std::optional<MallocChecker::CheckKind> MallocChecker::getCheckIfTracked(AllocationFamily Family, bool IsALeakCheck) const { switch (Family) { @@ -2083,7 +2072,7 @@ MallocChecker::getCheckIfTracked(AllocationFamily Family, case AF_IfNameIndex: { if (ChecksEnabled[CK_MallocChecker]) return CK_MallocChecker; - return None; + return std::nullopt; } case AF_CXXNew: case AF_CXXNewArray: { @@ -2095,12 +2084,12 @@ MallocChecker::getCheckIfTracked(AllocationFamily Family, if (ChecksEnabled[CK_NewDeleteChecker]) return CK_NewDeleteChecker; } - return None; + return std::nullopt; } case AF_InnerBuffer: { if (ChecksEnabled[CK_InnerPointerChecker]) return CK_InnerPointerChecker; - return None; + return std::nullopt; } case AF_None: { llvm_unreachable("no family"); @@ -2109,7 +2098,7 @@ MallocChecker::getCheckIfTracked(AllocationFamily Family, llvm_unreachable("unhandled family"); } -Optional<MallocChecker::CheckKind> +std::optional<MallocChecker::CheckKind> MallocChecker::getCheckIfTracked(CheckerContext &C, SymbolRef Sym, bool IsALeakCheck) const { if (C.getState()->contains<ReallocSizeZeroSymbols>(Sym)) @@ -2121,11 +2110,13 @@ MallocChecker::getCheckIfTracked(CheckerContext &C, SymbolRef Sym, } bool MallocChecker::SummarizeValue(raw_ostream &os, SVal V) { - if (Optional<nonloc::ConcreteInt> IntVal = V.getAs<nonloc::ConcreteInt>()) + if (std::optional<nonloc::ConcreteInt> IntVal = + V.getAs<nonloc::ConcreteInt>()) os << "an integer (" << IntVal->getValue() << ")"; - else if (Optional<loc::ConcreteInt> ConstAddr = V.getAs<loc::ConcreteInt>()) + else if (std::optional<loc::ConcreteInt> ConstAddr = + V.getAs<loc::ConcreteInt>()) os << "a constant address (" << ConstAddr->getValue() << ")"; - else if (Optional<loc::GotoLabel> Label = V.getAs<loc::GotoLabel>()) + else if (std::optional<loc::GotoLabel> Label = V.getAs<loc::GotoLabel>()) os << "the address of the label '" << Label->getLabel()->getName() << "'"; else return false; @@ -2217,7 +2208,7 @@ void MallocChecker::HandleNonHeapDealloc(CheckerContext &C, SVal ArgVal, return; } - Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(Family); + std::optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(Family); if (!CheckKind) return; @@ -2258,7 +2249,7 @@ void MallocChecker::HandleNonHeapDealloc(CheckerContext &C, SVal ArgVal, void MallocChecker::HandleFreeAlloca(CheckerContext &C, SVal ArgVal, SourceRange Range) const { - Optional<MallocChecker::CheckKind> CheckKind; + std::optional<MallocChecker::CheckKind> CheckKind; if (ChecksEnabled[CK_MallocChecker]) CheckKind = CK_MallocChecker; @@ -2350,7 +2341,7 @@ void MallocChecker::HandleOffsetFree(CheckerContext &C, SVal ArgVal, return; } - Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(Family); + std::optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(Family); if (!CheckKind) return; @@ -2407,7 +2398,7 @@ void MallocChecker::HandleUseAfterFree(CheckerContext &C, SourceRange Range, return; } - Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym); + std::optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym); if (!CheckKind) return; @@ -2446,7 +2437,7 @@ void MallocChecker::HandleDoubleFree(CheckerContext &C, SourceRange Range, return; } - Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym); + std::optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym); if (!CheckKind) return; @@ -2476,7 +2467,7 @@ void MallocChecker::HandleDoubleDelete(CheckerContext &C, SymbolRef Sym) const { return; } - Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym); + std::optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym); if (!CheckKind) return; @@ -2503,7 +2494,7 @@ void MallocChecker::HandleUseZeroAlloc(CheckerContext &C, SourceRange Range, return; } - Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym); + std::optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym); if (!CheckKind) return; @@ -2536,7 +2527,7 @@ void MallocChecker::HandleFunctionPtrFree(CheckerContext &C, SVal ArgVal, return; } - Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(Family); + std::optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(Family); if (!CheckKind) return; @@ -2747,8 +2738,8 @@ void MallocChecker::HandleLeak(SymbolRef Sym, ExplodedNode *N, if (Family == AF_Alloca) return; - Optional<MallocChecker::CheckKind> - CheckKind = getCheckIfTracked(Family, true); + std::optional<MallocChecker::CheckKind> CheckKind = + getCheckIfTracked(Family, true); if (!CheckKind) return; @@ -3151,7 +3142,7 @@ bool MallocChecker::mayFreeAnyEscapedMemoryOrIsModeledExplicitly( // about, we can't be sure that the object will use free() to deallocate the // memory, so we can't model it explicitly. The best we can do is use it to // decide whether the pointer escapes. - if (Optional<bool> FreeWhenDone = getFreeWhenDoneArg(*Msg)) + if (std::optional<bool> FreeWhenDone = getFreeWhenDoneArg(*Msg)) return *FreeWhenDone; // If the first selector piece ends with "NoCopy", and there is no @@ -3570,7 +3561,8 @@ void MallocChecker::printState(raw_ostream &Out, ProgramStateRef State, for (RegionStateTy::iterator I = RS.begin(), E = RS.end(); I != E; ++I) { const RefState *RefS = State->get<RegionState>(I.getKey()); AllocationFamily Family = RefS->getAllocationFamily(); - Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(Family); + std::optional<MallocChecker::CheckKind> CheckKind = + getCheckIfTracked(Family); if (!CheckKind) CheckKind = getCheckIfTracked(Family, true); diff --git a/clang/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp index a6e8fcd425d5..5266df2ae6a6 100644 --- a/clang/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp @@ -24,6 +24,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" #include "llvm/ADT/APSInt.h" #include "llvm/ADT/SmallVector.h" +#include <optional> #include <utility> using namespace clang; @@ -308,26 +309,27 @@ void MallocOverflowSecurityChecker::checkASTCodeBody(const Decl *D, CFGBlock *block = *it; for (CFGBlock::iterator bi = block->begin(), be = block->end(); bi != be; ++bi) { - if (Optional<CFGStmt> CS = bi->getAs<CFGStmt>()) { - if (const CallExpr *TheCall = dyn_cast<CallExpr>(CS->getStmt())) { - // Get the callee. - const FunctionDecl *FD = TheCall->getDirectCallee(); - - if (!FD) - continue; - - // Get the name of the callee. If it's a builtin, strip off the prefix. - IdentifierInfo *FnInfo = FD->getIdentifier(); - if (!FnInfo) - continue; - - if (FnInfo->isStr ("malloc") || FnInfo->isStr ("_MALLOC")) { - if (TheCall->getNumArgs() == 1) - CheckMallocArgument(PossibleMallocOverflows, TheCall, - mgr.getASTContext()); + if (std::optional<CFGStmt> CS = bi->getAs<CFGStmt>()) { + if (const CallExpr *TheCall = dyn_cast<CallExpr>(CS->getStmt())) { + // Get the callee. + const FunctionDecl *FD = TheCall->getDirectCallee(); + + if (!FD) + continue; + + // Get the name of the callee. If it's a builtin, strip off the + // prefix. + IdentifierInfo *FnInfo = FD->getIdentifier(); + if (!FnInfo) + continue; + + if (FnInfo->isStr("malloc") || FnInfo->isStr("_MALLOC")) { + if (TheCall->getNumArgs() == 1) + CheckMallocArgument(PossibleMallocOverflows, TheCall, + mgr.getASTContext()); + } } } - } } } diff --git a/clang/lib/StaticAnalyzer/Checkers/MismatchedIteratorChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MismatchedIteratorChecker.cpp index 1960873599f7..2020dc7cc791 100644 --- a/clang/lib/StaticAnalyzer/Checkers/MismatchedIteratorChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/MismatchedIteratorChecker.cpp @@ -176,8 +176,10 @@ void MismatchedIteratorChecker::checkPreCall(const CallEvent &Call, const auto *Param = Func->getParamDecl(J); const auto *ParamType = Param->getType()->getAs<SubstTemplateTypeParmType>(); - if (!ParamType || - ParamType->getReplacedParameter()->getDecl() != TPDecl) + if (!ParamType) + continue; + const TemplateTypeParmDecl *D = ParamType->getReplacedParameter(); + if (D != TPDecl) continue; if (LHS.isUndef()) { LHS = Call.getArgSVal(J); diff --git a/clang/lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp index 1906ca5c8f55..0b3d635a50a3 100644 --- a/clang/lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp @@ -33,7 +33,7 @@ class MmapWriteExecChecker : public Checker<check::PreCall> { static int ProtRead; mutable std::unique_ptr<BugType> BT; public: - MmapWriteExecChecker() : MmapFn("mmap", 6), MprotectFn("mprotect", 3) {} + MmapWriteExecChecker() : MmapFn({"mmap"}, 6), MprotectFn({"mprotect"}, 3) {} void checkPreCall(const CallEvent &Call, CheckerContext &C) const; int ProtExecOv; int ProtReadOv; diff --git a/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp index e78c130a9c22..c8ddf3b2c14f 100644 --- a/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp @@ -309,7 +309,7 @@ MoveChecker::MovedBugVisitor::VisitNode(const ExplodedNode *N, // If it's not a dereference, we don't care if it was reset to null // or that it is even a smart pointer. - LLVM_FALLTHROUGH; + [[fallthrough]]; case SK_NonStd: case SK_Safe: OS << "Object"; @@ -587,7 +587,7 @@ void MoveChecker::explainObject(llvm::raw_ostream &OS, const MemRegion *MR, break; // We only care about the type if it's a dereference. - LLVM_FALLTHROUGH; + [[fallthrough]]; case SK_Unsafe: OS << " of type '" << RD->getQualifiedNameAsString() << "'"; break; @@ -618,10 +618,6 @@ void MoveChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { if (!IC) return; - // Calling a destructor on a moved object is fine. - if (isa<CXXDestructorCall>(IC)) - return; - const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion(); if (!ThisRegion) return; @@ -631,6 +627,10 @@ void MoveChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { if (!MethodDecl) return; + // Calling a destructor on a moved object is fine. + if (isa<CXXDestructorDecl>(MethodDecl)) + return; + // We want to investigate the whole object, not only sub-object of a parent // class in which the encountered method defined. ThisRegion = ThisRegion->getMostDerivedObjectRegion(); diff --git a/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp index fea35d03cb81..59741dde1eea 100644 --- a/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp @@ -24,6 +24,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/raw_ostream.h" +#include <optional> using namespace clang; using namespace ento; @@ -118,7 +119,7 @@ void CFErrorFunctionChecker::checkASTDecl(const FunctionDecl *D, II = &D->getASTContext().Idents.get("CFErrorRef"); bool hasCFError = false; - for (auto I : D->parameters()) { + for (auto *I : D->parameters()) { if (IsCFError(I->getType(), II)) { hasCFError = true; break; @@ -197,7 +198,7 @@ static void setFlag(ProgramStateRef state, SVal val, CheckerContext &C) { static QualType parameterTypeFromSVal(SVal val, CheckerContext &C) { const StackFrameContext * SFC = C.getStackFrame(); - if (Optional<loc::MemRegionVal> X = val.getAs<loc::MemRegionVal>()) { + if (std::optional<loc::MemRegionVal> X = val.getAs<loc::MemRegionVal>()) { const MemRegion* R = X->getRegion(); if (const VarRegion *VR = R->getAs<VarRegion>()) if (const StackArgumentsSpaceRegion * diff --git a/clang/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp index af208e867318..17c3cb4e9e04 100644 --- a/clang/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp @@ -44,9 +44,11 @@ void NoReturnFunctionChecker::checkPostCall(const CallEvent &CE, if (const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(CE.getDecl())) BuildSinks = FD->hasAttr<AnalyzerNoReturnAttr>() || FD->isNoReturn(); - const Expr *Callee = CE.getOriginExpr(); - if (!BuildSinks && Callee) - BuildSinks = getFunctionExtInfo(Callee->getType()).getNoReturn(); + if (const CallExpr *CExpr = dyn_cast_or_null<CallExpr>(CE.getOriginExpr()); + CExpr && !BuildSinks) { + if (const Expr *C = CExpr->getCallee()) + BuildSinks = getFunctionExtInfo(C->getType()).getNoReturn(); + } if (!BuildSinks && CE.isGlobalCFunction()) { if (const IdentifierInfo *II = CE.getCalleeIdentifier()) { diff --git a/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp index fb6afd0fdabc..fd47e19cb786 100644 --- a/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp @@ -136,10 +136,10 @@ void NonNullParamChecker::checkPreCall(const CallEvent &Call, if (!DV) continue; - assert(!HasRefTypeParam || isa<Loc>(DV.value())); + assert(!HasRefTypeParam || isa<Loc>(*DV)); // Process the case when the argument is not a location. - if (ExpectedToBeNonNull && !isa<Loc>(DV.value())) { + if (ExpectedToBeNonNull && !isa<Loc>(*DV)) { // If the argument is a union type, we want to handle a potential // transparent_union GCC extension. if (!ArgE) diff --git a/clang/lib/StaticAnalyzer/Checkers/NonnullGlobalConstantsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/NonnullGlobalConstantsChecker.cpp index c5437b16c688..72c6a869d225 100644 --- a/clang/lib/StaticAnalyzer/Checkers/NonnullGlobalConstantsChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/NonnullGlobalConstantsChecker.cpp @@ -26,6 +26,7 @@ #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" +#include <optional> using namespace clang; using namespace ento; @@ -77,7 +78,8 @@ void NonnullGlobalConstantsChecker::checkLocation(SVal location, bool isLoad, if (isGlobalConstString(location)) { SVal V = State->getSVal(location.castAs<Loc>()); - Optional<DefinedOrUnknownSVal> Constr = V.getAs<DefinedOrUnknownSVal>(); + std::optional<DefinedOrUnknownSVal> Constr = + V.getAs<DefinedOrUnknownSVal>(); if (Constr) { @@ -91,7 +93,7 @@ void NonnullGlobalConstantsChecker::checkLocation(SVal location, bool isLoad, /// \param V loaded lvalue. /// \return whether @c val is a string-like const global. bool NonnullGlobalConstantsChecker::isGlobalConstString(SVal V) const { - Optional<loc::MemRegionVal> RegionVal = V.getAs<loc::MemRegionVal>(); + std::optional<loc::MemRegionVal> RegionVal = V.getAs<loc::MemRegionVal>(); if (!RegionVal) return false; auto *Region = dyn_cast<VarRegion>(RegionVal->getAsRegion()); @@ -109,17 +111,20 @@ bool NonnullGlobalConstantsChecker::isGlobalConstString(SVal V) const { // Look through the typedefs. while (const Type *T = Ty.getTypePtr()) { - if (const auto *TT = dyn_cast<TypedefType>(T)) { + if (const auto *AT = dyn_cast<AttributedType>(T)) { + if (AT->getAttrKind() == attr::TypeNonNull) + return true; + Ty = AT->getModifiedType(); + } else if (const auto *ET = dyn_cast<ElaboratedType>(T)) { + const auto *TT = dyn_cast<TypedefType>(ET->getNamedType()); + if (!TT) + return false; Ty = TT->getDecl()->getUnderlyingType(); // It is sufficient for any intermediate typedef // to be classified const. HasConst = HasConst || Ty.isConstQualified(); if (isNonnullType(Ty) && HasConst) return true; - } else if (const auto *AT = dyn_cast<AttributedType>(T)) { - if (AT->getAttrKind() == attr::TypeNonNull) - return true; - Ty = AT->getModifiedType(); } else { return false; } @@ -136,7 +141,7 @@ bool NonnullGlobalConstantsChecker::isNonnullType(QualType Ty) const { if (auto *T = dyn_cast<ObjCObjectPointerType>(Ty)) { return T->getInterfaceDecl() && T->getInterfaceDecl()->getIdentifier() == NSStringII; - } else if (auto *T = dyn_cast<TypedefType>(Ty)) { + } else if (auto *T = Ty->getAs<TypedefType>()) { IdentifierInfo* II = T->getDecl()->getIdentifier(); return II == CFStringRefII || II == CFBooleanRefII || II == CFNullRefII; } diff --git a/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp index 1d8835f6b474..da8529f4ea81 100644 --- a/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp @@ -80,7 +80,7 @@ enum class ErrorKind : int { class NullabilityChecker : public Checker<check::Bind, check::PreCall, check::PreStmt<ReturnStmt>, check::PostCall, check::PostStmt<ExplicitCastExpr>, - check::PostObjCMessage, check::DeadSymbols, + check::PostObjCMessage, check::DeadSymbols, eval::Assume, check::Location, check::Event<ImplicitNullDerefEvent>> { public: @@ -102,6 +102,8 @@ public: void checkEvent(ImplicitNullDerefEvent Event) const; void checkLocation(SVal Location, bool IsLoad, const Stmt *S, 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 override; @@ -129,7 +131,7 @@ public: // When set to false no nullability information will be tracked in // NullabilityMap. It is possible to catch errors like passing a null pointer // to a callee that expects nonnull argument without the information that is - // stroed in the NullabilityMap. This is an optimization. + // stored in the NullabilityMap. This is an optimization. bool NeedTracking = false; private: @@ -230,10 +232,41 @@ bool operator==(NullabilityState Lhs, NullabilityState Rhs) { Lhs.getNullabilitySource() == Rhs.getNullabilitySource(); } +// For the purpose of tracking historical property accesses, the key for lookup +// is an object pointer (could be an instance or a class) paired with the unique +// identifier for the property being invoked on that object. +using ObjectPropPair = std::pair<const MemRegion *, const IdentifierInfo *>; + +// Metadata associated with the return value from a recorded property access. +struct ConstrainedPropertyVal { + // This will reference the conjured return SVal for some call + // of the form [object property] + DefinedOrUnknownSVal Value; + + // If the SVal has been determined to be nonnull, that is recorded here + bool isConstrainedNonnull; + + ConstrainedPropertyVal(DefinedOrUnknownSVal SV) + : Value(SV), isConstrainedNonnull(false) {} + + void Profile(llvm::FoldingSetNodeID &ID) const { + Value.Profile(ID); + ID.AddInteger(isConstrainedNonnull ? 1 : 0); + } +}; + +bool operator==(const ConstrainedPropertyVal &Lhs, + const ConstrainedPropertyVal &Rhs) { + return Lhs.Value == Rhs.Value && + Lhs.isConstrainedNonnull == Rhs.isConstrainedNonnull; +} + } // end anonymous namespace REGISTER_MAP_WITH_PROGRAMSTATE(NullabilityMap, const MemRegion *, NullabilityState) +REGISTER_MAP_WITH_PROGRAMSTATE(PropertyAccessesMap, ObjectPropPair, + ConstrainedPropertyVal) // We say "the nullability type invariant is violated" when a location with a // non-null type contains NULL or a function with a non-null return type returns @@ -285,8 +318,11 @@ NullabilityChecker::getTrackRegion(SVal Val, bool CheckSuperRegion) const { const MemRegion *Region = RegionSVal->getRegion(); if (CheckSuperRegion) { - if (auto FieldReg = Region->getAs<FieldRegion>()) + if (const SubRegion *FieldReg = Region->getAs<FieldRegion>()) { + if (const auto *ER = dyn_cast<ElementRegion>(FieldReg->getSuperRegion())) + FieldReg = ER; return dyn_cast<SymbolicRegion>(FieldReg->getSuperRegion()); + } if (auto ElementReg = Region->getAs<ElementRegion>()) return dyn_cast<SymbolicRegion>(ElementReg->getSuperRegion()); } @@ -464,6 +500,19 @@ void NullabilityChecker::checkDeadSymbols(SymbolReaper &SR, State = State->remove<NullabilityMap>(I->first); } } + + // When an object goes out of scope, we can free the history associated + // with any property accesses on that object + PropertyAccessesMapTy PropertyAccesses = State->get<PropertyAccessesMap>(); + for (PropertyAccessesMapTy::iterator I = PropertyAccesses.begin(), + E = PropertyAccesses.end(); + I != E; ++I) { + const MemRegion *ReceiverRegion = I->first.first; + if (!SR.isLiveRegion(ReceiverRegion)) { + State = State->remove<PropertyAccessesMap>(I->first); + } + } + // When one of the nonnull arguments are constrained to be null, nullability // preconditions are violated. It is not enough to check this only when we // actually report an error, because at that time interesting symbols might be @@ -851,6 +900,32 @@ static Nullability getReceiverNullability(const ObjCMethodCall &M, return Nullability::Unspecified; } +// The return value of a property access is typically a temporary value which +// will not be tracked in a persistent manner by the analyzer. We use +// evalAssume() in order to immediately record constraints on those temporaries +// at the time they are imposed (e.g. by a nil-check conditional). +ProgramStateRef NullabilityChecker::evalAssume(ProgramStateRef State, SVal Cond, + bool Assumption) const { + PropertyAccessesMapTy PropertyAccesses = State->get<PropertyAccessesMap>(); + for (PropertyAccessesMapTy::iterator I = PropertyAccesses.begin(), + E = PropertyAccesses.end(); + I != E; ++I) { + if (!I->second.isConstrainedNonnull) { + ConditionTruthVal IsNonNull = State->isNonNull(I->second.Value); + if (IsNonNull.isConstrainedTrue()) { + ConstrainedPropertyVal Replacement = I->second; + Replacement.isConstrainedNonnull = true; + State = State->set<PropertyAccessesMap>(I->first, Replacement); + } else if (IsNonNull.isConstrainedFalse()) { + // Space optimization: no point in tracking constrained-null cases + State = State->remove<PropertyAccessesMap>(I->first); + } + } + } + + return State; +} + /// Calculate the nullability of the result of a message expr based on the /// nullability of the receiver, the nullability of the return value, and the /// constraints. @@ -907,7 +982,7 @@ void NullabilityChecker::checkPostObjCMessage(const ObjCMethodCall &M, // this class of methods reduced the emitted diagnostics by about 30% on // some projects (and all of that was false positives). if (Name.contains("String")) { - for (auto Param : M.parameters()) { + for (auto *Param : M.parameters()) { if (Param->getName() == "encoding") { State = State->set<NullabilityMap>(ReturnRegion, Nullability::Contradicted); @@ -947,12 +1022,53 @@ void NullabilityChecker::checkPostObjCMessage(const ObjCMethodCall &M, // No tracked information. Use static type information for return value. Nullability RetNullability = getNullabilityAnnotation(RetType); - // Properties might be computed. For this reason the static analyzer creates a - // new symbol each time an unknown property is read. To avoid false pozitives - // do not treat unknown properties as nullable, even when they explicitly - // marked nullable. - if (M.getMessageKind() == OCM_PropertyAccess && !C.wasInlined) - RetNullability = Nullability::Nonnull; + // Properties might be computed, which means the property value could + // theoretically change between calls even in commonly-observed cases like + // this: + // + // if (foo.prop) { // ok, it's nonnull here... + // [bar doStuffWithNonnullVal:foo.prop]; // ...but what about + // here? + // } + // + // If the property is nullable-annotated, a naive analysis would lead to many + // false positives despite the presence of probably-correct nil-checks. To + // reduce the false positive rate, we maintain a history of the most recently + // observed property value. For each property access, if the prior value has + // been constrained to be not nil then we will conservatively assume that the + // next access can be inferred as nonnull. + if (RetNullability != Nullability::Nonnull && + M.getMessageKind() == OCM_PropertyAccess && !C.wasInlined) { + bool LookupResolved = false; + if (const MemRegion *ReceiverRegion = getTrackRegion(M.getReceiverSVal())) { + if (IdentifierInfo *Ident = M.getSelector().getIdentifierInfoForSlot(0)) { + LookupResolved = true; + ObjectPropPair Key = std::make_pair(ReceiverRegion, Ident); + const ConstrainedPropertyVal *PrevPropVal = + State->get<PropertyAccessesMap>(Key); + if (PrevPropVal && PrevPropVal->isConstrainedNonnull) { + RetNullability = Nullability::Nonnull; + } else { + // If a previous property access was constrained as nonnull, we hold + // on to that constraint (effectively inferring that all subsequent + // accesses on that code path can be inferred as nonnull). If the + // previous property access was *not* constrained as nonnull, then + // let's throw it away in favor of keeping the SVal associated with + // this more recent access. + if (auto ReturnSVal = + M.getReturnValue().getAs<DefinedOrUnknownSVal>()) { + State = State->set<PropertyAccessesMap>( + Key, ConstrainedPropertyVal(*ReturnSVal)); + } + } + } + } + + if (!LookupResolved) { + // Fallback: err on the side of suppressing the false positive. + RetNullability = Nullability::Nonnull; + } + } Nullability ComputedNullab = getMostNullable(RetNullability, SelfNullability); if (ComputedNullab == Nullability::Nullable) { diff --git a/clang/lib/StaticAnalyzer/Checkers/NumberObjectConversionChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/NumberObjectConversionChecker.cpp index 3e9fc696f8e6..f217520d8f4a 100644 --- a/clang/lib/StaticAnalyzer/Checkers/NumberObjectConversionChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/NumberObjectConversionChecker.cpp @@ -196,12 +196,10 @@ void NumberObjectConversionChecker::checkASTCodeBody(const Decl *D, AnalysisManager &AM, BugReporter &BR) const { // Currently this matches CoreFoundation opaque pointer typedefs. - auto CSuspiciousNumberObjectExprM = - expr(ignoringParenImpCasts( - expr(hasType( - typedefType(hasDeclaration(anyOf( - typedefDecl(hasName("CFNumberRef")), - typedefDecl(hasName("CFBooleanRef"))))))) + auto CSuspiciousNumberObjectExprM = expr(ignoringParenImpCasts( + expr(hasType(elaboratedType(namesType(typedefType( + hasDeclaration(anyOf(typedefDecl(hasName("CFNumberRef")), + typedefDecl(hasName("CFBooleanRef"))))))))) .bind("c_object"))); // Currently this matches XNU kernel number-object pointers. @@ -240,8 +238,9 @@ void NumberObjectConversionChecker::checkASTCodeBody(const Decl *D, // The .bind here is in order to compose the error message more accurately. auto ObjCSuspiciousScalarBooleanTypeM = - qualType(typedefType(hasDeclaration( - typedefDecl(hasName("BOOL"))))).bind("objc_bool_type"); + qualType(elaboratedType(namesType( + typedefType(hasDeclaration(typedefDecl(hasName("BOOL"))))))) + .bind("objc_bool_type"); // The .bind here is in order to compose the error message more accurately. auto SuspiciousScalarBooleanTypeM = @@ -253,9 +252,9 @@ void NumberObjectConversionChecker::checkASTCodeBody(const Decl *D, // for storing pointers. auto SuspiciousScalarNumberTypeM = qualType(hasCanonicalType(isInteger()), - unless(typedefType(hasDeclaration( - typedefDecl(matchesName("^::u?intptr_t$")))))) - .bind("int_type"); + unless(elaboratedType(namesType(typedefType(hasDeclaration( + typedefDecl(matchesName("^::u?intptr_t$")))))))) + .bind("int_type"); auto SuspiciousScalarTypeM = qualType(anyOf(SuspiciousScalarBooleanTypeM, diff --git a/clang/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp index 6688278a7a33..2b008d1c775a 100644 --- a/clang/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp @@ -72,7 +72,7 @@ class WalkAST : public StmtVisitor<WalkAST> { public: WalkAST(BugReporter &br, const CheckerBase *checker, AnalysisDeclContext *ac) : BR(br), Checker(checker), AC(ac), ASTC(AC->getASTContext()), - PtrWidth(ASTC.getTargetInfo().getPointerWidth(0)) {} + PtrWidth(ASTC.getTargetInfo().getPointerWidth(LangAS::Default)) {} // Statement visitor methods. void VisitChildren(Stmt *S); diff --git a/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp index e877dd119ff6..929bd6bc3eb3 100644 --- a/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp @@ -87,7 +87,7 @@ private: CheckerKind CheckKind) const; CallDescriptionMap<FnCheck> PThreadCallbacks = { // Init. - {{"pthread_mutex_init", 2}, &PthreadLockChecker::InitAnyLock}, + {{{"pthread_mutex_init"}, 2}, &PthreadLockChecker::InitAnyLock}, // TODO: pthread_rwlock_init(2 arguments). // TODO: lck_mtx_init(3 arguments). // TODO: lck_mtx_alloc_init(2 arguments) => returns the mutex. @@ -95,74 +95,74 @@ private: // TODO: lck_rw_alloc_init(2 arguments) => returns the mutex. // Acquire. - {{"pthread_mutex_lock", 1}, &PthreadLockChecker::AcquirePthreadLock}, - {{"pthread_rwlock_rdlock", 1}, &PthreadLockChecker::AcquirePthreadLock}, - {{"pthread_rwlock_wrlock", 1}, &PthreadLockChecker::AcquirePthreadLock}, - {{"lck_mtx_lock", 1}, &PthreadLockChecker::AcquireXNULock}, - {{"lck_rw_lock_exclusive", 1}, &PthreadLockChecker::AcquireXNULock}, - {{"lck_rw_lock_shared", 1}, &PthreadLockChecker::AcquireXNULock}, + {{{"pthread_mutex_lock"}, 1}, &PthreadLockChecker::AcquirePthreadLock}, + {{{"pthread_rwlock_rdlock"}, 1}, &PthreadLockChecker::AcquirePthreadLock}, + {{{"pthread_rwlock_wrlock"}, 1}, &PthreadLockChecker::AcquirePthreadLock}, + {{{"lck_mtx_lock"}, 1}, &PthreadLockChecker::AcquireXNULock}, + {{{"lck_rw_lock_exclusive"}, 1}, &PthreadLockChecker::AcquireXNULock}, + {{{"lck_rw_lock_shared"}, 1}, &PthreadLockChecker::AcquireXNULock}, // Try. - {{"pthread_mutex_trylock", 1}, &PthreadLockChecker::TryPthreadLock}, - {{"pthread_rwlock_tryrdlock", 1}, &PthreadLockChecker::TryPthreadLock}, - {{"pthread_rwlock_trywrlock", 1}, &PthreadLockChecker::TryPthreadLock}, - {{"lck_mtx_try_lock", 1}, &PthreadLockChecker::TryXNULock}, - {{"lck_rw_try_lock_exclusive", 1}, &PthreadLockChecker::TryXNULock}, - {{"lck_rw_try_lock_shared", 1}, &PthreadLockChecker::TryXNULock}, + {{{"pthread_mutex_trylock"}, 1}, &PthreadLockChecker::TryPthreadLock}, + {{{"pthread_rwlock_tryrdlock"}, 1}, &PthreadLockChecker::TryPthreadLock}, + {{{"pthread_rwlock_trywrlock"}, 1}, &PthreadLockChecker::TryPthreadLock}, + {{{"lck_mtx_try_lock"}, 1}, &PthreadLockChecker::TryXNULock}, + {{{"lck_rw_try_lock_exclusive"}, 1}, &PthreadLockChecker::TryXNULock}, + {{{"lck_rw_try_lock_shared"}, 1}, &PthreadLockChecker::TryXNULock}, // Release. - {{"pthread_mutex_unlock", 1}, &PthreadLockChecker::ReleaseAnyLock}, - {{"pthread_rwlock_unlock", 1}, &PthreadLockChecker::ReleaseAnyLock}, - {{"lck_mtx_unlock", 1}, &PthreadLockChecker::ReleaseAnyLock}, - {{"lck_rw_unlock_exclusive", 1}, &PthreadLockChecker::ReleaseAnyLock}, - {{"lck_rw_unlock_shared", 1}, &PthreadLockChecker::ReleaseAnyLock}, - {{"lck_rw_done", 1}, &PthreadLockChecker::ReleaseAnyLock}, + {{{"pthread_mutex_unlock"}, 1}, &PthreadLockChecker::ReleaseAnyLock}, + {{{"pthread_rwlock_unlock"}, 1}, &PthreadLockChecker::ReleaseAnyLock}, + {{{"lck_mtx_unlock"}, 1}, &PthreadLockChecker::ReleaseAnyLock}, + {{{"lck_rw_unlock_exclusive"}, 1}, &PthreadLockChecker::ReleaseAnyLock}, + {{{"lck_rw_unlock_shared"}, 1}, &PthreadLockChecker::ReleaseAnyLock}, + {{{"lck_rw_done"}, 1}, &PthreadLockChecker::ReleaseAnyLock}, // Destroy. - {{"pthread_mutex_destroy", 1}, &PthreadLockChecker::DestroyPthreadLock}, - {{"lck_mtx_destroy", 2}, &PthreadLockChecker::DestroyXNULock}, + {{{"pthread_mutex_destroy"}, 1}, &PthreadLockChecker::DestroyPthreadLock}, + {{{"lck_mtx_destroy"}, 2}, &PthreadLockChecker::DestroyXNULock}, // TODO: pthread_rwlock_destroy(1 argument). // TODO: lck_rw_destroy(2 arguments). }; CallDescriptionMap<FnCheck> FuchsiaCallbacks = { // Init. - {{"spin_lock_init", 1}, &PthreadLockChecker::InitAnyLock}, + {{{"spin_lock_init"}, 1}, &PthreadLockChecker::InitAnyLock}, // Acquire. - {{"spin_lock", 1}, &PthreadLockChecker::AcquirePthreadLock}, - {{"spin_lock_save", 3}, &PthreadLockChecker::AcquirePthreadLock}, - {{"sync_mutex_lock", 1}, &PthreadLockChecker::AcquirePthreadLock}, - {{"sync_mutex_lock_with_waiter", 1}, + {{{"spin_lock"}, 1}, &PthreadLockChecker::AcquirePthreadLock}, + {{{"spin_lock_save"}, 3}, &PthreadLockChecker::AcquirePthreadLock}, + {{{"sync_mutex_lock"}, 1}, &PthreadLockChecker::AcquirePthreadLock}, + {{{"sync_mutex_lock_with_waiter"}, 1}, &PthreadLockChecker::AcquirePthreadLock}, // Try. - {{"spin_trylock", 1}, &PthreadLockChecker::TryFuchsiaLock}, - {{"sync_mutex_trylock", 1}, &PthreadLockChecker::TryFuchsiaLock}, - {{"sync_mutex_timedlock", 2}, &PthreadLockChecker::TryFuchsiaLock}, + {{{"spin_trylock"}, 1}, &PthreadLockChecker::TryFuchsiaLock}, + {{{"sync_mutex_trylock"}, 1}, &PthreadLockChecker::TryFuchsiaLock}, + {{{"sync_mutex_timedlock"}, 2}, &PthreadLockChecker::TryFuchsiaLock}, // Release. - {{"spin_unlock", 1}, &PthreadLockChecker::ReleaseAnyLock}, - {{"spin_unlock_restore", 3}, &PthreadLockChecker::ReleaseAnyLock}, - {{"sync_mutex_unlock", 1}, &PthreadLockChecker::ReleaseAnyLock}, + {{{"spin_unlock"}, 1}, &PthreadLockChecker::ReleaseAnyLock}, + {{{"spin_unlock_restore"}, 3}, &PthreadLockChecker::ReleaseAnyLock}, + {{{"sync_mutex_unlock"}, 1}, &PthreadLockChecker::ReleaseAnyLock}, }; CallDescriptionMap<FnCheck> C11Callbacks = { // Init. - {{"mtx_init", 2}, &PthreadLockChecker::InitAnyLock}, + {{{"mtx_init"}, 2}, &PthreadLockChecker::InitAnyLock}, // Acquire. - {{"mtx_lock", 1}, &PthreadLockChecker::AcquirePthreadLock}, + {{{"mtx_lock"}, 1}, &PthreadLockChecker::AcquirePthreadLock}, // Try. - {{"mtx_trylock", 1}, &PthreadLockChecker::TryC11Lock}, - {{"mtx_timedlock", 2}, &PthreadLockChecker::TryC11Lock}, + {{{"mtx_trylock"}, 1}, &PthreadLockChecker::TryC11Lock}, + {{{"mtx_timedlock"}, 2}, &PthreadLockChecker::TryC11Lock}, // Release. - {{"mtx_unlock", 1}, &PthreadLockChecker::ReleaseAnyLock}, + {{{"mtx_unlock"}, 1}, &PthreadLockChecker::ReleaseAnyLock}, // Destroy - {{"mtx_destroy", 1}, &PthreadLockChecker::DestroyPthreadLock}, + {{{"mtx_destroy"}, 1}, &PthreadLockChecker::DestroyPthreadLock}, }; ProgramStateRef resolvePossiblyDestroyedMutex(ProgramStateRef state, diff --git a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp index bee7c468812c..01c71d91d1a1 100644 --- a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp @@ -14,6 +14,7 @@ #include "RetainCountChecker.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include <optional> using namespace clang; using namespace ento; @@ -284,7 +285,7 @@ void RetainCountChecker::checkPostStmt(const ObjCBoxedExpr *Ex, void RetainCountChecker::checkPostStmt(const ObjCIvarRefExpr *IRE, CheckerContext &C) const { - Optional<Loc> IVarLoc = C.getSVal(IRE).getAs<Loc>(); + std::optional<Loc> IVarLoc = C.getSVal(IRE).getAs<Loc>(); if (!IVarLoc) return; @@ -412,15 +413,15 @@ static QualType GetReturnType(const Expr *RetE, ASTContext &Ctx) { return RetTy; } -static Optional<RefVal> refValFromRetEffect(RetEffect RE, - QualType ResultTy) { +static std::optional<RefVal> refValFromRetEffect(RetEffect RE, + QualType ResultTy) { if (RE.isOwned()) { return RefVal::makeOwned(RE.getObjKind(), ResultTy); } else if (RE.notOwned()) { return RefVal::makeNotOwned(RE.getObjKind(), ResultTy); } - return None; + return std::nullopt; } static bool isPointerToObject(QualType QT) { @@ -692,7 +693,7 @@ void RetainCountChecker::checkSummary(const RetainSummary &Summ, assert(Ex); ResultTy = GetReturnType(Ex, C.getASTContext()); } - if (Optional<RefVal> updatedRefVal = refValFromRetEffect(RE, ResultTy)) + if (std::optional<RefVal> updatedRefVal = refValFromRetEffect(RE, ResultTy)) state = setRefBinding(state, Sym, *updatedRefVal); } @@ -767,7 +768,7 @@ ProgramStateRef RetainCountChecker::updateSymbol(ProgramStateRef state, break; } - LLVM_FALLTHROUGH; + [[fallthrough]]; case DoNothing: return state; @@ -907,7 +908,7 @@ bool RetainCountChecker::evalCall(const CallEvent &Call, const LocationContext *LCtx = C.getLocationContext(); using BehaviorSummary = RetainSummaryManager::BehaviorSummary; - Optional<BehaviorSummary> BSmr = + std::optional<BehaviorSummary> BSmr = SmrMgr.canEval(CE, FD, hasTrustedImplementationAnnotation); // See if it's one of the specific functions we know how to eval. @@ -1336,7 +1337,7 @@ void RetainCountChecker::checkBeginFunction(CheckerContext &Ctx) const { RetainSummaryManager &SmrMgr = getSummaryManager(Ctx); const LocationContext *LCtx = Ctx.getLocationContext(); const Decl *D = LCtx->getDecl(); - Optional<AnyCall> C = AnyCall::forDecl(D); + std::optional<AnyCall> C = AnyCall::forDecl(D); if (!C || SmrMgr.isTrustedReferenceCountImplementation(D)) return; diff --git a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp index 5109ae668686..e11e509f159d 100644 --- a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp @@ -15,6 +15,7 @@ #include "RetainCountChecker.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" +#include <optional> using namespace clang; using namespace ento; @@ -165,13 +166,12 @@ static bool shouldGenerateNote(llvm::raw_string_ostream &os, /// Finds argument index of the out paramter in the call @c S /// corresponding to the symbol @c Sym. -/// If none found, returns None. -static Optional<unsigned> findArgIdxOfSymbol(ProgramStateRef CurrSt, - const LocationContext *LCtx, - SymbolRef &Sym, - Optional<CallEventRef<>> CE) { +/// If none found, returns std::nullopt. +static std::optional<unsigned> +findArgIdxOfSymbol(ProgramStateRef CurrSt, const LocationContext *LCtx, + SymbolRef &Sym, std::optional<CallEventRef<>> CE) { if (!CE) - return None; + return std::nullopt; for (unsigned Idx = 0; Idx < (*CE)->getNumArgs(); Idx++) if (const MemRegion *MR = (*CE)->getArgSVal(Idx).getAsRegion()) @@ -179,25 +179,25 @@ static Optional<unsigned> findArgIdxOfSymbol(ProgramStateRef CurrSt, if (CurrSt->getSVal(MR, TR->getValueType()).getAsSymbol() == Sym) return Idx; - return None; + return std::nullopt; } -static Optional<std::string> findMetaClassAlloc(const Expr *Callee) { +static std::optional<std::string> findMetaClassAlloc(const Expr *Callee) { if (const auto *ME = dyn_cast<MemberExpr>(Callee)) { if (ME->getMemberDecl()->getNameAsString() != "alloc") - return None; + return std::nullopt; const Expr *This = ME->getBase()->IgnoreParenImpCasts(); if (const auto *DRE = dyn_cast<DeclRefExpr>(This)) { const ValueDecl *VD = DRE->getDecl(); if (VD->getNameAsString() != "metaClass") - return None; + return std::nullopt; if (const auto *RD = dyn_cast<CXXRecordDecl>(VD->getDeclContext())) return RD->getNameAsString(); } } - return None; + return std::nullopt; } static std::string findAllocatedObjectName(const Stmt *S, QualType QT) { @@ -250,7 +250,7 @@ static void generateDiagnosticsForCallLike(ProgramStateRef CurrSt, } } - Optional<CallEventRef<>> CE = Mgr.getCall(S, CurrSt, LCtx); + std::optional<CallEventRef<>> CE = Mgr.getCall(S, CurrSt, LCtx); auto Idx = findArgIdxOfSymbol(CurrSt, LCtx, Sym, CE); // If index is not found, we assume that the symbol was returned. @@ -602,16 +602,17 @@ RefCountReportVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC, return std::move(P); } -static Optional<std::string> describeRegion(const MemRegion *MR) { +static std::optional<std::string> describeRegion(const MemRegion *MR) { if (const auto *VR = dyn_cast_or_null<VarRegion>(MR)) return std::string(VR->getDecl()->getName()); // Once we support more storage locations for bindings, // this would need to be improved. - return None; + return std::nullopt; } using Bindings = llvm::SmallVector<std::pair<const MemRegion *, SVal>, 4>; +namespace { class VarBindingsCollector : public StoreManager::BindingsHandler { SymbolRef Sym; Bindings &Result; @@ -632,6 +633,7 @@ public: return true; } }; +} // namespace Bindings getAllVarBindingsForSymbol(ProgramStateManager &Manager, const ExplodedNode *Node, SymbolRef Sym) { @@ -728,7 +730,7 @@ static AllocationInfo GetAllocationSite(ProgramStateManager &StateMgr, const LocationContext *InterestingMethodContext = nullptr; if (InitMethodContext) { const ProgramPoint AllocPP = AllocationNode->getLocation(); - if (Optional<StmtPoint> SP = AllocPP.getAs<StmtPoint>()) + if (std::optional<StmtPoint> SP = AllocPP.getAs<StmtPoint>()) if (const ObjCMessageExpr *ME = SP->getStmtAs<ObjCMessageExpr>()) if (ME->getMethodFamily() == OMF_alloc) InterestingMethodContext = InitMethodContext; @@ -771,7 +773,7 @@ RefLeakReportVisitor::getEndPath(BugReporterContext &BRC, os << "Object leaked: "; - Optional<std::string> RegionDescription = describeRegion(LastBinding); + std::optional<std::string> RegionDescription = describeRegion(LastBinding); if (RegionDescription) { os << "object allocated and stored into '" << *RegionDescription << '\''; } else { @@ -917,7 +919,7 @@ void RefLeakReport::createDescription(CheckerContext &Ctx) { llvm::raw_string_ostream os(Description); os << "Potential leak of an object"; - Optional<std::string> RegionDescription = + std::optional<std::string> RegionDescription = describeRegion(AllocBindingToReport); if (RegionDescription) { os << " stored into '" << *RegionDescription << '\''; @@ -969,7 +971,7 @@ void RefLeakReport::findBindingToReport(CheckerContext &Ctx, // Let's pick one of them at random (if there is something to pick from). AllocBindingToReport = AllVarBindings[0].first; - // Because 'AllocBindingToReport' is not the the same as + // Because 'AllocBindingToReport' is not the same as // 'AllocFirstBinding', we need to explain how the leaking object // got from one to another. // diff --git a/clang/lib/StaticAnalyzer/Checkers/ReturnValueChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/ReturnValueChecker.cpp index cf97439a468d..c3112ebe4e79 100644 --- a/clang/lib/StaticAnalyzer/Checkers/ReturnValueChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/ReturnValueChecker.cpp @@ -17,8 +17,8 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" -#include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallVector.h" +#include <optional> using namespace clang; using namespace ento; @@ -70,11 +70,11 @@ static std::string getName(const CallEvent &Call) { // The predefinitions ('CDM') could break due to the ever growing code base. // Check for the expected invariants and see whether they apply. -static Optional<bool> isInvariantBreak(bool ExpectedValue, SVal ReturnV, - CheckerContext &C) { +static std::optional<bool> isInvariantBreak(bool ExpectedValue, SVal ReturnV, + CheckerContext &C) { auto ReturnDV = ReturnV.getAs<DefinedOrUnknownSVal>(); if (!ReturnDV) - return None; + return std::nullopt; if (ExpectedValue) return C.getState()->isNull(*ReturnDV).isConstrainedTrue(); @@ -90,7 +90,8 @@ void ReturnValueChecker::checkPostCall(const CallEvent &Call, SVal ReturnV = Call.getReturnValue(); bool ExpectedValue = *RawExpectedValue; - Optional<bool> IsInvariantBreak = isInvariantBreak(ExpectedValue, ReturnV, C); + std::optional<bool> IsInvariantBreak = + isInvariantBreak(ExpectedValue, ReturnV, C); if (!IsInvariantBreak) return; @@ -137,7 +138,8 @@ void ReturnValueChecker::checkEndFunction(const ReturnStmt *RS, SVal ReturnV = State->getSVal(RS->getRetValue(), C.getLocationContext()); bool ExpectedValue = *RawExpectedValue; - Optional<bool> IsInvariantBreak = isInvariantBreak(ExpectedValue, ReturnV, C); + std::optional<bool> IsInvariantBreak = + isInvariantBreak(ExpectedValue, ReturnV, C); if (!IsInvariantBreak) return; diff --git a/clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp index 8c87a548fd91..9251c895614c 100644 --- a/clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp @@ -90,7 +90,7 @@ public: REGISTER_MAP_WITH_PROGRAMSTATE(StreamMap, SymbolRef, StreamState) SimpleStreamChecker::SimpleStreamChecker() - : OpenFn("fopen"), CloseFn("fclose", 1) { + : OpenFn({"fopen"}), CloseFn({"fclose"}, 1) { // Initialize the bug types. DoubleCloseBugType.reset( new BugType(this, "Double fclose", "Unix Stream API Error")); diff --git a/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp index 92eef20d2daa..5689a63f8dd8 100644 --- a/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp @@ -33,6 +33,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" #include "llvm/ADT/StringMap.h" #include "llvm/Support/ErrorHandling.h" +#include <optional> #include <string> using namespace clang; @@ -85,10 +86,10 @@ private: using SmartPtrMethodHandlerFn = void (SmartPtrModeling::*)(const CallEvent &Call, CheckerContext &) const; CallDescriptionMap<SmartPtrMethodHandlerFn> SmartPtrMethodHandlers{ - {{"reset"}, &SmartPtrModeling::handleReset}, - {{"release"}, &SmartPtrModeling::handleRelease}, - {{"swap", 1}, &SmartPtrModeling::handleSwapMethod}, - {{"get"}, &SmartPtrModeling::handleGet}}; + {{{"reset"}}, &SmartPtrModeling::handleReset}, + {{{"release"}}, &SmartPtrModeling::handleRelease}, + {{{"swap"}, 1}, &SmartPtrModeling::handleSwapMethod}, + {{{"get"}}, &SmartPtrModeling::handleGet}}; const CallDescription StdSwapCall{{"std", "swap"}, 2}; const CallDescription StdMakeUniqueCall{{"std", "make_unique"}}; const CallDescription StdMakeUniqueForOverwriteCall{ @@ -298,8 +299,9 @@ bool SmartPtrModeling::evalCall(const CallEvent &Call, if (matchesAny(Call, StdMakeUniqueCall, StdMakeUniqueForOverwriteCall)) { if (!ModelSmartPtrDereference) return false; - - const Optional<SVal> ThisRegionOpt = Call.getReturnValueUnderConstruction(); + + const std::optional<SVal> ThisRegionOpt = + Call.getReturnValueUnderConstruction(); if (!ThisRegionOpt) return false; diff --git a/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp index e6cea0fbff8c..c4b7411e9401 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp @@ -61,7 +61,6 @@ private: ASTContext &Ctx); static SmallVector<const MemRegion *, 4> getCapturedStackRegions(const BlockDataRegion &B, CheckerContext &C); - static bool isArcManagedBlock(const MemRegion *R, CheckerContext &C); static bool isNotInCurrentFrame(const MemRegion *R, CheckerContext &C); }; } // namespace @@ -110,13 +109,6 @@ SourceRange StackAddrEscapeChecker::genName(raw_ostream &os, const MemRegion *R, return range; } -bool StackAddrEscapeChecker::isArcManagedBlock(const MemRegion *R, - CheckerContext &C) { - assert(R && "MemRegion should not be null"); - return C.getASTContext().getLangOpts().ObjCAutoRefCount && - isa<BlockDataRegion>(R); -} - bool StackAddrEscapeChecker::isNotInCurrentFrame(const MemRegion *R, CheckerContext &C) { const StackSpaceRegion *S = cast<StackSpaceRegion>(R->getMemorySpace()); @@ -214,7 +206,7 @@ void StackAddrEscapeChecker::checkAsyncExecutedBlockCaptures( void StackAddrEscapeChecker::checkReturnedBlockCaptures( const BlockDataRegion &B, CheckerContext &C) const { for (const MemRegion *Region : getCapturedStackRegions(B, C)) { - if (isArcManagedBlock(Region, C) || isNotInCurrentFrame(Region, C)) + if (isNotInCurrentFrame(Region, C)) continue; ExplodedNode *N = C.generateNonFatalErrorNode(); if (!N) @@ -267,8 +259,7 @@ void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS, if (const BlockDataRegion *B = dyn_cast<BlockDataRegion>(R)) checkReturnedBlockCaptures(*B, C); - if (!isa<StackSpaceRegion>(R->getMemorySpace()) || - isNotInCurrentFrame(R, C) || isArcManagedBlock(R, C)) + if (!isa<StackSpaceRegion>(R->getMemorySpace()) || isNotInCurrentFrame(R, C)) return; // Returning a record by value is fine. (In this case, the returned @@ -348,8 +339,7 @@ void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS, // Check the globals for the same. if (!isa<GlobalsSpaceRegion>(Region->getMemorySpace())) return true; - if (VR && VR->hasStackStorage() && !isArcManagedBlock(VR, Ctx) && - !isNotInCurrentFrame(VR, Ctx)) + if (VR && VR->hasStackStorage() && !isNotInCurrentFrame(VR, Ctx)) V.emplace_back(Region, VR); return true; } diff --git a/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp index 5897e5096461..49b3db560843 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp @@ -52,24 +52,12 @@ #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" +#include <optional> #include <string> using namespace clang; using namespace clang::ento; -/// Produce a textual description of the state of \c errno (this describes the -/// way how it is allowed to be used). -/// The returned string is insertable into a longer warning message (in the form -/// "the value 'errno' <...>"). -/// Currently only the \c errno_modeling::MustNotBeChecked state is supported. -/// But later other kind of errno state may be needed if functions with special -/// handling of \c errno are added. -static const char *describeErrnoCheckState(errno_modeling::ErrnoCheckState CS) { - assert(CS == errno_modeling::MustNotBeChecked && - "Errno description not applicable."); - return "may be undefined after the call and should not be used"; -} - namespace { class StdLibraryFunctionsChecker : public Checker<check::PreCall, check::PostCall, eval::Call> { @@ -147,9 +135,18 @@ class StdLibraryFunctionsChecker virtual StringRef getName() const = 0; + // Represents that in which context do we require a description of the + // constraint. + enum class DescriptionKind { + // The constraint is violated. + Violation, + // We assume that the constraint is satisfied. + Assumption + }; + // Give a description that explains the constraint to the user. Used when // the bug is reported. - virtual std::string describe(ProgramStateRef State, + virtual std::string describe(DescriptionKind DK, ProgramStateRef State, const Summary &Summary) const { // There are some descendant classes that are not used as argument // constraints, e.g. ComparisonConstraint. In that case we can safely @@ -187,7 +184,7 @@ class StdLibraryFunctionsChecker RangeConstraint(ArgNo ArgN, RangeKind Kind, const IntRangeVector &Ranges) : ValueConstraint(ArgN), Kind(Kind), Ranges(Ranges) {} - std::string describe(ProgramStateRef State, + std::string describe(DescriptionKind DK, ProgramStateRef State, const Summary &Summary) const override; const IntRangeVector &getRanges() const { return Ranges; } @@ -257,7 +254,9 @@ class StdLibraryFunctionsChecker bool CannotBeNull = true; public: - std::string describe(ProgramStateRef State, + NotNullConstraint(ArgNo ArgN, bool CannotBeNull = true) + : ValueConstraint(ArgN), CannotBeNull(CannotBeNull) {} + std::string describe(DescriptionKind DK, ProgramStateRef State, const Summary &Summary) const override; StringRef getName() const override { return "NonNull"; } ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, @@ -300,13 +299,13 @@ class StdLibraryFunctionsChecker // // Here, ptr is the buffer, and its minimum size is `size * nmemb`. class BufferSizeConstraint : public ValueConstraint { // The concrete value which is the minimum size for the buffer. - llvm::Optional<llvm::APSInt> ConcreteSize; + std::optional<llvm::APSInt> ConcreteSize; // The argument which holds the size of the buffer. - llvm::Optional<ArgNo> SizeArgN; + std::optional<ArgNo> SizeArgN; // The argument which is a multiplier to size. This is set in case of // `fread` like functions where the size is computed as a multiplication of // two arguments. - llvm::Optional<ArgNo> SizeMultiplierArgN; + std::optional<ArgNo> SizeMultiplierArgN; // The operator we use in apply. This is negated in negate(). BinaryOperator::Opcode Op = BO_LE; @@ -329,7 +328,7 @@ class StdLibraryFunctionsChecker return Result; } - std::string describe(ProgramStateRef State, + std::string describe(DescriptionKind DK, ProgramStateRef State, const Summary &Summary) const override; ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, @@ -392,45 +391,67 @@ class StdLibraryFunctionsChecker using ConstraintSet = std::vector<ValueConstraintPtr>; /// Define how a function affects the system variable 'errno'. - /// This works together with the ErrnoModeling and ErrnoChecker classes. + /// This works together with the \c ErrnoModeling and \c ErrnoChecker classes. + /// Currently 3 use cases exist: success, failure, irrelevant. + /// In the future the failure case can be customized to set \c errno to a + /// more specific constraint (for example > 0), or new case can be added + /// for functions which require check of \c errno in both success and failure + /// case. class ErrnoConstraintBase { public: /// Apply specific state changes related to the errno variable. virtual ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, const Summary &Summary, CheckerContext &C) const = 0; - /// Get a description about what is applied to 'errno' and how is it allowed - /// to be used. If ErrnoChecker generates a bug then this message is - /// displayed as a note at the function call. - /// It may return empty string if no note tag is to be added. - virtual std::string describe(StringRef FunctionName) const { return ""; } + /// Get a NoteTag about the changes made to 'errno' and the possible bug. + /// It may return \c nullptr (if no bug report from \c ErrnoChecker is + /// expected). + virtual const NoteTag *describe(CheckerContext &C, + StringRef FunctionName) const { + return nullptr; + } virtual ~ErrnoConstraintBase() {} protected: - /// Many of the descendant classes use this value. - const errno_modeling::ErrnoCheckState CheckState; - - ErrnoConstraintBase(errno_modeling::ErrnoCheckState CS) : CheckState(CS) {} + ErrnoConstraintBase() = default; /// This is used for conjure symbol for errno to differentiate from the /// original call expression (same expression is used for the errno symbol). static int Tag; }; - /// Set value of 'errno' to be related to 0 in a specified way, with a - /// specified "errno check state". For example with \c BO_GT 'errno' is - /// constrained to be greater than 0. Use this for failure cases of functions. - class ZeroRelatedErrnoConstraint : public ErrnoConstraintBase { - BinaryOperatorKind Op; + /// Reset errno constraints to irrelevant. + /// This is applicable to functions that may change 'errno' and are not + /// modeled elsewhere. + class ResetErrnoConstraint : public ErrnoConstraintBase { + public: + ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, + const Summary &Summary, + CheckerContext &C) const override { + return errno_modeling::setErrnoState(State, errno_modeling::Irrelevant); + } + }; + /// Do not change errno constraints. + /// This is applicable to functions that are modeled in another checker + /// and the already set errno constraints should not be changed in the + /// post-call event. + class NoErrnoConstraint : public ErrnoConstraintBase { public: - ZeroRelatedErrnoConstraint(clang::BinaryOperatorKind OpK, - errno_modeling::ErrnoCheckState CS) - : ErrnoConstraintBase(CS), Op(OpK) { - assert(BinaryOperator::isComparisonOp(OpK)); + ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, + const Summary &Summary, + CheckerContext &C) const override { + return State; } + }; + /// Set errno constraint at failure cases of standard functions. + /// Failure case: 'errno' becomes not equal to 0 and may or may not be checked + /// by the program. \c ErrnoChecker does not emit a bug report after such a + /// function call. + class FailureErrnoConstraint : public ErrnoConstraintBase { + public: ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, const Summary &Summary, CheckerContext &C) const override { @@ -440,62 +461,40 @@ class StdLibraryFunctionsChecker C.getLocationContext(), C.getASTContext().IntTy, C.blockCount()) .castAs<NonLoc>(); - NonLoc ZeroVal = - SVB.makeZeroVal(C.getASTContext().IntTy).castAs<NonLoc>(); - DefinedOrUnknownSVal Cond = - SVB.evalBinOp(State, Op, ErrnoSVal, ZeroVal, SVB.getConditionType()) - .castAs<DefinedOrUnknownSVal>(); - State = State->assume(Cond, true); - if (!State) - return State; - return errno_modeling::setErrnoValue(State, C.getLocationContext(), - ErrnoSVal, CheckState); - } - - std::string describe(StringRef FunctionName) const override { - if (CheckState == errno_modeling::Irrelevant) - return ""; - return (Twine("Assuming that function '") + FunctionName.str() + - "' fails, in this case the value 'errno' becomes " + - BinaryOperator::getOpcodeStr(Op).str() + " 0 and " + - describeErrnoCheckState(CheckState)) - .str(); + return errno_modeling::setErrnoForStdFailure(State, C, ErrnoSVal); } }; - /// Applies the constraints to 'errno' for a common case when a standard - /// function is successful. The value of 'errno' after the call is not - /// specified by the standard (it may change or not). The \c ErrnoChecker can - /// generate a bug if 'errno' is read afterwards. + /// Set errno constraint at success cases of standard functions. + /// Success case: 'errno' is not allowed to be used. + /// \c ErrnoChecker can emit bug report after such a function call if errno + /// is used. class SuccessErrnoConstraint : public ErrnoConstraintBase { public: - SuccessErrnoConstraint() - : ErrnoConstraintBase(errno_modeling::MustNotBeChecked) {} - ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, const Summary &Summary, CheckerContext &C) const override { - return errno_modeling::setErrnoState(State, CheckState); + return errno_modeling::setErrnoForStdSuccess(State, C); } - std::string describe(StringRef FunctionName) const override { - return (Twine("Assuming that function '") + FunctionName.str() + - "' is successful, in this case the value 'errno' " + - describeErrnoCheckState(CheckState)) - .str(); + const NoteTag *describe(CheckerContext &C, + StringRef FunctionName) const override { + return errno_modeling::getNoteTagForStdSuccess(C, FunctionName); } }; - /// Set errno constraints if use of 'errno' is completely irrelevant to the - /// modeled function or modeling is not possible. - class NoErrnoConstraint : public ErrnoConstraintBase { + class ErrnoMustBeCheckedConstraint : public ErrnoConstraintBase { public: - NoErrnoConstraint() : ErrnoConstraintBase(errno_modeling::Irrelevant) {} - ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, const Summary &Summary, CheckerContext &C) const override { - return errno_modeling::setErrnoState(State, CheckState); + return errno_modeling::setErrnoStdMustBeChecked(State, C, + Call.getOriginExpr()); + } + + const NoteTag *describe(CheckerContext &C, + StringRef FunctionName) const override { + return errno_modeling::getNoteTagForStdMustBeChecked(C, FunctionName); } }; @@ -542,8 +541,8 @@ class StdLibraryFunctionsChecker StringRef getNote() const { return Note; } }; - using ArgTypes = std::vector<Optional<QualType>>; - using RetType = Optional<QualType>; + using ArgTypes = std::vector<std::optional<QualType>>; + using RetType = std::optional<QualType>; // A placeholder type, we use it whenever we do not care about the concrete // type in a Signature. @@ -565,7 +564,7 @@ class StdLibraryFunctionsChecker // Construct a signature from optional types. If any of the optional types // are not set then the signature will be invalid. Signature(ArgTypes ArgTys, RetType RetTy) { - for (Optional<QualType> Arg : ArgTys) { + for (std::optional<QualType> Arg : ArgTys) { if (!Arg) { Invalid = true; return; @@ -718,10 +717,10 @@ public: bool ShouldAssumeControlledEnvironment = false; private: - Optional<Summary> findFunctionSummary(const FunctionDecl *FD, - CheckerContext &C) const; - Optional<Summary> findFunctionSummary(const CallEvent &Call, - CheckerContext &C) const; + std::optional<Summary> findFunctionSummary(const FunctionDecl *FD, + CheckerContext &C) const; + std::optional<Summary> findFunctionSummary(const CallEvent &Call, + CheckerContext &C) const; void initFunctionSummaries(CheckerContext &C) const; @@ -746,9 +745,12 @@ private: // Highlight the range of the argument that was violated. R->addRange(Call.getArgSourceRange(VC->getArgNo())); - // Describe the argument constraint in a note. - R->addNote(VC->describe(C.getState(), Summary), R->getLocation(), - Call.getArgSourceRange(VC->getArgNo())); + // Describe the argument constraint violation in a note. + std::string Descr = VC->describe( + ValueConstraint::DescriptionKind::Violation, C.getState(), Summary); + // Capitalize the first letter b/c we want a full sentence. + Descr[0] = toupper(Descr[0]); + R->addNote(Descr, R->getLocation(), Call.getArgSourceRange(VC->getArgNo())); C.emitReport(std::move(R)); } @@ -758,10 +760,11 @@ private: /// Usually if a failure return value exists for function, that function /// needs different cases for success and failure with different errno /// constraints (and different return value constraints). - const NoErrnoConstraint ErrnoIrrelevant; - const SuccessErrnoConstraint ErrnoMustNotBeChecked; - const ZeroRelatedErrnoConstraint ErrnoNEZeroIrrelevant{ - clang::BinaryOperatorKind::BO_NE, errno_modeling::Irrelevant}; + const NoErrnoConstraint ErrnoUnchanged{}; + const ResetErrnoConstraint ErrnoIrrelevant{}; + const ErrnoMustBeCheckedConstraint ErrnoMustBeChecked{}; + const SuccessErrnoConstraint ErrnoMustNotBeChecked{}; + const FailureErrnoConstraint ErrnoNEZeroIrrelevant{}; }; int StdLibraryFunctionsChecker::ErrnoConstraintBase::Tag = 0; @@ -778,24 +781,26 @@ static BasicValueFactory &getBVF(ProgramStateRef State) { } std::string StdLibraryFunctionsChecker::NotNullConstraint::describe( - ProgramStateRef State, const Summary &Summary) const { + DescriptionKind DK, ProgramStateRef State, const Summary &Summary) const { SmallString<48> Result; - Result += "The "; + const auto Violation = ValueConstraint::DescriptionKind::Violation; + Result += "the "; Result += getArgDesc(ArgN); - Result += " should not be NULL"; + Result += DK == Violation ? " should not be NULL" : " is not NULL"; return Result.c_str(); } std::string StdLibraryFunctionsChecker::RangeConstraint::describe( - ProgramStateRef State, const Summary &Summary) const { + DescriptionKind DK, ProgramStateRef State, const Summary &Summary) const { BasicValueFactory &BVF = getBVF(State); QualType T = Summary.getArgType(getArgNo()); SmallString<48> Result; - Result += "The "; + const auto Violation = ValueConstraint::DescriptionKind::Violation; + Result += "the "; Result += getArgDesc(ArgN); - Result += " should be "; + Result += DK == Violation ? " should be " : " is "; // Range kind as a string. Kind == OutOfRange ? Result += "out of" : Result += "within"; @@ -827,16 +832,18 @@ StdLibraryFunctionsChecker::getArgDesc(StdLibraryFunctionsChecker::ArgNo ArgN) { SmallString<8> Result; Result += std::to_string(ArgN + 1); Result += llvm::getOrdinalSuffix(ArgN + 1); - Result += " arg"; + Result += " argument"; return Result; } std::string StdLibraryFunctionsChecker::BufferSizeConstraint::describe( - ProgramStateRef State, const Summary &Summary) const { + DescriptionKind DK, ProgramStateRef State, const Summary &Summary) const { SmallString<96> Result; - Result += "The size of the "; + const auto Violation = ValueConstraint::DescriptionKind::Violation; + Result += "the size of the "; Result += getArgDesc(ArgN); - Result += " should be equal to or less than the value of "; + Result += DK == Violation ? " should be " : " is "; + Result += "equal to or greater than the value of "; if (ConcreteSize) { ConcreteSize->toString(Result); } else if (SizeArgN) { @@ -962,7 +969,7 @@ ProgramStateRef StdLibraryFunctionsChecker::ComparisonConstraint::apply( void StdLibraryFunctionsChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { - Optional<Summary> FoundSummary = findFunctionSummary(Call, C); + std::optional<Summary> FoundSummary = findFunctionSummary(Call, C); if (!FoundSummary) return; @@ -970,31 +977,43 @@ void StdLibraryFunctionsChecker::checkPreCall(const CallEvent &Call, ProgramStateRef State = C.getState(); ProgramStateRef NewState = State; + ExplodedNode *NewNode = C.getPredecessor(); for (const ValueConstraintPtr &Constraint : Summary.getArgConstraints()) { ProgramStateRef SuccessSt = Constraint->apply(NewState, Call, Summary, C); ProgramStateRef FailureSt = Constraint->negate()->apply(NewState, Call, Summary, C); // The argument constraint is not satisfied. if (FailureSt && !SuccessSt) { - if (ExplodedNode *N = C.generateErrorNode(NewState)) + if (ExplodedNode *N = C.generateErrorNode(NewState, NewNode)) reportBug(Call, N, Constraint.get(), Summary, C); break; - } else { - // We will apply the constraint even if we cannot reason about the - // argument. This means both SuccessSt and FailureSt can be true. If we - // weren't applying the constraint that would mean that symbolic - // execution continues on a code whose behaviour is undefined. - assert(SuccessSt); - NewState = SuccessSt; + } + // We will apply the constraint even if we cannot reason about the + // argument. This means both SuccessSt and FailureSt can be true. If we + // weren't applying the constraint that would mean that symbolic + // execution continues on a code whose behaviour is undefined. + assert(SuccessSt); + NewState = SuccessSt; + if (NewState != State) { + SmallString<64> Msg; + Msg += "Assuming "; + Msg += Constraint->describe(ValueConstraint::DescriptionKind::Assumption, + NewState, Summary); + const auto ArgSVal = Call.getArgSVal(Constraint->getArgNo()); + NewNode = C.addTransition( + NewState, NewNode, + C.getNoteTag([Msg = std::move(Msg), ArgSVal]( + PathSensitiveBugReport &BR, llvm::raw_ostream &OS) { + if (BR.isInteresting(ArgSVal)) + OS << Msg; + })); } } - if (NewState && NewState != State) - C.addTransition(NewState); } void StdLibraryFunctionsChecker::checkPostCall(const CallEvent &Call, CheckerContext &C) const { - Optional<Summary> FoundSummary = findFunctionSummary(Call, C); + std::optional<Summary> FoundSummary = findFunctionSummary(Call, C); if (!FoundSummary) return; @@ -1017,13 +1036,10 @@ void StdLibraryFunctionsChecker::checkPostCall(const CallEvent &Call, if (NewState && NewState != State) { if (Case.getNote().empty()) { - std::string Note; + const NoteTag *NT = nullptr; if (const auto *D = dyn_cast_or_null<FunctionDecl>(Call.getDecl())) - Note = Case.getErrnoConstraint().describe(D->getNameAsString()); - if (Note.empty()) - C.addTransition(NewState); - else - C.addTransition(NewState, errno_modeling::getErrnoNoteTag(C, Note)); + NT = Case.getErrnoConstraint().describe(C, D->getNameAsString()); + C.addTransition(NewState, NT); } else { StringRef Note = Case.getNote(); const NoteTag *Tag = C.getNoteTag( @@ -1036,13 +1052,23 @@ void StdLibraryFunctionsChecker::checkPostCall(const CallEvent &Call, /*IsPrunable=*/true); C.addTransition(NewState, Tag); } + } else if (NewState == State) { + // It is possible that the function was evaluated in a checker callback + // where the state constraints are already applied, then no change happens + // here to the state (if the ErrnoConstraint did not change it either). + // If the evaluated function requires a NoteTag for errno change, it is + // added here. + if (const auto *D = dyn_cast_or_null<FunctionDecl>(Call.getDecl())) + if (const NoteTag *NT = + Case.getErrnoConstraint().describe(C, D->getNameAsString())) + C.addTransition(NewState, NT); } } } bool StdLibraryFunctionsChecker::evalCall(const CallEvent &Call, CheckerContext &C) const { - Optional<Summary> FoundSummary = findFunctionSummary(Call, C); + std::optional<Summary> FoundSummary = findFunctionSummary(Call, C); if (!FoundSummary) return false; @@ -1109,26 +1135,26 @@ bool StdLibraryFunctionsChecker::Signature::matches( return true; } -Optional<StdLibraryFunctionsChecker::Summary> +std::optional<StdLibraryFunctionsChecker::Summary> StdLibraryFunctionsChecker::findFunctionSummary(const FunctionDecl *FD, CheckerContext &C) const { if (!FD) - return None; + return std::nullopt; initFunctionSummaries(C); auto FSMI = FunctionSummaryMap.find(FD->getCanonicalDecl()); if (FSMI == FunctionSummaryMap.end()) - return None; + return std::nullopt; return FSMI->second; } -Optional<StdLibraryFunctionsChecker::Summary> +std::optional<StdLibraryFunctionsChecker::Summary> StdLibraryFunctionsChecker::findFunctionSummary(const CallEvent &Call, CheckerContext &C) const { const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl()); if (!FD) - return None; + return std::nullopt; return findFunctionSummary(FD, C); } @@ -1149,11 +1175,11 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( LookupType(const ASTContext &ACtx) : ACtx(ACtx) {} // Find the type. If not found then the optional is not set. - llvm::Optional<QualType> operator()(StringRef Name) { + std::optional<QualType> operator()(StringRef Name) { IdentifierInfo &II = ACtx.Idents.get(Name); auto LookupRes = ACtx.getTranslationUnitDecl()->lookup(&II); if (LookupRes.empty()) - return None; + return std::nullopt; // Prioritze typedef declarations. // This is needed in case of C struct typedefs. E.g.: @@ -1171,7 +1197,7 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( for (Decl *D : LookupRes) if (auto *TD = dyn_cast<TypeDecl>(D)) return ACtx.getTypeDeclType(TD).getCanonicalType(); - return None; + return std::nullopt; } } lookupTy(ACtx); @@ -1185,10 +1211,10 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( QualType operator()(QualType Ty) { return ACtx.getLangOpts().C99 ? ACtx.getRestrictType(Ty) : Ty; } - Optional<QualType> operator()(Optional<QualType> Ty) { + std::optional<QualType> operator()(std::optional<QualType> Ty) { if (Ty) return operator()(*Ty); - return None; + return std::nullopt; } } getRestrictTy(ACtx); class GetPointerTy { @@ -1197,16 +1223,16 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( public: GetPointerTy(const ASTContext &ACtx) : ACtx(ACtx) {} QualType operator()(QualType Ty) { return ACtx.getPointerType(Ty); } - Optional<QualType> operator()(Optional<QualType> Ty) { + std::optional<QualType> operator()(std::optional<QualType> Ty) { if (Ty) return operator()(*Ty); - return None; + return std::nullopt; } } getPointerTy(ACtx); class { public: - Optional<QualType> operator()(Optional<QualType> Ty) { - return Ty ? Optional<QualType>(Ty->withConst()) : None; + std::optional<QualType> operator()(std::optional<QualType> Ty) { + return Ty ? std::optional<QualType>(Ty->withConst()) : std::nullopt; } QualType operator()(QualType Ty) { return Ty.withConst(); } } getConstTy; @@ -1215,14 +1241,14 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( public: GetMaxValue(BasicValueFactory &BVF) : BVF(BVF) {} - Optional<RangeInt> operator()(QualType Ty) { + std::optional<RangeInt> operator()(QualType Ty) { return BVF.getMaxValue(Ty).getLimitedValue(); } - Optional<RangeInt> operator()(Optional<QualType> Ty) { + std::optional<RangeInt> operator()(std::optional<QualType> Ty) { if (Ty) { return operator()(*Ty); } - return None; + return std::nullopt; } } getMaxValue(BVF); @@ -1278,7 +1304,7 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( // The platform dependent value of EOF. // Try our best to parse this from the Preprocessor, otherwise fallback to -1. const auto EOFv = [&C]() -> RangeInt { - if (const llvm::Optional<int> OptInt = + if (const std::optional<int> OptInt = tryExpandAsInteger("EOF", C.getPreprocessor())) return *OptInt; return -1; @@ -1351,13 +1377,13 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( auto operator()(RangeInt b, RangeInt e) { return IntRangeVector{std::pair<RangeInt, RangeInt>{b, e}}; } - auto operator()(RangeInt b, Optional<RangeInt> e) { + auto operator()(RangeInt b, std::optional<RangeInt> e) { if (e) return IntRangeVector{std::pair<RangeInt, RangeInt>{b, *e}}; return IntRangeVector{}; } auto operator()(std::pair<RangeInt, RangeInt> i0, - std::pair<RangeInt, Optional<RangeInt>> i1) { + std::pair<RangeInt, std::optional<RangeInt>> i1) { if (i1.second) return IntRangeVector{i0, {i1.first, *(i1.second)}}; return IntRangeVector{i0}; @@ -1370,10 +1396,18 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( auto NotNull = [&](ArgNo ArgN) { return std::make_shared<NotNullConstraint>(ArgN); }; + auto IsNull = [&](ArgNo ArgN) { + return std::make_shared<NotNullConstraint>(ArgN, false); + }; - Optional<QualType> FileTy = lookupTy("FILE"); - Optional<QualType> FilePtrTy = getPointerTy(FileTy); - Optional<QualType> FilePtrRestrictTy = getRestrictTy(FilePtrTy); + std::optional<QualType> FileTy = lookupTy("FILE"); + std::optional<QualType> FilePtrTy = getPointerTy(FileTy); + std::optional<QualType> FilePtrRestrictTy = getRestrictTy(FilePtrTy); + + std::optional<QualType> FPosTTy = lookupTy("fpos_t"); + std::optional<QualType> FPosTPtrTy = getPointerTy(FPosTTy); + std::optional<QualType> ConstFPosTPtrTy = getPointerTy(getConstTy(FPosTTy)); + std::optional<QualType> FPosTPtrRestrictTy = getRestrictTy(FPosTPtrTy); // We are finally ready to define specifications for all supported functions. // @@ -1599,11 +1633,23 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( // read()-like functions that never return more than buffer size. auto FreadSummary = Summary(NoEvalCall) - .Case({ReturnValueCondition(LessThanOrEq, ArgNo(2)), + .Case({ArgumentCondition(1U, WithinRange, Range(1, SizeMax)), + ArgumentCondition(2U, WithinRange, Range(1, SizeMax)), + ReturnValueCondition(BO_LT, ArgNo(2)), ReturnValueCondition(WithinRange, Range(0, SizeMax))}, - ErrnoIrrelevant) + ErrnoNEZeroIrrelevant) + .Case({ArgumentCondition(1U, WithinRange, Range(1, SizeMax)), + ReturnValueCondition(BO_EQ, ArgNo(2)), + ReturnValueCondition(WithinRange, Range(0, SizeMax))}, + ErrnoMustNotBeChecked) + .Case({ArgumentCondition(1U, WithinRange, SingleValue(0)), + ReturnValueCondition(WithinRange, SingleValue(0))}, + ErrnoMustNotBeChecked) .ArgConstraint(NotNull(ArgNo(0))) .ArgConstraint(NotNull(ArgNo(3))) + // FIXME: It should be allowed to have a null buffer if any of + // args 1 or 2 are zero. Remove NotNull check of arg 0, add a check + // for non-null buffer if non-zero size to BufferSizeConstraint? .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(0), /*BufSize=*/ArgNo(1), /*BufSizeMultiplier=*/ArgNo(2))); @@ -1622,8 +1668,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( RetType{SizeTy}), FreadSummary); - Optional<QualType> Ssize_tTy = lookupTy("ssize_t"); - Optional<RangeInt> Ssize_tMax = getMaxValue(Ssize_tTy); + std::optional<QualType> Ssize_tTy = lookupTy("ssize_t"); + std::optional<RangeInt> Ssize_tMax = getMaxValue(Ssize_tTy); auto ReadSummary = Summary(NoEvalCall) @@ -1689,6 +1735,142 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( } if (ModelPOSIX) { + const auto ReturnsZeroOrMinusOne = + ConstraintSet{ReturnValueCondition(WithinRange, Range(-1, 0))}; + const auto ReturnsZero = + ConstraintSet{ReturnValueCondition(WithinRange, SingleValue(0))}; + const auto ReturnsMinusOne = + ConstraintSet{ReturnValueCondition(WithinRange, SingleValue(-1))}; + const auto ReturnsNonnegative = + ConstraintSet{ReturnValueCondition(WithinRange, Range(0, IntMax))}; + const auto ReturnsNonZero = + ConstraintSet{ReturnValueCondition(OutOfRange, SingleValue(0))}; + const auto ReturnsFileDescriptor = + ConstraintSet{ReturnValueCondition(WithinRange, Range(-1, IntMax))}; + const auto &ReturnsValidFileDescriptor = ReturnsNonnegative; + + // FILE *fopen(const char *restrict pathname, const char *restrict mode); + addToFunctionSummaryMap( + "fopen", + Signature(ArgTypes{ConstCharPtrRestrictTy, ConstCharPtrRestrictTy}, + RetType{FilePtrTy}), + Summary(NoEvalCall) + .Case({NotNull(Ret)}, ErrnoMustNotBeChecked) + .Case({IsNull(Ret)}, ErrnoNEZeroIrrelevant) + .ArgConstraint(NotNull(ArgNo(0))) + .ArgConstraint(NotNull(ArgNo(1)))); + + // FILE *tmpfile(void); + addToFunctionSummaryMap("tmpfile", + Signature(ArgTypes{}, RetType{FilePtrTy}), + Summary(NoEvalCall) + .Case({NotNull(Ret)}, ErrnoMustNotBeChecked) + .Case({IsNull(Ret)}, ErrnoNEZeroIrrelevant)); + + // FILE *freopen(const char *restrict pathname, const char *restrict mode, + // FILE *restrict stream); + addToFunctionSummaryMap( + "freopen", + Signature(ArgTypes{ConstCharPtrRestrictTy, ConstCharPtrRestrictTy, + FilePtrRestrictTy}, + RetType{FilePtrTy}), + Summary(NoEvalCall) + .Case({ReturnValueCondition(BO_EQ, ArgNo(2))}, + ErrnoMustNotBeChecked) + .Case({IsNull(Ret)}, ErrnoNEZeroIrrelevant) + .ArgConstraint(NotNull(ArgNo(1))) + .ArgConstraint(NotNull(ArgNo(2)))); + + // int fclose(FILE *stream); + addToFunctionSummaryMap( + "fclose", Signature(ArgTypes{FilePtrTy}, RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZero, ErrnoMustNotBeChecked) + .Case({ReturnValueCondition(WithinRange, SingleValue(EOFv))}, + ErrnoNEZeroIrrelevant) + .ArgConstraint(NotNull(ArgNo(0)))); + + // int fseek(FILE *stream, long offset, int whence); + // FIXME: It can be possible to get the 'SEEK_' values (like EOFv) and use + // these for condition of arg 2. + // Now the range [0,2] is used (the `SEEK_*` constants are usually 0,1,2). + addToFunctionSummaryMap( + "fseek", Signature(ArgTypes{FilePtrTy, LongTy, IntTy}, RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZero, ErrnoMustNotBeChecked) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant) + .ArgConstraint(NotNull(ArgNo(0))) + .ArgConstraint(ArgumentCondition(2, WithinRange, {{0, 2}}))); + + // int fgetpos(FILE *restrict stream, fpos_t *restrict pos); + // From 'The Open Group Base Specifications Issue 7, 2018 edition': + // "The fgetpos() function shall not change the setting of errno if + // successful." + addToFunctionSummaryMap( + "fgetpos", + Signature(ArgTypes{FilePtrRestrictTy, FPosTPtrRestrictTy}, + RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZero, ErrnoUnchanged) + .Case(ReturnsNonZero, ErrnoNEZeroIrrelevant) + .ArgConstraint(NotNull(ArgNo(0))) + .ArgConstraint(NotNull(ArgNo(1)))); + + // int fsetpos(FILE *stream, const fpos_t *pos); + // From 'The Open Group Base Specifications Issue 7, 2018 edition': + // "The fsetpos() function shall not change the setting of errno if + // successful." + addToFunctionSummaryMap( + "fsetpos", + Signature(ArgTypes{FilePtrTy, ConstFPosTPtrTy}, RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZero, ErrnoUnchanged) + .Case(ReturnsNonZero, ErrnoNEZeroIrrelevant) + .ArgConstraint(NotNull(ArgNo(0))) + .ArgConstraint(NotNull(ArgNo(1)))); + + // long ftell(FILE *stream); + // From 'The Open Group Base Specifications Issue 7, 2018 edition': + // "The ftell() function shall not change the setting of errno if + // successful." + addToFunctionSummaryMap( + "ftell", Signature(ArgTypes{FilePtrTy}, RetType{LongTy}), + Summary(NoEvalCall) + .Case({ReturnValueCondition(WithinRange, Range(1, LongMax))}, + ErrnoUnchanged) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant) + .ArgConstraint(NotNull(ArgNo(0)))); + + // int fileno(FILE *stream); + addToFunctionSummaryMap( + "fileno", Signature(ArgTypes{FilePtrTy}, RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsValidFileDescriptor, ErrnoMustNotBeChecked) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant) + .ArgConstraint(NotNull(ArgNo(0)))); + + // void rewind(FILE *stream); + // This function indicates error only by setting of 'errno'. + addToFunctionSummaryMap("rewind", + Signature(ArgTypes{FilePtrTy}, RetType{VoidTy}), + Summary(NoEvalCall) + .Case({}, ErrnoMustBeChecked) + .ArgConstraint(NotNull(ArgNo(0)))); + + // void clearerr(FILE *stream); + addToFunctionSummaryMap( + "clearerr", Signature(ArgTypes{FilePtrTy}, RetType{VoidTy}), + Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); + + // int feof(FILE *stream); + addToFunctionSummaryMap( + "feof", Signature(ArgTypes{FilePtrTy}, RetType{IntTy}), + Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); + + // int ferror(FILE *stream); + addToFunctionSummaryMap( + "ferror", Signature(ArgTypes{FilePtrTy}, RetType{IntTy}), + Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); // long a64l(const char *str64); addToFunctionSummaryMap( @@ -1702,18 +1884,6 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( .ArgConstraint(ArgumentCondition( 0, WithinRange, Range(0, LongMax)))); - const auto ReturnsZeroOrMinusOne = - ConstraintSet{ReturnValueCondition(WithinRange, Range(-1, 0))}; - const auto ReturnsZero = - ConstraintSet{ReturnValueCondition(WithinRange, SingleValue(0))}; - const auto ReturnsMinusOne = - ConstraintSet{ReturnValueCondition(WithinRange, SingleValue(-1))}; - const auto ReturnsNonnegative = - ConstraintSet{ReturnValueCondition(WithinRange, Range(0, IntMax))}; - const auto ReturnsFileDescriptor = - ConstraintSet{ReturnValueCondition(WithinRange, Range(-1, IntMax))}; - const auto &ReturnsValidFileDescriptor = ReturnsNonnegative; - // int access(const char *pathname, int amode); addToFunctionSummaryMap( "access", Signature(ArgTypes{ConstCharPtrTy, IntTy}, RetType{IntTy}), @@ -1777,7 +1947,7 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( .ArgConstraint(ArgumentCondition( 0, WithinRange, Range(0, IntMax)))); - Optional<QualType> Off_tTy = lookupTy("off_t"); + std::optional<QualType> Off_tTy = lookupTy("off_t"); // int truncate(const char *path, off_t length); addToFunctionSummaryMap( @@ -1819,7 +1989,7 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( .ArgConstraint( ArgumentCondition(0, WithinRange, Range(0, IntMax)))); - Optional<QualType> Mode_tTy = lookupTy("mode_t"); + std::optional<QualType> Mode_tTy = lookupTy("mode_t"); // int creat(const char *pathname, mode_t mode); addToFunctionSummaryMap( @@ -1836,8 +2006,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( .ArgConstraint( ArgumentCondition(0, WithinRange, Range(0, UnsignedIntMax)))); - Optional<QualType> DirTy = lookupTy("DIR"); - Optional<QualType> DirPtrTy = getPointerTy(DirTy); + std::optional<QualType> DirTy = lookupTy("DIR"); + std::optional<QualType> DirPtrTy = getPointerTy(DirTy); // int dirfd(DIR *dirp); addToFunctionSummaryMap( @@ -1920,7 +2090,7 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant) .ArgConstraint(NotNull(ArgNo(1)))); - Optional<QualType> Dev_tTy = lookupTy("dev_t"); + std::optional<QualType> Dev_tTy = lookupTy("dev_t"); // int mknod(const char *pathname, mode_t mode, dev_t dev); addToFunctionSummaryMap( @@ -1969,8 +2139,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( .ArgConstraint( ArgumentCondition(0, WithinRange, Range(0, IntMax)))); - Optional<QualType> Uid_tTy = lookupTy("uid_t"); - Optional<QualType> Gid_tTy = lookupTy("gid_t"); + std::optional<QualType> Uid_tTy = lookupTy("uid_t"); + std::optional<QualType> Gid_tTy = lookupTy("gid_t"); // int fchownat(int dirfd, const char *pathname, uid_t owner, gid_t group, // int flags); @@ -2069,9 +2239,10 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax))) .ArgConstraint(NotNull(ArgNo(1)))); - Optional<QualType> StructStatTy = lookupTy("stat"); - Optional<QualType> StructStatPtrTy = getPointerTy(StructStatTy); - Optional<QualType> StructStatPtrRestrictTy = getRestrictTy(StructStatPtrTy); + std::optional<QualType> StructStatTy = lookupTy("stat"); + std::optional<QualType> StructStatPtrTy = getPointerTy(StructStatTy); + std::optional<QualType> StructStatPtrRestrictTy = + getRestrictTy(StructStatPtrTy); // int fstat(int fd, struct stat *statbuf); addToFunctionSummaryMap( @@ -2200,14 +2371,6 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( "rand_r", Signature(ArgTypes{UnsignedIntPtrTy}, RetType{IntTy}), Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); - // int fileno(FILE *stream); - addToFunctionSummaryMap( - "fileno", Signature(ArgTypes{FilePtrTy}, RetType{IntTy}), - Summary(NoEvalCall) - .Case(ReturnsValidFileDescriptor, ErrnoMustNotBeChecked) - .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant) - .ArgConstraint(NotNull(ArgNo(0)))); - // int fseeko(FILE *stream, off_t offset, int whence); addToFunctionSummaryMap( "fseeko", @@ -2233,7 +2396,7 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( .ArgConstraint( ArgumentCondition(4, WithinRange, Range(-1, IntMax)))); - Optional<QualType> Off64_tTy = lookupTy("off64_t"); + std::optional<QualType> Off64_tTy = lookupTy("off64_t"); // void *mmap64(void *addr, size_t length, int prot, int flags, int fd, // off64_t offset); // FIXME: Improve for errno modeling. @@ -2358,18 +2521,20 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( .ArgConstraint(NotNull(ArgNo(1))) .ArgConstraint(NotNull(ArgNo(2)))); - Optional<QualType> StructSockaddrTy = lookupTy("sockaddr"); - Optional<QualType> StructSockaddrPtrTy = getPointerTy(StructSockaddrTy); - Optional<QualType> ConstStructSockaddrPtrTy = + std::optional<QualType> StructSockaddrTy = lookupTy("sockaddr"); + std::optional<QualType> StructSockaddrPtrTy = + getPointerTy(StructSockaddrTy); + std::optional<QualType> ConstStructSockaddrPtrTy = getPointerTy(getConstTy(StructSockaddrTy)); - Optional<QualType> StructSockaddrPtrRestrictTy = + std::optional<QualType> StructSockaddrPtrRestrictTy = getRestrictTy(StructSockaddrPtrTy); - Optional<QualType> ConstStructSockaddrPtrRestrictTy = + std::optional<QualType> ConstStructSockaddrPtrRestrictTy = getRestrictTy(ConstStructSockaddrPtrTy); - Optional<QualType> Socklen_tTy = lookupTy("socklen_t"); - Optional<QualType> Socklen_tPtrTy = getPointerTy(Socklen_tTy); - Optional<QualType> Socklen_tPtrRestrictTy = getRestrictTy(Socklen_tPtrTy); - Optional<RangeInt> Socklen_tMax = getMaxValue(Socklen_tTy); + std::optional<QualType> Socklen_tTy = lookupTy("socklen_t"); + std::optional<QualType> Socklen_tPtrTy = getPointerTy(Socklen_tTy); + std::optional<QualType> Socklen_tPtrRestrictTy = + getRestrictTy(Socklen_tPtrTy); + std::optional<RangeInt> Socklen_tMax = getMaxValue(Socklen_tTy); // In 'socket.h' of some libc implementations with C99, sockaddr parameter // is a transparent union of the underlying sockaddr_ family of pointers @@ -2568,9 +2733,9 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(1), /*BufSize=*/ArgNo(2)))); - Optional<QualType> StructMsghdrTy = lookupTy("msghdr"); - Optional<QualType> StructMsghdrPtrTy = getPointerTy(StructMsghdrTy); - Optional<QualType> ConstStructMsghdrPtrTy = + std::optional<QualType> StructMsghdrTy = lookupTy("msghdr"); + std::optional<QualType> StructMsghdrPtrTy = getPointerTy(StructMsghdrTy); + std::optional<QualType> ConstStructMsghdrPtrTy = getPointerTy(getConstTy(StructMsghdrTy)); // ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags); @@ -2676,8 +2841,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( .ArgConstraint( ArgumentCondition(5, WithinRange, Range(0, Socklen_tMax)))); - Optional<QualType> StructUtimbufTy = lookupTy("utimbuf"); - Optional<QualType> StructUtimbufPtrTy = getPointerTy(StructUtimbufTy); + std::optional<QualType> StructUtimbufTy = lookupTy("utimbuf"); + std::optional<QualType> StructUtimbufPtrTy = getPointerTy(StructUtimbufTy); // int utime(const char *filename, struct utimbuf *buf); addToFunctionSummaryMap( @@ -2688,9 +2853,10 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant) .ArgConstraint(NotNull(ArgNo(0)))); - Optional<QualType> StructTimespecTy = lookupTy("timespec"); - Optional<QualType> StructTimespecPtrTy = getPointerTy(StructTimespecTy); - Optional<QualType> ConstStructTimespecPtrTy = + std::optional<QualType> StructTimespecTy = lookupTy("timespec"); + std::optional<QualType> StructTimespecPtrTy = + getPointerTy(StructTimespecTy); + std::optional<QualType> ConstStructTimespecPtrTy = getPointerTy(getConstTy(StructTimespecTy)); // int futimens(int fd, const struct timespec times[2]); @@ -2714,8 +2880,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant) .ArgConstraint(NotNull(ArgNo(1)))); - Optional<QualType> StructTimevalTy = lookupTy("timeval"); - Optional<QualType> ConstStructTimevalPtrTy = + std::optional<QualType> StructTimevalTy = lookupTy("timeval"); + std::optional<QualType> ConstStructTimevalPtrTy = getPointerTy(getConstTy(StructTimevalTy)); // int utimes(const char *filename, const struct timeval times[2]); @@ -2738,17 +2904,19 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant) .ArgConstraint(NotNull(ArgNo(0)))); - Optional<QualType> Time_tTy = lookupTy("time_t"); - Optional<QualType> ConstTime_tPtrTy = getPointerTy(getConstTy(Time_tTy)); - Optional<QualType> ConstTime_tPtrRestrictTy = + std::optional<QualType> Time_tTy = lookupTy("time_t"); + std::optional<QualType> ConstTime_tPtrTy = + getPointerTy(getConstTy(Time_tTy)); + std::optional<QualType> ConstTime_tPtrRestrictTy = getRestrictTy(ConstTime_tPtrTy); - Optional<QualType> StructTmTy = lookupTy("tm"); - Optional<QualType> StructTmPtrTy = getPointerTy(StructTmTy); - Optional<QualType> StructTmPtrRestrictTy = getRestrictTy(StructTmPtrTy); - Optional<QualType> ConstStructTmPtrTy = + std::optional<QualType> StructTmTy = lookupTy("tm"); + std::optional<QualType> StructTmPtrTy = getPointerTy(StructTmTy); + std::optional<QualType> StructTmPtrRestrictTy = + getRestrictTy(StructTmPtrTy); + std::optional<QualType> ConstStructTmPtrTy = getPointerTy(getConstTy(StructTmTy)); - Optional<QualType> ConstStructTmPtrRestrictTy = + std::optional<QualType> ConstStructTmPtrRestrictTy = getRestrictTy(ConstStructTmPtrTy); // struct tm * localtime(const time_t *tp); @@ -2804,7 +2972,7 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( "gmtime", Signature(ArgTypes{ConstTime_tPtrTy}, RetType{StructTmPtrTy}), Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); - Optional<QualType> Clockid_tTy = lookupTy("clockid_t"); + std::optional<QualType> Clockid_tTy = lookupTy("clockid_t"); // int clock_gettime(clockid_t clock_id, struct timespec *tp); addToFunctionSummaryMap( @@ -2815,8 +2983,9 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant) .ArgConstraint(NotNull(ArgNo(1)))); - Optional<QualType> StructItimervalTy = lookupTy("itimerval"); - Optional<QualType> StructItimervalPtrTy = getPointerTy(StructItimervalTy); + std::optional<QualType> StructItimervalTy = lookupTy("itimerval"); + std::optional<QualType> StructItimervalPtrTy = + getPointerTy(StructItimervalTy); // int getitimer(int which, struct itimerval *curr_value); addToFunctionSummaryMap( @@ -2827,25 +2996,30 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant) .ArgConstraint(NotNull(ArgNo(1)))); - Optional<QualType> Pthread_cond_tTy = lookupTy("pthread_cond_t"); - Optional<QualType> Pthread_cond_tPtrTy = getPointerTy(Pthread_cond_tTy); - Optional<QualType> Pthread_tTy = lookupTy("pthread_t"); - Optional<QualType> Pthread_tPtrTy = getPointerTy(Pthread_tTy); - Optional<QualType> Pthread_tPtrRestrictTy = getRestrictTy(Pthread_tPtrTy); - Optional<QualType> Pthread_mutex_tTy = lookupTy("pthread_mutex_t"); - Optional<QualType> Pthread_mutex_tPtrTy = getPointerTy(Pthread_mutex_tTy); - Optional<QualType> Pthread_mutex_tPtrRestrictTy = + std::optional<QualType> Pthread_cond_tTy = lookupTy("pthread_cond_t"); + std::optional<QualType> Pthread_cond_tPtrTy = + getPointerTy(Pthread_cond_tTy); + std::optional<QualType> Pthread_tTy = lookupTy("pthread_t"); + std::optional<QualType> Pthread_tPtrTy = getPointerTy(Pthread_tTy); + std::optional<QualType> Pthread_tPtrRestrictTy = + getRestrictTy(Pthread_tPtrTy); + std::optional<QualType> Pthread_mutex_tTy = lookupTy("pthread_mutex_t"); + std::optional<QualType> Pthread_mutex_tPtrTy = + getPointerTy(Pthread_mutex_tTy); + std::optional<QualType> Pthread_mutex_tPtrRestrictTy = getRestrictTy(Pthread_mutex_tPtrTy); - Optional<QualType> Pthread_attr_tTy = lookupTy("pthread_attr_t"); - Optional<QualType> Pthread_attr_tPtrTy = getPointerTy(Pthread_attr_tTy); - Optional<QualType> ConstPthread_attr_tPtrTy = + std::optional<QualType> Pthread_attr_tTy = lookupTy("pthread_attr_t"); + std::optional<QualType> Pthread_attr_tPtrTy = + getPointerTy(Pthread_attr_tTy); + std::optional<QualType> ConstPthread_attr_tPtrTy = getPointerTy(getConstTy(Pthread_attr_tTy)); - Optional<QualType> ConstPthread_attr_tPtrRestrictTy = + std::optional<QualType> ConstPthread_attr_tPtrRestrictTy = getRestrictTy(ConstPthread_attr_tPtrTy); - Optional<QualType> Pthread_mutexattr_tTy = lookupTy("pthread_mutexattr_t"); - Optional<QualType> ConstPthread_mutexattr_tPtrTy = + std::optional<QualType> Pthread_mutexattr_tTy = + lookupTy("pthread_mutexattr_t"); + std::optional<QualType> ConstPthread_mutexattr_tPtrTy = getPointerTy(getConstTy(Pthread_mutexattr_tTy)); - Optional<QualType> ConstPthread_mutexattr_tPtrRestrictTy = + std::optional<QualType> ConstPthread_mutexattr_tPtrRestrictTy = getRestrictTy(ConstPthread_mutexattr_tPtrTy); QualType PthreadStartRoutineTy = getPointerTy( @@ -2929,6 +3103,10 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( // Test range values. addToFunctionSummaryMap( + "__single_val_0", Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) + .ArgConstraint(ArgumentCondition(0U, WithinRange, SingleValue(0)))); + addToFunctionSummaryMap( "__single_val_1", Signature(ArgTypes{IntTy}, RetType{IntTy}), Summary(EvalCallAsPure) .ArgConstraint(ArgumentCondition(0U, WithinRange, SingleValue(1)))); @@ -2996,6 +3174,15 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( "__test_restrict_param_2"}, Signature(ArgTypes{VoidPtrRestrictTy}, RetType{VoidTy}), Summary(EvalCallAsPure)); + + // Test the application of cases. + addToFunctionSummaryMap( + "__test_case_note", Signature(ArgTypes{}, RetType{IntTy}), + Summary(EvalCallAsPure) + .Case({ReturnValueCondition(WithinRange, SingleValue(0))}, + ErrnoIrrelevant, "Function returns 0") + .Case({ReturnValueCondition(WithinRange, SingleValue(1))}, + ErrnoIrrelevant, "Function returns 1")); } SummariesInitialized = true; diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 1aa665f0ef45..3f61dd823940 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -17,10 +17,12 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" #include <functional> +#include <optional> using namespace clang; using namespace ento; @@ -85,10 +87,10 @@ const StreamErrorState ErrorFError{false, false, true}; /// Full state information about a stream pointer. struct StreamState { /// The last file operation called in the stream. + /// Can be nullptr. const FnDescription *LastOperation; /// State of a stream symbol. - /// FIXME: We need maybe an "escaped" state later. enum KindTy { Opened, /// Stream is opened. Closed, /// Closed stream (an invalid stream pointer after it was closed). @@ -202,13 +204,12 @@ ProgramStateRef bindAndAssumeTrue(ProgramStateRef State, CheckerContext &C, ProgramStateRef bindInt(uint64_t Value, ProgramStateRef State, CheckerContext &C, const CallExpr *CE) { State = State->BindExpr(CE, C.getLocationContext(), - C.getSValBuilder().makeIntVal(Value, false)); + C.getSValBuilder().makeIntVal(Value, CE->getType())); return State; } class StreamChecker : public Checker<check::PreCall, eval::Call, check::DeadSymbols, check::PointerEscape> { - BugType BT_FileNull{this, "NULL stream pointer", "Stream handling error"}; BugType BT_UseAfterClose{this, "Closed stream", "Stream handling error"}; BugType BT_UseAfterOpenFailed{this, "Invalid stream", "Stream handling error"}; @@ -236,48 +237,55 @@ public: private: CallDescriptionMap<FnDescription> FnDescriptions = { - {{"fopen"}, {nullptr, &StreamChecker::evalFopen, ArgNone}}, - {{"freopen", 3}, + {{{"fopen"}}, {nullptr, &StreamChecker::evalFopen, ArgNone}}, + {{{"freopen"}, 3}, {&StreamChecker::preFreopen, &StreamChecker::evalFreopen, 2}}, - {{"tmpfile"}, {nullptr, &StreamChecker::evalFopen, ArgNone}}, - {{"fclose", 1}, + {{{"tmpfile"}}, {nullptr, &StreamChecker::evalFopen, ArgNone}}, + {{{"fclose"}, 1}, {&StreamChecker::preDefault, &StreamChecker::evalFclose, 0}}, - {{"fread", 4}, + {{{"fread"}, 4}, {&StreamChecker::preFread, std::bind(&StreamChecker::evalFreadFwrite, _1, _2, _3, _4, true), 3}}, - {{"fwrite", 4}, + {{{"fwrite"}, 4}, {&StreamChecker::preFwrite, std::bind(&StreamChecker::evalFreadFwrite, _1, _2, _3, _4, false), 3}}, - {{"fseek", 3}, {&StreamChecker::preFseek, &StreamChecker::evalFseek, 0}}, - {{"ftell", 1}, {&StreamChecker::preDefault, nullptr, 0}}, - {{"rewind", 1}, {&StreamChecker::preDefault, nullptr, 0}}, - {{"fgetpos", 2}, {&StreamChecker::preDefault, nullptr, 0}}, - {{"fsetpos", 2}, {&StreamChecker::preDefault, nullptr, 0}}, - {{"clearerr", 1}, + {{{"fseek"}, 3}, + {&StreamChecker::preFseek, &StreamChecker::evalFseek, 0}}, + {{{"ftell"}, 1}, + {&StreamChecker::preDefault, &StreamChecker::evalFtell, 0}}, + {{{"rewind"}, 1}, + {&StreamChecker::preDefault, &StreamChecker::evalRewind, 0}}, + {{{"fgetpos"}, 2}, + {&StreamChecker::preDefault, &StreamChecker::evalFgetpos, 0}}, + {{{"fsetpos"}, 2}, + {&StreamChecker::preDefault, &StreamChecker::evalFsetpos, 0}}, + {{{"clearerr"}, 1}, {&StreamChecker::preDefault, &StreamChecker::evalClearerr, 0}}, - {{"feof", 1}, + {{{"feof"}, 1}, {&StreamChecker::preDefault, std::bind(&StreamChecker::evalFeofFerror, _1, _2, _3, _4, ErrorFEof), 0}}, - {{"ferror", 1}, + {{{"ferror"}, 1}, {&StreamChecker::preDefault, std::bind(&StreamChecker::evalFeofFerror, _1, _2, _3, _4, ErrorFError), 0}}, - {{"fileno", 1}, {&StreamChecker::preDefault, nullptr, 0}}, + {{{"fileno"}, 1}, {&StreamChecker::preDefault, nullptr, 0}}, }; CallDescriptionMap<FnDescription> FnTestDescriptions = { - {{"StreamTesterChecker_make_feof_stream", 1}, + {{{"StreamTesterChecker_make_feof_stream"}, 1}, {nullptr, std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4, ErrorFEof), 0}}, - {{"StreamTesterChecker_make_ferror_stream", 1}, + {{{"StreamTesterChecker_make_ferror_stream"}, 1}, {nullptr, std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4, ErrorFError), 0}}, }; + mutable std::optional<int> EofVal; + void evalFopen(const FnDescription *Desc, const CallEvent &Call, CheckerContext &C) const; @@ -303,6 +311,18 @@ private: void evalFseek(const FnDescription *Desc, const CallEvent &Call, CheckerContext &C) const; + void evalFgetpos(const FnDescription *Desc, const CallEvent &Call, + CheckerContext &C) const; + + void evalFsetpos(const FnDescription *Desc, const CallEvent &Call, + CheckerContext &C) const; + + void evalFtell(const FnDescription *Desc, const CallEvent &Call, + CheckerContext &C) const; + + void evalRewind(const FnDescription *Desc, const CallEvent &Call, + CheckerContext &C) const; + void preDefault(const FnDescription *Desc, const CallEvent &Call, CheckerContext &C) const; @@ -318,7 +338,7 @@ private: const StreamErrorState &ErrorKind) const; /// Check that the stream (in StreamVal) is not NULL. - /// If it can only be NULL a fatal error is emitted and nullptr returned. + /// If it can only be NULL a sink node is generated and nullptr returned. /// Otherwise the return value is a new state where the stream is constrained /// to be non-null. ProgramStateRef ensureStreamNonNull(SVal StreamVal, const Expr *StreamE, @@ -368,7 +388,7 @@ private: // (and matching name) as stream functions. if (!Call.isGlobalCFunction()) return nullptr; - for (auto P : Call.parameters()) { + for (auto *P : Call.parameters()) { QualType T = P->getType(); if (!T->isIntegralOrEnumerationType() && !T->isPointerType()) return nullptr; @@ -411,6 +431,17 @@ private: }); } + void initEof(CheckerContext &C) const { + if (EofVal) + return; + + if (const std::optional<int> OptInt = + tryExpandAsInteger("EOF", C.getPreprocessor())) + EofVal = *OptInt; + else + EofVal = -1; + } + /// Searches for the ExplodedNode where the file descriptor was acquired for /// StreamSym. static const ExplodedNode *getAcquisitionSite(const ExplodedNode *N, @@ -426,8 +457,7 @@ private: REGISTER_MAP_WITH_PROGRAMSTATE(StreamMap, SymbolRef, StreamState) inline void assertStreamStateOpened(const StreamState *SS) { - assert(SS->isOpened() && - "Previous create of error node for non-opened stream failed?"); + assert(SS->isOpened() && "Stream is expected to be opened"); } const ExplodedNode *StreamChecker::getAcquisitionSite(const ExplodedNode *N, @@ -457,6 +487,8 @@ const ExplodedNode *StreamChecker::getAcquisitionSite(const ExplodedNode *N, void StreamChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { + initEof(C); + const FnDescription *Desc = lookupFn(Call); if (!Desc || !Desc->PreFn) return; @@ -526,7 +558,7 @@ void StreamChecker::evalFreopen(const FnDescription *Desc, if (!CE) return; - Optional<DefinedSVal> StreamVal = + std::optional<DefinedSVal> StreamVal = getStreamArg(Desc, Call).getAs<DefinedSVal>(); if (!StreamVal) return; @@ -574,6 +606,10 @@ void StreamChecker::evalFclose(const FnDescription *Desc, const CallEvent &Call, if (!SS) return; + auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); + if (!CE) + return; + assertStreamStateOpened(SS); // Close the File Descriptor. @@ -581,7 +617,16 @@ void StreamChecker::evalFclose(const FnDescription *Desc, const CallEvent &Call, // and can not be used any more. State = State->set<StreamMap>(Sym, StreamState::getClosed(Desc)); - C.addTransition(State); + // Return 0 on success, EOF on failure. + SValBuilder &SVB = C.getSValBuilder(); + ProgramStateRef StateSuccess = State->BindExpr( + CE, C.getLocationContext(), SVB.makeIntVal(0, C.getASTContext().IntTy)); + ProgramStateRef StateFailure = + State->BindExpr(CE, C.getLocationContext(), + SVB.makeIntVal(*EofVal, C.getASTContext().IntTy)); + + C.addTransition(StateSuccess); + C.addTransition(StateFailure); } void StreamChecker::preFread(const FnDescription *Desc, const CallEvent &Call, @@ -639,10 +684,10 @@ void StreamChecker::evalFreadFwrite(const FnDescription *Desc, if (!CE) return; - Optional<NonLoc> SizeVal = Call.getArgSVal(1).getAs<NonLoc>(); + std::optional<NonLoc> SizeVal = Call.getArgSVal(1).getAs<NonLoc>(); if (!SizeVal) return; - Optional<NonLoc> NMembVal = Call.getArgSVal(2).getAs<NonLoc>(); + std::optional<NonLoc> NMembVal = Call.getArgSVal(2).getAs<NonLoc>(); if (!NMembVal) return; @@ -766,6 +811,131 @@ void StreamChecker::evalFseek(const FnDescription *Desc, const CallEvent &Call, C.addTransition(StateFailed, constructSetEofNoteTag(C, StreamSym)); } +void StreamChecker::evalFgetpos(const FnDescription *Desc, + const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + SymbolRef Sym = getStreamArg(Desc, Call).getAsSymbol(); + if (!Sym) + return; + + // Do not evaluate if stream is not found. + if (!State->get<StreamMap>(Sym)) + return; + + auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); + if (!CE) + return; + + DefinedSVal RetVal = makeRetVal(C, CE); + State = State->BindExpr(CE, C.getLocationContext(), RetVal); + ProgramStateRef StateNotFailed, StateFailed; + std::tie(StateFailed, StateNotFailed) = + C.getConstraintManager().assumeDual(State, RetVal); + + // This function does not affect the stream state. + // Still we add success and failure state with the appropriate return value. + // StdLibraryFunctionsChecker can change these states (set the 'errno' state). + C.addTransition(StateNotFailed); + C.addTransition(StateFailed); +} + +void StreamChecker::evalFsetpos(const FnDescription *Desc, + const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); + if (!StreamSym) + return; + + const StreamState *SS = State->get<StreamMap>(StreamSym); + if (!SS) + return; + + auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); + if (!CE) + return; + + assertStreamStateOpened(SS); + + DefinedSVal RetVal = makeRetVal(C, CE); + State = State->BindExpr(CE, C.getLocationContext(), RetVal); + ProgramStateRef StateNotFailed, StateFailed; + std::tie(StateFailed, StateNotFailed) = + C.getConstraintManager().assumeDual(State, RetVal); + + StateNotFailed = StateNotFailed->set<StreamMap>( + StreamSym, StreamState::getOpened(Desc, ErrorNone, false)); + + // At failure ferror could be set. + // The standards do not tell what happens with the file position at failure. + // But we can assume that it is dangerous to make a next I/O operation after + // the position was not set correctly (similar to 'fseek'). + StateFailed = StateFailed->set<StreamMap>( + StreamSym, StreamState::getOpened(Desc, ErrorNone | ErrorFError, true)); + + C.addTransition(StateNotFailed); + C.addTransition(StateFailed); +} + +void StreamChecker::evalFtell(const FnDescription *Desc, const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + SymbolRef Sym = getStreamArg(Desc, Call).getAsSymbol(); + if (!Sym) + return; + + if (!State->get<StreamMap>(Sym)) + return; + + auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); + if (!CE) + return; + + SValBuilder &SVB = C.getSValBuilder(); + NonLoc RetVal = makeRetVal(C, CE).castAs<NonLoc>(); + ProgramStateRef StateNotFailed = + State->BindExpr(CE, C.getLocationContext(), RetVal); + auto Cond = SVB.evalBinOp(State, BO_GE, RetVal, + SVB.makeZeroVal(C.getASTContext().LongTy), + SVB.getConditionType()) + .getAs<DefinedOrUnknownSVal>(); + if (!Cond) + return; + StateNotFailed = StateNotFailed->assume(*Cond, true); + if (!StateNotFailed) + return; + + ProgramStateRef StateFailed = State->BindExpr( + CE, C.getLocationContext(), SVB.makeIntVal(-1, C.getASTContext().LongTy)); + + C.addTransition(StateNotFailed); + C.addTransition(StateFailed); +} + +void StreamChecker::evalRewind(const FnDescription *Desc, const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); + if (!StreamSym) + return; + + const StreamState *SS = State->get<StreamMap>(StreamSym); + if (!SS) + return; + + auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); + if (!CE) + return; + + assertStreamStateOpened(SS); + + State = State->set<StreamMap>(StreamSym, + StreamState::getOpened(Desc, ErrorNone, false)); + + C.addTransition(State); +} + void StreamChecker::evalClearerr(const FnDescription *Desc, const CallEvent &Call, CheckerContext &C) const { @@ -869,13 +1039,11 @@ StreamChecker::ensureStreamNonNull(SVal StreamVal, const Expr *StreamE, std::tie(StateNotNull, StateNull) = CM.assumeDual(C.getState(), *Stream); if (!StateNotNull && StateNull) { - if (ExplodedNode *N = C.generateErrorNode(StateNull)) { - auto R = std::make_unique<PathSensitiveBugReport>( - BT_FileNull, "Stream pointer might be NULL.", N); - if (StreamE) - bugreporter::trackExpressionValue(N, StreamE, *R); - C.emitReport(std::move(R)); - } + // Stream argument is NULL, stop analysis on this path. + // This case should occur only if StdLibraryFunctionsChecker (or ModelPOSIX + // option of it) is not turned on, otherwise that checker ensures non-null + // argument. + C.generateSink(StateNull, C.getPredecessor()); return nullptr; } @@ -976,7 +1144,8 @@ ProgramStateRef StreamChecker::ensureNoFilePositionIndeterminate( ProgramStateRef StreamChecker::ensureFseekWhenceCorrect(SVal WhenceVal, CheckerContext &C, ProgramStateRef State) const { - Optional<nonloc::ConcreteInt> CI = WhenceVal.getAs<nonloc::ConcreteInt>(); + std::optional<nonloc::ConcreteInt> CI = + WhenceVal.getAs<nonloc::ConcreteInt>(); if (!CI) return State; diff --git a/clang/lib/StaticAnalyzer/Checkers/Taint.cpp b/clang/lib/StaticAnalyzer/Checkers/Taint.cpp index 44162094e49a..a95c0e183284 100644 --- a/clang/lib/StaticAnalyzer/Checkers/Taint.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/Taint.cpp @@ -13,6 +13,7 @@ #include "clang/StaticAnalyzer/Checkers/Taint.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" +#include <optional> using namespace clang; using namespace ento; @@ -63,7 +64,7 @@ ProgramStateRef taint::addTaint(ProgramStateRef State, SVal V, // their parent region, which is a conjured symbol default-bound to the base // region of the parent region. if (auto LCV = V.getAs<nonloc::LazyCompoundVal>()) { - if (Optional<SVal> binding = + if (std::optional<SVal> binding = State->getStateManager().getStoreManager().getDefaultBinding( *LCV)) { if (SymbolRef Sym = binding->getAsSymbol()) diff --git a/clang/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp index eeec807ccee4..28fe11d5ed06 100644 --- a/clang/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp @@ -17,6 +17,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "llvm/ADT/FoldingSet.h" +#include <optional> using namespace clang; using namespace ento; @@ -100,7 +101,7 @@ DivisionBRVisitor::VisitNode(const ExplodedNode *Succ, BugReporterContext &BRC, const Expr *E = nullptr; - if (Optional<PostStmt> P = Succ->getLocationAs<PostStmt>()) + if (std::optional<PostStmt> P = Succ->getLocationAs<PostStmt>()) if (const BinaryOperator *BO = P->getStmtAs<BinaryOperator>()) { BinaryOperator::Opcode Op = BO->getOpcode(); if (Op == BO_Div || Op == BO_Rem || Op == BO_DivAssign || @@ -132,7 +133,7 @@ DivisionBRVisitor::VisitNode(const ExplodedNode *Succ, BugReporterContext &BRC, } bool TestAfterDivZeroChecker::isZero(SVal S, CheckerContext &C) const { - Optional<DefinedSVal> DSV = S.getAs<DefinedSVal>(); + std::optional<DefinedSVal> DSV = S.getAs<DefinedSVal>(); if (!DSV) return false; diff --git a/clang/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp index ebe5ad53cc30..b17b983f0345 100644 --- a/clang/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp @@ -18,6 +18,7 @@ #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include <optional> #include <utility> using namespace clang; @@ -93,7 +94,7 @@ void UndefBranchChecker::checkBranchCondition(const Stmt *Condition, ProgramPoint P = PrevN->getLocation(); ProgramStateRef St = N->getState(); - if (Optional<PostStmt> PS = P.getAs<PostStmt>()) + if (std::optional<PostStmt> PS = P.getAs<PostStmt>()) if (PS->getStmt() == Ex) St = PrevN->getState(); diff --git a/clang/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp index 816a547cadc3..27f3345e67ac 100644 --- a/clang/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp @@ -19,6 +19,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/raw_ostream.h" +#include <optional> using namespace clang; using namespace ento; @@ -69,8 +70,8 @@ UndefCapturedBlockVarChecker::checkPostStmt(const BlockExpr *BE, continue; // Get the VarRegion associated with VD in the local stack frame. - if (Optional<UndefinedVal> V = - state->getSVal(I.getOriginalRegion()).getAs<UndefinedVal>()) { + if (std::optional<UndefinedVal> V = + state->getSVal(I.getOriginalRegion()).getAs<UndefinedVal>()) { if (ExplodedNode *N = C.generateErrorNode()) { if (!BT) BT.reset( diff --git a/clang/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp index 05f8f6084c0b..0fa3d6043971 100644 --- a/clang/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp @@ -92,7 +92,7 @@ void UndefinedAssignmentChecker::checkBind(SVal location, SVal val, if (const auto *CD = dyn_cast<CXXConstructorDecl>(C.getStackFrame()->getDecl())) { if (CD->isImplicit()) { - for (auto I : CD->inits()) { + for (auto *I : CD->inits()) { if (I->getInit()->IgnoreImpCasts() == StoreE) { OS << "Value assigned to field '" << I->getMember()->getName() << "' in implicit constructor is garbage or undefined"; diff --git a/clang/lib/StaticAnalyzer/Checkers/UndefinedNewArraySizeChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/UndefinedNewArraySizeChecker.cpp new file mode 100644 index 000000000000..f053ee887a1a --- /dev/null +++ b/clang/lib/StaticAnalyzer/Checkers/UndefinedNewArraySizeChecker.cpp @@ -0,0 +1,80 @@ +//===--- UndefinedNewArraySizeChecker.cpp -----------------------*- C++ -*--==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This defines UndefinedNewArraySizeChecker, a builtin check in ExprEngine +// that checks if the size of the array in a new[] expression is undefined. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.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 { +class UndefinedNewArraySizeChecker : public Checker<check::PreCall> { + +private: + BugType BT{this, "Undefined array element count in new[]", + categories::LogicError}; + +public: + void checkPreCall(const CallEvent &Call, CheckerContext &C) const; + void HandleUndefinedArrayElementCount(CheckerContext &C, SVal ArgVal, + const Expr *Init, + SourceRange Range) const; +}; +} // namespace + +void UndefinedNewArraySizeChecker::checkPreCall(const CallEvent &Call, + CheckerContext &C) const { + if (const auto *AC = dyn_cast<CXXAllocatorCall>(&Call)) { + if (!AC->isArray()) + return; + + auto *SizeEx = *AC->getArraySizeExpr(); + auto SizeVal = AC->getArraySizeVal(); + + if (SizeVal.isUndef()) + HandleUndefinedArrayElementCount(C, SizeVal, SizeEx, + SizeEx->getSourceRange()); + } +} + +void UndefinedNewArraySizeChecker::HandleUndefinedArrayElementCount( + CheckerContext &C, SVal ArgVal, const Expr *Init, SourceRange Range) const { + + if (ExplodedNode *N = C.generateErrorNode()) { + + SmallString<100> buf; + llvm::raw_svector_ostream os(buf); + + os << "Element count in new[] is a garbage value"; + + auto R = std::make_unique<PathSensitiveBugReport>(BT, os.str(), N); + R->markInteresting(ArgVal); + R->addRange(Range); + bugreporter::trackExpressionValue(N, Init, *R); + + C.emitReport(std::move(R)); + } +} + +void ento::registerUndefinedNewArraySizeChecker(CheckerManager &mgr) { + mgr.registerChecker<UndefinedNewArraySizeChecker>(); +} + +bool ento::shouldRegisterUndefinedNewArraySizeChecker( + const CheckerManager &mgr) { + return mgr.getLangOpts().CPlusPlus; +} diff --git a/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp b/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp index f5bd765ff679..54e1e0e11909 100644 --- a/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp @@ -19,6 +19,7 @@ #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h" +#include <optional> using namespace clang; using namespace clang::ento; @@ -121,9 +122,9 @@ struct DereferenceInfo { /// Dereferences \p FR and returns with the pointee's region, and whether it /// needs to be casted back to it's location type. If for whatever reason -/// dereferencing fails, returns with None. -static llvm::Optional<DereferenceInfo> dereference(ProgramStateRef State, - const FieldRegion *FR); +/// dereferencing fails, returns std::nullopt. +static std::optional<DereferenceInfo> dereference(ProgramStateRef State, + const FieldRegion *FR); /// Returns whether \p T can be (transitively) dereferenced to a void pointer /// type (void*, void**, ...). @@ -159,7 +160,7 @@ bool FindUninitializedFields::isDereferencableUninit( // At this point the pointer itself is initialized and points to a valid // location, we'll now check the pointee. - llvm::Optional<DereferenceInfo> DerefInfo = dereference(State, FR); + std::optional<DereferenceInfo> DerefInfo = dereference(State, FR); if (!DerefInfo) { IsAnyFieldInitialized = true; return false; @@ -217,8 +218,8 @@ bool FindUninitializedFields::isDereferencableUninit( // Utility functions. //===----------------------------------------------------------------------===// -static llvm::Optional<DereferenceInfo> dereference(ProgramStateRef State, - const FieldRegion *FR) { +static std::optional<DereferenceInfo> dereference(ProgramStateRef State, + const FieldRegion *FR) { llvm::SmallSet<const TypedValueRegion *, 5> VisitedRegions; @@ -234,7 +235,7 @@ static llvm::Optional<DereferenceInfo> dereference(ProgramStateRef State, // The region we'd like to acquire. const auto *R = V.getAsRegion()->getAs<TypedValueRegion>(); if (!R) - return None; + return std::nullopt; VisitedRegions.insert(R); @@ -245,7 +246,7 @@ static llvm::Optional<DereferenceInfo> dereference(ProgramStateRef State, R = Tmp->getAs<TypedValueRegion>(); if (!R) - return None; + return std::nullopt; // We found a cyclic pointer, like int *ptr = (int *)&ptr. if (!VisitedRegions.insert(R).second) diff --git a/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp index aa3f4524798a..f503b3e88bb3 100644 --- a/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp @@ -17,11 +17,11 @@ #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" -#include "llvm/ADT/Optional.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/raw_ostream.h" +#include <optional> using namespace clang; using namespace ento; @@ -40,7 +40,7 @@ namespace { class UnixAPIMisuseChecker : public Checker< check::PreStmt<CallExpr> > { mutable std::unique_ptr<BugType> BT_open, BT_pthreadOnce; - mutable Optional<uint64_t> Val_O_CREAT; + mutable std::optional<uint64_t> Val_O_CREAT; public: void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; @@ -234,7 +234,7 @@ void UnixAPIMisuseChecker::CheckOpenVariant(CheckerContext &C, } NonLoc oflags = V.castAs<NonLoc>(); NonLoc ocreateFlag = C.getSValBuilder() - .makeIntVal(Val_O_CREAT.value(), oflagsEx->getType()) + .makeIntVal(*Val_O_CREAT, oflagsEx->getType()) .castAs<NonLoc>(); SVal maskedFlagsUC = C.getSValBuilder().evalBinOpNN(state, BO_And, oflags, ocreateFlag, diff --git a/clang/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp index d231be64c2e1..3ad6858ead46 100644 --- a/clang/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp @@ -24,6 +24,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" #include "llvm/ADT/SmallSet.h" +#include <optional> using namespace clang; using namespace ento; @@ -74,7 +75,7 @@ void UnreachableCodeChecker::checkEndAnalysis(ExplodedGraph &G, if (!PM) PM = &LC->getParentMap(); - if (Optional<BlockEntrance> BE = P.getAs<BlockEntrance>()) { + if (std::optional<BlockEntrance> BE = P.getAs<BlockEntrance>()) { const CFGBlock *CB = BE->getBlock(); reachable.insert(CB->getBlockID()); } @@ -129,7 +130,7 @@ void UnreachableCodeChecker::checkEndAnalysis(ExplodedGraph &G, bool foundUnreachable = false; for (CFGBlock::const_iterator ci = CB->begin(), ce = CB->end(); ci != ce; ++ci) { - if (Optional<CFGStmt> S = (*ci).getAs<CFGStmt>()) + if (std::optional<CFGStmt> S = (*ci).getAs<CFGStmt>()) if (const CallExpr *CE = dyn_cast<CallExpr>(S->getStmt())) { if (CE->getBuiltinCallee() == Builtin::BI__builtin_unreachable || CE->isBuiltinAssumeFalse(Eng.getContext())) { @@ -199,7 +200,7 @@ void UnreachableCodeChecker::FindUnreachableEntryPoints(const CFGBlock *CB, // Find the Stmt* in a CFGBlock for reporting a warning const Stmt *UnreachableCodeChecker::getUnreachableStmt(const CFGBlock *CB) { for (CFGBlock::const_iterator I = CB->begin(), E = CB->end(); I != E; ++I) { - if (Optional<CFGStmt> S = I->getAs<CFGStmt>()) { + if (std::optional<CFGStmt> S = I->getAs<CFGStmt>()) { if (!isa<DeclStmt>(S->getStmt())) return S->getStmt(); } diff --git a/clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp index cf519b085892..fe910ce35302 100644 --- a/clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp @@ -24,6 +24,7 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/raw_ostream.h" +#include <optional> using namespace clang; using namespace ento; @@ -192,7 +193,7 @@ ProgramStateRef VLASizeChecker::checkVLAIndexSize(CheckerContext &C, DefinedOrUnknownSVal Zero = SVB.makeZeroVal(SizeTy); SVal LessThanZeroVal = SVB.evalBinOp(State, BO_LT, SizeD, Zero, SizeTy); - if (Optional<DefinedSVal> LessThanZeroDVal = + if (std::optional<DefinedSVal> LessThanZeroDVal = LessThanZeroVal.getAs<DefinedSVal>()) { ConstraintManager &CM = C.getConstraintManager(); ProgramStateRef StatePos, StateNeg; diff --git a/clang/lib/StaticAnalyzer/Checkers/ValistChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/ValistChecker.cpp index fbefd5f9ffdc..2d1b873abf73 100644 --- a/clang/lib/StaticAnalyzer/Checkers/ValistChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/ValistChecker.cpp @@ -100,27 +100,26 @@ private: }; const SmallVector<ValistChecker::VAListAccepter, 15> - ValistChecker::VAListAccepters = { - {{"vfprintf", 3}, 2}, - {{"vfscanf", 3}, 2}, - {{"vprintf", 2}, 1}, - {{"vscanf", 2}, 1}, - {{"vsnprintf", 4}, 3}, - {{"vsprintf", 3}, 2}, - {{"vsscanf", 3}, 2}, - {{"vfwprintf", 3}, 2}, - {{"vfwscanf", 3}, 2}, - {{"vwprintf", 2}, 1}, - {{"vwscanf", 2}, 1}, - {{"vswprintf", 4}, 3}, - // vswprintf is the wide version of vsnprintf, - // vsprintf has no wide version - {{"vswscanf", 3}, 2}}; - -const CallDescription - ValistChecker::VaStart("__builtin_va_start", /*Args=*/2, /*Params=*/1), - ValistChecker::VaCopy("__builtin_va_copy", 2), - ValistChecker::VaEnd("__builtin_va_end", 1); + ValistChecker::VAListAccepters = {{{{"vfprintf"}, 3}, 2}, + {{{"vfscanf"}, 3}, 2}, + {{{"vprintf"}, 2}, 1}, + {{{"vscanf"}, 2}, 1}, + {{{"vsnprintf"}, 4}, 3}, + {{{"vsprintf"}, 3}, 2}, + {{{"vsscanf"}, 3}, 2}, + {{{"vfwprintf"}, 3}, 2}, + {{{"vfwscanf"}, 3}, 2}, + {{{"vwprintf"}, 2}, 1}, + {{{"vwscanf"}, 2}, 1}, + {{{"vswprintf"}, 4}, 3}, + // vswprintf is the wide version of + // vsnprintf, vsprintf has no wide version + {{{"vswscanf"}, 3}, 2}}; + +const CallDescription ValistChecker::VaStart({"__builtin_va_start"}, /*Args=*/2, + /*Params=*/1), + ValistChecker::VaCopy({"__builtin_va_copy"}, 2), + ValistChecker::VaEnd({"__builtin_va_end"}, 1); } // end anonymous namespace void ValistChecker::checkPreCall(const CallEvent &Call, diff --git a/clang/lib/StaticAnalyzer/Checkers/VforkChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/VforkChecker.cpp index 04e6603b4cbe..86a4e6fbcd6a 100644 --- a/clang/lib/StaticAnalyzer/Checkers/VforkChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/VforkChecker.cpp @@ -35,6 +35,7 @@ #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/AST/ParentMap.h" +#include <optional> using namespace clang; using namespace ento; @@ -154,8 +155,8 @@ void VforkChecker::checkPostCall(const CallEvent &Call, // Get return value of vfork. SVal VforkRetVal = Call.getReturnValue(); - Optional<DefinedOrUnknownSVal> DVal = - VforkRetVal.getAs<DefinedOrUnknownSVal>(); + std::optional<DefinedOrUnknownSVal> DVal = + VforkRetVal.getAs<DefinedOrUnknownSVal>(); if (!DVal) return; diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp index 9c7a59971763..64028b277021 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp @@ -12,8 +12,8 @@ #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/ExprCXX.h" +#include <optional> -using llvm::Optional; namespace clang { std::pair<const Expr *, bool> @@ -34,8 +34,7 @@ tryToFindPtrOrigin(const Expr *E, bool StopAtFirstRefCountedObj) { } if (auto *call = dyn_cast<CallExpr>(E)) { if (auto *memberCall = dyn_cast<CXXMemberCallExpr>(call)) { - Optional<bool> IsGetterOfRefCt = - isGetterOfRefCounted(memberCall->getMethodDecl()); + std::optional<bool> IsGetterOfRefCt = isGetterOfRefCounted(memberCall->getMethodDecl()); if (IsGetterOfRefCt && *IsGetterOfRefCt) { E = memberCall->getImplicitObjectArgument(); if (StopAtFirstRefCountedObj) { diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/NoUncountedMembersChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/NoUncountedMembersChecker.cpp index 97f75135bf92..66d8588e2531 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/NoUncountedMembersChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/NoUncountedMembersChecker.cpp @@ -19,6 +19,7 @@ #include "clang/StaticAnalyzer/Core/Checker.h" #include "llvm/ADT/DenseSet.h" #include "llvm/Support/Casting.h" +#include <optional> using namespace clang; using namespace ento; @@ -69,7 +70,7 @@ public: if (shouldSkipDecl(RD)) return; - for (auto Member : RD->fields()) { + for (auto *Member : RD->fields()) { const Type *MemberType = Member->getType().getTypePtrOrNull(); if (!MemberType) continue; @@ -77,9 +78,9 @@ public: if (auto *MemberCXXRD = MemberType->getPointeeCXXRecordDecl()) { // If we don't see the definition we just don't know. if (MemberCXXRD->hasDefinition()) { - llvm::Optional<bool> isRCAble = isRefCountable(MemberCXXRD); - if (isRCAble && *isRCAble) - reportBug(Member, MemberType, MemberCXXRD, RD); + std::optional<bool> isRCAble = isRefCountable(MemberCXXRD); + if (isRCAble && *isRCAble) + reportBug(Member, MemberType, MemberCXXRD, RD); } } } diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp index a198943c9433..9b1d7ae3e6a3 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp @@ -12,9 +12,8 @@ #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/ExprCXX.h" -#include "llvm/ADT/Optional.h" +#include <optional> -using llvm::Optional; using namespace clang; namespace { @@ -45,29 +44,31 @@ bool hasPublicRefAndDeref(const CXXRecordDecl *R) { namespace clang { -llvm::Optional<const clang::CXXRecordDecl *> -isRefCountable(const CXXBaseSpecifier *Base) { +std::optional<const clang::CXXRecordDecl*> +isRefCountable(const CXXBaseSpecifier* Base) +{ assert(Base); const Type *T = Base->getType().getTypePtrOrNull(); if (!T) - return llvm::None; + return std::nullopt; const CXXRecordDecl *R = T->getAsCXXRecordDecl(); if (!R) - return llvm::None; + return std::nullopt; if (!R->hasDefinition()) - return llvm::None; + return std::nullopt; return hasPublicRefAndDeref(R) ? R : nullptr; } -llvm::Optional<bool> isRefCountable(const CXXRecordDecl *R) { +std::optional<bool> isRefCountable(const CXXRecordDecl* R) +{ assert(R); R = R->getDefinition(); if (!R) - return llvm::None; + return std::nullopt; if (hasPublicRefAndDeref(R)) return true; @@ -77,20 +78,19 @@ llvm::Optional<bool> isRefCountable(const CXXRecordDecl *R) { bool AnyInconclusiveBase = false; const auto isRefCountableBase = - [&AnyInconclusiveBase](const CXXBaseSpecifier *Base, CXXBasePath &) { - Optional<const clang::CXXRecordDecl *> IsRefCountable = - clang::isRefCountable(Base); - if (!IsRefCountable) { - AnyInconclusiveBase = true; - return false; - } - return (*IsRefCountable) != nullptr; + [&AnyInconclusiveBase](const CXXBaseSpecifier* Base, CXXBasePath&) { + std::optional<const clang::CXXRecordDecl*> IsRefCountable = clang::isRefCountable(Base); + if (!IsRefCountable) { + AnyInconclusiveBase = true; + return false; + } + return (*IsRefCountable) != nullptr; }; bool BasesResult = R->lookupInBases(isRefCountableBase, Paths, /*LookupInDependent =*/true); if (AnyInconclusiveBase) - return llvm::None; + return std::nullopt; return BasesResult; } @@ -112,19 +112,21 @@ bool isCtorOfRefCounted(const clang::FunctionDecl *F) { || FunctionName == "Identifier"; } -llvm::Optional<bool> isUncounted(const CXXRecordDecl *Class) { +std::optional<bool> isUncounted(const CXXRecordDecl* Class) +{ // Keep isRefCounted first as it's cheaper. if (isRefCounted(Class)) return false; - llvm::Optional<bool> IsRefCountable = isRefCountable(Class); + std::optional<bool> IsRefCountable = isRefCountable(Class); if (!IsRefCountable) - return llvm::None; + return std::nullopt; return (*IsRefCountable); } -llvm::Optional<bool> isUncountedPtr(const Type *T) { +std::optional<bool> isUncountedPtr(const Type* T) +{ assert(T); if (T->isPointerType() || T->isReferenceType()) { @@ -135,7 +137,8 @@ llvm::Optional<bool> isUncountedPtr(const Type *T) { return false; } -Optional<bool> isGetterOfRefCounted(const CXXMethodDecl *M) { +std::optional<bool> isGetterOfRefCounted(const CXXMethodDecl* M) +{ assert(M); if (isa<CXXMethodDecl>(M)) { diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h index 753adea0d14d..91e3ccf2ec30 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h @@ -10,6 +10,7 @@ #define LLVM_CLANG_ANALYZER_WEBKIT_PTRTYPESEMANTICS_H #include "llvm/ADT/APInt.h" +#include <optional> namespace clang { class CXXBaseSpecifier; @@ -26,31 +27,31 @@ class Type; // Ref<T>. /// \returns CXXRecordDecl of the base if the type is ref-countable, nullptr if -/// not, None if inconclusive. -llvm::Optional<const clang::CXXRecordDecl *> -isRefCountable(const clang::CXXBaseSpecifier *Base); +/// not, std::nullopt if inconclusive. +std::optional<const clang::CXXRecordDecl*> +isRefCountable(const clang::CXXBaseSpecifier* Base); -/// \returns true if \p Class is ref-countable, false if not, None if +/// \returns true if \p Class is ref-countable, false if not, std::nullopt if /// inconclusive. -llvm::Optional<bool> isRefCountable(const clang::CXXRecordDecl *Class); +std::optional<bool> isRefCountable(const clang::CXXRecordDecl* Class); /// \returns true if \p Class is ref-counted, false if not. bool isRefCounted(const clang::CXXRecordDecl *Class); /// \returns true if \p Class is ref-countable AND not ref-counted, false if -/// not, None if inconclusive. -llvm::Optional<bool> isUncounted(const clang::CXXRecordDecl *Class); +/// not, std::nullopt if inconclusive. +std::optional<bool> isUncounted(const clang::CXXRecordDecl* Class); /// \returns true if \p T is either a raw pointer or reference to an uncounted -/// class, false if not, None if inconclusive. -llvm::Optional<bool> isUncountedPtr(const clang::Type *T); +/// class, false if not, std::nullopt if inconclusive. +std::optional<bool> isUncountedPtr(const clang::Type* T); /// \returns true if \p F creates ref-countable object from uncounted parameter, /// false if not. bool isCtorOfRefCounted(const clang::FunctionDecl *F); /// \returns true if \p M is getter of a ref-counted class, false if not. -llvm::Optional<bool> isGetterOfRefCounted(const clang::CXXMethodDecl *Method); +std::optional<bool> isGetterOfRefCounted(const clang::CXXMethodDecl* Method); /// \returns true if \p F is a conversion between ref-countable or ref-counted /// pointer types. diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp index fa9ece217cc0..48dcfc4a3c46 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp @@ -14,6 +14,7 @@ #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" +#include <optional> using namespace clang; using namespace ento; @@ -76,8 +77,7 @@ public: (AccSpec == AS_none && RD->isClass())) return false; - llvm::Optional<const CXXRecordDecl *> RefCntblBaseRD = - isRefCountable(Base); + std::optional<const CXXRecordDecl*> RefCntblBaseRD = isRefCountable(Base); if (!RefCntblBaseRD || !(*RefCntblBaseRD)) return false; diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp index e6d0948f71bb..4ae8c442fa70 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp @@ -19,6 +19,7 @@ #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "llvm/ADT/DenseSet.h" +#include <optional> using namespace clang; using namespace ento; @@ -85,7 +86,7 @@ public: continue; // FIXME? Should we bail? // FIXME: more complex types (arrays, references to raw pointers, etc) - Optional<bool> IsUncounted = isUncountedPtr(ArgType); + std::optional<bool> IsUncounted = isUncountedPtr(ArgType); if (!IsUncounted || !(*IsUncounted)) continue; diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp index deebbd603b2c..004b0b9d398b 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp @@ -14,6 +14,7 @@ #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" +#include <optional> using namespace clang; using namespace ento; @@ -57,18 +58,18 @@ public: void visitLambdaExpr(LambdaExpr *L) const { for (const LambdaCapture &C : L->captures()) { if (C.capturesVariable()) { - VarDecl *CapturedVar = C.getCapturedVar(); + ValueDecl *CapturedVar = C.getCapturedVar(); if (auto *CapturedVarType = CapturedVar->getType().getTypePtrOrNull()) { - Optional<bool> IsUncountedPtr = isUncountedPtr(CapturedVarType); - if (IsUncountedPtr && *IsUncountedPtr) { - reportBug(C, CapturedVar, CapturedVarType); - } + std::optional<bool> IsUncountedPtr = isUncountedPtr(CapturedVarType); + if (IsUncountedPtr && *IsUncountedPtr) { + reportBug(C, CapturedVar, CapturedVarType); + } } } } } - void reportBug(const LambdaCapture &Capture, VarDecl *CapturedVar, + void reportBug(const LambdaCapture &Capture, ValueDecl *CapturedVar, const Type *T) const { assert(CapturedVar); diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLocalVarsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLocalVarsChecker.cpp index 7e86f28cb70f..fa7475934981 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLocalVarsChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLocalVarsChecker.cpp @@ -20,6 +20,7 @@ #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "llvm/ADT/DenseSet.h" +#include <optional> using namespace clang; using namespace ento; @@ -169,7 +170,7 @@ public: if (!ArgType) return; - Optional<bool> IsUncountedPtr = isUncountedPtr(ArgType); + std::optional<bool> IsUncountedPtr = isUncountedPtr(ArgType); if (IsUncountedPtr && *IsUncountedPtr) { const Expr *const InitExpr = V->getInit(); if (!InitExpr) diff --git a/clang/lib/StaticAnalyzer/Checkers/Yaml.h b/clang/lib/StaticAnalyzer/Checkers/Yaml.h index 497189f4c160..4159e14105f8 100644 --- a/clang/lib/StaticAnalyzer/Checkers/Yaml.h +++ b/clang/lib/StaticAnalyzer/Checkers/Yaml.h @@ -17,6 +17,7 @@ #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "llvm/Support/VirtualFileSystem.h" #include "llvm/Support/YAMLTraits.h" +#include <optional> namespace clang { namespace ento { @@ -25,10 +26,10 @@ namespace ento { /// template parameter must have a yaml MappingTraits. /// Emit diagnostic error in case of any failure. template <class T, class Checker> -llvm::Optional<T> getConfiguration(CheckerManager &Mgr, Checker *Chk, - StringRef Option, StringRef ConfigFile) { +std::optional<T> getConfiguration(CheckerManager &Mgr, Checker *Chk, + StringRef Option, StringRef ConfigFile) { if (ConfigFile.trim().empty()) - return None; + return std::nullopt; llvm::vfs::FileSystem *FS = llvm::vfs::getRealFileSystem().get(); llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Buffer = @@ -38,7 +39,7 @@ llvm::Optional<T> getConfiguration(CheckerManager &Mgr, Checker *Chk, Mgr.reportInvalidCheckerOptionValue(Chk, Option, "a valid filename instead of '" + std::string(ConfigFile) + "'"); - return None; + return std::nullopt; } llvm::yaml::Input Input(Buffer.get()->getBuffer()); @@ -48,7 +49,7 @@ llvm::Optional<T> getConfiguration(CheckerManager &Mgr, Checker *Chk, if (std::error_code ec = Input.error()) { Mgr.reportInvalidCheckerOptionValue(Chk, Option, "a valid yaml file: " + ec.message()); - return None; + return std::nullopt; } return Config; diff --git a/clang/lib/StaticAnalyzer/Checkers/cert/InvalidPtrChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/cert/InvalidPtrChecker.cpp index 084789509533..aae1a17bc0ae 100644 --- a/clang/lib/StaticAnalyzer/Checkers/cert/InvalidPtrChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/cert/InvalidPtrChecker.cpp @@ -39,11 +39,11 @@ private: // SEI CERT ENV31-C const CallDescriptionMap<HandlerFn> EnvpInvalidatingFunctions = { - {{"setenv", 3}, &InvalidPtrChecker::EnvpInvalidatingCall}, - {{"unsetenv", 1}, &InvalidPtrChecker::EnvpInvalidatingCall}, - {{"putenv", 1}, &InvalidPtrChecker::EnvpInvalidatingCall}, - {{"_putenv_s", 2}, &InvalidPtrChecker::EnvpInvalidatingCall}, - {{"_wputenv_s", 2}, &InvalidPtrChecker::EnvpInvalidatingCall}, + {{{"setenv"}, 3}, &InvalidPtrChecker::EnvpInvalidatingCall}, + {{{"unsetenv"}, 1}, &InvalidPtrChecker::EnvpInvalidatingCall}, + {{{"putenv"}, 1}, &InvalidPtrChecker::EnvpInvalidatingCall}, + {{{"_putenv_s"}, 2}, &InvalidPtrChecker::EnvpInvalidatingCall}, + {{{"_wputenv_s"}, 2}, &InvalidPtrChecker::EnvpInvalidatingCall}, }; void postPreviousReturnInvalidatingCall(const CallEvent &Call, @@ -51,13 +51,15 @@ private: // SEI CERT ENV34-C const CallDescriptionMap<HandlerFn> PreviousCallInvalidatingFunctions = { - {{"getenv", 1}, &InvalidPtrChecker::postPreviousReturnInvalidatingCall}, - {{"setlocale", 2}, + {{{"getenv"}, 1}, &InvalidPtrChecker::postPreviousReturnInvalidatingCall}, + {{{"setlocale"}, 2}, &InvalidPtrChecker::postPreviousReturnInvalidatingCall}, - {{"strerror", 1}, &InvalidPtrChecker::postPreviousReturnInvalidatingCall}, - {{"localeconv", 0}, + {{{"strerror"}, 1}, + &InvalidPtrChecker::postPreviousReturnInvalidatingCall}, + {{{"localeconv"}, 0}, + &InvalidPtrChecker::postPreviousReturnInvalidatingCall}, + {{{"asctime"}, 1}, &InvalidPtrChecker::postPreviousReturnInvalidatingCall}, - {{"asctime", 1}, &InvalidPtrChecker::postPreviousReturnInvalidatingCall}, }; public: diff --git a/clang/lib/StaticAnalyzer/Checkers/cert/PutenvWithAutoChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/cert/PutenvWithAutoChecker.cpp index ed3bdafad084..eae162cda693 100644 --- a/clang/lib/StaticAnalyzer/Checkers/cert/PutenvWithAutoChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/cert/PutenvWithAutoChecker.cpp @@ -30,7 +30,7 @@ class PutenvWithAutoChecker : public Checker<check::PostCall> { private: BugType BT{this, "'putenv' function should not be called with auto variables", categories::SecurityError}; - const CallDescription Putenv{"putenv", 1}; + const CallDescription Putenv{{"putenv"}, 1}; public: void checkPostCall(const CallEvent &Call, CheckerContext &C) const; |