diff options
| author | Dimitry Andric <dim@FreeBSD.org> | 2023-12-18 20:30:12 +0000 |
|---|---|---|
| committer | Dimitry Andric <dim@FreeBSD.org> | 2024-04-06 20:11:55 +0000 |
| commit | 5f757f3ff9144b609b3c433dfd370cc6bdc191ad (patch) | |
| tree | 1b4e980b866cd26a00af34c0a653eb640bd09caf /contrib/llvm-project/clang/lib/StaticAnalyzer | |
| parent | 3e1c8a35f741a5d114d0ba670b15191355711fe9 (diff) | |
| parent | 312c0ed19cc5276a17bacf2120097bec4515b0f1 (diff) | |
Diffstat (limited to 'contrib/llvm-project/clang/lib/StaticAnalyzer')
96 files changed, 2865 insertions, 1209 deletions
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp index 03e85b9c4373..ce1265412655 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp @@ -25,7 +25,7 @@ using namespace ento; namespace { class ArrayBoundChecker : public Checker<check::Location> { - mutable std::unique_ptr<BuiltinBug> BT; + mutable std::unique_ptr<BugType> BT; public: void checkLocation(SVal l, bool isLoad, const Stmt* S, @@ -66,17 +66,15 @@ void ArrayBoundChecker::checkLocation(SVal l, bool isLoad, const Stmt* LoadS, return; if (!BT) - BT.reset(new BuiltinBug( - this, "Out-of-bound array access", - "Access out-of-bound array element (buffer overflow)")); + BT.reset(new BugType(this, "Out-of-bound array access")); // FIXME: It would be nice to eventually make this diagnostic more clear, // e.g., by referencing the original declaration or by saying *why* this // reference is outside the range. // Generate a report for this bug. - auto report = - std::make_unique<PathSensitiveBugReport>(*BT, BT->getDescription(), N); + auto report = std::make_unique<PathSensitiveBugReport>( + *BT, "Access out-of-bound array element (buffer overflow)", N); report->addRange(LoadS->getSourceRange()); C.emitReport(std::move(report)); diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp index 269277aaf357..6c7a1601402e 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "clang/AST/CharUnits.h" +#include "clang/AST/ParentMapContext.h" #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/StaticAnalyzer/Checkers/Taint.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" @@ -22,52 +23,114 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" #include "llvm/ADT/SmallString.h" +#include "llvm/Support/FormatVariadic.h" #include "llvm/Support/raw_ostream.h" #include <optional> using namespace clang; using namespace ento; using namespace taint; +using llvm::formatv; namespace { -class ArrayBoundCheckerV2 : - public Checker<check::Location> { - mutable std::unique_ptr<BuiltinBug> BT; - mutable std::unique_ptr<BugType> TaintBT; +enum OOB_Kind { OOB_Precedes, OOB_Exceeds, OOB_Taint }; - enum OOB_Kind { OOB_Precedes, OOB_Excedes }; +struct Messages { + std::string Short, Full; +}; + +// NOTE: The `ArraySubscriptExpr` and `UnaryOperator` callbacks are `PostStmt` +// instead of `PreStmt` because the current implementation passes the whole +// expression to `CheckerContext::getSVal()` which only works after the +// symbolic evaluation of the expression. (To turn them into `PreStmt` +// callbacks, we'd need to duplicate the logic that evaluates these +// expressions.) The `MemberExpr` callback would work as `PreStmt` but it's +// defined as `PostStmt` for the sake of consistency with the other callbacks. +class ArrayBoundCheckerV2 : public Checker<check::PostStmt<ArraySubscriptExpr>, + check::PostStmt<UnaryOperator>, + check::PostStmt<MemberExpr>> { + BugType BT{this, "Out-of-bound access"}; + BugType TaintBT{this, "Out-of-bound access", categories::TaintedData}; + + void performCheck(const Expr *E, CheckerContext &C) const; - void reportOOB(CheckerContext &C, ProgramStateRef errorState, - OOB_Kind kind) const; - void reportTaintOOB(CheckerContext &C, ProgramStateRef errorState, - SVal TaintedSVal) const; + void reportOOB(CheckerContext &C, ProgramStateRef ErrorState, OOB_Kind Kind, + NonLoc Offset, Messages Msgs) const; static bool isFromCtypeMacro(const Stmt *S, ASTContext &AC); + static bool isInAddressOf(const Stmt *S, ASTContext &AC); + public: - void checkLocation(SVal l, bool isLoad, const Stmt *S, - CheckerContext &C) const; + void checkPostStmt(const ArraySubscriptExpr *E, CheckerContext &C) const { + performCheck(E, C); + } + void checkPostStmt(const UnaryOperator *E, CheckerContext &C) const { + if (E->getOpcode() == UO_Deref) + performCheck(E, C); + } + void checkPostStmt(const MemberExpr *E, CheckerContext &C) const { + if (E->isArrow()) + performCheck(E->getBase(), C); + } }; -// FIXME: Eventually replace RegionRawOffset with this class. -class RegionRawOffsetV2 { -private: - const SubRegion *baseRegion; - NonLoc byteOffset; +} // anonymous namespace -public: - RegionRawOffsetV2(const SubRegion *base, NonLoc offset) - : baseRegion(base), byteOffset(offset) { assert(base); } +/// For a given Location that can be represented as a symbolic expression +/// Arr[Idx] (or perhaps Arr[Idx1][Idx2] etc.), return the parent memory block +/// Arr and the distance of Location from the beginning of Arr (expressed in a +/// NonLoc that specifies the number of CharUnits). Returns nullopt when these +/// cannot be determined. +static std::optional<std::pair<const SubRegion *, NonLoc>> +computeOffset(ProgramStateRef State, SValBuilder &SVB, SVal Location) { + QualType T = SVB.getArrayIndexType(); + auto EvalBinOp = [&SVB, State, T](BinaryOperatorKind Op, NonLoc L, NonLoc R) { + // We will use this utility to add and multiply values. + return SVB.evalBinOpNN(State, Op, L, R, T).getAs<NonLoc>(); + }; - NonLoc getByteOffset() const { return byteOffset; } - const SubRegion *getRegion() const { return baseRegion; } + const SubRegion *OwnerRegion = nullptr; + std::optional<NonLoc> Offset = SVB.makeZeroArrayIndex(); + + const ElementRegion *CurRegion = + dyn_cast_or_null<ElementRegion>(Location.getAsRegion()); + + while (CurRegion) { + const auto Index = CurRegion->getIndex().getAs<NonLoc>(); + if (!Index) + return std::nullopt; + + QualType ElemType = CurRegion->getElementType(); + + // FIXME: The following early return was presumably added to safeguard the + // getTypeSizeInChars() call (which doesn't accept an incomplete type), but + // it seems that `ElemType` cannot be incomplete at this point. + if (ElemType->isIncompleteType()) + return std::nullopt; + + // Calculate Delta = Index * sizeof(ElemType). + NonLoc Size = SVB.makeArrayIndex( + SVB.getContext().getTypeSizeInChars(ElemType).getQuantity()); + auto Delta = EvalBinOp(BO_Mul, *Index, Size); + if (!Delta) + return std::nullopt; + + // Perform Offset += Delta. + Offset = EvalBinOp(BO_Add, *Offset, *Delta); + if (!Offset) + return std::nullopt; + + OwnerRegion = CurRegion->getSuperRegion()->getAs<SubRegion>(); + // When this is just another ElementRegion layer, we need to continue the + // offset calculations: + CurRegion = dyn_cast_or_null<ElementRegion>(OwnerRegion); + } - static std::optional<RegionRawOffsetV2> - computeOffset(ProgramStateRef State, SValBuilder &SVB, SVal Location); + if (OwnerRegion) + return std::make_pair(OwnerRegion, *Offset); - void dump() const; - void dumpToStream(raw_ostream &os) const; -}; + return std::nullopt; } // TODO: once the constraint manager is smart enough to handle non simplified @@ -86,8 +149,8 @@ getSimplifiedOffsets(NonLoc offset, nonloc::ConcreteInt extent, APSIntType(extent.getValue()).convert(SIE->getRHS()); switch (SIE->getOpcode()) { case BO_Mul: - // The constant should never be 0 here, since it the result of scaling - // based on the size of a type which is never 0. + // The constant should never be 0 here, becasue multiplication by zero + // is simplified by the engine. if ((extent.getValue() % constant) != 0) return std::pair<NonLoc, nonloc::ConcreteInt>(offset, extent); else @@ -113,9 +176,11 @@ getSimplifiedOffsets(NonLoc offset, nonloc::ConcreteInt extent, // where the first one corresponds to "value below threshold" and the second // corresponds to "value at or above threshold". Returns {nullptr, nullptr} in // the case when the evaluation fails. +// If the optional argument CheckEquality is true, then use BO_EQ instead of +// the default BO_LT after consistently applying the same simplification steps. static std::pair<ProgramStateRef, ProgramStateRef> compareValueToThreshold(ProgramStateRef State, NonLoc Value, NonLoc Threshold, - SValBuilder &SVB) { + SValBuilder &SVB, bool CheckEquality = false) { if (auto ConcreteThreshold = Threshold.getAs<nonloc::ConcreteInt>()) { std::tie(Value, Threshold) = getSimplifiedOffsets(Value, *ConcreteThreshold, SVB); } @@ -131,8 +196,10 @@ compareValueToThreshold(ProgramStateRef State, NonLoc Value, NonLoc Threshold, return {nullptr, State}; } } + const BinaryOperatorKind OpKind = CheckEquality ? BO_EQ : BO_LT; auto BelowThreshold = - SVB.evalBinOpNN(State, BO_LT, Value, Threshold, SVB.getConditionType()).getAs<NonLoc>(); + SVB.evalBinOpNN(State, OpKind, Value, Threshold, SVB.getConditionType()) + .getAs<NonLoc>(); if (BelowThreshold) return State->assume(*BelowThreshold); @@ -140,10 +207,118 @@ compareValueToThreshold(ProgramStateRef State, NonLoc Value, NonLoc Threshold, return {nullptr, nullptr}; } -void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad, - const Stmt* LoadS, - CheckerContext &checkerContext) const { +static std::string getRegionName(const SubRegion *Region) { + if (std::string RegName = Region->getDescriptiveName(); !RegName.empty()) + return RegName; + + // Field regions only have descriptive names when their parent has a + // descriptive name; so we provide a fallback representation for them: + if (const auto *FR = Region->getAs<FieldRegion>()) { + if (StringRef Name = FR->getDecl()->getName(); !Name.empty()) + return formatv("the field '{0}'", Name); + return "the unnamed field"; + } + + if (isa<AllocaRegion>(Region)) + return "the memory returned by 'alloca'"; + + if (isa<SymbolicRegion>(Region) && + isa<HeapSpaceRegion>(Region->getMemorySpace())) + return "the heap area"; + + if (isa<StringRegion>(Region)) + return "the string literal"; + + return "the region"; +} + +static std::optional<int64_t> getConcreteValue(NonLoc SV) { + if (auto ConcreteVal = SV.getAs<nonloc::ConcreteInt>()) { + return ConcreteVal->getValue().tryExtValue(); + } + return std::nullopt; +} + +static std::string getShortMsg(OOB_Kind Kind, std::string RegName) { + static const char *ShortMsgTemplates[] = { + "Out of bound access to memory preceding {0}", + "Out of bound access to memory after the end of {0}", + "Potential out of bound access to {0} with tainted offset"}; + + return formatv(ShortMsgTemplates[Kind], RegName); +} + +static Messages getPrecedesMsgs(const SubRegion *Region, NonLoc Offset) { + std::string RegName = getRegionName(Region); + SmallString<128> Buf; + llvm::raw_svector_ostream Out(Buf); + Out << "Access of " << RegName << " at negative byte offset"; + if (auto ConcreteIdx = Offset.getAs<nonloc::ConcreteInt>()) + Out << ' ' << ConcreteIdx->getValue(); + return {getShortMsg(OOB_Precedes, RegName), std::string(Buf)}; +} + +static Messages getExceedsMsgs(ASTContext &ACtx, const SubRegion *Region, + NonLoc Offset, NonLoc Extent, SVal Location) { + std::string RegName = getRegionName(Region); + const auto *EReg = Location.getAsRegion()->getAs<ElementRegion>(); + assert(EReg && "this checker only handles element access"); + QualType ElemType = EReg->getElementType(); + + std::optional<int64_t> OffsetN = getConcreteValue(Offset); + std::optional<int64_t> ExtentN = getConcreteValue(Extent); + + bool UseByteOffsets = true; + if (int64_t ElemSize = ACtx.getTypeSizeInChars(ElemType).getQuantity()) { + const bool OffsetHasRemainder = OffsetN && *OffsetN % ElemSize; + const bool ExtentHasRemainder = ExtentN && *ExtentN % ElemSize; + if (!OffsetHasRemainder && !ExtentHasRemainder) { + UseByteOffsets = false; + if (OffsetN) + *OffsetN /= ElemSize; + if (ExtentN) + *ExtentN /= ElemSize; + } + } + + SmallString<256> Buf; + llvm::raw_svector_ostream Out(Buf); + Out << "Access of "; + if (!ExtentN && !UseByteOffsets) + Out << "'" << ElemType.getAsString() << "' element in "; + Out << RegName << " at "; + if (OffsetN) { + Out << (UseByteOffsets ? "byte offset " : "index ") << *OffsetN; + } else { + Out << "an overflowing " << (UseByteOffsets ? "byte offset" : "index"); + } + if (ExtentN) { + Out << ", while it holds only "; + if (*ExtentN != 1) + Out << *ExtentN; + else + Out << "a single"; + if (UseByteOffsets) + Out << " byte"; + else + Out << " '" << ElemType.getAsString() << "' element"; + + if (*ExtentN > 1) + Out << "s"; + } + + return {getShortMsg(OOB_Exceeds, RegName), std::string(Buf)}; +} + +static Messages getTaintMsgs(const SubRegion *Region, const char *OffsetName) { + std::string RegName = getRegionName(Region); + return {formatv("Potential out of bound access to {0} with tainted {1}", + RegName, OffsetName), + formatv("Access of {0} with a tainted {1} that may be too large", + RegName, OffsetName)}; +} +void ArrayBoundCheckerV2::performCheck(const Expr *E, CheckerContext &C) const { // NOTE: Instead of using ProgramState::assumeInBound(), we are prototyping // some new logic here that reasons directly about memory region extents. // Once that logic is more mature, we can bring it back to assumeInBound() @@ -154,124 +329,120 @@ void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad, // have some flexibility in defining the base region, we can achieve // various levels of conservatism in our buffer overflow checking. + const SVal Location = C.getSVal(E); + // The header ctype.h (from e.g. glibc) implements the isXXXXX() macros as // #define isXXXXX(arg) (LOOKUP_TABLE[arg] & BITMASK_FOR_XXXXX) // and incomplete analysis of these leads to false positives. As even // accurate reports would be confusing for the users, just disable reports // from these macros: - if (isFromCtypeMacro(LoadS, checkerContext.getASTContext())) + if (isFromCtypeMacro(E, C.getASTContext())) return; - ProgramStateRef state = checkerContext.getState(); + ProgramStateRef State = C.getState(); + SValBuilder &SVB = C.getSValBuilder(); - SValBuilder &svalBuilder = checkerContext.getSValBuilder(); - const std::optional<RegionRawOffsetV2> &RawOffset = - RegionRawOffsetV2::computeOffset(state, svalBuilder, location); + const std::optional<std::pair<const SubRegion *, NonLoc>> &RawOffset = + computeOffset(State, SVB, Location); if (!RawOffset) return; - NonLoc ByteOffset = RawOffset->getByteOffset(); + auto [Reg, ByteOffset] = *RawOffset; // CHECK LOWER BOUND - const MemSpaceRegion *SR = RawOffset->getRegion()->getMemorySpace(); - if (!llvm::isa<UnknownSpaceRegion>(SR)) { - // A pointer to UnknownSpaceRegion may point to the middle of - // an allocated region. - - auto [state_precedesLowerBound, state_withinLowerBound] = - compareValueToThreshold(state, ByteOffset, - svalBuilder.makeZeroArrayIndex(), svalBuilder); - - if (state_precedesLowerBound && !state_withinLowerBound) { + const MemSpaceRegion *Space = Reg->getMemorySpace(); + if (!(isa<SymbolicRegion>(Reg) && isa<UnknownSpaceRegion>(Space))) { + // A symbolic region in unknown space represents an unknown pointer that + // may point into the middle of an array, so we don't look for underflows. + // Both conditions are significant because we want to check underflows in + // symbolic regions on the heap (which may be introduced by checkers like + // MallocChecker that call SValBuilder::getConjuredHeapSymbolVal()) and + // non-symbolic regions (e.g. a field subregion of a symbolic region) in + // unknown space. + auto [PrecedesLowerBound, WithinLowerBound] = compareValueToThreshold( + State, ByteOffset, SVB.makeZeroArrayIndex(), SVB); + + if (PrecedesLowerBound && !WithinLowerBound) { // We know that the index definitely precedes the lower bound. - reportOOB(checkerContext, state_precedesLowerBound, OOB_Precedes); + Messages Msgs = getPrecedesMsgs(Reg, ByteOffset); + reportOOB(C, PrecedesLowerBound, OOB_Precedes, ByteOffset, Msgs); return; } - if (state_withinLowerBound) - state = state_withinLowerBound; + if (WithinLowerBound) + State = WithinLowerBound; } // CHECK UPPER BOUND - DefinedOrUnknownSVal Size = - getDynamicExtent(state, RawOffset->getRegion(), svalBuilder); + DefinedOrUnknownSVal Size = getDynamicExtent(State, Reg, SVB); if (auto KnownSize = Size.getAs<NonLoc>()) { - auto [state_withinUpperBound, state_exceedsUpperBound] = - compareValueToThreshold(state, ByteOffset, *KnownSize, svalBuilder); + auto [WithinUpperBound, ExceedsUpperBound] = + compareValueToThreshold(State, ByteOffset, *KnownSize, SVB); - if (state_exceedsUpperBound) { - if (!state_withinUpperBound) { + if (ExceedsUpperBound) { + if (!WithinUpperBound) { // We know that the index definitely exceeds the upper bound. - reportOOB(checkerContext, state_exceedsUpperBound, OOB_Excedes); + if (isa<ArraySubscriptExpr>(E) && isInAddressOf(E, C.getASTContext())) { + // ...but this is within an addressof expression, so we need to check + // for the exceptional case that `&array[size]` is valid. + auto [EqualsToThreshold, NotEqualToThreshold] = + compareValueToThreshold(ExceedsUpperBound, ByteOffset, *KnownSize, + SVB, /*CheckEquality=*/true); + if (EqualsToThreshold && !NotEqualToThreshold) { + // We are definitely in the exceptional case, so return early + // instead of reporting a bug. + C.addTransition(EqualsToThreshold); + return; + } + } + Messages Msgs = getExceedsMsgs(C.getASTContext(), Reg, ByteOffset, + *KnownSize, Location); + reportOOB(C, ExceedsUpperBound, OOB_Exceeds, ByteOffset, Msgs); return; } - if (isTainted(state, ByteOffset)) { - // Both cases are possible, but the index is tainted, so report. - reportTaintOOB(checkerContext, state_exceedsUpperBound, ByteOffset); + if (isTainted(State, ByteOffset)) { + // Both cases are possible, but the offset is tainted, so report. + std::string RegName = getRegionName(Reg); + + // Diagnostic detail: "tainted offset" is always correct, but the + // common case is that 'idx' is tainted in 'arr[idx]' and then it's + // nicer to say "tainted index". + const char *OffsetName = "offset"; + if (const auto *ASE = dyn_cast<ArraySubscriptExpr>(E)) + if (isTainted(State, ASE->getIdx(), C.getLocationContext())) + OffsetName = "index"; + + Messages Msgs = getTaintMsgs(Reg, OffsetName); + reportOOB(C, ExceedsUpperBound, OOB_Taint, ByteOffset, Msgs); return; } } - if (state_withinUpperBound) - state = state_withinUpperBound; + if (WithinUpperBound) + State = WithinUpperBound; } - checkerContext.addTransition(state); + C.addTransition(State); } -void ArrayBoundCheckerV2::reportTaintOOB(CheckerContext &checkerContext, - ProgramStateRef errorState, - SVal TaintedSVal) const { - ExplodedNode *errorNode = checkerContext.generateErrorNode(errorState); - if (!errorNode) - return; +void ArrayBoundCheckerV2::reportOOB(CheckerContext &C, + ProgramStateRef ErrorState, OOB_Kind Kind, + NonLoc Offset, Messages Msgs) const { - if (!TaintBT) - TaintBT.reset( - new BugType(this, "Out-of-bound access", categories::TaintedData)); + ExplodedNode *ErrorNode = C.generateErrorNode(ErrorState); + if (!ErrorNode) + return; - SmallString<256> buf; - llvm::raw_svector_ostream os(buf); - os << "Out of bound memory access (index is tainted)"; - auto BR = - std::make_unique<PathSensitiveBugReport>(*TaintBT, os.str(), errorNode); + auto BR = std::make_unique<PathSensitiveBugReport>( + Kind == OOB_Taint ? TaintBT : BT, Msgs.Short, Msgs.Full, ErrorNode); // Track back the propagation of taintedness. - for (SymbolRef Sym : getTaintedSymbols(errorState, TaintedSVal)) { - BR->markInteresting(Sym); - } - - checkerContext.emitReport(std::move(BR)); -} - -void ArrayBoundCheckerV2::reportOOB(CheckerContext &checkerContext, - ProgramStateRef errorState, - OOB_Kind kind) const { + if (Kind == OOB_Taint) + for (SymbolRef Sym : getTaintedSymbols(ErrorState, Offset)) + BR->markInteresting(Sym); - ExplodedNode *errorNode = checkerContext.generateErrorNode(errorState); - if (!errorNode) - return; - - if (!BT) - BT.reset(new BuiltinBug(this, "Out-of-bound access")); - - // FIXME: This diagnostics are preliminary. We should get far better - // diagnostics for explaining buffer overruns. - - SmallString<256> buf; - llvm::raw_svector_ostream os(buf); - os << "Out of bound memory access "; - switch (kind) { - case OOB_Precedes: - os << "(accessed memory precedes memory block)"; - break; - case OOB_Excedes: - os << "(access exceeds upper limit of memory block)"; - break; - } - auto BR = std::make_unique<PathSensitiveBugReport>(*BT, os.str(), errorNode); - checkerContext.emitReport(std::move(BR)); + C.emitReport(std::move(BR)); } bool ArrayBoundCheckerV2::isFromCtypeMacro(const Stmt *S, ASTContext &ACtx) { @@ -293,65 +464,16 @@ bool ArrayBoundCheckerV2::isFromCtypeMacro(const Stmt *S, ASTContext &ACtx) { (MacroName == "isupper") || (MacroName == "isxdigit")); } -#ifndef NDEBUG -LLVM_DUMP_METHOD void RegionRawOffsetV2::dump() const { - dumpToStream(llvm::errs()); -} - -void RegionRawOffsetV2::dumpToStream(raw_ostream &os) const { - os << "raw_offset_v2{" << getRegion() << ',' << getByteOffset() << '}'; -} -#endif - -/// For a given Location that can be represented as a symbolic expression -/// Arr[Idx] (or perhaps Arr[Idx1][Idx2] etc.), return the parent memory block -/// Arr and the distance of Location from the beginning of Arr (expressed in a -/// NonLoc that specifies the number of CharUnits). Returns nullopt when these -/// cannot be determined. -std::optional<RegionRawOffsetV2> -RegionRawOffsetV2::computeOffset(ProgramStateRef State, SValBuilder &SVB, - SVal Location) { - QualType T = SVB.getArrayIndexType(); - auto Calc = [&SVB, State, T](BinaryOperatorKind Op, NonLoc LHS, NonLoc RHS) { - // We will use this utility to add and multiply values. - return SVB.evalBinOpNN(State, Op, LHS, RHS, T).getAs<NonLoc>(); - }; - - const MemRegion *Region = Location.getAsRegion(); - NonLoc Offset = SVB.makeZeroArrayIndex(); - - while (Region) { - if (const auto *ERegion = dyn_cast<ElementRegion>(Region)) { - if (const auto Index = ERegion->getIndex().getAs<NonLoc>()) { - QualType ElemType = ERegion->getElementType(); - // If the element is an incomplete type, go no further. - if (ElemType->isIncompleteType()) - return std::nullopt; - - // Perform Offset += Index * sizeof(ElemType); then continue the offset - // calculations with SuperRegion: - NonLoc Size = SVB.makeArrayIndex( - SVB.getContext().getTypeSizeInChars(ElemType).getQuantity()); - if (auto Delta = Calc(BO_Mul, *Index, Size)) { - if (auto NewOffset = Calc(BO_Add, Offset, *Delta)) { - Offset = *NewOffset; - Region = ERegion->getSuperRegion(); - continue; - } - } - } - } else if (const auto *SRegion = dyn_cast<SubRegion>(Region)) { - // NOTE: The dyn_cast<>() is expected to succeed, it'd be very surprising - // to see a MemSpaceRegion at this point. - // FIXME: We may return with {<Region>, 0} even if we didn't handle any - // ElementRegion layers. I think that this behavior was introduced - // accidentally by 8a4c760c204546aba566e302f299f7ed2e00e287 in 2011, so - // it may be useful to review it in the future. - return RegionRawOffsetV2(SRegion, Offset); - } - return std::nullopt; - } - return std::nullopt; +bool ArrayBoundCheckerV2::isInAddressOf(const Stmt *S, ASTContext &ACtx) { + ParentMapContext &ParentCtx = ACtx.getParentMapContext(); + do { + const DynTypedNodeList Parents = ParentCtx.getParents(*S); + if (Parents.empty()) + return false; + S = Parents[0].get<Stmt>(); + } while (isa_and_nonnull<ParenExpr, ImplicitCastExpr>(S)); + const auto *UnaryOp = dyn_cast_or_null<UnaryOperator>(S); + return UnaryOp && UnaryOp->getOpcode() == UO_AddrOf; } void ento::registerArrayBoundCheckerV2(CheckerManager &mgr) { diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp index 4a5b8913c22f..5e25153a148f 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp @@ -352,9 +352,9 @@ void NilArgChecker::checkPostStmt(const ObjCDictionaryLiteral *DL, namespace { class CFNumberChecker : public Checker< check::PreStmt<CallExpr> > { mutable std::unique_ptr<APIMisuse> BT; - mutable IdentifierInfo *ICreate, *IGetValue; + mutable IdentifierInfo *ICreate = nullptr, *IGetValue = nullptr; public: - CFNumberChecker() : ICreate(nullptr), IGetValue(nullptr) {} + CFNumberChecker() = default; void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; }; @@ -819,13 +819,13 @@ class ObjCLoopChecker check::PostObjCMessage, check::DeadSymbols, check::PointerEscape > { - mutable IdentifierInfo *CountSelectorII; + mutable IdentifierInfo *CountSelectorII = nullptr; bool isCollectionCountMethod(const ObjCMethodCall &M, CheckerContext &C) const; public: - ObjCLoopChecker() : CountSelectorII(nullptr) {} + ObjCLoopChecker() = default; void checkPostStmt(const ObjCForCollectionStmt *FCS, CheckerContext &C) const; void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; @@ -1145,13 +1145,13 @@ class ObjCNonNilReturnValueChecker check::PostStmt<ObjCArrayLiteral>, check::PostStmt<ObjCDictionaryLiteral>, check::PostStmt<ObjCBoxedExpr> > { - mutable bool Initialized; + mutable bool Initialized = false; mutable Selector ObjectAtIndex; mutable Selector ObjectAtIndexedSubscript; mutable Selector NullSelector; public: - ObjCNonNilReturnValueChecker() : Initialized(false) {} + ObjCNonNilReturnValueChecker() = default; ProgramStateRef assumeExprIsNonNull(const Expr *NonNullExpr, ProgramStateRef State, diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BitwiseShiftChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BitwiseShiftChecker.cpp new file mode 100644 index 000000000000..339927c165fe --- /dev/null +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BitwiseShiftChecker.cpp @@ -0,0 +1,370 @@ +//== BitwiseShiftChecker.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 file defines BitwiseShiftChecker, which is a path-sensitive checker +// that looks for undefined behavior when the operands of the bitwise shift +// operators '<<' and '>>' are invalid (negative or too large). +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/ASTContext.h" +#include "clang/AST/CharUnits.h" +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.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/APSIntType.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "llvm/Support/FormatVariadic.h" +#include <memory> + +using namespace clang; +using namespace ento; +using llvm::formatv; + +namespace { +enum class OperandSide { Left, Right }; + +using BugReportPtr = std::unique_ptr<PathSensitiveBugReport>; + +struct NoteTagTemplate { + llvm::StringLiteral SignInfo; + llvm::StringLiteral UpperBoundIntro; +}; + +constexpr NoteTagTemplate NoteTagTemplates[] = { + {"", "right operand of bit shift is less than "}, + {"left operand of bit shift is non-negative", " and right operand is less than "}, + {"right operand of bit shift is non-negative", " but less than "}, + {"both operands of bit shift are non-negative", " and right operand is less than "} +}; + +/// An implementation detail class which is introduced to split the checker +/// logic into several methods while maintaining a consistently updated state +/// and access to other contextual data. +class BitwiseShiftValidator { + CheckerContext &Ctx; + ProgramStateRef FoldedState; + const BinaryOperator *const Op; + const BugType &BT; + const bool PedanticFlag; + + // The following data members are only used for note tag creation: + enum { NonNegLeft = 1, NonNegRight = 2 }; + unsigned NonNegOperands = 0; + + std::optional<unsigned> UpperBoundBitCount = std::nullopt; + +public: + BitwiseShiftValidator(const BinaryOperator *O, CheckerContext &C, + const BugType &B, bool P) + : Ctx(C), FoldedState(C.getState()), Op(O), BT(B), PedanticFlag(P) {} + void run(); + +private: + const Expr *operandExpr(OperandSide Side) const { + return Side == OperandSide::Left ? Op->getLHS() : Op->getRHS(); + } + + bool shouldPerformPedanticChecks() const { + // The pedantic flag has no effect under C++20 because the affected issues + // are no longer undefined under that version of the standard. + return PedanticFlag && !Ctx.getASTContext().getLangOpts().CPlusPlus20; + } + + bool assumeRequirement(OperandSide Side, BinaryOperator::Opcode Cmp, unsigned Limit); + + void recordAssumption(OperandSide Side, BinaryOperator::Opcode Cmp, unsigned Limit); + const NoteTag *createNoteTag() const; + + BugReportPtr createBugReport(StringRef ShortMsg, StringRef Msg) const; + + BugReportPtr checkOvershift(); + BugReportPtr checkOperandNegative(OperandSide Side); + BugReportPtr checkLeftShiftOverflow(); + + bool isLeftShift() const { return Op->getOpcode() == BO_Shl; } + StringRef shiftDir() const { return isLeftShift() ? "left" : "right"; } + static StringRef pluralSuffix(unsigned n) { return n <= 1 ? "" : "s"; } + static StringRef verbSuffix(unsigned n) { return n <= 1 ? "s" : ""; } +}; + +void BitwiseShiftValidator::run() { + // Report a bug if the right operand is >= the bit width of the type of the + // left operand: + if (BugReportPtr BR = checkOvershift()) { + Ctx.emitReport(std::move(BR)); + return; + } + + // Report a bug if the right operand is negative: + if (BugReportPtr BR = checkOperandNegative(OperandSide::Right)) { + Ctx.emitReport(std::move(BR)); + return; + } + + if (shouldPerformPedanticChecks()) { + // Report a bug if the left operand is negative: + if (BugReportPtr BR = checkOperandNegative(OperandSide::Left)) { + Ctx.emitReport(std::move(BR)); + return; + } + + // Report a bug when left shift of a concrete signed value overflows: + if (BugReportPtr BR = checkLeftShiftOverflow()) { + Ctx.emitReport(std::move(BR)); + return; + } + } + + // No bugs detected, update the state and add a single note tag which + // summarizes the new assumptions. + Ctx.addTransition(FoldedState, createNoteTag()); +} + +/// This method checks a requirement that must be satisfied by the value on the +/// given Side of a bitwise shift operator in well-defined code. If the +/// requirement is incompatible with prior knowledge, this method reports +/// failure by returning false. +bool BitwiseShiftValidator::assumeRequirement(OperandSide Side, + BinaryOperator::Opcode Comparison, + unsigned Limit) { + SValBuilder &SVB = Ctx.getSValBuilder(); + + const SVal OperandVal = Ctx.getSVal(operandExpr(Side)); + const auto LimitVal = SVB.makeIntVal(Limit, Ctx.getASTContext().IntTy); + // Note that the type of `LimitVal` must be a signed, because otherwise a + // negative `Val` could be converted to a large positive value. + + auto ResultVal = SVB.evalBinOp(FoldedState, Comparison, OperandVal, LimitVal, + SVB.getConditionType()); + if (auto DURes = ResultVal.getAs<DefinedOrUnknownSVal>()) { + auto [StTrue, StFalse] = FoldedState->assume(DURes.value()); + if (!StTrue) { + // We detected undefined behavior (the caller will report it). + FoldedState = StFalse; + return false; + } + // The code may be valid, so let's assume that it's valid: + FoldedState = StTrue; + if (StFalse) { + // Record note tag data for the assumption that we made + recordAssumption(Side, Comparison, Limit); + } + } + return true; +} + +BugReportPtr BitwiseShiftValidator::checkOvershift() { + const QualType LHSTy = Op->getLHS()->getType(); + const unsigned LHSBitWidth = Ctx.getASTContext().getIntWidth(LHSTy); + + if (assumeRequirement(OperandSide::Right, BO_LT, LHSBitWidth)) + return nullptr; + + const SVal Right = Ctx.getSVal(operandExpr(OperandSide::Right)); + + std::string RightOpStr = "", LowerBoundStr = ""; + if (auto ConcreteRight = Right.getAs<nonloc::ConcreteInt>()) + RightOpStr = formatv(" '{0}'", ConcreteRight->getValue()); + else { + SValBuilder &SVB = Ctx.getSValBuilder(); + if (const llvm::APSInt *MinRight = SVB.getMinValue(FoldedState, Right)) { + LowerBoundStr = formatv(" >= {0},", MinRight->getExtValue()); + } + } + + std::string ShortMsg = formatv( + "{0} shift{1}{2} overflows the capacity of '{3}'", + isLeftShift() ? "Left" : "Right", RightOpStr.empty() ? "" : " by", + RightOpStr, LHSTy.getAsString()); + std::string Msg = formatv( + "The result of {0} shift is undefined because the right " + "operand{1} is{2} not smaller than {3}, the capacity of '{4}'", + shiftDir(), RightOpStr, LowerBoundStr, LHSBitWidth, LHSTy.getAsString()); + return createBugReport(ShortMsg, Msg); +} + +// Before C++20, at 5.8 [expr.shift] (N4296, 2014-11-19) the standard says +// 1. "... The behaviour is undefined if the right operand is negative..." +// 2. "The value of E1 << E2 ... +// if E1 has a signed type and non-negative value ... +// otherwise, the behavior is undefined." +// 3. "The value of E1 >> E2 ... +// If E1 has a signed type and a negative value, +// the resulting value is implementation-defined." +// However, negative left arguments work in practice and the C++20 standard +// eliminates conditions 2 and 3. +BugReportPtr BitwiseShiftValidator::checkOperandNegative(OperandSide Side) { + // If the type is unsigned, it cannot be negative + if (!operandExpr(Side)->getType()->isSignedIntegerType()) + return nullptr; + + // Main check: determine whether the operand is constrained to be negative + if (assumeRequirement(Side, BO_GE, 0)) + return nullptr; + + std::string ShortMsg = formatv("{0} operand is negative in {1} shift", + Side == OperandSide::Left ? "Left" : "Right", + shiftDir()) + .str(); + std::string Msg = formatv("The result of {0} shift is undefined " + "because the {1} operand is negative", + shiftDir(), + Side == OperandSide::Left ? "left" : "right") + .str(); + + return createBugReport(ShortMsg, Msg); +} + +BugReportPtr BitwiseShiftValidator::checkLeftShiftOverflow() { + // A right shift cannot be an overflowing left shift... + if (!isLeftShift()) + return nullptr; + + // In C++ it's well-defined to shift to the sign bit. In C however, it's UB. + // 5.8.2 [expr.shift] (N4296, 2014-11-19) + const bool ShouldPreserveSignBit = !Ctx.getLangOpts().CPlusPlus; + + const Expr *LHS = operandExpr(OperandSide::Left); + const QualType LHSTy = LHS->getType(); + const unsigned LeftBitWidth = Ctx.getASTContext().getIntWidth(LHSTy); + assert(LeftBitWidth > 0); + + // Quote "For unsigned lhs, the value of LHS << RHS is the value of LHS * + // 2^RHS, reduced modulo maximum value of the return type plus 1." + if (LHSTy->isUnsignedIntegerType()) + return nullptr; + + // We only support concrete integers as left operand. + const auto Left = Ctx.getSVal(LHS).getAs<nonloc::ConcreteInt>(); + if (!Left.has_value()) + return nullptr; + + // We should have already reported a bug if the left operand of the shift was + // negative, so it cannot be negative here. + assert(Left->getValue().isNonNegative()); + + const unsigned LeftAvailableBitWidth = + LeftBitWidth - static_cast<unsigned>(ShouldPreserveSignBit); + const unsigned UsedBitsInLeftOperand = Left->getValue().getActiveBits(); + assert(LeftBitWidth >= UsedBitsInLeftOperand); + const unsigned MaximalAllowedShift = + LeftAvailableBitWidth - UsedBitsInLeftOperand; + + if (assumeRequirement(OperandSide::Right, BO_LT, MaximalAllowedShift + 1)) + return nullptr; + + const std::string CapacityMsg = + formatv("because '{0}' can hold only {1} bits ({2} the sign bit)", + LHSTy.getAsString(), LeftAvailableBitWidth, + ShouldPreserveSignBit ? "excluding" : "including"); + + const SVal Right = Ctx.getSVal(Op->getRHS()); + + std::string ShortMsg, Msg; + if (const auto ConcreteRight = Right.getAs<nonloc::ConcreteInt>()) { + // Here ConcreteRight must contain a small non-negative integer, because + // otherwise one of the earlier checks should've reported a bug. + const unsigned RHS = ConcreteRight->getValue().getExtValue(); + assert(RHS > MaximalAllowedShift); + const unsigned OverflownBits = RHS - MaximalAllowedShift; + ShortMsg = formatv( + "The shift '{0} << {1}' overflows the capacity of '{2}'", + Left->getValue(), ConcreteRight->getValue(), LHSTy.getAsString()); + Msg = formatv( + "The shift '{0} << {1}' is undefined {2}, so {3} bit{4} overflow{5}", + Left->getValue(), ConcreteRight->getValue(), CapacityMsg, OverflownBits, + pluralSuffix(OverflownBits), verbSuffix(OverflownBits)); + } else { + ShortMsg = formatv("Left shift of '{0}' overflows the capacity of '{1}'", + Left->getValue(), LHSTy.getAsString()); + Msg = formatv( + "Left shift of '{0}' is undefined {1}, so some bits overflow", + Left->getValue(), CapacityMsg); + } + + return createBugReport(ShortMsg, Msg); +} + +void BitwiseShiftValidator::recordAssumption(OperandSide Side, + BinaryOperator::Opcode Comparison, + unsigned Limit) { + switch (Comparison) { + case BO_GE: + assert(Limit == 0); + NonNegOperands |= (Side == OperandSide::Left ? NonNegLeft : NonNegRight); + break; + case BO_LT: + assert(Side == OperandSide::Right); + if (!UpperBoundBitCount || Limit < UpperBoundBitCount.value()) + UpperBoundBitCount = Limit; + break; + default: + llvm_unreachable("this checker does not use other comparison operators"); + } +} + +const NoteTag *BitwiseShiftValidator::createNoteTag() const { + if (!NonNegOperands && !UpperBoundBitCount) + return nullptr; + + SmallString<128> Buf; + llvm::raw_svector_ostream Out(Buf); + Out << "Assuming "; + NoteTagTemplate Templ = NoteTagTemplates[NonNegOperands]; + Out << Templ.SignInfo; + if (UpperBoundBitCount) + Out << Templ.UpperBoundIntro << UpperBoundBitCount.value(); + const std::string Msg(Out.str()); + + return Ctx.getNoteTag(Msg, /*isPrunable=*/true); +} + +std::unique_ptr<PathSensitiveBugReport> +BitwiseShiftValidator::createBugReport(StringRef ShortMsg, StringRef Msg) const { + ProgramStateRef State = Ctx.getState(); + if (ExplodedNode *ErrNode = Ctx.generateErrorNode(State)) { + auto BR = + std::make_unique<PathSensitiveBugReport>(BT, ShortMsg, Msg, ErrNode); + bugreporter::trackExpressionValue(ErrNode, Op->getLHS(), *BR); + bugreporter::trackExpressionValue(ErrNode, Op->getRHS(), *BR); + return BR; + } + return nullptr; +} +} // anonymous namespace + +class BitwiseShiftChecker : public Checker<check::PreStmt<BinaryOperator>> { + BugType BT{this, "Bitwise shift", "Suspicious operation"}; + +public: + void checkPreStmt(const BinaryOperator *B, CheckerContext &Ctx) const { + BinaryOperator::Opcode Op = B->getOpcode(); + + if (Op != BO_Shl && Op != BO_Shr) + return; + + BitwiseShiftValidator(B, Ctx, BT, Pedantic).run(); + } + + bool Pedantic = false; +}; + +void ento::registerBitwiseShiftChecker(CheckerManager &Mgr) { + auto *Chk = Mgr.registerChecker<BitwiseShiftChecker>(); + const AnalyzerOptions &Opts = Mgr.getAnalyzerOptions(); + Chk->Pedantic = Opts.getCheckerBooleanOption(Chk, "Pedantic"); +} + +bool ento::shouldRegisterBitwiseShiftChecker(const CheckerManager &mgr) { + return true; +} diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp index 2d20e394681d..361a4eed9221 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp @@ -24,7 +24,7 @@ using namespace ento; namespace { class BoolAssignmentChecker : public Checker< check::Bind > { - mutable std::unique_ptr<BuiltinBug> BT; + mutable std::unique_ptr<BugType> BT; void emitReport(ProgramStateRef state, CheckerContext &C, bool IsTainted = false) const; @@ -37,7 +37,7 @@ void BoolAssignmentChecker::emitReport(ProgramStateRef state, CheckerContext &C, bool IsTainted) const { if (ExplodedNode *N = C.generateNonFatalErrorNode(state)) { if (!BT) - BT.reset(new BuiltinBug(this, "Assignment of a non-Boolean value")); + BT.reset(new BugType(this, "Assignment of a non-Boolean value")); StringRef Msg = IsTainted ? "Might assign a tainted non-Boolean value" : "Assignment of a non-Boolean value"; diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp index 4a56156de4b2..61521c259ca9 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp @@ -81,22 +81,20 @@ bool BuiltinFunctionChecker::evalCall(const CallEvent &Call, case Builtin::BI__builtin_alloca_with_align: case Builtin::BI__builtin_alloca: { - // FIXME: Refactor into StoreManager itself? - MemRegionManager& RM = C.getStoreManager().getRegionManager(); - const AllocaRegion* R = - RM.getAllocaRegion(CE, C.blockCount(), C.getLocationContext()); - - // Set the extent of the region in bytes. This enables us to use the - // SVal of the argument directly. If we save the extent in bits, we - // cannot represent values like symbol*8. - auto Size = Call.getArgSVal(0); - if (Size.isUndef()) - return true; // Return true to model purity. - - state = setDynamicExtent(state, R, Size.castAs<DefinedOrUnknownSVal>(), - C.getSValBuilder()); + SValBuilder &SVB = C.getSValBuilder(); + const loc::MemRegionVal R = + SVB.getAllocaRegionVal(CE, C.getLocationContext(), C.blockCount()); - C.addTransition(state->BindExpr(CE, LCtx, loc::MemRegionVal(R))); + // Set the extent of the region in bytes. This enables us to use the SVal + // of the argument directly. If we saved the extent in bits, it'd be more + // difficult to reason about values like symbol*8. + auto Size = Call.getArgSVal(0); + if (auto DefSize = Size.getAs<DefinedOrUnknownSVal>()) { + // This `getAs()` is mostly paranoia, because core.CallAndMessage reports + // undefined function arguments (unless it's disabled somehow). + state = setDynamicExtent(state, R.getRegion(), *DefSize, SVB); + } + C.addTransition(state->BindExpr(CE, LCtx, R)); return true; } diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp index 387edd8c3b18..31f5b03dcdeb 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp @@ -480,6 +480,14 @@ CStringChecker::CheckBufferAccess(CheckerContext &C, ProgramStateRef State, if (!Filter.CheckCStringOutOfBounds) return State; + SVal BufStart = + svalBuilder.evalCast(BufVal, PtrTy, Buffer.Expression->getType()); + + // Check if the first byte of the buffer is accessible. + State = CheckLocation(C, State, Buffer, BufStart, Access, CK); + if (!State) + return nullptr; + // Get the access length and make sure it is known. // FIXME: This assumes the caller has already checked that the access length // is positive. And that it's unsigned. @@ -496,8 +504,6 @@ CStringChecker::CheckBufferAccess(CheckerContext &C, ProgramStateRef State, NonLoc LastOffset = Offset.castAs<NonLoc>(); // Check that the first buffer is sufficiently long. - SVal BufStart = - svalBuilder.evalCast(BufVal, PtrTy, Buffer.Expression->getType()); if (std::optional<Loc> BufLoc = BufStart.getAs<Loc>()) { SVal BufEnd = @@ -653,13 +659,15 @@ void CStringChecker::emitOverlapBug(CheckerContext &C, ProgramStateRef state, void CStringChecker::emitNullArgBug(CheckerContext &C, ProgramStateRef State, const Stmt *S, StringRef WarningMsg) const { if (ExplodedNode *N = C.generateErrorNode(State)) { - if (!BT_Null) - BT_Null.reset(new BuiltinBug( - Filter.CheckNameCStringNullArg, categories::UnixAPI, - "Null pointer argument in call to byte string function")); + if (!BT_Null) { + // FIXME: This call uses the string constant 'categories::UnixAPI' as the + // description of the bug; it should be replaced by a real description. + BT_Null.reset( + new BugType(Filter.CheckNameCStringNullArg, categories::UnixAPI)); + } - BuiltinBug *BT = static_cast<BuiltinBug *>(BT_Null.get()); - auto Report = std::make_unique<PathSensitiveBugReport>(*BT, WarningMsg, N); + auto Report = + std::make_unique<PathSensitiveBugReport>(*BT_Null, WarningMsg, N); Report->addRange(S->getSourceRange()); if (const auto *Ex = dyn_cast<Expr>(S)) bugreporter::trackExpressionValue(N, Ex, *Report); @@ -674,13 +682,11 @@ void CStringChecker::emitUninitializedReadBug(CheckerContext &C, const char *Msg = "Bytes string function accesses uninitialized/garbage values"; if (!BT_UninitRead) - BT_UninitRead.reset( - new BuiltinBug(Filter.CheckNameCStringUninitializedRead, - "Accessing unitialized/garbage values", Msg)); + BT_UninitRead.reset(new BugType(Filter.CheckNameCStringUninitializedRead, + "Accessing unitialized/garbage values")); - BuiltinBug *BT = static_cast<BuiltinBug *>(BT_UninitRead.get()); - - auto Report = std::make_unique<PathSensitiveBugReport>(*BT, Msg, N); + auto Report = + std::make_unique<PathSensitiveBugReport>(*BT_UninitRead, Msg, N); Report->addRange(E->getSourceRange()); bugreporter::trackExpressionValue(N, E, *Report); C.emitReport(std::move(Report)); @@ -692,18 +698,16 @@ void CStringChecker::emitOutOfBoundsBug(CheckerContext &C, StringRef WarningMsg) const { if (ExplodedNode *N = C.generateErrorNode(State)) { if (!BT_Bounds) - BT_Bounds.reset(new BuiltinBug( - Filter.CheckCStringOutOfBounds ? Filter.CheckNameCStringOutOfBounds - : Filter.CheckNameCStringNullArg, - "Out-of-bound array access", - "Byte string function accesses out-of-bound array element")); - - BuiltinBug *BT = static_cast<BuiltinBug *>(BT_Bounds.get()); + BT_Bounds.reset(new BugType(Filter.CheckCStringOutOfBounds + ? Filter.CheckNameCStringOutOfBounds + : Filter.CheckNameCStringNullArg, + "Out-of-bound array access")); // FIXME: It would be nice to eventually make this diagnostic more clear, // e.g., by referencing the original declaration or by saying *why* this // reference is outside the range. - auto Report = std::make_unique<PathSensitiveBugReport>(*BT, WarningMsg, N); + auto Report = + std::make_unique<PathSensitiveBugReport>(*BT_Bounds, WarningMsg, N); Report->addRange(S->getSourceRange()); C.emitReport(std::move(Report)); } @@ -713,10 +717,12 @@ void CStringChecker::emitNotCStringBug(CheckerContext &C, ProgramStateRef State, const Stmt *S, StringRef WarningMsg) const { if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) { - if (!BT_NotCString) - BT_NotCString.reset(new BuiltinBug( - Filter.CheckNameCStringNotNullTerm, categories::UnixAPI, - "Argument is not a null-terminated string.")); + if (!BT_NotCString) { + // FIXME: This call uses the string constant 'categories::UnixAPI' as the + // description of the bug; it should be replaced by a real description. + BT_NotCString.reset( + new BugType(Filter.CheckNameCStringNotNullTerm, categories::UnixAPI)); + } auto Report = std::make_unique<PathSensitiveBugReport>(*BT_NotCString, WarningMsg, N); @@ -729,10 +735,13 @@ void CStringChecker::emitNotCStringBug(CheckerContext &C, ProgramStateRef State, void CStringChecker::emitAdditionOverflowBug(CheckerContext &C, ProgramStateRef State) const { if (ExplodedNode *N = C.generateErrorNode(State)) { - if (!BT_AdditionOverflow) + if (!BT_AdditionOverflow) { + // FIXME: This call uses the word "API" as the description of the bug; + // it should be replaced by a better error message (if this unlikely + // situation continues to exist as a separate bug type). BT_AdditionOverflow.reset( - new BuiltinBug(Filter.CheckNameCStringOutOfBounds, "API", - "Sum of expressions causes overflow.")); + new BugType(Filter.CheckNameCStringOutOfBounds, "API")); + } // This isn't a great error message, but this should never occur in real // code anyway -- you'd have to create a buffer longer than a size_t can @@ -872,8 +881,8 @@ SVal CStringChecker::getCStringLengthForRegion(CheckerContext &C, const llvm::APSInt *maxLengthInt = BVF.evalAPSInt(BO_Div, maxValInt, fourInt); NonLoc maxLength = svalBuilder.makeIntVal(*maxLengthInt); - SVal evalLength = svalBuilder.evalBinOpNN(state, BO_LE, *strLn, - maxLength, sizeTy); + SVal evalLength = svalBuilder.evalBinOpNN(state, BO_LE, *strLn, maxLength, + svalBuilder.getConditionType()); state = state->assume(evalLength.castAs<DefinedOrUnknownSVal>(), true); } state = state->set<CStringLength>(MR, strLength); @@ -921,9 +930,23 @@ SVal CStringChecker::getCStringLength(CheckerContext &C, ProgramStateRef &state, const StringLiteral *strLit = cast<StringRegion>(MR)->getStringLiteral(); return svalBuilder.makeIntVal(strLit->getLength(), sizeTy); } + case MemRegion::NonParamVarRegionKind: { + // If we have a global constant with a string literal initializer, + // compute the initializer's length. + const VarDecl *Decl = cast<NonParamVarRegion>(MR)->getDecl(); + if (Decl->getType().isConstQualified() && Decl->hasGlobalStorage()) { + if (const Expr *Init = Decl->getInit()) { + if (auto *StrLit = dyn_cast<StringLiteral>(Init)) { + SValBuilder &SvalBuilder = C.getSValBuilder(); + QualType SizeTy = SvalBuilder.getContext().getSizeType(); + return SvalBuilder.makeIntVal(StrLit->getLength(), SizeTy); + } + } + } + [[fallthrough]]; + } case MemRegion::SymbolicRegionKind: case MemRegion::AllocaRegionKind: - case MemRegion::NonParamVarRegionKind: case MemRegion::ParamVarRegionKind: case MemRegion::FieldRegionKind: case MemRegion::ObjCIvarRegionKind: @@ -2006,6 +2029,11 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, SVal maxLastElement = svalBuilder.evalBinOpLN(state, BO_Add, *dstRegVal, *maxLastNL, ptrTy); + // Check if the first byte of the destination is writable. + state = CheckLocation(C, state, Dst, DstVal, AccessKind::write); + if (!state) + return; + // Check if the last byte of the destination is writable. state = CheckLocation(C, state, Dst, maxLastElement, AccessKind::write); if (!state) return; @@ -2018,6 +2046,11 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, // ...and we haven't checked the bound, we'll check the actual copy. if (!boundWarning) { + // Check if the first byte of the destination is writable. + state = CheckLocation(C, state, Dst, DstVal, AccessKind::write); + if (!state) + return; + // Check if the last byte of the destination is writable. state = CheckLocation(C, state, Dst, lastElement, AccessKind::write); if (!state) return; diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CXXDeleteChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CXXDeleteChecker.cpp new file mode 100644 index 000000000000..eb265f4dde68 --- /dev/null +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CXXDeleteChecker.cpp @@ -0,0 +1,248 @@ +//=== CXXDeleteChecker.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 file defines the following new checkers for C++ delete expressions: +// +// * DeleteWithNonVirtualDtorChecker +// Defines a checker for the OOP52-CPP CERT rule: Do not delete a +// polymorphic object without a virtual destructor. +// +// Diagnostic flags -Wnon-virtual-dtor and -Wdelete-non-virtual-dtor +// report if an object with a virtual function but a non-virtual +// destructor exists or is deleted, respectively. +// +// This check exceeds them by comparing the dynamic and static types of +// the object at the point of destruction and only warns if it happens +// through a pointer to a base type without a virtual destructor. The +// check places a note at the last point where the conversion from +// derived to base happened. +// +// * CXXArrayDeleteChecker +// Defines a checker for the EXP51-CPP CERT rule: Do not delete an array +// through a pointer of the incorrect type. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.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" +#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" + +using namespace clang; +using namespace ento; + +namespace { +class CXXDeleteChecker : public Checker<check::PreStmt<CXXDeleteExpr>> { +protected: + class PtrCastVisitor : public BugReporterVisitor { + public: + void Profile(llvm::FoldingSetNodeID &ID) const override { + static int X = 0; + ID.AddPointer(&X); + } + PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + PathSensitiveBugReport &BR) override; + }; + + virtual void + checkTypedDeleteExpr(const CXXDeleteExpr *DE, CheckerContext &C, + const TypedValueRegion *BaseClassRegion, + const SymbolicRegion *DerivedClassRegion) const = 0; + +public: + void checkPreStmt(const CXXDeleteExpr *DE, CheckerContext &C) const; +}; + +class DeleteWithNonVirtualDtorChecker : public CXXDeleteChecker { + mutable std::unique_ptr<BugType> BT; + + void + checkTypedDeleteExpr(const CXXDeleteExpr *DE, CheckerContext &C, + const TypedValueRegion *BaseClassRegion, + const SymbolicRegion *DerivedClassRegion) const override; +}; + +class CXXArrayDeleteChecker : public CXXDeleteChecker { + mutable std::unique_ptr<BugType> BT; + + void + checkTypedDeleteExpr(const CXXDeleteExpr *DE, CheckerContext &C, + const TypedValueRegion *BaseClassRegion, + const SymbolicRegion *DerivedClassRegion) const override; +}; +} // namespace + +void CXXDeleteChecker::checkPreStmt(const CXXDeleteExpr *DE, + CheckerContext &C) const { + const Expr *DeletedObj = DE->getArgument(); + const MemRegion *MR = C.getSVal(DeletedObj).getAsRegion(); + if (!MR) + return; + + OverloadedOperatorKind DeleteKind = + DE->getOperatorDelete()->getOverloadedOperator(); + + if (DeleteKind != OO_Delete && DeleteKind != OO_Array_Delete) + return; + + const auto *BaseClassRegion = MR->getAs<TypedValueRegion>(); + const auto *DerivedClassRegion = MR->getBaseRegion()->getAs<SymbolicRegion>(); + if (!BaseClassRegion || !DerivedClassRegion) + return; + + checkTypedDeleteExpr(DE, C, BaseClassRegion, DerivedClassRegion); +} + +void DeleteWithNonVirtualDtorChecker::checkTypedDeleteExpr( + const CXXDeleteExpr *DE, CheckerContext &C, + const TypedValueRegion *BaseClassRegion, + const SymbolicRegion *DerivedClassRegion) const { + const auto *BaseClass = BaseClassRegion->getValueType()->getAsCXXRecordDecl(); + const auto *DerivedClass = + DerivedClassRegion->getSymbol()->getType()->getPointeeCXXRecordDecl(); + if (!BaseClass || !DerivedClass) + return; + + if (!BaseClass->hasDefinition() || !DerivedClass->hasDefinition()) + return; + + if (BaseClass->getDestructor()->isVirtual()) + return; + + if (!DerivedClass->isDerivedFrom(BaseClass)) + return; + + if (!BT) + BT.reset(new BugType(this, + "Destruction of a polymorphic object with no " + "virtual destructor", + "Logic error")); + + ExplodedNode *N = C.generateNonFatalErrorNode(); + if (!N) + return; + auto R = + std::make_unique<PathSensitiveBugReport>(*BT, BT->getDescription(), N); + + // Mark region of problematic base class for later use in the BugVisitor. + R->markInteresting(BaseClassRegion); + R->addVisitor<PtrCastVisitor>(); + C.emitReport(std::move(R)); +} + +void CXXArrayDeleteChecker::checkTypedDeleteExpr( + const CXXDeleteExpr *DE, CheckerContext &C, + const TypedValueRegion *BaseClassRegion, + const SymbolicRegion *DerivedClassRegion) const { + const auto *BaseClass = BaseClassRegion->getValueType()->getAsCXXRecordDecl(); + const auto *DerivedClass = + DerivedClassRegion->getSymbol()->getType()->getPointeeCXXRecordDecl(); + if (!BaseClass || !DerivedClass) + return; + + if (!BaseClass->hasDefinition() || !DerivedClass->hasDefinition()) + return; + + if (DE->getOperatorDelete()->getOverloadedOperator() != OO_Array_Delete) + return; + + if (!DerivedClass->isDerivedFrom(BaseClass)) + return; + + if (!BT) + BT.reset(new BugType(this, + "Deleting an array of polymorphic objects " + "is undefined", + "Logic error")); + + ExplodedNode *N = C.generateNonFatalErrorNode(); + if (!N) + return; + + SmallString<256> Buf; + llvm::raw_svector_ostream OS(Buf); + + QualType SourceType = BaseClassRegion->getValueType(); + QualType TargetType = + DerivedClassRegion->getSymbol()->getType()->getPointeeType(); + + OS << "Deleting an array of '" << TargetType.getAsString() + << "' objects as their base class '" + << SourceType.getAsString(C.getASTContext().getPrintingPolicy()) + << "' is undefined"; + + auto R = std::make_unique<PathSensitiveBugReport>(*BT, OS.str(), N); + + // Mark region of problematic base class for later use in the BugVisitor. + R->markInteresting(BaseClassRegion); + R->addVisitor<PtrCastVisitor>(); + C.emitReport(std::move(R)); +} + +PathDiagnosticPieceRef +CXXDeleteChecker::PtrCastVisitor::VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + PathSensitiveBugReport &BR) { + const Stmt *S = N->getStmtForDiagnostics(); + if (!S) + return nullptr; + + const auto *CastE = dyn_cast<CastExpr>(S); + if (!CastE) + return nullptr; + + // FIXME: This way of getting base types does not support reference types. + QualType SourceType = CastE->getSubExpr()->getType()->getPointeeType(); + QualType TargetType = CastE->getType()->getPointeeType(); + + if (SourceType.isNull() || TargetType.isNull() || SourceType == TargetType) + return nullptr; + + // Region associated with the current cast expression. + const MemRegion *M = N->getSVal(CastE).getAsRegion(); + if (!M) + return nullptr; + + // Check if target region was marked as problematic previously. + if (!BR.isInteresting(M)) + return nullptr; + + SmallString<256> Buf; + llvm::raw_svector_ostream OS(Buf); + + OS << "Casting from '" << SourceType.getAsString() << "' to '" + << TargetType.getAsString() << "' here"; + + PathDiagnosticLocation Pos(S, BRC.getSourceManager(), + N->getLocationContext()); + return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), + /*addPosRange=*/true); +} + +void ento::registerCXXArrayDeleteChecker(CheckerManager &mgr) { + mgr.registerChecker<CXXArrayDeleteChecker>(); +} + +bool ento::shouldRegisterCXXArrayDeleteChecker(const CheckerManager &mgr) { + return true; +} + +void ento::registerDeleteWithNonVirtualDtorChecker(CheckerManager &mgr) { + mgr.registerChecker<DeleteWithNonVirtualDtorChecker>(); +} + +bool ento::shouldRegisterDeleteWithNonVirtualDtorChecker( + const CheckerManager &mgr) { + return true; +} diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp index 79225e8e6ca3..ea74256935ca 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp @@ -123,7 +123,7 @@ private: void LazyInit_BT(const char *desc, std::unique_ptr<BugType> &BT) const { if (!BT) - BT.reset(new BuiltinBug(OriginalName, desc)); + BT.reset(new BugType(OriginalName, desc)); } bool uninitRefOrPointer(CheckerContext &C, const SVal &V, SourceRange ArgRange, const Expr *ArgEx, @@ -379,7 +379,7 @@ ProgramStateRef CallAndMessageChecker::checkFunctionPointerCall( return nullptr; } if (!BT_call_undef) - BT_call_undef.reset(new BuiltinBug( + BT_call_undef.reset(new BugType( OriginalName, "Called function pointer is an uninitialized pointer value")); emitBadCall(BT_call_undef.get(), C, Callee); @@ -395,7 +395,7 @@ ProgramStateRef CallAndMessageChecker::checkFunctionPointerCall( return nullptr; } if (!BT_call_null) - BT_call_null.reset(new BuiltinBug( + BT_call_null.reset(new BugType( OriginalName, "Called function pointer is null (null dereference)")); emitBadCall(BT_call_null.get(), C, Callee); return nullptr; @@ -450,7 +450,7 @@ ProgramStateRef CallAndMessageChecker::checkCXXMethodCall( return nullptr; } if (!BT_cxx_call_undef) - BT_cxx_call_undef.reset(new BuiltinBug( + BT_cxx_call_undef.reset(new BugType( OriginalName, "Called C++ object pointer is uninitialized")); emitBadCall(BT_cxx_call_undef.get(), C, CC->getCXXThisExpr()); return nullptr; @@ -466,7 +466,7 @@ ProgramStateRef CallAndMessageChecker::checkCXXMethodCall( } if (!BT_cxx_call_null) BT_cxx_call_null.reset( - new BuiltinBug(OriginalName, "Called C++ object pointer is null")); + new BugType(OriginalName, "Called C++ object pointer is null")); emitBadCall(BT_cxx_call_null.get(), C, CC->getCXXThisExpr()); return nullptr; } @@ -495,13 +495,13 @@ CallAndMessageChecker::checkCXXDeallocation(const CXXDeallocatorCall *DC, return nullptr; if (!BT_cxx_delete_undef) BT_cxx_delete_undef.reset( - new BuiltinBug(OriginalName, "Uninitialized argument value")); + new BugType(OriginalName, "Uninitialized argument value")); if (DE->isArrayFormAsWritten()) Desc = "Argument to 'delete[]' is uninitialized"; else Desc = "Argument to 'delete' is uninitialized"; - BugType *BT = BT_cxx_delete_undef.get(); - auto R = std::make_unique<PathSensitiveBugReport>(*BT, Desc, N); + auto R = + std::make_unique<PathSensitiveBugReport>(*BT_cxx_delete_undef, Desc, N); bugreporter::trackExpressionValue(N, DE, *R); C.emitReport(std::move(R)); return nullptr; @@ -585,21 +585,21 @@ void CallAndMessageChecker::checkPreObjCMessage(const ObjCMethodCall &msg, switch (msg.getMessageKind()) { case OCM_Message: if (!BT_msg_undef) - BT_msg_undef.reset(new BuiltinBug(OriginalName, - "Receiver in message expression " - "is an uninitialized value")); + BT_msg_undef.reset(new BugType(OriginalName, + "Receiver in message expression " + "is an uninitialized value")); BT = BT_msg_undef.get(); break; case OCM_PropertyAccess: if (!BT_objc_prop_undef) - BT_objc_prop_undef.reset(new BuiltinBug( + BT_objc_prop_undef.reset(new BugType( OriginalName, "Property access on an uninitialized object pointer")); BT = BT_objc_prop_undef.get(); break; case OCM_Subscript: if (!BT_objc_subscript_undef) - BT_objc_subscript_undef.reset(new BuiltinBug( + BT_objc_subscript_undef.reset(new BugType( OriginalName, "Subscript access on an uninitialized object pointer")); BT = BT_objc_subscript_undef.get(); @@ -634,8 +634,8 @@ void CallAndMessageChecker::emitNilReceiverBug(CheckerContext &C, } if (!BT_msg_ret) - BT_msg_ret.reset(new BuiltinBug(OriginalName, - "Receiver in message expression is 'nil'")); + BT_msg_ret.reset( + new BugType(OriginalName, "Receiver in message expression is 'nil'")); const ObjCMessageExpr *ME = msg.getOriginExpr(); diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp index 2d2e14de3f2b..d1d4f3baf6a8 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp @@ -24,7 +24,7 @@ using namespace ento; namespace { class CastSizeChecker : public Checker< check::PreStmt<CastExpr> > { - mutable std::unique_ptr<BuiltinBug> BT; + mutable std::unique_ptr<BugType> BT; public: void checkPreStmt(const CastExpr *CE, CheckerContext &C) const; @@ -132,11 +132,11 @@ void CastSizeChecker::checkPreStmt(const CastExpr *CE,CheckerContext &C) const { if (ExplodedNode *errorNode = C.generateErrorNode()) { if (!BT) - BT.reset(new BuiltinBug(this, "Cast region with wrong size.", - "Cast a region whose size is not a multiple" - " of the destination type size.")); - auto R = std::make_unique<PathSensitiveBugReport>(*BT, BT->getDescription(), - errorNode); + BT.reset(new BugType(this, "Cast region with wrong size.")); + constexpr llvm::StringLiteral Msg = + "Cast a region whose size is not a multiple of the destination type " + "size."; + auto R = std::make_unique<PathSensitiveBugReport>(*BT, Msg, errorNode); R->addRange(CE->getSourceRange()); C.emitReport(std::move(R)); } diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp index bd6655cc1e3f..fedc6db3723a 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp @@ -1045,8 +1045,8 @@ bool ObjCDeallocChecker::isReleasedByCIFilterDealloc( StringRef IvarName = PropImpl->getPropertyIvarDecl()->getName(); const char *ReleasePrefix = "input"; - if (!(PropName.startswith(ReleasePrefix) || - IvarName.startswith(ReleasePrefix))) { + if (!(PropName.starts_with(ReleasePrefix) || + IvarName.starts_with(ReleasePrefix))) { return false; } diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp index 18d575041ba7..afc5e6b48008 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp @@ -27,7 +27,6 @@ using namespace ento; static bool isArc4RandomAvailable(const ASTContext &Ctx) { const llvm::Triple &T = Ctx.getTargetInfo().getTriple(); return T.getVendor() == llvm::Triple::Apple || - T.getOS() == llvm::Triple::CloudABI || T.isOSFreeBSD() || T.isOSNetBSD() || T.isOSOpenBSD() || @@ -141,42 +140,43 @@ void WalkAST::VisitCallExpr(CallExpr *CE) { if (!II) // if no identifier, not a simple C function return; StringRef Name = II->getName(); - if (Name.startswith("__builtin_")) + if (Name.starts_with("__builtin_")) Name = Name.substr(10); // Set the evaluation function by switching on the callee name. - FnCheck evalFunction = llvm::StringSwitch<FnCheck>(Name) - .Case("bcmp", &WalkAST::checkCall_bcmp) - .Case("bcopy", &WalkAST::checkCall_bcopy) - .Case("bzero", &WalkAST::checkCall_bzero) - .Case("gets", &WalkAST::checkCall_gets) - .Case("getpw", &WalkAST::checkCall_getpw) - .Case("mktemp", &WalkAST::checkCall_mktemp) - .Case("mkstemp", &WalkAST::checkCall_mkstemp) - .Case("mkdtemp", &WalkAST::checkCall_mkstemp) - .Case("mkstemps", &WalkAST::checkCall_mkstemp) - .Cases("strcpy", "__strcpy_chk", &WalkAST::checkCall_strcpy) - .Cases("strcat", "__strcat_chk", &WalkAST::checkCall_strcat) - .Cases("sprintf", "vsprintf", "scanf", "wscanf", "fscanf", "fwscanf", - "vscanf", "vwscanf", "vfscanf", "vfwscanf", - &WalkAST::checkDeprecatedOrUnsafeBufferHandling) - .Cases("sscanf", "swscanf", "vsscanf", "vswscanf", "swprintf", - "snprintf", "vswprintf", "vsnprintf", "memcpy", "memmove", - &WalkAST::checkDeprecatedOrUnsafeBufferHandling) - .Cases("strncpy", "strncat", "memset", - &WalkAST::checkDeprecatedOrUnsafeBufferHandling) - .Case("drand48", &WalkAST::checkCall_rand) - .Case("erand48", &WalkAST::checkCall_rand) - .Case("jrand48", &WalkAST::checkCall_rand) - .Case("lrand48", &WalkAST::checkCall_rand) - .Case("mrand48", &WalkAST::checkCall_rand) - .Case("nrand48", &WalkAST::checkCall_rand) - .Case("lcong48", &WalkAST::checkCall_rand) - .Case("rand", &WalkAST::checkCall_rand) - .Case("rand_r", &WalkAST::checkCall_rand) - .Case("random", &WalkAST::checkCall_random) - .Case("vfork", &WalkAST::checkCall_vfork) - .Default(nullptr); + FnCheck evalFunction = + llvm::StringSwitch<FnCheck>(Name) + .Case("bcmp", &WalkAST::checkCall_bcmp) + .Case("bcopy", &WalkAST::checkCall_bcopy) + .Case("bzero", &WalkAST::checkCall_bzero) + .Case("gets", &WalkAST::checkCall_gets) + .Case("getpw", &WalkAST::checkCall_getpw) + .Case("mktemp", &WalkAST::checkCall_mktemp) + .Case("mkstemp", &WalkAST::checkCall_mkstemp) + .Case("mkdtemp", &WalkAST::checkCall_mkstemp) + .Case("mkstemps", &WalkAST::checkCall_mkstemp) + .Cases("strcpy", "__strcpy_chk", &WalkAST::checkCall_strcpy) + .Cases("strcat", "__strcat_chk", &WalkAST::checkCall_strcat) + .Cases("sprintf", "vsprintf", "scanf", "wscanf", "fscanf", "fwscanf", + "vscanf", "vwscanf", "vfscanf", "vfwscanf", + &WalkAST::checkDeprecatedOrUnsafeBufferHandling) + .Cases("sscanf", "swscanf", "vsscanf", "vswscanf", "swprintf", + "snprintf", "vswprintf", "vsnprintf", "memcpy", "memmove", + &WalkAST::checkDeprecatedOrUnsafeBufferHandling) + .Cases("strncpy", "strncat", "memset", "fprintf", + &WalkAST::checkDeprecatedOrUnsafeBufferHandling) + .Case("drand48", &WalkAST::checkCall_rand) + .Case("erand48", &WalkAST::checkCall_rand) + .Case("jrand48", &WalkAST::checkCall_rand) + .Case("lrand48", &WalkAST::checkCall_rand) + .Case("mrand48", &WalkAST::checkCall_rand) + .Case("nrand48", &WalkAST::checkCall_rand) + .Case("lcong48", &WalkAST::checkCall_rand) + .Case("rand", &WalkAST::checkCall_rand) + .Case("rand_r", &WalkAST::checkCall_rand) + .Case("random", &WalkAST::checkCall_random) + .Case("vfork", &WalkAST::checkCall_vfork) + .Default(nullptr); // If the callee isn't defined, it is not of security concern. // Check and evaluate the call. @@ -219,7 +219,6 @@ void WalkAST::VisitForStmt(ForStmt *FS) { //===----------------------------------------------------------------------===// // Check: floating point variable used as loop counter. -// Originally: <rdar://problem/6336718> // Implements: CERT security coding advisory FLP-30. //===----------------------------------------------------------------------===// @@ -467,8 +466,8 @@ void WalkAST::checkCall_bzero(const CallExpr *CE, const FunctionDecl *FD) { //===----------------------------------------------------------------------===// -// Check: Any use of 'gets' is insecure. -// Originally: <rdar://problem/6335715> +// Check: Any use of 'gets' is insecure. Most man pages literally says this. +// // Implements (part of): 300-BSI (buildsecurityin.us-cert.gov) // CWE-242: Use of Inherently Dangerous Function //===----------------------------------------------------------------------===// @@ -739,10 +738,10 @@ void WalkAST::checkCall_strcat(const CallExpr *CE, const FunctionDecl *FD) { // Check: Any use of 'sprintf', 'vsprintf', 'scanf', 'wscanf', 'fscanf', // 'fwscanf', 'vscanf', 'vwscanf', 'vfscanf', 'vfwscanf', 'sscanf', // 'swscanf', 'vsscanf', 'vswscanf', 'swprintf', 'snprintf', 'vswprintf', -// 'vsnprintf', 'memcpy', 'memmove', 'strncpy', 'strncat', 'memset' -// is deprecated since C11. +// 'vsnprintf', 'memcpy', 'memmove', 'strncpy', 'strncat', 'memset', +// 'fprintf' is deprecated since C11. // -// Use of 'sprintf', 'vsprintf', 'scanf', 'wscanf','fscanf', +// Use of 'sprintf', 'fprintf', 'vsprintf', 'scanf', 'wscanf', 'fscanf', // 'fwscanf', 'vscanf', 'vwscanf', 'vfscanf', 'vfwscanf', 'sscanf', // 'swscanf', 'vsscanf', 'vswscanf' without buffer limitations // is insecure. @@ -764,14 +763,15 @@ void WalkAST::checkDeprecatedOrUnsafeBufferHandling(const CallExpr *CE, enum { DEPR_ONLY = -1, UNKNOWN_CALL = -2 }; StringRef Name = FD->getIdentifier()->getName(); - if (Name.startswith("__builtin_")) + if (Name.starts_with("__builtin_")) Name = Name.substr(10); int ArgIndex = llvm::StringSwitch<int>(Name) .Cases("scanf", "wscanf", "vscanf", "vwscanf", 0) - .Cases("sprintf", "vsprintf", "fscanf", "fwscanf", "vfscanf", - "vfwscanf", "sscanf", "swscanf", "vsscanf", "vswscanf", 1) + .Cases("fscanf", "fwscanf", "vfscanf", "vfwscanf", "sscanf", + "swscanf", "vsscanf", "vswscanf", 1) + .Cases("sprintf", "vsprintf", "fprintf", 1) .Cases("swprintf", "snprintf", "vswprintf", "vsnprintf", "memcpy", "memmove", "memset", "strncpy", "strncat", DEPR_ONLY) .Default(UNKNOWN_CALL); @@ -847,8 +847,13 @@ bool WalkAST::checkCall_strCommon(const CallExpr *CE, const FunctionDecl *FD) { } //===----------------------------------------------------------------------===// -// Check: Linear congruent random number generators should not be used -// Originally: <rdar://problem/63371000> +// Check: Linear congruent random number generators should not be used, +// i.e. rand(), random(). +// +// E. Bach, "Efficient prediction of Marsaglia-Zaman random number generators," +// in IEEE Transactions on Information Theory, vol. 44, no. 3, pp. 1253-1257, +// May 1998, https://doi.org/10.1109/18.669305 +// // CWE-338: Use of cryptographically weak prng //===----------------------------------------------------------------------===// @@ -890,11 +895,7 @@ void WalkAST::checkCall_rand(const CallExpr *CE, const FunctionDecl *FD) { CE->getCallee()->getSourceRange()); } -//===----------------------------------------------------------------------===// -// Check: 'random' should not be used -// Originally: <rdar://problem/63371000> -//===----------------------------------------------------------------------===// - +// See justification for rand(). void WalkAST::checkCall_random(const CallExpr *CE, const FunctionDecl *FD) { if (!CheckRand || !filter.check_rand) return; @@ -990,8 +991,18 @@ void WalkAST::checkMsg_decodeValueOfObjCType(const ObjCMessageExpr *ME) { } //===----------------------------------------------------------------------===// -// Check: Should check whether privileges are dropped successfully. -// Originally: <rdar://problem/6337132> +// Check: The caller should always verify that the privileges +// were dropped successfully. +// +// Some library functions, like setuid() and setgid(), should always be used +// with a check of the return value to verify that the function completed +// successfully. If the drop fails, the software will continue to run +// with the raised privileges, which might provide additional access +// to unprivileged users. +// +// (Note that this check predates __attribute__((warn_unused_result)). +// Do we still need it now that we have a compiler warning for this? +// Are these standard functions already annotated this way?) //===----------------------------------------------------------------------===// void WalkAST::checkUncheckedReturnValue(CallExpr *CE) { diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp index 9ace1583eb53..9e11d8d9ecbc 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp @@ -41,7 +41,7 @@ bool isRootChanged(intptr_t k) { return k == ROOT_CHANGED; } // bug<--foo()-- JAIL_ENTERED<--foo()-- 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; + mutable std::unique_ptr<BugType> BT_BreakJail; const CallDescription Chroot{{"chroot"}, 1}, Chdir{{"chdir"}, 1}; @@ -125,11 +125,11 @@ void ChrootChecker::checkPreCall(const CallEvent &Call, if (isRootChanged((intptr_t) *k)) if (ExplodedNode *N = C.generateNonFatalErrorNode()) { if (!BT_BreakJail) - BT_BreakJail.reset(new BuiltinBug( - this, "Break out of jail", "No call of chdir(\"/\") immediately " - "after chroot")); - C.emitReport(std::make_unique<PathSensitiveBugReport>( - *BT_BreakJail, BT_BreakJail->getDescription(), N)); + BT_BreakJail.reset(new BugType(this, "Break out of jail")); + constexpr llvm::StringLiteral Msg = + "No call of chdir(\"/\") immediately after chroot"; + C.emitReport( + std::make_unique<PathSensitiveBugReport>(*BT_BreakJail, Msg, N)); } } diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ConversionChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ConversionChecker.cpp index 6ea39fb95e9a..8b34b41bab21 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ConversionChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ConversionChecker.cpp @@ -42,7 +42,7 @@ public: void checkPreStmt(const ImplicitCastExpr *Cast, CheckerContext &C) const; private: - mutable std::unique_ptr<BuiltinBug> BT; + mutable std::unique_ptr<BugType> BT; bool isLossOfPrecision(const ImplicitCastExpr *Cast, QualType DestType, CheckerContext &C) const; @@ -127,8 +127,7 @@ void ConversionChecker::checkPreStmt(const ImplicitCastExpr *Cast, void ConversionChecker::reportBug(ExplodedNode *N, const Expr *E, CheckerContext &C, const char Msg[]) const { if (!BT) - BT.reset( - new BuiltinBug(this, "Conversion", "Possible loss of sign/precision.")); + BT.reset(new BugType(this, "Conversion")); // Generate a report for this bug. auto R = std::make_unique<PathSensitiveBugReport>(*BT, Msg, N); diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp index 5f44c9476928..86f446fc411c 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp @@ -183,7 +183,7 @@ public: // Files autogenerated by DriverKit IIG contain some dead stores that // we don't want to report. - if (Data.startswith("/* iig")) + if (Data.starts_with("/* iig")) return true; return false; diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp index 2fe91467c8c5..04bbe85473c0 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp @@ -322,7 +322,7 @@ bool ento::shouldRegisterExplodedGraphViewer(const CheckerManager &mgr) { namespace { class ReportStmts : public Checker<check::PreStmt<Stmt>> { - BuiltinBug BT_stmtLoc{this, "Statement"}; + BugType BT_stmtLoc{this, "Statement"}; public: void checkPreStmt(const Stmt *S, CheckerContext &C) const { diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DeleteWithNonVirtualDtorChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DeleteWithNonVirtualDtorChecker.cpp deleted file mode 100644 index 7c5833762008..000000000000 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DeleteWithNonVirtualDtorChecker.cpp +++ /dev/null @@ -1,155 +0,0 @@ -//===-- DeleteWithNonVirtualDtorChecker.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 -// -//===----------------------------------------------------------------------===// -// -// Defines a checker for the OOP52-CPP CERT rule: Do not delete a polymorphic -// object without a virtual destructor. -// -// Diagnostic flags -Wnon-virtual-dtor and -Wdelete-non-virtual-dtor report if -// an object with a virtual function but a non-virtual destructor exists or is -// deleted, respectively. -// -// This check exceeds them by comparing the dynamic and static types of the -// object at the point of destruction and only warns if it happens through a -// pointer to a base type without a virtual destructor. The check places a note -// at the last point where the conversion from derived to base happened. -// -//===----------------------------------------------------------------------===// - -#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" -#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.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" -#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" - -using namespace clang; -using namespace ento; - -namespace { -class DeleteWithNonVirtualDtorChecker - : public Checker<check::PreStmt<CXXDeleteExpr>> { - mutable std::unique_ptr<BugType> BT; - - class DeleteBugVisitor : public BugReporterVisitor { - public: - DeleteBugVisitor() : Satisfied(false) {} - void Profile(llvm::FoldingSetNodeID &ID) const override { - static int X = 0; - ID.AddPointer(&X); - } - PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, - BugReporterContext &BRC, - PathSensitiveBugReport &BR) override; - - private: - bool Satisfied; - }; - -public: - void checkPreStmt(const CXXDeleteExpr *DE, CheckerContext &C) const; -}; -} // end anonymous namespace - -void DeleteWithNonVirtualDtorChecker::checkPreStmt(const CXXDeleteExpr *DE, - CheckerContext &C) const { - const Expr *DeletedObj = DE->getArgument(); - const MemRegion *MR = C.getSVal(DeletedObj).getAsRegion(); - if (!MR) - return; - - const auto *BaseClassRegion = MR->getAs<TypedValueRegion>(); - const auto *DerivedClassRegion = MR->getBaseRegion()->getAs<SymbolicRegion>(); - if (!BaseClassRegion || !DerivedClassRegion) - return; - - const auto *BaseClass = BaseClassRegion->getValueType()->getAsCXXRecordDecl(); - const auto *DerivedClass = - DerivedClassRegion->getSymbol()->getType()->getPointeeCXXRecordDecl(); - if (!BaseClass || !DerivedClass) - return; - - if (!BaseClass->hasDefinition() || !DerivedClass->hasDefinition()) - return; - - if (BaseClass->getDestructor()->isVirtual()) - return; - - if (!DerivedClass->isDerivedFrom(BaseClass)) - return; - - if (!BT) - BT.reset(new BugType(this, - "Destruction of a polymorphic object with no " - "virtual destructor", - "Logic error")); - - ExplodedNode *N = C.generateNonFatalErrorNode(); - if (!N) - return; - auto R = std::make_unique<PathSensitiveBugReport>(*BT, BT->getDescription(), N); - - // Mark region of problematic base class for later use in the BugVisitor. - R->markInteresting(BaseClassRegion); - R->addVisitor(std::make_unique<DeleteBugVisitor>()); - C.emitReport(std::move(R)); -} - -PathDiagnosticPieceRef -DeleteWithNonVirtualDtorChecker::DeleteBugVisitor::VisitNode( - const ExplodedNode *N, BugReporterContext &BRC, - PathSensitiveBugReport &BR) { - // Stop traversal after the first conversion was found on a path. - if (Satisfied) - return nullptr; - - const Stmt *S = N->getStmtForDiagnostics(); - if (!S) - return nullptr; - - const auto *CastE = dyn_cast<CastExpr>(S); - if (!CastE) - return nullptr; - - // Only interested in DerivedToBase implicit casts. - // Explicit casts can have different CastKinds. - if (const auto *ImplCastE = dyn_cast<ImplicitCastExpr>(CastE)) { - if (ImplCastE->getCastKind() != CK_DerivedToBase) - return nullptr; - } - - // Region associated with the current cast expression. - const MemRegion *M = N->getSVal(CastE).getAsRegion(); - if (!M) - return nullptr; - - // Check if target region was marked as problematic previously. - if (!BR.isInteresting(M)) - return nullptr; - - // Stop traversal on this path. - Satisfied = true; - - SmallString<256> Buf; - llvm::raw_svector_ostream OS(Buf); - OS << "Conversion from derived to base happened here"; - PathDiagnosticLocation Pos(S, BRC.getSourceManager(), - N->getLocationContext()); - return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true); -} - -void ento::registerDeleteWithNonVirtualDtorChecker(CheckerManager &mgr) { - mgr.registerChecker<DeleteWithNonVirtualDtorChecker>(); -} - -bool ento::shouldRegisterDeleteWithNonVirtualDtorChecker( - const CheckerManager &mgr) { - return true; -} diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp index 1f3e9e00d3e6..034774a252b1 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp @@ -268,12 +268,12 @@ void DynamicTypePropagation::checkPreCall(const CallEvent &Call, // a more-derived class. switch (Ctor->getOriginExpr()->getConstructionKind()) { - case CXXConstructExpr::CK_Complete: - case CXXConstructExpr::CK_Delegating: + case CXXConstructionKind::Complete: + case CXXConstructionKind::Delegating: // No additional type info necessary. return; - case CXXConstructExpr::CK_NonVirtualBase: - case CXXConstructExpr::CK_VirtualBase: + case CXXConstructionKind::NonVirtualBase: + case CXXConstructionKind::VirtualBase: if (const MemRegion *Target = Ctor->getCXXThisVal().getAsRegion()) recordFixedType(Target, Ctor->getDecl(), C); return; @@ -360,16 +360,16 @@ void DynamicTypePropagation::checkPostCall(const CallEvent &Call, if (const CXXConstructorCall *Ctor = dyn_cast<CXXConstructorCall>(&Call)) { // We may need to undo the effects of our pre-call check. switch (Ctor->getOriginExpr()->getConstructionKind()) { - case CXXConstructExpr::CK_Complete: - case CXXConstructExpr::CK_Delegating: + case CXXConstructionKind::Complete: + case CXXConstructionKind::Delegating: // No additional work necessary. // Note: This will leave behind the actual type of the object for // complete constructors, but arguably that's a good thing, since it // means the dynamic type info will be correct even for objects // constructed with operator new. return; - case CXXConstructExpr::CK_NonVirtualBase: - case CXXConstructExpr::CK_VirtualBase: + case CXXConstructionKind::NonVirtualBase: + case CXXConstructionKind::VirtualBase: if (const MemRegion *Target = Ctor->getCXXThisVal().getAsRegion()) { // We just finished a base constructor. Now we can use the subclass's // type when resolving virtual calls. @@ -713,7 +713,7 @@ static bool isObjCTypeParamDependent(QualType Type) { class IsObjCTypeParamDependentTypeVisitor : public RecursiveASTVisitor<IsObjCTypeParamDependentTypeVisitor> { public: - IsObjCTypeParamDependentTypeVisitor() : Result(false) {} + IsObjCTypeParamDependentTypeVisitor() = default; bool VisitObjCTypeParamType(const ObjCTypeParamType *Type) { if (isa<ObjCTypeParamDecl>(Type->getDecl())) { Result = true; @@ -722,7 +722,7 @@ static bool isObjCTypeParamDependent(QualType Type) { return true; } - bool Result; + bool Result = false; }; IsObjCTypeParamDependentTypeVisitor Visitor; diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp index 1077ceb6288e..7c51673422a0 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp @@ -22,10 +22,12 @@ #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "llvm/Support/FormatVariadic.h" #include <optional> using namespace clang; using namespace ento; +using llvm::formatv; namespace { // This evaluator checks two SVals for equality. The first SVal is provided via @@ -58,8 +60,9 @@ public: // Being conservative, it does not warn if there is slight possibility the // value can be matching. class EnumCastOutOfRangeChecker : public Checker<check::PreStmt<CastExpr>> { - mutable std::unique_ptr<BuiltinBug> EnumValueCastOutOfRange; - void reportWarning(CheckerContext &C) const; + mutable std::unique_ptr<BugType> EnumValueCastOutOfRange; + void reportWarning(CheckerContext &C, const CastExpr *CE, + const EnumDecl *E) const; public: void checkPreStmt(const CastExpr *CE, CheckerContext &C) const; @@ -72,21 +75,43 @@ EnumValueVector getDeclValuesForEnum(const EnumDecl *ED) { EnumValueVector DeclValues( std::distance(ED->enumerator_begin(), ED->enumerator_end())); llvm::transform(ED->enumerators(), DeclValues.begin(), - [](const EnumConstantDecl *D) { return D->getInitVal(); }); + [](const EnumConstantDecl *D) { return D->getInitVal(); }); return DeclValues; } } // namespace -void EnumCastOutOfRangeChecker::reportWarning(CheckerContext &C) const { +void EnumCastOutOfRangeChecker::reportWarning(CheckerContext &C, + const CastExpr *CE, + const EnumDecl *E) const { + assert(E && "valid EnumDecl* is expected"); if (const ExplodedNode *N = C.generateNonFatalErrorNode()) { if (!EnumValueCastOutOfRange) EnumValueCastOutOfRange.reset( - new BuiltinBug(this, "Enum cast out of range", - "The value provided to the cast expression is not in " - "the valid range of values for the enum")); - C.emitReport(std::make_unique<PathSensitiveBugReport>( - *EnumValueCastOutOfRange, EnumValueCastOutOfRange->getDescription(), - N)); + new BugType(this, "Enum cast out of range")); + + std::string ValueStr = "", NameStr = "the enum"; + + // Try to add details to the message: + const auto ConcreteValue = + C.getSVal(CE->getSubExpr()).getAs<nonloc::ConcreteInt>(); + if (ConcreteValue) { + ValueStr = formatv(" '{0}'", ConcreteValue->getValue()); + } + if (StringRef EnumName{E->getName()}; !EnumName.empty()) { + NameStr = formatv("'{0}'", EnumName); + } + + std::string Msg = formatv("The value{0} provided to the cast expression is " + "not in the valid range of values for {1}", + ValueStr, NameStr); + + auto BR = std::make_unique<PathSensitiveBugReport>(*EnumValueCastOutOfRange, + Msg, N); + bugreporter::trackExpressionValue(N, CE->getSubExpr(), *BR); + BR->addNote("enum declared here", + PathDiagnosticLocation::create(E, C.getSourceManager()), + {E->getSourceRange()}); + C.emitReport(std::move(BR)); } } @@ -129,14 +154,25 @@ void EnumCastOutOfRangeChecker::checkPreStmt(const CastExpr *CE, const EnumDecl *ED = T->castAs<EnumType>()->getDecl(); EnumValueVector DeclValues = getDeclValuesForEnum(ED); + + // If the declarator list is empty, bail out. + // Every initialization an enum with a fixed underlying type but without any + // enumerators would produce a warning if we were to continue at this point. + // The most notable example is std::byte in the C++17 standard library. + // TODO: Create heuristics to bail out when the enum type is intended to be + // used to store combinations of flag values (to mitigate the limitation + // described in the docs). + if (DeclValues.size() == 0) + return; + // Check if any of the enum values possibly match. - bool PossibleValueMatch = llvm::any_of( - DeclValues, ConstraintBasedEQEvaluator(C, *ValueToCast)); + bool PossibleValueMatch = + llvm::any_of(DeclValues, ConstraintBasedEQEvaluator(C, *ValueToCast)); // If there is no value that can possibly match any of the enum values, then // warn. if (!PossibleValueMatch) - reportWarning(C); + reportWarning(C, CE, ED); } void ento::registerEnumCastOutOfRangeChecker(CheckerManager &mgr) { diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.cpp index be2fa91b994a..1b34ea0e056e 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.cpp @@ -312,18 +312,6 @@ ProgramStateRef setErrnoStdMustBeChecked(ProgramStateRef State, return setErrnoState(State, MustBeChecked); } -const NoteTag *getNoteTagForStdSuccess(CheckerContext &C, llvm::StringRef Fn) { - return getErrnoNoteTag( - C, llvm::formatv( - "'errno' may be undefined after successful call to '{0}'", Fn)); -} - -const NoteTag *getNoteTagForStdMustBeChecked(CheckerContext &C, - llvm::StringRef Fn) { - return getErrnoNoteTag( - C, llvm::formatv("'{0}' indicates failure only by setting 'errno'", Fn)); -} - } // namespace errno_modeling } // namespace ento } // namespace clang diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.h b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.h index 0707fd16d6e6..6b53572fe5e2 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.h +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.h @@ -84,8 +84,7 @@ 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. +/// affected). ProgramStateRef setErrnoForStdSuccess(ProgramStateRef State, CheckerContext &C); /// Set errno state for the common case when a standard function fails. @@ -100,23 +99,10 @@ ProgramStateRef setErrnoForStdFailure(ProgramStateRef State, CheckerContext &C, /// 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/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp index 15ba29050e90..2c7bacac33a6 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp @@ -325,12 +325,12 @@ void ExprInspectionChecker::analyzerDump(const CallExpr *CE, void ExprInspectionChecker::analyzerGetExtent(const CallExpr *CE, CheckerContext &C) const { - const MemRegion *MR = getArgRegion(CE, C); - if (!MR) + const Expr *Arg = getArgExpr(CE, C); + if (!Arg) return; ProgramStateRef State = C.getState(); - DefinedOrUnknownSVal Size = getDynamicExtent(State, MR, C.getSValBuilder()); + SVal Size = getDynamicExtentWithOffset(State, C.getSVal(Arg)); State = State->BindExpr(CE, C.getLocationContext(), Size); C.addTransition(State); @@ -338,12 +338,12 @@ void ExprInspectionChecker::analyzerGetExtent(const CallExpr *CE, void ExprInspectionChecker::analyzerDumpExtent(const CallExpr *CE, CheckerContext &C) const { - const MemRegion *MR = getArgRegion(CE, C); - if (!MR) + const Expr *Arg = getArgExpr(CE, C); + if (!Arg) return; - DefinedOrUnknownSVal Size = - getDynamicExtent(C.getState(), MR, C.getSValBuilder()); + ProgramStateRef State = C.getState(); + SVal Size = getDynamicExtentWithOffset(State, C.getSVal(Arg)); printAndReport(C, Size); } @@ -362,8 +362,8 @@ void ExprInspectionChecker::analyzerDumpElementCount(const CallExpr *CE, assert(!ElementTy->isPointerType()); - DefinedOrUnknownSVal ElementCount = - getDynamicElementCount(C.getState(), MR, C.getSValBuilder(), ElementTy); + DefinedOrUnknownSVal ElementCount = getDynamicElementCountWithOffset( + C.getState(), C.getSVal(getArgExpr(CE, C)), ElementTy); printAndReport(C, ElementCount); } diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp index 6275e49e51ae..2ee201b64008 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp @@ -24,7 +24,7 @@ using namespace ento; namespace { class FixedAddressChecker : public Checker< check::PreStmt<BinaryOperator> > { - mutable std::unique_ptr<BuiltinBug> BT; + mutable std::unique_ptr<BugType> BT; public: void checkPreStmt(const BinaryOperator *B, CheckerContext &C) const; @@ -49,14 +49,13 @@ void FixedAddressChecker::checkPreStmt(const BinaryOperator *B, return; if (ExplodedNode *N = C.generateNonFatalErrorNode()) { + // FIXME: improve grammar in the following strings: if (!BT) - BT.reset( - new BuiltinBug(this, "Use fixed address", - "Using a fixed address is not portable because that " - "address will probably not be valid in all " - "environments or platforms.")); - auto R = - std::make_unique<PathSensitiveBugReport>(*BT, BT->getDescription(), N); + BT.reset(new BugType(this, "Use fixed address")); + constexpr llvm::StringLiteral Msg = + "Using a fixed address is not portable because that address will " + "probably not be valid in all environments or platforms."; + auto R = std::make_unique<PathSensitiveBugReport>(*BT, Msg, N); R->addRange(B->getRHS()->getSourceRange()); C.emitReport(std::move(R)); } diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/FuchsiaHandleChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/FuchsiaHandleChecker.cpp index 38b4caa12aef..079bc61a87d9 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/FuchsiaHandleChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/FuchsiaHandleChecker.cpp @@ -381,7 +381,7 @@ void FuchsiaHandleChecker::checkPostCall(const CallEvent &Call, SymbolRef RetSym = Call.getReturnValue().getAsSymbol(); Notes.push_back([RetSym, FuncDecl](BugReport &BR) -> std::string { auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR); - if (auto IsInteresting = PathBR->getInterestingnessKind(RetSym)) { + if (PathBR->getInterestingnessKind(RetSym)) { std::string SBuf; llvm::raw_string_ostream OS(SBuf); OS << "Function '" << FuncDecl->getDeclName() @@ -397,7 +397,7 @@ void FuchsiaHandleChecker::checkPostCall(const CallEvent &Call, SymbolRef RetSym = Call.getReturnValue().getAsSymbol(); Notes.push_back([RetSym, FuncDecl](BugReport &BR) -> std::string { auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR); - if (auto IsInteresting = PathBR->getInterestingnessKind(RetSym)) { + if (PathBR->getInterestingnessKind(RetSym)) { std::string SBuf; llvm::raw_string_ostream OS(SBuf); OS << "Function '" << FuncDecl->getDeclName() @@ -431,7 +431,7 @@ void FuchsiaHandleChecker::checkPostCall(const CallEvent &Call, } else { Notes.push_back([Handle, ParamDiagIdx](BugReport &BR) -> std::string { auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR); - if (auto IsInteresting = PathBR->getInterestingnessKind(Handle)) { + if (PathBR->getInterestingnessKind(Handle)) { std::string SBuf; llvm::raw_string_ostream OS(SBuf); OS << "Handle released through " << ParamDiagIdx @@ -445,7 +445,7 @@ void FuchsiaHandleChecker::checkPostCall(const CallEvent &Call, } else if (hasFuchsiaAttr<AcquireHandleAttr>(PVD)) { Notes.push_back([Handle, ParamDiagIdx](BugReport &BR) -> std::string { auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR); - if (auto IsInteresting = PathBR->getInterestingnessKind(Handle)) { + if (PathBR->getInterestingnessKind(Handle)) { std::string SBuf; llvm::raw_string_ostream OS(SBuf); OS << "Handle allocated through " << ParamDiagIdx @@ -459,7 +459,7 @@ void FuchsiaHandleChecker::checkPostCall(const CallEvent &Call, } else if (hasFuchsiaUnownedAttr<AcquireHandleAttr>(PVD)) { Notes.push_back([Handle, ParamDiagIdx](BugReport &BR) -> std::string { auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR); - if (auto IsInteresting = PathBR->getInterestingnessKind(Handle)) { + if (PathBR->getInterestingnessKind(Handle)) { std::string SBuf; llvm::raw_string_ostream OS(SBuf); OS << "Unowned handle allocated through " << ParamDiagIdx @@ -653,10 +653,11 @@ void FuchsiaHandleChecker::reportBug(SymbolRef Sym, ExplodedNode *ErrorNode, if (Type.isSuppressOnSink()) { const ExplodedNode *AcquireNode = getAcquireSite(ErrorNode, Sym, C); if (AcquireNode) { + const Stmt *S = AcquireNode->getStmtForDiagnostics(); + assert(S && "Statement cannot be null."); PathDiagnosticLocation LocUsedForUniqueing = PathDiagnosticLocation::createBegin( - AcquireNode->getStmtForDiagnostics(), C.getSourceManager(), - AcquireNode->getLocationContext()); + S, C.getSourceManager(), AcquireNode->getLocationContext()); R = std::make_unique<PathSensitiveBugReport>( Type, Msg, ErrorNode, LocUsedForUniqueing, diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/GCDAntipatternChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/GCDAntipatternChecker.cpp index 8e02ef74c668..5637941a58f0 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/GCDAntipatternChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/GCDAntipatternChecker.cpp @@ -73,7 +73,7 @@ decltype(auto) bindAssignmentToDecl(const char *DeclName) { static bool isTest(const Decl *D) { if (const auto* ND = dyn_cast<NamedDecl>(D)) { std::string DeclName = ND->getNameAsString(); - if (StringRef(DeclName).startswith("test")) + if (StringRef(DeclName).starts_with("test")) return true; } if (const auto *OD = dyn_cast<ObjCMethodDecl>(D)) { diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/GTestChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/GTestChecker.cpp index 43d2eeeb4b27..6c32a8dec844 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/GTestChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/GTestChecker.cpp @@ -92,11 +92,11 @@ using namespace ento; namespace { class GTestChecker : public Checker<check::PostCall> { - mutable IdentifierInfo *AssertionResultII; - mutable IdentifierInfo *SuccessII; + mutable IdentifierInfo *AssertionResultII = nullptr; + mutable IdentifierInfo *SuccessII = nullptr; public: - GTestChecker(); + GTestChecker() = default; void checkPostCall(const CallEvent &Call, CheckerContext &C) const; @@ -120,8 +120,6 @@ private: }; } // End anonymous namespace. -GTestChecker::GTestChecker() : AssertionResultII(nullptr), SuccessII(nullptr) {} - /// Model a call to an un-inlined AssertionResult(bool) or /// AssertionResult(bool &, ...). /// To do so, constrain the value of the newly-constructed instance's 'success_' diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp index 3dcb45c0b110..4ceaf933d0bf 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp @@ -104,8 +104,7 @@ bool isStdin(SVal Val, const ASTContext &ACtx) { // variable named stdin with the proper type. if (const auto *D = dyn_cast_or_null<VarDecl>(DeclReg->getDecl())) { D = D->getCanonicalDecl(); - // FIXME: This should look for an exact match. - if (D->getName().contains("stdin") && D->isExternC()) { + if (D->getName() == "stdin" && D->hasExternalStorage() && D->isExternC()) { const QualType FILETy = ACtx.getFILEType().getCanonicalType(); const QualType Ty = D->getType().getCanonicalType(); @@ -622,12 +621,14 @@ void GenericTaintChecker::initTaintRules(CheckerContext &C) const { {{{"getlogin_r"}}, TR::Source({{0}})}, // Props + {{{"accept"}}, TR::Prop({{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}})}, + {{{"fgetws"}}, TR::Prop({{2}}, {{0, ReturnValueIndex}})}, {{{"fscanf"}}, TR::Prop({{0}}, {{}, 2})}, {{{"fscanf_s"}}, TR::Prop({{0}}, {{}, {2}})}, {{{"sscanf"}}, TR::Prop({{0}}, {{}, 2})}, @@ -694,8 +695,10 @@ void GenericTaintChecker::initTaintRules(CheckerContext &C) const { {{{"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}})}, + + // strlen, wcslen, strnlen and alike intentionally don't propagate taint. + // See the details here: https://github.com/llvm/llvm-project/pull/66086 + {{{"strtol"}}, TR::Prop({{0}}, {{1, ReturnValueIndex}})}, {{{"strtoll"}}, TR::Prop({{0}}, {{1, ReturnValueIndex}})}, {{{"strtoul"}}, TR::Prop({{0}}, {{1, ReturnValueIndex}})}, @@ -731,6 +734,8 @@ void GenericTaintChecker::initTaintRules(CheckerContext &C) const { TR::Prop({{1}}, {{0, ReturnValueIndex}})}, {{CDF_MaybeBuiltin, {{"strcat"}}}, TR::Prop({{1}}, {{0, ReturnValueIndex}})}, + {{CDF_MaybeBuiltin, {{"wcsncat"}}}, + TR::Prop({{1}}, {{0, ReturnValueIndex}})}, {{CDF_MaybeBuiltin, {{"strdup"}}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, {{CDF_MaybeBuiltin, {{"strdupa"}}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, @@ -739,12 +744,14 @@ void GenericTaintChecker::initTaintRules(CheckerContext &C) const { // 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)}, + {{{"execl"}}, TR::Sink({{}, {0}}, MsgSanitizeSystemArgs)}, + {{{"execle"}}, TR::Sink({{}, {0}}, MsgSanitizeSystemArgs)}, + {{{"execlp"}}, TR::Sink({{}, {0}}, MsgSanitizeSystemArgs)}, + {{{"execv"}}, TR::Sink({{0, 1}}, MsgSanitizeSystemArgs)}, + {{{"execve"}}, TR::Sink({{0, 1, 2}}, MsgSanitizeSystemArgs)}, + {{{"fexecve"}}, TR::Sink({{0, 1, 2}}, MsgSanitizeSystemArgs)}, + {{{"execvp"}}, TR::Sink({{0, 1}}, MsgSanitizeSystemArgs)}, + {{{"execvpe"}}, TR::Sink({{0, 1, 2}}, MsgSanitizeSystemArgs)}, {{{"dlopen"}}, TR::Sink({{0}}, MsgSanitizeSystemArgs)}, {{CDF_MaybeBuiltin, {{"malloc"}}}, TR::Sink({{0}}, MsgTaintedBufferSize)}, {{CDF_MaybeBuiltin, {{"calloc"}}}, TR::Sink({{0}}, MsgTaintedBufferSize)}, diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IteratorRangeChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IteratorRangeChecker.cpp index c682449921ac..7740c3d4da1e 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IteratorRangeChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IteratorRangeChecker.cpp @@ -228,7 +228,7 @@ void IteratorRangeChecker::verifyRandomIncrOrDecr(CheckerContext &C, Value = State->getRawSVal(*ValAsLoc); } - if (Value.isUnknownOrUndef()) + if (Value.isUnknownOrUndef() || !isa<NonLoc>(Value)) return; // Incremention or decremention by 0 is never a bug. diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp index 3496af731aa6..f0276a57bdf9 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp @@ -64,12 +64,12 @@ class IvarInvalidationCheckerImpl { struct InvalidationInfo { /// Has the ivar been invalidated? - bool IsInvalidated; + bool IsInvalidated = false; /// The methods which can be used to invalidate the ivar. MethodSet InvalidationMethods; - InvalidationInfo() : IsInvalidated(false) {} + InvalidationInfo() = default; void addInvalidationMethod(const ObjCMethodDecl *MD) { InvalidationMethods.insert(MD); } diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp index b77e9bf09a33..70f911fc66ab 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp @@ -817,9 +817,9 @@ void NonLocalizedStringChecker::checkPreObjCMessage(const ObjCMethodCall &msg, // Handle the case where the receiver is an NSString // These special NSString methods draw to the screen - if (!(SelectorName.startswith("drawAtPoint") || - SelectorName.startswith("drawInRect") || - SelectorName.startswith("drawWithRect"))) + if (!(SelectorName.starts_with("drawAtPoint") || + SelectorName.starts_with("drawInRect") || + SelectorName.starts_with("drawWithRect"))) return; SVal svTitle = msg.getReceiverSVal(); diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp index d2b564d022b5..79ab05f2c786 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -1141,22 +1141,28 @@ MallocChecker::performKernelMalloc(const CallEvent &Call, CheckerContext &C, llvm::Triple::OSType OS = Ctx.getTargetInfo().getTriple().getOS(); if (!KernelZeroFlagVal) { - if (OS == llvm::Triple::FreeBSD) + switch (OS) { + case llvm::Triple::FreeBSD: KernelZeroFlagVal = 0x0100; - else if (OS == llvm::Triple::NetBSD) + break; + case llvm::Triple::NetBSD: KernelZeroFlagVal = 0x0002; - else if (OS == llvm::Triple::OpenBSD) + break; + case llvm::Triple::OpenBSD: KernelZeroFlagVal = 0x0008; - else if (OS == llvm::Triple::Linux) + break; + case llvm::Triple::Linux: // __GFP_ZERO KernelZeroFlagVal = 0x8000; - else + break; + default: // FIXME: We need a more general way of getting the M_ZERO value. // See also: O_CREAT in UnixAPIChecker.cpp. // Fall back to normal malloc behavior on platforms where we don't // know M_ZERO. return std::nullopt; + } } // We treat the last argument as the flags argument, and callers fall-back to @@ -1722,13 +1728,15 @@ ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C, return nullptr; // Bind the return value to the symbolic value from the heap region. - // TODO: We could rewrite post visit to eval call; 'malloc' does not have - // side effects other than what we model here. + // TODO: move use of this functions to an EvalCall callback, becasue + // BindExpr() should'nt be used elsewhere. unsigned Count = C.blockCount(); - SValBuilder &svalBuilder = C.getSValBuilder(); + SValBuilder &SVB = C.getSValBuilder(); const LocationContext *LCtx = C.getPredecessor()->getLocationContext(); - DefinedSVal RetVal = svalBuilder.getConjuredHeapSymbolVal(CE, LCtx, Count) - .castAs<DefinedSVal>(); + DefinedSVal RetVal = + ((Family == AF_Alloca) ? SVB.getAllocaRegionVal(CE, LCtx, Count) + : SVB.getConjuredHeapSymbolVal(CE, LCtx, Count) + .castAs<DefinedSVal>()); State = State->BindExpr(CE, C.getLocationContext(), RetVal); // Fill the region with the initialization value. @@ -1740,7 +1748,7 @@ ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C, // Set the region's extent. State = setDynamicExtent(State, RetVal.getAsRegion(), - Size.castAs<DefinedOrUnknownSVal>(), svalBuilder); + Size.castAs<DefinedOrUnknownSVal>(), SVB); return MallocUpdateRefState(C, CE, State, Family); } @@ -1955,13 +1963,10 @@ ProgramStateRef MallocChecker::FreeMemAux( // Parameters, locals, statics, globals, and memory returned by // __builtin_alloca() shouldn't be freed. if (!isa<UnknownSpaceRegion, HeapSpaceRegion>(MS)) { - // FIXME: at the time this code was written, malloc() regions were - // represented by conjured symbols, which are all in UnknownSpaceRegion. - // This means that there isn't actually anything from HeapSpaceRegion - // that should be freed, even though we allow it here. - // Of course, free() can work on memory allocated outside the current - // function, so UnknownSpaceRegion is always a possibility. - // False negatives are better than false positives. + // Regions returned by malloc() are represented by SymbolicRegion objects + // within HeapSpaceRegion. Of course, free() can work on memory allocated + // outside the current function, so UnknownSpaceRegion is also a + // possibility here. if (isa<AllocaRegion>(R)) HandleFreeAlloca(C, ArgVal, ArgExpr->getSourceRange()); @@ -3145,16 +3150,16 @@ bool MallocChecker::mayFreeAnyEscapedMemoryOrIsModeledExplicitly( // transferred. Again, though, we can't be sure that the object will use // free() to deallocate the memory, so we can't model it explicitly. StringRef FirstSlot = Msg->getSelector().getNameForSlot(0); - if (FirstSlot.endswith("NoCopy")) + if (FirstSlot.ends_with("NoCopy")) return true; // If the first selector starts with addPointer, insertPointer, // or replacePointer, assume we are dealing with NSPointerArray or similar. // This is similar to C++ containers (vector); we still might want to check // that the pointers get freed by following the container itself. - if (FirstSlot.startswith("addPointer") || - FirstSlot.startswith("insertPointer") || - FirstSlot.startswith("replacePointer") || + if (FirstSlot.starts_with("addPointer") || + FirstSlot.starts_with("insertPointer") || + FirstSlot.starts_with("replacePointer") || FirstSlot.equals("valueWithPointer")) { return true; } @@ -3194,7 +3199,7 @@ bool MallocChecker::mayFreeAnyEscapedMemoryOrIsModeledExplicitly( // White list the 'XXXNoCopy' CoreFoundation functions. // We specifically check these before - if (FName.endswith("NoCopy")) { + if (FName.ends_with("NoCopy")) { // Look for the deallocator argument. We know that the memory ownership // is not transferred only if the deallocator argument is // 'kCFAllocatorNull'. diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp index 0b3d635a50a3..8fc44e78be6f 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp @@ -48,8 +48,10 @@ void MmapWriteExecChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { if (matchesAny(Call, MmapFn, MprotectFn)) { SVal ProtVal = Call.getArgSVal(2); - auto ProtLoc = ProtVal.castAs<nonloc::ConcreteInt>(); - int64_t Prot = ProtLoc.getValue().getSExtValue(); + auto ProtLoc = ProtVal.getAs<nonloc::ConcreteInt>(); + if (!ProtLoc) + return; + int64_t Prot = ProtLoc->getValue().getSExtValue(); if (ProtExecOv != ProtExec) ProtExec = ProtExecOv; if (ProtReadOv != ProtRead) diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp index 59741dde1eea..54870bcb4bb2 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp @@ -39,10 +39,10 @@ static bool IsCFError(QualType T, IdentifierInfo *II); namespace { class NSErrorMethodChecker : public Checker< check::ASTDecl<ObjCMethodDecl> > { - mutable IdentifierInfo *II; + mutable IdentifierInfo *II = nullptr; public: - NSErrorMethodChecker() : II(nullptr) {} + NSErrorMethodChecker() = default; void checkASTDecl(const ObjCMethodDecl *D, AnalysisManager &mgr, BugReporter &BR) const; diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp index fd47e19cb786..44b69ef31911 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp @@ -303,7 +303,7 @@ std::unique_ptr<PathSensitiveBugReport> NonNullParamChecker::genReportReferenceToNullPointer( const ExplodedNode *ErrorNode, const Expr *ArgE) const { if (!BTNullRefArg) - BTNullRefArg.reset(new BuiltinBug(this, "Dereference of null pointer")); + BTNullRefArg.reset(new BugType(this, "Dereference of null pointer")); auto R = std::make_unique<PathSensitiveBugReport>( *BTNullRefArg, "Forming reference to null pointer", ErrorNode); diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp index 906f4e85a8e5..06f1ad00eaf2 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp @@ -890,7 +890,7 @@ void NullabilityChecker::checkPostCall(const CallEvent &Call, // of CG calls. const SourceManager &SM = C.getSourceManager(); StringRef FilePath = SM.getFilename(SM.getSpellingLoc(Decl->getBeginLoc())); - if (llvm::sys::path::filename(FilePath).startswith("CG")) { + if (llvm::sys::path::filename(FilePath).starts_with("CG")) { State = State->set<NullabilityMap>(Region, Nullability::Contradicted); C.addTransition(State); return; @@ -899,6 +899,14 @@ void NullabilityChecker::checkPostCall(const CallEvent &Call, const NullabilityState *TrackedNullability = State->get<NullabilityMap>(Region); + // ObjCMessageExpr gets the actual type through + // Sema::getMessageSendResultType, instead of using the return type of + // MethodDecl directly. The final type is generated by considering the + // nullability of receiver and MethodDecl together. Thus, The type of + // ObjCMessageExpr is prefer. + if (const Expr *E = Call.getOriginExpr()) + ReturnType = E->getType(); + if (!TrackedNullability && getNullabilityAnnotation(ReturnType) == Nullability::Nullable) { State = State->set<NullabilityMap>(Region, Nullability::Nullable); @@ -984,7 +992,7 @@ void NullabilityChecker::checkPostObjCMessage(const ObjCMethodCall &M, // In order to reduce the noise in the diagnostics generated by this checker, // some framework and programming style based heuristics are used. These // heuristics are for Cocoa APIs which have NS prefix. - if (Name.startswith("NS")) { + if (Name.starts_with("NS")) { // Developers rely on dynamic invariants such as an item should be available // in a collection, or a collection is not empty often. Those invariants can // not be inferred by any static analysis tool. To not to bother the users @@ -1053,7 +1061,7 @@ void NullabilityChecker::checkPostObjCMessage(const ObjCMethodCall &M, } // No tracked information. Use static type information for return value. - Nullability RetNullability = getNullabilityAnnotation(RetType); + Nullability RetNullability = getNullabilityAnnotation(Message->getType()); // Properties might be computed, which means the property value could // theoretically change between calls even in commonly-observed cases like diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp index a6383009e1fe..7906b787cd53 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp @@ -25,8 +25,8 @@ using namespace ento; namespace { class ObjCAtSyncChecker : public Checker< check::PreStmt<ObjCAtSynchronizedStmt> > { - mutable std::unique_ptr<BuiltinBug> BT_null; - mutable std::unique_ptr<BuiltinBug> BT_undef; + mutable std::unique_ptr<BugType> BT_null; + mutable std::unique_ptr<BugType> BT_undef; public: void checkPreStmt(const ObjCAtSynchronizedStmt *S, CheckerContext &C) const; @@ -44,8 +44,8 @@ void ObjCAtSyncChecker::checkPreStmt(const ObjCAtSynchronizedStmt *S, if (isa<UndefinedVal>(V)) { if (ExplodedNode *N = C.generateErrorNode()) { if (!BT_undef) - BT_undef.reset(new BuiltinBug(this, "Uninitialized value used as mutex " - "for @synchronized")); + BT_undef.reset(new BugType(this, "Uninitialized value used as mutex " + "for @synchronized")); auto report = std::make_unique<PathSensitiveBugReport>( *BT_undef, BT_undef->getDescription(), N); bugreporter::trackExpressionValue(N, Ex, *report); @@ -67,9 +67,9 @@ void ObjCAtSyncChecker::checkPreStmt(const ObjCAtSynchronizedStmt *S, // a null mutex just means no synchronization occurs. if (ExplodedNode *N = C.generateNonFatalErrorNode(nullState)) { if (!BT_null) - BT_null.reset(new BuiltinBug( - this, "Nil value used as mutex for @synchronized() " - "(no synchronization will occur)")); + BT_null.reset( + new BugType(this, "Nil value used as mutex for @synchronized() " + "(no synchronization will occur)")); auto report = std::make_unique<PathSensitiveBugReport>( *BT_null, BT_null->getDescription(), N); bugreporter::trackExpressionValue(N, Ex, *report); diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp index fbbc32a40e89..598b368e74d4 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp @@ -64,7 +64,7 @@ private: class ObjCSuperCallChecker : public Checker< check::ASTDecl<ObjCImplementationDecl> > { public: - ObjCSuperCallChecker() : IsInitialized(false) {} + ObjCSuperCallChecker() = default; void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager &Mgr, BugReporter &BR) const; @@ -75,7 +75,7 @@ private: void fillSelectors(ASTContext &Ctx, ArrayRef<SelectorDescriptor> Sel, StringRef ClassName) const; mutable llvm::StringMap<llvm::SmallPtrSet<Selector, 16>> SelectorsForClass; - mutable bool IsInitialized; + mutable bool IsInitialized = false; }; } diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCPropertyChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCPropertyChecker.cpp index 4636fd160511..08ad6877cbe6 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCPropertyChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCPropertyChecker.cpp @@ -50,7 +50,7 @@ void ObjCPropertyChecker::checkCopyMutable(const ObjCPropertyDecl *D, const std::string &PropTypeName(T->getPointeeType().getCanonicalType() .getUnqualifiedType() .getAsString()); - if (!StringRef(PropTypeName).startswith("NSMutable")) + if (!StringRef(PropTypeName).starts_with("NSMutable")) return; const ObjCImplDecl *ImplD = nullptr; diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp index 27364eb72523..1d63c0dd01f3 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp @@ -11,13 +11,14 @@ // //===----------------------------------------------------------------------===// -#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/ExprCXX.h" +#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/CheckerContext.h" +#include "llvm/ADT/StringRef.h" using namespace clang; using namespace ento; @@ -55,8 +56,8 @@ class PointerArithChecker bool PointedNeeded = false) const; void initAllocIdentifiers(ASTContext &C) const; - mutable std::unique_ptr<BuiltinBug> BT_pointerArith; - mutable std::unique_ptr<BuiltinBug> BT_polyArray; + mutable std::unique_ptr<BugType> BT_pointerArith; + mutable std::unique_ptr<BugType> BT_polyArray; mutable llvm::SmallSet<IdentifierInfo *, 8> AllocFunctions; public: @@ -168,12 +169,11 @@ void PointerArithChecker::reportPointerArithMisuse(const Expr *E, return; if (ExplodedNode *N = C.generateNonFatalErrorNode()) { if (!BT_polyArray) - BT_polyArray.reset(new BuiltinBug( - this, "Dangerous pointer arithmetic", - "Pointer arithmetic on a pointer to base class is dangerous " - "because derived and base class may have different size.")); - auto R = std::make_unique<PathSensitiveBugReport>( - *BT_polyArray, BT_polyArray->getDescription(), N); + BT_polyArray.reset(new BugType(this, "Dangerous pointer arithmetic")); + constexpr llvm::StringLiteral Msg = + "Pointer arithmetic on a pointer to base class is dangerous " + "because derived and base class may have different size."; + auto R = std::make_unique<PathSensitiveBugReport>(*BT_polyArray, Msg, N); R->addRange(E->getSourceRange()); R->markInteresting(ArrayRegion); C.emitReport(std::move(R)); @@ -191,12 +191,11 @@ void PointerArithChecker::reportPointerArithMisuse(const Expr *E, if (ExplodedNode *N = C.generateNonFatalErrorNode()) { if (!BT_pointerArith) - BT_pointerArith.reset(new BuiltinBug(this, "Dangerous pointer arithmetic", - "Pointer arithmetic on non-array " - "variables relies on memory layout, " - "which is dangerous.")); - auto R = std::make_unique<PathSensitiveBugReport>( - *BT_pointerArith, BT_pointerArith->getDescription(), N); + BT_pointerArith.reset(new BugType(this, "Dangerous pointer arithmetic")); + constexpr llvm::StringLiteral Msg = + "Pointer arithmetic on non-array variables relies on memory layout, " + "which is dangerous."; + auto R = std::make_unique<PathSensitiveBugReport>(*BT_pointerArith, Msg, N); R->addRange(SR); R->markInteresting(Region); C.emitReport(std::move(R)); diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp index 81c19d9a0940..96d38eef3c03 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PointerSubChecker.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 "llvm/ADT/StringRef.h" using namespace clang; using namespace ento; @@ -24,7 +25,7 @@ using namespace ento; namespace { class PointerSubChecker : public Checker< check::PreStmt<BinaryOperator> > { - mutable std::unique_ptr<BuiltinBug> BT; + mutable std::unique_ptr<BugType> BT; public: void checkPreStmt(const BinaryOperator *B, CheckerContext &C) const; @@ -59,12 +60,11 @@ void PointerSubChecker::checkPreStmt(const BinaryOperator *B, if (ExplodedNode *N = C.generateNonFatalErrorNode()) { if (!BT) - BT.reset( - new BuiltinBug(this, "Pointer subtraction", - "Subtraction of two pointers that do not point to " - "the same memory chunk may cause incorrect result.")); - auto R = - std::make_unique<PathSensitiveBugReport>(*BT, BT->getDescription(), N); + BT.reset(new BugType(this, "Pointer subtraction")); + constexpr llvm::StringLiteral Msg = + "Subtraction of two pointers that do not point to the same memory " + "chunk may cause incorrect result."; + auto R = std::make_unique<PathSensitiveBugReport>(*BT, Msg, N); R->addRange(B->getSourceRange()); C.emitReport(std::move(R)); } diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h index 223e28c2c5b8..d4d7c4c74c56 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h @@ -36,7 +36,6 @@ #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/ImmutableList.h" -#include "llvm/ADT/ImmutableMap.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp index 379163e12787..c3acb73ba717 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp @@ -786,9 +786,6 @@ RefLeakReportVisitor::getEndPath(BugReporterContext &BRC, assert(RV); if (RV->getKind() == RefVal::ErrorLeakReturned) { - // FIXME: Per comments in rdar://6320065, "create" only applies to CF - // objects. Only "copy", "alloc", "retain" and "new" transfer ownership - // to the caller for NS objects. const Decl *D = &EndN->getCodeDecl(); os << (isa<ObjCMethodDecl>(D) ? " is returned from a method " diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp index b85d0adb8eaf..11dca1ff8831 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp @@ -26,7 +26,7 @@ using namespace ento; namespace { class ReturnPointerRangeChecker : public Checker< check::PreStmt<ReturnStmt> > { - mutable std::unique_ptr<BuiltinBug> BT; + mutable std::unique_ptr<BugType> BT; public: void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const; @@ -79,14 +79,13 @@ void ReturnPointerRangeChecker::checkPreStmt(const ReturnStmt *RS, // FIXME: This bug correspond to CWE-466. Eventually we should have bug // types explicitly reference such exploit categories (when applicable). if (!BT) - BT.reset(new BuiltinBug( - this, "Buffer overflow", - "Returned pointer value points outside the original object " - "(potential buffer overflow)")); + BT.reset(new BugType(this, "Buffer overflow")); + constexpr llvm::StringLiteral Msg = + "Returned pointer value points outside the original object " + "(potential buffer overflow)"; // Generate a report for this bug. - auto Report = - std::make_unique<PathSensitiveBugReport>(*BT, BT->getDescription(), N); + auto Report = std::make_unique<PathSensitiveBugReport>(*BT, Msg, N); Report->addRange(RetE->getSourceRange()); const auto ConcreteElementCount = ElementCount.getAs<nonloc::ConcreteInt>(); diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp index 5266cbf86b44..78cd0100bea4 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp @@ -24,8 +24,8 @@ using namespace ento; namespace { class ReturnUndefChecker : public Checker< check::PreStmt<ReturnStmt> > { - mutable std::unique_ptr<BuiltinBug> BT_Undef; - mutable std::unique_ptr<BuiltinBug> BT_NullReference; + mutable std::unique_ptr<BugType> BT_Undef; + mutable std::unique_ptr<BugType> BT_NullReference; void emitUndef(CheckerContext &C, const Expr *RetE) const; void checkReference(CheckerContext &C, const Expr *RetE, @@ -77,14 +77,13 @@ void ReturnUndefChecker::checkPreStmt(const ReturnStmt *RS, } } -static void emitBug(CheckerContext &C, BuiltinBug &BT, const Expr *RetE, - const Expr *TrackingE = nullptr) { +static void emitBug(CheckerContext &C, BugType &BT, StringRef Msg, + const Expr *RetE, const Expr *TrackingE = nullptr) { ExplodedNode *N = C.generateErrorNode(); if (!N) return; - auto Report = - std::make_unique<PathSensitiveBugReport>(BT, BT.getDescription(), N); + auto Report = std::make_unique<PathSensitiveBugReport>(BT, Msg, N); Report->addRange(RetE->getSourceRange()); bugreporter::trackExpressionValue(N, TrackingE ? TrackingE : RetE, *Report); @@ -94,10 +93,8 @@ static void emitBug(CheckerContext &C, BuiltinBug &BT, const Expr *RetE, void ReturnUndefChecker::emitUndef(CheckerContext &C, const Expr *RetE) const { if (!BT_Undef) - BT_Undef.reset( - new BuiltinBug(this, "Garbage return value", - "Undefined or garbage value returned to caller")); - emitBug(C, *BT_Undef, RetE); + BT_Undef.reset(new BugType(this, "Garbage return value")); + emitBug(C, *BT_Undef, "Undefined or garbage value returned to caller", RetE); } void ReturnUndefChecker::checkReference(CheckerContext &C, const Expr *RetE, @@ -113,9 +110,10 @@ void ReturnUndefChecker::checkReference(CheckerContext &C, const Expr *RetE, // The return value is known to be null. Emit a bug report. if (!BT_NullReference) - BT_NullReference.reset(new BuiltinBug(this, "Returning null reference")); + BT_NullReference.reset(new BugType(this, "Returning null reference")); - emitBug(C, *BT_NullReference, RetE, bugreporter::getDerefExpr(RetE)); + emitBug(C, *BT_NullReference, BT_NullReference->getDescription(), RetE, + bugreporter::getDerefExpr(RetE)); } void ento::registerReturnUndefChecker(CheckerManager &mgr) { diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp index 32d95e944195..2ac9f65c9793 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp +++ b/contrib/llvm-project/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"}, 2), CloseFn({"fclose"}, 1) { // Initialize the bug types. DoubleCloseBugType.reset( new BugType(this, "Double fclose", "Unix Stream API Error")); diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp index a20d24db158f..268fc742f050 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp @@ -32,7 +32,6 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" #include "llvm/ADT/STLExtras.h" -#include "llvm/ADT/StringMap.h" #include "llvm/Support/ErrorHandling.h" #include <optional> #include <string> @@ -202,7 +201,7 @@ static QualType getInnerPointerType(CheckerContext C, const CXXRecordDecl *RD) { static QualType getPointerTypeFromTemplateArg(const CallEvent &Call, CheckerContext &C) { const auto *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl()); - if (!FD || !FD->isFunctionTemplateSpecialization()) + if (!FD || !FD->getPrimaryTemplate()) return {}; const auto &TemplateArgs = FD->getTemplateSpecializationArgs()->asArray(); if (TemplateArgs.size() == 0) diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp index abf9914f2ca4..ea09c43cc5ce 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp @@ -30,10 +30,10 @@ class StackAddrEscapeChecker : public Checker<check::PreCall, check::PreStmt<ReturnStmt>, check::EndFunction> { mutable IdentifierInfo *dispatch_semaphore_tII = nullptr; - mutable std::unique_ptr<BuiltinBug> BT_stackleak; - mutable std::unique_ptr<BuiltinBug> BT_returnstack; - mutable std::unique_ptr<BuiltinBug> BT_capturedstackasync; - mutable std::unique_ptr<BuiltinBug> BT_capturedstackret; + mutable std::unique_ptr<BugType> BT_stackleak; + mutable std::unique_ptr<BugType> BT_returnstack; + mutable std::unique_ptr<BugType> BT_capturedstackasync; + mutable std::unique_ptr<BugType> BT_capturedstackret; public: enum CheckKind { @@ -154,7 +154,7 @@ void StackAddrEscapeChecker::EmitStackError(CheckerContext &C, if (!N) return; if (!BT_returnstack) - BT_returnstack = std::make_unique<BuiltinBug>( + BT_returnstack = std::make_unique<BugType>( CheckNames[CK_StackAddrEscapeChecker], "Return of address to stack-allocated memory"); // Generate a report for this bug. @@ -194,7 +194,7 @@ void StackAddrEscapeChecker::checkAsyncExecutedBlockCaptures( if (!N) continue; if (!BT_capturedstackasync) - BT_capturedstackasync = std::make_unique<BuiltinBug>( + BT_capturedstackasync = std::make_unique<BugType>( CheckNames[CK_StackAddrAsyncEscapeChecker], "Address of stack-allocated memory is captured"); SmallString<128> Buf; @@ -218,7 +218,7 @@ void StackAddrEscapeChecker::checkReturnedBlockCaptures( if (!N) continue; if (!BT_capturedstackret) - BT_capturedstackret = std::make_unique<BuiltinBug>( + BT_capturedstackret = std::make_unique<BugType>( CheckNames[CK_StackAddrEscapeChecker], "Address of stack-allocated memory is captured"); SmallString<128> Buf; @@ -364,15 +364,12 @@ void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS, return; if (!BT_stackleak) - BT_stackleak = std::make_unique<BuiltinBug>( - CheckNames[CK_StackAddrEscapeChecker], - "Stack address stored into global variable", - "Stack address was saved into a global variable. " - "This is dangerous because the address will become " - "invalid after returning from the function"); + BT_stackleak = + std::make_unique<BugType>(CheckNames[CK_StackAddrEscapeChecker], + "Stack address stored into global variable"); for (const auto &P : Cb.V) { - const MemRegion *Referrer = P.first; + const MemRegion *Referrer = P.first->getBaseRegion(); const MemRegion *Referred = P.second; // Generate a report for this bug. @@ -387,6 +384,8 @@ void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS, << CommonSuffix; auto Report = std::make_unique<PathSensitiveBugReport>(*BT_stackleak, Out.str(), N); + if (Range.isValid()) + Report->addRange(Range); Ctx.emitReport(std::move(Report)); return; } @@ -400,8 +399,14 @@ void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS, return "stack"; }(Referrer->getMemorySpace()); - // This cast supposed to succeed. - const VarRegion *ReferrerVar = cast<VarRegion>(Referrer->getBaseRegion()); + // We should really only have VarRegions here. + // Anything else is really surprising, and we should get notified if such + // ever happens. + const auto *ReferrerVar = dyn_cast<VarRegion>(Referrer); + if (!ReferrerVar) { + assert(false && "We should have a VarRegion here"); + continue; // Defensively skip this one. + } const std::string ReferrerVarName = ReferrerVar->getDecl()->getDeclName().getAsString(); diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp index d18e6f63df44..fffcaf7ed18f 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp @@ -533,13 +533,11 @@ class StdLibraryFunctionsChecker virtual ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, const Summary &Summary, CheckerContext &C) const = 0; - /// 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; - } + /// Get a description about what happens with 'errno' here and how it causes + /// a later bug report created by ErrnoChecker. + /// Empty return value means that 'errno' related bug may not happen from + /// the current analyzed function. + virtual const std::string describe(CheckerContext &C) const { return ""; } virtual ~ErrnoConstraintBase() {} @@ -596,7 +594,8 @@ class StdLibraryFunctionsChecker }; /// Set errno constraint at success cases of standard functions. - /// Success case: 'errno' is not allowed to be used. + /// Success case: 'errno' is not allowed to be used because the value is + /// undefined after successful call. /// \c ErrnoChecker can emit bug report after such a function call if errno /// is used. class SuccessErrnoConstraint : public ErrnoConstraintBase { @@ -607,12 +606,15 @@ class StdLibraryFunctionsChecker return errno_modeling::setErrnoForStdSuccess(State, C); } - const NoteTag *describe(CheckerContext &C, - StringRef FunctionName) const override { - return errno_modeling::getNoteTagForStdSuccess(C, FunctionName); + const std::string describe(CheckerContext &C) const override { + return "'errno' becomes undefined after the call"; } }; + /// Set errno constraint at functions that indicate failure only with 'errno'. + /// In this case 'errno' is required to be observed. + /// \c ErrnoChecker can emit bug report after such a function call if errno + /// is overwritten without a read before. class ErrnoMustBeCheckedConstraint : public ErrnoConstraintBase { public: ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, @@ -622,9 +624,8 @@ class StdLibraryFunctionsChecker Call.getOriginExpr()); } - const NoteTag *describe(CheckerContext &C, - StringRef FunctionName) const override { - return errno_modeling::getNoteTagForStdMustBeChecked(C, FunctionName); + const std::string describe(CheckerContext &C) const override { + return "reading 'errno' is required to find out if the call has failed"; } }; @@ -1387,22 +1388,33 @@ void StdLibraryFunctionsChecker::checkPostCall(const CallEvent &Call, if (!NewState) continue; - // It is possible that NewState == State is true. - // It can occur if another checker has applied the state before us. + // Here it's possible that NewState == State, e.g. when other checkers + // already applied the same constraints (or stricter ones). // Still add these note tags, the other checker should add only its // specialized note tags. These general note tags are handled always by // StdLibraryFunctionsChecker. + ExplodedNode *Pred = Node; - if (!Case.getNote().empty()) { - const SVal RV = Call.getReturnValue(); - // If there is a description for this execution branch (summary case), - // use it as a note tag. - std::string Note = - llvm::formatv(Case.getNote().str().c_str(), - cast<NamedDecl>(Call.getDecl())->getDeclName()); - if (Summary.getInvalidationKd() == EvalCallAsPure) { + DeclarationName FunctionName = + cast<NamedDecl>(Call.getDecl())->getDeclName(); + + std::string ErrnoNote = Case.getErrnoConstraint().describe(C); + std::string CaseNote; + if (Case.getNote().empty()) { + if (!ErrnoNote.empty()) + ErrnoNote = + llvm::formatv("After calling '{0}' {1}", FunctionName, ErrnoNote); + } else { + CaseNote = llvm::formatv(Case.getNote().str().c_str(), FunctionName); + } + const SVal RV = Call.getReturnValue(); + + if (Summary.getInvalidationKd() == EvalCallAsPure) { + // Do not expect that errno is interesting (the "pure" functions do not + // affect it). + if (!CaseNote.empty()) { const NoteTag *Tag = C.getNoteTag( - [Node, Note, RV](PathSensitiveBugReport &BR) -> std::string { + [Node, CaseNote, RV](PathSensitiveBugReport &BR) -> std::string { // Try to omit the note if we know in advance which branch is // taken (this means, only one branch exists). // This check is performed inside the lambda, after other @@ -1414,32 +1426,42 @@ void StdLibraryFunctionsChecker::checkPostCall(const CallEvent &Call, // the successors). This is why this check is only used in the // EvalCallAsPure case. if (BR.isInteresting(RV) && Node->succ_size() > 1) - return Note; + return CaseNote; return ""; }); Pred = C.addTransition(NewState, Pred, Tag); - } else { + } + } else { + if (!CaseNote.empty() || !ErrnoNote.empty()) { const NoteTag *Tag = - C.getNoteTag([Note, RV](PathSensitiveBugReport &BR) -> std::string { - if (BR.isInteresting(RV)) - return Note; + C.getNoteTag([CaseNote, ErrnoNote, + RV](PathSensitiveBugReport &BR) -> std::string { + // If 'errno' is interesting, show the user a note about the case + // (what happened at the function call) and about how 'errno' + // causes the problem. ErrnoChecker sets the errno (but not RV) to + // interesting. + // If only the return value is interesting, show only the case + // note. + std::optional<Loc> ErrnoLoc = + errno_modeling::getErrnoLoc(BR.getErrorNode()->getState()); + bool ErrnoImportant = !ErrnoNote.empty() && ErrnoLoc && + BR.isInteresting(ErrnoLoc->getAsRegion()); + if (ErrnoImportant) { + BR.markNotInteresting(ErrnoLoc->getAsRegion()); + if (CaseNote.empty()) + return ErrnoNote; + return llvm::formatv("{0}; {1}", CaseNote, ErrnoNote); + } else { + if (BR.isInteresting(RV)) + return CaseNote; + } return ""; }); Pred = C.addTransition(NewState, Pred, Tag); } - if (!Pred) - continue; } - // If we can get a note tag for the errno change, add this additionally to - // the previous. This note is only about value of 'errno' and is displayed - // if 'errno' is interesting. - if (const auto *D = dyn_cast<FunctionDecl>(Call.getDecl())) - if (const NoteTag *NT = - Case.getErrnoConstraint().describe(C, D->getNameAsString())) - Pred = C.addTransition(NewState, Pred, NT); - - // Add the transition if no note tag could be added. + // Add the transition if no note tag was added. if (Pred == Node && NewState != State) C.addTransition(NewState); } @@ -2033,7 +2055,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( ErrnoMustNotBeChecked, GenericSuccessMsg) .Case({ArgumentCondition(1U, WithinRange, SingleValue(0)), ReturnValueCondition(WithinRange, SingleValue(0))}, - ErrnoMustNotBeChecked, GenericSuccessMsg) + ErrnoMustNotBeChecked, + "Assuming that argument 'size' to '{0}' is 0") .ArgConstraint(NotNullBuffer(ArgNo(0), ArgNo(1), ArgNo(2))) .ArgConstraint(NotNull(ArgNo(3))) .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(0), /*BufSize=*/ArgNo(1), @@ -2860,9 +2883,14 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( Signature(ArgTypes{ConstCharPtrRestrictTy, CharPtrRestrictTy, SizeTy}, RetType{Ssize_tTy}), Summary(NoEvalCall) - .Case({ReturnValueCondition(LessThanOrEq, ArgNo(2)), - ReturnValueCondition(WithinRange, Range(0, Ssize_tMax))}, + .Case({ArgumentCondition(2, WithinRange, Range(1, IntMax)), + ReturnValueCondition(LessThanOrEq, ArgNo(2)), + ReturnValueCondition(WithinRange, Range(1, Ssize_tMax))}, ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case({ArgumentCondition(2, WithinRange, SingleValue(0)), + ReturnValueCondition(WithinRange, SingleValue(0))}, + ErrnoMustNotBeChecked, + "Assuming that argument 'bufsize' is 0") .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) .ArgConstraint(NotNull(ArgNo(0))) .ArgConstraint(NotNull(ArgNo(1))) @@ -2879,9 +2907,14 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( ArgTypes{IntTy, ConstCharPtrRestrictTy, CharPtrRestrictTy, SizeTy}, RetType{Ssize_tTy}), Summary(NoEvalCall) - .Case({ReturnValueCondition(LessThanOrEq, ArgNo(3)), - ReturnValueCondition(WithinRange, Range(0, Ssize_tMax))}, + .Case({ArgumentCondition(3, WithinRange, Range(1, IntMax)), + ReturnValueCondition(LessThanOrEq, ArgNo(3)), + ReturnValueCondition(WithinRange, Range(1, Ssize_tMax))}, ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case({ArgumentCondition(3, WithinRange, SingleValue(0)), + ReturnValueCondition(WithinRange, SingleValue(0))}, + ErrnoMustNotBeChecked, + "Assuming that argument 'bufsize' is 0") .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) .ArgConstraint(ValidFileDescriptorOrAtFdcwd(ArgNo(0))) .ArgConstraint(NotNull(ArgNo(1))) @@ -3096,7 +3129,10 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( auto Recvfrom = Summary(NoEvalCall) .Case({ReturnValueCondition(LessThanOrEq, ArgNo(2)), - ReturnValueCondition(WithinRange, Range(0, Ssize_tMax))}, + ReturnValueCondition(WithinRange, Range(1, Ssize_tMax))}, + ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case({ReturnValueCondition(WithinRange, SingleValue(0)), + ArgumentCondition(2, WithinRange, SingleValue(0))}, ErrnoMustNotBeChecked, GenericSuccessMsg) .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax))) @@ -3123,7 +3159,10 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( auto Sendto = Summary(NoEvalCall) .Case({ReturnValueCondition(LessThanOrEq, ArgNo(2)), - ReturnValueCondition(WithinRange, Range(0, Ssize_tMax))}, + ReturnValueCondition(WithinRange, Range(1, Ssize_tMax))}, + ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case({ReturnValueCondition(WithinRange, SingleValue(0)), + ArgumentCondition(2, WithinRange, SingleValue(0))}, ErrnoMustNotBeChecked, GenericSuccessMsg) .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax))) @@ -3161,7 +3200,10 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( RetType{Ssize_tTy}), Summary(NoEvalCall) .Case({ReturnValueCondition(LessThanOrEq, ArgNo(2)), - ReturnValueCondition(WithinRange, Range(0, Ssize_tMax))}, + ReturnValueCondition(WithinRange, Range(1, Ssize_tMax))}, + ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case({ReturnValueCondition(WithinRange, SingleValue(0)), + ArgumentCondition(2, WithinRange, SingleValue(0))}, ErrnoMustNotBeChecked, GenericSuccessMsg) .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax))) @@ -3179,7 +3221,7 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( Signature(ArgTypes{IntTy, StructMsghdrPtrTy, IntTy}, RetType{Ssize_tTy}), Summary(NoEvalCall) - .Case({ReturnValueCondition(WithinRange, Range(0, Ssize_tMax))}, + .Case({ReturnValueCondition(WithinRange, Range(1, Ssize_tMax))}, ErrnoMustNotBeChecked, GenericSuccessMsg) .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) .ArgConstraint( @@ -3191,7 +3233,7 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( Signature(ArgTypes{IntTy, ConstStructMsghdrPtrTy, IntTy}, RetType{Ssize_tTy}), Summary(NoEvalCall) - .Case({ReturnValueCondition(WithinRange, Range(0, Ssize_tMax))}, + .Case({ReturnValueCondition(WithinRange, Range(1, Ssize_tMax))}, ErrnoMustNotBeChecked, GenericSuccessMsg) .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) .ArgConstraint( @@ -3233,7 +3275,10 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( RetType{Ssize_tTy}), Summary(NoEvalCall) .Case({ReturnValueCondition(LessThanOrEq, ArgNo(2)), - ReturnValueCondition(WithinRange, Range(0, Ssize_tMax))}, + ReturnValueCondition(WithinRange, Range(1, Ssize_tMax))}, + ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case({ReturnValueCondition(WithinRange, SingleValue(0)), + ArgumentCondition(2, WithinRange, SingleValue(0))}, ErrnoMustNotBeChecked, GenericSuccessMsg) .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax))) diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StdVariantChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StdVariantChecker.cpp new file mode 100644 index 000000000000..f7b7befe28ee --- /dev/null +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StdVariantChecker.cpp @@ -0,0 +1,298 @@ +//===- StdVariantChecker.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 +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/Type.h" +#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/CallDescription.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "llvm/ADT/FoldingSet.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Casting.h" +#include <optional> +#include <string_view> + +#include "TaggedUnionModeling.h" + +using namespace clang; +using namespace ento; +using namespace tagged_union_modeling; + +REGISTER_MAP_WITH_PROGRAMSTATE(VariantHeldTypeMap, const MemRegion *, QualType) + +namespace clang::ento::tagged_union_modeling { + +const CXXConstructorDecl * +getConstructorDeclarationForCall(const CallEvent &Call) { + const auto *ConstructorCall = dyn_cast<CXXConstructorCall>(&Call); + if (!ConstructorCall) + return nullptr; + + return ConstructorCall->getDecl(); +} + +bool isCopyConstructorCall(const CallEvent &Call) { + if (const CXXConstructorDecl *ConstructorDecl = + getConstructorDeclarationForCall(Call)) + return ConstructorDecl->isCopyConstructor(); + return false; +} + +bool isCopyAssignmentCall(const CallEvent &Call) { + const Decl *CopyAssignmentDecl = Call.getDecl(); + + if (const auto *AsMethodDecl = + dyn_cast_or_null<CXXMethodDecl>(CopyAssignmentDecl)) + return AsMethodDecl->isCopyAssignmentOperator(); + return false; +} + +bool isMoveConstructorCall(const CallEvent &Call) { + const CXXConstructorDecl *ConstructorDecl = + getConstructorDeclarationForCall(Call); + if (!ConstructorDecl) + return false; + + return ConstructorDecl->isMoveConstructor(); +} + +bool isMoveAssignmentCall(const CallEvent &Call) { + const Decl *CopyAssignmentDecl = Call.getDecl(); + + const auto *AsMethodDecl = + dyn_cast_or_null<CXXMethodDecl>(CopyAssignmentDecl); + if (!AsMethodDecl) + return false; + + return AsMethodDecl->isMoveAssignmentOperator(); +} + +bool isStdType(const Type *Type, llvm::StringRef TypeName) { + auto *Decl = Type->getAsRecordDecl(); + if (!Decl) + return false; + return (Decl->getName() == TypeName) && Decl->isInStdNamespace(); +} + +bool isStdVariant(const Type *Type) { + return isStdType(Type, llvm::StringLiteral("variant")); +} + +} // end of namespace clang::ento::tagged_union_modeling + +static std::optional<ArrayRef<TemplateArgument>> +getTemplateArgsFromVariant(const Type *VariantType) { + const auto *TempSpecType = VariantType->getAs<TemplateSpecializationType>(); + if (!TempSpecType) + return {}; + + return TempSpecType->template_arguments(); +} + +static std::optional<QualType> +getNthTemplateTypeArgFromVariant(const Type *varType, unsigned i) { + std::optional<ArrayRef<TemplateArgument>> VariantTemplates = + getTemplateArgsFromVariant(varType); + if (!VariantTemplates) + return {}; + + return (*VariantTemplates)[i].getAsType(); +} + +static bool isVowel(char a) { + switch (a) { + case 'a': + case 'e': + case 'i': + case 'o': + case 'u': + return true; + default: + return false; + } +} + +static llvm::StringRef indefiniteArticleBasedOnVowel(char a) { + if (isVowel(a)) + return "an"; + return "a"; +} + +class StdVariantChecker : public Checker<eval::Call, check::RegionChanges> { + // Call descriptors to find relevant calls + CallDescription VariantConstructor{{"std", "variant", "variant"}}; + CallDescription VariantAssignmentOperator{{"std", "variant", "operator="}}; + CallDescription StdGet{{"std", "get"}, 1, 1}; + + BugType BadVariantType{this, "BadVariantType", "BadVariantType"}; + +public: + ProgramStateRef checkRegionChanges(ProgramStateRef State, + const InvalidatedSymbols *, + ArrayRef<const MemRegion *>, + ArrayRef<const MemRegion *> Regions, + const LocationContext *, + const CallEvent *Call) const { + if (!Call) + return State; + + return removeInformationStoredForDeadInstances<VariantHeldTypeMap>( + *Call, State, Regions); + } + + bool evalCall(const CallEvent &Call, CheckerContext &C) const { + // Check if the call was not made from a system header. If it was then + // we do an early return because it is part of the implementation. + if (Call.isCalledFromSystemHeader()) + return false; + + if (StdGet.matches(Call)) + return handleStdGetCall(Call, C); + + // First check if a constructor call is happening. If it is a + // constructor call, check if it is an std::variant constructor call. + bool IsVariantConstructor = + isa<CXXConstructorCall>(Call) && VariantConstructor.matches(Call); + bool IsVariantAssignmentOperatorCall = + isa<CXXMemberOperatorCall>(Call) && + VariantAssignmentOperator.matches(Call); + + if (IsVariantConstructor || IsVariantAssignmentOperatorCall) { + if (Call.getNumArgs() == 0 && IsVariantConstructor) { + handleDefaultConstructor(cast<CXXConstructorCall>(&Call), C); + return true; + } + + // FIXME Later this checker should be extended to handle constructors + // with multiple arguments. + if (Call.getNumArgs() != 1) + return false; + + SVal ThisSVal; + if (IsVariantConstructor) { + const auto &AsConstructorCall = cast<CXXConstructorCall>(Call); + ThisSVal = AsConstructorCall.getCXXThisVal(); + } else if (IsVariantAssignmentOperatorCall) { + const auto &AsMemberOpCall = cast<CXXMemberOperatorCall>(Call); + ThisSVal = AsMemberOpCall.getCXXThisVal(); + } else { + return false; + } + + handleConstructorAndAssignment<VariantHeldTypeMap>(Call, C, ThisSVal); + return true; + } + return false; + } + +private: + // The default constructed std::variant must be handled separately + // by default the std::variant is going to hold a default constructed instance + // of the first type of the possible types + void handleDefaultConstructor(const CXXConstructorCall *ConstructorCall, + CheckerContext &C) const { + SVal ThisSVal = ConstructorCall->getCXXThisVal(); + + const auto *const ThisMemRegion = ThisSVal.getAsRegion(); + if (!ThisMemRegion) + return; + + std::optional<QualType> DefaultType = getNthTemplateTypeArgFromVariant( + ThisSVal.getType(C.getASTContext())->getPointeeType().getTypePtr(), 0); + if (!DefaultType) + return; + + ProgramStateRef State = ConstructorCall->getState(); + State = State->set<VariantHeldTypeMap>(ThisMemRegion, *DefaultType); + C.addTransition(State); + } + + bool handleStdGetCall(const CallEvent &Call, CheckerContext &C) const { + ProgramStateRef State = Call.getState(); + + const auto &ArgType = Call.getArgSVal(0) + .getType(C.getASTContext()) + ->getPointeeType() + .getTypePtr(); + // We have to make sure that the argument is an std::variant. + // There is another std::get with std::pair argument + if (!isStdVariant(ArgType)) + return false; + + // Get the mem region of the argument std::variant and look up the type + // information that we know about it. + const MemRegion *ArgMemRegion = Call.getArgSVal(0).getAsRegion(); + const QualType *StoredType = State->get<VariantHeldTypeMap>(ArgMemRegion); + if (!StoredType) + return false; + + const CallExpr *CE = cast<CallExpr>(Call.getOriginExpr()); + const FunctionDecl *FD = CE->getDirectCallee(); + if (FD->getTemplateSpecializationArgs()->size() < 1) + return false; + + const auto &TypeOut = FD->getTemplateSpecializationArgs()->asArray()[0]; + // std::get's first template parameter can be the type we want to get + // out of the std::variant or a natural number which is the position of + // the requested type in the argument type list of the std::variant's + // argument. + QualType RetrievedType; + switch (TypeOut.getKind()) { + case TemplateArgument::ArgKind::Type: + RetrievedType = TypeOut.getAsType(); + break; + case TemplateArgument::ArgKind::Integral: + // In the natural number case we look up which type corresponds to the + // number. + if (std::optional<QualType> NthTemplate = + getNthTemplateTypeArgFromVariant( + ArgType, TypeOut.getAsIntegral().getSExtValue())) { + RetrievedType = *NthTemplate; + break; + } + [[fallthrough]]; + default: + return false; + } + + QualType RetrievedCanonicalType = RetrievedType.getCanonicalType(); + QualType StoredCanonicalType = StoredType->getCanonicalType(); + if (RetrievedCanonicalType == StoredCanonicalType) + return true; + + ExplodedNode *ErrNode = C.generateNonFatalErrorNode(); + if (!ErrNode) + return false; + llvm::SmallString<128> Str; + llvm::raw_svector_ostream OS(Str); + std::string StoredTypeName = StoredType->getAsString(); + std::string RetrievedTypeName = RetrievedType.getAsString(); + OS << "std::variant " << ArgMemRegion->getDescriptiveName() << " held " + << indefiniteArticleBasedOnVowel(StoredTypeName[0]) << " \'" + << StoredTypeName << "\', not " + << indefiniteArticleBasedOnVowel(RetrievedTypeName[0]) << " \'" + << RetrievedTypeName << "\'"; + auto R = std::make_unique<PathSensitiveBugReport>(BadVariantType, OS.str(), + ErrNode); + C.emitReport(std::move(R)); + return true; + } +}; + +bool clang::ento::shouldRegisterStdVariantChecker( + clang::ento::CheckerManager const &mgr) { + return true; +} + +void clang::ento::registerStdVariantChecker(clang::ento::CheckerManager &mgr) { + mgr.registerChecker<StdVariantChecker>(); +}
\ No newline at end of file diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index bad86682c91e..925fc90e3554 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -238,18 +238,30 @@ public: private: CallDescriptionMap<FnDescription> FnDescriptions = { - {{{"fopen"}}, {nullptr, &StreamChecker::evalFopen, ArgNone}}, + {{{"fopen"}, 2}, {nullptr, &StreamChecker::evalFopen, ArgNone}}, {{{"freopen"}, 3}, {&StreamChecker::preFreopen, &StreamChecker::evalFreopen, 2}}, - {{{"tmpfile"}}, {nullptr, &StreamChecker::evalFopen, ArgNone}}, + {{{"tmpfile"}, 0}, {nullptr, &StreamChecker::evalFopen, ArgNone}}, {{{"fclose"}, 1}, {&StreamChecker::preDefault, &StreamChecker::evalFclose, 0}}, {{{"fread"}, 4}, - {&StreamChecker::preFread, + {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, true), std::bind(&StreamChecker::evalFreadFwrite, _1, _2, _3, _4, true), 3}}, {{{"fwrite"}, 4}, - {&StreamChecker::preFwrite, + {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, false), std::bind(&StreamChecker::evalFreadFwrite, _1, _2, _3, _4, false), 3}}, + {{{"fgetc"}, 1}, + {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, true), + std::bind(&StreamChecker::evalFgetx, _1, _2, _3, _4, true), 0}}, + {{{"fgets"}, 3}, + {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, true), + std::bind(&StreamChecker::evalFgetx, _1, _2, _3, _4, false), 2}}, + {{{"fputc"}, 2}, + {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, false), + std::bind(&StreamChecker::evalFputx, _1, _2, _3, _4, true), 1}}, + {{{"fputs"}, 2}, + {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, false), + std::bind(&StreamChecker::evalFputx, _1, _2, _3, _4, false), 1}}, {{{"fseek"}, 3}, {&StreamChecker::preFseek, &StreamChecker::evalFseek, 0}}, {{{"ftell"}, 1}, @@ -305,15 +317,18 @@ private: void evalFclose(const FnDescription *Desc, const CallEvent &Call, CheckerContext &C) const; - void preFread(const FnDescription *Desc, const CallEvent &Call, - CheckerContext &C) const; - - void preFwrite(const FnDescription *Desc, const CallEvent &Call, - CheckerContext &C) const; + void preReadWrite(const FnDescription *Desc, const CallEvent &Call, + CheckerContext &C, bool IsRead) const; void evalFreadFwrite(const FnDescription *Desc, const CallEvent &Call, CheckerContext &C, bool IsFread) const; + void evalFgetx(const FnDescription *Desc, const CallEvent &Call, + CheckerContext &C, bool SingleChar) const; + + void evalFputx(const FnDescription *Desc, const CallEvent &Call, + CheckerContext &C, bool IsSingleChar) const; + void preFseek(const FnDescription *Desc, const CallEvent &Call, CheckerContext &C) const; void evalFseek(const FnDescription *Desc, const CallEvent &Call, @@ -407,23 +422,14 @@ private: /// Generate a message for BugReporterVisitor if the stored symbol is /// marked as interesting by the actual bug report. - // FIXME: Use lambda instead. - struct NoteFn { - const BugType *BT_ResourceLeak; - SymbolRef StreamSym; - std::string Message; - - std::string operator()(PathSensitiveBugReport &BR) const { - if (BR.isInteresting(StreamSym) && &BR.getBugType() == BT_ResourceLeak) - return Message; - - return ""; - } - }; - const NoteTag *constructNoteTag(CheckerContext &C, SymbolRef StreamSym, const std::string &Message) const { - return C.getNoteTag(NoteFn{&BT_ResourceLeak, StreamSym, Message}); + return C.getNoteTag([this, StreamSym, + Message](PathSensitiveBugReport &BR) -> std::string { + if (BR.isInteresting(StreamSym) && &BR.getBugType() == &BT_ResourceLeak) + return Message; + return ""; + }); } const NoteTag *constructSetEofNoteTag(CheckerContext &C, @@ -646,8 +652,9 @@ void StreamChecker::evalFclose(const FnDescription *Desc, const CallEvent &Call, C.addTransition(StateFailure); } -void StreamChecker::preFread(const FnDescription *Desc, const CallEvent &Call, - CheckerContext &C) const { +void StreamChecker::preReadWrite(const FnDescription *Desc, + const CallEvent &Call, CheckerContext &C, + bool IsRead) const { ProgramStateRef State = C.getState(); SVal StreamVal = getStreamArg(Desc, Call); State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C, @@ -661,6 +668,11 @@ void StreamChecker::preFread(const FnDescription *Desc, const CallEvent &Call, if (!State) return; + if (!IsRead) { + C.addTransition(State); + return; + } + SymbolRef Sym = StreamVal.getAsSymbol(); if (Sym && State->get<StreamMap>(Sym)) { const StreamState *SS = State->get<StreamMap>(Sym); @@ -671,24 +683,6 @@ void StreamChecker::preFread(const FnDescription *Desc, const CallEvent &Call, } } -void StreamChecker::preFwrite(const FnDescription *Desc, const CallEvent &Call, - CheckerContext &C) const { - ProgramStateRef State = C.getState(); - SVal StreamVal = getStreamArg(Desc, Call); - State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C, - State); - if (!State) - return; - State = ensureStreamOpened(StreamVal, C, State); - if (!State) - return; - State = ensureNoFilePositionIndeterminate(StreamVal, C, State); - if (!State) - return; - - C.addTransition(State); -} - void StreamChecker::evalFreadFwrite(const FnDescription *Desc, const CallEvent &Call, CheckerContext &C, bool IsFread) const { @@ -743,9 +737,9 @@ void StreamChecker::evalFreadFwrite(const FnDescription *Desc, NonLoc RetVal = makeRetVal(C, CE).castAs<NonLoc>(); ProgramStateRef StateFailed = State->BindExpr(CE, C.getLocationContext(), RetVal); + SValBuilder &SVB = C.getSValBuilder(); auto Cond = - C.getSValBuilder() - .evalBinOpNN(State, BO_LT, RetVal, *NMembVal, C.getASTContext().IntTy) + SVB.evalBinOpNN(State, BO_LT, RetVal, *NMembVal, SVB.getConditionType()) .getAs<DefinedOrUnknownSVal>(); if (!Cond) return; @@ -769,6 +763,150 @@ void StreamChecker::evalFreadFwrite(const FnDescription *Desc, C.addTransition(StateFailed); } +void StreamChecker::evalFgetx(const FnDescription *Desc, const CallEvent &Call, + CheckerContext &C, bool SingleChar) const { + ProgramStateRef State = C.getState(); + SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); + if (!StreamSym) + return; + + const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); + if (!CE) + return; + + const StreamState *OldSS = State->get<StreamMap>(StreamSym); + if (!OldSS) + return; + + assertStreamStateOpened(OldSS); + + // `fgetc` returns the read character on success, otherwise returns EOF. + // `fgets` returns the read buffer address on success, otherwise returns NULL. + + if (OldSS->ErrorState != ErrorFEof) { + if (SingleChar) { + // Generate a transition for the success state of `fgetc`. + NonLoc RetVal = makeRetVal(C, CE).castAs<NonLoc>(); + ProgramStateRef StateNotFailed = + State->BindExpr(CE, C.getLocationContext(), RetVal); + SValBuilder &SVB = C.getSValBuilder(); + ASTContext &ASTC = C.getASTContext(); + // The returned 'unsigned char' of `fgetc` is converted to 'int', + // so we need to check if it is in range [0, 255]. + auto CondLow = + SVB.evalBinOp(State, BO_GE, RetVal, SVB.makeZeroVal(ASTC.IntTy), + SVB.getConditionType()) + .getAs<DefinedOrUnknownSVal>(); + auto CondHigh = + SVB.evalBinOp(State, BO_LE, RetVal, + SVB.makeIntVal(SVB.getBasicValueFactory() + .getMaxValue(ASTC.UnsignedCharTy) + .getLimitedValue(), + ASTC.IntTy), + SVB.getConditionType()) + .getAs<DefinedOrUnknownSVal>(); + if (!CondLow || !CondHigh) + return; + StateNotFailed = StateNotFailed->assume(*CondLow, true); + if (!StateNotFailed) + return; + StateNotFailed = StateNotFailed->assume(*CondHigh, true); + if (!StateNotFailed) + return; + C.addTransition(StateNotFailed); + } else { + // Generate a transition for the success state of `fgets`. + std::optional<DefinedSVal> GetBuf = + Call.getArgSVal(0).getAs<DefinedSVal>(); + if (!GetBuf) + return; + ProgramStateRef StateNotFailed = + State->BindExpr(CE, C.getLocationContext(), *GetBuf); + StateNotFailed = StateNotFailed->set<StreamMap>( + StreamSym, StreamState::getOpened(Desc)); + C.addTransition(StateNotFailed); + } + } + + // Add transition for the failed state. + ProgramStateRef StateFailed; + if (SingleChar) + StateFailed = bindInt(*EofVal, State, C, CE); + else + StateFailed = + State->BindExpr(CE, C.getLocationContext(), + C.getSValBuilder().makeNullWithType(CE->getType())); + + // If a (non-EOF) error occurs, the resulting value of the file position + // indicator for the stream is indeterminate. + StreamErrorState NewES = + OldSS->ErrorState == ErrorFEof ? ErrorFEof : ErrorFEof | ErrorFError; + StreamState NewSS = StreamState::getOpened(Desc, NewES, !NewES.isFEof()); + StateFailed = StateFailed->set<StreamMap>(StreamSym, NewSS); + if (OldSS->ErrorState != ErrorFEof) + C.addTransition(StateFailed, constructSetEofNoteTag(C, StreamSym)); + else + C.addTransition(StateFailed); +} + +void StreamChecker::evalFputx(const FnDescription *Desc, const CallEvent &Call, + CheckerContext &C, bool IsSingleChar) const { + ProgramStateRef State = C.getState(); + SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); + if (!StreamSym) + return; + + const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); + if (!CE) + return; + + const StreamState *OldSS = State->get<StreamMap>(StreamSym); + if (!OldSS) + return; + + assertStreamStateOpened(OldSS); + + // `fputc` returns the written character on success, otherwise returns EOF. + // `fputs` returns a non negative value on sucecess, otherwise returns EOF. + + if (IsSingleChar) { + // Generate a transition for the success state of `fputc`. + std::optional<NonLoc> PutVal = Call.getArgSVal(0).getAs<NonLoc>(); + if (!PutVal) + return; + ProgramStateRef StateNotFailed = + State->BindExpr(CE, C.getLocationContext(), *PutVal); + StateNotFailed = + StateNotFailed->set<StreamMap>(StreamSym, StreamState::getOpened(Desc)); + C.addTransition(StateNotFailed); + } else { + // Generate a transition for the success state of `fputs`. + NonLoc RetVal = makeRetVal(C, CE).castAs<NonLoc>(); + ProgramStateRef StateNotFailed = + State->BindExpr(CE, C.getLocationContext(), RetVal); + SValBuilder &SVB = C.getSValBuilder(); + auto &ASTC = C.getASTContext(); + auto Cond = SVB.evalBinOp(State, BO_GE, RetVal, SVB.makeZeroVal(ASTC.IntTy), + SVB.getConditionType()) + .getAs<DefinedOrUnknownSVal>(); + if (!Cond) + return; + StateNotFailed = StateNotFailed->assume(*Cond, true); + if (!StateNotFailed) + return; + StateNotFailed = + StateNotFailed->set<StreamMap>(StreamSym, StreamState::getOpened(Desc)); + C.addTransition(StateNotFailed); + } + + // Add transition for the failed state. The resulting value of the file + // position indicator for the stream is indeterminate. + ProgramStateRef StateFailed = bindInt(*EofVal, State, C, CE); + StreamState NewSS = StreamState::getOpened(Desc, ErrorFError, true); + StateFailed = StateFailed->set<StreamMap>(StreamSym, NewSS); + C.addTransition(StateFailed); +} + void StreamChecker::preFseek(const FnDescription *Desc, const CallEvent &Call, CheckerContext &C) const { ProgramStateRef State = C.getState(); @@ -934,6 +1072,9 @@ void StreamChecker::evalFtell(const FnDescription *Desc, const CallEvent &Call, ProgramStateRef StateFailed = State->BindExpr( CE, C.getLocationContext(), SVB.makeIntVal(-1, C.getASTContext().LongTy)); + // 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); } @@ -1061,7 +1202,7 @@ StreamChecker::ensureStreamNonNull(SVal StreamVal, const Expr *StreamE, ConstraintManager &CM = C.getConstraintManager(); ProgramStateRef StateNotNull, StateNull; - std::tie(StateNotNull, StateNull) = CM.assumeDual(C.getState(), *Stream); + std::tie(StateNotNull, StateNull) = CM.assumeDual(State, *Stream); if (!StateNotNull && StateNull) { if (ExplodedNode *N = C.generateErrorNode(StateNull)) { @@ -1117,7 +1258,6 @@ ProgramStateRef StreamChecker::ensureStreamOpened(SVal StreamVal, N)); return nullptr; } - return State; } return State; @@ -1231,7 +1371,7 @@ StreamChecker::reportLeaks(const SmallVector<SymbolRef, 2> &LeakedSyms, PathDiagnosticLocation LocUsedForUniqueing; if (const Stmt *StreamStmt = StreamOpenNode->getStmtForDiagnostics()) - LocUsedForUniqueing = PathDiagnosticLocation::createBegin( + LocUsedForUniqueing = PathDiagnosticLocation::createBegin( StreamStmt, C.getSourceManager(), StreamOpenNode->getLocationContext()); diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/TaggedUnionModeling.h b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/TaggedUnionModeling.h new file mode 100644 index 000000000000..557e8a76506e --- /dev/null +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/TaggedUnionModeling.h @@ -0,0 +1,99 @@ +//===- TaggedUnionModeling.h -------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_TAGGEDUNIONMODELING_H +#define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_TAGGEDUNIONMODELING_H + +#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/CallDescription.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "llvm/ADT/FoldingSet.h" +#include <numeric> + +namespace clang::ento::tagged_union_modeling { + +// The implementation of all these functions can be found in the file +// StdVariantChecker.cpp under the same directory as this file. + +bool isCopyConstructorCall(const CallEvent &Call); +bool isCopyAssignmentCall(const CallEvent &Call); +bool isMoveAssignmentCall(const CallEvent &Call); +bool isMoveConstructorCall(const CallEvent &Call); +bool isStdType(const Type *Type, const std::string &TypeName); +bool isStdVariant(const Type *Type); + +// When invalidating regions, we also have to follow that by invalidating the +// corresponding custom data in the program state. +template <class TypeMap> +ProgramStateRef +removeInformationStoredForDeadInstances(const CallEvent &Call, + ProgramStateRef State, + ArrayRef<const MemRegion *> Regions) { + // If we do not know anything about the call we shall not continue. + // If the call is happens within a system header it is implementation detail. + // We should not take it into consideration. + if (Call.isInSystemHeader()) + return State; + + for (const MemRegion *Region : Regions) + State = State->remove<TypeMap>(Region); + + return State; +} + +template <class TypeMap> +void handleConstructorAndAssignment(const CallEvent &Call, CheckerContext &C, + const SVal &ThisSVal) { + ProgramStateRef State = Call.getState(); + + if (!State) + return; + + auto ArgSVal = Call.getArgSVal(0); + const auto *ThisRegion = ThisSVal.getAsRegion(); + const auto *ArgMemRegion = ArgSVal.getAsRegion(); + + // Make changes to the state according to type of constructor/assignment + bool IsCopy = isCopyConstructorCall(Call) || isCopyAssignmentCall(Call); + bool IsMove = isMoveConstructorCall(Call) || isMoveAssignmentCall(Call); + // First we handle copy and move operations + if (IsCopy || IsMove) { + const QualType *OtherQType = State->get<TypeMap>(ArgMemRegion); + + // If the argument of a copy constructor or assignment is unknown then + // we will not know the argument of the copied to object. + if (!OtherQType) { + State = State->remove<TypeMap>(ThisRegion); + } else { + // When move semantics is used we can only know that the moved from + // object must be in a destructible state. Other usage of the object + // than destruction is undefined. + if (IsMove) + State = State->remove<TypeMap>(ArgMemRegion); + + State = State->set<TypeMap>(ThisRegion, *OtherQType); + } + } else { + // Value constructor + auto ArgQType = ArgSVal.getType(C.getASTContext()); + const Type *ArgTypePtr = ArgQType.getTypePtr(); + + QualType WoPointer = ArgTypePtr->getPointeeType(); + State = State->set<TypeMap>(ThisRegion, WoPointer); + } + + C.addTransition(State); +} + +} // namespace clang::ento::tagged_union_modeling + +#endif // LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_TAGGEDUNIONMODELING_H
\ No newline at end of file diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp index 9d3a909f50c1..5cdcc1075f44 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp @@ -78,7 +78,7 @@ public: class TestAfterDivZeroChecker : public Checker<check::PreStmt<BinaryOperator>, check::BranchCondition, check::EndFunction> { - mutable std::unique_ptr<BuiltinBug> DivZeroBug; + mutable std::unique_ptr<BugType> DivZeroBug; void reportBug(SVal Val, CheckerContext &C) const; public: @@ -166,7 +166,7 @@ bool TestAfterDivZeroChecker::hasDivZeroMap(SVal Var, void TestAfterDivZeroChecker::reportBug(SVal Val, CheckerContext &C) const { if (ExplodedNode *N = C.generateErrorNode(C.getState())) { if (!DivZeroBug) - DivZeroBug.reset(new BuiltinBug(this, "Division by zero")); + DivZeroBug.reset(new BugType(this, "Division by zero")); auto R = std::make_unique<PathSensitiveBugReport>( *DivZeroBug, "Value being compared against zero has already been used " diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp index b17b983f0345..db886501a162 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp @@ -27,7 +27,7 @@ using namespace ento; namespace { class UndefBranchChecker : public Checker<check::BranchCondition> { - mutable std::unique_ptr<BuiltinBug> BT; + mutable std::unique_ptr<BugType> BT; struct FindUndefExpr { ProgramStateRef St; @@ -71,8 +71,8 @@ void UndefBranchChecker::checkBranchCondition(const Stmt *Condition, ExplodedNode *N = Ctx.generateErrorNode(); if (N) { if (!BT) - BT.reset(new BuiltinBug( - this, "Branch condition evaluates to a garbage value")); + BT.reset( + new BugType(this, "Branch condition evaluates to a garbage value")); // What's going on here: we want to highlight the subexpression of the // condition that is the most likely source of the "uninitialized diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp index 2973dd5457c6..ecb6ed36ee40 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp @@ -72,7 +72,7 @@ UndefCapturedBlockVarChecker::checkPostStmt(const BlockExpr *BE, if (ExplodedNode *N = C.generateErrorNode()) { if (!BT) BT.reset( - new BuiltinBug(this, "uninitialized variable captured by block")); + new BugType(this, "uninitialized variable captured by block")); // Generate a bug report. SmallString<128> buf; diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp index f20b38a53151..d593a6bd74cf 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp @@ -58,28 +58,12 @@ static bool isArrayIndexOutOfBounds(CheckerContext &C, const Expr *Ex) { return StOutBound && !StInBound; } -static bool isShiftOverflow(const BinaryOperator *B, CheckerContext &C) { - return C.isGreaterOrEqual( - B->getRHS(), C.getASTContext().getIntWidth(B->getLHS()->getType())); -} - -static bool isLeftShiftResultUnrepresentable(const BinaryOperator *B, - CheckerContext &C) { - SValBuilder &SB = C.getSValBuilder(); - ProgramStateRef State = C.getState(); - const llvm::APSInt *LHS = SB.getKnownValue(State, C.getSVal(B->getLHS())); - const llvm::APSInt *RHS = SB.getKnownValue(State, C.getSVal(B->getRHS())); - assert(LHS && RHS && "Values unknown, inconsistent state"); - return (unsigned)RHS->getZExtValue() > LHS->countl_zero(); -} - void UndefResultChecker::checkPostStmt(const BinaryOperator *B, CheckerContext &C) const { if (C.getSVal(B).isUndef()) { // Do not report assignments of uninitialized values inside swap functions. // This should allow to swap partially uninitialized structs - // (radar://14129997) if (const FunctionDecl *EnclosingFunctionDecl = dyn_cast<FunctionDecl>(C.getStackFrame()->getDecl())) if (C.getCalleeName(EnclosingFunctionDecl) == "swap") @@ -92,7 +76,7 @@ void UndefResultChecker::checkPostStmt(const BinaryOperator *B, if (!BT) BT.reset( - new BuiltinBug(this, "Result of operation is garbage or undefined")); + new BugType(this, "Result of operation is garbage or undefined")); SmallString<256> sbuf; llvm::raw_svector_ostream OS(sbuf); @@ -116,59 +100,9 @@ void UndefResultChecker::checkPostStmt(const BinaryOperator *B, OS << " due to array index out of bounds"; } else { // Neither operand was undefined, but the result is undefined. - if ((B->getOpcode() == BinaryOperatorKind::BO_Shl || - B->getOpcode() == BinaryOperatorKind::BO_Shr) && - C.isNegative(B->getRHS())) { - OS << "The result of the " - << ((B->getOpcode() == BinaryOperatorKind::BO_Shl) ? "left" - : "right") - << " shift is undefined because the right operand is negative"; - Ex = B->getRHS(); - } else if ((B->getOpcode() == BinaryOperatorKind::BO_Shl || - B->getOpcode() == BinaryOperatorKind::BO_Shr) && - isShiftOverflow(B, C)) { - - OS << "The result of the " - << ((B->getOpcode() == BinaryOperatorKind::BO_Shl) ? "left" - : "right") - << " shift is undefined due to shifting by "; - Ex = B->getRHS(); - - SValBuilder &SB = C.getSValBuilder(); - const llvm::APSInt *I = - SB.getKnownValue(C.getState(), C.getSVal(B->getRHS())); - if (!I) - OS << "a value that is"; - else if (I->isUnsigned()) - OS << '\'' << I->getZExtValue() << "\', which is"; - else - OS << '\'' << I->getSExtValue() << "\', which is"; - - OS << " greater or equal to the width of type '" - << B->getLHS()->getType() << "'."; - } else if (B->getOpcode() == BinaryOperatorKind::BO_Shl && - C.isNegative(B->getLHS())) { - OS << "The result of the left shift is undefined because the left " - "operand is negative"; - Ex = B->getLHS(); - } else if (B->getOpcode() == BinaryOperatorKind::BO_Shl && - isLeftShiftResultUnrepresentable(B, C)) { - ProgramStateRef State = C.getState(); - SValBuilder &SB = C.getSValBuilder(); - const llvm::APSInt *LHS = - SB.getKnownValue(State, C.getSVal(B->getLHS())); - const llvm::APSInt *RHS = - SB.getKnownValue(State, C.getSVal(B->getRHS())); - OS << "The result of the left shift is undefined due to shifting \'" - << LHS->getSExtValue() << "\' by \'" << RHS->getZExtValue() - << "\', which is unrepresentable in the unsigned version of " - << "the return type \'" << B->getLHS()->getType() << "\'"; - Ex = B->getLHS(); - } else { - OS << "The result of the '" - << BinaryOperator::getOpcodeStr(B->getOpcode()) - << "' expression is undefined"; - } + OS << "The result of the '" + << BinaryOperator::getOpcodeStr(B->getOpcode()) + << "' expression is undefined"; } auto report = std::make_unique<PathSensitiveBugReport>(*BT, OS.str(), N); if (Ex) { diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp index fdefe75e8201..a6cc8cac8c99 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp @@ -49,7 +49,7 @@ UndefinedArraySubscriptChecker::checkPreStmt(const ArraySubscriptExpr *A, if (!N) return; if (!BT) - BT.reset(new BuiltinBug(this, "Array subscript is undefined")); + BT.reset(new BugType(this, "Array subscript is undefined")); // Generate a report for this bug. auto R = std::make_unique<PathSensitiveBugReport>(*BT, BT->getDescription(), N); diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp index 0fa3d6043971..49ac94f65dd0 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp @@ -39,7 +39,6 @@ void UndefinedAssignmentChecker::checkBind(SVal location, SVal val, // Do not report assignments of uninitialized values inside swap functions. // This should allow to swap partially uninitialized structs - // (radar://14129997) if (const FunctionDecl *EnclosingFunctionDecl = dyn_cast<FunctionDecl>(C.getStackFrame()->getDecl())) if (C.getCalleeName(EnclosingFunctionDecl) == "swap") @@ -53,7 +52,7 @@ void UndefinedAssignmentChecker::checkBind(SVal location, SVal val, static const char *const DefaultMsg = "Assigned value is garbage or undefined"; if (!BT) - BT.reset(new BuiltinBug(this, DefaultMsg)); + BT.reset(new BugType(this, DefaultMsg)); // Generate a report for this bug. llvm::SmallString<128> Str; diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp index cd91fa9b090c..3647c49cf3f9 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp @@ -38,14 +38,14 @@ namespace { class UninitializedObjectChecker : public Checker<check::EndFunction, check::DeadSymbols> { - std::unique_ptr<BuiltinBug> BT_uninitField; + std::unique_ptr<BugType> BT_uninitField; public: // The fields of this struct will be initialized when registering the checker. UninitObjCheckerOptions Opts; UninitializedObjectChecker() - : BT_uninitField(new BuiltinBug(this, "Uninitialized fields")) {} + : BT_uninitField(new BugType(this, "Uninitialized fields")) {} void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const; void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const; diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp index b195d912cadf..1d03d1656b3c 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp @@ -188,7 +188,8 @@ ProgramStateRef VLASizeChecker::checkVLAIndexSize(CheckerContext &C, QualType SizeTy = SizeE->getType(); DefinedOrUnknownSVal Zero = SVB.makeZeroVal(SizeTy); - SVal LessThanZeroVal = SVB.evalBinOp(State, BO_LT, SizeD, Zero, SizeTy); + SVal LessThanZeroVal = + SVB.evalBinOp(State, BO_LT, SizeD, Zero, SVB.getConditionType()); if (std::optional<DefinedSVal> LessThanZeroDVal = LessThanZeroVal.getAs<DefinedSVal>()) { ConstraintManager &CM = C.getConstraintManager(); diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/VforkChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/VforkChecker.cpp index 86a4e6fbcd6a..8a1e02748c9b 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/VforkChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/VforkChecker.cpp @@ -44,9 +44,9 @@ namespace { class VforkChecker : public Checker<check::PreCall, check::PostCall, check::Bind, check::PreStmt<ReturnStmt>> { - mutable std::unique_ptr<BuiltinBug> BT; + mutable std::unique_ptr<BugType> BT; mutable llvm::SmallSet<const IdentifierInfo *, 10> VforkAllowlist; - mutable const IdentifierInfo *II_vfork; + mutable const IdentifierInfo *II_vfork = nullptr; static bool isChildProcess(const ProgramStateRef State); @@ -58,7 +58,7 @@ class VforkChecker : public Checker<check::PreCall, check::PostCall, const char *Details = nullptr) const; public: - VforkChecker() : II_vfork(nullptr) {} + VforkChecker() = default; void checkPreCall(const CallEvent &Call, CheckerContext &C) const; void checkPostCall(const CallEvent &Call, CheckerContext &C) const; @@ -124,8 +124,7 @@ void VforkChecker::reportBug(const char *What, CheckerContext &C, const char *Details) const { if (ExplodedNode *N = C.generateErrorNode(C.getState())) { if (!BT) - BT.reset(new BuiltinBug(this, - "Dangerous construct in a vforked process")); + BT.reset(new BugType(this, "Dangerous construct in a vforked process")); SmallString<256> buf; llvm::raw_svector_ostream os(buf); diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/NoUncountedMembersChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/NoUncountedMembersChecker.cpp index 66d8588e2531..c753ed84a700 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/NoUncountedMembersChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/NoUncountedMembersChecker.cpp @@ -17,7 +17,6 @@ #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" -#include "llvm/ADT/DenseSet.h" #include "llvm/Support/Casting.h" #include <optional> @@ -104,7 +103,7 @@ public: const auto Kind = RD->getTagKind(); // FIMXE: Should we check union members too? - if (Kind != TTK_Struct && Kind != TTK_Class) + if (Kind != TagTypeKind::Struct && Kind != TagTypeKind::Class) return true; // Ignore CXXRecords that come from system headers. diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp index 9b1d7ae3e6a3..d2b663410580 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp @@ -18,24 +18,15 @@ using namespace clang; namespace { -bool hasPublicRefAndDeref(const CXXRecordDecl *R) { +bool hasPublicMethodInBaseClass(const CXXRecordDecl *R, + const char *NameToMatch) { assert(R); assert(R->hasDefinition()); - bool hasRef = false; - bool hasDeref = false; for (const CXXMethodDecl *MD : R->methods()) { const auto MethodName = safeGetName(MD); - - if (MethodName == "ref" && MD->getAccess() == AS_public) { - if (hasDeref) - return true; - hasRef = true; - } else if (MethodName == "deref" && MD->getAccess() == AS_public) { - if (hasRef) - return true; - hasDeref = true; - } + if (MethodName == NameToMatch && MD->getAccess() == AS_public) + return true; } return false; } @@ -44,9 +35,8 @@ bool hasPublicRefAndDeref(const CXXRecordDecl *R) { namespace clang { -std::optional<const clang::CXXRecordDecl*> -isRefCountable(const CXXBaseSpecifier* Base) -{ +std::optional<const clang::CXXRecordDecl *> +hasPublicMethodInBase(const CXXBaseSpecifier *Base, const char *NameToMatch) { assert(Base); const Type *T = Base->getType().getTypePtrOrNull(); @@ -59,7 +49,7 @@ isRefCountable(const CXXBaseSpecifier* Base) if (!R->hasDefinition()) return std::nullopt; - return hasPublicRefAndDeref(R) ? R : nullptr; + return hasPublicMethodInBaseClass(R, NameToMatch) ? R : nullptr; } std::optional<bool> isRefCountable(const CXXRecordDecl* R) @@ -70,29 +60,45 @@ std::optional<bool> isRefCountable(const CXXRecordDecl* R) if (!R) return std::nullopt; - if (hasPublicRefAndDeref(R)) + bool hasRef = hasPublicMethodInBaseClass(R, "ref"); + bool hasDeref = hasPublicMethodInBaseClass(R, "deref"); + if (hasRef && hasDeref) return true; CXXBasePaths Paths; Paths.setOrigin(const_cast<CXXRecordDecl *>(R)); bool AnyInconclusiveBase = false; - const auto isRefCountableBase = - [&AnyInconclusiveBase](const CXXBaseSpecifier* Base, CXXBasePath&) { - std::optional<const clang::CXXRecordDecl*> IsRefCountable = clang::isRefCountable(Base); - if (!IsRefCountable) { - AnyInconclusiveBase = true; - return false; - } - return (*IsRefCountable) != nullptr; + const auto hasPublicRefInBase = + [&AnyInconclusiveBase](const CXXBaseSpecifier *Base, CXXBasePath &) { + auto hasRefInBase = clang::hasPublicMethodInBase(Base, "ref"); + if (!hasRefInBase) { + AnyInconclusiveBase = true; + return false; + } + return (*hasRefInBase) != nullptr; }; - bool BasesResult = R->lookupInBases(isRefCountableBase, Paths, + hasRef = hasRef || R->lookupInBases(hasPublicRefInBase, Paths, /*LookupInDependent =*/true); if (AnyInconclusiveBase) return std::nullopt; - return BasesResult; + const auto hasPublicDerefInBase = + [&AnyInconclusiveBase](const CXXBaseSpecifier *Base, CXXBasePath &) { + auto hasDerefInBase = clang::hasPublicMethodInBase(Base, "deref"); + if (!hasDerefInBase) { + AnyInconclusiveBase = true; + return false; + } + return (*hasDerefInBase) != nullptr; + }; + hasDeref = hasDeref || R->lookupInBases(hasPublicDerefInBase, Paths, + /*LookupInDependent =*/true); + if (AnyInconclusiveBase) + return std::nullopt; + + return hasRef && hasDeref; } bool isCtorOfRefCounted(const clang::FunctionDecl *F) { @@ -186,8 +192,7 @@ bool isPtrConversion(const FunctionDecl *F) { // FIXME: check # of params == 1 const auto FunctionName = safeGetName(F); if (FunctionName == "getPtr" || FunctionName == "WeakPtr" || - FunctionName == "makeWeakPtr" - + FunctionName == "dynamicDowncast" || FunctionName == "downcast" || FunctionName == "bitwise_cast") return true; diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h index 91e3ccf2ec30..45b21cc09184 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h @@ -26,10 +26,10 @@ class Type; // In WebKit there are two ref-counted templated smart pointers: RefPtr<T> and // Ref<T>. -/// \returns CXXRecordDecl of the base if the type is ref-countable, nullptr if -/// not, std::nullopt if inconclusive. -std::optional<const clang::CXXRecordDecl*> -isRefCountable(const clang::CXXBaseSpecifier* Base); +/// \returns CXXRecordDecl of the base if the type has ref as a public method, +/// nullptr if not, std::nullopt if inconclusive. +std::optional<const clang::CXXRecordDecl *> +hasPublicMethodInBase(const CXXBaseSpecifier *Base, const char *NameToMatch); /// \returns true if \p Class is ref-countable, false if not, std::nullopt if /// inconclusive. diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp index 48dcfc4a3c46..d879c110b75d 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp @@ -77,14 +77,53 @@ public: (AccSpec == AS_none && RD->isClass())) return false; - std::optional<const CXXRecordDecl*> RefCntblBaseRD = isRefCountable(Base); - if (!RefCntblBaseRD || !(*RefCntblBaseRD)) + auto hasRefInBase = clang::hasPublicMethodInBase(Base, "ref"); + auto hasDerefInBase = clang::hasPublicMethodInBase(Base, "deref"); + + bool hasRef = hasRefInBase && *hasRefInBase != nullptr; + bool hasDeref = hasDerefInBase && *hasDerefInBase != nullptr; + + QualType T = Base->getType(); + if (T.isNull()) + return false; + + const CXXRecordDecl *C = T->getAsCXXRecordDecl(); + if (!C) + return false; + bool AnyInconclusiveBase = false; + const auto hasPublicRefInBase = + [&AnyInconclusiveBase](const CXXBaseSpecifier *Base, + CXXBasePath &) { + auto hasRefInBase = clang::hasPublicMethodInBase(Base, "ref"); + if (!hasRefInBase) { + AnyInconclusiveBase = true; + return false; + } + return (*hasRefInBase) != nullptr; + }; + const auto hasPublicDerefInBase = [&AnyInconclusiveBase]( + const CXXBaseSpecifier *Base, + CXXBasePath &) { + auto hasDerefInBase = clang::hasPublicMethodInBase(Base, "deref"); + if (!hasDerefInBase) { + AnyInconclusiveBase = true; + return false; + } + return (*hasDerefInBase) != nullptr; + }; + CXXBasePaths Paths; + Paths.setOrigin(C); + hasRef = hasRef || C->lookupInBases(hasPublicRefInBase, Paths, + /*LookupInDependent =*/true); + hasDeref = hasDeref || C->lookupInBases(hasPublicDerefInBase, Paths, + /*LookupInDependent =*/true); + if (AnyInconclusiveBase || !hasRef || !hasDeref) return false; - const auto *Dtor = (*RefCntblBaseRD)->getDestructor(); + const auto *Dtor = C->getDestructor(); if (!Dtor || !Dtor->isVirtual()) { ProblematicBaseSpecifier = Base; - ProblematicBaseClass = *RefCntblBaseRD; + ProblematicBaseClass = C; return true; } @@ -114,7 +153,7 @@ public: return true; const auto Kind = RD->getTagKind(); - if (Kind != TTK_Struct && Kind != TTK_Class) + if (Kind != TagTypeKind::Struct && Kind != TagTypeKind::Class) return true; // Ignore CXXRecords that come from system headers. diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp index 4ae8c442fa70..31ccae8b097b 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp @@ -18,7 +18,6 @@ #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" -#include "llvm/ADT/DenseSet.h" #include <optional> using namespace clang; @@ -149,7 +148,7 @@ public: auto name = safeGetName(Callee); if (name == "adoptRef" || name == "getPtr" || name == "WeakPtr" || - name == "makeWeakPtr" || name == "downcast" || name == "bitwise_cast" || + name == "dynamicDowncast" || name == "downcast" || name == "bitwise_cast" || name == "is" || name == "equal" || name == "hash" || name == "isType" // FIXME: Most/all of these should be implemented via attributes. diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLocalVarsChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLocalVarsChecker.cpp index fa7475934981..5a72f53b12ed 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLocalVarsChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLocalVarsChecker.cpp @@ -19,7 +19,6 @@ #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" -#include "llvm/ADT/DenseSet.h" #include <optional> using namespace clang; diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Yaml.h b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Yaml.h index 4159e14105f8..b2d17420686e 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Yaml.h +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Yaml.h @@ -35,7 +35,7 @@ std::optional<T> getConfiguration(CheckerManager &Mgr, Checker *Chk, llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Buffer = FS->getBufferForFile(ConfigFile.str()); - if (std::error_code ec = Buffer.getError()) { + if (Buffer.getError()) { Mgr.reportInvalidCheckerOptionValue(Chk, Option, "a valid filename instead of '" + std::string(ConfigFile) + "'"); diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/cert/InvalidPtrChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/cert/InvalidPtrChecker.cpp index aae1a17bc0ae..e5dd907c660d 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/cert/InvalidPtrChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/cert/InvalidPtrChecker.cpp @@ -30,7 +30,10 @@ namespace { class InvalidPtrChecker : public Checker<check::Location, check::BeginFunction, check::PostCall> { private: - BugType BT{this, "Use of invalidated pointer", categories::MemoryError}; + // For accurate emission of NoteTags, the BugType of this checker should have + // a unique address. + BugType InvalidPtrBugType{this, "Use of invalidated pointer", + categories::MemoryError}; void EnvpInvalidatingCall(const CallEvent &Call, CheckerContext &C) const; @@ -38,6 +41,15 @@ private: CheckerContext &C) const; // SEI CERT ENV31-C + + // If set to true, consider getenv calls as invalidating operations on the + // environment variable buffer. This is implied in the standard, but in + // practice does not cause problems (in the commonly used environments). + bool InvalidatingGetEnv = false; + + // GetEnv can be treated invalidating and non-invalidating as well. + const CallDescription GetEnvCall{{"getenv"}, 1}; + const CallDescriptionMap<HandlerFn> EnvpInvalidatingFunctions = { {{{"setenv"}, 3}, &InvalidPtrChecker::EnvpInvalidatingCall}, {{{"unsetenv"}, 1}, &InvalidPtrChecker::EnvpInvalidatingCall}, @@ -51,7 +63,6 @@ private: // SEI CERT ENV34-C const CallDescriptionMap<HandlerFn> PreviousCallInvalidatingFunctions = { - {{{"getenv"}, 1}, &InvalidPtrChecker::postPreviousReturnInvalidatingCall}, {{{"setlocale"}, 2}, &InvalidPtrChecker::postPreviousReturnInvalidatingCall}, {{{"strerror"}, 1}, @@ -62,6 +73,10 @@ private: &InvalidPtrChecker::postPreviousReturnInvalidatingCall}, }; + // The private members of this checker corresponding to commandline options + // are set in this function. + friend void ento::registerInvalidPtrChecker(CheckerManager &); + public: // Obtain the environment pointer from 'main()' (if present). void checkBeginFunction(CheckerContext &C) const; @@ -76,6 +91,11 @@ public: // Check if invalidated region is being dereferenced. void checkLocation(SVal l, bool isLoad, const Stmt *S, CheckerContext &C) const; + +private: + const NoteTag *createEnvInvalidationNote(CheckerContext &C, + ProgramStateRef State, + StringRef FunctionName) const; }; } // namespace @@ -84,33 +104,74 @@ public: REGISTER_SET_WITH_PROGRAMSTATE(InvalidMemoryRegions, const MemRegion *) // Stores the region of the environment pointer of 'main' (if present). -REGISTER_TRAIT_WITH_PROGRAMSTATE(EnvPtrRegion, const MemRegion *) +REGISTER_TRAIT_WITH_PROGRAMSTATE(MainEnvPtrRegion, const MemRegion *) + +// Stores the regions of environments returned by getenv calls. +REGISTER_SET_WITH_PROGRAMSTATE(GetenvEnvPtrRegions, const MemRegion *) // Stores key-value pairs, where key is function declaration and value is // pointer to memory region returned by previous call of this function REGISTER_MAP_WITH_PROGRAMSTATE(PreviousCallResultMap, const FunctionDecl *, const MemRegion *) +const NoteTag *InvalidPtrChecker::createEnvInvalidationNote( + CheckerContext &C, ProgramStateRef State, StringRef FunctionName) const { + + const MemRegion *MainRegion = State->get<MainEnvPtrRegion>(); + const auto GetenvRegions = State->get<GetenvEnvPtrRegions>(); + + return C.getNoteTag([this, MainRegion, GetenvRegions, + FunctionName = std::string{FunctionName}]( + PathSensitiveBugReport &BR, llvm::raw_ostream &Out) { + // Only handle the BugType of this checker. + if (&BR.getBugType() != &InvalidPtrBugType) + return; + + // Mark all regions that were interesting before as NOT interesting now + // to avoid extra notes coming from invalidation points higher up the + // bugpath. This ensures that only the last invalidation point is marked + // with a note tag. + llvm::SmallVector<std::string, 2> InvalidLocationNames; + if (BR.isInteresting(MainRegion)) { + BR.markNotInteresting(MainRegion); + InvalidLocationNames.push_back("the environment parameter of 'main'"); + } + bool InterestingGetenvFound = false; + for (const MemRegion *MR : GetenvRegions) { + if (BR.isInteresting(MR)) { + BR.markNotInteresting(MR); + if (!InterestingGetenvFound) { + InterestingGetenvFound = true; + InvalidLocationNames.push_back( + "the environment returned by 'getenv'"); + } + } + } + + // Emit note tag message. + if (InvalidLocationNames.size() >= 1) + Out << '\'' << FunctionName << "' call may invalidate " + << InvalidLocationNames[0]; + if (InvalidLocationNames.size() == 2) + Out << ", and " << InvalidLocationNames[1]; + }); +} + void InvalidPtrChecker::EnvpInvalidatingCall(const CallEvent &Call, CheckerContext &C) const { - StringRef FunctionName = Call.getCalleeIdentifier()->getName(); + // This callevent invalidates all previously generated pointers to the + // environment. ProgramStateRef State = C.getState(); - const MemRegion *SymbolicEnvPtrRegion = State->get<EnvPtrRegion>(); - if (!SymbolicEnvPtrRegion) - return; - - State = State->add<InvalidMemoryRegions>(SymbolicEnvPtrRegion); + if (const MemRegion *MainEnvPtr = State->get<MainEnvPtrRegion>()) + State = State->add<InvalidMemoryRegions>(MainEnvPtr); + for (const MemRegion *EnvPtr : State->get<GetenvEnvPtrRegions>()) + State = State->add<InvalidMemoryRegions>(EnvPtr); - const NoteTag *Note = - C.getNoteTag([SymbolicEnvPtrRegion, FunctionName]( - PathSensitiveBugReport &BR, llvm::raw_ostream &Out) { - if (!BR.isInteresting(SymbolicEnvPtrRegion)) - return; - Out << '\'' << FunctionName - << "' call may invalidate the environment parameter of 'main'"; - }); + StringRef FunctionName = Call.getCalleeIdentifier()->getName(); + const NoteTag *InvalidationNote = + createEnvInvalidationNote(C, State, FunctionName); - C.addTransition(State, Note); + C.addTransition(State, InvalidationNote); } void InvalidPtrChecker::postPreviousReturnInvalidatingCall( @@ -124,9 +185,9 @@ void InvalidPtrChecker::postPreviousReturnInvalidatingCall( if (const MemRegion *const *Reg = State->get<PreviousCallResultMap>(FD)) { const MemRegion *PrevReg = *Reg; State = State->add<InvalidMemoryRegions>(PrevReg); - Note = C.getNoteTag([PrevReg, FD](PathSensitiveBugReport &BR, - llvm::raw_ostream &Out) { - if (!BR.isInteresting(PrevReg)) + Note = C.getNoteTag([this, PrevReg, FD](PathSensitiveBugReport &BR, + llvm::raw_ostream &Out) { + if (!BR.isInteresting(PrevReg) || &BR.getBugType() != &InvalidPtrBugType) return; Out << '\''; FD->getNameForDiagnostic(Out, FD->getASTContext().getLangOpts(), true); @@ -146,16 +207,15 @@ void InvalidPtrChecker::postPreviousReturnInvalidatingCall( // Remember to this region. const auto *SymRegOfRetVal = cast<SymbolicRegion>(RetVal.getAsRegion()); - const MemRegion *MR = - const_cast<MemRegion *>(SymRegOfRetVal->getBaseRegion()); + const MemRegion *MR = SymRegOfRetVal->getBaseRegion(); State = State->set<PreviousCallResultMap>(FD, MR); ExplodedNode *Node = C.addTransition(State, Note); - const NoteTag *PreviousCallNote = - C.getNoteTag([MR](PathSensitiveBugReport &BR, llvm::raw_ostream &Out) { - if (!BR.isInteresting(MR)) + const NoteTag *PreviousCallNote = C.getNoteTag( + [this, MR](PathSensitiveBugReport &BR, llvm::raw_ostream &Out) { + if (!BR.isInteresting(MR) || &BR.getBugType() != &InvalidPtrBugType) return; - Out << '\'' << "'previous function call was here" << '\''; + Out << "previous function call was here"; }); C.addTransition(State, Node, PreviousCallNote); @@ -185,6 +245,18 @@ static const MemRegion *findInvalidatedSymbolicBase(ProgramStateRef State, // function call as an argument. void InvalidPtrChecker::checkPostCall(const CallEvent &Call, CheckerContext &C) const { + + ProgramStateRef State = C.getState(); + + // Model 'getenv' calls + if (GetEnvCall.matches(Call)) { + const MemRegion *Region = Call.getReturnValue().getAsRegion(); + if (Region) { + State = State->add<GetenvEnvPtrRegions>(Region); + C.addTransition(State); + } + } + // Check if function invalidates 'envp' argument of 'main' if (const auto *Handler = EnvpInvalidatingFunctions.lookup(Call)) (this->**Handler)(Call, C); @@ -193,14 +265,16 @@ void InvalidPtrChecker::checkPostCall(const CallEvent &Call, if (const auto *Handler = PreviousCallInvalidatingFunctions.lookup(Call)) (this->**Handler)(Call, C); + // If pedantic mode is on, regard 'getenv' calls invalidating as well + if (InvalidatingGetEnv && GetEnvCall.matches(Call)) + postPreviousReturnInvalidatingCall(Call, C); + // Check if one of the arguments of the function call is invalidated // If call was inlined, don't report invalidated argument if (C.wasInlined) return; - ProgramStateRef State = C.getState(); - for (unsigned I = 0, NumArgs = Call.getNumArgs(); I < NumArgs; ++I) { if (const auto *SR = dyn_cast_or_null<SymbolicRegion>( @@ -218,8 +292,8 @@ void InvalidPtrChecker::checkPostCall(const CallEvent &Call, C.getASTContext().getPrintingPolicy()); Out << "' in a function call"; - auto Report = - std::make_unique<PathSensitiveBugReport>(BT, Out.str(), ErrorNode); + auto Report = std::make_unique<PathSensitiveBugReport>( + InvalidPtrBugType, Out.str(), ErrorNode); Report->markInteresting(InvalidatedSymbolicBase); Report->addRange(Call.getArgSourceRange(I)); C.emitReport(std::move(Report)); @@ -243,7 +317,7 @@ void InvalidPtrChecker::checkBeginFunction(CheckerContext &C) const { // Save the memory region pointed by the environment pointer parameter of // 'main'. - C.addTransition(State->set<EnvPtrRegion>(EnvpReg)); + C.addTransition(State->set<MainEnvPtrRegion>(EnvpReg)); } // Check if invalidated region is being dereferenced. @@ -262,13 +336,16 @@ void InvalidPtrChecker::checkLocation(SVal Loc, bool isLoad, const Stmt *S, return; auto Report = std::make_unique<PathSensitiveBugReport>( - BT, "dereferencing an invalid pointer", ErrorNode); + InvalidPtrBugType, "dereferencing an invalid pointer", ErrorNode); Report->markInteresting(InvalidatedSymbolicBase); C.emitReport(std::move(Report)); } void ento::registerInvalidPtrChecker(CheckerManager &Mgr) { - Mgr.registerChecker<InvalidPtrChecker>(); + auto *Checker = Mgr.registerChecker<InvalidPtrChecker>(); + Checker->InvalidatingGetEnv = + Mgr.getAnalyzerOptions().getCheckerBooleanOption(Checker, + "InvalidatingGetEnv"); } bool ento::shouldRegisterInvalidPtrChecker(const CheckerManager &) { diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp index 5924f6a671c2..5c10e757244d 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp @@ -272,7 +272,7 @@ BasicValueFactory::evalAPSInt(BinaryOperator::Opcode Op, // FIXME: This logic should probably go higher up, where we can // test these conditions symbolically. - if (V2.isSigned() && V2.isNegative()) + if (V2.isNegative() || V2.getBitWidth() > 64) return nullptr; uint64_t Amt = V2.getZExtValue(); @@ -280,14 +280,6 @@ BasicValueFactory::evalAPSInt(BinaryOperator::Opcode Op, if (Amt >= V1.getBitWidth()) return nullptr; - if (!Ctx.getLangOpts().CPlusPlus20) { - if (V1.isSigned() && V1.isNegative()) - return nullptr; - - if (V1.isSigned() && Amt > V1.countl_zero()) - return nullptr; - } - return &getValue( V1.operator<<( (unsigned) Amt )); } @@ -295,7 +287,7 @@ BasicValueFactory::evalAPSInt(BinaryOperator::Opcode Op, // FIXME: This logic should probably go higher up, where we can // test these conditions symbolically. - if (V2.isSigned() && V2.isNegative()) + if (V2.isNegative() || V2.getBitWidth() > 64) return nullptr; uint64_t Amt = V2.getZExtValue(); diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BugReporter.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BugReporter.cpp index dc9820b61f1f..f3e0a5f9f314 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BugReporter.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BugReporter.cpp @@ -12,12 +12,15 @@ //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/AST/ASTTypeTraits.h" +#include "clang/AST/Attr.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclBase.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/ParentMap.h" +#include "clang/AST/ParentMapContext.h" #include "clang/AST/Stmt.h" #include "clang/AST/StmtCXX.h" #include "clang/AST/StmtObjC.h" @@ -2099,8 +2102,6 @@ PathDiagnosticBuilder::generate(const PathDiagnosticConsumer *PDC) const { void BugType::anchor() {} -void BuiltinBug::anchor() {} - //===----------------------------------------------------------------------===// // Methods for BugReport and subclasses. //===----------------------------------------------------------------------===// @@ -2141,15 +2142,14 @@ PathSensitiveBugReport::PathSensitiveBugReport( "checkers to emit warnings, because checkers should depend on " "*modeling*, not *diagnostics*."); - assert( - (bt.getCheckerName().startswith("debug") || - !isHidden(ErrorNode->getState() - ->getAnalysisManager() - .getCheckerManager() - ->getCheckerRegistryData(), - bt.getCheckerName())) && - "Hidden checkers musn't emit diagnostics as they are by definition " - "non-user facing!"); + assert((bt.getCheckerName().starts_with("debug") || + !isHidden(ErrorNode->getState() + ->getAnalysisManager() + .getCheckerManager() + ->getCheckerRegistryData(), + bt.getCheckerName())) && + "Hidden checkers musn't emit diagnostics as they are by definition " + "non-user facing!"); } void PathSensitiveBugReport::addVisitor( @@ -2427,6 +2427,12 @@ PathSensitiveBugReport::getLocation() const { } if (S) { + // Attributed statements usually have corrupted begin locations, + // it's OK to ignore attributes for our purposes and deal with + // the actual annotated statement. + if (const auto *AS = dyn_cast<AttributedStmt>(S)) + S = AS->getSubStmt(); + // For member expressions, return the location of the '.' or '->'. if (const auto *ME = dyn_cast<MemberExpr>(S)) return PathDiagnosticLocation::createMemberLoc(ME, SM); @@ -2899,6 +2905,10 @@ void BugReporter::emitReport(std::unique_ptr<BugReport> R) { if (!ValidSourceLoc) return; + // If the user asked to suppress this report, we should skip it. + if (UserSuppressions.isSuppressed(*R)) + return; + // Compute the bug report's hash to determine its equivalence class. llvm::FoldingSetNodeID ID; R->Profile(ID); @@ -3066,8 +3076,7 @@ void BugReporter::FlushReport(BugReportEquivClass& EQ) { // See whether we need to silence the checker/package. for (const std::string &CheckerOrPackage : getAnalyzerOptions().SilencedCheckersAndPackages) { - if (report->getBugType().getCheckerName().startswith( - CheckerOrPackage)) + if (report->getBugType().getCheckerName().starts_with(CheckerOrPackage)) return; } diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp index 42d03f67510c..2f9965036b9e 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp @@ -132,6 +132,16 @@ const Expr *bugreporter::getDerefExpr(const Stmt *S) { } // Pattern match for a few useful cases: a[0], p->f, *p etc. else if (const auto *ME = dyn_cast<MemberExpr>(E)) { + // This handles the case when the dereferencing of a member reference + // happens. This is needed, because the AST for dereferencing a + // member reference looks like the following: + // |-MemberExpr + // `-DeclRefExpr + // Without this special case the notes would refer to the whole object + // (struct, class or union variable) instead of just the relevant member. + + if (ME->getMemberDecl()->getType()->isReferenceType()) + break; E = ME->getBase(); } else if (const auto *IvarRef = dyn_cast<ObjCIvarRefExpr>(E)) { E = IvarRef->getBase(); @@ -157,26 +167,42 @@ const Expr *bugreporter::getDerefExpr(const Stmt *S) { return E; } +static const VarDecl *getVarDeclForExpression(const Expr *E) { + if (const auto *DR = dyn_cast<DeclRefExpr>(E)) + return dyn_cast<VarDecl>(DR->getDecl()); + return nullptr; +} + static const MemRegion * getLocationRegionIfReference(const Expr *E, const ExplodedNode *N, bool LookingForReference = true) { - if (const auto *DR = dyn_cast<DeclRefExpr>(E)) { - if (const auto *VD = dyn_cast<VarDecl>(DR->getDecl())) { - if (LookingForReference && !VD->getType()->isReferenceType()) - return nullptr; - return N->getState() - ->getLValue(VD, N->getLocationContext()) - .getAsRegion(); + if (const auto *ME = dyn_cast<MemberExpr>(E)) { + // This handles null references from FieldRegions, for example: + // struct Wrapper { int &ref; }; + // Wrapper w = { *(int *)0 }; + // w.ref = 1; + const Expr *Base = ME->getBase(); + const VarDecl *VD = getVarDeclForExpression(Base); + if (!VD) + return nullptr; + + const auto *FD = dyn_cast<FieldDecl>(ME->getMemberDecl()); + if (!FD) + return nullptr; + + if (FD->getType()->isReferenceType()) { + SVal StructSVal = N->getState()->getLValue(VD, N->getLocationContext()); + return N->getState()->getLValue(FD, StructSVal).getAsRegion(); } + return nullptr; } - // FIXME: This does not handle other kinds of null references, - // for example, references from FieldRegions: - // struct Wrapper { int &ref; }; - // Wrapper w = { *(int *)0 }; - // w.ref = 1; - - return nullptr; + const VarDecl *VD = getVarDeclForExpression(E); + if (!VD) + return nullptr; + if (LookingForReference && !VD->getType()->isReferenceType()) + return nullptr; + return N->getState()->getLValue(VD, N->getLocationContext()).getAsRegion(); } /// Comparing internal representations of symbolic values (via @@ -606,7 +632,7 @@ static bool potentiallyWritesIntoIvar(const Decl *Parent, if (const auto *DRE = dyn_cast<DeclRefExpr>(Base)) if (const auto *ID = dyn_cast<ImplicitParamDecl>(DRE->getDecl())) - if (ID->getParameterKind() == ImplicitParamDecl::ObjCSelf) + if (ID->getParameterKind() == ImplicitParamKind::ObjCSelf) return true; return false; @@ -1368,8 +1394,7 @@ static void showBRParamDiagnostics(llvm::raw_svector_ostream &OS, VR->printPretty(OS); } } else if (const auto *ImplParam = dyn_cast<ImplicitParamDecl>(D)) { - if (ImplParam->getParameterKind() == - ImplicitParamDecl::ImplicitParamKind::ObjCSelf) { + if (ImplParam->getParameterKind() == ImplicitParamKind::ObjCSelf) { OS << " via implicit parameter 'self'"; } } @@ -3347,7 +3372,7 @@ void LikelyFalsePositiveSuppressionBRVisitor::finalizeVisitor( FullSourceLoc Loc = BR.getLocation().asLocation(); while (Loc.isMacroID()) { Loc = Loc.getSpellingLoc(); - if (SM.getFilename(Loc).endswith("sys/queue.h")) { + if (SM.getFilename(Loc).ends_with("sys/queue.h")) { BR.markInvalid(getTag(), nullptr); return; } diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BugSuppression.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BugSuppression.cpp new file mode 100644 index 000000000000..b5991e47a538 --- /dev/null +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BugSuppression.cpp @@ -0,0 +1,169 @@ +//===- BugSuppression.cpp - Suppression interface -------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Core/BugReporter/BugSuppression.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" + +using namespace clang; +using namespace ento; + +namespace { + +using Ranges = llvm::SmallVectorImpl<SourceRange>; + +inline bool hasSuppression(const Decl *D) { + // FIXME: Implement diagnostic identifier arguments + // (checker names, "hashtags"). + if (const auto *Suppression = D->getAttr<SuppressAttr>()) + return !Suppression->isGSL() && + (Suppression->diagnosticIdentifiers().empty()); + return false; +} +inline bool hasSuppression(const AttributedStmt *S) { + // FIXME: Implement diagnostic identifier arguments + // (checker names, "hashtags"). + return llvm::any_of(S->getAttrs(), [](const Attr *A) { + const auto *Suppression = dyn_cast<SuppressAttr>(A); + return Suppression && !Suppression->isGSL() && + (Suppression->diagnosticIdentifiers().empty()); + }); +} + +template <class NodeType> inline SourceRange getRange(const NodeType *Node) { + return Node->getSourceRange(); +} +template <> inline SourceRange getRange(const AttributedStmt *S) { + // Begin location for attributed statement node seems to be ALWAYS invalid. + // + // It is unlikely that we ever report any warnings on suppression + // attribute itself, but even if we do, we wouldn't want that warning + // to be suppressed by that same attribute. + // + // Long story short, we can use inner statement and it's not going to break + // anything. + return getRange(S->getSubStmt()); +} + +inline bool isLessOrEqual(SourceLocation LHS, SourceLocation RHS, + const SourceManager &SM) { + // SourceManager::isBeforeInTranslationUnit tests for strict + // inequality, when we need a non-strict comparison (bug + // can be reported directly on the annotated note). + // For this reason, we use the following equivalence: + // + // A <= B <==> !(B < A) + // + return !SM.isBeforeInTranslationUnit(RHS, LHS); +} + +inline bool fullyContains(SourceRange Larger, SourceRange Smaller, + const SourceManager &SM) { + // Essentially this means: + // + // Larger.fullyContains(Smaller) + // + // However, that method has a very trivial implementation and couldn't + // compare regular locations and locations from macro expansions. + // We could've converted everything into regular locations as a solution, + // but the following solution seems to be the most bulletproof. + return isLessOrEqual(Larger.getBegin(), Smaller.getBegin(), SM) && + isLessOrEqual(Smaller.getEnd(), Larger.getEnd(), SM); +} + +class CacheInitializer : public RecursiveASTVisitor<CacheInitializer> { +public: + static void initialize(const Decl *D, Ranges &ToInit) { + CacheInitializer(ToInit).TraverseDecl(const_cast<Decl *>(D)); + } + + bool VisitVarDecl(VarDecl *VD) { + // Bug location could be somewhere in the init value of + // a freshly declared variable. Even though it looks like the + // user applied attribute to a statement, it will apply to a + // variable declaration, and this is where we check for it. + return VisitAttributedNode(VD); + } + + bool VisitAttributedStmt(AttributedStmt *AS) { + // When we apply attributes to statements, it actually creates + // a wrapper statement that only contains attributes and the wrapped + // statement. + return VisitAttributedNode(AS); + } + +private: + template <class NodeType> bool VisitAttributedNode(NodeType *Node) { + if (hasSuppression(Node)) { + // TODO: In the future, when we come up with good stable IDs for checkers + // we can return a list of kinds to ignore, or all if no arguments + // were provided. + addRange(getRange(Node)); + } + // We should keep traversing AST. + return true; + } + + void addRange(SourceRange R) { + if (R.isValid()) { + Result.push_back(R); + } + } + + CacheInitializer(Ranges &R) : Result(R) {} + Ranges &Result; +}; + +} // end anonymous namespace + +// TODO: Introduce stable IDs for checkers and check for those here +// to be more specific. Attribute without arguments should still +// be considered as "suppress all". +// It is already much finer granularity than what we have now +// (i.e. removing the whole function from the analysis). +bool BugSuppression::isSuppressed(const BugReport &R) { + PathDiagnosticLocation Location = R.getLocation(); + PathDiagnosticLocation UniqueingLocation = R.getUniqueingLocation(); + const Decl *DeclWithIssue = R.getDeclWithIssue(); + + return isSuppressed(Location, DeclWithIssue, {}) || + isSuppressed(UniqueingLocation, DeclWithIssue, {}); +} + +bool BugSuppression::isSuppressed(const PathDiagnosticLocation &Location, + const Decl *DeclWithIssue, + DiagnosticIdentifierList Hashtags) { + if (!Location.isValid() || DeclWithIssue == nullptr) + return false; + + // While some warnings are attached to AST nodes (mostly path-sensitive + // checks), others are simply associated with a plain source location + // or range. Figuring out the node based on locations can be tricky, + // so instead, we traverse the whole body of the declaration and gather + // information on ALL suppressions. After that we can simply check if + // any of those suppressions affect the warning in question. + // + // Traversing AST of a function is not a heavy operation, but for + // large functions with a lot of bugs it can make a dent in performance. + // In order to avoid this scenario, we cache traversal results. + auto InsertionResult = CachedSuppressionLocations.insert( + std::make_pair(DeclWithIssue, CachedRanges{})); + Ranges &SuppressionRanges = InsertionResult.first->second; + if (InsertionResult.second) { + // We haven't checked this declaration for suppressions yet! + CacheInitializer::initialize(DeclWithIssue, SuppressionRanges); + } + + SourceRange BugRange = Location.asRange(); + const SourceManager &SM = Location.getManager(); + + return llvm::any_of(SuppressionRanges, + [BugRange, &SM](SourceRange Suppression) { + return fullyContains(Suppression, BugRange, SM); + }); +} diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CallEvent.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CallEvent.cpp index 195940e5e643..0ac1d91b79be 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CallEvent.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CallEvent.cpp @@ -517,6 +517,26 @@ const ConstructionContext *CallEvent::getConstructionContext() const { return nullptr; } +const CallEventRef<> CallEvent::getCaller() const { + const auto *CallLocationContext = this->getLocationContext(); + if (!CallLocationContext || CallLocationContext->inTopFrame()) + return nullptr; + + const auto *CallStackFrameContext = CallLocationContext->getStackFrame(); + if (!CallStackFrameContext) + return nullptr; + + CallEventManager &CEMgr = State->getStateManager().getCallEventManager(); + return CEMgr.getCaller(CallStackFrameContext, State); +} + +bool CallEvent::isCalledFromSystemHeader() const { + if (const CallEventRef<> Caller = getCaller()) + return Caller->isInSystemHeader(); + + return false; +} + std::optional<SVal> CallEvent::getReturnValueUnderConstruction() const { const auto *CC = getConstructionContext(); if (!CC) @@ -639,17 +659,17 @@ bool AnyFunctionCall::argumentsMayEscape() const { // - CoreFoundation functions that end with "NoCopy" can free a passed-in // buffer even if it is const. - if (FName.endswith("NoCopy")) + if (FName.ends_with("NoCopy")) return true; // - NSXXInsertXX, for example NSMapInsertIfAbsent, since they can // be deallocated by NSMapRemove. - if (FName.startswith("NS") && FName.contains("Insert")) + if (FName.starts_with("NS") && FName.contains("Insert")) return true; // - Many CF containers allow objects to escape through custom // allocators/deallocators upon container construction. (PR12101) - if (FName.startswith("CF") || FName.startswith("CG")) { + if (FName.starts_with("CF") || FName.starts_with("CG")) { return StrInStrNoCase(FName, "InsertValue") != StringRef::npos || StrInStrNoCase(FName, "AddValue") != StringRef::npos || StrInStrNoCase(FName, "SetValue") != StringRef::npos || @@ -715,10 +735,14 @@ void CXXInstanceCall::getExtraInvalidatedValues( SVal CXXInstanceCall::getCXXThisVal() const { const Expr *Base = getCXXThisExpr(); // FIXME: This doesn't handle an overloaded ->* operator. - if (!Base) - return UnknownVal(); + SVal ThisVal = Base ? getSVal(Base) : UnknownVal(); + + if (isa<NonLoc>(ThisVal)) { + SValBuilder &SVB = getState()->getStateManager().getSValBuilder(); + QualType OriginalTy = ThisVal.getType(SVB.getContext()); + return SVB.evalCast(ThisVal, Base->getType(), OriginalTy); + } - SVal ThisVal = getSVal(Base); assert(ThisVal.isUnknownOrUndef() || isa<Loc>(ThisVal)); return ThisVal; } @@ -765,8 +789,9 @@ RuntimeDefinition CXXInstanceCall::getRuntimeDefinition() const { // the static type. However, because we currently don't update // DynamicTypeInfo when an object is cast, we can't actually be sure the // DynamicTypeInfo is up to date. This assert should be re-enabled once - // this is fixed. <rdar://problem/12287087> - //assert(!MD->getParent()->isDerivedFrom(RD) && "Bad DynamicTypeInfo"); + // this is fixed. + // + // assert(!MD->getParent()->isDerivedFrom(RD) && "Bad DynamicTypeInfo"); return {}; } diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CheckerContext.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CheckerContext.cpp index c25165cce128..d6d4cec9dd3d 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CheckerContext.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CheckerContext.cpp @@ -105,10 +105,11 @@ bool CheckerContext::isCLibraryFunction(const FunctionDecl *FD, if (FName.equals(Name)) return true; - if (FName.startswith("__inline") && FName.contains(Name)) + if (FName.starts_with("__inline") && FName.contains(Name)) return true; - if (FName.startswith("__") && FName.endswith("_chk") && FName.contains(Name)) + if (FName.starts_with("__") && FName.ends_with("_chk") && + FName.contains(Name)) return true; return false; diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CheckerRegistryData.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CheckerRegistryData.cpp index 1b3e8b11549d..b9c6278991f4 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CheckerRegistryData.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CheckerRegistryData.cpp @@ -82,7 +82,7 @@ static constexpr char PackageSeparator = '.'; static bool isInPackage(const CheckerInfo &Checker, StringRef PackageName) { // Does the checker's full name have the package as a prefix? - if (!Checker.FullName.startswith(PackageName)) + if (!Checker.FullName.starts_with(PackageName)) return false; // Is the package actually just the name of a specific checker? @@ -158,7 +158,7 @@ void CheckerRegistryData::printCheckerWithDescList( continue; } - if (Checker.FullName.startswith("alpha")) { + if (Checker.FullName.starts_with("alpha")) { if (AnOpts.ShowCheckerHelpAlpha) Print(Out, Checker, ("(Enable only for development!) " + Checker.Desc).str()); @@ -228,7 +228,7 @@ void CheckerRegistryData::printCheckerOptionList(const AnalyzerOptions &AnOpts, } if (Option.DevelopmentStatus == "alpha" || - Entry.first.startswith("alpha")) { + Entry.first.starts_with("alpha")) { if (AnOpts.ShowCheckerOptionAlphaList) Print(Out, FullOption, llvm::Twine("(Enable only for development!) " + Desc).str()); diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ConstraintManager.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ConstraintManager.cpp index 9ef3455a110a..c0b3f346b654 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ConstraintManager.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ConstraintManager.cpp @@ -91,7 +91,7 @@ ConstraintManager::assumeDualImpl(ProgramStateRef &State, ConstraintManager::ProgramStatePair ConstraintManager::assumeDual(ProgramStateRef State, DefinedSVal Cond) { - auto AssumeFun = [&](bool Assumption) { + auto AssumeFun = [&, Cond](bool Assumption) { return assumeInternal(State, Cond, Assumption); }; return assumeDualImpl(State, AssumeFun); diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp index 386a34f6cd4a..d3499e7a917d 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp @@ -503,8 +503,8 @@ void CoreEngine::HandleVirtualBaseBranch(const CFGBlock *B, if (const auto *CallerCtor = dyn_cast_or_null<CXXConstructExpr>( LCtx->getStackFrame()->getCallSite())) { switch (CallerCtor->getConstructionKind()) { - case CXXConstructExpr::CK_NonVirtualBase: - case CXXConstructExpr::CK_VirtualBase: { + case CXXConstructionKind::NonVirtualBase: + case CXXConstructionKind::VirtualBase: { BlockEdge Loc(B, *B->succ_begin(), LCtx); HandleBlockEdge(Loc, Pred); return; diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/DynamicExtent.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/DynamicExtent.cpp index 6a86536492cd..6cf06413b537 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/DynamicExtent.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/DynamicExtent.cpp @@ -30,7 +30,9 @@ DefinedOrUnknownSVal getDynamicExtent(ProgramStateRef State, MR = MR->StripCasts(); if (const DefinedOrUnknownSVal *Size = State->get<DynamicExtentMap>(MR)) - return *Size; + if (auto SSize = + SVB.convertToArrayIndex(*Size).getAs<DefinedOrUnknownSVal>()) + return *SSize; return MR->getMemRegionManager().getStaticSize(MR, SVB); } @@ -40,6 +42,32 @@ DefinedOrUnknownSVal getElementExtent(QualType Ty, SValBuilder &SVB) { SVB.getArrayIndexType()); } +static DefinedOrUnknownSVal getConstantArrayElementCount(SValBuilder &SVB, + const MemRegion *MR) { + MR = MR->StripCasts(); + + const auto *TVR = MR->getAs<TypedValueRegion>(); + if (!TVR) + return UnknownVal(); + + if (const ConstantArrayType *CAT = + SVB.getContext().getAsConstantArrayType(TVR->getValueType())) + return SVB.makeIntVal(CAT->getSize(), /* isUnsigned = */ false); + + return UnknownVal(); +} + +static DefinedOrUnknownSVal +getDynamicElementCount(ProgramStateRef State, SVal Size, + DefinedOrUnknownSVal ElementSize) { + SValBuilder &SVB = State->getStateManager().getSValBuilder(); + + auto ElementCount = + SVB.evalBinOp(State, BO_Div, Size, ElementSize, SVB.getArrayIndexType()) + .getAs<DefinedOrUnknownSVal>(); + return ElementCount.value_or(UnknownVal()); +} + DefinedOrUnknownSVal getDynamicElementCount(ProgramStateRef State, const MemRegion *MR, SValBuilder &SVB, @@ -47,17 +75,16 @@ DefinedOrUnknownSVal getDynamicElementCount(ProgramStateRef State, assert(MR != nullptr && "Not-null region expected"); MR = MR->StripCasts(); - DefinedOrUnknownSVal Size = getDynamicExtent(State, MR, SVB); - SVal ElementSize = getElementExtent(ElementTy, SVB); + DefinedOrUnknownSVal ElementSize = getElementExtent(ElementTy, SVB); + if (ElementSize.isZeroConstant()) + return getConstantArrayElementCount(SVB, MR); - SVal ElementCount = - SVB.evalBinOp(State, BO_Div, Size, ElementSize, SVB.getArrayIndexType()); - - return ElementCount.castAs<DefinedOrUnknownSVal>(); + return getDynamicElementCount(State, getDynamicExtent(State, MR, SVB), + ElementSize); } SVal getDynamicExtentWithOffset(ProgramStateRef State, SVal BufV) { - SValBuilder &SvalBuilder = State->getStateManager().getSValBuilder(); + SValBuilder &SVB = State->getStateManager().getSValBuilder(); const MemRegion *MRegion = BufV.getAsRegion(); if (!MRegion) return UnknownVal(); @@ -68,15 +95,28 @@ SVal getDynamicExtentWithOffset(ProgramStateRef State, SVal BufV) { if (!BaseRegion) return UnknownVal(); - NonLoc OffsetInBytes = SvalBuilder.makeArrayIndex( - Offset.getOffset() / - MRegion->getMemRegionManager().getContext().getCharWidth()); - DefinedOrUnknownSVal ExtentInBytes = - getDynamicExtent(State, BaseRegion, SvalBuilder); + NonLoc OffsetInChars = + SVB.makeArrayIndex(Offset.getOffset() / SVB.getContext().getCharWidth()); + DefinedOrUnknownSVal ExtentInBytes = getDynamicExtent(State, BaseRegion, SVB); + + return SVB.evalBinOp(State, BinaryOperator::Opcode::BO_Sub, ExtentInBytes, + OffsetInChars, SVB.getArrayIndexType()); +} + +DefinedOrUnknownSVal getDynamicElementCountWithOffset(ProgramStateRef State, + SVal BufV, + QualType ElementTy) { + const MemRegion *MR = BufV.getAsRegion(); + if (!MR) + return UnknownVal(); + + SValBuilder &SVB = State->getStateManager().getSValBuilder(); + DefinedOrUnknownSVal ElementSize = getElementExtent(ElementTy, SVB); + if (ElementSize.isZeroConstant()) + return getConstantArrayElementCount(SVB, MR); - return SvalBuilder.evalBinOp(State, BinaryOperator::Opcode::BO_Sub, - ExtentInBytes, OffsetInBytes, - SvalBuilder.getArrayIndexType()); + return getDynamicElementCount(State, getDynamicExtentWithOffset(State, BufV), + ElementSize); } ProgramStateRef setDynamicExtent(ProgramStateRef State, const MemRegion *MR, diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index 144f034a9dfe..24e91a22fd68 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -299,7 +299,7 @@ ProgramStateRef ExprEngine::getInitialState(const LocationContext *InitLoc) { } if (const auto *MD = dyn_cast<CXXMethodDecl>(D)) { - if (!MD->isStatic()) { + if (MD->isImplicitObjectMemberFunction()) { // Precondition: 'this' is always non-null upon entry to the // top-level function. This is our starting assumption for // analyzing an "open" program. @@ -993,6 +993,7 @@ void ExprEngine::processCFGElement(const CFGElement E, ExplodedNode *Pred, ProcessLoopExit(E.castAs<CFGLoopExit>().getLoopStmt(), Pred); return; case CFGElement::LifetimeEnds: + case CFGElement::CleanupFunction: case CFGElement::ScopeBegin: case CFGElement::ScopeEnd: return; @@ -1221,6 +1222,14 @@ void ExprEngine::ProcessInitializer(const CFGInitializer CFGInit, PostInitializer PP(BMI, FieldLoc.getAsRegion(), stackFrame); evalBind(Tmp, Init, Pred, FieldLoc, InitVal, /*isInit=*/true, &PP); } + } else if (BMI->isBaseInitializer() && isa<InitListExpr>(Init)) { + // When the base class is initialized with an initialization list and the + // base class does not have a ctor, there will not be a CXXConstructExpr to + // initialize the base region. Hence, we need to make the bind for it. + SVal BaseLoc = getStoreManager().evalDerivedToBase( + thisVal, QualType(BMI->getBaseClass(), 0), BMI->isBaseVirtual()); + SVal InitVal = State->getSVal(Init, stackFrame); + evalBind(Tmp, Init, Pred, BaseLoc, InitVal, /*isInit=*/true); } else { assert(BMI->isBaseInitializer() || BMI->isDelegatingInitializer()); Tmp.insert(Pred); @@ -1746,6 +1755,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::OMPForSimdDirectiveClass: case Stmt::OMPSectionsDirectiveClass: case Stmt::OMPSectionDirectiveClass: + case Stmt::OMPScopeDirectiveClass: case Stmt::OMPSingleDirectiveClass: case Stmt::OMPMasterDirectiveClass: case Stmt::OMPCriticalDirectiveClass: @@ -2112,7 +2122,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, // valid region. const Decl *Callee = OCE->getCalleeDecl(); if (const auto *MD = dyn_cast_or_null<CXXMethodDecl>(Callee)) { - if (MD->isInstance()) { + if (MD->isImplicitObjectMemberFunction()) { ProgramStateRef State = Pred->getState(); const LocationContext *LCtx = Pred->getLocationContext(); ProgramStateRef NewState = @@ -2507,7 +2517,7 @@ void ExprEngine::processCFGBlockEntrance(const BlockEdge &L, if (BlockCount == AMgr.options.maxBlockVisitOnPath - 1 && AMgr.options.ShouldWidenLoops) { const Stmt *Term = nodeBuilder.getContext().getBlock()->getTerminatorStmt(); - if (!isa_and_nonnull<ForStmt, WhileStmt, DoStmt>(Term)) + if (!isa_and_nonnull<ForStmt, WhileStmt, DoStmt, CXXForRangeStmt>(Term)) return; // Widen. const LocationContext *LCtx = Pred->getLocationContext(); @@ -3355,7 +3365,7 @@ void ExprEngine::VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred, // Handle C++ method calls. if (const auto *MD = dyn_cast<CXXMethodDecl>(Member)) { - if (MD->isInstance()) + if (MD->isImplicitObjectMemberFunction()) state = createTemporaryRegionIfNeeded(state, LCtx, BaseExpr); SVal MDVal = svalBuilder.getFunctionPointer(MD); diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp index 2a47116db55a..7e431f7e598c 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp @@ -264,7 +264,8 @@ ProgramStateRef ExprEngine::handleLValueBitCast( } // Delegate to SValBuilder to process. SVal OrigV = state->getSVal(Ex, LCtx); - SVal V = svalBuilder.evalCast(OrigV, T, ExTy); + SVal SimplifiedOrigV = svalBuilder.simplifySVal(state, OrigV); + SVal V = svalBuilder.evalCast(SimplifiedOrigV, T, ExTy); // Negate the result if we're treating the boolean as a signed i1 if (CastE->getCastKind() == CK_BooleanToSignedIntegral && V.isValid()) V = svalBuilder.evalMinus(V.castAs<NonLoc>()); diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp index 7ee7c1394a67..504fd7f05e0f 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -612,10 +612,10 @@ void ExprEngine::handleConstructor(const Expr *E, assert(C || getCurrentCFGElement().getAs<CFGStmt>()); const ConstructionContext *CC = C ? C->getConstructionContext() : nullptr; - const CXXConstructExpr::ConstructionKind CK = + const CXXConstructionKind CK = CE ? CE->getConstructionKind() : CIE->getConstructionKind(); switch (CK) { - case CXXConstructExpr::CK_Complete: { + case CXXConstructionKind::Complete: { // Inherited constructors are always base class constructors. assert(CE && !CIE && "A complete constructor is inherited?!"); @@ -666,21 +666,21 @@ void ExprEngine::handleConstructor(const Expr *E, CE, State, currBldrCtx, LCtx, CC, CallOpts, Idx); break; } - case CXXConstructExpr::CK_VirtualBase: { + case CXXConstructionKind::VirtualBase: { // Make sure we are not calling virtual base class initializers twice. // Only the most-derived object should initialize virtual base classes. const auto *OuterCtor = dyn_cast_or_null<CXXConstructExpr>( LCtx->getStackFrame()->getCallSite()); assert( (!OuterCtor || - OuterCtor->getConstructionKind() == CXXConstructExpr::CK_Complete || - OuterCtor->getConstructionKind() == CXXConstructExpr::CK_Delegating) && + OuterCtor->getConstructionKind() == CXXConstructionKind::Complete || + OuterCtor->getConstructionKind() == CXXConstructionKind::Delegating) && ("This virtual base should have already been initialized by " "the most derived class!")); (void)OuterCtor; [[fallthrough]]; } - case CXXConstructExpr::CK_NonVirtualBase: + case CXXConstructionKind::NonVirtualBase: // In C++17, classes with non-virtual bases may be aggregates, so they would // be initialized as aggregates without a constructor call, so we may have // a base class constructed directly into an initializer list without @@ -699,17 +699,17 @@ void ExprEngine::handleConstructor(const Expr *E, break; } [[fallthrough]]; - case CXXConstructExpr::CK_Delegating: { + case CXXConstructionKind::Delegating: { const CXXMethodDecl *CurCtor = cast<CXXMethodDecl>(LCtx->getDecl()); Loc ThisPtr = getSValBuilder().getCXXThis(CurCtor, LCtx->getStackFrame()); SVal ThisVal = State->getSVal(ThisPtr); - if (CK == CXXConstructExpr::CK_Delegating) { + if (CK == CXXConstructionKind::Delegating) { Target = ThisVal; } else { // Cast to the base type. - bool IsVirtual = (CK == CXXConstructExpr::CK_VirtualBase); + bool IsVirtual = (CK == CXXConstructionKind::VirtualBase); SVal BaseVal = getStoreManager().evalDerivedToBase(ThisVal, E->getType(), IsVirtual); Target = BaseVal; diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp index b987ce278936..4755b6bfa6dc 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -888,7 +888,7 @@ ExprEngine::mayInlineCallKind(const CallEvent &Call, const ExplodedNode *Pred, if (!Opts.mayInlineCXXMemberFunction(CIMK_Destructors)) return CIP_DisallowedAlways; - if (CtorExpr->getConstructionKind() == CXXConstructExpr::CK_Complete) { + if (CtorExpr->getConstructionKind() == CXXConstructionKind::Complete) { // If we don't handle temporary destructors, we shouldn't inline // their constructors. if (CallOpts.IsTemporaryCtorOrDtor && diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp index 8072531ef6fd..f075df3ab5e4 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp @@ -167,19 +167,32 @@ void ExprEngine::VisitObjCMessage(const ObjCMessageExpr *ME, // intentionally drops coverage in order to prevent false alarms // in the following scenario: // - // id result = [o someMethod] - // if (result) { - // if (!o) { - // // <-- This program point should be unreachable because if o is nil - // // it must the case that result is nil as well. + // id result = [o someMethod] + // if (result) { + // if (!o) { + // // <-- This program point should be unreachable because if o is nil + // // it must the case that result is nil as well. + // } // } - // } // - // We could avoid dropping coverage by performing an explicit case split - // on each method call -- but this would get very expensive. An alternative - // would be to introduce lazy constraints. - // FIXME: This ignores many potential bugs (<rdar://problem/11733396>). - // Revisit once we have lazier constraints. + // However, it also loses coverage of the nil path prematurely, + // leading to missed reports. + // + // It's possible to handle this by performing a state split on every call: + // explore the state where the receiver is non-nil, and independently + // explore the state where it's nil. But this is not only slow, but + // completely unwarranted. The mere presence of the message syntax in the code + // isn't sufficient evidence that nil is a realistic possibility. + // + // An ideal solution would be to add the following constraint that captures + // both possibilities without splitting the state: + // + // ($x == 0) => ($y == 0) (1) + // + // where in our case '$x' is the receiver symbol, '$y' is the returned symbol, + // and '=>' is logical implication. But RangeConstraintManager can't handle + // such constraints yet, so for now we go with a simpler, more restrictive + // constraint: $x != 0, from which (1) follows as a vacuous truth. if (Msg->isInstanceMessage()) { SVal recVal = Msg->getReceiverSVal(); if (!recVal.isUndef()) { diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp index 0fe0c93dc016..69d25120dcd4 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp @@ -112,7 +112,7 @@ public: // Add HTML header/footers to file specified by FID void FinalizeHTML(const PathDiagnostic &D, Rewriter &R, const SourceManager &SMgr, const PathPieces &path, - FileID FID, const FileEntry *Entry, const char *declName); + FileID FID, FileEntryRef Entry, const char *declName); // Rewrite the file specified by FID with HTML formatting. void RewriteFile(Rewriter &R, const PathPieces &path, FileID FID); @@ -326,7 +326,7 @@ void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D, FileID ReportFile = path.back()->getLocation().asLocation().getExpansionLoc().getFileID(); - const FileEntry *Entry = SMgr.getFileEntryForID(ReportFile); + OptionalFileEntryRef Entry = SMgr.getFileEntryRefForID(ReportFile); FileName << llvm::sys::path::filename(Entry->getName()).str() << "-" << declName.c_str() << "-" << offsetDecl << "-"; @@ -396,7 +396,7 @@ std::string HTMLDiagnostics::GenerateHTML(const PathDiagnostic& D, Rewriter &R, os << "<div class=FileNav><a href=\"#File" << (I - 1)->getHashValue() << "\">←</a></div>"; - os << "<h4 class=FileName>" << SMgr.getFileEntryForID(*I)->getName() + os << "<h4 class=FileName>" << SMgr.getFileEntryRefForID(*I)->getName() << "</h4>\n"; // Right nav arrow @@ -429,8 +429,8 @@ std::string HTMLDiagnostics::GenerateHTML(const PathDiagnostic& D, Rewriter &R, // Add CSS, header, and footer. FileID FID = path.back()->getLocation().asLocation().getExpansionLoc().getFileID(); - const FileEntry* Entry = SMgr.getFileEntryForID(FID); - FinalizeHTML(D, R, SMgr, path, FileIDs[0], Entry, declName); + OptionalFileEntryRef Entry = SMgr.getFileEntryRefForID(FID); + FinalizeHTML(D, R, SMgr, path, FileIDs[0], *Entry, declName); std::string file; llvm::raw_string_ostream os(file); @@ -537,16 +537,17 @@ document.addEventListener("DOMContentLoaded", function() { return s; } -void HTMLDiagnostics::FinalizeHTML(const PathDiagnostic& D, Rewriter &R, - const SourceManager& SMgr, const PathPieces& path, FileID FID, - const FileEntry *Entry, const char *declName) { +void HTMLDiagnostics::FinalizeHTML(const PathDiagnostic &D, Rewriter &R, + const SourceManager &SMgr, + const PathPieces &path, FileID FID, + FileEntryRef Entry, const char *declName) { // This is a cludge; basically we want to append either the full // working directory if we have no directory information. This is // a work in progress. llvm::SmallString<0> DirName; - if (llvm::sys::path::is_relative(Entry->getName())) { + if (llvm::sys::path::is_relative(Entry.getName())) { llvm::sys::fs::current_path(DirName); DirName += '/'; } @@ -575,7 +576,7 @@ void HTMLDiagnostics::FinalizeHTML(const PathDiagnostic& D, Rewriter &R, << "<h3>Bug Summary</h3>\n<table class=\"simpletable\">\n" "<tr><td class=\"rowname\">File:</td><td>" << html::EscapeText(DirName) - << html::EscapeText(Entry->getName()) + << html::EscapeText(Entry.getName()) << "</td></tr>\n<tr><td class=\"rowname\">Warning:</td><td>" "<a href=\"#EndPath\">line " << LineNumber @@ -592,11 +593,11 @@ void HTMLDiagnostics::FinalizeHTML(const PathDiagnostic& D, Rewriter &R, P->getLocation().asLocation().getExpansionLineNumber(); int ColumnNumber = P->getLocation().asLocation().getExpansionColumnNumber(); + ++NumExtraPieces; os << "<tr><td class=\"rowname\">Note:</td><td>" << "<a href=\"#Note" << NumExtraPieces << "\">line " << LineNumber << ", column " << ColumnNumber << "</a><br />" << P->getString() << "</td></tr>"; - ++NumExtraPieces; } } @@ -656,9 +657,9 @@ void HTMLDiagnostics::FinalizeHTML(const PathDiagnostic& D, Rewriter &R, if (!BugCategory.empty()) os << "\n<!-- BUGCATEGORY " << BugCategory << " -->\n"; - os << "\n<!-- BUGFILE " << DirName << Entry->getName() << " -->\n"; + os << "\n<!-- BUGFILE " << DirName << Entry.getName() << " -->\n"; - os << "\n<!-- FILENAME " << llvm::sys::path::filename(Entry->getName()) << " -->\n"; + os << "\n<!-- FILENAME " << llvm::sys::path::filename(Entry.getName()) << " -->\n"; os << "\n<!-- FUNCTIONNAME " << declName << " -->\n"; @@ -682,7 +683,7 @@ void HTMLDiagnostics::FinalizeHTML(const PathDiagnostic& D, Rewriter &R, R.InsertTextBefore(SMgr.getLocForStartOfFile(FID), os.str()); } - html::AddHeaderFooterInternalBuiltinCSS(R, FID, Entry->getName()); + html::AddHeaderFooterInternalBuiltinCSS(R, FID, Entry.getName()); } StringRef HTMLDiagnostics::showHelpJavascript() { diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/LoopWidening.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/LoopWidening.cpp index 748c65f578a8..9e4280176062 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/LoopWidening.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/LoopWidening.cpp @@ -35,6 +35,8 @@ static const Expr *getLoopCondition(const Stmt *LoopStmt) { return cast<WhileStmt>(LoopStmt)->getCond(); case Stmt::DoStmtClass: return cast<DoStmt>(LoopStmt)->getCond(); + case Stmt::CXXForRangeStmtClass: + return cast<CXXForRangeStmt>(LoopStmt)->getCond(); } } @@ -45,7 +47,7 @@ ProgramStateRef getWidenedLoopState(ProgramStateRef PrevState, const LocationContext *LCtx, unsigned BlockCount, const Stmt *LoopStmt) { - assert((isa<ForStmt, WhileStmt, DoStmt>(LoopStmt))); + assert((isa<ForStmt, WhileStmt, DoStmt, CXXForRangeStmt>(LoopStmt))); // Invalidate values in the current state. // TODO Make this more conservative by only invalidating values that might @@ -84,7 +86,7 @@ ProgramStateRef getWidenedLoopState(ProgramStateRef PrevState, // pointer should remain unchanged. Ignore static methods, since they do not // have 'this' pointers. const CXXMethodDecl *CXXMD = dyn_cast<CXXMethodDecl>(STC->getDecl()); - if (CXXMD && !CXXMD->isStatic()) { + if (CXXMD && CXXMD->isImplicitObjectMemberFunction()) { const CXXThisRegion *ThisR = MRMgr.getCXXThisRegion(CXXMD->getThisType(), STC); ITraits.setTrait(ThisR, diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp index bdf485364cef..be19a1c118ea 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp @@ -804,7 +804,7 @@ void PlistDiagnostics::FlushDiagnosticsImpl( o << " <key>files</key>\n" " <array>\n"; for (FileID FID : Fids) - EmitString(o << " ", SM.getFileEntryForID(FID)->getName()) << '\n'; + EmitString(o << " ", SM.getFileEntryRefForID(FID)->getName()) << '\n'; o << " </array>\n"; if (llvm::AreStatisticsEnabled() && DiagOpts.ShouldSerializeStats) { diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp index 5de99384449a..25d066c4652f 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp @@ -1876,6 +1876,12 @@ public: const llvm::APSInt *getSymVal(ProgramStateRef State, SymbolRef Sym) const override; + const llvm::APSInt *getSymMinVal(ProgramStateRef State, + SymbolRef Sym) const override; + + const llvm::APSInt *getSymMaxVal(ProgramStateRef State, + SymbolRef Sym) const override; + ProgramStateRef removeDeadBindings(ProgramStateRef State, SymbolReaper &SymReaper) override; @@ -2863,6 +2869,22 @@ const llvm::APSInt *RangeConstraintManager::getSymVal(ProgramStateRef St, return T ? T->getConcreteValue() : nullptr; } +const llvm::APSInt *RangeConstraintManager::getSymMinVal(ProgramStateRef St, + SymbolRef Sym) const { + const RangeSet *T = getConstraint(St, Sym); + if (!T || T->isEmpty()) + return nullptr; + return &T->getMinValue(); +} + +const llvm::APSInt *RangeConstraintManager::getSymMaxVal(ProgramStateRef St, + SymbolRef Sym) const { + const RangeSet *T = getConstraint(St, Sym); + if (!T || T->isEmpty()) + return nullptr; + return &T->getMaxValue(); +} + //===----------------------------------------------------------------------===// // Remove dead symbols from existing constraints //===----------------------------------------------------------------------===// diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp index 4fe828bdf768..eb9cde5c8918 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp @@ -114,7 +114,7 @@ nonloc::SymbolVal SValBuilder::makeNonLoc(const SymExpr *operand, assert(operand); assert(!Loc::isLocType(toTy)); if (fromTy == toTy) - return operand; + return nonloc::SymbolVal(operand); return nonloc::SymbolVal(SymMgr.getCastSymbol(operand, fromTy, toTy)); } @@ -231,6 +231,14 @@ SValBuilder::getConjuredHeapSymbolVal(const Expr *E, return loc::MemRegionVal(MemMgr.getSymbolicHeapRegion(sym)); } +loc::MemRegionVal SValBuilder::getAllocaRegionVal(const Expr *E, + const LocationContext *LCtx, + unsigned VisitCount) { + const AllocaRegion *R = + getRegionManager().getAllocaRegion(E, VisitCount, LCtx); + return loc::MemRegionVal(R); +} + DefinedSVal SValBuilder::getMetadataSymbolVal(const void *symbolTag, const MemRegion *region, const Expr *expr, QualType type, @@ -275,7 +283,7 @@ DefinedSVal SValBuilder::getMemberPointer(const NamedDecl *ND) { // We don't need to play a similar trick for static member fields // because these are represented as plain VarDecls and not FieldDecls // in the AST. - if (MD->isStatic()) + if (!MD->isImplicitObjectMemberFunction()) return getFunctionPointer(MD); } @@ -446,7 +454,7 @@ SVal SValBuilder::makeSymExprValNN(BinaryOperator::Opcode Op, } SVal SValBuilder::evalMinus(NonLoc X) { - switch (X.getSubKind()) { + switch (X.getKind()) { case nonloc::ConcreteIntKind: return makeIntVal(-X.castAs<nonloc::ConcreteInt>().getValue()); case nonloc::SymbolValKind: @@ -458,7 +466,7 @@ SVal SValBuilder::evalMinus(NonLoc X) { } SVal SValBuilder::evalComplement(NonLoc X) { - switch (X.getSubKind()) { + switch (X.getKind()) { case nonloc::ConcreteIntKind: return makeIntVal(~X.castAs<nonloc::ConcreteInt>().getValue()); case nonloc::SymbolValKind: @@ -598,11 +606,9 @@ SVal SValBuilder::evalIntegralCast(ProgramStateRef state, SVal val, APSIntType ToType(getContext().getTypeSize(castTy), castTy->isUnsignedIntegerType()); llvm::APSInt ToTypeMax = ToType.getMaxValue(); - NonLoc ToTypeMaxVal = - makeIntVal(ToTypeMax.isUnsigned() ? ToTypeMax.getZExtValue() - : ToTypeMax.getSExtValue(), - castTy) - .castAs<NonLoc>(); + + NonLoc ToTypeMaxVal = makeIntVal(ToTypeMax); + // Check the range of the symbol being casted against the maximum value of the // target type. NonLoc FromVal = val.castAs<NonLoc>(); @@ -660,7 +666,7 @@ public: } SVal VisitUndefinedVal(UndefinedVal V) { return V; } SVal VisitUnknownVal(UnknownVal V) { return V; } - SVal VisitLocConcreteInt(loc::ConcreteInt V) { + SVal VisitConcreteInt(loc::ConcreteInt V) { // Pointer to bool. if (CastTy->isBooleanType()) return VB.makeTruthVal(V.getValue().getBoolValue(), CastTy); @@ -682,7 +688,7 @@ public: // Pointer to whatever else. return UnknownVal(); } - SVal VisitLocGotoLabel(loc::GotoLabel V) { + SVal VisitGotoLabel(loc::GotoLabel V) { // Pointer to bool. if (CastTy->isBooleanType()) // Labels are always true. @@ -709,7 +715,7 @@ public: // Pointer to whatever else. return UnknownVal(); } - SVal VisitLocMemRegionVal(loc::MemRegionVal V) { + SVal VisitMemRegionVal(loc::MemRegionVal V) { // Pointer to bool. if (CastTy->isBooleanType()) { const MemRegion *R = V.getRegion(); @@ -854,11 +860,11 @@ public: // necessary for bit-level reasoning as well. return UnknownVal(); } - SVal VisitNonLocCompoundVal(nonloc::CompoundVal V) { + SVal VisitCompoundVal(nonloc::CompoundVal V) { // Compound to whatever. return UnknownVal(); } - SVal VisitNonLocConcreteInt(nonloc::ConcreteInt V) { + SVal VisitConcreteInt(nonloc::ConcreteInt V) { auto CastedValue = [V, this]() { llvm::APSInt Value = V.getValue(); VB.getBasicValueFactory().getAPSIntType(CastTy).apply(Value); @@ -880,11 +886,11 @@ public: // Pointer to whatever else. return UnknownVal(); } - SVal VisitNonLocLazyCompoundVal(nonloc::LazyCompoundVal V) { + SVal VisitLazyCompoundVal(nonloc::LazyCompoundVal V) { // LazyCompound to whatever. return UnknownVal(); } - SVal VisitNonLocLocAsInteger(nonloc::LocAsInteger V) { + SVal VisitLocAsInteger(nonloc::LocAsInteger V) { Loc L = V.getLoc(); // Pointer as integer to bool. @@ -906,7 +912,7 @@ public: const MemRegion *R = L.getAsRegion(); if (!IsUnknownOriginalType && R) { if (CastTy->isIntegralOrEnumerationType()) - return VisitLocMemRegionVal(loc::MemRegionVal(R)); + return VisitMemRegionVal(loc::MemRegionVal(R)); if (Loc::isLocType(CastTy)) { assert(Loc::isLocType(OriginalTy) || OriginalTy->isFunctionType() || @@ -920,7 +926,7 @@ public: } else { if (Loc::isLocType(CastTy)) { if (IsUnknownOriginalType) - return VisitLocMemRegionVal(loc::MemRegionVal(R)); + return VisitMemRegionVal(loc::MemRegionVal(R)); return L; } @@ -945,7 +951,7 @@ public: // Pointer as integer to whatever else. return UnknownVal(); } - SVal VisitNonLocSymbolVal(nonloc::SymbolVal V) { + SVal VisitSymbolVal(nonloc::SymbolVal V) { SymbolRef SE = V.getSymbol(); const bool IsUnknownOriginalType = OriginalTy.isNull(); @@ -982,10 +988,15 @@ public: return VB.makeNonLoc(SE, T, CastTy); } + // FIXME: We should be able to cast NonLoc -> Loc + // (when Loc::isLocType(CastTy) is true) + // But it's hard to do as SymbolicRegions can't refer to SymbolCasts holding + // generic SymExprs. Check the commit message for the details. + // Symbol to pointer and whatever else. return UnknownVal(); } - SVal VisitNonLocPointerToMember(nonloc::PointerToMember V) { + SVal VisitPointerToMember(nonloc::PointerToMember V) { // Member pointer to whatever. return V; } diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SVals.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SVals.cpp index 2a43a01ff886..0e1351215bb4 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SVals.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SVals.cpp @@ -136,10 +136,10 @@ private: public: TypeRetrievingVisitor(const ASTContext &Context) : Context(Context) {} - QualType VisitLocMemRegionVal(loc::MemRegionVal MRV) { + QualType VisitMemRegionVal(loc::MemRegionVal MRV) { return Visit(MRV.getRegion()); } - QualType VisitLocGotoLabel(loc::GotoLabel GL) { + QualType VisitGotoLabel(loc::GotoLabel GL) { return QualType{Context.VoidPtrTy}; } template <class ConcreteInt> QualType VisitConcreteInt(ConcreteInt CI) { @@ -148,13 +148,7 @@ public: return Context.BoolTy; return Context.getIntTypeForBitwidth(Value.getBitWidth(), Value.isSigned()); } - QualType VisitLocConcreteInt(loc::ConcreteInt CI) { - return VisitConcreteInt(CI); - } - QualType VisitNonLocConcreteInt(nonloc::ConcreteInt CI) { - return VisitConcreteInt(CI); - } - QualType VisitNonLocLocAsInteger(nonloc::LocAsInteger LI) { + QualType VisitLocAsInteger(nonloc::LocAsInteger LI) { QualType NestedType = Visit(LI.getLoc()); if (NestedType.isNull()) return NestedType; @@ -162,13 +156,13 @@ public: return Context.getIntTypeForBitwidth(LI.getNumBits(), NestedType->isSignedIntegerType()); } - QualType VisitNonLocCompoundVal(nonloc::CompoundVal CV) { + QualType VisitCompoundVal(nonloc::CompoundVal CV) { return CV.getValue()->getType(); } - QualType VisitNonLocLazyCompoundVal(nonloc::LazyCompoundVal LCV) { + QualType VisitLazyCompoundVal(nonloc::LazyCompoundVal LCV) { return LCV.getRegion()->getValueType(); } - QualType VisitNonLocSymbolVal(nonloc::SymbolVal SV) { + QualType VisitSymbolVal(nonloc::SymbolVal SV) { return Visit(SV.getSymbol()); } QualType VisitSymbolicRegion(const SymbolicRegion *SR) { @@ -281,30 +275,33 @@ void SVal::printJson(raw_ostream &Out, bool AddQuotes) const { } void SVal::dumpToStream(raw_ostream &os) const { - switch (getBaseKind()) { - case UnknownValKind: - os << "Unknown"; - break; - case NonLocKind: - castAs<NonLoc>().dumpToStream(os); - break; - case LocKind: - castAs<Loc>().dumpToStream(os); - break; - case UndefinedValKind: - os << "Undefined"; - break; + if (isUndef()) { + os << "Undefined"; + return; + } + if (isUnknown()) { + os << "Unknown"; + return; + } + if (NonLoc::classof(*this)) { + castAs<NonLoc>().dumpToStream(os); + return; + } + if (Loc::classof(*this)) { + castAs<Loc>().dumpToStream(os); + return; } + llvm_unreachable("Unhandled SVal kind!"); } void NonLoc::dumpToStream(raw_ostream &os) const { - switch (getSubKind()) { - case nonloc::ConcreteIntKind: { - const auto &Value = castAs<nonloc::ConcreteInt>().getValue(); - os << Value << ' ' << (Value.isSigned() ? 'S' : 'U') - << Value.getBitWidth() << 'b'; - break; - } + switch (getKind()) { + case nonloc::ConcreteIntKind: { + const auto &Value = castAs<nonloc::ConcreteInt>().getValue(); + os << Value << ' ' << (Value.isSigned() ? 'S' : 'U') << Value.getBitWidth() + << 'b'; + break; + } case nonloc::SymbolValKind: os << castAs<nonloc::SymbolVal>().getSymbol(); break; @@ -360,21 +357,21 @@ void NonLoc::dumpToStream(raw_ostream &os) const { default: assert(false && "Pretty-printed not implemented for this NonLoc."); break; - } + } } void Loc::dumpToStream(raw_ostream &os) const { - switch (getSubKind()) { - case loc::ConcreteIntKind: - os << castAs<loc::ConcreteInt>().getValue().getZExtValue() << " (Loc)"; - break; - case loc::GotoLabelKind: - os << "&&" << castAs<loc::GotoLabel>().getLabel()->getName(); - break; - case loc::MemRegionValKind: - os << '&' << castAs<loc::MemRegionVal>().getRegion()->getString(); - break; - default: - llvm_unreachable("Pretty-printing not implemented for this Loc."); + switch (getKind()) { + case loc::ConcreteIntKind: + os << castAs<loc::ConcreteInt>().getValue().getZExtValue() << " (Loc)"; + break; + case loc::GotoLabelKind: + os << "&&" << castAs<loc::GotoLabel>().getLabel()->getName(); + break; + case loc::MemRegionValKind: + os << '&' << castAs<loc::MemRegionVal>().getRegion()->getString(); + break; + default: + llvm_unreachable("Pretty-printing not implemented for this Loc."); } } diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp index 3286d7f468f0..8ca2cdb9d3ab 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp @@ -63,7 +63,7 @@ ProgramStateRef SimpleConstraintManager::assumeAux(ProgramStateRef State, return assumeSymUnsupported(State, Sym, Assumption); } - switch (Cond.getSubKind()) { + switch (Cond.getKind()) { default: llvm_unreachable("'Assume' not implemented for this NonLoc"); @@ -107,7 +107,7 @@ ProgramStateRef SimpleConstraintManager::assumeInclusiveRangeInternal( return assumeSymInclusiveRange(State, Sym, From, To, InRange); } - switch (Value.getSubKind()) { + switch (Value.getKind()) { default: llvm_unreachable("'assumeInclusiveRange' is not implemented" "for this NonLoc"); diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp index 58d360a2e2db..45e48d435aca 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp @@ -28,7 +28,11 @@ class SimpleSValBuilder : public SValBuilder { // returns NULL. // This is an implementation detail. Checkers should use `getKnownValue()` // instead. - const llvm::APSInt *getConstValue(ProgramStateRef state, SVal V); + static const llvm::APSInt *getConstValue(ProgramStateRef state, SVal V); + + // Helper function that returns the value stored in a nonloc::ConcreteInt or + // loc::ConcreteInt. + static const llvm::APSInt *getConcreteValue(SVal V); // With one `simplifySValOnce` call, a compound symbols might collapse to // simpler symbol tree that is still possible to further simplify. Thus, we @@ -76,6 +80,16 @@ public: /// (integer) value, that value is returned. Otherwise, returns NULL. const llvm::APSInt *getKnownValue(ProgramStateRef state, SVal V) override; + /// Evaluates a given SVal by recursively evaluating and simplifying the + /// children SVals, then returns its minimal possible (integer) value. If the + /// constraint manager cannot provide a meaningful answer, this returns NULL. + const llvm::APSInt *getMinValue(ProgramStateRef state, SVal V) override; + + /// Evaluates a given SVal by recursively evaluating and simplifying the + /// children SVals, then returns its maximal possible (integer) value. If the + /// constraint manager cannot provide a meaningful answer, this returns NULL. + const llvm::APSInt *getMaxValue(ProgramStateRef state, SVal V) override; + SVal simplifySVal(ProgramStateRef State, SVal V) override; SVal MakeSymIntVal(const SymExpr *LHS, BinaryOperator::Opcode op, @@ -445,11 +459,11 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state, } while (true) { - switch (lhs.getSubKind()) { + switch (lhs.getKind()) { default: return makeSymExprValNN(op, lhs, rhs, resultTy); case nonloc::PointerToMemberKind: { - assert(rhs.getSubKind() == nonloc::PointerToMemberKind && + assert(rhs.getKind() == nonloc::PointerToMemberKind && "Both SVals should have pointer-to-member-type"); auto LPTM = lhs.castAs<nonloc::PointerToMember>(), RPTM = rhs.castAs<nonloc::PointerToMember>(); @@ -465,36 +479,36 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state, } case nonloc::LocAsIntegerKind: { Loc lhsL = lhs.castAs<nonloc::LocAsInteger>().getLoc(); - switch (rhs.getSubKind()) { - case nonloc::LocAsIntegerKind: - // FIXME: at the moment the implementation - // of modeling "pointers as integers" is not complete. - if (!BinaryOperator::isComparisonOp(op)) - return UnknownVal(); - return evalBinOpLL(state, op, lhsL, - rhs.castAs<nonloc::LocAsInteger>().getLoc(), - resultTy); - case nonloc::ConcreteIntKind: { - // FIXME: at the moment the implementation - // of modeling "pointers as integers" is not complete. - if (!BinaryOperator::isComparisonOp(op)) - return UnknownVal(); - // Transform the integer into a location and compare. - // FIXME: This only makes sense for comparisons. If we want to, say, - // add 1 to a LocAsInteger, we'd better unpack the Loc and add to it, - // then pack it back into a LocAsInteger. - llvm::APSInt i = rhs.castAs<nonloc::ConcreteInt>().getValue(); - // If the region has a symbolic base, pay attention to the type; it - // might be coming from a non-default address space. For non-symbolic - // regions it doesn't matter that much because such comparisons would - // most likely evaluate to concrete false anyway. FIXME: We might - // still need to handle the non-comparison case. - if (SymbolRef lSym = lhs.getAsLocSymbol(true)) - BasicVals.getAPSIntType(lSym->getType()).apply(i); - else - BasicVals.getAPSIntType(Context.VoidPtrTy).apply(i); - return evalBinOpLL(state, op, lhsL, makeLoc(i), resultTy); - } + switch (rhs.getKind()) { + case nonloc::LocAsIntegerKind: + // FIXME: at the moment the implementation + // of modeling "pointers as integers" is not complete. + if (!BinaryOperator::isComparisonOp(op)) + return UnknownVal(); + return evalBinOpLL(state, op, lhsL, + rhs.castAs<nonloc::LocAsInteger>().getLoc(), + resultTy); + case nonloc::ConcreteIntKind: { + // FIXME: at the moment the implementation + // of modeling "pointers as integers" is not complete. + if (!BinaryOperator::isComparisonOp(op)) + return UnknownVal(); + // Transform the integer into a location and compare. + // FIXME: This only makes sense for comparisons. If we want to, say, + // add 1 to a LocAsInteger, we'd better unpack the Loc and add to it, + // then pack it back into a LocAsInteger. + llvm::APSInt i = rhs.castAs<nonloc::ConcreteInt>().getValue(); + // If the region has a symbolic base, pay attention to the type; it + // might be coming from a non-default address space. For non-symbolic + // regions it doesn't matter that much because such comparisons would + // most likely evaluate to concrete false anyway. FIXME: We might + // still need to handle the non-comparison case. + if (SymbolRef lSym = lhs.getAsLocSymbol(true)) + BasicVals.getAPSIntType(lSym->getType()).apply(i); + else + BasicVals.getAPSIntType(Context.VoidPtrTy).apply(i); + return evalBinOpLL(state, op, lhsL, makeLoc(i), resultTy); + } default: switch (op) { case BO_EQ: @@ -505,7 +519,7 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state, // This case also handles pointer arithmetic. return makeSymExprValNN(op, InputLHS, InputRHS, resultTy); } - } + } } case nonloc::ConcreteIntKind: { llvm::APSInt LHSValue = lhs.castAs<nonloc::ConcreteInt>().getValue(); @@ -529,8 +543,21 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state, const llvm::APSInt *Result = BasicVals.evalAPSInt(op, LHSValue, RHSValue); - if (!Result) + if (!Result) { + if (op == BO_Shl || op == BO_Shr) { + // FIXME: At this point the constant folding claims that the result + // of a bitwise shift is undefined. However, constant folding + // relies on the inaccurate type information that is stored in the + // bit size of APSInt objects, and if we reached this point, then + // the checker core.BitwiseShift already determined that the shift + // is valid (in a PreStmt callback, by querying the real type from + // the AST node). + // To avoid embarrassing false positives, let's just say that we + // don't know anything about the result of the shift. + return UnknownVal(); + } return UndefinedVal(); + } return nonloc::ConcreteInt(*Result); } @@ -752,8 +779,7 @@ static void assertEqualBitWidths(ProgramStateRef State, Loc RhsLoc, RhsLoc.getType(Ctx).isNull() ? 0 : Ctx.getTypeSize(RhsLoc.getType(Ctx)); uint64_t LhsBitwidth = LhsLoc.getType(Ctx).isNull() ? 0 : Ctx.getTypeSize(LhsLoc.getType(Ctx)); - if (RhsBitwidth && LhsBitwidth && - (LhsLoc.getSubKind() == RhsLoc.getSubKind())) { + if (RhsBitwidth && LhsBitwidth && (LhsLoc.getKind() == RhsLoc.getKind())) { assert(RhsBitwidth == LhsBitwidth && "RhsLoc and LhsLoc bitwidth must be same!"); } @@ -799,7 +825,7 @@ SVal SimpleSValBuilder::evalBinOpLL(ProgramStateRef state, } } - switch (lhs.getSubKind()) { + switch (lhs.getKind()) { default: llvm_unreachable("Ordering not implemented for this Loc."); @@ -1170,18 +1196,22 @@ SVal SimpleSValBuilder::evalBinOpLN(ProgramStateRef state, const llvm::APSInt *SimpleSValBuilder::getConstValue(ProgramStateRef state, SVal V) { - if (V.isUnknownOrUndef()) - return nullptr; + if (const llvm::APSInt *Res = getConcreteValue(V)) + return Res; + if (SymbolRef Sym = V.getAsSymbol()) + return state->getConstraintManager().getSymVal(state, Sym); + + return nullptr; +} + +const llvm::APSInt *SimpleSValBuilder::getConcreteValue(SVal V) { if (std::optional<loc::ConcreteInt> X = V.getAs<loc::ConcreteInt>()) return &X->getValue(); if (std::optional<nonloc::ConcreteInt> X = V.getAs<nonloc::ConcreteInt>()) return &X->getValue(); - if (SymbolRef Sym = V.getAsSymbol()) - return state->getConstraintManager().getSymVal(state, Sym); - return nullptr; } @@ -1190,6 +1220,32 @@ const llvm::APSInt *SimpleSValBuilder::getKnownValue(ProgramStateRef state, return getConstValue(state, simplifySVal(state, V)); } +const llvm::APSInt *SimpleSValBuilder::getMinValue(ProgramStateRef state, + SVal V) { + V = simplifySVal(state, V); + + if (const llvm::APSInt *Res = getConcreteValue(V)) + return Res; + + if (SymbolRef Sym = V.getAsSymbol()) + return state->getConstraintManager().getSymMinVal(state, Sym); + + return nullptr; +} + +const llvm::APSInt *SimpleSValBuilder::getMaxValue(ProgramStateRef state, + SVal V) { + V = simplifySVal(state, V); + + if (const llvm::APSInt *Res = getConcreteValue(V)) + return Res; + + if (SymbolRef Sym = V.getAsSymbol()) + return state->getConstraintManager().getSymMaxVal(state, Sym); + + return nullptr; +} + SVal SimpleSValBuilder::simplifyUntilFixpoint(ProgramStateRef State, SVal Val) { SVal SimplifiedVal = simplifySValOnce(State, Val); while (SimplifiedVal != Val) { @@ -1359,7 +1415,7 @@ SVal SimpleSValBuilder::simplifySValOnce(ProgramStateRef State, SVal V) { SVal VisitMemRegion(const MemRegion *R) { return loc::MemRegionVal(R); } - SVal VisitNonLocSymbolVal(nonloc::SymbolVal V) { + SVal VisitSymbolVal(nonloc::SymbolVal V) { // Simplification is much more costly than computing complexity. // For high complexity, it may be not worth it. return Visit(V.getSymbol()); diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/Store.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/Store.cpp index 7577b7682a95..67ca61bb56ba 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/Store.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/Store.cpp @@ -402,7 +402,7 @@ SVal StoreManager::getLValueFieldOrIvar(const Decl *D, SVal Base) { Loc BaseL = Base.castAs<Loc>(); const SubRegion* BaseR = nullptr; - switch (BaseL.getSubKind()) { + switch (BaseL.getKind()) { case loc::MemRegionValKind: BaseR = cast<SubRegion>(BaseL.castAs<loc::MemRegionVal>().getRegion()); break; diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp index 54e0f4bee5eb..b6ef40595e3c 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp @@ -87,7 +87,7 @@ public: ASTContext *Ctx; Preprocessor &PP; const std::string OutDir; - AnalyzerOptionsRef Opts; + AnalyzerOptions &Opts; ArrayRef<std::string> Plugins; CodeInjector *Injector; cross_tu::CrossTranslationUnitContext CTU; @@ -121,15 +121,15 @@ public: FunctionSummariesTy FunctionSummaries; AnalysisConsumer(CompilerInstance &CI, const std::string &outdir, - AnalyzerOptionsRef opts, ArrayRef<std::string> plugins, + AnalyzerOptions &opts, ArrayRef<std::string> plugins, CodeInjector *injector) : RecVisitorMode(0), RecVisitorBR(nullptr), Ctx(nullptr), - PP(CI.getPreprocessor()), OutDir(outdir), Opts(std::move(opts)), + PP(CI.getPreprocessor()), OutDir(outdir), Opts(opts), Plugins(plugins), Injector(injector), CTU(CI), MacroExpansions(CI.getLangOpts()) { DigestAnalyzerOptions(); - if (Opts->AnalyzerDisplayProgress || Opts->PrintStats || - Opts->ShouldSerializeStats) { + if (Opts.AnalyzerDisplayProgress || Opts.PrintStats || + Opts.ShouldSerializeStats) { AnalyzerTimers = std::make_unique<llvm::TimerGroup>( "analyzer", "Analyzer timers"); SyntaxCheckTimer = std::make_unique<llvm::Timer>( @@ -141,27 +141,27 @@ public: *AnalyzerTimers); } - if (Opts->PrintStats || Opts->ShouldSerializeStats) { + if (Opts.PrintStats || Opts.ShouldSerializeStats) { llvm::EnableStatistics(/* DoPrintOnExit= */ false); } - if (Opts->ShouldDisplayMacroExpansions) + if (Opts.ShouldDisplayMacroExpansions) MacroExpansions.registerForPreprocessor(PP); } ~AnalysisConsumer() override { - if (Opts->PrintStats) { + if (Opts.PrintStats) { llvm::PrintStatistics(); } } void DigestAnalyzerOptions() { - switch (Opts->AnalysisDiagOpt) { + switch (Opts.AnalysisDiagOpt) { case PD_NONE: break; #define ANALYSIS_DIAGNOSTICS(NAME, CMDFLAG, DESC, CREATEFN) \ case PD_##NAME: \ - CREATEFN(Opts->getDiagOpts(), PathConsumers, OutDir, PP, CTU, \ + CREATEFN(Opts.getDiagOpts(), PathConsumers, OutDir, PP, CTU, \ MacroExpansions); \ break; #include "clang/StaticAnalyzer/Core/Analyses.def" @@ -172,7 +172,7 @@ public: // Create the analyzer component creators. CreateStoreMgr = &CreateRegionStoreManager; - switch (Opts->AnalysisConstraintsOpt) { + switch (Opts.AnalysisConstraintsOpt) { default: llvm_unreachable("Unknown constraint manager."); #define ANALYSIS_CONSTRAINTS(NAME, CMDFLAG, DESC, CREATEFN) \ @@ -182,7 +182,7 @@ public: } void DisplayTime(llvm::TimeRecord &Time) { - if (!Opts->AnalyzerDisplayProgress) { + if (!Opts.AnalyzerDisplayProgress) { return; } llvm::errs() << " : " << llvm::format("%1.1f", Time.getWallTime() * 1000) @@ -191,7 +191,7 @@ public: void DisplayFunction(const Decl *D, AnalysisMode Mode, ExprEngine::InliningModes IMode) { - if (!Opts->AnalyzerDisplayProgress) + if (!Opts.AnalyzerDisplayProgress) return; SourceManager &SM = Mgr->getASTContext().getSourceManager(); @@ -222,12 +222,12 @@ public: void Initialize(ASTContext &Context) override { Ctx = &Context; - checkerMgr = std::make_unique<CheckerManager>(*Ctx, *Opts, PP, Plugins, + checkerMgr = std::make_unique<CheckerManager>(*Ctx, Opts, PP, Plugins, CheckerRegistrationFns); Mgr = std::make_unique<AnalysisManager>(*Ctx, PP, PathConsumers, CreateStoreMgr, CreateConstraintMgr, - checkerMgr.get(), *Opts, Injector); + checkerMgr.get(), Opts, Injector); } /// Store the top level decls in the set to be processed later on. @@ -278,7 +278,7 @@ public: } bool VisitVarDecl(VarDecl *VD) { - if (!Opts->IsNaiveCTUEnabled) + if (!Opts.IsNaiveCTUEnabled) return true; if (VD->hasExternalStorage() || VD->isStaticDataMember()) { @@ -293,8 +293,8 @@ public: return true; llvm::Expected<const VarDecl *> CTUDeclOrError = - CTU.getCrossTUDefinition(VD, Opts->CTUDir, Opts->CTUIndexName, - Opts->DisplayCTUProgress); + CTU.getCrossTUDefinition(VD, Opts.CTUDir, Opts.CTUIndexName, + Opts.DisplayCTUProgress); if (!CTUDeclOrError) { handleAllErrors(CTUDeclOrError.takeError(), @@ -308,7 +308,7 @@ public: bool VisitFunctionDecl(FunctionDecl *FD) { IdentifierInfo *II = FD->getIdentifier(); - if (II && II->getName().startswith("__inline")) + if (II && II->getName().starts_with("__inline")) return true; // We skip function template definitions, as their semantics is @@ -356,7 +356,7 @@ private: AnalysisMode getModeForDecl(Decl *D, AnalysisMode Mode); void runAnalysisOnTranslationUnit(ASTContext &C); - /// Print \p S to stderr if \c Opts->AnalyzerDisplayProgress is set. + /// Print \p S to stderr if \c Opts.AnalyzerDisplayProgress is set. void reportAnalyzerProgress(StringRef S); }; // namespace } // end anonymous namespace @@ -567,12 +567,12 @@ void AnalysisConsumer::runAnalysisOnTranslationUnit(ASTContext &C) { // name correctly. // FIXME: The user might have analyzed the requested function in Syntax mode, // but we are unaware of that. - if (!Opts->AnalyzeSpecificFunction.empty() && NumFunctionsAnalyzed == 0) - reportAnalyzerFunctionMisuse(*Opts, *Ctx); + if (!Opts.AnalyzeSpecificFunction.empty() && NumFunctionsAnalyzed == 0) + reportAnalyzerFunctionMisuse(Opts, *Ctx); } void AnalysisConsumer::reportAnalyzerProgress(StringRef S) { - if (Opts->AnalyzerDisplayProgress) + if (Opts.AnalyzerDisplayProgress) llvm::errs() << S; } @@ -589,13 +589,13 @@ void AnalysisConsumer::HandleTranslationUnit(ASTContext &C) { const auto DiagFlusherScopeExit = llvm::make_scope_exit([this] { Mgr.reset(); }); - if (Opts->ShouldIgnoreBisonGeneratedFiles && + if (Opts.ShouldIgnoreBisonGeneratedFiles && fileContainsString("/* A Bison parser, made by", C)) { reportAnalyzerProgress("Skipping bison-generated file\n"); return; } - if (Opts->ShouldIgnoreFlexGeneratedFiles && + if (Opts.ShouldIgnoreFlexGeneratedFiles && fileContainsString("/* A lexical scanner generated by flex", C)) { reportAnalyzerProgress("Skipping flex-generated file\n"); return; @@ -603,7 +603,7 @@ void AnalysisConsumer::HandleTranslationUnit(ASTContext &C) { // Don't analyze if the user explicitly asked for no checks to be performed // on this file. - if (Opts->DisableAllCheckers) { + if (Opts.DisableAllCheckers) { reportAnalyzerProgress("All checks are disabled using a supplied option\n"); return; } @@ -623,8 +623,8 @@ void AnalysisConsumer::HandleTranslationUnit(ASTContext &C) { AnalysisConsumer::AnalysisMode AnalysisConsumer::getModeForDecl(Decl *D, AnalysisMode Mode) { - if (!Opts->AnalyzeSpecificFunction.empty() && - AnalysisDeclContext::getFunctionName(D) != Opts->AnalyzeSpecificFunction) + if (!Opts.AnalyzeSpecificFunction.empty() && + AnalysisDeclContext::getFunctionName(D) != Opts.AnalyzeSpecificFunction) return AM_None; // Unless -analyze-all is specified, treat decls differently depending on @@ -632,7 +632,7 @@ AnalysisConsumer::getModeForDecl(Decl *D, AnalysisMode Mode) { // - Main source file: run both path-sensitive and non-path-sensitive checks. // - Header files: run non-path-sensitive checks only. // - System headers: don't run any checks. - if (Opts->AnalyzeAll) + if (Opts.AnalyzeAll) return Mode; const SourceManager &SM = Ctx->getSourceManager(); @@ -757,8 +757,8 @@ ento::CreateAnalysisConsumer(CompilerInstance &CI) { // Disable the effects of '-Werror' when using the AnalysisConsumer. CI.getPreprocessor().getDiagnostics().setWarningsAsErrors(false); - AnalyzerOptionsRef analyzerOpts = CI.getAnalyzerOpts(); - bool hasModelPath = analyzerOpts->Config.count("model-path") > 0; + AnalyzerOptions &analyzerOpts = CI.getAnalyzerOpts(); + bool hasModelPath = analyzerOpts.Config.count("model-path") > 0; return std::make_unique<AnalysisConsumer>( CI, CI.getFrontendOpts().OutputFile, analyzerOpts, diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/AnalyzerHelpFlags.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/AnalyzerHelpFlags.cpp index 7cd15f0f6595..ea75c794f0b7 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/AnalyzerHelpFlags.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/AnalyzerHelpFlags.cpp @@ -30,18 +30,18 @@ void ento::printCheckerHelp(raw_ostream &out, CompilerInstance &CI) { out << "USAGE: -analyzer-checker <CHECKER or PACKAGE,...>\n\n"; auto CheckerMgr = std::make_unique<CheckerManager>( - *CI.getAnalyzerOpts(), CI.getLangOpts(), CI.getDiagnostics(), + CI.getAnalyzerOpts(), CI.getLangOpts(), CI.getDiagnostics(), CI.getFrontendOpts().Plugins); CheckerMgr->getCheckerRegistryData().printCheckerWithDescList( - *CI.getAnalyzerOpts(), out); + CI.getAnalyzerOpts(), out); } void ento::printEnabledCheckerList(raw_ostream &out, CompilerInstance &CI) { out << "OVERVIEW: Clang Static Analyzer Enabled Checkers List\n\n"; auto CheckerMgr = std::make_unique<CheckerManager>( - *CI.getAnalyzerOpts(), CI.getLangOpts(), CI.getDiagnostics(), + CI.getAnalyzerOpts(), CI.getLangOpts(), CI.getDiagnostics(), CI.getFrontendOpts().Plugins); CheckerMgr->getCheckerRegistryData().printEnabledCheckerList(out); @@ -50,11 +50,11 @@ void ento::printEnabledCheckerList(raw_ostream &out, CompilerInstance &CI) { void ento::printCheckerConfigList(raw_ostream &out, CompilerInstance &CI) { auto CheckerMgr = std::make_unique<CheckerManager>( - *CI.getAnalyzerOpts(), CI.getLangOpts(), CI.getDiagnostics(), + CI.getAnalyzerOpts(), CI.getLangOpts(), CI.getDiagnostics(), CI.getFrontendOpts().Plugins); CheckerMgr->getCheckerRegistryData().printCheckerOptionList( - *CI.getAnalyzerOpts(), out); + CI.getAnalyzerOpts(), out); } void ento::printAnalyzerConfigList(raw_ostream &out) { diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp index f0d3f43c414c..317df90a7781 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp @@ -310,8 +310,8 @@ template <bool IsWeak> void CheckerRegistry::resolveDependencies() { "Failed to find the dependency of a checker!"); // We do allow diagnostics from unit test/example dependency checkers. - assert((DependencyIt->FullName.startswith("test") || - DependencyIt->FullName.startswith("example") || IsWeak || + assert((DependencyIt->FullName.starts_with("test") || + DependencyIt->FullName.starts_with("example") || IsWeak || DependencyIt->IsHidden) && "Strong dependencies are modeling checkers, and as such " "non-user facing! Mark them hidden in Checkers.td!"); diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/ModelInjector.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/ModelInjector.cpp index 7baae6778ebd..ae11fbbe32b7 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/ModelInjector.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/ModelInjector.cpp @@ -48,8 +48,7 @@ void ModelInjector::onBodySynthesis(const NamedDecl *D) { SourceManager &SM = CI.getSourceManager(); FileID mainFileID = SM.getMainFileID(); - AnalyzerOptionsRef analyzerOpts = CI.getAnalyzerOpts(); - llvm::StringRef modelPath = analyzerOpts->ModelPath; + llvm::StringRef modelPath = CI.getAnalyzerOpts().ModelPath; llvm::SmallString<128> fileName; |
