diff options
Diffstat (limited to 'contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers')
131 files changed, 9733 insertions, 4764 deletions
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp index c06604b6cffe..a54f1b1e71d4 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp @@ -7,18 +7,20 @@ //===----------------------------------------------------------------------===// // This file reports various statistics about analyzer visitation. //===----------------------------------------------------------------------===// -#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/AST/DeclObjC.h" #include "clang/Basic/SourceManager.h" +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/Statistic.h" #include "llvm/Support/raw_ostream.h" +#include <optional> using namespace clang; using namespace ento; @@ -51,15 +53,14 @@ void AnalyzerStatsChecker::checkEndAnalysis(ExplodedGraph &G, const Decl *D = LC->getDecl(); // Iterate over the exploded graph. - for (ExplodedGraph::node_iterator I = G.nodes_begin(); - I != G.nodes_end(); ++I) { - const ProgramPoint &P = I->getLocation(); + for (const ExplodedNode &N : G.nodes()) { + const ProgramPoint &P = N.getLocation(); // Only check the coverage in the top level function (optimization). if (D != P.getLocationContext()->getDecl()) continue; - if (Optional<BlockEntrance> BE = P.getAs<BlockEntrance>()) { + if (std::optional<BlockEntrance> BE = P.getAs<BlockEntrance>()) { const CFGBlock *CB = BE->getBlock(); reachable.insert(CB); } @@ -93,11 +94,10 @@ void AnalyzerStatsChecker::checkEndAnalysis(ExplodedGraph &G, if (!Loc.isValid()) return; - if (isa<FunctionDecl>(D) || isa<ObjCMethodDecl>(D)) { + if (isa<FunctionDecl, ObjCMethodDecl>(D)) { const NamedDecl *ND = cast<NamedDecl>(D); output << *ND; - } - else if (isa<BlockDecl>(D)) { + } else if (isa<BlockDecl>(D)) { output << "block(line:" << Loc.getLine() << ":col:" << Loc.getColumn(); } @@ -115,16 +115,13 @@ void AnalyzerStatsChecker::checkEndAnalysis(ExplodedGraph &G, output.str(), PathDiagnosticLocation(D, SM)); // Emit warning for each block we bailed out on. - typedef CoreEngine::BlocksExhausted::const_iterator ExhaustedIterator; const CoreEngine &CE = Eng.getCoreEngine(); - for (ExhaustedIterator I = CE.blocks_exhausted_begin(), - E = CE.blocks_exhausted_end(); I != E; ++I) { - const BlockEdge &BE = I->first; + for (const BlockEdge &BE : make_first_range(CE.exhausted_blocks())) { const CFGBlock *Exit = BE.getDst(); if (Exit->empty()) continue; const CFGElement &CE = Exit->front(); - if (Optional<CFGStmt> CS = CE.getAs<CFGStmt>()) { + if (std::optional<CFGStmt> CS = CE.getAs<CFGStmt>()) { SmallString<128> bufI; llvm::raw_svector_ostream outputI(bufI); outputI << "(" << NameOfRootFunction << ")" << diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp index 605b11874ef5..c990ad138f89 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; + const BugType BT{this, "Out-of-bound array access"}; public: void checkLocation(SVal l, bool isLoad, const Stmt* S, @@ -58,25 +58,20 @@ void ArrayBoundChecker::checkLocation(SVal l, bool isLoad, const Stmt* LoadS, DefinedOrUnknownSVal ElementCount = getDynamicElementCount( state, ER->getSuperRegion(), C.getSValBuilder(), ER->getValueType()); - ProgramStateRef StInBound = state->assumeInBound(Idx, ElementCount, true); - ProgramStateRef StOutBound = state->assumeInBound(Idx, ElementCount, false); + ProgramStateRef StInBound, StOutBound; + std::tie(StInBound, StOutBound) = state->assumeInBoundDual(Idx, ElementCount); if (StOutBound && !StInBound) { ExplodedNode *N = C.generateErrorNode(StOutBound); if (!N) return; - if (!BT) - BT.reset(new BuiltinBug( - this, "Out-of-bound array access", - "Access out-of-bound array element (buffer overflow)")); - // 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 2a5fe9d8ed92..6c7a1601402e 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp @@ -11,9 +11,10 @@ // //===----------------------------------------------------------------------===// -#include "Taint.h" #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" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" @@ -22,77 +23,134 @@ #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; +enum OOB_Kind { OOB_Precedes, OOB_Exceeds, OOB_Taint }; - enum OOB_Kind { OOB_Precedes, OOB_Excedes, OOB_Tainted }; - - void reportOOB(CheckerContext &C, ProgramStateRef errorState, OOB_Kind kind, - std::unique_ptr<BugReporterVisitor> Visitor = nullptr) const; - -public: - void checkLocation(SVal l, bool isLoad, const Stmt*S, - CheckerContext &C) const; +struct Messages { + std::string Short, Full; }; -// FIXME: Eventually replace RegionRawOffset with this class. -class RegionRawOffsetV2 { -private: - const SubRegion *baseRegion; - SVal byteOffset; +// 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}; - RegionRawOffsetV2() - : baseRegion(nullptr), byteOffset(UnknownVal()) {} + void performCheck(const Expr *E, CheckerContext &C) const; -public: - RegionRawOffsetV2(const SubRegion* base, SVal offset) - : baseRegion(base), byteOffset(offset) {} + void reportOOB(CheckerContext &C, ProgramStateRef ErrorState, OOB_Kind Kind, + NonLoc Offset, Messages Msgs) const; - NonLoc getByteOffset() const { return byteOffset.castAs<NonLoc>(); } - const SubRegion *getRegion() const { return baseRegion; } + static bool isFromCtypeMacro(const Stmt *S, ASTContext &AC); - static RegionRawOffsetV2 computeOffset(ProgramStateRef state, - SValBuilder &svalBuilder, - SVal location); + static bool isInAddressOf(const Stmt *S, ASTContext &AC); - void dump() const; - void dumpToStream(raw_ostream &os) const; +public: + 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); + } }; -} -static SVal computeExtentBegin(SValBuilder &svalBuilder, - const MemRegion *region) { - const MemSpaceRegion *SR = region->getMemorySpace(); - if (SR->getKind() == MemRegion::UnknownSpaceRegionKind) - return UnknownVal(); - else - return svalBuilder.makeZeroArrayIndex(); +} // anonymous namespace + +/// 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>(); + }; + + 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); + } + + if (OwnerRegion) + return std::make_pair(OwnerRegion, *Offset); + + return std::nullopt; } // TODO: once the constraint manager is smart enough to handle non simplified // symbolic expressions remove this function. Note that this can not be used in // the constraint manager as is, since this does not handle overflows. It is // safe to assume, however, that memory offsets will not overflow. +// NOTE: callers of this function need to be aware of the effects of overflows +// and signed<->unsigned conversions! static std::pair<NonLoc, nonloc::ConcreteInt> getSimplifiedOffsets(NonLoc offset, nonloc::ConcreteInt extent, SValBuilder &svalBuilder) { - Optional<nonloc::SymbolVal> SymVal = offset.getAs<nonloc::SymbolVal>(); + std::optional<nonloc::SymbolVal> SymVal = offset.getAs<nonloc::SymbolVal>(); if (SymVal && SymVal->isExpression()) { if (const SymIntExpr *SIE = dyn_cast<SymIntExpr>(SymVal->getSymbol())) { llvm::APSInt constant = 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,10 +171,154 @@ getSimplifiedOffsets(NonLoc offset, nonloc::ConcreteInt extent, return std::pair<NonLoc, nonloc::ConcreteInt>(offset, extent); } -void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad, - const Stmt* LoadS, - CheckerContext &checkerContext) const { +// Evaluate the comparison Value < Threshold with the help of the custom +// simplification algorithm defined for this checker. Return a pair of states, +// 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, bool CheckEquality = false) { + if (auto ConcreteThreshold = Threshold.getAs<nonloc::ConcreteInt>()) { + std::tie(Value, Threshold) = getSimplifiedOffsets(Value, *ConcreteThreshold, SVB); + } + if (auto ConcreteThreshold = Threshold.getAs<nonloc::ConcreteInt>()) { + QualType T = Value.getType(SVB.getContext()); + if (T->isUnsignedIntegerType() && ConcreteThreshold->getValue().isNegative()) { + // In this case we reduced the bound check to a comparison of the form + // (symbol or value with unsigned type) < (negative number) + // which is always false. We are handling these cases separately because + // evalBinOpNN can perform a signed->unsigned conversion that turns the + // negative number into a huge positive value and leads to wildly + // inaccurate conclusions. + return {nullptr, State}; + } + } + const BinaryOperatorKind OpKind = CheckEquality ? BO_EQ : BO_LT; + auto BelowThreshold = + SVB.evalBinOpNN(State, OpKind, Value, Threshold, SVB.getConditionType()) + .getAs<NonLoc>(); + + if (BelowThreshold) + return State->assume(*BelowThreshold); + + return {nullptr, nullptr}; +} + +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() @@ -126,230 +328,152 @@ void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad, // memory access is within the extent of the base region. Since we // have some flexibility in defining the base region, we can achieve // various levels of conservatism in our buffer overflow checking. - ProgramStateRef state = checkerContext.getState(); - SValBuilder &svalBuilder = checkerContext.getSValBuilder(); - const RegionRawOffsetV2 &rawOffset = - RegionRawOffsetV2::computeOffset(state, svalBuilder, location); + const SVal Location = C.getSVal(E); - if (!rawOffset.getRegion()) + // 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(E, C.getASTContext())) return; - NonLoc rawOffsetVal = rawOffset.getByteOffset(); - - // CHECK LOWER BOUND: Is byteOffset < extent begin? - // If so, we are doing a load/store - // before the first valid offset in the memory region. - - SVal extentBegin = computeExtentBegin(svalBuilder, rawOffset.getRegion()); + ProgramStateRef State = C.getState(); + SValBuilder &SVB = C.getSValBuilder(); - if (Optional<NonLoc> NV = extentBegin.getAs<NonLoc>()) { - if (NV->getAs<nonloc::ConcreteInt>()) { - std::pair<NonLoc, nonloc::ConcreteInt> simplifiedOffsets = - getSimplifiedOffsets(rawOffset.getByteOffset(), - NV->castAs<nonloc::ConcreteInt>(), - svalBuilder); - rawOffsetVal = simplifiedOffsets.first; - *NV = simplifiedOffsets.second; - } - - SVal lowerBound = svalBuilder.evalBinOpNN(state, BO_LT, rawOffsetVal, *NV, - svalBuilder.getConditionType()); + const std::optional<std::pair<const SubRegion *, NonLoc>> &RawOffset = + computeOffset(State, SVB, Location); - Optional<NonLoc> lowerBoundToCheck = lowerBound.getAs<NonLoc>(); - if (!lowerBoundToCheck) - return; - - ProgramStateRef state_precedesLowerBound, state_withinLowerBound; - std::tie(state_precedesLowerBound, state_withinLowerBound) = - state->assume(*lowerBoundToCheck); + if (!RawOffset) + return; - // Are we constrained enough to definitely precede the lower bound? - if (state_precedesLowerBound && !state_withinLowerBound) { - reportOOB(checkerContext, state_precedesLowerBound, OOB_Precedes); + auto [Reg, ByteOffset] = *RawOffset; + + // CHECK LOWER BOUND + 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. + Messages Msgs = getPrecedesMsgs(Reg, ByteOffset); + reportOOB(C, PrecedesLowerBound, OOB_Precedes, ByteOffset, Msgs); return; } - // Otherwise, assume the constraint of the lower bound. - assert(state_withinLowerBound); - state = state_withinLowerBound; + if (WithinLowerBound) + State = WithinLowerBound; } - do { - // CHECK UPPER BOUND: Is byteOffset >= size(baseRegion)? If so, - // we are doing a load/store after the last valid offset. - const MemRegion *MR = rawOffset.getRegion(); - DefinedOrUnknownSVal Size = getDynamicExtent(state, MR, svalBuilder); - if (!Size.getAs<NonLoc>()) - break; - - if (Size.getAs<nonloc::ConcreteInt>()) { - std::pair<NonLoc, nonloc::ConcreteInt> simplifiedOffsets = - getSimplifiedOffsets(rawOffset.getByteOffset(), - Size.castAs<nonloc::ConcreteInt>(), svalBuilder); - rawOffsetVal = simplifiedOffsets.first; - Size = simplifiedOffsets.second; - } - - SVal upperbound = svalBuilder.evalBinOpNN(state, BO_GE, rawOffsetVal, - Size.castAs<NonLoc>(), - svalBuilder.getConditionType()); - - Optional<NonLoc> upperboundToCheck = upperbound.getAs<NonLoc>(); - if (!upperboundToCheck) - break; - - ProgramStateRef state_exceedsUpperBound, state_withinUpperBound; - std::tie(state_exceedsUpperBound, state_withinUpperBound) = - state->assume(*upperboundToCheck); - - // If we are under constrained and the index variables are tainted, report. - if (state_exceedsUpperBound && state_withinUpperBound) { - SVal ByteOffset = rawOffset.getByteOffset(); - if (isTainted(state, ByteOffset)) { - reportOOB(checkerContext, state_exceedsUpperBound, OOB_Tainted, - std::make_unique<TaintBugVisitor>(ByteOffset)); + // CHECK UPPER BOUND + DefinedOrUnknownSVal Size = getDynamicExtent(State, Reg, SVB); + if (auto KnownSize = Size.getAs<NonLoc>()) { + auto [WithinUpperBound, ExceedsUpperBound] = + compareValueToThreshold(State, ByteOffset, *KnownSize, SVB); + + if (ExceedsUpperBound) { + if (!WithinUpperBound) { + // We know that the index definitely exceeds the upper bound. + 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 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; } - } else if (state_exceedsUpperBound) { - // If we are constrained enough to definitely exceed the upper bound, - // report. - assert(!state_withinUpperBound); - reportOOB(checkerContext, state_exceedsUpperBound, OOB_Excedes); - return; } - assert(state_withinUpperBound); - state = state_withinUpperBound; + if (WithinUpperBound) + State = WithinUpperBound; } - while (false); - checkerContext.addTransition(state); + C.addTransition(State); } -void ArrayBoundCheckerV2::reportOOB( - CheckerContext &checkerContext, ProgramStateRef errorState, OOB_Kind kind, - std::unique_ptr<BugReporterVisitor> Visitor) const { +void ArrayBoundCheckerV2::reportOOB(CheckerContext &C, + ProgramStateRef ErrorState, OOB_Kind Kind, + NonLoc Offset, Messages Msgs) const { - ExplodedNode *errorNode = checkerContext.generateErrorNode(errorState); - if (!errorNode) + ExplodedNode *ErrorNode = C.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; - case OOB_Tainted: - os << "(index is tainted)"; - break; - } + auto BR = std::make_unique<PathSensitiveBugReport>( + Kind == OOB_Taint ? TaintBT : BT, Msgs.Short, Msgs.Full, ErrorNode); - auto BR = std::make_unique<PathSensitiveBugReport>(*BT, os.str(), errorNode); - BR->addVisitor(std::move(Visitor)); - checkerContext.emitReport(std::move(BR)); -} + // Track back the propagation of taintedness. + if (Kind == OOB_Taint) + for (SymbolRef Sym : getTaintedSymbols(ErrorState, Offset)) + BR->markInteresting(Sym); -#ifndef NDEBUG -LLVM_DUMP_METHOD void RegionRawOffsetV2::dump() const { - dumpToStream(llvm::errs()); + C.emitReport(std::move(BR)); } -void RegionRawOffsetV2::dumpToStream(raw_ostream &os) const { - os << "raw_offset_v2{" << getRegion() << ',' << getByteOffset() << '}'; -} -#endif +bool ArrayBoundCheckerV2::isFromCtypeMacro(const Stmt *S, ASTContext &ACtx) { + SourceLocation Loc = S->getBeginLoc(); + if (!Loc.isMacroID()) + return false; -// Lazily computes a value to be used by 'computeOffset'. If 'val' -// is unknown or undefined, we lazily substitute '0'. Otherwise, -// return 'val'. -static inline SVal getValue(SVal val, SValBuilder &svalBuilder) { - return val.getAs<UndefinedVal>() ? svalBuilder.makeArrayIndex(0) : val; -} + StringRef MacroName = Lexer::getImmediateMacroName( + Loc, ACtx.getSourceManager(), ACtx.getLangOpts()); -// Scale a base value by a scaling factor, and return the scaled -// value as an SVal. Used by 'computeOffset'. -static inline SVal scaleValue(ProgramStateRef state, - NonLoc baseVal, CharUnits scaling, - SValBuilder &sb) { - return sb.evalBinOpNN(state, BO_Mul, baseVal, - sb.makeArrayIndex(scaling.getQuantity()), - sb.getArrayIndexType()); -} + if (MacroName.size() < 7 || MacroName[0] != 'i' || MacroName[1] != 's') + return false; -// Add an SVal to another, treating unknown and undefined values as -// summing to UnknownVal. Used by 'computeOffset'. -static SVal addValue(ProgramStateRef state, SVal x, SVal y, - SValBuilder &svalBuilder) { - // We treat UnknownVals and UndefinedVals the same here because we - // only care about computing offsets. - if (x.isUnknownOrUndef() || y.isUnknownOrUndef()) - return UnknownVal(); - - return svalBuilder.evalBinOpNN(state, BO_Add, x.castAs<NonLoc>(), - y.castAs<NonLoc>(), - svalBuilder.getArrayIndexType()); + return ((MacroName == "isalnum") || (MacroName == "isalpha") || + (MacroName == "isblank") || (MacroName == "isdigit") || + (MacroName == "isgraph") || (MacroName == "islower") || + (MacroName == "isnctrl") || (MacroName == "isprint") || + (MacroName == "ispunct") || (MacroName == "isspace") || + (MacroName == "isupper") || (MacroName == "isxdigit")); } -/// Compute a raw byte offset from a base region. Used for array bounds -/// checking. -RegionRawOffsetV2 RegionRawOffsetV2::computeOffset(ProgramStateRef state, - SValBuilder &svalBuilder, - SVal location) -{ - const MemRegion *region = location.getAsRegion(); - SVal offset = UndefinedVal(); - - while (region) { - switch (region->getKind()) { - default: { - if (const SubRegion *subReg = dyn_cast<SubRegion>(region)) { - offset = getValue(offset, svalBuilder); - if (!offset.isUnknownOrUndef()) - return RegionRawOffsetV2(subReg, offset); - } - return RegionRawOffsetV2(); - } - case MemRegion::ElementRegionKind: { - const ElementRegion *elemReg = cast<ElementRegion>(region); - SVal index = elemReg->getIndex(); - if (!index.getAs<NonLoc>()) - return RegionRawOffsetV2(); - QualType elemType = elemReg->getElementType(); - // If the element is an incomplete type, go no further. - ASTContext &astContext = svalBuilder.getContext(); - if (elemType->isIncompleteType()) - return RegionRawOffsetV2(); - - // Update the offset. - offset = addValue(state, - getValue(offset, svalBuilder), - scaleValue(state, - index.castAs<NonLoc>(), - astContext.getTypeSizeInChars(elemType), - svalBuilder), - svalBuilder); - - if (offset.isUnknownOrUndef()) - return RegionRawOffsetV2(); - - region = elemReg->getSuperRegion(); - continue; - } - } - } - return RegionRawOffsetV2(); +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 a86a410ebcbc..c72a97cc01e9 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp @@ -12,7 +12,6 @@ // //===----------------------------------------------------------------------===// -#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/AST/ASTContext.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/Expr.h" @@ -20,18 +19,22 @@ #include "clang/AST/StmtObjC.h" #include "clang/Analysis/DomainSpecific/CocoaConventions.h" #include "clang/Analysis/SelectorExtras.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/ExplodedGraph.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringMap.h" #include "llvm/Support/raw_ostream.h" +#include <optional> using namespace clang; using namespace ento; @@ -41,7 +44,7 @@ namespace { class APIMisuse : public BugType { public: APIMisuse(const CheckerBase *checker, const char *name) - : BugType(checker, name, "API Misuse (Apple)") {} + : BugType(checker, name, categories::AppleAPIMisuse) {} }; } // end anonymous namespace @@ -93,56 +96,64 @@ static FoundationClass findKnownClass(const ObjCInterfaceDecl *ID, //===----------------------------------------------------------------------===// namespace { - class NilArgChecker : public Checker<check::PreObjCMessage, - check::PostStmt<ObjCDictionaryLiteral>, - check::PostStmt<ObjCArrayLiteral> > { - mutable std::unique_ptr<APIMisuse> BT; - - mutable llvm::SmallDenseMap<Selector, unsigned, 16> StringSelectors; - mutable Selector ArrayWithObjectSel; - mutable Selector AddObjectSel; - mutable Selector InsertObjectAtIndexSel; - mutable Selector ReplaceObjectAtIndexWithObjectSel; - mutable Selector SetObjectAtIndexedSubscriptSel; - mutable Selector ArrayByAddingObjectSel; - mutable Selector DictionaryWithObjectForKeySel; - mutable Selector SetObjectForKeySel; - mutable Selector SetObjectForKeyedSubscriptSel; - mutable Selector RemoveObjectForKeySel; - - void warnIfNilExpr(const Expr *E, - const char *Msg, - CheckerContext &C) const; - - void warnIfNilArg(CheckerContext &C, - const ObjCMethodCall &msg, unsigned Arg, - FoundationClass Class, - bool CanBeSubscript = false) const; - - void generateBugReport(ExplodedNode *N, - StringRef Msg, - SourceRange Range, - const Expr *Expr, - CheckerContext &C) const; - - public: - void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; - void checkPostStmt(const ObjCDictionaryLiteral *DL, - CheckerContext &C) const; - void checkPostStmt(const ObjCArrayLiteral *AL, - CheckerContext &C) const; - }; +class NilArgChecker : public Checker<check::PreObjCMessage, + check::PostStmt<ObjCDictionaryLiteral>, + check::PostStmt<ObjCArrayLiteral>, + EventDispatcher<ImplicitNullDerefEvent>> { + mutable std::unique_ptr<APIMisuse> BT; + + mutable llvm::SmallDenseMap<Selector, unsigned, 16> StringSelectors; + mutable Selector ArrayWithObjectSel; + mutable Selector AddObjectSel; + mutable Selector InsertObjectAtIndexSel; + mutable Selector ReplaceObjectAtIndexWithObjectSel; + mutable Selector SetObjectAtIndexedSubscriptSel; + mutable Selector ArrayByAddingObjectSel; + mutable Selector DictionaryWithObjectForKeySel; + mutable Selector SetObjectForKeySel; + mutable Selector SetObjectForKeyedSubscriptSel; + mutable Selector RemoveObjectForKeySel; + + void warnIfNilExpr(const Expr *E, const char *Msg, CheckerContext &C) const; + + void warnIfNilArg(CheckerContext &C, const ObjCMethodCall &msg, unsigned Arg, + FoundationClass Class, bool CanBeSubscript = false) const; + + void generateBugReport(ExplodedNode *N, StringRef Msg, SourceRange Range, + const Expr *Expr, CheckerContext &C) const; + +public: + void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; + void checkPostStmt(const ObjCDictionaryLiteral *DL, CheckerContext &C) const; + void checkPostStmt(const ObjCArrayLiteral *AL, CheckerContext &C) const; +}; } // end anonymous namespace void NilArgChecker::warnIfNilExpr(const Expr *E, const char *Msg, CheckerContext &C) const { - ProgramStateRef State = C.getState(); - if (State->isNull(C.getSVal(E)).isConstrainedTrue()) { + auto Location = C.getSVal(E).getAs<Loc>(); + if (!Location) + return; + + auto [NonNull, Null] = C.getState()->assume(*Location); + // If it's known to be null. + if (!NonNull && Null) { if (ExplodedNode *N = C.generateErrorNode()) { generateBugReport(N, Msg, E->getSourceRange(), E, C); + return; + } + } + + // If it might be null, assume that it cannot after this operation. + if (Null) { + // One needs to make sure the pointer is non-null to be used here. + if (ExplodedNode *N = C.generateSink(Null, C.getPredecessor())) { + dispatchEvent({*Location, /*IsLoad=*/false, N, &C.getBugReporter(), + /*IsDirectDereference=*/false}); } + C.addTransition(NonNull); } } @@ -341,15 +352,11 @@ 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; - -private: - void EmitError(const TypedRegion* R, const Expr *Ex, - uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind); }; } // end anonymous namespace @@ -372,7 +379,7 @@ enum CFNumberType { kCFNumberCGFloatType = 16 }; -static Optional<uint64_t> GetCFNumberSize(ASTContext &Ctx, uint64_t i) { +static std::optional<uint64_t> GetCFNumberSize(ASTContext &Ctx, uint64_t i) { static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 }; if (i < kCFNumberCharType) @@ -393,7 +400,7 @@ static Optional<uint64_t> GetCFNumberSize(ASTContext &Ctx, uint64_t i) { case kCFNumberCGFloatType: // FIXME: We need a way to map from names to Type*. default: - return None; + return std::nullopt; } return Ctx.getTypeSize(T); @@ -445,12 +452,13 @@ void CFNumberChecker::checkPreStmt(const CallExpr *CE, // FIXME: We really should allow ranges of valid theType values, and // bifurcate the state appropriately. - Optional<nonloc::ConcreteInt> V = TheTypeVal.getAs<nonloc::ConcreteInt>(); + std::optional<nonloc::ConcreteInt> V = + dyn_cast<nonloc::ConcreteInt>(TheTypeVal); if (!V) return; uint64_t NumberKind = V->getValue().getLimitedValue(); - Optional<uint64_t> OptCFNumberSize = GetCFNumberSize(Ctx, NumberKind); + std::optional<uint64_t> OptCFNumberSize = GetCFNumberSize(Ctx, NumberKind); // FIXME: In some cases we can emit an error. if (!OptCFNumberSize) @@ -465,7 +473,7 @@ void CFNumberChecker::checkPreStmt(const CallExpr *CE, // FIXME: Eventually we should handle arbitrary locations. We can do this // by having an enhanced memory model that does low-level typing. - Optional<loc::MemRegionVal> LV = TheValueExpr.getAs<loc::MemRegionVal>(); + std::optional<loc::MemRegionVal> LV = TheValueExpr.getAs<loc::MemRegionVal>(); if (!LV) return; @@ -533,10 +541,12 @@ void CFNumberChecker::checkPreStmt(const CallExpr *CE, namespace { class CFRetainReleaseChecker : public Checker<check::PreCall> { mutable APIMisuse BT{this, "null passed to CF memory management function"}; - CallDescription CFRetain{"CFRetain", 1}, - CFRelease{"CFRelease", 1}, - CFMakeCollectable{"CFMakeCollectable", 1}, - CFAutorelease{"CFAutorelease", 1}; + const CallDescriptionSet ModelledCalls = { + {{"CFRetain"}, 1}, + {{"CFRelease"}, 1}, + {{"CFMakeCollectable"}, 1}, + {{"CFAutorelease"}, 1}, + }; public: void checkPreCall(const CallEvent &Call, CheckerContext &C) const; @@ -550,13 +560,12 @@ void CFRetainReleaseChecker::checkPreCall(const CallEvent &Call, return; // Check if we called CFRetain/CFRelease/CFMakeCollectable/CFAutorelease. - if (!(Call.isCalled(CFRetain) || Call.isCalled(CFRelease) || - Call.isCalled(CFMakeCollectable) || Call.isCalled(CFAutorelease))) + if (!ModelledCalls.contains(Call)) return; // Get the argument's value. SVal ArgVal = Call.getArgSVal(0); - Optional<DefinedSVal> DefArgVal = ArgVal.getAs<DefinedSVal>(); + std::optional<DefinedSVal> DefArgVal = ArgVal.getAs<DefinedSVal>(); if (!DefArgVal) return; @@ -744,7 +753,7 @@ void VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &msg, return; // Verify that all arguments have Objective-C types. - Optional<ExplodedNode*> errorNode; + std::optional<ExplodedNode *> errorNode; for (unsigned I = variadicArgsBegin; I != variadicArgsEnd; ++I) { QualType ArgTy = msg.getArgExpr(I)->getType(); @@ -756,7 +765,7 @@ void VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &msg, continue; // Ignore pointer constants. - if (msg.getArgSVal(I).getAs<loc::ConcreteInt>()) + if (isa<loc::ConcreteInt>(msg.getArgSVal(I))) continue; // Ignore pointer types annotated with 'NSObject' attribute. @@ -768,10 +777,10 @@ void VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &msg, continue; // Generate only one error node to use for all bug reports. - if (!errorNode.hasValue()) + if (!errorNode) errorNode = C.generateNonFatalErrorNode(); - if (!errorNode.getValue()) + if (!*errorNode) continue; SmallString<128> sbuf; @@ -788,8 +797,8 @@ void VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &msg, ArgTy.print(os, C.getLangOpts()); os << "'"; - auto R = std::make_unique<PathSensitiveBugReport>(*BT, os.str(), - errorNode.getValue()); + auto R = + std::make_unique<PathSensitiveBugReport>(*BT, os.str(), *errorNode); R->addRange(msg.getArgSourceRange(I)); C.emitReport(std::move(R)); } @@ -810,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; @@ -859,7 +868,8 @@ static ProgramStateRef checkCollectionNonNil(CheckerContext &C, return nullptr; SVal CollectionVal = C.getSVal(FCS->getCollection()); - Optional<DefinedSVal> KnownCollection = CollectionVal.getAs<DefinedSVal>(); + std::optional<DefinedSVal> KnownCollection = + CollectionVal.getAs<DefinedSVal>(); if (!KnownCollection) return State; @@ -891,7 +901,7 @@ static ProgramStateRef checkElementNonNil(CheckerContext &C, const Stmt *Element = FCS->getElement(); // FIXME: Copied from ExprEngineObjC. - Optional<Loc> ElementLoc; + std::optional<Loc> ElementLoc; if (const DeclStmt *DS = dyn_cast<DeclStmt>(Element)) { const VarDecl *ElemDecl = cast<VarDecl>(DS->getSingleDecl()); assert(ElemDecl->getInit() == nullptr); @@ -905,7 +915,7 @@ static ProgramStateRef checkElementNonNil(CheckerContext &C, // Go ahead and assume the value is non-nil. SVal Val = State->getSVal(*ElementLoc); - return State->assume(Val.castAs<DefinedOrUnknownSVal>(), true); + return State->assume(cast<DefinedOrUnknownSVal>(Val), true); } /// Returns NULL state if the collection is known to contain elements @@ -930,8 +940,8 @@ assumeCollectionNonEmpty(CheckerContext &C, ProgramStateRef State, nonloc::SymbolVal(*CountS), SvalBuilder.makeIntVal(0, (*CountS)->getType()), SvalBuilder.getConditionType()); - Optional<DefinedSVal> CountGreaterThanZero = - CountGreaterThanZeroVal.getAs<DefinedSVal>(); + std::optional<DefinedSVal> CountGreaterThanZero = + CountGreaterThanZeroVal.getAs<DefinedSVal>(); if (!CountGreaterThanZero) { // The SValBuilder cannot construct a valid SVal for this condition. // This means we cannot properly reason about it. @@ -959,14 +969,13 @@ static bool alreadyExecutedAtLeastOneLoopIteration(const ExplodedNode *N, return false; ProgramPoint P = N->getLocation(); - if (Optional<BlockEdge> BE = P.getAs<BlockEdge>()) { + if (std::optional<BlockEdge> BE = P.getAs<BlockEdge>()) { return BE->getSrc()->getLoopTarget() == FCS; } // Keep looking for a block edge. - for (ExplodedNode::const_pred_iterator I = N->pred_begin(), - E = N->pred_end(); I != E; ++I) { - if (alreadyExecutedAtLeastOneLoopIteration(*I, FCS)) + for (const ExplodedNode *N : N->preds()) { + if (alreadyExecutedAtLeastOneLoopIteration(N, FCS)) return true; } @@ -1094,12 +1103,8 @@ ObjCLoopChecker::checkPointerEscape(ProgramStateRef State, PointerEscapeKind Kind) const { SymbolRef ImmutableReceiver = getMethodReceiverIfKnownImmutable(Call); - // Remove the invalidated symbols form the collection count map. - for (InvalidatedSymbols::const_iterator I = Escaped.begin(), - E = Escaped.end(); - I != E; ++I) { - SymbolRef Sym = *I; - + // Remove the invalidated symbols from the collection count map. + for (SymbolRef Sym : Escaped) { // Don't invalidate this symbol's count if we know the method being called // is declared on an immutable class. This isn't completely correct if the // receiver is also passed as an argument, but in most uses of NSArray, @@ -1121,9 +1126,7 @@ void ObjCLoopChecker::checkDeadSymbols(SymbolReaper &SymReaper, // Remove the dead symbols from the collection count map. ContainerCountMapTy Tracked = State->get<ContainerCountMap>(); - for (ContainerCountMapTy::iterator I = Tracked.begin(), - E = Tracked.end(); I != E; ++I) { - SymbolRef Sym = I->first; + for (SymbolRef Sym : llvm::make_first_range(Tracked)) { if (SymReaper.isDead(Sym)) { State = State->remove<ContainerCountMap>(Sym); State = State->remove<ContainerNonEmptyMap>(Sym); @@ -1142,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, @@ -1176,7 +1179,8 @@ ObjCNonNilReturnValueChecker::assumeExprIsNonNull(const Expr *NonNullExpr, ProgramStateRef State, CheckerContext &C) const { SVal Val = C.getSVal(NonNullExpr); - if (Optional<DefinedOrUnknownSVal> DV = Val.getAs<DefinedOrUnknownSVal>()) + if (std::optional<DefinedOrUnknownSVal> DV = + Val.getAs<DefinedOrUnknownSVal>()) return State->assume(*DV, true); return State; } diff --git a/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/BlockInCriticalSectionChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp index 2752b37f9b3f..66e080adb138 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp @@ -17,6 +17,7 @@ #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" @@ -24,20 +25,31 @@ using namespace clang; using namespace ento; namespace { - class BlockInCriticalSectionChecker : public Checker<check::PostCall> { - - mutable IdentifierInfo *IILockGuard, *IIUniqueLock; - - CallDescription LockFn, UnlockFn, SleepFn, GetcFn, FgetsFn, ReadFn, RecvFn, - PthreadLockFn, PthreadTryLockFn, PthreadUnlockFn, - MtxLock, MtxTimedLock, MtxTryLock, MtxUnlock; - - StringRef ClassLockGuard, ClassUniqueLock; - - mutable bool IdentifierInfoInitialized; - - std::unique_ptr<BugType> BlockInCritSectionBugType; + mutable IdentifierInfo *IILockGuard = nullptr; + mutable IdentifierInfo *IIUniqueLock = nullptr; + mutable bool IdentifierInfoInitialized = false; + + const CallDescription LockFn{{"lock"}}; + const CallDescription UnlockFn{{"unlock"}}; + const CallDescription SleepFn{{"sleep"}}; + const CallDescription GetcFn{{"getc"}}; + const CallDescription FgetsFn{{"fgets"}}; + const CallDescription ReadFn{{"read"}}; + const CallDescription RecvFn{{"recv"}}; + const CallDescription PthreadLockFn{{"pthread_mutex_lock"}}; + const CallDescription PthreadTryLockFn{{"pthread_mutex_trylock"}}; + const CallDescription PthreadUnlockFn{{"pthread_mutex_unlock"}}; + const CallDescription MtxLock{{"mtx_lock"}}; + const CallDescription MtxTimedLock{{"mtx_timedlock"}}; + const CallDescription MtxTryLock{{"mtx_trylock"}}; + const CallDescription MtxUnlock{{"mtx_unlock"}}; + + const llvm::StringLiteral ClassLockGuard{"lock_guard"}; + const llvm::StringLiteral ClassUniqueLock{"unique_lock"}; + + const BugType BlockInCritSectionBugType{ + this, "Call to blocking function in critical section", "Blocking Error"}; void initIdentifierInfo(ASTContext &Ctx) const; @@ -46,8 +58,6 @@ class BlockInCriticalSectionChecker : public Checker<check::PostCall> { CheckerContext &C) const; public: - BlockInCriticalSectionChecker(); - bool isBlockingFunction(const CallEvent &Call) const; bool isLockFunction(const CallEvent &Call) const; bool isUnlockFunction(const CallEvent &Call) const; @@ -62,26 +72,6 @@ public: REGISTER_TRAIT_WITH_PROGRAMSTATE(MutexCounter, unsigned) -BlockInCriticalSectionChecker::BlockInCriticalSectionChecker() - : IILockGuard(nullptr), IIUniqueLock(nullptr), - LockFn("lock"), UnlockFn("unlock"), SleepFn("sleep"), GetcFn("getc"), - FgetsFn("fgets"), ReadFn("read"), RecvFn("recv"), - PthreadLockFn("pthread_mutex_lock"), - PthreadTryLockFn("pthread_mutex_trylock"), - PthreadUnlockFn("pthread_mutex_unlock"), - MtxLock("mtx_lock"), - MtxTimedLock("mtx_timedlock"), - MtxTryLock("mtx_trylock"), - MtxUnlock("mtx_unlock"), - ClassLockGuard("lock_guard"), - ClassUniqueLock("unique_lock"), - IdentifierInfoInitialized(false) { - // Initialize the bug type. - BlockInCritSectionBugType.reset( - new BugType(this, "Call to blocking function in critical section", - "Blocking Error")); -} - void BlockInCriticalSectionChecker::initIdentifierInfo(ASTContext &Ctx) const { if (!IdentifierInfoInitialized) { /* In case of checking C code, or when the corresponding headers are not @@ -96,14 +86,7 @@ void BlockInCriticalSectionChecker::initIdentifierInfo(ASTContext &Ctx) const { } bool BlockInCriticalSectionChecker::isBlockingFunction(const CallEvent &Call) const { - if (Call.isCalled(SleepFn) - || Call.isCalled(GetcFn) - || Call.isCalled(FgetsFn) - || Call.isCalled(ReadFn) - || Call.isCalled(RecvFn)) { - return true; - } - return false; + return matchesAny(Call, SleepFn, GetcFn, FgetsFn, ReadFn, RecvFn); } bool BlockInCriticalSectionChecker::isLockFunction(const CallEvent &Call) const { @@ -113,15 +96,8 @@ bool BlockInCriticalSectionChecker::isLockFunction(const CallEvent &Call) const return true; } - if (Call.isCalled(LockFn) - || Call.isCalled(PthreadLockFn) - || Call.isCalled(PthreadTryLockFn) - || Call.isCalled(MtxLock) - || Call.isCalled(MtxTimedLock) - || Call.isCalled(MtxTryLock)) { - return true; - } - return false; + return matchesAny(Call, LockFn, PthreadLockFn, PthreadTryLockFn, MtxLock, + MtxTimedLock, MtxTryLock); } bool BlockInCriticalSectionChecker::isUnlockFunction(const CallEvent &Call) const { @@ -132,12 +108,7 @@ bool BlockInCriticalSectionChecker::isUnlockFunction(const CallEvent &Call) cons return true; } - if (Call.isCalled(UnlockFn) - || Call.isCalled(PthreadUnlockFn) - || Call.isCalled(MtxUnlock)) { - return true; - } - return false; + return matchesAny(Call, UnlockFn, PthreadUnlockFn, MtxUnlock); } void BlockInCriticalSectionChecker::checkPostCall(const CallEvent &Call, @@ -173,7 +144,7 @@ void BlockInCriticalSectionChecker::reportBlockInCritSection( llvm::raw_string_ostream os(msg); os << "Call to blocking function '" << Call.getCalleeIdentifier()->getName() << "' inside of critical section"; - auto R = std::make_unique<PathSensitiveBugReport>(*BlockInCritSectionBugType, + auto R = std::make_unique<PathSensitiveBugReport>(BlockInCritSectionBugType, os.str(), ErrNode); R->addRange(Call.getSourceRange()); R->markInteresting(BlockDescSym); diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp index 6c0caf3c4e78..a09db6d2d0ec 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp @@ -12,31 +12,33 @@ //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Checkers/Taint.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 <optional> using namespace clang; using namespace ento; namespace { class BoolAssignmentChecker : public Checker< check::Bind > { - mutable std::unique_ptr<BuiltinBug> BT; - void emitReport(ProgramStateRef state, CheckerContext &C) const; + const BugType BT{this, "Assignment of a non-Boolean value"}; + void emitReport(ProgramStateRef state, CheckerContext &C, + bool IsTainted = false) const; + public: void checkBind(SVal loc, SVal val, const Stmt *S, CheckerContext &C) const; }; } // end anonymous namespace -void BoolAssignmentChecker::emitReport(ProgramStateRef state, - CheckerContext &C) const { +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")); - - C.emitReport( - std::make_unique<PathSensitiveBugReport>(*BT, BT->getDescription(), N)); + StringRef Msg = IsTainted ? "Might assign a tainted non-Boolean value" + : "Assignment of a non-Boolean value"; + C.emitReport(std::make_unique<PathSensitiveBugReport>(BT, Msg, N)); } } @@ -70,7 +72,7 @@ void BoolAssignmentChecker::checkBind(SVal loc, SVal val, const Stmt *S, // Get the value of the right-hand side. We only care about values // that are defined (UnknownVals and UndefinedVals are handled by other // checkers). - Optional<NonLoc> NV = val.getAs<NonLoc>(); + std::optional<NonLoc> NV = val.getAs<NonLoc>(); if (!NV) return; @@ -90,6 +92,8 @@ void BoolAssignmentChecker::checkBind(SVal loc, SVal val, const Stmt *S, if (!StIn) emitReport(StOut, C); + if (StIn && StOut && taint::isTainted(state, *NV)) + emitReport(StOut, C, /*IsTainted=*/true); } void ento::registerBoolAssignmentChecker(CheckerManager &mgr) { diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp index 13781b336426..61521c259ca9 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp @@ -66,7 +66,8 @@ bool BuiltinFunctionChecker::evalCall(const CallEvent &Call, case Builtin::BI__builtin_expect: case Builtin::BI__builtin_expect_with_probability: case Builtin::BI__builtin_assume_aligned: - case Builtin::BI__builtin_addressof: { + case Builtin::BI__builtin_addressof: + case Builtin::BI__builtin_function_start: { // For __builtin_unpredictable, __builtin_expect, // __builtin_expect_with_probability and __builtin_assume_aligned, // just return the value of the subexpression. @@ -80,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 69b90be9aa7e..b7b64c3da4f6 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp @@ -12,11 +12,13 @@ //===----------------------------------------------------------------------===// #include "InterCheckerAPI.h" +#include "clang/Basic/Builtins.h" #include "clang/Basic/CharInfo.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/DynamicExtent.h" @@ -25,30 +27,21 @@ #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/raw_ostream.h" +#include <functional> +#include <optional> using namespace clang; using namespace ento; +using namespace std::placeholders; namespace { struct AnyArgExpr { - // FIXME: Remove constructor in C++17 to turn it into an aggregate. - AnyArgExpr(const Expr *Expression, unsigned ArgumentIndex) - : Expression{Expression}, ArgumentIndex{ArgumentIndex} {} const Expr *Expression; unsigned ArgumentIndex; }; - -struct SourceArgExpr : AnyArgExpr { - using AnyArgExpr::AnyArgExpr; // FIXME: Remove using in C++17. -}; - -struct DestinationArgExpr : AnyArgExpr { - using AnyArgExpr::AnyArgExpr; // FIXME: Same. -}; - -struct SizeArgExpr : AnyArgExpr { - using AnyArgExpr::AnyArgExpr; // FIXME: Same. -}; +struct SourceArgExpr : AnyArgExpr {}; +struct DestinationArgExpr : AnyArgExpr {}; +struct SizeArgExpr : AnyArgExpr {}; using ErrorMessage = SmallString<128>; enum class AccessKind { write, read }; @@ -72,6 +65,16 @@ static ErrorMessage createOutOfBoundErrorMsg(StringRef FunctionDescription, } enum class ConcatFnKind { none = 0, strcat = 1, strlcat = 2 }; + +enum class CharKind { Regular = 0, Wide }; +constexpr CharKind CK_Regular = CharKind::Regular; +constexpr CharKind CK_Wide = CharKind::Wide; + +static QualType getCharPtrType(ASTContext &Ctx, CharKind CK) { + return Ctx.getPointerType(CK == CharKind::Regular ? Ctx.CharTy + : Ctx.WideCharTy); +} + class CStringChecker : public Checker< eval::Call, check::PreStmt<DeclStmt>, check::LiveSymbols, @@ -79,23 +82,25 @@ class CStringChecker : public Checker< eval::Call, check::RegionChanges > { mutable std::unique_ptr<BugType> BT_Null, BT_Bounds, BT_Overlap, - BT_NotCString, BT_AdditionOverflow; + BT_NotCString, BT_AdditionOverflow, BT_UninitRead; - mutable const char *CurrentFunctionDescription; + mutable const char *CurrentFunctionDescription = nullptr; public: /// The filter is used to filter out the diagnostics which are not enabled by /// the user. struct CStringChecksFilter { - DefaultBool CheckCStringNullArg; - DefaultBool CheckCStringOutOfBounds; - DefaultBool CheckCStringBufferOverlap; - DefaultBool CheckCStringNotNullTerm; + bool CheckCStringNullArg = false; + bool CheckCStringOutOfBounds = false; + bool CheckCStringBufferOverlap = false; + bool CheckCStringNotNullTerm = false; + bool CheckCStringUninitializedRead = false; CheckerNameRef CheckNameCStringNullArg; CheckerNameRef CheckNameCStringOutOfBounds; CheckerNameRef CheckNameCStringBufferOverlap; CheckerNameRef CheckNameCStringNotNullTerm; + CheckerNameRef CheckNameCStringUninitializedRead; }; CStringChecksFilter Filter; @@ -115,33 +120,52 @@ public: const LocationContext *LCtx, const CallEvent *Call) const; - typedef void (CStringChecker::*FnCheck)(CheckerContext &, - const CallExpr *) const; + using FnCheck = std::function<void(const CStringChecker *, CheckerContext &, + const CallEvent &)>; + CallDescriptionMap<FnCheck> Callbacks = { - {{CDF_MaybeBuiltin, "memcpy", 3}, &CStringChecker::evalMemcpy}, - {{CDF_MaybeBuiltin, "mempcpy", 3}, &CStringChecker::evalMempcpy}, - {{CDF_MaybeBuiltin, "memcmp", 3}, &CStringChecker::evalMemcmp}, - {{CDF_MaybeBuiltin, "memmove", 3}, &CStringChecker::evalMemmove}, - {{CDF_MaybeBuiltin, "memset", 3}, &CStringChecker::evalMemset}, - {{CDF_MaybeBuiltin, "explicit_memset", 3}, &CStringChecker::evalMemset}, - {{CDF_MaybeBuiltin, "strcpy", 2}, &CStringChecker::evalStrcpy}, - {{CDF_MaybeBuiltin, "strncpy", 3}, &CStringChecker::evalStrncpy}, - {{CDF_MaybeBuiltin, "stpcpy", 2}, &CStringChecker::evalStpcpy}, - {{CDF_MaybeBuiltin, "strlcpy", 3}, &CStringChecker::evalStrlcpy}, - {{CDF_MaybeBuiltin, "strcat", 2}, &CStringChecker::evalStrcat}, - {{CDF_MaybeBuiltin, "strncat", 3}, &CStringChecker::evalStrncat}, - {{CDF_MaybeBuiltin, "strlcat", 3}, &CStringChecker::evalStrlcat}, - {{CDF_MaybeBuiltin, "strlen", 1}, &CStringChecker::evalstrLength}, - {{CDF_MaybeBuiltin, "strnlen", 2}, &CStringChecker::evalstrnLength}, - {{CDF_MaybeBuiltin, "strcmp", 2}, &CStringChecker::evalStrcmp}, - {{CDF_MaybeBuiltin, "strncmp", 3}, &CStringChecker::evalStrncmp}, - {{CDF_MaybeBuiltin, "strcasecmp", 2}, &CStringChecker::evalStrcasecmp}, - {{CDF_MaybeBuiltin, "strncasecmp", 3}, &CStringChecker::evalStrncasecmp}, - {{CDF_MaybeBuiltin, "strsep", 2}, &CStringChecker::evalStrsep}, - {{CDF_MaybeBuiltin, "bcopy", 3}, &CStringChecker::evalBcopy}, - {{CDF_MaybeBuiltin, "bcmp", 3}, &CStringChecker::evalMemcmp}, - {{CDF_MaybeBuiltin, "bzero", 2}, &CStringChecker::evalBzero}, - {{CDF_MaybeBuiltin, "explicit_bzero", 2}, &CStringChecker::evalBzero}, + {{CDF_MaybeBuiltin, {"memcpy"}, 3}, + std::bind(&CStringChecker::evalMemcpy, _1, _2, _3, CK_Regular)}, + {{CDF_MaybeBuiltin, {"wmemcpy"}, 3}, + std::bind(&CStringChecker::evalMemcpy, _1, _2, _3, CK_Wide)}, + {{CDF_MaybeBuiltin, {"mempcpy"}, 3}, + std::bind(&CStringChecker::evalMempcpy, _1, _2, _3, CK_Regular)}, + {{CDF_None, {"wmempcpy"}, 3}, + std::bind(&CStringChecker::evalMempcpy, _1, _2, _3, CK_Wide)}, + {{CDF_MaybeBuiltin, {"memcmp"}, 3}, + std::bind(&CStringChecker::evalMemcmp, _1, _2, _3, CK_Regular)}, + {{CDF_MaybeBuiltin, {"wmemcmp"}, 3}, + std::bind(&CStringChecker::evalMemcmp, _1, _2, _3, CK_Wide)}, + {{CDF_MaybeBuiltin, {"memmove"}, 3}, + std::bind(&CStringChecker::evalMemmove, _1, _2, _3, CK_Regular)}, + {{CDF_MaybeBuiltin, {"wmemmove"}, 3}, + std::bind(&CStringChecker::evalMemmove, _1, _2, _3, CK_Wide)}, + {{CDF_MaybeBuiltin, {"memset"}, 3}, &CStringChecker::evalMemset}, + {{CDF_MaybeBuiltin, {"explicit_memset"}, 3}, &CStringChecker::evalMemset}, + {{CDF_MaybeBuiltin, {"strcpy"}, 2}, &CStringChecker::evalStrcpy}, + {{CDF_MaybeBuiltin, {"strncpy"}, 3}, &CStringChecker::evalStrncpy}, + {{CDF_MaybeBuiltin, {"stpcpy"}, 2}, &CStringChecker::evalStpcpy}, + {{CDF_MaybeBuiltin, {"strlcpy"}, 3}, &CStringChecker::evalStrlcpy}, + {{CDF_MaybeBuiltin, {"strcat"}, 2}, &CStringChecker::evalStrcat}, + {{CDF_MaybeBuiltin, {"strncat"}, 3}, &CStringChecker::evalStrncat}, + {{CDF_MaybeBuiltin, {"strlcat"}, 3}, &CStringChecker::evalStrlcat}, + {{CDF_MaybeBuiltin, {"strlen"}, 1}, &CStringChecker::evalstrLength}, + {{CDF_MaybeBuiltin, {"wcslen"}, 1}, &CStringChecker::evalstrLength}, + {{CDF_MaybeBuiltin, {"strnlen"}, 2}, &CStringChecker::evalstrnLength}, + {{CDF_MaybeBuiltin, {"wcsnlen"}, 2}, &CStringChecker::evalstrnLength}, + {{CDF_MaybeBuiltin, {"strcmp"}, 2}, &CStringChecker::evalStrcmp}, + {{CDF_MaybeBuiltin, {"strncmp"}, 3}, &CStringChecker::evalStrncmp}, + {{CDF_MaybeBuiltin, {"strcasecmp"}, 2}, &CStringChecker::evalStrcasecmp}, + {{CDF_MaybeBuiltin, {"strncasecmp"}, 3}, + &CStringChecker::evalStrncasecmp}, + {{CDF_MaybeBuiltin, {"strsep"}, 2}, &CStringChecker::evalStrsep}, + {{CDF_MaybeBuiltin, {"bcopy"}, 3}, &CStringChecker::evalBcopy}, + {{CDF_MaybeBuiltin, {"bcmp"}, 3}, + std::bind(&CStringChecker::evalMemcmp, _1, _2, _3, CK_Regular)}, + {{CDF_MaybeBuiltin, {"bzero"}, 2}, &CStringChecker::evalBzero}, + {{CDF_MaybeBuiltin, {"explicit_bzero"}, 2}, &CStringChecker::evalBzero}, + {{CDF_MaybeBuiltin, {"sprintf"}, 2}, &CStringChecker::evalSprintf}, + {{CDF_MaybeBuiltin, {"snprintf"}, 2}, &CStringChecker::evalSnprintf}, }; // These require a bit of special handling. @@ -149,51 +173,53 @@ public: StdCopyBackward{{"std", "copy_backward"}, 3}; FnCheck identifyCall(const CallEvent &Call, CheckerContext &C) const; - void evalMemcpy(CheckerContext &C, const CallExpr *CE) const; - void evalMempcpy(CheckerContext &C, const CallExpr *CE) const; - void evalMemmove(CheckerContext &C, const CallExpr *CE) const; - void evalBcopy(CheckerContext &C, const CallExpr *CE) const; - void evalCopyCommon(CheckerContext &C, const CallExpr *CE, + void evalMemcpy(CheckerContext &C, const CallEvent &Call, CharKind CK) const; + void evalMempcpy(CheckerContext &C, const CallEvent &Call, CharKind CK) const; + void evalMemmove(CheckerContext &C, const CallEvent &Call, CharKind CK) const; + void evalBcopy(CheckerContext &C, const CallEvent &Call) const; + void evalCopyCommon(CheckerContext &C, const CallEvent &Call, ProgramStateRef state, SizeArgExpr Size, DestinationArgExpr Dest, SourceArgExpr Source, - bool Restricted, bool IsMempcpy) const; + bool Restricted, bool IsMempcpy, CharKind CK) const; - void evalMemcmp(CheckerContext &C, const CallExpr *CE) const; + void evalMemcmp(CheckerContext &C, const CallEvent &Call, CharKind CK) const; - void evalstrLength(CheckerContext &C, const CallExpr *CE) const; - void evalstrnLength(CheckerContext &C, const CallExpr *CE) const; - void evalstrLengthCommon(CheckerContext &C, - const CallExpr *CE, + void evalstrLength(CheckerContext &C, const CallEvent &Call) const; + void evalstrnLength(CheckerContext &C, const CallEvent &Call) const; + void evalstrLengthCommon(CheckerContext &C, const CallEvent &Call, bool IsStrnlen = false) const; - void evalStrcpy(CheckerContext &C, const CallExpr *CE) const; - void evalStrncpy(CheckerContext &C, const CallExpr *CE) const; - void evalStpcpy(CheckerContext &C, const CallExpr *CE) const; - void evalStrlcpy(CheckerContext &C, const CallExpr *CE) const; - void evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, bool ReturnEnd, - bool IsBounded, ConcatFnKind appendK, + void evalStrcpy(CheckerContext &C, const CallEvent &Call) const; + void evalStrncpy(CheckerContext &C, const CallEvent &Call) const; + void evalStpcpy(CheckerContext &C, const CallEvent &Call) const; + void evalStrlcpy(CheckerContext &C, const CallEvent &Call) const; + void evalStrcpyCommon(CheckerContext &C, const CallEvent &Call, + bool ReturnEnd, bool IsBounded, ConcatFnKind appendK, bool returnPtr = true) const; - void evalStrcat(CheckerContext &C, const CallExpr *CE) const; - void evalStrncat(CheckerContext &C, const CallExpr *CE) const; - void evalStrlcat(CheckerContext &C, const CallExpr *CE) const; + void evalStrcat(CheckerContext &C, const CallEvent &Call) const; + void evalStrncat(CheckerContext &C, const CallEvent &Call) const; + void evalStrlcat(CheckerContext &C, const CallEvent &Call) const; - void evalStrcmp(CheckerContext &C, const CallExpr *CE) const; - void evalStrncmp(CheckerContext &C, const CallExpr *CE) const; - void evalStrcasecmp(CheckerContext &C, const CallExpr *CE) const; - void evalStrncasecmp(CheckerContext &C, const CallExpr *CE) const; - void evalStrcmpCommon(CheckerContext &C, - const CallExpr *CE, - bool IsBounded = false, - bool IgnoreCase = false) const; + void evalStrcmp(CheckerContext &C, const CallEvent &Call) const; + void evalStrncmp(CheckerContext &C, const CallEvent &Call) const; + void evalStrcasecmp(CheckerContext &C, const CallEvent &Call) const; + void evalStrncasecmp(CheckerContext &C, const CallEvent &Call) const; + void evalStrcmpCommon(CheckerContext &C, const CallEvent &Call, + bool IsBounded = false, bool IgnoreCase = false) const; - void evalStrsep(CheckerContext &C, const CallExpr *CE) const; + void evalStrsep(CheckerContext &C, const CallEvent &Call) const; - void evalStdCopy(CheckerContext &C, const CallExpr *CE) const; - void evalStdCopyBackward(CheckerContext &C, const CallExpr *CE) const; - void evalStdCopyCommon(CheckerContext &C, const CallExpr *CE) const; - void evalMemset(CheckerContext &C, const CallExpr *CE) const; - void evalBzero(CheckerContext &C, const CallExpr *CE) const; + void evalStdCopy(CheckerContext &C, const CallEvent &Call) const; + void evalStdCopyBackward(CheckerContext &C, const CallEvent &Call) const; + void evalStdCopyCommon(CheckerContext &C, const CallEvent &Call) const; + void evalMemset(CheckerContext &C, const CallEvent &Call) const; + void evalBzero(CheckerContext &C, const CallEvent &Call) const; + + void evalSprintf(CheckerContext &C, const CallEvent &Call) const; + void evalSnprintf(CheckerContext &C, const CallEvent &Call) const; + void evalSprintfCommon(CheckerContext &C, const CallEvent &Call, + bool IsBounded, bool IsBuiltin) const; // Utility methods std::pair<ProgramStateRef , ProgramStateRef > @@ -219,11 +245,34 @@ public: const Expr *expr, SVal val) const; - static ProgramStateRef InvalidateBuffer(CheckerContext &C, - ProgramStateRef state, - const Expr *Ex, SVal V, - bool IsSourceBuffer, - const Expr *Size); + /// Invalidate the destination buffer determined by characters copied. + static ProgramStateRef + invalidateDestinationBufferBySize(CheckerContext &C, ProgramStateRef S, + const Expr *BufE, SVal BufV, SVal SizeV, + QualType SizeTy); + + /// Operation never overflows, do not invalidate the super region. + static ProgramStateRef invalidateDestinationBufferNeverOverflows( + CheckerContext &C, ProgramStateRef S, const Expr *BufE, SVal BufV); + + /// We do not know whether the operation can overflow (e.g. size is unknown), + /// invalidate the super region and escape related pointers. + static ProgramStateRef invalidateDestinationBufferAlwaysEscapeSuperRegion( + CheckerContext &C, ProgramStateRef S, const Expr *BufE, SVal BufV); + + /// Invalidate the source buffer for escaping pointers. + static ProgramStateRef invalidateSourceBuffer(CheckerContext &C, + ProgramStateRef S, + const Expr *BufE, SVal BufV); + + /// @param InvalidationTraitOperations Determine how to invlidate the + /// MemRegion by setting the invalidation traits. Return true to cause pointer + /// escape, or false otherwise. + static ProgramStateRef invalidateBufferAux( + CheckerContext &C, ProgramStateRef State, const Expr *Ex, SVal V, + llvm::function_ref<bool(RegionAndSymbolInvalidationTraits &, + const MemRegion *)> + InvalidationTraitOperations); static bool SummarizeRegion(raw_ostream &os, ASTContext &Ctx, const MemRegion *MR); @@ -237,13 +286,16 @@ public: AnyArgExpr Arg, SVal l) const; ProgramStateRef CheckLocation(CheckerContext &C, ProgramStateRef state, AnyArgExpr Buffer, SVal Element, - AccessKind Access) const; + AccessKind Access, + CharKind CK = CharKind::Regular) const; ProgramStateRef CheckBufferAccess(CheckerContext &C, ProgramStateRef State, AnyArgExpr Buffer, SizeArgExpr Size, - AccessKind Access) const; + AccessKind Access, + CharKind CK = CharKind::Regular) const; ProgramStateRef CheckOverlap(CheckerContext &C, ProgramStateRef state, SizeArgExpr Size, AnyArgExpr First, - AnyArgExpr Second) const; + AnyArgExpr Second, + CharKind CK = CharKind::Regular) const; void emitOverlapBug(CheckerContext &C, ProgramStateRef state, const Stmt *First, @@ -256,7 +308,8 @@ public: void emitNotCStringBug(CheckerContext &C, ProgramStateRef State, const Stmt *S, StringRef WarningMsg) const; void emitAdditionOverflowBug(CheckerContext &C, ProgramStateRef State) const; - + void emitUninitializedReadBug(CheckerContext &C, ProgramStateRef State, + const Expr *E) const; ProgramStateRef checkAdditionOverflow(CheckerContext &C, ProgramStateRef state, NonLoc left, @@ -265,10 +318,9 @@ public: // Return true if the destination buffer of the copy function may be in bound. // Expects SVal of Size to be positive and unsigned. // Expects SVal of FirstBuf to be a FieldRegion. - static bool IsFirstBufInBound(CheckerContext &C, - ProgramStateRef state, - const Expr *FirstBuf, - const Expr *Size); + static bool isFirstBufInBound(CheckerContext &C, ProgramStateRef State, + SVal BufVal, QualType BufTy, SVal LengthVal, + QualType LengthTy); }; } //end anonymous namespace @@ -282,7 +334,7 @@ REGISTER_MAP_WITH_PROGRAMSTATE(CStringLength, const MemRegion *, SVal) std::pair<ProgramStateRef , ProgramStateRef > CStringChecker::assumeZero(CheckerContext &C, ProgramStateRef state, SVal V, QualType Ty) { - Optional<DefinedSVal> val = V.getAs<DefinedSVal>(); + std::optional<DefinedSVal> val = V.getAs<DefinedSVal>(); if (!val) return std::pair<ProgramStateRef , ProgramStateRef >(state, state); @@ -325,7 +377,8 @@ ProgramStateRef CStringChecker::checkNonNull(CheckerContext &C, ProgramStateRef CStringChecker::CheckLocation(CheckerContext &C, ProgramStateRef state, AnyArgExpr Buffer, SVal Element, - AccessKind Access) const { + AccessKind Access, + CharKind CK) const { // If a previous check has failed, propagate the failure. if (!state) @@ -340,19 +393,38 @@ ProgramStateRef CStringChecker::CheckLocation(CheckerContext &C, if (!ER) return state; - if (ER->getValueType() != C.getASTContext().CharTy) - return state; + SValBuilder &svalBuilder = C.getSValBuilder(); + ASTContext &Ctx = svalBuilder.getContext(); + + // Get the index of the accessed element. + NonLoc Idx = ER->getIndex(); + + if (CK == CharKind::Regular) { + if (ER->getValueType() != Ctx.CharTy) + return state; + } else { + if (ER->getValueType() != Ctx.WideCharTy) + return state; + + QualType SizeTy = Ctx.getSizeType(); + NonLoc WideSize = + svalBuilder + .makeIntVal(Ctx.getTypeSizeInChars(Ctx.WideCharTy).getQuantity(), + SizeTy) + .castAs<NonLoc>(); + SVal Offset = svalBuilder.evalBinOpNN(state, BO_Mul, Idx, WideSize, SizeTy); + if (Offset.isUnknown()) + return state; + Idx = Offset.castAs<NonLoc>(); + } // Get the size of the array. const auto *superReg = cast<SubRegion>(ER->getSuperRegion()); DefinedOrUnknownSVal Size = getDynamicExtent(state, superReg, C.getSValBuilder()); - // Get the index of the accessed element. - DefinedOrUnknownSVal Idx = ER->getIndex().castAs<DefinedOrUnknownSVal>(); - - ProgramStateRef StInBound = state->assumeInBound(Idx, Size, true); - ProgramStateRef StOutBound = state->assumeInBound(Idx, Size, false); + ProgramStateRef StInBound, StOutBound; + std::tie(StInBound, StOutBound) = state->assumeInBoundDual(Idx, Size); if (StOutBound && !StInBound) { // These checks are either enabled by the CString out-of-bounds checker // explicitly or implicitly by the Malloc checker. @@ -367,16 +439,24 @@ ProgramStateRef CStringChecker::CheckLocation(CheckerContext &C, return nullptr; } + // Ensure that we wouldn't read uninitialized value. + if (Access == AccessKind::read) { + if (Filter.CheckCStringUninitializedRead && + StInBound->getSVal(ER).isUndef()) { + emitUninitializedReadBug(C, StInBound, Buffer.Expression); + return nullptr; + } + } + // Array bound check succeeded. From this point forward the array bound // should always succeed. return StInBound; } -ProgramStateRef CStringChecker::CheckBufferAccess(CheckerContext &C, - ProgramStateRef State, - AnyArgExpr Buffer, - SizeArgExpr Size, - AccessKind Access) const { +ProgramStateRef +CStringChecker::CheckBufferAccess(CheckerContext &C, ProgramStateRef State, + AnyArgExpr Buffer, SizeArgExpr Size, + AccessKind Access, CharKind CK) const { // If a previous check has failed, propagate the failure. if (!State) return nullptr; @@ -385,7 +465,7 @@ ProgramStateRef CStringChecker::CheckBufferAccess(CheckerContext &C, ASTContext &Ctx = svalBuilder.getContext(); QualType SizeTy = Size.Expression->getType(); - QualType PtrTy = Ctx.getPointerType(Ctx.CharTy); + QualType PtrTy = getCharPtrType(Ctx, CK); // Check that the first buffer is non-null. SVal BufVal = C.getSVal(Buffer.Expression); @@ -397,11 +477,19 @@ ProgramStateRef CStringChecker::CheckBufferAccess(CheckerContext &C, 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. SVal LengthVal = C.getSVal(Size.Expression); - Optional<NonLoc> Length = LengthVal.getAs<NonLoc>(); + std::optional<NonLoc> Length = LengthVal.getAs<NonLoc>(); if (!Length) return State; @@ -413,14 +501,11 @@ ProgramStateRef CStringChecker::CheckBufferAccess(CheckerContext &C, NonLoc LastOffset = Offset.castAs<NonLoc>(); // Check that the first buffer is sufficiently long. - SVal BufStart = - svalBuilder.evalCast(BufVal, PtrTy, Buffer.Expression->getType()); - if (Optional<Loc> BufLoc = BufStart.getAs<Loc>()) { + if (std::optional<Loc> BufLoc = BufStart.getAs<Loc>()) { SVal BufEnd = svalBuilder.evalBinOpLN(State, BO_Add, *BufLoc, LastOffset, PtrTy); - - State = CheckLocation(C, State, Buffer, BufEnd, Access); + State = CheckLocation(C, State, Buffer, BufEnd, Access, CK); // If the buffer isn't large enough, abort. if (!State) @@ -434,7 +519,8 @@ ProgramStateRef CStringChecker::CheckBufferAccess(CheckerContext &C, ProgramStateRef CStringChecker::CheckOverlap(CheckerContext &C, ProgramStateRef state, SizeArgExpr Size, AnyArgExpr First, - AnyArgExpr Second) const { + AnyArgExpr Second, + CharKind CK) const { if (!Filter.CheckCStringBufferOverlap) return state; @@ -448,16 +534,21 @@ ProgramStateRef CStringChecker::CheckOverlap(CheckerContext &C, ProgramStateRef stateTrue, stateFalse; + // Assume different address spaces cannot overlap. + if (First.Expression->getType()->getPointeeType().getAddressSpace() != + Second.Expression->getType()->getPointeeType().getAddressSpace()) + return state; + // Get the buffer values and make sure they're known locations. const LocationContext *LCtx = C.getLocationContext(); SVal firstVal = state->getSVal(First.Expression, LCtx); SVal secondVal = state->getSVal(Second.Expression, LCtx); - Optional<Loc> firstLoc = firstVal.getAs<Loc>(); + std::optional<Loc> firstLoc = firstVal.getAs<Loc>(); if (!firstLoc) return state; - Optional<Loc> secondLoc = secondVal.getAs<Loc>(); + std::optional<Loc> secondLoc = secondVal.getAs<Loc>(); if (!secondLoc) return state; @@ -480,7 +571,7 @@ ProgramStateRef CStringChecker::CheckOverlap(CheckerContext &C, QualType cmpTy = svalBuilder.getConditionType(); SVal reverse = svalBuilder.evalBinOpLL(state, BO_GT, *firstLoc, *secondLoc, cmpTy); - Optional<DefinedOrUnknownSVal> reverseTest = + std::optional<DefinedOrUnknownSVal> reverseTest = reverse.getAs<DefinedOrUnknownSVal>(); if (!reverseTest) return state; @@ -501,31 +592,31 @@ ProgramStateRef CStringChecker::CheckOverlap(CheckerContext &C, // Get the length, and make sure it too is known. SVal LengthVal = state->getSVal(Size.Expression, LCtx); - Optional<NonLoc> Length = LengthVal.getAs<NonLoc>(); + std::optional<NonLoc> Length = LengthVal.getAs<NonLoc>(); if (!Length) return state; // Convert the first buffer's start address to char*. // Bail out if the cast fails. ASTContext &Ctx = svalBuilder.getContext(); - QualType CharPtrTy = Ctx.getPointerType(Ctx.CharTy); + QualType CharPtrTy = getCharPtrType(Ctx, CK); SVal FirstStart = svalBuilder.evalCast(*firstLoc, CharPtrTy, First.Expression->getType()); - Optional<Loc> FirstStartLoc = FirstStart.getAs<Loc>(); + std::optional<Loc> FirstStartLoc = FirstStart.getAs<Loc>(); if (!FirstStartLoc) return state; // Compute the end of the first buffer. Bail out if THAT fails. SVal FirstEnd = svalBuilder.evalBinOpLN(state, BO_Add, *FirstStartLoc, *Length, CharPtrTy); - Optional<Loc> FirstEndLoc = FirstEnd.getAs<Loc>(); + std::optional<Loc> FirstEndLoc = FirstEnd.getAs<Loc>(); if (!FirstEndLoc) return state; // Is the end of the first buffer past the start of the second buffer? SVal Overlap = svalBuilder.evalBinOpLL(state, BO_GT, *FirstEndLoc, *secondLoc, cmpTy); - Optional<DefinedOrUnknownSVal> OverlapTest = + std::optional<DefinedOrUnknownSVal> OverlapTest = Overlap.getAs<DefinedOrUnknownSVal>(); if (!OverlapTest) return state; @@ -565,13 +656,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); @@ -579,23 +672,39 @@ void CStringChecker::emitNullArgBug(CheckerContext &C, ProgramStateRef State, } } +void CStringChecker::emitUninitializedReadBug(CheckerContext &C, + ProgramStateRef State, + const Expr *E) const { + if (ExplodedNode *N = C.generateErrorNode(State)) { + const char *Msg = + "Bytes string function accesses uninitialized/garbage values"; + if (!BT_UninitRead) + BT_UninitRead.reset(new BugType(Filter.CheckNameCStringUninitializedRead, + "Accessing unitialized/garbage values")); + + auto Report = + std::make_unique<PathSensitiveBugReport>(*BT_UninitRead, Msg, N); + Report->addRange(E->getSourceRange()); + bugreporter::trackExpressionValue(N, E, *Report); + C.emitReport(std::move(Report)); + } +} + void CStringChecker::emitOutOfBoundsBug(CheckerContext &C, ProgramStateRef State, const Stmt *S, 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)); } @@ -605,10 +714,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); @@ -621,10 +732,13 @@ void CStringChecker::emitNotCStringBug(CheckerContext &C, ProgramStateRef State, void CStringChecker::emitAdditionOverflowBug(CheckerContext &C, ProgramStateRef State) const { if (ExplodedNode *N = C.generateErrorNode(State)) { - if (!BT_NotCString) - BT_NotCString.reset( - new BuiltinBug(Filter.CheckNameCStringOutOfBounds, "API", - "Sum of expressions causes overflow.")); + 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 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 @@ -633,8 +747,8 @@ void CStringChecker::emitAdditionOverflowBug(CheckerContext &C, "This expression will create a string whose length is too big to " "be represented as a size_t"; - auto Report = - std::make_unique<PathSensitiveBugReport>(*BT_NotCString, WarningMsg, N); + auto Report = std::make_unique<PathSensitiveBugReport>(*BT_AdditionOverflow, + WarningMsg, N); C.emitReport(std::move(Report)); } } @@ -659,7 +773,7 @@ ProgramStateRef CStringChecker::checkAdditionOverflow(CheckerContext &C, NonLoc maxVal = svalBuilder.makeIntVal(maxValInt); SVal maxMinusRight; - if (right.getAs<nonloc::ConcreteInt>()) { + if (isa<nonloc::ConcreteInt>(right)) { maxMinusRight = svalBuilder.evalBinOpNN(state, BO_Sub, maxVal, right, sizeTy); } else { @@ -670,7 +784,7 @@ ProgramStateRef CStringChecker::checkAdditionOverflow(CheckerContext &C, left = right; } - if (Optional<NonLoc> maxMinusRightNL = maxMinusRight.getAs<NonLoc>()) { + if (std::optional<NonLoc> maxMinusRightNL = maxMinusRight.getAs<NonLoc>()) { QualType cmpTy = svalBuilder.getConditionType(); // If left > max - right, we have an overflow. SVal willOverflow = svalBuilder.evalBinOpNN(state, BO_GT, left, @@ -756,7 +870,7 @@ SVal CStringChecker::getCStringLengthForRegion(CheckerContext &C, C.blockCount()); if (!hypothetical) { - if (Optional<NonLoc> strLn = strLength.getAs<NonLoc>()) { + if (std::optional<NonLoc> strLn = strLength.getAs<NonLoc>()) { // In case of unbounded calls strlen etc bound the range to SIZE_MAX/4 BasicValueFactory &BVF = svalBuilder.getBasicValueFactory(); const llvm::APSInt &maxValInt = BVF.getMaxValue(sizeTy); @@ -764,8 +878,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); @@ -782,7 +896,7 @@ SVal CStringChecker::getCStringLength(CheckerContext &C, ProgramStateRef &state, // If we can't get a region, see if it's something we /know/ isn't a // C string. In the context of locations, the only time we can issue such // a warning is for labels. - if (Optional<loc::GotoLabel> Label = Buf.getAs<loc::GotoLabel>()) { + if (std::optional<loc::GotoLabel> Label = Buf.getAs<loc::GotoLabel>()) { if (Filter.CheckCStringNotNullTerm) { SmallString<120> buf; llvm::raw_svector_ostream os(buf); @@ -811,11 +925,25 @@ SVal CStringChecker::getCStringLength(CheckerContext &C, ProgramStateRef &state, SValBuilder &svalBuilder = C.getSValBuilder(); QualType sizeTy = svalBuilder.getContext().getSizeType(); const StringLiteral *strLit = cast<StringRegion>(MR)->getStringLiteral(); - return svalBuilder.makeIntVal(strLit->getByteLength(), sizeTy); + 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: @@ -869,43 +997,40 @@ const StringLiteral *CStringChecker::getCStringLiteral(CheckerContext &C, return strRegion->getStringLiteral(); } -bool CStringChecker::IsFirstBufInBound(CheckerContext &C, - ProgramStateRef state, - const Expr *FirstBuf, - const Expr *Size) { +bool CStringChecker::isFirstBufInBound(CheckerContext &C, ProgramStateRef State, + SVal BufVal, QualType BufTy, + SVal LengthVal, QualType LengthTy) { // If we do not know that the buffer is long enough we return 'true'. // Otherwise the parent region of this field region would also get // invalidated, which would lead to warnings based on an unknown state. + if (LengthVal.isUnknown()) + return false; + // Originally copied from CheckBufferAccess and CheckLocation. - SValBuilder &svalBuilder = C.getSValBuilder(); - ASTContext &Ctx = svalBuilder.getContext(); - const LocationContext *LCtx = C.getLocationContext(); + SValBuilder &SB = C.getSValBuilder(); + ASTContext &Ctx = C.getASTContext(); - QualType sizeTy = Size->getType(); QualType PtrTy = Ctx.getPointerType(Ctx.CharTy); - SVal BufVal = state->getSVal(FirstBuf, LCtx); - SVal LengthVal = state->getSVal(Size, LCtx); - Optional<NonLoc> Length = LengthVal.getAs<NonLoc>(); + std::optional<NonLoc> Length = LengthVal.getAs<NonLoc>(); if (!Length) return true; // cf top comment. // Compute the offset of the last element to be accessed: size-1. - NonLoc One = svalBuilder.makeIntVal(1, sizeTy).castAs<NonLoc>(); - SVal Offset = svalBuilder.evalBinOpNN(state, BO_Sub, *Length, One, sizeTy); + NonLoc One = SB.makeIntVal(1, LengthTy).castAs<NonLoc>(); + SVal Offset = SB.evalBinOpNN(State, BO_Sub, *Length, One, LengthTy); if (Offset.isUnknown()) return true; // cf top comment NonLoc LastOffset = Offset.castAs<NonLoc>(); // Check that the first buffer is sufficiently long. - SVal BufStart = svalBuilder.evalCast(BufVal, PtrTy, FirstBuf->getType()); - Optional<Loc> BufLoc = BufStart.getAs<Loc>(); + SVal BufStart = SB.evalCast(BufVal, PtrTy, BufTy); + std::optional<Loc> BufLoc = BufStart.getAs<Loc>(); if (!BufLoc) return true; // cf top comment. - SVal BufEnd = - svalBuilder.evalBinOpLN(state, BO_Add, *BufLoc, LastOffset, PtrTy); + SVal BufEnd = SB.evalBinOpLN(State, BO_Add, *BufLoc, LastOffset, PtrTy); // Check for out of bound array element access. const MemRegion *R = BufEnd.getAsRegion(); @@ -919,33 +1044,95 @@ bool CStringChecker::IsFirstBufInBound(CheckerContext &C, // FIXME: Does this crash when a non-standard definition // of a library function is encountered? assert(ER->getValueType() == C.getASTContext().CharTy && - "IsFirstBufInBound should only be called with char* ElementRegions"); + "isFirstBufInBound should only be called with char* ElementRegions"); // Get the size of the array. const SubRegion *superReg = cast<SubRegion>(ER->getSuperRegion()); - DefinedOrUnknownSVal SizeDV = getDynamicExtent(state, superReg, svalBuilder); + DefinedOrUnknownSVal SizeDV = getDynamicExtent(State, superReg, SB); // Get the index of the accessed element. DefinedOrUnknownSVal Idx = ER->getIndex().castAs<DefinedOrUnknownSVal>(); - ProgramStateRef StInBound = state->assumeInBound(Idx, SizeDV, true); + ProgramStateRef StInBound = State->assumeInBound(Idx, SizeDV, true); return static_cast<bool>(StInBound); } -ProgramStateRef CStringChecker::InvalidateBuffer(CheckerContext &C, - ProgramStateRef state, - const Expr *E, SVal V, - bool IsSourceBuffer, - const Expr *Size) { - Optional<Loc> L = V.getAs<Loc>(); +ProgramStateRef CStringChecker::invalidateDestinationBufferBySize( + CheckerContext &C, ProgramStateRef S, const Expr *BufE, SVal BufV, + SVal SizeV, QualType SizeTy) { + auto InvalidationTraitOperations = + [&C, S, BufTy = BufE->getType(), BufV, SizeV, + SizeTy](RegionAndSymbolInvalidationTraits &ITraits, const MemRegion *R) { + // If destination buffer is a field region and access is in bound, do + // not invalidate its super region. + if (MemRegion::FieldRegionKind == R->getKind() && + isFirstBufInBound(C, S, BufV, BufTy, SizeV, SizeTy)) { + ITraits.setTrait( + R, + RegionAndSymbolInvalidationTraits::TK_DoNotInvalidateSuperRegion); + } + return false; + }; + + return invalidateBufferAux(C, S, BufE, BufV, InvalidationTraitOperations); +} + +ProgramStateRef +CStringChecker::invalidateDestinationBufferAlwaysEscapeSuperRegion( + CheckerContext &C, ProgramStateRef S, const Expr *BufE, SVal BufV) { + auto InvalidationTraitOperations = [](RegionAndSymbolInvalidationTraits &, + const MemRegion *R) { + return isa<FieldRegion>(R); + }; + + return invalidateBufferAux(C, S, BufE, BufV, InvalidationTraitOperations); +} + +ProgramStateRef CStringChecker::invalidateDestinationBufferNeverOverflows( + CheckerContext &C, ProgramStateRef S, const Expr *BufE, SVal BufV) { + auto InvalidationTraitOperations = + [](RegionAndSymbolInvalidationTraits &ITraits, const MemRegion *R) { + if (MemRegion::FieldRegionKind == R->getKind()) + ITraits.setTrait( + R, + RegionAndSymbolInvalidationTraits::TK_DoNotInvalidateSuperRegion); + return false; + }; + + return invalidateBufferAux(C, S, BufE, BufV, InvalidationTraitOperations); +} + +ProgramStateRef CStringChecker::invalidateSourceBuffer(CheckerContext &C, + ProgramStateRef S, + const Expr *BufE, + SVal BufV) { + auto InvalidationTraitOperations = + [](RegionAndSymbolInvalidationTraits &ITraits, const MemRegion *R) { + ITraits.setTrait( + R->getBaseRegion(), + RegionAndSymbolInvalidationTraits::TK_PreserveContents); + ITraits.setTrait(R, + RegionAndSymbolInvalidationTraits::TK_SuppressEscape); + return true; + }; + + return invalidateBufferAux(C, S, BufE, BufV, InvalidationTraitOperations); +} + +ProgramStateRef CStringChecker::invalidateBufferAux( + CheckerContext &C, ProgramStateRef State, const Expr *E, SVal V, + llvm::function_ref<bool(RegionAndSymbolInvalidationTraits &, + const MemRegion *)> + InvalidationTraitOperations) { + std::optional<Loc> L = V.getAs<Loc>(); if (!L) - return state; + return State; // FIXME: This is a simplified version of what's in CFRefCount.cpp -- it makes // some assumptions about the value that CFRefCount can't. Even so, it should // probably be refactored. - if (Optional<loc::MemRegionVal> MR = L->getAs<loc::MemRegionVal>()) { + if (std::optional<loc::MemRegionVal> MR = L->getAs<loc::MemRegionVal>()) { const MemRegion *R = MR->getRegion()->StripCasts(); // Are we dealing with an ElementRegion? If so, we should be invalidating @@ -957,29 +1144,10 @@ ProgramStateRef CStringChecker::InvalidateBuffer(CheckerContext &C, // Invalidate this region. const LocationContext *LCtx = C.getPredecessor()->getLocationContext(); - - bool CausesPointerEscape = false; RegionAndSymbolInvalidationTraits ITraits; - // Invalidate and escape only indirect regions accessible through the source - // buffer. - if (IsSourceBuffer) { - ITraits.setTrait(R->getBaseRegion(), - RegionAndSymbolInvalidationTraits::TK_PreserveContents); - ITraits.setTrait(R, RegionAndSymbolInvalidationTraits::TK_SuppressEscape); - CausesPointerEscape = true; - } else { - const MemRegion::Kind& K = R->getKind(); - if (K == MemRegion::FieldRegionKind) - if (Size && IsFirstBufInBound(C, state, E, Size)) { - // If destination buffer is a field region and access is in bound, - // do not invalidate its super region. - ITraits.setTrait( - R, - RegionAndSymbolInvalidationTraits::TK_DoNotInvalidateSuperRegion); - } - } + bool CausesPointerEscape = InvalidationTraitOperations(ITraits, R); - return state->invalidateRegions(R, E, C.blockCount(), LCtx, + return State->invalidateRegions(R, E, C.blockCount(), LCtx, CausesPointerEscape, nullptr, nullptr, &ITraits); } @@ -987,7 +1155,7 @@ ProgramStateRef CStringChecker::InvalidateBuffer(CheckerContext &C, // If we have a non-region value by chance, just remove the binding. // FIXME: is this necessary or correct? This handles the non-Region // cases. Is it ever valid to store to these? - return state->killBinding(*L); + return State->killBinding(*L); } bool CStringChecker::SummarizeRegion(raw_ostream &os, ASTContext &Ctx, @@ -1009,23 +1177,20 @@ bool CStringChecker::SummarizeRegion(raw_ostream &os, ASTContext &Ctx, case MemRegion::CXXThisRegionKind: case MemRegion::CXXTempObjectRegionKind: os << "a C++ temp object of type " - << cast<TypedValueRegion>(MR)->getValueType().getAsString(); + << cast<TypedValueRegion>(MR)->getValueType(); return true; case MemRegion::NonParamVarRegionKind: - os << "a variable of type" - << cast<TypedValueRegion>(MR)->getValueType().getAsString(); + os << "a variable of type" << cast<TypedValueRegion>(MR)->getValueType(); return true; case MemRegion::ParamVarRegionKind: - os << "a parameter of type" - << cast<TypedValueRegion>(MR)->getValueType().getAsString(); + os << "a parameter of type" << cast<TypedValueRegion>(MR)->getValueType(); return true; case MemRegion::FieldRegionKind: - os << "a field of type " - << cast<TypedValueRegion>(MR)->getValueType().getAsString(); + os << "a field of type " << cast<TypedValueRegion>(MR)->getValueType(); return true; case MemRegion::ObjCIvarRegionKind: os << "an instance variable of type " - << cast<TypedValueRegion>(MR)->getValueType().getAsString(); + << cast<TypedValueRegion>(MR)->getValueType(); return true; default: return false; @@ -1048,7 +1213,7 @@ bool CStringChecker::memsetAux(const Expr *DstBuffer, SVal CharVal, RegionOffset Offset = MR->getAsOffset(); const MemRegion *BR = Offset.getRegion(); - Optional<NonLoc> SizeNL = SizeVal.getAs<NonLoc>(); + std::optional<NonLoc> SizeNL = SizeVal.getAs<NonLoc>(); if (!SizeNL) return false; @@ -1087,8 +1252,8 @@ bool CStringChecker::memsetAux(const Expr *DstBuffer, SVal CharVal, } else { // If the destination buffer's extent is not equal to the value of // third argument, just invalidate buffer. - State = InvalidateBuffer(C, State, DstBuffer, MemVal, - /*IsSourceBuffer*/ false, Size); + State = invalidateDestinationBufferBySize(C, State, DstBuffer, MemVal, + SizeVal, Size->getType()); } if (StateNullChar && !StateNonNullChar) { @@ -1113,8 +1278,8 @@ bool CStringChecker::memsetAux(const Expr *DstBuffer, SVal CharVal, } else { // If the offset is not zero and char value is not concrete, we can do // nothing but invalidate the buffer. - State = InvalidateBuffer(C, State, DstBuffer, MemVal, - /*IsSourceBuffer*/ false, Size); + State = invalidateDestinationBufferBySize(C, State, DstBuffer, MemVal, + SizeVal, Size->getType()); } return true; } @@ -1123,11 +1288,11 @@ bool CStringChecker::memsetAux(const Expr *DstBuffer, SVal CharVal, // evaluation of individual function calls. //===----------------------------------------------------------------------===// -void CStringChecker::evalCopyCommon(CheckerContext &C, const CallExpr *CE, +void CStringChecker::evalCopyCommon(CheckerContext &C, const CallEvent &Call, ProgramStateRef state, SizeArgExpr Size, DestinationArgExpr Dest, SourceArgExpr Source, bool Restricted, - bool IsMempcpy) const { + bool IsMempcpy, CharKind CK) const { CurrentFunctionDescription = "memory copy function"; // See if the size argument is zero. @@ -1145,7 +1310,8 @@ void CStringChecker::evalCopyCommon(CheckerContext &C, const CallExpr *CE, // If the size is zero, there won't be any actual memory access, so // just bind the return value to the destination buffer and return. if (stateZeroSize && !stateNonZeroSize) { - stateZeroSize = stateZeroSize->BindExpr(CE, LCtx, destVal); + stateZeroSize = + stateZeroSize->BindExpr(Call.getOriginExpr(), LCtx, destVal); C.addTransition(stateZeroSize); return; } @@ -1170,11 +1336,11 @@ void CStringChecker::evalCopyCommon(CheckerContext &C, const CallExpr *CE, return; // Ensure the accesses are valid and that the buffers do not overlap. - state = CheckBufferAccess(C, state, Dest, Size, AccessKind::write); - state = CheckBufferAccess(C, state, Source, Size, AccessKind::read); + state = CheckBufferAccess(C, state, Dest, Size, AccessKind::write, CK); + state = CheckBufferAccess(C, state, Source, Size, AccessKind::read, CK); if (Restricted) - state = CheckOverlap(C, state, Size, Dest, Source); + state = CheckOverlap(C, state, Size, Dest, Source, CK); if (!state) return; @@ -1185,7 +1351,7 @@ void CStringChecker::evalCopyCommon(CheckerContext &C, const CallExpr *CE, // Get the byte after the last byte copied. SValBuilder &SvalBuilder = C.getSValBuilder(); ASTContext &Ctx = SvalBuilder.getContext(); - QualType CharPtrTy = Ctx.getPointerType(Ctx.CharTy); + QualType CharPtrTy = getCharPtrType(Ctx, CK); SVal DestRegCharVal = SvalBuilder.evalCast(destVal, CharPtrTy, Dest.Expression->getType()); SVal lastElement = C.getSValBuilder().evalBinOp( @@ -1193,15 +1359,15 @@ void CStringChecker::evalCopyCommon(CheckerContext &C, const CallExpr *CE, // If we don't know how much we copied, we can at least // conjure a return value for later. if (lastElement.isUnknown()) - lastElement = C.getSValBuilder().conjureSymbolVal(nullptr, CE, LCtx, - C.blockCount()); + lastElement = C.getSValBuilder().conjureSymbolVal( + nullptr, Call.getOriginExpr(), LCtx, C.blockCount()); // The byte after the last byte copied is the return value. - state = state->BindExpr(CE, LCtx, lastElement); + state = state->BindExpr(Call.getOriginExpr(), LCtx, lastElement); } else { // All other copies return the destination buffer. // (Well, bcopy() has a void return type, but this won't hurt.) - state = state->BindExpr(CE, LCtx, destVal); + state = state->BindExpr(Call.getOriginExpr(), LCtx, destVal); } // Invalidate the destination (regular invalidation without pointer-escaping @@ -1210,76 +1376,82 @@ void CStringChecker::evalCopyCommon(CheckerContext &C, const CallExpr *CE, // can use LazyCompoundVals to copy the source values into the destination. // This would probably remove any existing bindings past the end of the // copied region, but that's still an improvement over blank invalidation. - state = - InvalidateBuffer(C, state, Dest.Expression, C.getSVal(Dest.Expression), - /*IsSourceBuffer*/ false, Size.Expression); + state = invalidateDestinationBufferBySize( + C, state, Dest.Expression, C.getSVal(Dest.Expression), sizeVal, + Size.Expression->getType()); // Invalidate the source (const-invalidation without const-pointer-escaping // the address of the top-level region). - state = InvalidateBuffer(C, state, Source.Expression, - C.getSVal(Source.Expression), - /*IsSourceBuffer*/ true, nullptr); + state = invalidateSourceBuffer(C, state, Source.Expression, + C.getSVal(Source.Expression)); C.addTransition(state); } } -void CStringChecker::evalMemcpy(CheckerContext &C, const CallExpr *CE) const { +void CStringChecker::evalMemcpy(CheckerContext &C, const CallEvent &Call, + CharKind CK) const { // void *memcpy(void *restrict dst, const void *restrict src, size_t n); // The return value is the address of the destination buffer. - DestinationArgExpr Dest = {CE->getArg(0), 0}; - SourceArgExpr Src = {CE->getArg(1), 1}; - SizeArgExpr Size = {CE->getArg(2), 2}; + DestinationArgExpr Dest = {{Call.getArgExpr(0), 0}}; + SourceArgExpr Src = {{Call.getArgExpr(1), 1}}; + SizeArgExpr Size = {{Call.getArgExpr(2), 2}}; ProgramStateRef State = C.getState(); constexpr bool IsRestricted = true; constexpr bool IsMempcpy = false; - evalCopyCommon(C, CE, State, Size, Dest, Src, IsRestricted, IsMempcpy); + evalCopyCommon(C, Call, State, Size, Dest, Src, IsRestricted, IsMempcpy, CK); } -void CStringChecker::evalMempcpy(CheckerContext &C, const CallExpr *CE) const { +void CStringChecker::evalMempcpy(CheckerContext &C, const CallEvent &Call, + CharKind CK) const { // void *mempcpy(void *restrict dst, const void *restrict src, size_t n); // The return value is a pointer to the byte following the last written byte. - DestinationArgExpr Dest = {CE->getArg(0), 0}; - SourceArgExpr Src = {CE->getArg(1), 1}; - SizeArgExpr Size = {CE->getArg(2), 2}; + DestinationArgExpr Dest = {{Call.getArgExpr(0), 0}}; + SourceArgExpr Src = {{Call.getArgExpr(1), 1}}; + SizeArgExpr Size = {{Call.getArgExpr(2), 2}}; constexpr bool IsRestricted = true; constexpr bool IsMempcpy = true; - evalCopyCommon(C, CE, C.getState(), Size, Dest, Src, IsRestricted, IsMempcpy); + evalCopyCommon(C, Call, C.getState(), Size, Dest, Src, IsRestricted, + IsMempcpy, CK); } -void CStringChecker::evalMemmove(CheckerContext &C, const CallExpr *CE) const { +void CStringChecker::evalMemmove(CheckerContext &C, const CallEvent &Call, + CharKind CK) const { // void *memmove(void *dst, const void *src, size_t n); // The return value is the address of the destination buffer. - DestinationArgExpr Dest = {CE->getArg(0), 0}; - SourceArgExpr Src = {CE->getArg(1), 1}; - SizeArgExpr Size = {CE->getArg(2), 2}; + DestinationArgExpr Dest = {{Call.getArgExpr(0), 0}}; + SourceArgExpr Src = {{Call.getArgExpr(1), 1}}; + SizeArgExpr Size = {{Call.getArgExpr(2), 2}}; constexpr bool IsRestricted = false; constexpr bool IsMempcpy = false; - evalCopyCommon(C, CE, C.getState(), Size, Dest, Src, IsRestricted, IsMempcpy); + evalCopyCommon(C, Call, C.getState(), Size, Dest, Src, IsRestricted, + IsMempcpy, CK); } -void CStringChecker::evalBcopy(CheckerContext &C, const CallExpr *CE) const { +void CStringChecker::evalBcopy(CheckerContext &C, const CallEvent &Call) const { // void bcopy(const void *src, void *dst, size_t n); - SourceArgExpr Src(CE->getArg(0), 0); - DestinationArgExpr Dest = {CE->getArg(1), 1}; - SizeArgExpr Size = {CE->getArg(2), 2}; + SourceArgExpr Src{{Call.getArgExpr(0), 0}}; + DestinationArgExpr Dest = {{Call.getArgExpr(1), 1}}; + SizeArgExpr Size = {{Call.getArgExpr(2), 2}}; constexpr bool IsRestricted = false; constexpr bool IsMempcpy = false; - evalCopyCommon(C, CE, C.getState(), Size, Dest, Src, IsRestricted, IsMempcpy); + evalCopyCommon(C, Call, C.getState(), Size, Dest, Src, IsRestricted, + IsMempcpy, CharKind::Regular); } -void CStringChecker::evalMemcmp(CheckerContext &C, const CallExpr *CE) const { +void CStringChecker::evalMemcmp(CheckerContext &C, const CallEvent &Call, + CharKind CK) const { // int memcmp(const void *s1, const void *s2, size_t n); CurrentFunctionDescription = "memory comparison function"; - AnyArgExpr Left = {CE->getArg(0), 0}; - AnyArgExpr Right = {CE->getArg(1), 1}; - SizeArgExpr Size = {CE->getArg(2), 2}; + AnyArgExpr Left = {Call.getArgExpr(0), 0}; + AnyArgExpr Right = {Call.getArgExpr(1), 1}; + SizeArgExpr Size = {{Call.getArgExpr(2), 2}}; ProgramStateRef State = C.getState(); SValBuilder &Builder = C.getSValBuilder(); @@ -1297,7 +1469,8 @@ void CStringChecker::evalMemcmp(CheckerContext &C, const CallExpr *CE) const { // have to check either of the buffers. if (stateZeroSize) { State = stateZeroSize; - State = State->BindExpr(CE, LCtx, Builder.makeZeroVal(CE->getType())); + State = State->BindExpr(Call.getOriginExpr(), LCtx, + Builder.makeZeroVal(Call.getResultType())); C.addTransition(State); } @@ -1323,8 +1496,8 @@ void CStringChecker::evalMemcmp(CheckerContext &C, const CallExpr *CE) const { State = SameBuffer; State = CheckBufferAccess(C, State, Left, Size, AccessKind::read); if (State) { - State = - SameBuffer->BindExpr(CE, LCtx, Builder.makeZeroVal(CE->getType())); + State = SameBuffer->BindExpr(Call.getOriginExpr(), LCtx, + Builder.makeZeroVal(Call.getResultType())); C.addTransition(State); } return; @@ -1333,37 +1506,39 @@ void CStringChecker::evalMemcmp(CheckerContext &C, const CallExpr *CE) const { // If the two arguments might be different buffers, we have to check // the size of both of them. assert(NotSameBuffer); - State = CheckBufferAccess(C, State, Right, Size, AccessKind::read); - State = CheckBufferAccess(C, State, Left, Size, AccessKind::read); + State = CheckBufferAccess(C, State, Right, Size, AccessKind::read, CK); + State = CheckBufferAccess(C, State, Left, Size, AccessKind::read, CK); if (State) { // The return value is the comparison result, which we don't know. - SVal CmpV = Builder.conjureSymbolVal(nullptr, CE, LCtx, C.blockCount()); - State = State->BindExpr(CE, LCtx, CmpV); + SVal CmpV = Builder.conjureSymbolVal(nullptr, Call.getOriginExpr(), LCtx, + C.blockCount()); + State = State->BindExpr(Call.getOriginExpr(), LCtx, CmpV); C.addTransition(State); } } } void CStringChecker::evalstrLength(CheckerContext &C, - const CallExpr *CE) const { + const CallEvent &Call) const { // size_t strlen(const char *s); - evalstrLengthCommon(C, CE, /* IsStrnlen = */ false); + evalstrLengthCommon(C, Call, /* IsStrnlen = */ false); } void CStringChecker::evalstrnLength(CheckerContext &C, - const CallExpr *CE) const { + const CallEvent &Call) const { // size_t strnlen(const char *s, size_t maxlen); - evalstrLengthCommon(C, CE, /* IsStrnlen = */ true); + evalstrLengthCommon(C, Call, /* IsStrnlen = */ true); } -void CStringChecker::evalstrLengthCommon(CheckerContext &C, const CallExpr *CE, +void CStringChecker::evalstrLengthCommon(CheckerContext &C, + const CallEvent &Call, bool IsStrnlen) const { CurrentFunctionDescription = "string length function"; ProgramStateRef state = C.getState(); const LocationContext *LCtx = C.getLocationContext(); if (IsStrnlen) { - const Expr *maxlenExpr = CE->getArg(1); + const Expr *maxlenExpr = Call.getArgExpr(1); SVal maxlenVal = state->getSVal(maxlenExpr, LCtx); ProgramStateRef stateZeroSize, stateNonZeroSize; @@ -1373,8 +1548,8 @@ void CStringChecker::evalstrLengthCommon(CheckerContext &C, const CallExpr *CE, // If the size can be zero, the result will be 0 in that case, and we don't // have to check the string itself. if (stateZeroSize) { - SVal zero = C.getSValBuilder().makeZeroVal(CE->getType()); - stateZeroSize = stateZeroSize->BindExpr(CE, LCtx, zero); + SVal zero = C.getSValBuilder().makeZeroVal(Call.getResultType()); + stateZeroSize = stateZeroSize->BindExpr(Call.getOriginExpr(), LCtx, zero); C.addTransition(stateZeroSize); } @@ -1387,7 +1562,7 @@ void CStringChecker::evalstrLengthCommon(CheckerContext &C, const CallExpr *CE, } // Check that the string argument is non-null. - AnyArgExpr Arg = {CE->getArg(0), 0}; + AnyArgExpr Arg = {Call.getArgExpr(0), 0}; SVal ArgVal = state->getSVal(Arg.Expression, LCtx); state = checkNonNull(C, state, Arg, ArgVal); @@ -1410,11 +1585,11 @@ void CStringChecker::evalstrLengthCommon(CheckerContext &C, const CallExpr *CE, // It's a little unfortunate to be getting this again, // but it's not that expensive... - const Expr *maxlenExpr = CE->getArg(1); + const Expr *maxlenExpr = Call.getArgExpr(1); SVal maxlenVal = state->getSVal(maxlenExpr, LCtx); - Optional<NonLoc> strLengthNL = strLength.getAs<NonLoc>(); - Optional<NonLoc> maxlenValNL = maxlenVal.getAs<NonLoc>(); + std::optional<NonLoc> strLengthNL = strLength.getAs<NonLoc>(); + std::optional<NonLoc> maxlenValNL = maxlenVal.getAs<NonLoc>(); if (strLengthNL && maxlenValNL) { ProgramStateRef stateStringTooLong, stateStringNotTooLong; @@ -1439,8 +1614,8 @@ void CStringChecker::evalstrLengthCommon(CheckerContext &C, const CallExpr *CE, // no guarantee the full string length will actually be returned. // All we know is the return value is the min of the string length // and the limit. This is better than nothing. - result = C.getSValBuilder().conjureSymbolVal(nullptr, CE, LCtx, - C.blockCount()); + result = C.getSValBuilder().conjureSymbolVal( + nullptr, Call.getOriginExpr(), LCtx, C.blockCount()); NonLoc resultNL = result.castAs<NonLoc>(); if (strLengthNL) { @@ -1463,78 +1638,85 @@ void CStringChecker::evalstrLengthCommon(CheckerContext &C, const CallExpr *CE, // If we don't know the length of the string, conjure a return // value, so it can be used in constraints, at least. if (result.isUnknown()) { - result = C.getSValBuilder().conjureSymbolVal(nullptr, CE, LCtx, - C.blockCount()); + result = C.getSValBuilder().conjureSymbolVal( + nullptr, Call.getOriginExpr(), LCtx, C.blockCount()); } } // Bind the return value. assert(!result.isUnknown() && "Should have conjured a value by now"); - state = state->BindExpr(CE, LCtx, result); + state = state->BindExpr(Call.getOriginExpr(), LCtx, result); C.addTransition(state); } -void CStringChecker::evalStrcpy(CheckerContext &C, const CallExpr *CE) const { +void CStringChecker::evalStrcpy(CheckerContext &C, + const CallEvent &Call) const { // char *strcpy(char *restrict dst, const char *restrict src); - evalStrcpyCommon(C, CE, + evalStrcpyCommon(C, Call, /* ReturnEnd = */ false, /* IsBounded = */ false, /* appendK = */ ConcatFnKind::none); } -void CStringChecker::evalStrncpy(CheckerContext &C, const CallExpr *CE) const { +void CStringChecker::evalStrncpy(CheckerContext &C, + const CallEvent &Call) const { // char *strncpy(char *restrict dst, const char *restrict src, size_t n); - evalStrcpyCommon(C, CE, + evalStrcpyCommon(C, Call, /* ReturnEnd = */ false, /* IsBounded = */ true, /* appendK = */ ConcatFnKind::none); } -void CStringChecker::evalStpcpy(CheckerContext &C, const CallExpr *CE) const { +void CStringChecker::evalStpcpy(CheckerContext &C, + const CallEvent &Call) const { // char *stpcpy(char *restrict dst, const char *restrict src); - evalStrcpyCommon(C, CE, + evalStrcpyCommon(C, Call, /* ReturnEnd = */ true, /* IsBounded = */ false, /* appendK = */ ConcatFnKind::none); } -void CStringChecker::evalStrlcpy(CheckerContext &C, const CallExpr *CE) const { +void CStringChecker::evalStrlcpy(CheckerContext &C, + const CallEvent &Call) const { // size_t strlcpy(char *dest, const char *src, size_t size); - evalStrcpyCommon(C, CE, + evalStrcpyCommon(C, Call, /* ReturnEnd = */ true, /* IsBounded = */ true, /* appendK = */ ConcatFnKind::none, /* returnPtr = */ false); } -void CStringChecker::evalStrcat(CheckerContext &C, const CallExpr *CE) const { +void CStringChecker::evalStrcat(CheckerContext &C, + const CallEvent &Call) const { // char *strcat(char *restrict s1, const char *restrict s2); - evalStrcpyCommon(C, CE, + evalStrcpyCommon(C, Call, /* ReturnEnd = */ false, /* IsBounded = */ false, /* appendK = */ ConcatFnKind::strcat); } -void CStringChecker::evalStrncat(CheckerContext &C, const CallExpr *CE) const { - //char *strncat(char *restrict s1, const char *restrict s2, size_t n); - evalStrcpyCommon(C, CE, +void CStringChecker::evalStrncat(CheckerContext &C, + const CallEvent &Call) const { + // char *strncat(char *restrict s1, const char *restrict s2, size_t n); + evalStrcpyCommon(C, Call, /* ReturnEnd = */ false, /* IsBounded = */ true, /* appendK = */ ConcatFnKind::strcat); } -void CStringChecker::evalStrlcat(CheckerContext &C, const CallExpr *CE) const { +void CStringChecker::evalStrlcat(CheckerContext &C, + const CallEvent &Call) const { // size_t strlcat(char *dst, const char *src, size_t size); // It will append at most size - strlen(dst) - 1 bytes, // NULL-terminating the result. - evalStrcpyCommon(C, CE, + evalStrcpyCommon(C, Call, /* ReturnEnd = */ false, /* IsBounded = */ true, /* appendK = */ ConcatFnKind::strlcat, /* returnPtr = */ false); } -void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, +void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallEvent &Call, bool ReturnEnd, bool IsBounded, ConcatFnKind appendK, bool returnPtr) const { @@ -1547,14 +1729,14 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, const LocationContext *LCtx = C.getLocationContext(); // Check that the destination is non-null. - DestinationArgExpr Dst = {CE->getArg(0), 0}; + DestinationArgExpr Dst = {{Call.getArgExpr(0), 0}}; SVal DstVal = state->getSVal(Dst.Expression, LCtx); state = checkNonNull(C, state, Dst, DstVal); if (!state) return; // Check that the source is non-null. - SourceArgExpr srcExpr = {CE->getArg(1), 1}; + SourceArgExpr srcExpr = {{Call.getArgExpr(1), 1}}; SVal srcVal = state->getSVal(srcExpr.Expression, LCtx); state = checkNonNull(C, state, srcExpr, srcVal); if (!state) @@ -1562,11 +1744,11 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, // Get the string length of the source. SVal strLength = getCStringLength(C, state, srcExpr.Expression, srcVal); - Optional<NonLoc> strLengthNL = strLength.getAs<NonLoc>(); + std::optional<NonLoc> strLengthNL = strLength.getAs<NonLoc>(); // Get the string length of the destination buffer. SVal dstStrLength = getCStringLength(C, state, Dst.Expression, DstVal); - Optional<NonLoc> dstStrLengthNL = dstStrLength.getAs<NonLoc>(); + std::optional<NonLoc> dstStrLengthNL = dstStrLength.getAs<NonLoc>(); // If the source isn't a valid C string, give up. if (strLength.isUndef()) @@ -1585,11 +1767,12 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, // FIXME: Why do we choose the srcExpr if the access has no size? // Note that the 3rd argument of the call would be the size parameter. - SizeArgExpr SrcExprAsSizeDummy = {srcExpr.Expression, srcExpr.ArgumentIndex}; + SizeArgExpr SrcExprAsSizeDummy = { + {srcExpr.Expression, srcExpr.ArgumentIndex}}; state = CheckOverlap( C, state, - (IsBounded ? SizeArgExpr{CE->getArg(2), 2} : SrcExprAsSizeDummy), Dst, - srcExpr); + (IsBounded ? SizeArgExpr{{Call.getArgExpr(2), 2}} : SrcExprAsSizeDummy), + Dst, srcExpr); if (!state) return; @@ -1597,14 +1780,14 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, // If the function is strncpy, strncat, etc... it is bounded. if (IsBounded) { // Get the max number of characters to copy. - SizeArgExpr lenExpr = {CE->getArg(2), 2}; + SizeArgExpr lenExpr = {{Call.getArgExpr(2), 2}}; SVal lenVal = state->getSVal(lenExpr.Expression, LCtx); // Protect against misdeclared strncpy(). lenVal = svalBuilder.evalCast(lenVal, sizeTy, lenExpr.Expression->getType()); - Optional<NonLoc> lenValNL = lenVal.getAs<NonLoc>(); + std::optional<NonLoc> lenValNL = lenVal.getAs<NonLoc>(); // If we know both values, we might be able to figure out how much // we're copying. @@ -1641,12 +1824,12 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, // amountCopied = min (size - dstLen - 1 , srcLen) SVal freeSpace = svalBuilder.evalBinOpNN(state, BO_Sub, *lenValNL, *dstStrLengthNL, sizeTy); - if (!freeSpace.getAs<NonLoc>()) + if (!isa<NonLoc>(freeSpace)) return; freeSpace = svalBuilder.evalBinOp(state, BO_Sub, freeSpace, svalBuilder.makeIntVal(1, sizeTy), sizeTy); - Optional<NonLoc> freeSpaceNL = freeSpace.getAs<NonLoc>(); + std::optional<NonLoc> freeSpaceNL = freeSpace.getAs<NonLoc>(); // While unlikely, it is possible that the subtraction is // too complex to compute, let's check whether it succeeded. @@ -1711,16 +1894,19 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, // If the size is known to be zero, we're done. if (StateZeroSize && !StateNonZeroSize) { if (returnPtr) { - StateZeroSize = StateZeroSize->BindExpr(CE, LCtx, DstVal); + StateZeroSize = + StateZeroSize->BindExpr(Call.getOriginExpr(), LCtx, DstVal); } else { if (appendK == ConcatFnKind::none) { // strlcpy returns strlen(src) - StateZeroSize = StateZeroSize->BindExpr(CE, LCtx, strLength); + StateZeroSize = StateZeroSize->BindExpr(Call.getOriginExpr(), + LCtx, strLength); } else { // strlcat returns strlen(src) + strlen(dst) SVal retSize = svalBuilder.evalBinOp( state, BO_Add, strLength, dstStrLength, sizeTy); - StateZeroSize = StateZeroSize->BindExpr(CE, LCtx, retSize); + StateZeroSize = + StateZeroSize->BindExpr(Call.getOriginExpr(), LCtx, retSize); } } C.addTransition(StateZeroSize); @@ -1771,7 +1957,7 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, *dstStrLengthNL, sizeTy); } - Optional<NonLoc> amountCopiedNL = amountCopied.getAs<NonLoc>(); + std::optional<NonLoc> amountCopiedNL = amountCopied.getAs<NonLoc>(); // If we know both string lengths, we might know the final string length. if (amountCopiedNL && dstStrLengthNL) { @@ -1789,10 +1975,12 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, if (finalStrLength.isUnknown()) { // Try to get a "hypothetical" string length symbol, which we can later // set as a real value if that turns out to be the case. - finalStrLength = getCStringLength(C, state, CE, DstVal, true); + finalStrLength = + getCStringLength(C, state, Call.getOriginExpr(), DstVal, true); assert(!finalStrLength.isUndef()); - if (Optional<NonLoc> finalStrLengthNL = finalStrLength.getAs<NonLoc>()) { + if (std::optional<NonLoc> finalStrLengthNL = + finalStrLength.getAs<NonLoc>()) { if (amountCopiedNL && appendK == ConcatFnKind::none) { // we overwrite dst string with the src // finalStrLength >= srcStrLength @@ -1843,28 +2031,38 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, // If the destination is a MemRegion, try to check for a buffer overflow and // record the new string length. - if (Optional<loc::MemRegionVal> dstRegVal = - DstVal.getAs<loc::MemRegionVal>()) { + if (std::optional<loc::MemRegionVal> dstRegVal = + DstVal.getAs<loc::MemRegionVal>()) { QualType ptrTy = Dst.Expression->getType(); // If we have an exact value on a bounded copy, use that to check for // overflows, rather than our estimate about how much is actually copied. - if (Optional<NonLoc> maxLastNL = maxLastElementIndex.getAs<NonLoc>()) { + if (std::optional<NonLoc> maxLastNL = maxLastElementIndex.getAs<NonLoc>()) { SVal maxLastElement = svalBuilder.evalBinOpLN(state, BO_Add, *dstRegVal, *maxLastNL, ptrTy); + // 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; } // Then, if the final length is known... - if (Optional<NonLoc> knownStrLength = finalStrLength.getAs<NonLoc>()) { + if (std::optional<NonLoc> knownStrLength = finalStrLength.getAs<NonLoc>()) { SVal lastElement = svalBuilder.evalBinOpLN(state, BO_Add, *dstRegVal, *knownStrLength, ptrTy); // ...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; @@ -1882,13 +2080,13 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, // can use LazyCompoundVals to copy the source values into the destination. // This would probably remove any existing bindings past the end of the // string, but that's still an improvement over blank invalidation. - state = InvalidateBuffer(C, state, Dst.Expression, *dstRegVal, - /*IsSourceBuffer*/ false, nullptr); + state = invalidateDestinationBufferBySize(C, state, Dst.Expression, + *dstRegVal, amountCopied, + C.getASTContext().getSizeType()); // Invalidate the source (const-invalidation without const-pointer-escaping // the address of the top-level region). - state = InvalidateBuffer(C, state, srcExpr.Expression, srcVal, - /*IsSourceBuffer*/ true, nullptr); + state = invalidateSourceBuffer(C, state, srcExpr.Expression, srcVal); // Set the C string length of the destination, if we know it. if (IsBounded && (appendK == ConcatFnKind::none)) { @@ -1908,51 +2106,54 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, // If this is a stpcpy-style copy, but we were unable to check for a buffer // overflow, we still need a result. Conjure a return value. if (ReturnEnd && Result.isUnknown()) { - Result = svalBuilder.conjureSymbolVal(nullptr, CE, LCtx, C.blockCount()); + Result = svalBuilder.conjureSymbolVal(nullptr, Call.getOriginExpr(), LCtx, + C.blockCount()); } } // Set the return value. - state = state->BindExpr(CE, LCtx, Result); + state = state->BindExpr(Call.getOriginExpr(), LCtx, Result); C.addTransition(state); } -void CStringChecker::evalStrcmp(CheckerContext &C, const CallExpr *CE) const { +void CStringChecker::evalStrcmp(CheckerContext &C, + const CallEvent &Call) const { //int strcmp(const char *s1, const char *s2); - evalStrcmpCommon(C, CE, /* IsBounded = */ false, /* IgnoreCase = */ false); + evalStrcmpCommon(C, Call, /* IsBounded = */ false, /* IgnoreCase = */ false); } -void CStringChecker::evalStrncmp(CheckerContext &C, const CallExpr *CE) const { +void CStringChecker::evalStrncmp(CheckerContext &C, + const CallEvent &Call) const { //int strncmp(const char *s1, const char *s2, size_t n); - evalStrcmpCommon(C, CE, /* IsBounded = */ true, /* IgnoreCase = */ false); + evalStrcmpCommon(C, Call, /* IsBounded = */ true, /* IgnoreCase = */ false); } void CStringChecker::evalStrcasecmp(CheckerContext &C, - const CallExpr *CE) const { + const CallEvent &Call) const { //int strcasecmp(const char *s1, const char *s2); - evalStrcmpCommon(C, CE, /* IsBounded = */ false, /* IgnoreCase = */ true); + evalStrcmpCommon(C, Call, /* IsBounded = */ false, /* IgnoreCase = */ true); } void CStringChecker::evalStrncasecmp(CheckerContext &C, - const CallExpr *CE) const { + const CallEvent &Call) const { //int strncasecmp(const char *s1, const char *s2, size_t n); - evalStrcmpCommon(C, CE, /* IsBounded = */ true, /* IgnoreCase = */ true); + evalStrcmpCommon(C, Call, /* IsBounded = */ true, /* IgnoreCase = */ true); } -void CStringChecker::evalStrcmpCommon(CheckerContext &C, const CallExpr *CE, - bool IsBounded, bool IgnoreCase) const { +void CStringChecker::evalStrcmpCommon(CheckerContext &C, const CallEvent &Call, + bool IsBounded, bool IgnoreCase) const { CurrentFunctionDescription = "string comparison function"; ProgramStateRef state = C.getState(); const LocationContext *LCtx = C.getLocationContext(); // Check that the first string is non-null - AnyArgExpr Left = {CE->getArg(0), 0}; + AnyArgExpr Left = {Call.getArgExpr(0), 0}; SVal LeftVal = state->getSVal(Left.Expression, LCtx); state = checkNonNull(C, state, Left, LeftVal); if (!state) return; // Check that the second string is non-null. - AnyArgExpr Right = {CE->getArg(1), 1}; + AnyArgExpr Right = {Call.getArgExpr(1), 1}; SVal RightVal = state->getSVal(Right.Expression, LCtx); state = checkNonNull(C, state, Right, RightVal); if (!state) @@ -1983,8 +2184,9 @@ void CStringChecker::evalStrcmpCommon(CheckerContext &C, const CallExpr *CE, // If the two arguments might be the same buffer, we know the result is 0, // and we only need to check one size. if (StSameBuf) { - StSameBuf = StSameBuf->BindExpr(CE, LCtx, - svalBuilder.makeZeroVal(CE->getType())); + StSameBuf = + StSameBuf->BindExpr(Call.getOriginExpr(), LCtx, + svalBuilder.makeZeroVal(Call.getResultType())); C.addTransition(StSameBuf); // If the two arguments are GUARANTEED to be the same, we're done! @@ -2004,8 +2206,8 @@ void CStringChecker::evalStrcmpCommon(CheckerContext &C, const CallExpr *CE, const StringLiteral *RightStrLiteral = getCStringLiteral(C, state, Right.Expression, RightVal); bool canComputeResult = false; - SVal resultVal = svalBuilder.conjureSymbolVal(nullptr, CE, LCtx, - C.blockCount()); + SVal resultVal = svalBuilder.conjureSymbolVal(nullptr, Call.getOriginExpr(), + LCtx, C.blockCount()); if (LeftStrLiteral && RightStrLiteral) { StringRef LeftStrRef = LeftStrLiteral->getString(); @@ -2013,7 +2215,7 @@ void CStringChecker::evalStrcmpCommon(CheckerContext &C, const CallExpr *CE, if (IsBounded) { // Get the max number of characters to compare. - const Expr *lenExpr = CE->getArg(2); + const Expr *lenExpr = Call.getArgExpr(2); SVal lenVal = state->getSVal(lenExpr, LCtx); // If the length is known, we can get the right substrings. @@ -2045,13 +2247,13 @@ void CStringChecker::evalStrcmpCommon(CheckerContext &C, const CallExpr *CE, // The strcmp function returns an integer greater than, equal to, or less // than zero, [c11, p7.24.4.2]. if (compareRes == 0) { - resultVal = svalBuilder.makeIntVal(compareRes, CE->getType()); + resultVal = svalBuilder.makeIntVal(compareRes, Call.getResultType()); } else { - DefinedSVal zeroVal = svalBuilder.makeIntVal(0, CE->getType()); + DefinedSVal zeroVal = svalBuilder.makeIntVal(0, Call.getResultType()); // Constrain strcmp's result range based on the result of StringRef's // comparison methods. - BinaryOperatorKind op = (compareRes == 1) ? BO_GT : BO_LT; + BinaryOperatorKind op = (compareRes > 0) ? BO_GT : BO_LT; SVal compareWithZero = svalBuilder.evalBinOp(state, op, resultVal, zeroVal, svalBuilder.getConditionType()); @@ -2061,20 +2263,21 @@ void CStringChecker::evalStrcmpCommon(CheckerContext &C, const CallExpr *CE, } } - state = state->BindExpr(CE, LCtx, resultVal); + state = state->BindExpr(Call.getOriginExpr(), LCtx, resultVal); // Record this as a possible path. C.addTransition(state); } -void CStringChecker::evalStrsep(CheckerContext &C, const CallExpr *CE) const { - //char *strsep(char **stringp, const char *delim); - // Sanity: does the search string parameter match the return type? - SourceArgExpr SearchStrPtr = {CE->getArg(0), 0}; +void CStringChecker::evalStrsep(CheckerContext &C, + const CallEvent &Call) const { + // char *strsep(char **stringp, const char *delim); + // Verify whether the search string parameter matches the return type. + SourceArgExpr SearchStrPtr = {{Call.getArgExpr(0), 0}}; QualType CharPtrTy = SearchStrPtr.Expression->getType()->getPointeeType(); - if (CharPtrTy.isNull() || - CE->getType().getUnqualifiedType() != CharPtrTy.getUnqualifiedType()) + if (CharPtrTy.isNull() || Call.getResultType().getUnqualifiedType() != + CharPtrTy.getUnqualifiedType()) return; CurrentFunctionDescription = "strsep()"; @@ -2089,7 +2292,7 @@ void CStringChecker::evalStrsep(CheckerContext &C, const CallExpr *CE) const { return; // Check that the delimiter string is non-null. - AnyArgExpr DelimStr = {CE->getArg(1), 1}; + AnyArgExpr DelimStr = {Call.getArgExpr(1), 1}; SVal DelimStrVal = State->getSVal(DelimStr.Expression, LCtx); State = checkNonNull(C, State, DelimStr, DelimStrVal); if (!State) @@ -2097,48 +2300,49 @@ void CStringChecker::evalStrsep(CheckerContext &C, const CallExpr *CE) const { SValBuilder &SVB = C.getSValBuilder(); SVal Result; - if (Optional<Loc> SearchStrLoc = SearchStrVal.getAs<Loc>()) { + if (std::optional<Loc> SearchStrLoc = SearchStrVal.getAs<Loc>()) { // Get the current value of the search string pointer, as a char*. Result = State->getSVal(*SearchStrLoc, CharPtrTy); // Invalidate the search string, representing the change of one delimiter // character to NUL. - State = InvalidateBuffer(C, State, SearchStrPtr.Expression, Result, - /*IsSourceBuffer*/ false, nullptr); + // As the replacement never overflows, do not invalidate its super region. + State = invalidateDestinationBufferNeverOverflows( + C, State, SearchStrPtr.Expression, Result); // Overwrite the search string pointer. The new value is either an address // further along in the same string, or NULL if there are no more tokens. - State = State->bindLoc(*SearchStrLoc, - SVB.conjureSymbolVal(getTag(), - CE, - LCtx, - CharPtrTy, - C.blockCount()), - LCtx); + State = + State->bindLoc(*SearchStrLoc, + SVB.conjureSymbolVal(getTag(), Call.getOriginExpr(), + LCtx, CharPtrTy, C.blockCount()), + LCtx); } else { assert(SearchStrVal.isUnknown()); // Conjure a symbolic value. It's the best we can do. - Result = SVB.conjureSymbolVal(nullptr, CE, LCtx, C.blockCount()); + Result = SVB.conjureSymbolVal(nullptr, Call.getOriginExpr(), LCtx, + C.blockCount()); } // Set the return value, and finish. - State = State->BindExpr(CE, LCtx, Result); + State = State->BindExpr(Call.getOriginExpr(), LCtx, Result); C.addTransition(State); } // These should probably be moved into a C++ standard library checker. -void CStringChecker::evalStdCopy(CheckerContext &C, const CallExpr *CE) const { - evalStdCopyCommon(C, CE); +void CStringChecker::evalStdCopy(CheckerContext &C, + const CallEvent &Call) const { + evalStdCopyCommon(C, Call); } void CStringChecker::evalStdCopyBackward(CheckerContext &C, - const CallExpr *CE) const { - evalStdCopyCommon(C, CE); + const CallEvent &Call) const { + evalStdCopyCommon(C, Call); } void CStringChecker::evalStdCopyCommon(CheckerContext &C, - const CallExpr *CE) const { - if (!CE->getArg(2)->getType()->isPointerType()) + const CallEvent &Call) const { + if (!Call.getArgExpr(2)->getType()->isPointerType()) return; ProgramStateRef State = C.getState(); @@ -2151,26 +2355,30 @@ void CStringChecker::evalStdCopyCommon(CheckerContext &C, // _OutputIterator __result) // Invalidate the destination buffer - const Expr *Dst = CE->getArg(2); + const Expr *Dst = Call.getArgExpr(2); SVal DstVal = State->getSVal(Dst, LCtx); - State = InvalidateBuffer(C, State, Dst, DstVal, /*IsSource=*/false, - /*Size=*/nullptr); + // FIXME: As we do not know how many items are copied, we also invalidate the + // super region containing the target location. + State = + invalidateDestinationBufferAlwaysEscapeSuperRegion(C, State, Dst, DstVal); SValBuilder &SVB = C.getSValBuilder(); - SVal ResultVal = SVB.conjureSymbolVal(nullptr, CE, LCtx, C.blockCount()); - State = State->BindExpr(CE, LCtx, ResultVal); + SVal ResultVal = + SVB.conjureSymbolVal(nullptr, Call.getOriginExpr(), LCtx, C.blockCount()); + State = State->BindExpr(Call.getOriginExpr(), LCtx, ResultVal); C.addTransition(State); } -void CStringChecker::evalMemset(CheckerContext &C, const CallExpr *CE) const { +void CStringChecker::evalMemset(CheckerContext &C, + const CallEvent &Call) const { // void *memset(void *s, int c, size_t n); CurrentFunctionDescription = "memory set function"; - DestinationArgExpr Buffer = {CE->getArg(0), 0}; - AnyArgExpr CharE = {CE->getArg(1), 1}; - SizeArgExpr Size = {CE->getArg(2), 2}; + DestinationArgExpr Buffer = {{Call.getArgExpr(0), 0}}; + AnyArgExpr CharE = {Call.getArgExpr(1), 1}; + SizeArgExpr Size = {{Call.getArgExpr(2), 2}}; ProgramStateRef State = C.getState(); @@ -2188,7 +2396,7 @@ void CStringChecker::evalMemset(CheckerContext &C, const CallExpr *CE) const { // If the size is zero, there won't be any actual memory access, so // just bind the return value to the buffer and return. if (ZeroSize && !NonZeroSize) { - ZeroSize = ZeroSize->BindExpr(CE, LCtx, BufferPtrVal); + ZeroSize = ZeroSize->BindExpr(Call.getOriginExpr(), LCtx, BufferPtrVal); C.addTransition(ZeroSize); return; } @@ -2210,15 +2418,15 @@ void CStringChecker::evalMemset(CheckerContext &C, const CallExpr *CE) const { Size.Expression, C, State)) return; - State = State->BindExpr(CE, LCtx, BufferPtrVal); + State = State->BindExpr(Call.getOriginExpr(), LCtx, BufferPtrVal); C.addTransition(State); } -void CStringChecker::evalBzero(CheckerContext &C, const CallExpr *CE) const { +void CStringChecker::evalBzero(CheckerContext &C, const CallEvent &Call) const { CurrentFunctionDescription = "memory clearance function"; - DestinationArgExpr Buffer = {CE->getArg(0), 0}; - SizeArgExpr Size = {CE->getArg(1), 1}; + DestinationArgExpr Buffer = {{Call.getArgExpr(0), 0}}; + SizeArgExpr Size = {{Call.getArgExpr(1), 1}}; SVal Zero = C.getSValBuilder().makeZeroVal(C.getASTContext().IntTy); ProgramStateRef State = C.getState(); @@ -2257,6 +2465,57 @@ void CStringChecker::evalBzero(CheckerContext &C, const CallExpr *CE) const { C.addTransition(State); } +void CStringChecker::evalSprintf(CheckerContext &C, + const CallEvent &Call) const { + CurrentFunctionDescription = "'sprintf'"; + const auto *CE = cast<CallExpr>(Call.getOriginExpr()); + bool IsBI = CE->getBuiltinCallee() == Builtin::BI__builtin___sprintf_chk; + evalSprintfCommon(C, Call, /* IsBounded */ false, IsBI); +} + +void CStringChecker::evalSnprintf(CheckerContext &C, + const CallEvent &Call) const { + CurrentFunctionDescription = "'snprintf'"; + const auto *CE = cast<CallExpr>(Call.getOriginExpr()); + bool IsBI = CE->getBuiltinCallee() == Builtin::BI__builtin___snprintf_chk; + evalSprintfCommon(C, Call, /* IsBounded */ true, IsBI); +} + +void CStringChecker::evalSprintfCommon(CheckerContext &C, const CallEvent &Call, + bool IsBounded, bool IsBuiltin) const { + ProgramStateRef State = C.getState(); + const auto *CE = cast<CallExpr>(Call.getOriginExpr()); + DestinationArgExpr Dest = {{Call.getArgExpr(0), 0}}; + + const auto NumParams = Call.parameters().size(); + assert(CE->getNumArgs() >= NumParams); + + const auto AllArguments = + llvm::make_range(CE->getArgs(), CE->getArgs() + CE->getNumArgs()); + const auto VariadicArguments = drop_begin(enumerate(AllArguments), NumParams); + + for (const auto &[ArgIdx, ArgExpr] : VariadicArguments) { + // We consider only string buffers + if (const QualType type = ArgExpr->getType(); + !type->isAnyPointerType() || + !type->getPointeeType()->isAnyCharacterType()) + continue; + SourceArgExpr Source = {{ArgExpr, unsigned(ArgIdx)}}; + + // Ensure the buffers do not overlap. + SizeArgExpr SrcExprAsSizeDummy = { + {Source.Expression, Source.ArgumentIndex}}; + State = CheckOverlap( + C, State, + (IsBounded ? SizeArgExpr{{Call.getArgExpr(1), 1}} : SrcExprAsSizeDummy), + Dest, Source); + if (!State) + return; + } + + C.addTransition(State); +} + //===----------------------------------------------------------------------===// // The driver method, and other Checker callbacks. //===----------------------------------------------------------------------===// @@ -2271,11 +2530,10 @@ CStringChecker::FnCheck CStringChecker::identifyCall(const CallEvent &Call, if (!FD) return nullptr; - if (Call.isCalled(StdCopy)) { + if (StdCopy.matches(Call)) return &CStringChecker::evalStdCopy; - } else if (Call.isCalled(StdCopyBackward)) { + if (StdCopyBackward.matches(Call)) return &CStringChecker::evalStdCopyBackward; - } // Pro-actively check that argument types are safe to do arithmetic upon. // We do not want to crash if someone accidentally passes a structure @@ -2302,8 +2560,8 @@ bool CStringChecker::evalCall(const CallEvent &Call, CheckerContext &C) const { return false; // Check and evaluate the call. - const auto *CE = cast<CallExpr>(Call.getOriginExpr()); - (this->*Callback)(C, CE); + assert(isa<CallExpr>(Call.getOriginExpr())); + Callback(this, C, Call); // If the evaluate call resulted in no change, chain to the next eval call // handler. @@ -2364,9 +2622,7 @@ CStringChecker::checkRegionChanges(ProgramStateRef state, llvm::SmallPtrSet<const MemRegion *, 32> SuperRegions; // First build sets for the changed regions and their super-regions. - for (ArrayRef<const MemRegion *>::iterator - I = Regions.begin(), E = Regions.end(); I != E; ++I) { - const MemRegion *MR = *I; + for (const MemRegion *MR : Regions) { Invalidated.insert(MR); SuperRegions.insert(MR); @@ -2379,10 +2635,7 @@ CStringChecker::checkRegionChanges(ProgramStateRef state, CStringLengthTy::Factory &F = state->get_context<CStringLength>(); // Then loop over the entries in the current state. - for (CStringLengthTy::iterator I = Entries.begin(), - E = Entries.end(); I != E; ++I) { - const MemRegion *MR = I.getKey(); - + for (const MemRegion *MR : llvm::make_first_range(Entries)) { // Is this entry for a super-region of a changed region? if (SuperRegions.count(MR)) { Entries = F.remove(Entries, MR); @@ -2408,13 +2661,9 @@ void CStringChecker::checkLiveSymbols(ProgramStateRef state, // Mark all symbols in our string length map as valid. CStringLengthTy Entries = state->get<CStringLength>(); - for (CStringLengthTy::iterator I = Entries.begin(), E = Entries.end(); - I != E; ++I) { - SVal Len = I.getData(); - - for (SymExpr::symbol_iterator si = Len.symbol_begin(), - se = Len.symbol_end(); si != se; ++si) - SR.markInUse(*si); + for (SVal Len : llvm::make_second_range(Entries)) { + for (SymbolRef Sym : Len.symbols()) + SR.markInUse(Sym); } } @@ -2426,12 +2675,10 @@ void CStringChecker::checkDeadSymbols(SymbolReaper &SR, return; CStringLengthTy::Factory &F = state->get_context<CStringLength>(); - for (CStringLengthTy::iterator I = Entries.begin(), E = Entries.end(); - I != E; ++I) { - SVal Len = I.getData(); + for (auto [Reg, Len] : Entries) { if (SymbolRef Sym = Len.getAsSymbol()) { if (SR.isDead(Sym)) - Entries = F.remove(Entries, I.getKey()); + Entries = F.remove(Entries, Reg); } } @@ -2460,3 +2707,4 @@ REGISTER_CHECKER(CStringNullArg) REGISTER_CHECKER(CStringOutOfBounds) REGISTER_CHECKER(CStringBufferOverlap) REGISTER_CHECKER(CStringNotNullTerm) +REGISTER_CHECKER(CStringUninitializedRead) 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..b4dee1e300e8 --- /dev/null +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CXXDeleteChecker.cpp @@ -0,0 +1,238 @@ +//=== 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/BugReporter/CommonBugCategories.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 { + const BugType BT{ + this, "Destruction of a polymorphic object with no virtual destructor"}; + + void + checkTypedDeleteExpr(const CXXDeleteExpr *DE, CheckerContext &C, + const TypedValueRegion *BaseClassRegion, + const SymbolicRegion *DerivedClassRegion) const override; +}; + +class CXXArrayDeleteChecker : public CXXDeleteChecker { + const BugType BT{this, + "Deleting an array of polymorphic objects is undefined"}; + + 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; + + 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; + + 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 3e46e2372516..f2e1f69c32cf 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp @@ -73,7 +73,7 @@ public: CK_NumCheckKinds }; - DefaultBool ChecksEnabled[CK_NumCheckKinds]; + bool ChecksEnabled[CK_NumCheckKinds] = {false}; // The original core.CallAndMessage checker name. This should rather be an // array, as seen in MallocChecker and CStringChecker. CheckerNameRef OriginalName; @@ -123,11 +123,10 @@ 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, - std::unique_ptr<BugType> &BT, + bool uninitRefOrPointer(CheckerContext &C, SVal V, SourceRange ArgRange, + const Expr *ArgEx, std::unique_ptr<BugType> &BT, const ParmVarDecl *ParamDecl, const char *BD, int ArgumentNumber) const; }; @@ -185,7 +184,7 @@ static void describeUninitializedArgumentInCall(const CallEvent &Call, } bool CallAndMessageChecker::uninitRefOrPointer( - CheckerContext &C, const SVal &V, SourceRange ArgRange, const Expr *ArgEx, + CheckerContext &C, SVal V, SourceRange ArgRange, const Expr *ArgEx, std::unique_ptr<BugType> &BT, const ParmVarDecl *ParamDecl, const char *BD, int ArgumentNumber) const { @@ -263,7 +262,7 @@ public: if (Find(FR)) return true; } else { - const SVal &V = StoreMgr.getBinding(store, loc::MemRegionVal(FR)); + SVal V = StoreMgr.getBinding(store, loc::MemRegionVal(FR)); if (V.isUndef()) return true; } @@ -379,7 +378,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 +394,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 +449,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 +465,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 +494,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 +584,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 +633,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..a50772f881f7 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; + const BugType BT{this, "Cast region with wrong size."}; public: void checkPreStmt(const CastExpr *CE, CheckerContext &C) const; @@ -131,12 +131,10 @@ void CastSizeChecker::checkPreStmt(const CastExpr *CE,CheckerContext &C) const { return; 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); + 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/CastValueChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp index 131c1345af99..f02d20d45678 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp @@ -20,10 +20,11 @@ #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.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/DynamicType.h" -#include "llvm/ADT/Optional.h" +#include <optional> #include <utility> using namespace clang; @@ -107,7 +108,7 @@ static const NoteTag *getNoteTag(CheckerContext &C, bool CastSucceeds, bool IsKnownCast) { std::string CastToName = CastInfo ? CastInfo->to()->getAsCXXRecordDecl()->getNameAsString() - : CastToTy->getPointeeCXXRecordDecl()->getNameAsString(); + : CastToTy.getAsString(); Object = Object->IgnoreParenImpCasts(); return C.getNoteTag( @@ -162,9 +163,9 @@ static const NoteTag *getNoteTag(CheckerContext &C, bool First = true; for (QualType CastToTy: CastToTyVec) { std::string CastToName = - CastToTy->getAsCXXRecordDecl() ? - CastToTy->getAsCXXRecordDecl()->getNameAsString() : - CastToTy->getPointeeCXXRecordDecl()->getNameAsString(); + CastToTy->getAsCXXRecordDecl() + ? CastToTy->getAsCXXRecordDecl()->getNameAsString() + : CastToTy.getAsString(); Out << ' ' << ((CastToTyVec.size() == 1) ? "not" : (First ? "neither" : "nor")) << " a '" << CastToName << '\''; @@ -249,7 +250,7 @@ static void addCastTransition(const CallEvent &Call, DefinedOrUnknownSVal DV, CastSucceeds); SVal V = CastSucceeds ? C.getSValBuilder().evalCast(DV, CastToTy, CastFromTy) - : C.getSValBuilder().makeNull(); + : C.getSValBuilder().makeNullWithType(CastToTy); C.addTransition( State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), V, false), getNoteTag(C, CastInfo, CastToTy, Object, CastSucceeds, IsKnownCast)); @@ -358,7 +359,9 @@ static void evalNullParamNullReturn(const CallEvent &Call, if (ProgramStateRef State = C.getState()->assume(DV, false)) C.addTransition(State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), - C.getSValBuilder().makeNull(), false), + C.getSValBuilder().makeNullWithType( + Call.getOriginExpr()->getType()), + false), C.getNoteTag("Assuming null pointer is passed into cast", /*IsPrunable=*/true)); } @@ -469,7 +472,7 @@ bool CastValueChecker::evalCall(const CallEvent &Call, const CastCheck &Check = Lookup->first; CallKind Kind = Lookup->second; - Optional<DefinedOrUnknownSVal> DV; + std::optional<DefinedOrUnknownSVal> DV; switch (Kind) { case CallKind::Function: { diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp index 78b3c209ad6b..978bc0bb082f 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp @@ -45,6 +45,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" #include "llvm/Support/raw_ostream.h" +#include <optional> using namespace clang; using namespace ento; @@ -98,18 +99,23 @@ class ObjCDeallocChecker check::PointerEscape, check::PreStmt<ReturnStmt>> { - mutable IdentifierInfo *NSObjectII, *SenTestCaseII, *XCTestCaseII, - *Block_releaseII, *CIFilterII; + mutable const IdentifierInfo *NSObjectII = nullptr; + mutable const IdentifierInfo *SenTestCaseII = nullptr; + mutable const IdentifierInfo *XCTestCaseII = nullptr; + mutable const IdentifierInfo *Block_releaseII = nullptr; + mutable const IdentifierInfo *CIFilterII = nullptr; - mutable Selector DeallocSel, ReleaseSel; + mutable Selector DeallocSel; + mutable Selector ReleaseSel; - std::unique_ptr<BugType> MissingReleaseBugType; - std::unique_ptr<BugType> ExtraReleaseBugType; - std::unique_ptr<BugType> MistakenDeallocBugType; + const BugType MissingReleaseBugType{this, "Missing ivar release (leak)", + categories::MemoryRefCount}; + const BugType ExtraReleaseBugType{this, "Extra ivar release", + categories::MemoryRefCount}; + const BugType MistakenDeallocBugType{this, "Mistaken dealloc", + categories::MemoryRefCount}; public: - ObjCDeallocChecker(); - void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& Mgr, BugReporter &BR) const; void checkBeginFunction(CheckerContext &Ctx) const; @@ -282,11 +288,11 @@ void ObjCDeallocChecker::checkBeginFunction( continue; SVal LVal = State->getLValue(PropImpl->getPropertyIvarDecl(), SelfVal); - Optional<Loc> LValLoc = LVal.getAs<Loc>(); + std::optional<Loc> LValLoc = LVal.getAs<Loc>(); if (!LValLoc) continue; - SVal InitialVal = State->getSVal(LValLoc.getValue()); + SVal InitialVal = State->getSVal(*LValLoc); SymbolRef Symbol = InitialVal.getAsSymbol(); if (!Symbol || !isa<SymbolRegionValue>(Symbol)) continue; @@ -320,7 +326,9 @@ ObjCDeallocChecker::getInstanceSymbolFromIvarSymbol(SymbolRef IvarSym) const { if (!IvarRegion) return nullptr; - return IvarRegion->getSymbolicBase()->getSymbol(); + const SymbolicRegion *SR = IvarRegion->getSymbolicBase(); + assert(SR && "Symbolic base should not be nullptr"); + return SR->getSymbol(); } /// If we are in -dealloc or -dealloc is on the stack, handle the call if it is @@ -576,7 +584,7 @@ void ObjCDeallocChecker::diagnoseMissingReleases(CheckerContext &C) const { OS << " by a synthesized property but not released" " before '[super dealloc]'"; - auto BR = std::make_unique<PathSensitiveBugReport>(*MissingReleaseBugType, + auto BR = std::make_unique<PathSensitiveBugReport>(MissingReleaseBugType, OS.str(), ErrNode); C.emitReport(std::move(BR)); } @@ -698,7 +706,7 @@ bool ObjCDeallocChecker::diagnoseExtraRelease(SymbolRef ReleasedValue, OS << " property but was released in 'dealloc'"; } - auto BR = std::make_unique<PathSensitiveBugReport>(*ExtraReleaseBugType, + auto BR = std::make_unique<PathSensitiveBugReport>(ExtraReleaseBugType, OS.str(), ErrNode); BR->addRange(M.getOriginExpr()->getSourceRange()); @@ -740,7 +748,7 @@ bool ObjCDeallocChecker::diagnoseMistakenDealloc(SymbolRef DeallocedValue, OS << "'" << *PropImpl->getPropertyIvarDecl() << "' should be released rather than deallocated"; - auto BR = std::make_unique<PathSensitiveBugReport>(*MistakenDeallocBugType, + auto BR = std::make_unique<PathSensitiveBugReport>(MistakenDeallocBugType, OS.str(), ErrNode); BR->addRange(M.getOriginExpr()->getSourceRange()); @@ -749,23 +757,6 @@ bool ObjCDeallocChecker::diagnoseMistakenDealloc(SymbolRef DeallocedValue, return true; } -ObjCDeallocChecker::ObjCDeallocChecker() - : NSObjectII(nullptr), SenTestCaseII(nullptr), XCTestCaseII(nullptr), - CIFilterII(nullptr) { - - MissingReleaseBugType.reset( - new BugType(this, "Missing ivar release (leak)", - categories::MemoryRefCount)); - - ExtraReleaseBugType.reset( - new BugType(this, "Extra ivar release", - categories::MemoryRefCount)); - - MistakenDeallocBugType.reset( - new BugType(this, "Mistaken dealloc", - categories::MemoryRefCount)); -} - void ObjCDeallocChecker::initIdentifierInfoAndSelectors( ASTContext &Ctx) const { if (NSObjectII) @@ -817,8 +808,8 @@ const ObjCPropertyDecl *ObjCDeallocChecker::findShadowedPropertyDecl( IdentifierInfo *ID = PropDecl->getIdentifier(); DeclContext::lookup_result R = CatDecl->getClassInterface()->lookup(ID); - for (DeclContext::lookup_iterator I = R.begin(), E = R.end(); I != E; ++I) { - auto *ShadowedPropDecl = dyn_cast<ObjCPropertyDecl>(*I); + for (const NamedDecl *D : R) { + auto *ShadowedPropDecl = dyn_cast<ObjCPropertyDecl>(D); if (!ShadowedPropDecl) continue; @@ -953,11 +944,11 @@ ObjCDeallocChecker::getValueReleasedByNillingOut(const ObjCMethodCall &M, ProgramStateRef State = C.getState(); SVal LVal = State->getLValue(PropIvarDecl, ReceiverVal); - Optional<Loc> LValLoc = LVal.getAs<Loc>(); + std::optional<Loc> LValLoc = LVal.getAs<Loc>(); if (!LValLoc) return nullptr; - SVal CurrentValInIvar = State->getSVal(LValLoc.getValue()); + SVal CurrentValInIvar = State->getSVal(*LValLoc); return CurrentValInIvar.getAsSymbol(); } @@ -1004,7 +995,7 @@ bool ObjCDeallocChecker::instanceDeallocIsOnStack(const CheckerContext &C, return false; } -/// Returns true if the ID is a class in which which is known to have +/// Returns true if the ID is a class in which is known to have /// a separate teardown lifecycle. In this case, -dealloc warnings /// about missing releases should be suppressed. bool ObjCDeallocChecker::classHasSeparateTeardown( @@ -1042,8 +1033,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/CheckObjCInstMethSignature.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp index 175dfcef0df4..c8fe5c2ccf38 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// // -// This file defines a CheckObjCInstMethSignature, a flow-insenstive check +// This file defines a CheckObjCInstMethSignature, a flow-insensitive check // that determines if an Objective-C class interface incorrectly redefines // the method signature in a subclass. // @@ -55,13 +55,11 @@ static void CompareReturnTypes(const ObjCMethodDecl *MethDerived, << *MethAncestor->getClassInterface() << "', defines the instance method '"; MethDerived->getSelector().print(os); - os << "' whose return type is '" - << ResDerived.getAsString() + os << "' whose return type is '" << ResDerived << "'. A method with the same name (same selector) is also defined in " "class '" - << *MethAncestor->getClassInterface() - << "' and has a return type of '" - << ResAncestor.getAsString() + << *MethAncestor->getClassInterface() << "' and has a return type of '" + << ResAncestor << "'. These two types are incompatible, and may result in undefined " "behavior for clients of these classes."; diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp index d06c87631bfb..17af1aebd6d2 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() || @@ -36,20 +35,20 @@ static bool isArc4RandomAvailable(const ASTContext &Ctx) { namespace { struct ChecksFilter { - DefaultBool check_bcmp; - DefaultBool check_bcopy; - DefaultBool check_bzero; - DefaultBool check_gets; - DefaultBool check_getpw; - DefaultBool check_mktemp; - DefaultBool check_mkstemp; - DefaultBool check_strcpy; - DefaultBool check_DeprecatedOrUnsafeBufferHandling; - DefaultBool check_rand; - DefaultBool check_vfork; - DefaultBool check_FloatLoopCounter; - DefaultBool check_UncheckedReturn; - DefaultBool check_decodeValueOfObjCType; + bool check_bcmp = false; + bool check_bcopy = false; + bool check_bzero = false; + bool check_gets = false; + bool check_getpw = false; + bool check_mktemp = false; + bool check_mkstemp = false; + bool check_strcpy = false; + bool check_DeprecatedOrUnsafeBufferHandling = false; + bool check_rand = false; + bool check_vfork = false; + bool check_FloatLoopCounter = false; + bool check_UncheckedReturn = false; + bool check_decodeValueOfObjCType = false; CheckerNameRef checkName_bcmp; CheckerNameRef checkName_bcopy; @@ -141,42 +140,42 @@ void WalkAST::VisitCallExpr(CallExpr *CE) { if (!II) // if no identifier, not a simple C function return; StringRef Name = II->getName(); - if (Name.startswith("__builtin_")) - Name = Name.substr(10); + Name.consume_front("__builtin_"); // 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 +218,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. //===----------------------------------------------------------------------===// @@ -325,7 +323,7 @@ void WalkAST::checkLoopConditionForFloat(const ForStmt *FS) { llvm::raw_svector_ostream os(sbuf); os << "Variable '" << drCond->getDecl()->getName() - << "' with floating point type '" << drCond->getType().getAsString() + << "' with floating point type '" << drCond->getType() << "' should not be used as a loop counter"; ranges.push_back(drCond->getSourceRange()); @@ -467,8 +465,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 +737,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 +762,14 @@ void WalkAST::checkDeprecatedOrUnsafeBufferHandling(const CallExpr *CE, enum { DEPR_ONLY = -1, UNKNOWN_CALL = -2 }; StringRef Name = FD->getIdentifier()->getName(); - if (Name.startswith("__builtin_")) - Name = Name.substr(10); + Name.consume_front("__builtin_"); 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); @@ -785,9 +783,8 @@ void WalkAST::checkDeprecatedOrUnsafeBufferHandling(const CallExpr *CE, // real flow analysis. auto FormatString = dyn_cast<StringLiteral>(CE->getArg(ArgIndex)->IgnoreParenImpCasts()); - if (FormatString && - FormatString->getString().find("%s") == StringRef::npos && - FormatString->getString().find("%[") == StringRef::npos) + if (FormatString && !FormatString->getString().contains("%s") && + !FormatString->getString().contains("%[")) BoundsProvided = true; } @@ -848,8 +845,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 //===----------------------------------------------------------------------===// @@ -891,11 +893,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; @@ -975,6 +973,8 @@ void WalkAST::checkMsg_decodeValueOfObjCType(const ObjCMessageExpr *ME) { if (VT < VersionTuple(11, 0)) return; break; + case llvm::Triple::XROS: + break; default: return; } @@ -991,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 fd53c04f4bbf..be7be15022d3 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp @@ -14,6 +14,7 @@ #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/ProgramState.h" @@ -40,9 +41,9 @@ 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; + const BugType BT_BreakJail{this, "Break out of jail"}; - const CallDescription Chroot{"chroot", 1}, Chdir{"chdir", 1}; + const CallDescription Chroot{{"chroot"}, 1}, Chdir{{"chdir"}, 1}; public: ChrootChecker() {} @@ -63,11 +64,11 @@ private: } // end anonymous namespace bool ChrootChecker::evalCall(const CallEvent &Call, CheckerContext &C) const { - if (Call.isCalled(Chroot)) { + if (Chroot.matches(Call)) { evalChroot(Call, C); return true; } - if (Call.isCalled(Chdir)) { + if (Chdir.matches(Call)) { evalChdir(Call, C); return true; } @@ -115,7 +116,7 @@ void ChrootChecker::evalChdir(const CallEvent &Call, CheckerContext &C) const { void ChrootChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { // Ignore chroot and chdir. - if (Call.isCalled(Chroot) || Call.isCalled(Chdir)) + if (matchesAny(Call, Chroot, Chdir)) return; // If jail state is ROOT_CHANGED, generate BugReport. @@ -123,12 +124,10 @@ void ChrootChecker::checkPreCall(const CallEvent &Call, if (k) 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)); + 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/CloneChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CloneChecker.cpp index 7968aed85e1b..6692a45a09f7 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CloneChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CloneChecker.cpp @@ -30,12 +30,13 @@ class CloneChecker public: // Checker options. int MinComplexity; - bool ReportNormalClones; + bool ReportNormalClones = false; StringRef IgnoredFilesPattern; private: mutable CloneDetector Detector; - mutable std::unique_ptr<BugType> BT_Exact, BT_Suspicious; + const BugType BT_Exact{this, "Exact code clone", "Code clone"}; + const BugType BT_Suspicious{this, "Suspicious code clone", "Code clone"}; public: void checkASTCodeBody(const Decl *D, AnalysisManager &Mgr, @@ -107,15 +108,11 @@ static PathDiagnosticLocation makeLocation(const StmtSequence &S, void CloneChecker::reportClones( BugReporter &BR, AnalysisManager &Mgr, std::vector<CloneDetector::CloneGroup> &CloneGroups) const { - - if (!BT_Exact) - BT_Exact.reset(new BugType(this, "Exact code clone", "Code clone")); - for (const CloneDetector::CloneGroup &Group : CloneGroups) { // We group the clones by printing the first as a warning and all others // as a note. auto R = std::make_unique<BasicBugReport>( - *BT_Exact, "Duplicate code detected", makeLocation(Group.front(), Mgr)); + BT_Exact, "Duplicate code detected", makeLocation(Group.front(), Mgr)); R->addRange(Group.front().getSourceRange()); for (unsigned i = 1; i < Group.size(); ++i) @@ -154,10 +151,6 @@ void CloneChecker::reportSuspiciousClones( } } - if (!BT_Suspicious) - BT_Suspicious.reset( - new BugType(this, "Suspicious code clone", "Code clone")); - ASTContext &ACtx = BR.getContext(); SourceManager &SM = ACtx.getSourceManager(); AnalysisDeclContext *ADC = @@ -170,7 +163,7 @@ void CloneChecker::reportSuspiciousClones( // Think how to perform more accurate suggestions? auto R = std::make_unique<BasicBugReport>( - *BT_Suspicious, + BT_Suspicious, "Potential copy-paste error; did you really mean to use '" + Pair.FirstCloneInfo.Variable->getNameAsString() + "' here?", PathDiagnosticLocation::createBegin(Pair.FirstCloneInfo.Mention, SM, diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ContainerModeling.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ContainerModeling.cpp index 1a7f0d5ab74c..65a2ec4076fd 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ContainerModeling.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ContainerModeling.cpp @@ -10,11 +10,12 @@ // //===----------------------------------------------------------------------===// -#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/AST/DeclTemplate.h" #include "clang/Driver/DriverDiagnostic.h" +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.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/DynamicType.h" @@ -71,42 +72,27 @@ public: SVal) const; CallDescriptionMap<NoItParamFn> NoIterParamFunctions = { - {{0, "clear", 0}, - &ContainerModeling::handleClear}, - {{0, "assign", 2}, - &ContainerModeling::handleAssign}, - {{0, "push_back", 1}, - &ContainerModeling::handlePushBack}, - {{0, "emplace_back", 1}, - &ContainerModeling::handlePushBack}, - {{0, "pop_back", 0}, - &ContainerModeling::handlePopBack}, - {{0, "push_front", 1}, - &ContainerModeling::handlePushFront}, - {{0, "emplace_front", 1}, - &ContainerModeling::handlePushFront}, - {{0, "pop_front", 0}, - &ContainerModeling::handlePopFront}, + {{{"clear"}, 0}, &ContainerModeling::handleClear}, + {{{"assign"}, 2}, &ContainerModeling::handleAssign}, + {{{"push_back"}, 1}, &ContainerModeling::handlePushBack}, + {{{"emplace_back"}, 1}, &ContainerModeling::handlePushBack}, + {{{"pop_back"}, 0}, &ContainerModeling::handlePopBack}, + {{{"push_front"}, 1}, &ContainerModeling::handlePushFront}, + {{{"emplace_front"}, 1}, &ContainerModeling::handlePushFront}, + {{{"pop_front"}, 0}, &ContainerModeling::handlePopFront}, }; - + CallDescriptionMap<OneItParamFn> OneIterParamFunctions = { - {{0, "insert", 2}, - &ContainerModeling::handleInsert}, - {{0, "emplace", 2}, - &ContainerModeling::handleInsert}, - {{0, "erase", 1}, - &ContainerModeling::handleErase}, - {{0, "erase_after", 1}, - &ContainerModeling::handleEraseAfter}, + {{{"insert"}, 2}, &ContainerModeling::handleInsert}, + {{{"emplace"}, 2}, &ContainerModeling::handleInsert}, + {{{"erase"}, 1}, &ContainerModeling::handleErase}, + {{{"erase_after"}, 1}, &ContainerModeling::handleEraseAfter}, }; - + CallDescriptionMap<TwoItParamFn> TwoIterParamFunctions = { - {{0, "erase", 2}, - &ContainerModeling::handleErase}, - {{0, "erase_after", 2}, - &ContainerModeling::handleEraseAfter}, + {{{"erase"}, 2}, &ContainerModeling::handleErase}, + {{{"erase_after"}, 2}, &ContainerModeling::handleEraseAfter}, }; - }; bool isBeginCall(const FunctionDecl *Func); @@ -241,7 +227,7 @@ void ContainerModeling::checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const { // Cleanup auto State = C.getState(); - + auto ContMap = State->get<ContainerMap>(); for (const auto &Cont : ContMap) { if (!SR.isLiveRegion(Cont.first)) { @@ -763,14 +749,14 @@ bool isBeginCall(const FunctionDecl *Func) { const auto *IdInfo = Func->getIdentifier(); if (!IdInfo) return false; - return IdInfo->getName().endswith_insensitive("begin"); + return IdInfo->getName().ends_with_insensitive("begin"); } bool isEndCall(const FunctionDecl *Func) { const auto *IdInfo = Func->getIdentifier(); if (!IdInfo) return false; - return IdInfo->getName().endswith_insensitive("end"); + return IdInfo->getName().ends_with_insensitive("end"); } const CXXRecordDecl *getCXXRecordDecl(ProgramStateRef State, @@ -1035,7 +1021,7 @@ SymbolRef rebaseSymbol(ProgramStateRef State, SValBuilder &SVB, SymbolRef NewSym) { auto &SymMgr = SVB.getSymbolManager(); auto Diff = SVB.evalBinOpNN(State, BO_Sub, nonloc::SymbolVal(OrigExpr), - nonloc::SymbolVal(OldExpr), + nonloc::SymbolVal(OldExpr), SymMgr.getType(OrigExpr)); const auto DiffInt = Diff.getAs<nonloc::ConcreteInt>(); diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ConversionChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ConversionChecker.cpp index 4216a6883119..eca8d3cc0722 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ConversionChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ConversionChecker.cpp @@ -42,22 +42,22 @@ public: void checkPreStmt(const ImplicitCastExpr *Cast, CheckerContext &C) const; private: - mutable std::unique_ptr<BuiltinBug> BT; + const BugType BT{this, "Conversion"}; bool isLossOfPrecision(const ImplicitCastExpr *Cast, QualType DestType, CheckerContext &C) const; bool isLossOfSign(const ImplicitCastExpr *Cast, CheckerContext &C) const; - void reportBug(ExplodedNode *N, CheckerContext &C, const char Msg[]) const; + void reportBug(ExplodedNode *N, const Expr *E, CheckerContext &C, + const char Msg[]) const; }; } void ConversionChecker::checkPreStmt(const ImplicitCastExpr *Cast, CheckerContext &C) const { - // TODO: For now we only warn about DeclRefExpr, to avoid noise. Warn for - // calculations also. - if (!isa<DeclRefExpr>(Cast->IgnoreParenImpCasts())) + // Don't warn for implicit conversions to bool + if (Cast->getType()->isBooleanType()) return; // Don't warn for loss of sign/precision in macros. @@ -69,6 +69,9 @@ void ConversionChecker::checkPreStmt(const ImplicitCastExpr *Cast, const Stmt *Parent = PM.getParent(Cast); if (!Parent) return; + // Dont warn if this is part of an explicit cast + if (isa<ExplicitCastExpr>(Parent)) + return; bool LossOfSign = false; bool LossOfPrecision = false; @@ -77,8 +80,10 @@ void ConversionChecker::checkPreStmt(const ImplicitCastExpr *Cast, if (const auto *B = dyn_cast<BinaryOperator>(Parent)) { BinaryOperator::Opcode Opc = B->getOpcode(); if (Opc == BO_Assign) { - LossOfSign = isLossOfSign(Cast, C); - LossOfPrecision = isLossOfPrecision(Cast, Cast->getType(), C); + if (!Cast->IgnoreParenImpCasts()->isEvaluatable(C.getASTContext())) { + LossOfSign = isLossOfSign(Cast, C); + LossOfPrecision = isLossOfPrecision(Cast, Cast->getType(), C); + } } else if (Opc == BO_AddAssign || Opc == BO_SubAssign) { // No loss of sign. LossOfPrecision = isLossOfPrecision(Cast, B->getLHS()->getType(), C); @@ -97,7 +102,12 @@ void ConversionChecker::checkPreStmt(const ImplicitCastExpr *Cast, } else if (B->isRelationalOp() || B->isMultiplicativeOp()) { LossOfSign = isLossOfSign(Cast, C); } - } else if (isa<DeclStmt>(Parent)) { + } else if (isa<DeclStmt, ReturnStmt>(Parent)) { + if (!Cast->IgnoreParenImpCasts()->isEvaluatable(C.getASTContext())) { + LossOfSign = isLossOfSign(Cast, C); + LossOfPrecision = isLossOfPrecision(Cast, Cast->getType(), C); + } + } else { LossOfSign = isLossOfSign(Cast, C); LossOfPrecision = isLossOfPrecision(Cast, Cast->getType(), C); } @@ -108,20 +118,17 @@ void ConversionChecker::checkPreStmt(const ImplicitCastExpr *Cast, if (!N) return; if (LossOfSign) - reportBug(N, C, "Loss of sign in implicit conversion"); + reportBug(N, Cast, C, "Loss of sign in implicit conversion"); if (LossOfPrecision) - reportBug(N, C, "Loss of precision in implicit conversion"); + reportBug(N, Cast, C, "Loss of precision in implicit conversion"); } } -void ConversionChecker::reportBug(ExplodedNode *N, CheckerContext &C, - const char Msg[]) const { - if (!BT) - BT.reset( - new BuiltinBug(this, "Conversion", "Possible loss of sign/precision.")); - +void ConversionChecker::reportBug(ExplodedNode *N, const Expr *E, + CheckerContext &C, const char Msg[]) const { // Generate a report for this bug. - auto R = std::make_unique<PathSensitiveBugReport>(*BT, Msg, N); + auto R = std::make_unique<PathSensitiveBugReport>(BT, Msg, N); + bugreporter::trackExpressionValue(N, E, *R); C.emitReport(std::move(R)); } diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp index 8070d869f678..86f446fc411c 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp @@ -38,17 +38,17 @@ public: llvm::DenseSet<const VarDecl *> &S; bool TraverseObjCAtFinallyStmt(ObjCAtFinallyStmt *S) { - SaveAndRestore<bool> inFinally(inEH, true); + SaveAndRestore inFinally(inEH, true); return ::RecursiveASTVisitor<EHCodeVisitor>::TraverseObjCAtFinallyStmt(S); } bool TraverseObjCAtCatchStmt(ObjCAtCatchStmt *S) { - SaveAndRestore<bool> inCatch(inEH, true); + SaveAndRestore inCatch(inEH, true); return ::RecursiveASTVisitor<EHCodeVisitor>::TraverseObjCAtCatchStmt(S); } bool TraverseCXXCatchStmt(CXXCatchStmt *S) { - SaveAndRestore<bool> inCatch(inEH, true); + SaveAndRestore inCatch(inEH, true); return TraverseStmt(S->getHandlerBlock()); } @@ -93,9 +93,9 @@ void ReachableCode::computeReachableBlocks() { if (isReachable) continue; isReachable = true; - for (CFGBlock::const_succ_iterator i = block->succ_begin(), - e = block->succ_end(); i != e; ++i) - if (const CFGBlock *succ = *i) + + for (const CFGBlock *succ : block->succs()) + if (succ) worklist.push_back(succ); } } @@ -103,15 +103,12 @@ void ReachableCode::computeReachableBlocks() { static const Expr * LookThroughTransitiveAssignmentsAndCommaOperators(const Expr *Ex) { while (Ex) { - const BinaryOperator *BO = - dyn_cast<BinaryOperator>(Ex->IgnoreParenCasts()); + Ex = Ex->IgnoreParenCasts(); + const BinaryOperator *BO = dyn_cast<BinaryOperator>(Ex); if (!BO) break; - if (BO->getOpcode() == BO_Assign) { - Ex = BO->getRHS(); - continue; - } - if (BO->getOpcode() == BO_Comma) { + BinaryOperatorKind Op = BO->getOpcode(); + if (Op == BO_Assign || Op == BO_Comma) { Ex = BO->getRHS(); continue; } @@ -186,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; @@ -243,7 +240,7 @@ public: case DeadIncrement: BugType = "Dead increment"; - LLVM_FALLTHROUGH; + [[fallthrough]]; case Standard: if (!BugType) BugType = "Dead assignment"; os << "Value stored to '" << *V << "' is never read"; @@ -334,8 +331,7 @@ public: // Special case: check for assigning null to a pointer. // This is a common form of defensive programming. const Expr *RHS = - LookThroughTransitiveAssignmentsAndCommaOperators(B->getRHS()); - RHS = RHS->IgnoreParenCasts(); + LookThroughTransitiveAssignmentsAndCommaOperators(B->getRHS()); QualType T = VD->getType(); if (T.isVolatileQualified()) @@ -418,8 +414,7 @@ public: if (isConstant(E)) return; - if (const DeclRefExpr *DRE = - dyn_cast<DeclRefExpr>(E->IgnoreParenCasts())) + if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) if (const VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl())) { // Special case: check for initialization from constant // variables. @@ -441,7 +436,7 @@ public: PathDiagnosticLocation Loc = PathDiagnosticLocation::create(V, BR.getSourceManager()); - Report(V, DeadInit, Loc, E->getSourceRange()); + Report(V, DeadInit, Loc, V->getInit()->getSourceRange()); } } } @@ -453,8 +448,9 @@ private: bool isConstant(const InitListExpr *Candidate) const { // We consider init list to be constant if each member of the list can be // interpreted as constant. - return llvm::all_of(Candidate->inits(), - [this](const Expr *Init) { return isConstant(Init); }); + return llvm::all_of(Candidate->inits(), [this](const Expr *Init) { + return isConstant(Init->IgnoreParenCasts()); + }); } /// Return true if the given expression can be interpreted as constant @@ -464,7 +460,7 @@ private: return true; // We should also allow defensive initialization of structs, i.e. { 0 } - if (const auto *ILE = dyn_cast<InitListExpr>(E->IgnoreParenCasts())) { + if (const auto *ILE = dyn_cast<InitListExpr>(E)) { return isConstant(ILE); } @@ -507,7 +503,7 @@ public: // Treat local variables captured by reference in C++ lambdas as escaped. void findLambdaReferenceCaptures(const LambdaExpr *LE) { const CXXRecordDecl *LambdaClass = LE->getLambdaClass(); - llvm::DenseMap<const VarDecl *, FieldDecl *> CaptureFields; + llvm::DenseMap<const ValueDecl *, FieldDecl *> CaptureFields; FieldDecl *ThisCaptureField; LambdaClass->getCaptureFields(CaptureFields, ThisCaptureField); @@ -515,14 +511,14 @@ public: if (!C.capturesVariable()) continue; - VarDecl *VD = C.getCapturedVar(); + ValueDecl *VD = C.getCapturedVar(); const FieldDecl *FD = CaptureFields[VD]; - if (!FD) + if (!FD || !isa<VarDecl>(VD)) continue; // If the capture field is a reference type, it is capture-by-reference. if (FD->getType()->isReferenceType()) - Escaped.insert(VD); + Escaped.insert(cast<VarDecl>(VD)); } } }; diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp index 7cdd78b8adfb..04bbe85473c0 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp @@ -271,9 +271,8 @@ public: const Table &Config = mgr.options.Config; SmallVector<const Table::MapEntryTy *, 32> Keys; - for (Table::const_iterator I = Config.begin(), E = Config.end(); I != E; - ++I) - Keys.push_back(&*I); + for (const auto &Entry : Config) + Keys.push_back(&Entry); llvm::array_pod_sort(Keys.begin(), Keys.end(), compareEntry); llvm::errs() << "[config]\n"; @@ -302,7 +301,7 @@ class ExplodedGraphViewer : public Checker< check::EndAnalysis > { public: ExplodedGraphViewer() {} void checkEndAnalysis(ExplodedGraph &G, BugReporter &B,ExprEngine &Eng) const { - Eng.ViewGraph(0); + Eng.ViewGraph(false); } }; @@ -323,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/DebugContainerModeling.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DebugContainerModeling.cpp index 6fed999ffc80..97f769b1c451 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DebugContainerModeling.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DebugContainerModeling.cpp @@ -13,6 +13,7 @@ #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" @@ -27,7 +28,8 @@ namespace { class DebugContainerModeling : public Checker<eval::Call> { - std::unique_ptr<BugType> DebugMsgBugType; + const BugType DebugMsgBugType{this, "Checking analyzer assumptions", "debug", + /*SuppressOnSink=*/true}; template <typename Getter> void analyzerContainerDataField(const CallExpr *CE, CheckerContext &C, @@ -40,26 +42,18 @@ class DebugContainerModeling CheckerContext &) const; CallDescriptionMap<FnCheck> Callbacks = { - {{0, "clang_analyzer_container_begin", 1}, - &DebugContainerModeling::analyzerContainerBegin}, - {{0, "clang_analyzer_container_end", 1}, - &DebugContainerModeling::analyzerContainerEnd}, + {{{"clang_analyzer_container_begin"}, 1}, + &DebugContainerModeling::analyzerContainerBegin}, + {{{"clang_analyzer_container_end"}, 1}, + &DebugContainerModeling::analyzerContainerEnd}, }; public: - DebugContainerModeling(); - bool evalCall(const CallEvent &Call, CheckerContext &C) const; }; } //namespace -DebugContainerModeling::DebugContainerModeling() { - DebugMsgBugType.reset( - new BugType(this, "Checking analyzer assumptions", "debug", - /*SuppressOnSink=*/true)); -} - bool DebugContainerModeling::evalCall(const CallEvent &Call, CheckerContext &C) const { const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); @@ -136,8 +130,8 @@ ExplodedNode *DebugContainerModeling::reportDebugMsg(llvm::StringRef Msg, return nullptr; auto &BR = C.getBugReporter(); - BR.emitReport(std::make_unique<PathSensitiveBugReport>(*DebugMsgBugType, - Msg, N)); + BR.emitReport( + std::make_unique<PathSensitiveBugReport>(DebugMsgBugType, Msg, N)); return N; } diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DebugIteratorModeling.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DebugIteratorModeling.cpp index 5833eea56da8..ff479c7b0ac8 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DebugIteratorModeling.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DebugIteratorModeling.cpp @@ -13,6 +13,7 @@ #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" @@ -27,7 +28,8 @@ namespace { class DebugIteratorModeling : public Checker<eval::Call> { - std::unique_ptr<BugType> DebugMsgBugType; + const BugType DebugMsgBugType{this, "Checking analyzer assumptions", "debug", + /*SuppressOnSink=*/true}; template <typename Getter> void analyzerIteratorDataField(const CallExpr *CE, CheckerContext &C, @@ -41,28 +43,20 @@ class DebugIteratorModeling CheckerContext &) const; CallDescriptionMap<FnCheck> Callbacks = { - {{0, "clang_analyzer_iterator_position", 1}, - &DebugIteratorModeling::analyzerIteratorPosition}, - {{0, "clang_analyzer_iterator_container", 1}, - &DebugIteratorModeling::analyzerIteratorContainer}, - {{0, "clang_analyzer_iterator_validity", 1}, - &DebugIteratorModeling::analyzerIteratorValidity}, + {{{"clang_analyzer_iterator_position"}, 1}, + &DebugIteratorModeling::analyzerIteratorPosition}, + {{{"clang_analyzer_iterator_container"}, 1}, + &DebugIteratorModeling::analyzerIteratorContainer}, + {{{"clang_analyzer_iterator_validity"}, 1}, + &DebugIteratorModeling::analyzerIteratorValidity}, }; public: - DebugIteratorModeling(); - bool evalCall(const CallEvent &Call, CheckerContext &C) const; }; } //namespace -DebugIteratorModeling::DebugIteratorModeling() { - DebugMsgBugType.reset( - new BugType(this, "Checking analyzer assumptions", "debug", - /*SuppressOnSink=*/true)); -} - bool DebugIteratorModeling::evalCall(const CallEvent &Call, CheckerContext &C) const { const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); @@ -130,8 +124,8 @@ ExplodedNode *DebugIteratorModeling::reportDebugMsg(llvm::StringRef Msg, return nullptr; auto &BR = C.getBugReporter(); - BR.emitReport(std::make_unique<PathSensitiveBugReport>(*DebugMsgBugType, - Msg, N)); + BR.emitReport( + std::make_unique<PathSensitiveBugReport>(DebugMsgBugType, Msg, N)); return N; } 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/DereferenceChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp index 4a9c7ce3c66d..a678c3827e7f 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp @@ -11,9 +11,10 @@ // //===----------------------------------------------------------------------===// -#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/AST/ExprObjC.h" #include "clang/AST/ExprOpenMP.h" +#include "clang/Basic/TargetInfo.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" @@ -39,6 +40,8 @@ class DereferenceChecker void reportBug(DerefKind K, ProgramStateRef State, const Stmt *S, CheckerContext &C) const; + bool suppressReport(CheckerContext &C, const Expr *E) const; + public: void checkLocation(SVal location, bool isLoad, const Stmt* S, CheckerContext &C) const; @@ -49,6 +52,8 @@ public: const Expr *Ex, const ProgramState *state, const LocationContext *LCtx, bool loadedFrom = false); + + bool SuppressAddressSpaces = false; }; } // end anonymous namespace @@ -109,9 +114,35 @@ static const Expr *getDereferenceExpr(const Stmt *S, bool IsBind=false){ return E; } -static bool suppressReport(const Expr *E) { - // Do not report dereferences on memory in non-default address spaces. - return E->getType().hasAddressSpace(); +bool DereferenceChecker::suppressReport(CheckerContext &C, + const Expr *E) const { + // Do not report dereferences on memory that use address space #256, #257, + // and #258. Those address spaces are used when dereferencing address spaces + // relative to the GS, FS, and SS segments on x86/x86-64 targets. + // Dereferencing a null pointer in these address spaces is not defined + // as an error. All other null dereferences in other address spaces + // are defined as an error unless explicitly defined. + // See https://clang.llvm.org/docs/LanguageExtensions.html, the section + // "X86/X86-64 Language Extensions" + + QualType Ty = E->getType(); + if (!Ty.hasAddressSpace()) + return false; + if (SuppressAddressSpaces) + return true; + + const llvm::Triple::ArchType Arch = + C.getASTContext().getTargetInfo().getTriple().getArch(); + + if ((Arch == llvm::Triple::x86) || (Arch == llvm::Triple::x86_64)) { + switch (toTargetAddressSpace(E->getType().getAddressSpace())) { + case 256: + case 257: + case 258: + return true; + } + } + return false; } static bool isDeclRefExprToReference(const Expr *E) { @@ -209,7 +240,7 @@ void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S, // Check for dereference of an undefined value. if (l.isUndef()) { const Expr *DerefExpr = getDereferenceExpr(S); - if (!suppressReport(DerefExpr)) + if (!suppressReport(C, DerefExpr)) reportBug(DerefKind::UndefinedPointerValue, C.getState(), DerefExpr, C); return; } @@ -217,7 +248,7 @@ void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S, DefinedOrUnknownSVal location = l.castAs<DefinedOrUnknownSVal>(); // Check for null dereferences. - if (!location.getAs<Loc>()) + if (!isa<Loc>(location)) return; ProgramStateRef state = C.getState(); @@ -230,7 +261,7 @@ void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S, // We know that 'location' can only be null. This is what // we call an "explicit" null dereference. const Expr *expr = getDereferenceExpr(S); - if (!suppressReport(expr)) { + if (!suppressReport(C, expr)) { reportBug(DerefKind::NullPointer, nullState, expr, C); return; } @@ -272,7 +303,7 @@ void DereferenceChecker::checkBind(SVal L, SVal V, const Stmt *S, if (StNull) { if (!StNonNull) { const Expr *expr = getDereferenceExpr(S, /*IsBind=*/true); - if (!suppressReport(expr)) { + if (!suppressReport(C, expr)) { reportBug(DerefKind::NullPointer, StNull, expr, C); return; } @@ -308,7 +339,9 @@ void DereferenceChecker::checkBind(SVal L, SVal V, const Stmt *S, } void ento::registerDereferenceChecker(CheckerManager &mgr) { - mgr.registerChecker<DereferenceChecker>(); + auto *Chk = mgr.registerChecker<DereferenceChecker>(); + Chk->SuppressAddressSpaces = mgr.getAnalyzerOptions().getCheckerBooleanOption( + mgr.getCurrentCheckerName(), "SuppressAddressSpaces"); } bool ento::shouldRegisterDereferenceChecker(const CheckerManager &mgr) { diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DirectIvarAssignment.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DirectIvarAssignment.cpp index df88b71ff063..49486ea796c2 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DirectIvarAssignment.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DirectIvarAssignment.cpp @@ -44,8 +44,8 @@ static bool DefaultMethodFilter(const ObjCMethodDecl *M) { M->getMethodFamily() == OMF_dealloc || M->getMethodFamily() == OMF_copy || M->getMethodFamily() == OMF_mutableCopy || - M->getSelector().getNameForSlot(0).find("init") != StringRef::npos || - M->getSelector().getNameForSlot(0).find("Init") != StringRef::npos; + M->getSelector().getNameForSlot(0).contains("init") || + M->getSelector().getNameForSlot(0).contains("Init"); } class DirectIvarAssignment : diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp index 2b3164ba4a2c..5496f087447f 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp @@ -11,12 +11,14 @@ // //===----------------------------------------------------------------------===// -#include "Taint.h" #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Checkers/Taint.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include <optional> using namespace clang; using namespace ento; @@ -24,9 +26,13 @@ using namespace taint; namespace { class DivZeroChecker : public Checker< check::PreStmt<BinaryOperator> > { - mutable std::unique_ptr<BuiltinBug> BT; - void reportBug(const char *Msg, ProgramStateRef StateZero, CheckerContext &C, - std::unique_ptr<BugReporterVisitor> Visitor = nullptr) const; + const BugType BT{this, "Division by zero"}; + const BugType TaintBT{this, "Division by zero", categories::TaintedData}; + void reportBug(StringRef Msg, ProgramStateRef StateZero, + CheckerContext &C) const; + void reportTaintBug(StringRef Msg, ProgramStateRef StateZero, + CheckerContext &C, + llvm::ArrayRef<SymbolRef> TaintedSyms) const; public: void checkPreStmt(const BinaryOperator *B, CheckerContext &C) const; @@ -40,16 +46,23 @@ static const Expr *getDenomExpr(const ExplodedNode *N) { return nullptr; } -void DivZeroChecker::reportBug( - const char *Msg, ProgramStateRef StateZero, CheckerContext &C, - std::unique_ptr<BugReporterVisitor> Visitor) const { +void DivZeroChecker::reportBug(StringRef Msg, ProgramStateRef StateZero, + CheckerContext &C) const { if (ExplodedNode *N = C.generateErrorNode(StateZero)) { - if (!BT) - BT.reset(new BuiltinBug(this, "Division by zero")); + auto R = std::make_unique<PathSensitiveBugReport>(BT, Msg, N); + bugreporter::trackExpressionValue(N, getDenomExpr(N), *R); + C.emitReport(std::move(R)); + } +} - auto R = std::make_unique<PathSensitiveBugReport>(*BT, Msg, N); - R->addVisitor(std::move(Visitor)); +void DivZeroChecker::reportTaintBug( + StringRef Msg, ProgramStateRef StateZero, CheckerContext &C, + llvm::ArrayRef<SymbolRef> TaintedSyms) const { + if (ExplodedNode *N = C.generateErrorNode(StateZero)) { + auto R = std::make_unique<PathSensitiveBugReport>(TaintBT, Msg, N); bugreporter::trackExpressionValue(N, getDenomExpr(N), *R); + for (auto Sym : TaintedSyms) + R->markInteresting(Sym); C.emitReport(std::move(R)); } } @@ -67,7 +80,7 @@ void DivZeroChecker::checkPreStmt(const BinaryOperator *B, return; SVal Denom = C.getSVal(B->getRHS()); - Optional<DefinedSVal> DV = Denom.getAs<DefinedSVal>(); + std::optional<DefinedSVal> DV = Denom.getAs<DefinedSVal>(); // Divide-by-undefined handled in the generic checking for uses of // undefined values. @@ -85,11 +98,13 @@ void DivZeroChecker::checkPreStmt(const BinaryOperator *B, return; } - bool TaintedD = isTainted(C.getState(), *DV); - if ((stateNotZero && stateZero && TaintedD)) { - reportBug("Division by a tainted value, possibly zero", stateZero, C, - std::make_unique<taint::TaintBugVisitor>(*DV)); - return; + if ((stateNotZero && stateZero)) { + std::vector<SymbolRef> taintedSyms = getTaintedSymbols(C.getState(), *DV); + if (!taintedSyms.empty()) { + reportTaintBug("Division by a tainted value, possibly zero", stateZero, C, + taintedSyms); + return; + } } // If we get here, then the denom should not be zero. We abandon the implicit diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DynamicTypeChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DynamicTypeChecker.cpp index dbc930d7d37b..0ad307d3ebd5 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DynamicTypeChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DynamicTypeChecker.cpp @@ -30,12 +30,7 @@ using namespace ento; namespace { class DynamicTypeChecker : public Checker<check::PostStmt<ImplicitCastExpr>> { - mutable std::unique_ptr<BugType> BT; - void initBugType() const { - if (!BT) - BT.reset( - new BugType(this, "Dynamic and static type mismatch", "Type Error")); - } + const BugType BT{this, "Dynamic and static type mismatch", "Type Error"}; class DynamicTypeBugVisitor : public BugReporterVisitor { public: @@ -70,7 +65,6 @@ void DynamicTypeChecker::reportTypeError(QualType DynamicType, const MemRegion *Reg, const Stmt *ReportedNode, CheckerContext &C) const { - initBugType(); SmallString<192> Buf; llvm::raw_svector_ostream OS(Buf); OS << "Object has a dynamic type '"; @@ -81,7 +75,7 @@ void DynamicTypeChecker::reportTypeError(QualType DynamicType, llvm::Twine()); OS << "'"; auto R = std::make_unique<PathSensitiveBugReport>( - *BT, OS.str(), C.generateNonFatalErrorNode()); + BT, OS.str(), C.generateNonFatalErrorNode()); R->markInteresting(Reg); R->addVisitor(std::make_unique<DynamicTypeBugVisitor>(Reg)); R->addRange(ReportedNode->getSourceRange()); diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp index 14ba5d769969..034774a252b1 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp @@ -31,6 +31,8 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" +#include "llvm/ADT/STLExtras.h" +#include <optional> using namespace clang; using namespace ento; @@ -56,9 +58,6 @@ class DynamicTypePropagation: check::PreObjCMessage, check::PostObjCMessage > { - const ObjCObjectType *getObjectTypeForAllocAndNew(const ObjCMessageExpr *MsgE, - CheckerContext &C) const; - /// Return a better dynamic type if one can be derived from the cast. const ObjCObjectPointerType *getBetterObjCType(const Expr *CastE, CheckerContext &C) const; @@ -108,7 +107,7 @@ public: void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; /// This value is set to true, when the Generics checker is turned on. - DefaultBool CheckGenerics; + bool CheckGenerics = false; CheckerNameRef GenericCheckName; }; @@ -235,11 +234,9 @@ void DynamicTypePropagation::checkDeadSymbols(SymbolReaper &SR, MostSpecializedTypeArgsMapTy TyArgMap = State->get<MostSpecializedTypeArgsMap>(); - for (MostSpecializedTypeArgsMapTy::iterator I = TyArgMap.begin(), - E = TyArgMap.end(); - I != E; ++I) { - if (SR.isDead(I->first)) { - State = State->remove<MostSpecializedTypeArgsMap>(I->first); + for (SymbolRef Sym : llvm::make_first_range(TyArgMap)) { + if (SR.isDead(Sym)) { + State = State->remove<MostSpecializedTypeArgsMap>(Sym); } } @@ -271,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; @@ -363,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. @@ -384,7 +381,7 @@ void DynamicTypePropagation::checkPostCall(const CallEvent &Call, // FIXME: Instead of relying on the ParentMap, we should have the // trigger-statement (InitListExpr in this case) available in this // callback, ideally as part of CallEvent. - if (dyn_cast_or_null<InitListExpr>( + if (isa_and_nonnull<InitListExpr>( LCtx->getParentMap().getParent(Ctor->getOriginExpr()))) return; @@ -716,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; @@ -725,7 +722,7 @@ static bool isObjCTypeParamDependent(QualType Type) { return true; } - bool Result; + bool Result = false; }; IsObjCTypeParamDependentTypeVisitor Visitor; @@ -744,8 +741,6 @@ findMethodDecl(const ObjCMessageExpr *MessageExpr, const ObjCMethodDecl *Method = nullptr; QualType ReceiverType = MessageExpr->getReceiverType(); - const auto *ReceiverObjectPtrType = - ReceiverType->getAs<ObjCObjectPointerType>(); // Do this "devirtualization" on instance and class methods only. Trust the // static type on super and super class calls. @@ -755,7 +750,8 @@ findMethodDecl(const ObjCMessageExpr *MessageExpr, // type, look up the method in the tracked type, not in the receiver type. // This way we preserve more information. if (ReceiverType->isObjCIdType() || ReceiverType->isObjCClassType() || - ASTCtxt.canAssignObjCInterfaces(ReceiverObjectPtrType, TrackedType)) { + ASTCtxt.canAssignObjCInterfaces( + ReceiverType->castAs<ObjCObjectPointerType>(), TrackedType)) { const ObjCInterfaceDecl *InterfaceDecl = TrackedType->getInterfaceDecl(); // The method might not be found. Selector Sel = MessageExpr->getSelector(); @@ -849,7 +845,7 @@ void DynamicTypePropagation::checkPreObjCMessage(const ObjCMethodCall &M, return; } - Optional<ArrayRef<QualType>> TypeArgs = + std::optional<ArrayRef<QualType>> TypeArgs = (*TrackedType)->getObjCSubstitutions(Method->getDeclContext()); // This case might happen when there is an unspecialized override of a // specialized method. @@ -982,7 +978,7 @@ void DynamicTypePropagation::checkPostObjCMessage(const ObjCMethodCall &M, if (!Method) return; - Optional<ArrayRef<QualType>> TypeArgs = + std::optional<ArrayRef<QualType>> TypeArgs = (*TrackedType)->getObjCSubstitutions(Method->getDeclContext()); if (!TypeArgs) return; diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp index 0e94b915a468..0fa20428c1b5 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp @@ -22,9 +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 @@ -57,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; + const BugType EnumValueCastOutOfRange{this, "Enum cast out of range"}; + void reportWarning(CheckerContext &C, const CastExpr *CE, + const EnumDecl *E) const; public: void checkPreStmt(const CastExpr *CE, CheckerContext &C) const; @@ -71,21 +75,39 @@ 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)); + 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)); } } @@ -94,10 +116,10 @@ void EnumCastOutOfRangeChecker::checkPreStmt(const CastExpr *CE, // Only perform enum range check on casts where such checks are valid. For // all other cast kinds (where enum range checks are unnecessary or invalid), - // just return immediately. TODO: The set of casts whitelisted for enum - // range checking may be incomplete. Better to add a missing cast kind to - // enable a missing check than to generate false negatives and have to remove - // those later. + // just return immediately. TODO: The set of casts allowed for enum range + // checking may be incomplete. Better to add a missing cast kind to enable a + // missing check than to generate false negatives and have to remove those + // later. switch (CE->getCastKind()) { case CK_IntegralCast: break; @@ -108,7 +130,7 @@ void EnumCastOutOfRangeChecker::checkPreStmt(const CastExpr *CE, } // Get the value of the expression to cast. - const llvm::Optional<DefinedOrUnknownSVal> ValueToCast = + const std::optional<DefinedOrUnknownSVal> ValueToCast = C.getSVal(CE->getSubExpr()).getAs<DefinedOrUnknownSVal>(); // If the value cannot be reasoned about (not even a DefinedOrUnknownSVal), @@ -128,14 +150,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/ErrnoChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ErrnoChecker.cpp new file mode 100644 index 000000000000..265185e64107 --- /dev/null +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ErrnoChecker.cpp @@ -0,0 +1,250 @@ +//=== ErrnoChecker.cpp ------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This defines an "errno checker" that can detect some invalid use of the +// system-defined value 'errno'. This checker works together with the +// ErrnoModeling checker and other checkers like StdCLibraryFunctions. +// +//===----------------------------------------------------------------------===// + +#include "ErrnoModeling.h" +#include "clang/AST/ParentMapContext.h" +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.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/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "llvm/ADT/STLExtras.h" +#include <optional> + +using namespace clang; +using namespace ento; +using namespace errno_modeling; + +namespace { + +class ErrnoChecker + : public Checker<check::Location, check::PreCall, check::RegionChanges> { +public: + void checkLocation(SVal Loc, bool IsLoad, const Stmt *S, + CheckerContext &) const; + void checkPreCall(const CallEvent &Call, CheckerContext &C) const; + ProgramStateRef + checkRegionChanges(ProgramStateRef State, + const InvalidatedSymbols *Invalidated, + ArrayRef<const MemRegion *> ExplicitRegions, + ArrayRef<const MemRegion *> Regions, + const LocationContext *LCtx, const CallEvent *Call) const; + void checkBranchCondition(const Stmt *Condition, CheckerContext &Ctx) const; + + /// Indicates if a read (load) of \c errno is allowed in a non-condition part + /// of \c if, \c switch, loop and conditional statements when the errno + /// value may be undefined. + bool AllowErrnoReadOutsideConditions = true; + +private: + void generateErrnoNotCheckedBug(CheckerContext &C, ProgramStateRef State, + const MemRegion *ErrnoRegion, + const CallEvent *CallMayChangeErrno) const; + + BugType BT_InvalidErrnoRead{this, "Value of 'errno' could be undefined", + "Error handling"}; + BugType BT_ErrnoNotChecked{this, "Value of 'errno' was not checked", + "Error handling"}; +}; + +} // namespace + +static ProgramStateRef setErrnoStateIrrelevant(ProgramStateRef State) { + return setErrnoState(State, Irrelevant); +} + +/// Check if a statement (expression) or an ancestor of it is in a condition +/// part of a (conditional, loop, switch) statement. +static bool isInCondition(const Stmt *S, CheckerContext &C) { + ParentMapContext &ParentCtx = C.getASTContext().getParentMapContext(); + bool CondFound = false; + while (S && !CondFound) { + const DynTypedNodeList Parents = ParentCtx.getParents(*S); + if (Parents.empty()) + break; + const auto *ParentS = Parents[0].get<Stmt>(); + if (!ParentS || isa<CallExpr>(ParentS)) + break; + switch (ParentS->getStmtClass()) { + case Expr::IfStmtClass: + CondFound = (S == cast<IfStmt>(ParentS)->getCond()); + break; + case Expr::ForStmtClass: + CondFound = (S == cast<ForStmt>(ParentS)->getCond()); + break; + case Expr::DoStmtClass: + CondFound = (S == cast<DoStmt>(ParentS)->getCond()); + break; + case Expr::WhileStmtClass: + CondFound = (S == cast<WhileStmt>(ParentS)->getCond()); + break; + case Expr::SwitchStmtClass: + CondFound = (S == cast<SwitchStmt>(ParentS)->getCond()); + break; + case Expr::ConditionalOperatorClass: + CondFound = (S == cast<ConditionalOperator>(ParentS)->getCond()); + break; + case Expr::BinaryConditionalOperatorClass: + CondFound = (S == cast<BinaryConditionalOperator>(ParentS)->getCommon()); + break; + default: + break; + } + S = ParentS; + } + return CondFound; +} + +void ErrnoChecker::generateErrnoNotCheckedBug( + CheckerContext &C, ProgramStateRef State, const MemRegion *ErrnoRegion, + const CallEvent *CallMayChangeErrno) const { + if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) { + SmallString<100> StrBuf; + llvm::raw_svector_ostream OS(StrBuf); + if (CallMayChangeErrno) { + OS << "Value of 'errno' was not checked and may be overwritten by " + "function '"; + const auto *CallD = + dyn_cast_or_null<FunctionDecl>(CallMayChangeErrno->getDecl()); + assert(CallD && CallD->getIdentifier()); + OS << CallD->getIdentifier()->getName() << "'"; + } else { + OS << "Value of 'errno' was not checked and is overwritten here"; + } + auto BR = std::make_unique<PathSensitiveBugReport>(BT_ErrnoNotChecked, + OS.str(), N); + BR->markInteresting(ErrnoRegion); + C.emitReport(std::move(BR)); + } +} + +void ErrnoChecker::checkLocation(SVal Loc, bool IsLoad, const Stmt *S, + CheckerContext &C) const { + std::optional<ento::Loc> ErrnoLoc = getErrnoLoc(C.getState()); + if (!ErrnoLoc) + return; + + auto L = Loc.getAs<ento::Loc>(); + if (!L || *ErrnoLoc != *L) + return; + + ProgramStateRef State = C.getState(); + ErrnoCheckState EState = getErrnoState(State); + + if (IsLoad) { + switch (EState) { + case MustNotBeChecked: + // Read of 'errno' when it may have undefined value. + if (!AllowErrnoReadOutsideConditions || isInCondition(S, C)) { + if (ExplodedNode *N = C.generateErrorNode()) { + auto BR = std::make_unique<PathSensitiveBugReport>( + BT_InvalidErrnoRead, + "An undefined value may be read from 'errno'", N); + BR->markInteresting(ErrnoLoc->getAsRegion()); + C.emitReport(std::move(BR)); + } + } + break; + case MustBeChecked: + // 'errno' has to be checked. A load is required for this, with no more + // information we can assume that it is checked somehow. + // After this place 'errno' is allowed to be read and written. + State = setErrnoStateIrrelevant(State); + C.addTransition(State); + break; + default: + break; + } + } else { + switch (EState) { + case MustBeChecked: + // 'errno' is overwritten without a read before but it should have been + // checked. + generateErrnoNotCheckedBug(C, setErrnoStateIrrelevant(State), + ErrnoLoc->getAsRegion(), nullptr); + break; + case MustNotBeChecked: + // Write to 'errno' when it is not allowed to be read. + // After this place 'errno' is allowed to be read and written. + State = setErrnoStateIrrelevant(State); + C.addTransition(State); + break; + default: + break; + } + } +} + +void ErrnoChecker::checkPreCall(const CallEvent &Call, + CheckerContext &C) const { + const auto *CallF = dyn_cast_or_null<FunctionDecl>(Call.getDecl()); + if (!CallF) + return; + + CallF = CallF->getCanonicalDecl(); + // If 'errno' must be checked, it should be done as soon as possible, and + // before any other call to a system function (something in a system header). + // To avoid use of a long list of functions that may change 'errno' + // (which may be different with standard library versions) assume that any + // function can change it. + // A list of special functions can be used that are allowed here without + // generation of diagnostic. For now the only such case is 'errno' itself. + // Probably 'strerror'? + if (CallF->isExternC() && CallF->isGlobal() && + C.getSourceManager().isInSystemHeader(CallF->getLocation()) && + !isErrno(CallF)) { + if (getErrnoState(C.getState()) == MustBeChecked) { + std::optional<ento::Loc> ErrnoLoc = getErrnoLoc(C.getState()); + assert(ErrnoLoc && "ErrnoLoc should exist if an errno state is set."); + generateErrnoNotCheckedBug(C, setErrnoStateIrrelevant(C.getState()), + ErrnoLoc->getAsRegion(), &Call); + } + } +} + +ProgramStateRef ErrnoChecker::checkRegionChanges( + ProgramStateRef State, const InvalidatedSymbols *Invalidated, + ArrayRef<const MemRegion *> ExplicitRegions, + ArrayRef<const MemRegion *> Regions, const LocationContext *LCtx, + const CallEvent *Call) const { + std::optional<ento::Loc> ErrnoLoc = getErrnoLoc(State); + if (!ErrnoLoc) + return State; + const MemRegion *ErrnoRegion = ErrnoLoc->getAsRegion(); + + // If 'errno' is invalidated we can not know if it is checked or written into, + // allow read and write without bug reports. + if (llvm::is_contained(Regions, ErrnoRegion)) + return clearErrnoState(State); + + // Always reset errno state when the system memory space is invalidated. + // The ErrnoRegion is not always found in the list in this case. + if (llvm::is_contained(Regions, ErrnoRegion->getMemorySpace())) + return clearErrnoState(State); + + return State; +} + +void ento::registerErrnoChecker(CheckerManager &mgr) { + const AnalyzerOptions &Opts = mgr.getAnalyzerOptions(); + auto *Checker = mgr.registerChecker<ErrnoChecker>(); + Checker->AllowErrnoReadOutsideConditions = Opts.getCheckerBooleanOption( + Checker, "AllowErrnoReadOutsideConditionExpressions"); +} + +bool ento::shouldRegisterErrnoChecker(const CheckerManager &mgr) { + return true; +} diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.cpp new file mode 100644 index 000000000000..1b34ea0e056e --- /dev/null +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.cpp @@ -0,0 +1,325 @@ +//=== ErrnoModeling.cpp -----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This defines a checker `ErrnoModeling`, which is used to make the system +// value 'errno' available to other checkers. +// The 'errno' value is stored at a special memory region that is accessible +// through the `errno_modeling` namespace. The memory region is either the +// region of `errno` itself if it is a variable, otherwise an artifically +// created region (in the system memory space). If `errno` is defined by using +// a function which returns the address of it (this is always the case if it is +// not a variable) this function is recognized and evaluated. In this way +// `errno` becomes visible to the analysis and checkers can change its value. +// +//===----------------------------------------------------------------------===// + +#include "ErrnoModeling.h" +#include "clang/AST/ParentMapContext.h" +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.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/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/FormatVariadic.h" +#include <optional> + +using namespace clang; +using namespace ento; + +namespace { + +// Name of the "errno" variable. +// FIXME: Is there a system where it is not called "errno" but is a variable? +const char *ErrnoVarName = "errno"; +// Names of functions that return a location of the "errno" value. +// FIXME: Are there other similar function names? +const char *ErrnoLocationFuncNames[] = {"__errno_location", "___errno", + "__errno", "_errno", "__error"}; + +class ErrnoModeling + : public Checker<check::ASTDecl<TranslationUnitDecl>, check::BeginFunction, + check::LiveSymbols, eval::Call> { +public: + void checkASTDecl(const TranslationUnitDecl *D, AnalysisManager &Mgr, + BugReporter &BR) const; + void checkBeginFunction(CheckerContext &C) const; + void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const; + bool evalCall(const CallEvent &Call, CheckerContext &C) const; + + // The declaration of an "errno" variable or "errno location" function. + mutable const Decl *ErrnoDecl = nullptr; + +private: + // FIXME: Names from `ErrnoLocationFuncNames` are used to build this set. + CallDescriptionSet ErrnoLocationCalls{{{"__errno_location"}, 0, 0}, + {{"___errno"}, 0, 0}, + {{"__errno"}, 0, 0}, + {{"_errno"}, 0, 0}, + {{"__error"}, 0, 0}}; +}; + +} // namespace + +/// Store a MemRegion that contains the 'errno' integer value. +/// The value is null if the 'errno' value was not recognized in the AST. +REGISTER_TRAIT_WITH_PROGRAMSTATE(ErrnoRegion, const MemRegion *) + +REGISTER_TRAIT_WITH_PROGRAMSTATE(ErrnoState, errno_modeling::ErrnoCheckState) + +/// Search for a variable called "errno" in the AST. +/// Return nullptr if not found. +static const VarDecl *getErrnoVar(ASTContext &ACtx) { + IdentifierInfo &II = ACtx.Idents.get(ErrnoVarName); + auto LookupRes = ACtx.getTranslationUnitDecl()->lookup(&II); + auto Found = llvm::find_if(LookupRes, [&ACtx](const Decl *D) { + if (auto *VD = dyn_cast<VarDecl>(D)) + return ACtx.getSourceManager().isInSystemHeader(VD->getLocation()) && + VD->hasExternalStorage() && + VD->getType().getCanonicalType() == ACtx.IntTy; + return false; + }); + if (Found == LookupRes.end()) + return nullptr; + + return cast<VarDecl>(*Found); +} + +/// Search for a function with a specific name that is used to return a pointer +/// to "errno". +/// Return nullptr if no such function was found. +static const FunctionDecl *getErrnoFunc(ASTContext &ACtx) { + SmallVector<const Decl *> LookupRes; + for (StringRef ErrnoName : ErrnoLocationFuncNames) { + IdentifierInfo &II = ACtx.Idents.get(ErrnoName); + llvm::append_range(LookupRes, ACtx.getTranslationUnitDecl()->lookup(&II)); + } + + auto Found = llvm::find_if(LookupRes, [&ACtx](const Decl *D) { + if (auto *FD = dyn_cast<FunctionDecl>(D)) + return ACtx.getSourceManager().isInSystemHeader(FD->getLocation()) && + FD->isExternC() && FD->getNumParams() == 0 && + FD->getReturnType().getCanonicalType() == + ACtx.getPointerType(ACtx.IntTy); + return false; + }); + if (Found == LookupRes.end()) + return nullptr; + + return cast<FunctionDecl>(*Found); +} + +void ErrnoModeling::checkASTDecl(const TranslationUnitDecl *D, + AnalysisManager &Mgr, BugReporter &BR) const { + // Try to find an usable `errno` value. + // It can be an external variable called "errno" or a function that returns a + // pointer to the "errno" value. This function can have different names. + // The actual case is dependent on the C library implementation, we + // can only search for a match in one of these variations. + // We assume that exactly one of these cases might be true. + ErrnoDecl = getErrnoVar(Mgr.getASTContext()); + if (!ErrnoDecl) + ErrnoDecl = getErrnoFunc(Mgr.getASTContext()); +} + +void ErrnoModeling::checkBeginFunction(CheckerContext &C) const { + if (!C.inTopFrame()) + return; + + ASTContext &ACtx = C.getASTContext(); + ProgramStateRef State = C.getState(); + + if (const auto *ErrnoVar = dyn_cast_or_null<VarDecl>(ErrnoDecl)) { + // There is an external 'errno' variable. + // Use its memory region. + // The memory region for an 'errno'-like variable is allocated in system + // space by MemRegionManager. + const MemRegion *ErrnoR = + State->getRegion(ErrnoVar, C.getLocationContext()); + assert(ErrnoR && "Memory region should exist for the 'errno' variable."); + State = State->set<ErrnoRegion>(ErrnoR); + State = + errno_modeling::setErrnoValue(State, C, 0, errno_modeling::Irrelevant); + C.addTransition(State); + } else if (ErrnoDecl) { + assert(isa<FunctionDecl>(ErrnoDecl) && "Invalid errno location function."); + // There is a function that returns the location of 'errno'. + // We must create a memory region for it in system space. + // Currently a symbolic region is used with an artifical symbol. + // FIXME: It is better to have a custom (new) kind of MemRegion for such + // cases. + SValBuilder &SVB = C.getSValBuilder(); + MemRegionManager &RMgr = C.getStateManager().getRegionManager(); + + const MemSpaceRegion *GlobalSystemSpace = + RMgr.getGlobalsRegion(MemRegion::GlobalSystemSpaceRegionKind); + + // Create an artifical symbol for the region. + // It is not possible to associate a statement or expression in this case. + const SymbolConjured *Sym = SVB.conjureSymbol( + nullptr, C.getLocationContext(), + ACtx.getLValueReferenceType(ACtx.IntTy), C.blockCount(), &ErrnoDecl); + + // The symbolic region is untyped, create a typed sub-region in it. + // The ElementRegion is used to make the errno region a typed region. + const MemRegion *ErrnoR = RMgr.getElementRegion( + ACtx.IntTy, SVB.makeZeroArrayIndex(), + RMgr.getSymbolicRegion(Sym, GlobalSystemSpace), C.getASTContext()); + State = State->set<ErrnoRegion>(ErrnoR); + State = + errno_modeling::setErrnoValue(State, C, 0, errno_modeling::Irrelevant); + C.addTransition(State); + } +} + +bool ErrnoModeling::evalCall(const CallEvent &Call, CheckerContext &C) const { + // Return location of "errno" at a call to an "errno address returning" + // function. + if (ErrnoLocationCalls.contains(Call)) { + ProgramStateRef State = C.getState(); + + const MemRegion *ErrnoR = State->get<ErrnoRegion>(); + if (!ErrnoR) + return false; + + State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), + loc::MemRegionVal{ErrnoR}); + C.addTransition(State); + return true; + } + + return false; +} + +void ErrnoModeling::checkLiveSymbols(ProgramStateRef State, + SymbolReaper &SR) const { + // The special errno region should never garbage collected. + if (const auto *ErrnoR = State->get<ErrnoRegion>()) + SR.markLive(ErrnoR); +} + +namespace clang { +namespace ento { +namespace errno_modeling { + +std::optional<SVal> getErrnoValue(ProgramStateRef State) { + const MemRegion *ErrnoR = State->get<ErrnoRegion>(); + if (!ErrnoR) + return {}; + QualType IntTy = State->getAnalysisManager().getASTContext().IntTy; + return State->getSVal(ErrnoR, IntTy); +} + +ProgramStateRef setErrnoValue(ProgramStateRef State, + const LocationContext *LCtx, SVal Value, + ErrnoCheckState EState) { + const MemRegion *ErrnoR = State->get<ErrnoRegion>(); + if (!ErrnoR) + return State; + // First set the errno value, the old state is still available at 'checkBind' + // or 'checkLocation' for errno value. + State = State->bindLoc(loc::MemRegionVal{ErrnoR}, Value, LCtx); + return State->set<ErrnoState>(EState); +} + +ProgramStateRef setErrnoValue(ProgramStateRef State, CheckerContext &C, + uint64_t Value, ErrnoCheckState EState) { + const MemRegion *ErrnoR = State->get<ErrnoRegion>(); + if (!ErrnoR) + return State; + State = State->bindLoc( + loc::MemRegionVal{ErrnoR}, + C.getSValBuilder().makeIntVal(Value, C.getASTContext().IntTy), + C.getLocationContext()); + return State->set<ErrnoState>(EState); +} + +std::optional<Loc> getErrnoLoc(ProgramStateRef State) { + const MemRegion *ErrnoR = State->get<ErrnoRegion>(); + if (!ErrnoR) + return {}; + return loc::MemRegionVal{ErrnoR}; +} + +ErrnoCheckState getErrnoState(ProgramStateRef State) { + return State->get<ErrnoState>(); +} + +ProgramStateRef setErrnoState(ProgramStateRef State, ErrnoCheckState EState) { + return State->set<ErrnoState>(EState); +} + +ProgramStateRef clearErrnoState(ProgramStateRef State) { + return setErrnoState(State, Irrelevant); +} + +bool isErrno(const Decl *D) { + if (const auto *VD = dyn_cast_or_null<VarDecl>(D)) + if (const IdentifierInfo *II = VD->getIdentifier()) + return II->getName() == ErrnoVarName; + if (const auto *FD = dyn_cast_or_null<FunctionDecl>(D)) + if (const IdentifierInfo *II = FD->getIdentifier()) + return llvm::is_contained(ErrnoLocationFuncNames, II->getName()); + return false; +} + +const NoteTag *getErrnoNoteTag(CheckerContext &C, const std::string &Message) { + return C.getNoteTag([Message](PathSensitiveBugReport &BR) -> std::string { + const MemRegion *ErrnoR = BR.getErrorNode()->getState()->get<ErrnoRegion>(); + if (ErrnoR && BR.isInteresting(ErrnoR)) { + BR.markNotInteresting(ErrnoR); + return Message; + } + return ""; + }); +} + +ProgramStateRef setErrnoForStdSuccess(ProgramStateRef State, + CheckerContext &C) { + return setErrnoState(State, MustNotBeChecked); +} + +ProgramStateRef setErrnoForStdFailure(ProgramStateRef State, CheckerContext &C, + NonLoc ErrnoSym) { + SValBuilder &SVB = C.getSValBuilder(); + NonLoc ZeroVal = SVB.makeZeroVal(C.getASTContext().IntTy).castAs<NonLoc>(); + DefinedOrUnknownSVal Cond = + SVB.evalBinOp(State, BO_NE, ErrnoSym, ZeroVal, SVB.getConditionType()) + .castAs<DefinedOrUnknownSVal>(); + State = State->assume(Cond, true); + if (!State) + return nullptr; + return setErrnoValue(State, C.getLocationContext(), ErrnoSym, Irrelevant); +} + +ProgramStateRef setErrnoStdMustBeChecked(ProgramStateRef State, + CheckerContext &C, + const Expr *InvalE) { + const MemRegion *ErrnoR = State->get<ErrnoRegion>(); + if (!ErrnoR) + return State; + State = State->invalidateRegions(ErrnoR, InvalE, C.blockCount(), + C.getLocationContext(), false); + if (!State) + return nullptr; + return setErrnoState(State, MustBeChecked); +} + +} // namespace errno_modeling +} // namespace ento +} // namespace clang + +void ento::registerErrnoModeling(CheckerManager &mgr) { + mgr.registerChecker<ErrnoModeling>(); +} + +bool ento::shouldRegisterErrnoModeling(const CheckerManager &mgr) { + return true; +} diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.h b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.h new file mode 100644 index 000000000000..6b53572fe5e2 --- /dev/null +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.h @@ -0,0 +1,110 @@ +//=== ErrnoModeling.h - Tracking value of 'errno'. -----------------*- 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 inter-checker API for using the system value 'errno'. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_ERRNOMODELING_H +#define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_ERRNOMODELING_H + +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include <optional> + +namespace clang { +namespace ento { +namespace errno_modeling { + +/// Describe how reads and writes of \c errno are handled by the checker. +enum ErrnoCheckState : unsigned { + /// We do not know anything about 'errno'. + /// Read and write is always allowed. + Irrelevant = 0, + + /// Value of 'errno' should be checked to find out if a previous function call + /// has failed. + /// When this state is set \c errno must be read by the program before a next + /// standard function call or other overwrite of \c errno follows, otherwise + /// a bug report is emitted. + MustBeChecked = 1, + + /// Value of 'errno' is not allowed to be read, it can contain an unspecified + /// value. + /// When this state is set \c errno is not allowed to be read by the program + /// until it is overwritten or invalidated. + MustNotBeChecked = 2 +}; + +/// Returns the value of 'errno', if 'errno' was found in the AST. +std::optional<SVal> getErrnoValue(ProgramStateRef State); + +/// Returns the errno check state, \c Errno_Irrelevant if 'errno' was not found +/// (this is not the only case for that value). +ErrnoCheckState getErrnoState(ProgramStateRef State); + +/// Returns the location that points to the \c MemoryRegion where the 'errno' +/// value is stored. Returns \c std::nullopt if 'errno' was not found. Otherwise +/// it always returns a valid memory region in the system global memory space. +std::optional<Loc> getErrnoLoc(ProgramStateRef State); + +/// Set value of 'errno' to any SVal, if possible. +/// The errno check state is set always when the 'errno' value is set. +ProgramStateRef setErrnoValue(ProgramStateRef State, + const LocationContext *LCtx, SVal Value, + ErrnoCheckState EState); + +/// Set value of 'errno' to a concrete (signed) integer, if possible. +/// The errno check state is set always when the 'errno' value is set. +ProgramStateRef setErrnoValue(ProgramStateRef State, CheckerContext &C, + uint64_t Value, ErrnoCheckState EState); + +/// Set the errno check state, do not modify the errno value. +ProgramStateRef setErrnoState(ProgramStateRef State, ErrnoCheckState EState); + +/// Clear state of errno (make it irrelevant). +ProgramStateRef clearErrnoState(ProgramStateRef State); + +/// Determine if a `Decl` node related to 'errno'. +/// This is true if the declaration is the errno variable or a function +/// that returns a pointer to the 'errno' value (usually the 'errno' macro is +/// defined with this function). \p D is not required to be a canonical +/// declaration. +bool isErrno(const Decl *D); + +/// Create a NoteTag that displays the message if the 'errno' memory region is +/// marked as interesting, and resets the interestingness. +const NoteTag *getErrnoNoteTag(CheckerContext &C, const std::string &Message); + +/// Set errno state for the common case when a standard function is successful. +/// Set \c ErrnoCheckState to \c MustNotBeChecked (the \c errno value is not +/// affected). +ProgramStateRef setErrnoForStdSuccess(ProgramStateRef State, CheckerContext &C); + +/// Set errno state for the common case when a standard function fails. +/// Set \c errno value to be not equal to zero and \c ErrnoCheckState to +/// \c Irrelevant . The irrelevant errno state ensures that no related bug +/// report is emitted later and no note tag is needed. +/// \arg \c ErrnoSym Value to be used for \c errno and constrained to be +/// non-zero. +ProgramStateRef setErrnoForStdFailure(ProgramStateRef State, CheckerContext &C, + NonLoc ErrnoSym); + +/// Set errno state for the common case when a standard function indicates +/// failure only by \c errno. Sets \c ErrnoCheckState to \c MustBeChecked, and +/// invalidates the errno region (clear of previous value). +/// \arg \c InvalE Expression that causes invalidation of \c errno. +ProgramStateRef setErrnoStdMustBeChecked(ProgramStateRef State, + CheckerContext &C, const Expr *InvalE); + +} // namespace errno_modeling +} // namespace ento +} // namespace clang + +#endif // LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_ERRNOMODELING_H diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ErrnoTesterChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ErrnoTesterChecker.cpp new file mode 100644 index 000000000000..c46ebee0c94f --- /dev/null +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ErrnoTesterChecker.cpp @@ -0,0 +1,185 @@ +//=== ErrnoTesterChecker.cpp ------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This defines ErrnoTesterChecker, which is used to test functionality of the +// errno_check API. +// +//===----------------------------------------------------------------------===// + +#include "ErrnoModeling.h" +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.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/CheckerContext.h" +#include <optional> + +using namespace clang; +using namespace ento; +using namespace errno_modeling; + +namespace { + +class ErrnoTesterChecker : public Checker<eval::Call> { +public: + bool evalCall(const CallEvent &Call, CheckerContext &C) const; + +private: + /// Evaluate function \code void ErrnoTesterChecker_setErrno(int) \endcode. + /// Set value of \c errno to the argument. + static void evalSetErrno(CheckerContext &C, const CallEvent &Call); + /// Evaluate function \code int ErrnoTesterChecker_getErrno() \endcode. + /// Return the value of \c errno. + static void evalGetErrno(CheckerContext &C, const CallEvent &Call); + /// Evaluate function \code int ErrnoTesterChecker_setErrnoIfError() \endcode. + /// Simulate a standard library function tha returns 0 on success and 1 on + /// failure. On the success case \c errno is not allowed to be used (may be + /// undefined). On the failure case \c errno is set to a fixed value 11 and + /// is not needed to be checked. + static void evalSetErrnoIfError(CheckerContext &C, const CallEvent &Call); + /// Evaluate function \code int ErrnoTesterChecker_setErrnoIfErrorRange() + /// \endcode. Same as \c ErrnoTesterChecker_setErrnoIfError but \c errno is + /// set to a range (to be nonzero) at the failure case. + static void evalSetErrnoIfErrorRange(CheckerContext &C, + const CallEvent &Call); + /// Evaluate function \code int ErrnoTesterChecker_setErrnoCheckState() + /// \endcode. This function simulates the following: + /// - Return 0 and leave \c errno with undefined value. + /// This is the case of a successful standard function call. + /// For example if \c ftell returns not -1. + /// - Return 1 and sets \c errno to a specific error code (1). + /// This is the case of a failed standard function call. + /// The function indicates the failure by a special return value + /// that is returned only at failure. + /// \c errno can be checked but it is not required. + /// For example if \c ftell returns -1. + /// - Return 2 and may set errno to a value (actually it does not set it). + /// This is the case of a standard function call where the failure can only + /// be checked by reading from \c errno. The value of \c errno is changed by + /// the function only at failure, the user should set \c errno to 0 before + /// the call (\c ErrnoChecker does not check for this rule). + /// \c strtol is an example of this case, if it returns \c LONG_MIN (or + /// \c LONG_MAX). This case applies only if \c LONG_MIN or \c LONG_MAX is + /// returned, otherwise the first case in this list applies. + static void evalSetErrnoCheckState(CheckerContext &C, const CallEvent &Call); + + using EvalFn = std::function<void(CheckerContext &, const CallEvent &)>; + const CallDescriptionMap<EvalFn> TestCalls{ + {{{"ErrnoTesterChecker_setErrno"}, 1}, &ErrnoTesterChecker::evalSetErrno}, + {{{"ErrnoTesterChecker_getErrno"}, 0}, &ErrnoTesterChecker::evalGetErrno}, + {{{"ErrnoTesterChecker_setErrnoIfError"}, 0}, + &ErrnoTesterChecker::evalSetErrnoIfError}, + {{{"ErrnoTesterChecker_setErrnoIfErrorRange"}, 0}, + &ErrnoTesterChecker::evalSetErrnoIfErrorRange}, + {{{"ErrnoTesterChecker_setErrnoCheckState"}, 0}, + &ErrnoTesterChecker::evalSetErrnoCheckState}}; +}; + +} // namespace + +void ErrnoTesterChecker::evalSetErrno(CheckerContext &C, + const CallEvent &Call) { + C.addTransition(setErrnoValue(C.getState(), C.getLocationContext(), + Call.getArgSVal(0), Irrelevant)); +} + +void ErrnoTesterChecker::evalGetErrno(CheckerContext &C, + const CallEvent &Call) { + ProgramStateRef State = C.getState(); + + std::optional<SVal> ErrnoVal = getErrnoValue(State); + assert(ErrnoVal && "Errno value should be available."); + State = + State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), *ErrnoVal); + + C.addTransition(State); +} + +void ErrnoTesterChecker::evalSetErrnoIfError(CheckerContext &C, + const CallEvent &Call) { + ProgramStateRef State = C.getState(); + SValBuilder &SVB = C.getSValBuilder(); + + ProgramStateRef StateSuccess = State->BindExpr( + Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(0, true)); + StateSuccess = setErrnoState(StateSuccess, MustNotBeChecked); + + ProgramStateRef StateFailure = State->BindExpr( + Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(1, true)); + StateFailure = setErrnoValue(StateFailure, C, 11, Irrelevant); + + C.addTransition(StateSuccess); + C.addTransition(StateFailure); +} + +void ErrnoTesterChecker::evalSetErrnoIfErrorRange(CheckerContext &C, + const CallEvent &Call) { + ProgramStateRef State = C.getState(); + SValBuilder &SVB = C.getSValBuilder(); + + ProgramStateRef StateSuccess = State->BindExpr( + Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(0, true)); + StateSuccess = setErrnoState(StateSuccess, MustNotBeChecked); + + ProgramStateRef StateFailure = State->BindExpr( + Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(1, true)); + DefinedOrUnknownSVal ErrnoVal = SVB.conjureSymbolVal( + nullptr, Call.getOriginExpr(), C.getLocationContext(), C.blockCount()); + StateFailure = StateFailure->assume(ErrnoVal, true); + assert(StateFailure && "Failed to assume on an initial value."); + StateFailure = + setErrnoValue(StateFailure, C.getLocationContext(), ErrnoVal, Irrelevant); + + C.addTransition(StateSuccess); + C.addTransition(StateFailure); +} + +void ErrnoTesterChecker::evalSetErrnoCheckState(CheckerContext &C, + const CallEvent &Call) { + ProgramStateRef State = C.getState(); + SValBuilder &SVB = C.getSValBuilder(); + + ProgramStateRef StateSuccess = State->BindExpr( + Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(0, true)); + StateSuccess = setErrnoState(StateSuccess, MustNotBeChecked); + + ProgramStateRef StateFailure1 = State->BindExpr( + Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(1, true)); + StateFailure1 = setErrnoValue(StateFailure1, C, 1, Irrelevant); + + ProgramStateRef StateFailure2 = State->BindExpr( + Call.getOriginExpr(), C.getLocationContext(), SVB.makeIntVal(2, true)); + StateFailure2 = setErrnoValue(StateFailure2, C, 2, MustBeChecked); + + C.addTransition(StateSuccess, + getErrnoNoteTag(C, "Assuming that this function succeeds but " + "sets 'errno' to an unspecified value.")); + C.addTransition(StateFailure1); + C.addTransition( + StateFailure2, + getErrnoNoteTag(C, "Assuming that this function returns 2. 'errno' " + "should be checked to test for failure.")); +} + +bool ErrnoTesterChecker::evalCall(const CallEvent &Call, + CheckerContext &C) const { + const EvalFn *Fn = TestCalls.lookup(Call); + if (Fn) { + (*Fn)(C, Call); + return C.isDifferent(); + } + return false; +} + +void ento::registerErrnoTesterChecker(CheckerManager &Mgr) { + Mgr.registerChecker<ErrnoTesterChecker>(); +} + +bool ento::shouldRegisterErrnoTesterChecker(const CheckerManager &Mgr) { + return true; +} diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp index 2ce1bef6d228..3096999e9fd1 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp @@ -6,10 +6,10 @@ // //===----------------------------------------------------------------------===// -#include "Taint.h" #include "clang/Analysis/IssueHash.h" #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/StaticAnalyzer/Checkers/SValExplainer.h" +#include "clang/StaticAnalyzer/Checkers/Taint.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" @@ -17,6 +17,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/ScopedPrinter.h" +#include <optional> using namespace clang; using namespace ento; @@ -24,7 +25,7 @@ using namespace ento; namespace { class ExprInspectionChecker : public Checker<eval::Call, check::DeadSymbols, check::EndAnalysis> { - mutable std::unique_ptr<BugType> BT; + const BugType BT{this, "Checking analyzer assumptions", "debug"}; // These stats are per-analysis, not per-branch, hence they shouldn't // stay inside the program state. @@ -40,6 +41,8 @@ class ExprInspectionChecker void analyzerNumTimesReached(const CallExpr *CE, CheckerContext &C) const; void analyzerCrash(const CallExpr *CE, CheckerContext &C) const; void analyzerWarnOnDeadSymbol(const CallExpr *CE, CheckerContext &C) const; + void analyzerValue(const CallExpr *CE, CheckerContext &C) const; + void analyzerDumpSValType(const CallExpr *CE, CheckerContext &C) const; void analyzerDump(const CallExpr *CE, CheckerContext &C) const; void analyzerExplain(const CallExpr *CE, CheckerContext &C) const; void analyzerPrintState(const CallExpr *CE, CheckerContext &C) const; @@ -56,9 +59,10 @@ class ExprInspectionChecker // Optional parameter `ExprVal` for expression value to be marked interesting. ExplodedNode *reportBug(llvm::StringRef Msg, CheckerContext &C, - Optional<SVal> ExprVal = None) const; + std::optional<SVal> ExprVal = std::nullopt) const; ExplodedNode *reportBug(llvm::StringRef Msg, BugReporter &BR, ExplodedNode *N, - Optional<SVal> ExprVal = None) const; + std::optional<SVal> ExprVal = std::nullopt) const; + template <typename T> void printAndReport(CheckerContext &C, T What) const; const Expr *getArgExpr(const CallExpr *CE, CheckerContext &C) const; const MemRegion *getArgRegion(const CallExpr *CE, CheckerContext &C) const; @@ -98,6 +102,9 @@ bool ExprInspectionChecker::evalCall(const CallEvent &Call, &ExprInspectionChecker::analyzerDumpExtent) .Case("clang_analyzer_dumpElementCount", &ExprInspectionChecker::analyzerDumpElementCount) + .Case("clang_analyzer_value", &ExprInspectionChecker::analyzerValue) + .StartsWith("clang_analyzer_dumpSvalType", + &ExprInspectionChecker::analyzerDumpSValType) .StartsWith("clang_analyzer_dump", &ExprInspectionChecker::analyzerDump) .Case("clang_analyzer_getExtent", @@ -109,7 +116,8 @@ bool ExprInspectionChecker::evalCall(const CallEvent &Call, .Case("clang_analyzer_hashDump", &ExprInspectionChecker::analyzerHashDump) .Case("clang_analyzer_denote", &ExprInspectionChecker::analyzerDenote) - .Case("clang_analyzer_express", + .Case("clang_analyzer_express", // This also marks the argument as + // interesting. &ExprInspectionChecker::analyzerExpress) .StartsWith("clang_analyzer_isTainted", &ExprInspectionChecker::analyzerIsTainted) @@ -154,24 +162,21 @@ static const char *getArgumentValueString(const CallExpr *CE, } } -ExplodedNode *ExprInspectionChecker::reportBug(llvm::StringRef Msg, - CheckerContext &C, - Optional<SVal> ExprVal) const { +ExplodedNode * +ExprInspectionChecker::reportBug(llvm::StringRef Msg, CheckerContext &C, + std::optional<SVal> ExprVal) const { ExplodedNode *N = C.generateNonFatalErrorNode(); reportBug(Msg, C.getBugReporter(), N, ExprVal); return N; } -ExplodedNode *ExprInspectionChecker::reportBug(llvm::StringRef Msg, - BugReporter &BR, ExplodedNode *N, - Optional<SVal> ExprVal) const { +ExplodedNode * +ExprInspectionChecker::reportBug(llvm::StringRef Msg, BugReporter &BR, + ExplodedNode *N, + std::optional<SVal> ExprVal) const { if (!N) return nullptr; - - if (!BT) - BT.reset(new BugType(this, "Checking analyzer assumptions", "debug")); - - auto R = std::make_unique<PathSensitiveBugReport>(*BT, Msg, N); + auto R = std::make_unique<PathSensitiveBugReport>(BT, Msg, N); if (ExprVal) { R->markInteresting(*ExprVal); } @@ -255,6 +260,55 @@ void ExprInspectionChecker::analyzerExplain(const CallExpr *CE, reportBug(Ex.Visit(V), C); } +static void printHelper(llvm::raw_svector_ostream &Out, CheckerContext &C, + const llvm::APSInt &I) { + Out << I.getBitWidth() << (I.isUnsigned() ? "u:" : "s:"); + Out << I; +} + +static void printHelper(llvm::raw_svector_ostream &Out, CheckerContext &C, + SymbolRef Sym) { + C.getConstraintManager().printValue(Out, C.getState(), Sym); +} + +static void printHelper(llvm::raw_svector_ostream &Out, CheckerContext &C, + SVal V) { + Out << V; +} + +template <typename T> +void ExprInspectionChecker::printAndReport(CheckerContext &C, T What) const { + llvm::SmallString<64> Str; + llvm::raw_svector_ostream OS(Str); + printHelper(OS, C, What); + reportBug(OS.str(), C); +} + +void ExprInspectionChecker::analyzerValue(const CallExpr *CE, + CheckerContext &C) const { + const Expr *Arg = getArgExpr(CE, C); + if (!Arg) + return; + + SVal V = C.getSVal(Arg); + if (const SymbolRef Sym = V.getAsSymbol()) + printAndReport(C, Sym); + else if (const llvm::APSInt *I = V.getAsInteger()) + printAndReport(C, *I); + else + reportBug("n/a", C); +} + +void ExprInspectionChecker::analyzerDumpSValType(const CallExpr *CE, + CheckerContext &C) const { + const Expr *Arg = getArgExpr(CE, C); + if (!Arg) + return; + + QualType Ty = C.getSVal(Arg).getType(C.getASTContext()); + reportBug(Ty.getAsString(), C); +} + void ExprInspectionChecker::analyzerDump(const CallExpr *CE, CheckerContext &C) const { const Expr *Arg = getArgExpr(CE, C); @@ -262,21 +316,17 @@ void ExprInspectionChecker::analyzerDump(const CallExpr *CE, return; SVal V = C.getSVal(Arg); - - llvm::SmallString<32> Str; - llvm::raw_svector_ostream OS(Str); - V.dumpToStream(OS); - reportBug(OS.str(), C); + printAndReport(C, V); } 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); @@ -284,17 +334,13 @@ 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()); - - SmallString<64> Msg; - llvm::raw_svector_ostream Out(Msg); - Out << Size; - reportBug(Out.str(), C); + ProgramStateRef State = C.getState(); + SVal Size = getDynamicExtentWithOffset(State, C.getSVal(Arg)); + printAndReport(C, Size); } void ExprInspectionChecker::analyzerDumpElementCount(const CallExpr *CE, @@ -307,19 +353,14 @@ void ExprInspectionChecker::analyzerDumpElementCount(const CallExpr *CE, if (const auto *TVR = MR->getAs<TypedValueRegion>()) { ElementTy = TVR->getValueType(); } else { - ElementTy = - MR->castAs<SymbolicRegion>()->getSymbol()->getType()->getPointeeType(); + ElementTy = MR->castAs<SymbolicRegion>()->getPointeeStaticType(); } assert(!ElementTy->isPointerType()); - DefinedOrUnknownSVal ElementCount = - getDynamicElementCount(C.getState(), MR, C.getSValBuilder(), ElementTy); - - SmallString<128> Msg; - llvm::raw_svector_ostream Out(Msg); - Out << ElementCount; - reportBug(Out.str(), C); + DefinedOrUnknownSVal ElementCount = getDynamicElementCountWithOffset( + C.getState(), C.getSVal(getArgExpr(CE, C)), ElementTy); + printAndReport(C, ElementCount); } void ExprInspectionChecker::analyzerPrintState(const CallExpr *CE, @@ -348,8 +389,7 @@ void ExprInspectionChecker::checkDeadSymbols(SymbolReaper &SymReaper, ProgramStateRef State = C.getState(); const MarkedSymbolsTy &Syms = State->get<MarkedSymbols>(); ExplodedNode *N = C.getPredecessor(); - for (auto I = Syms.begin(), E = Syms.end(); I != E; ++I) { - SymbolRef Sym = *I; + for (SymbolRef Sym : Syms) { if (!SymReaper.isDead(Sym)) continue; @@ -423,50 +463,60 @@ void ExprInspectionChecker::analyzerDenote(const CallExpr *CE, namespace { class SymbolExpressor - : public SymExprVisitor<SymbolExpressor, Optional<std::string>> { + : public SymExprVisitor<SymbolExpressor, std::optional<std::string>> { ProgramStateRef State; public: SymbolExpressor(ProgramStateRef State) : State(State) {} - Optional<std::string> lookup(const SymExpr *S) { + std::optional<std::string> lookup(const SymExpr *S) { if (const StringLiteral *const *SLPtr = State->get<DenotedSymbols>(S)) { const StringLiteral *SL = *SLPtr; return std::string(SL->getBytes()); } - return None; + return std::nullopt; } - Optional<std::string> VisitSymExpr(const SymExpr *S) { return lookup(S); } + std::optional<std::string> VisitSymExpr(const SymExpr *S) { + return lookup(S); + } - Optional<std::string> VisitSymIntExpr(const SymIntExpr *S) { - if (Optional<std::string> Str = lookup(S)) + std::optional<std::string> VisitSymIntExpr(const SymIntExpr *S) { + if (std::optional<std::string> Str = lookup(S)) return Str; - if (Optional<std::string> Str = Visit(S->getLHS())) + if (std::optional<std::string> Str = Visit(S->getLHS())) return (*Str + " " + BinaryOperator::getOpcodeStr(S->getOpcode()) + " " + std::to_string(S->getRHS().getLimitedValue()) + (S->getRHS().isUnsigned() ? "U" : "")) .str(); - return None; + return std::nullopt; } - Optional<std::string> VisitSymSymExpr(const SymSymExpr *S) { - if (Optional<std::string> Str = lookup(S)) + std::optional<std::string> VisitSymSymExpr(const SymSymExpr *S) { + if (std::optional<std::string> Str = lookup(S)) return Str; - if (Optional<std::string> Str1 = Visit(S->getLHS())) - if (Optional<std::string> Str2 = Visit(S->getRHS())) + if (std::optional<std::string> Str1 = Visit(S->getLHS())) + if (std::optional<std::string> Str2 = Visit(S->getRHS())) return (*Str1 + " " + BinaryOperator::getOpcodeStr(S->getOpcode()) + " " + *Str2) .str(); - return None; + return std::nullopt; } - Optional<std::string> VisitSymbolCast(const SymbolCast *S) { - if (Optional<std::string> Str = lookup(S)) + std::optional<std::string> VisitUnarySymExpr(const UnarySymExpr *S) { + if (std::optional<std::string> Str = lookup(S)) return Str; - if (Optional<std::string> Str = Visit(S->getOperand())) + if (std::optional<std::string> Str = Visit(S->getOperand())) + return (UnaryOperator::getOpcodeStr(S->getOpcode()) + *Str).str(); + return std::nullopt; + } + + std::optional<std::string> VisitSymbolCast(const SymbolCast *S) { + if (std::optional<std::string> Str = lookup(S)) + return Str; + if (std::optional<std::string> Str = Visit(S->getOperand())) return (Twine("(") + S->getType().getAsString() + ")" + *Str).str(); - return None; + return std::nullopt; } }; } // namespace @@ -480,14 +530,14 @@ void ExprInspectionChecker::analyzerExpress(const CallExpr *CE, SVal ArgVal = C.getSVal(CE->getArg(0)); SymbolRef Sym = ArgVal.getAsSymbol(); if (!Sym) { - reportBug("Not a symbol", C); + reportBug("Not a symbol", C, ArgVal); return; } SymbolExpressor V(C.getState()); auto Str = V.Visit(Sym); if (!Str) { - reportBug("Unable to express", C); + reportBug("Unable to express", C, ArgVal); return; } diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp index 6275e49e51ae..7aefcdc6d358 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; + const BugType BT{this, "Use fixed address"}; public: void checkPreStmt(const BinaryOperator *B, CheckerContext &C) const; @@ -49,14 +49,11 @@ void FixedAddressChecker::checkPreStmt(const BinaryOperator *B, return; if (ExplodedNode *N = C.generateNonFatalErrorNode()) { - 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); + // FIXME: improve grammar in the following strings: + 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 e3f4be0726c8..079bc61a87d9 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/FuchsiaHandleChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/FuchsiaHandleChecker.cpp @@ -101,6 +101,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" #include "llvm/ADT/StringExtras.h" +#include <optional> using namespace clang; using namespace ento; @@ -254,9 +255,6 @@ static const ExplodedNode *getAcquireSite(const ExplodedNode *N, SymbolRef Sym, namespace { class FuchsiaHandleSymbolVisitor final : public SymbolVisitor { public: - FuchsiaHandleSymbolVisitor(ProgramStateRef State) : State(std::move(State)) {} - ProgramStateRef getState() const { return State; } - bool VisitSymbol(SymbolRef S) override { if (const auto *HandleType = S->getType()->getAs<TypedefType>()) if (HandleType->getDecl()->getName() == HandleTypeName) @@ -268,7 +266,6 @@ public: private: SmallVector<SymbolRef, 1024> Symbols; - ProgramStateRef State; }; } // end anonymous namespace @@ -284,7 +281,7 @@ getFuchsiaHandleSymbols(QualType QT, SVal Arg, ProgramStateRef State) { if (QT->isStructureType()) { // If we see a structure, see if there is any handle referenced by the // structure. - FuchsiaHandleSymbolVisitor Visitor(State); + FuchsiaHandleSymbolVisitor Visitor; State->scanReachableSymbols(Arg, Visitor); return Visitor.GetSymbols(); } @@ -304,7 +301,7 @@ getFuchsiaHandleSymbols(QualType QT, SVal Arg, ProgramStateRef State) { } } else { assert(PtrToHandleLevel == 1); - if (Optional<Loc> ArgLoc = Arg.getAs<Loc>()) { + if (std::optional<Loc> ArgLoc = Arg.getAs<Loc>()) { SymbolRef Sym = State->getSVal(*ArgLoc).getAsSymbol(); if (Sym) { return {Sym}; @@ -384,12 +381,12 @@ 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() << "' returns an open handle"; - return OS.str(); + return SBuf; } else return ""; }); @@ -400,12 +397,12 @@ 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() << "' returns an unowned handle"; - return OS.str(); + return SBuf; } else return ""; }); @@ -434,12 +431,12 @@ 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 << llvm::getOrdinalSuffix(ParamDiagIdx) << " parameter"; - return OS.str(); + return SBuf; } else return ""; }); @@ -448,12 +445,12 @@ 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 << llvm::getOrdinalSuffix(ParamDiagIdx) << " parameter"; - return OS.str(); + return SBuf; } else return ""; }); @@ -462,12 +459,12 @@ 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 << llvm::getOrdinalSuffix(ParamDiagIdx) << " parameter"; - return OS.str(); + return SBuf; } else return ""; }); @@ -656,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, @@ -689,11 +687,10 @@ void FuchsiaHandleChecker::printState(raw_ostream &Out, ProgramStateRef State, if (!StateMap.isEmpty()) { Out << Sep << "FuchsiaHandleChecker :" << NL; - for (HStateMapTy::iterator I = StateMap.begin(), E = StateMap.end(); I != E; - ++I) { - I.getKey()->dumpToStream(Out); + for (const auto &[Sym, HandleState] : StateMap) { + Sym->dumpToStream(Out); Out << " : "; - I.getData().dump(Out); + HandleState.dump(Out); Out << NL; } } 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 8d9afbe88aa8..6c32a8dec844 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/GTestChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/GTestChecker.cpp @@ -20,6 +20,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "llvm/Support/raw_ostream.h" +#include <optional> using namespace clang; using namespace ento; @@ -91,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; @@ -119,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_' @@ -135,7 +134,7 @@ void GTestChecker::modelAssertionResultBoolConstructor( SVal BooleanArgVal = Call->getArgSVal(0); if (IsRef) { // The argument is a reference, so load from it to get the boolean value. - if (!BooleanArgVal.getAs<Loc>()) + if (!isa<Loc>(BooleanArgVal)) return; BooleanArgVal = C.getState()->getSVal(BooleanArgVal.castAs<Loc>()); } @@ -258,9 +257,9 @@ SVal GTestChecker::getAssertionResultSuccessFieldValue( if (!SuccessField) return UnknownVal(); - Optional<Loc> FieldLoc = + std::optional<Loc> FieldLoc = State->getLValue(SuccessField, Instance).getAs<Loc>(); - if (!FieldLoc.hasValue()) + if (!FieldLoc) return UnknownVal(); return State->getSVal(*FieldLoc); @@ -270,20 +269,17 @@ SVal GTestChecker::getAssertionResultSuccessFieldValue( ProgramStateRef GTestChecker::assumeValuesEqual(SVal Val1, SVal Val2, ProgramStateRef State, CheckerContext &C) { - if (!Val1.getAs<DefinedOrUnknownSVal>() || - !Val2.getAs<DefinedOrUnknownSVal>()) + auto DVal1 = Val1.getAs<DefinedOrUnknownSVal>(); + auto DVal2 = Val2.getAs<DefinedOrUnknownSVal>(); + if (!DVal1 || !DVal2) return State; auto ValuesEqual = - C.getSValBuilder().evalEQ(State, Val1.castAs<DefinedOrUnknownSVal>(), - Val2.castAs<DefinedOrUnknownSVal>()); - - if (!ValuesEqual.getAs<DefinedSVal>()) + C.getSValBuilder().evalEQ(State, *DVal1, *DVal2).getAs<DefinedSVal>(); + if (!ValuesEqual) return State; - State = C.getConstraintManager().assume( - State, ValuesEqual.castAs<DefinedSVal>(), true); - + State = C.getConstraintManager().assume(State, *ValuesEqual, true); return State; } diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp index 42c777eb2c52..4ceaf933d0bf 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp @@ -14,838 +14,1065 @@ // //===----------------------------------------------------------------------===// -#include "Taint.h" #include "Yaml.h" #include "clang/AST/Attr.h" #include "clang/Basic/Builtins.h" #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Checkers/Taint.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/ProgramStateTrait.h" +#include "llvm/ADT/StringExtras.h" #include "llvm/Support/YAMLTraits.h" -#include <algorithm> #include <limits> #include <memory> -#include <unordered_map> +#include <optional> #include <utility> +#include <vector> + +#define DEBUG_TYPE "taint-checker" using namespace clang; using namespace ento; using namespace taint; +using llvm::ImmutableSet; + namespace { -class GenericTaintChecker : public Checker<check::PreCall, check::PostCall> { -public: - static void *getTag() { - static int Tag; - return &Tag; + +class GenericTaintChecker; + +/// Check for CWE-134: Uncontrolled Format String. +constexpr llvm::StringLiteral MsgUncontrolledFormatString = + "Untrusted data is used as a format string " + "(CWE-134: Uncontrolled Format String)"; + +/// Check for: +/// CERT/STR02-C. "Sanitize data passed to complex subsystems" +/// CWE-78, "Failure to Sanitize Data into an OS Command" +constexpr llvm::StringLiteral MsgSanitizeSystemArgs = + "Untrusted data is passed to a system call " + "(CERT/STR02-C. Sanitize data passed to complex subsystems)"; + +/// Check if tainted data is used as a buffer size in strn.. functions, +/// and allocators. +constexpr llvm::StringLiteral MsgTaintedBufferSize = + "Untrusted data is used to specify the buffer size " + "(CERT/STR31-C. Guarantee that storage for strings has sufficient space " + "for character data and the null terminator)"; + +/// Check if tainted data is used as a custom sink's parameter. +constexpr llvm::StringLiteral MsgCustomSink = + "Untrusted data is passed to a user-defined sink"; + +using ArgIdxTy = int; +using ArgVecTy = llvm::SmallVector<ArgIdxTy, 2>; + +/// Denotes the return value. +constexpr ArgIdxTy ReturnValueIndex{-1}; + +static ArgIdxTy fromArgumentCount(unsigned Count) { + assert(Count <= + static_cast<std::size_t>(std::numeric_limits<ArgIdxTy>::max()) && + "ArgIdxTy is not large enough to represent the number of arguments."); + return Count; +} + +/// Check if the region the expression evaluates to is the standard input, +/// and thus, is tainted. +/// FIXME: Move this to Taint.cpp. +bool isStdin(SVal Val, const ASTContext &ACtx) { + // FIXME: What if Val is NonParamVarRegion? + + // The region should be symbolic, we do not know it's value. + const auto *SymReg = dyn_cast_or_null<SymbolicRegion>(Val.getAsRegion()); + if (!SymReg) + return false; + + // Get it's symbol and find the declaration region it's pointing to. + const auto *DeclReg = + dyn_cast_or_null<DeclRegion>(SymReg->getSymbol()->getOriginRegion()); + if (!DeclReg) + return false; + + // This region corresponds to a declaration, find out if it's a global/extern + // variable named stdin with the proper type. + if (const auto *D = dyn_cast_or_null<VarDecl>(DeclReg->getDecl())) { + D = D->getCanonicalDecl(); + if (D->getName() == "stdin" && D->hasExternalStorage() && D->isExternC()) { + const QualType FILETy = ACtx.getFILEType().getCanonicalType(); + const QualType Ty = D->getType().getCanonicalType(); + + if (Ty->isPointerType()) + return Ty->getPointeeType() == FILETy; + } } + return false; +} - void checkPreCall(const CallEvent &Call, CheckerContext &C) const; - void checkPostCall(const CallEvent &Call, CheckerContext &C) const; +SVal getPointeeOf(ProgramStateRef State, Loc LValue) { + const QualType ArgTy = LValue.getType(State->getStateManager().getContext()); + if (!ArgTy->isPointerType() || !ArgTy->getPointeeType()->isVoidType()) + return State->getSVal(LValue); - void printState(raw_ostream &Out, ProgramStateRef State, const char *NL, - const char *Sep) const override; + // Do not dereference void pointers. Treat them as byte pointers instead. + // FIXME: we might want to consider more than just the first byte. + return State->getSVal(LValue, State->getStateManager().getContext().CharTy); +} - using ArgVector = SmallVector<unsigned, 2>; - using SignedArgVector = SmallVector<int, 2>; +/// Given a pointer/reference argument, return the value it refers to. +std::optional<SVal> getPointeeOf(ProgramStateRef State, SVal Arg) { + if (auto LValue = Arg.getAs<Loc>()) + return getPointeeOf(State, *LValue); + return std::nullopt; +} - enum class VariadicType { None, Src, Dst }; +/// Given a pointer, return the SVal of its pointee or if it is tainted, +/// otherwise return the pointer's SVal if tainted. +/// Also considers stdin as a taint source. +std::optional<SVal> getTaintedPointeeOrPointer(ProgramStateRef State, + SVal Arg) { + if (auto Pointee = getPointeeOf(State, Arg)) + if (isTainted(State, *Pointee)) // FIXME: isTainted(...) ? Pointee : None; + return Pointee; + + if (isTainted(State, Arg)) + return Arg; + return std::nullopt; +} - /// Used to parse the configuration file. - struct TaintConfiguration { - using NameScopeArgs = std::tuple<std::string, std::string, ArgVector>; - - struct Propagation { - std::string Name; - std::string Scope; - ArgVector SrcArgs; - SignedArgVector DstArgs; - VariadicType VarType; - unsigned VarIndex; - }; - - std::vector<Propagation> Propagations; - std::vector<NameScopeArgs> Filters; - std::vector<NameScopeArgs> Sinks; - - TaintConfiguration() = default; - TaintConfiguration(const TaintConfiguration &) = default; - TaintConfiguration(TaintConfiguration &&) = default; - TaintConfiguration &operator=(const TaintConfiguration &) = default; - TaintConfiguration &operator=(TaintConfiguration &&) = default; - }; +bool isTaintedOrPointsToTainted(ProgramStateRef State, SVal ExprSVal) { + return getTaintedPointeeOrPointer(State, ExprSVal).has_value(); +} - /// Convert SignedArgVector to ArgVector. - ArgVector convertToArgVector(CheckerManager &Mgr, const std::string &Option, - const SignedArgVector &Args); +/// Helps in printing taint diagnostics. +/// Marks the incoming parameters of a function interesting (to be printed) +/// when the return value, or the outgoing parameters are tainted. +const NoteTag *taintOriginTrackerTag(CheckerContext &C, + std::vector<SymbolRef> TaintedSymbols, + std::vector<ArgIdxTy> TaintedArgs, + const LocationContext *CallLocation) { + return C.getNoteTag([TaintedSymbols = std::move(TaintedSymbols), + TaintedArgs = std::move(TaintedArgs), CallLocation]( + PathSensitiveBugReport &BR) -> std::string { + SmallString<256> Msg; + // We give diagnostics only for taint related reports + if (!BR.isInteresting(CallLocation) || + BR.getBugType().getCategory() != categories::TaintedData) { + return ""; + } + if (TaintedSymbols.empty()) + return "Taint originated here"; - /// Parse the config. - void parseConfiguration(CheckerManager &Mgr, const std::string &Option, - TaintConfiguration &&Config); + for (auto Sym : TaintedSymbols) { + BR.markInteresting(Sym); + } + LLVM_DEBUG(for (auto Arg + : TaintedArgs) { + llvm::dbgs() << "Taint Propagated from argument " << Arg + 1 << "\n"; + }); + return ""; + }); +} + +/// Helps in printing taint diagnostics. +/// Marks the function interesting (to be printed) +/// when the return value, or the outgoing parameters are tainted. +const NoteTag *taintPropagationExplainerTag( + CheckerContext &C, std::vector<SymbolRef> TaintedSymbols, + std::vector<ArgIdxTy> TaintedArgs, const LocationContext *CallLocation) { + assert(TaintedSymbols.size() == TaintedArgs.size()); + return C.getNoteTag([TaintedSymbols = std::move(TaintedSymbols), + TaintedArgs = std::move(TaintedArgs), CallLocation]( + PathSensitiveBugReport &BR) -> std::string { + SmallString<256> Msg; + llvm::raw_svector_ostream Out(Msg); + // We give diagnostics only for taint related reports + if (TaintedSymbols.empty() || + BR.getBugType().getCategory() != categories::TaintedData) { + return ""; + } + int nofTaintedArgs = 0; + for (auto [Idx, Sym] : llvm::enumerate(TaintedSymbols)) { + if (BR.isInteresting(Sym)) { + BR.markInteresting(CallLocation); + if (TaintedArgs[Idx] != ReturnValueIndex) { + LLVM_DEBUG(llvm::dbgs() << "Taint Propagated to argument " + << TaintedArgs[Idx] + 1 << "\n"); + if (nofTaintedArgs == 0) + Out << "Taint propagated to the "; + else + Out << ", "; + Out << TaintedArgs[Idx] + 1 + << llvm::getOrdinalSuffix(TaintedArgs[Idx] + 1) << " argument"; + nofTaintedArgs++; + } else { + LLVM_DEBUG(llvm::dbgs() << "Taint Propagated to return value.\n"); + Out << "Taint propagated to the return value"; + } + } + } + return std::string(Out.str()); + }); +} + +/// ArgSet is used to describe arguments relevant for taint detection or +/// taint application. A discrete set of argument indexes and a variadic +/// argument list signified by a starting index are supported. +class ArgSet { +public: + ArgSet() = default; + ArgSet(ArgVecTy &&DiscreteArgs, + std::optional<ArgIdxTy> VariadicIndex = std::nullopt) + : DiscreteArgs(std::move(DiscreteArgs)), + VariadicIndex(std::move(VariadicIndex)) {} + + bool contains(ArgIdxTy ArgIdx) const { + if (llvm::is_contained(DiscreteArgs, ArgIdx)) + return true; + + return VariadicIndex && ArgIdx >= *VariadicIndex; + } - static const unsigned InvalidArgIndex{std::numeric_limits<unsigned>::max()}; - /// Denotes the return vale. - static const unsigned ReturnValueIndex{std::numeric_limits<unsigned>::max() - - 1}; + bool isEmpty() const { return DiscreteArgs.empty() && !VariadicIndex; } private: - mutable std::unique_ptr<BugType> BT; - void initBugType() const { - if (!BT) - BT = std::make_unique<BugType>(this, "Use of Untrusted Data", - "Untrusted Data"); + ArgVecTy DiscreteArgs; + std::optional<ArgIdxTy> VariadicIndex; +}; + +/// A struct used to specify taint propagation rules for a function. +/// +/// If any of the possible taint source arguments is tainted, all of the +/// destination arguments should also be tainted. If ReturnValueIndex is added +/// to the dst list, the return value will be tainted. +class GenericTaintRule { + /// Arguments which are taints sinks and should be checked, and a report + /// should be emitted if taint reaches these. + ArgSet SinkArgs; + /// Arguments which should be sanitized on function return. + ArgSet FilterArgs; + /// Arguments which can participate in taint propagation. If any of the + /// arguments in PropSrcArgs is tainted, all arguments in PropDstArgs should + /// be tainted. + ArgSet PropSrcArgs; + ArgSet PropDstArgs; + + /// A message that explains why the call is sensitive to taint. + std::optional<StringRef> SinkMsg; + + GenericTaintRule() = default; + + GenericTaintRule(ArgSet &&Sink, ArgSet &&Filter, ArgSet &&Src, ArgSet &&Dst, + std::optional<StringRef> SinkMsg = std::nullopt) + : SinkArgs(std::move(Sink)), FilterArgs(std::move(Filter)), + PropSrcArgs(std::move(Src)), PropDstArgs(std::move(Dst)), + SinkMsg(SinkMsg) {} + +public: + /// Make a rule that reports a warning if taint reaches any of \p FilterArgs + /// arguments. + static GenericTaintRule Sink(ArgSet &&SinkArgs, + std::optional<StringRef> Msg = std::nullopt) { + return {std::move(SinkArgs), {}, {}, {}, Msg}; } - struct FunctionData { - FunctionData() = delete; - FunctionData(const FunctionDecl *FDecl, StringRef Name, - std::string FullName) - : FDecl(FDecl), Name(Name), FullName(std::move(FullName)) {} - FunctionData(const FunctionData &) = default; - FunctionData(FunctionData &&) = default; - FunctionData &operator=(const FunctionData &) = delete; - FunctionData &operator=(FunctionData &&) = delete; - - static Optional<FunctionData> create(const CallEvent &Call, - const CheckerContext &C) { - if (!Call.getDecl()) - return None; - - const FunctionDecl *FDecl = Call.getDecl()->getAsFunction(); - if (!FDecl || (FDecl->getKind() != Decl::Function && - FDecl->getKind() != Decl::CXXMethod)) - return None; - - StringRef Name = C.getCalleeName(FDecl); - std::string FullName = FDecl->getQualifiedNameAsString(); - if (Name.empty() || FullName.empty()) - return None; - - return FunctionData{FDecl, Name, std::move(FullName)}; - } + /// Make a rule that sanitizes all FilterArgs arguments. + static GenericTaintRule Filter(ArgSet &&FilterArgs) { + return {{}, std::move(FilterArgs), {}, {}}; + } - bool isInScope(StringRef Scope) const { - return StringRef(FullName).startswith(Scope); - } + /// Make a rule that unconditionally taints all Args. + /// If Func is provided, it must also return true for taint to propagate. + static GenericTaintRule Source(ArgSet &&SourceArgs) { + return {{}, {}, {}, std::move(SourceArgs)}; + } - const FunctionDecl *const FDecl; - const StringRef Name; - const std::string FullName; - }; + /// Make a rule that taints all PropDstArgs if any of PropSrcArgs is tainted. + static GenericTaintRule Prop(ArgSet &&SrcArgs, ArgSet &&DstArgs) { + return {{}, {}, std::move(SrcArgs), std::move(DstArgs)}; + } - /// Catch taint related bugs. Check if tainted data is passed to a - /// system call etc. Returns true on matching. - bool checkPre(const CallEvent &Call, const FunctionData &FData, - CheckerContext &C) const; + /// Make a rule that taints all PropDstArgs if any of PropSrcArgs is tainted. + static GenericTaintRule + SinkProp(ArgSet &&SinkArgs, ArgSet &&SrcArgs, ArgSet &&DstArgs, + std::optional<StringRef> Msg = std::nullopt) { + return { + std::move(SinkArgs), {}, std::move(SrcArgs), std::move(DstArgs), Msg}; + } - /// Add taint sources on a pre-visit. Returns true on matching. - bool addSourcesPre(const CallEvent &Call, const FunctionData &FData, - CheckerContext &C) const; + /// Process a function which could either be a taint source, a taint sink, a + /// taint filter or a taint propagator. + void process(const GenericTaintChecker &Checker, const CallEvent &Call, + CheckerContext &C) const; - /// Mark filter's arguments not tainted on a pre-visit. Returns true on - /// matching. - bool addFiltersPre(const CallEvent &Call, const FunctionData &FData, - CheckerContext &C) const; + /// Handles the resolution of indexes of type ArgIdxTy to Expr*-s. + static const Expr *GetArgExpr(ArgIdxTy ArgIdx, const CallEvent &Call) { + return ArgIdx == ReturnValueIndex ? Call.getOriginExpr() + : Call.getArgExpr(ArgIdx); + }; - /// Propagate taint generated at pre-visit. Returns true on matching. - static bool propagateFromPre(const CallEvent &Call, CheckerContext &C); + /// Functions for custom taintedness propagation. + static bool UntrustedEnv(CheckerContext &C); +}; - /// Check if the region the expression evaluates to is the standard input, - /// and thus, is tainted. - static bool isStdin(const Expr *E, CheckerContext &C); +using RuleLookupTy = CallDescriptionMap<GenericTaintRule>; - /// Given a pointer argument, return the value it points to. - static Optional<SVal> getPointeeOf(CheckerContext &C, const Expr *Arg); +/// Used to parse the configuration file. +struct TaintConfiguration { + using NameScopeArgs = std::tuple<std::string, std::string, ArgVecTy>; + enum class VariadicType { None, Src, Dst }; - /// Check for CWE-134: Uncontrolled Format String. - static constexpr llvm::StringLiteral MsgUncontrolledFormatString = - "Untrusted data is used as a format string " - "(CWE-134: Uncontrolled Format String)"; - bool checkUncontrolledFormatString(const CallEvent &Call, - CheckerContext &C) const; + struct Common { + std::string Name; + std::string Scope; + }; - /// Check for: - /// CERT/STR02-C. "Sanitize data passed to complex subsystems" - /// CWE-78, "Failure to Sanitize Data into an OS Command" - static constexpr llvm::StringLiteral MsgSanitizeSystemArgs = - "Untrusted data is passed to a system call " - "(CERT/STR02-C. Sanitize data passed to complex subsystems)"; - bool checkSystemCall(const CallEvent &Call, StringRef Name, - CheckerContext &C) const; - - /// Check if tainted data is used as a buffer size ins strn.. functions, - /// and allocators. - static constexpr llvm::StringLiteral MsgTaintedBufferSize = - "Untrusted data is used to specify the buffer size " - "(CERT/STR31-C. Guarantee that storage for strings has sufficient space " - "for character data and the null terminator)"; - bool checkTaintedBufferSize(const CallEvent &Call, CheckerContext &C) const; - - /// Check if tainted data is used as a custom sink's parameter. - static constexpr llvm::StringLiteral MsgCustomSink = - "Untrusted data is passed to a user-defined sink"; - bool checkCustomSinks(const CallEvent &Call, const FunctionData &FData, - CheckerContext &C) const; + struct Sink : Common { + ArgVecTy SinkArgs; + }; - /// Generate a report if the expression is tainted or points to tainted data. - bool generateReportIfTainted(const Expr *E, StringRef Msg, - CheckerContext &C) const; + struct Filter : Common { + ArgVecTy FilterArgs; + }; - struct TaintPropagationRule; - template <typename T> - using ConfigDataMap = - std::unordered_multimap<std::string, std::pair<std::string, T>>; - using NameRuleMap = ConfigDataMap<TaintPropagationRule>; - using NameArgMap = ConfigDataMap<ArgVector>; - - /// Find a function with the given name and scope. Returns the first match - /// or the end of the map. - template <typename T> - static auto findFunctionInConfig(const ConfigDataMap<T> &Map, - const FunctionData &FData); - - /// A struct used to specify taint propagation rules for a function. - /// - /// If any of the possible taint source arguments is tainted, all of the - /// destination arguments should also be tainted. Use InvalidArgIndex in the - /// src list to specify that all of the arguments can introduce taint. Use - /// InvalidArgIndex in the dst arguments to signify that all the non-const - /// pointer and reference arguments might be tainted on return. If - /// ReturnValueIndex is added to the dst list, the return value will be - /// tainted. - struct TaintPropagationRule { - using PropagationFuncType = bool (*)(bool IsTainted, const CallEvent &Call, - CheckerContext &C); - - /// List of arguments which can be taint sources and should be checked. - ArgVector SrcArgs; - /// List of arguments which should be tainted on function return. - ArgVector DstArgs; - /// Index for the first variadic parameter if exist. - unsigned VariadicIndex; - /// Show when a function has variadic parameters. If it has, it marks all - /// of them as source or destination. + struct Propagation : Common { + ArgVecTy SrcArgs; + ArgVecTy DstArgs; VariadicType VarType; - /// Special function for tainted source determination. If defined, it can - /// override the default behavior. - PropagationFuncType PropagationFunc; - - TaintPropagationRule() - : VariadicIndex(InvalidArgIndex), VarType(VariadicType::None), - PropagationFunc(nullptr) {} - - TaintPropagationRule(ArgVector &&Src, ArgVector &&Dst, - VariadicType Var = VariadicType::None, - unsigned VarIndex = InvalidArgIndex, - PropagationFuncType Func = nullptr) - : SrcArgs(std::move(Src)), DstArgs(std::move(Dst)), - VariadicIndex(VarIndex), VarType(Var), PropagationFunc(Func) {} - - /// Get the propagation rule for a given function. - static TaintPropagationRule - getTaintPropagationRule(const NameRuleMap &CustomPropagations, - const FunctionData &FData, CheckerContext &C); - - void addSrcArg(unsigned A) { SrcArgs.push_back(A); } - void addDstArg(unsigned A) { DstArgs.push_back(A); } - - bool isNull() const { - return SrcArgs.empty() && DstArgs.empty() && - VariadicType::None == VarType; - } + ArgIdxTy VarIndex; + }; - bool isDestinationArgument(unsigned ArgNum) const { - return (llvm::find(DstArgs, ArgNum) != DstArgs.end()); - } + std::vector<Propagation> Propagations; + std::vector<Filter> Filters; + std::vector<Sink> Sinks; - static bool isTaintedOrPointsToTainted(const Expr *E, - const ProgramStateRef &State, - CheckerContext &C) { - if (isTainted(State, E, C.getLocationContext()) || isStdin(E, C)) - return true; + TaintConfiguration() = default; + TaintConfiguration(const TaintConfiguration &) = default; + TaintConfiguration(TaintConfiguration &&) = default; + TaintConfiguration &operator=(const TaintConfiguration &) = default; + TaintConfiguration &operator=(TaintConfiguration &&) = default; +}; - if (!E->getType().getTypePtr()->isPointerType()) - return false; +struct GenericTaintRuleParser { + GenericTaintRuleParser(CheckerManager &Mgr) : Mgr(Mgr) {} + /// Container type used to gather call identification objects grouped into + /// pairs with their corresponding taint rules. It is temporary as it is used + /// to finally initialize RuleLookupTy, which is considered to be immutable. + using RulesContTy = std::vector<std::pair<CallDescription, GenericTaintRule>>; + RulesContTy parseConfiguration(const std::string &Option, + TaintConfiguration &&Config) const; - Optional<SVal> V = getPointeeOf(C, E); - return (V && isTainted(State, *V)); - } +private: + using NamePartsTy = llvm::SmallVector<StringRef, 2>; - /// Pre-process a function which propagates taint according to the - /// taint rule. - ProgramStateRef process(const CallEvent &Call, CheckerContext &C) const; + /// Validate part of the configuration, which contains a list of argument + /// indexes. + void validateArgVector(const std::string &Option, const ArgVecTy &Args) const; - // Functions for custom taintedness propagation. - static bool postSocket(bool IsTainted, const CallEvent &Call, - CheckerContext &C); - }; + template <typename Config> static NamePartsTy parseNameParts(const Config &C); - /// Defines a map between the propagation function's name, scope - /// and TaintPropagationRule. - NameRuleMap CustomPropagations; + // Takes the config and creates a CallDescription for it and associates a Rule + // with that. + template <typename Config> + static void consumeRulesFromConfig(const Config &C, GenericTaintRule &&Rule, + RulesContTy &Rules); - /// Defines a map between the filter function's name, scope and filtering - /// args. - NameArgMap CustomFilters; + void parseConfig(const std::string &Option, TaintConfiguration::Sink &&P, + RulesContTy &Rules) const; + void parseConfig(const std::string &Option, TaintConfiguration::Filter &&P, + RulesContTy &Rules) const; + void parseConfig(const std::string &Option, + TaintConfiguration::Propagation &&P, + RulesContTy &Rules) const; - /// Defines a map between the sink function's name, scope and sinking args. - NameArgMap CustomSinks; + CheckerManager &Mgr; }; -const unsigned GenericTaintChecker::ReturnValueIndex; -const unsigned GenericTaintChecker::InvalidArgIndex; +class GenericTaintChecker : public Checker<check::PreCall, check::PostCall> { +public: + void checkPreCall(const CallEvent &Call, CheckerContext &C) const; + void checkPostCall(const CallEvent &Call, CheckerContext &C) const; -// FIXME: these lines can be removed in C++17 -constexpr llvm::StringLiteral GenericTaintChecker::MsgUncontrolledFormatString; -constexpr llvm::StringLiteral GenericTaintChecker::MsgSanitizeSystemArgs; -constexpr llvm::StringLiteral GenericTaintChecker::MsgTaintedBufferSize; -constexpr llvm::StringLiteral GenericTaintChecker::MsgCustomSink; -} // end of anonymous namespace + void printState(raw_ostream &Out, ProgramStateRef State, const char *NL, + const char *Sep) const override; -using TaintConfig = GenericTaintChecker::TaintConfiguration; + /// Generate a report if the expression is tainted or points to tainted data. + bool generateReportIfTainted(const Expr *E, StringRef Msg, + CheckerContext &C) const; -LLVM_YAML_IS_SEQUENCE_VECTOR(TaintConfig::Propagation) -LLVM_YAML_IS_SEQUENCE_VECTOR(TaintConfig::NameScopeArgs) +private: + const BugType BT{this, "Use of Untrusted Data", categories::TaintedData}; + + bool checkUncontrolledFormatString(const CallEvent &Call, + CheckerContext &C) const; + + void taintUnsafeSocketProtocol(const CallEvent &Call, + CheckerContext &C) const; + + /// Default taint rules are initalized with the help of a CheckerContext to + /// access the names of built-in functions like memcpy. + void initTaintRules(CheckerContext &C) const; + + /// CallDescription currently cannot restrict matches to the global namespace + /// only, which is why multiple CallDescriptionMaps are used, as we want to + /// disambiguate global C functions from functions inside user-defined + /// namespaces. + // TODO: Remove separation to simplify matching logic once CallDescriptions + // are more expressive. + + mutable std::optional<RuleLookupTy> StaticTaintRules; + mutable std::optional<RuleLookupTy> DynamicTaintRules; +}; +} // end of anonymous namespace + +/// YAML serialization mapping. +LLVM_YAML_IS_SEQUENCE_VECTOR(TaintConfiguration::Sink) +LLVM_YAML_IS_SEQUENCE_VECTOR(TaintConfiguration::Filter) +LLVM_YAML_IS_SEQUENCE_VECTOR(TaintConfiguration::Propagation) namespace llvm { namespace yaml { -template <> struct MappingTraits<TaintConfig> { - static void mapping(IO &IO, TaintConfig &Config) { +template <> struct MappingTraits<TaintConfiguration> { + static void mapping(IO &IO, TaintConfiguration &Config) { IO.mapOptional("Propagations", Config.Propagations); IO.mapOptional("Filters", Config.Filters); IO.mapOptional("Sinks", Config.Sinks); } }; -template <> struct MappingTraits<TaintConfig::Propagation> { - static void mapping(IO &IO, TaintConfig::Propagation &Propagation) { +template <> struct MappingTraits<TaintConfiguration::Sink> { + static void mapping(IO &IO, TaintConfiguration::Sink &Sink) { + IO.mapRequired("Name", Sink.Name); + IO.mapOptional("Scope", Sink.Scope); + IO.mapRequired("Args", Sink.SinkArgs); + } +}; + +template <> struct MappingTraits<TaintConfiguration::Filter> { + static void mapping(IO &IO, TaintConfiguration::Filter &Filter) { + IO.mapRequired("Name", Filter.Name); + IO.mapOptional("Scope", Filter.Scope); + IO.mapRequired("Args", Filter.FilterArgs); + } +}; + +template <> struct MappingTraits<TaintConfiguration::Propagation> { + static void mapping(IO &IO, TaintConfiguration::Propagation &Propagation) { IO.mapRequired("Name", Propagation.Name); IO.mapOptional("Scope", Propagation.Scope); IO.mapOptional("SrcArgs", Propagation.SrcArgs); IO.mapOptional("DstArgs", Propagation.DstArgs); - IO.mapOptional("VariadicType", Propagation.VarType, - GenericTaintChecker::VariadicType::None); - IO.mapOptional("VariadicIndex", Propagation.VarIndex, - GenericTaintChecker::InvalidArgIndex); + IO.mapOptional("VariadicType", Propagation.VarType); + IO.mapOptional("VariadicIndex", Propagation.VarIndex); } }; -template <> struct ScalarEnumerationTraits<GenericTaintChecker::VariadicType> { - static void enumeration(IO &IO, GenericTaintChecker::VariadicType &Value) { - IO.enumCase(Value, "None", GenericTaintChecker::VariadicType::None); - IO.enumCase(Value, "Src", GenericTaintChecker::VariadicType::Src); - IO.enumCase(Value, "Dst", GenericTaintChecker::VariadicType::Dst); - } -}; - -template <> struct MappingTraits<TaintConfig::NameScopeArgs> { - static void mapping(IO &IO, TaintConfig::NameScopeArgs &NSA) { - IO.mapRequired("Name", std::get<0>(NSA)); - IO.mapOptional("Scope", std::get<1>(NSA)); - IO.mapRequired("Args", std::get<2>(NSA)); +template <> struct ScalarEnumerationTraits<TaintConfiguration::VariadicType> { + static void enumeration(IO &IO, TaintConfiguration::VariadicType &Value) { + IO.enumCase(Value, "None", TaintConfiguration::VariadicType::None); + IO.enumCase(Value, "Src", TaintConfiguration::VariadicType::Src); + IO.enumCase(Value, "Dst", TaintConfiguration::VariadicType::Dst); } }; } // namespace yaml } // namespace llvm /// A set which is used to pass information from call pre-visit instruction -/// to the call post-visit. The values are unsigned integers, which are either +/// to the call post-visit. The values are signed integers, which are either /// ReturnValueIndex, or indexes of the pointer/reference argument, which /// points to data, which should be tainted on return. -REGISTER_SET_WITH_PROGRAMSTATE(TaintArgsOnPostVisit, unsigned) - -GenericTaintChecker::ArgVector -GenericTaintChecker::convertToArgVector(CheckerManager &Mgr, - const std::string &Option, - const SignedArgVector &Args) { - ArgVector Result; - for (int Arg : Args) { - if (Arg == -1) - Result.push_back(ReturnValueIndex); - else if (Arg < -1) { - Result.push_back(InvalidArgIndex); +REGISTER_MAP_WITH_PROGRAMSTATE(TaintArgsOnPostVisit, const LocationContext *, + ImmutableSet<ArgIdxTy>) +REGISTER_SET_FACTORY_WITH_PROGRAMSTATE(ArgIdxFactory, ArgIdxTy) + +void GenericTaintRuleParser::validateArgVector(const std::string &Option, + const ArgVecTy &Args) const { + for (ArgIdxTy Arg : Args) { + if (Arg < ReturnValueIndex) { Mgr.reportInvalidCheckerOptionValue( - this, Option, + Mgr.getChecker<GenericTaintChecker>(), Option, "an argument number for propagation rules greater or equal to -1"); - } else - Result.push_back(static_cast<unsigned>(Arg)); + } } - return Result; } -void GenericTaintChecker::parseConfiguration(CheckerManager &Mgr, - const std::string &Option, - TaintConfiguration &&Config) { - for (auto &P : Config.Propagations) { - GenericTaintChecker::CustomPropagations.emplace( - P.Name, - std::make_pair(P.Scope, TaintPropagationRule{ - std::move(P.SrcArgs), - convertToArgVector(Mgr, Option, P.DstArgs), - P.VarType, P.VarIndex})); +template <typename Config> +GenericTaintRuleParser::NamePartsTy +GenericTaintRuleParser::parseNameParts(const Config &C) { + NamePartsTy NameParts; + if (!C.Scope.empty()) { + // If the Scope argument contains multiple "::" parts, those are considered + // namespace identifiers. + StringRef{C.Scope}.split(NameParts, "::", /*MaxSplit*/ -1, + /*KeepEmpty*/ false); } + NameParts.emplace_back(C.Name); + return NameParts; +} - for (auto &F : Config.Filters) { - GenericTaintChecker::CustomFilters.emplace( - std::get<0>(F), - std::make_pair(std::move(std::get<1>(F)), std::move(std::get<2>(F)))); - } +template <typename Config> +void GenericTaintRuleParser::consumeRulesFromConfig(const Config &C, + GenericTaintRule &&Rule, + RulesContTy &Rules) { + NamePartsTy NameParts = parseNameParts(C); + Rules.emplace_back(CallDescription(NameParts), std::move(Rule)); +} - for (auto &S : Config.Sinks) { - GenericTaintChecker::CustomSinks.emplace( - std::get<0>(S), - std::make_pair(std::move(std::get<1>(S)), std::move(std::get<2>(S)))); - } +void GenericTaintRuleParser::parseConfig(const std::string &Option, + TaintConfiguration::Sink &&S, + RulesContTy &Rules) const { + validateArgVector(Option, S.SinkArgs); + consumeRulesFromConfig(S, GenericTaintRule::Sink(std::move(S.SinkArgs)), + Rules); } -template <typename T> -auto GenericTaintChecker::findFunctionInConfig(const ConfigDataMap<T> &Map, - const FunctionData &FData) { - auto Range = Map.equal_range(std::string(FData.Name)); - auto It = - std::find_if(Range.first, Range.second, [&FData](const auto &Entry) { - const auto &Value = Entry.second; - StringRef Scope = Value.first; - return Scope.empty() || FData.isInScope(Scope); - }); - return It != Range.second ? It : Map.end(); +void GenericTaintRuleParser::parseConfig(const std::string &Option, + TaintConfiguration::Filter &&S, + RulesContTy &Rules) const { + validateArgVector(Option, S.FilterArgs); + consumeRulesFromConfig(S, GenericTaintRule::Filter(std::move(S.FilterArgs)), + Rules); } -GenericTaintChecker::TaintPropagationRule -GenericTaintChecker::TaintPropagationRule::getTaintPropagationRule( - const NameRuleMap &CustomPropagations, const FunctionData &FData, - CheckerContext &C) { - // TODO: Currently, we might lose precision here: we always mark a return - // value as tainted even if it's just a pointer, pointing to tainted data. +void GenericTaintRuleParser::parseConfig(const std::string &Option, + TaintConfiguration::Propagation &&P, + RulesContTy &Rules) const { + validateArgVector(Option, P.SrcArgs); + validateArgVector(Option, P.DstArgs); + bool IsSrcVariadic = P.VarType == TaintConfiguration::VariadicType::Src; + bool IsDstVariadic = P.VarType == TaintConfiguration::VariadicType::Dst; + std::optional<ArgIdxTy> JustVarIndex = P.VarIndex; + + ArgSet SrcDesc(std::move(P.SrcArgs), + IsSrcVariadic ? JustVarIndex : std::nullopt); + ArgSet DstDesc(std::move(P.DstArgs), + IsDstVariadic ? JustVarIndex : std::nullopt); + + consumeRulesFromConfig( + P, GenericTaintRule::Prop(std::move(SrcDesc), std::move(DstDesc)), Rules); +} + +GenericTaintRuleParser::RulesContTy +GenericTaintRuleParser::parseConfiguration(const std::string &Option, + TaintConfiguration &&Config) const { + + RulesContTy Rules; + + for (auto &F : Config.Filters) + parseConfig(Option, std::move(F), Rules); + + for (auto &S : Config.Sinks) + parseConfig(Option, std::move(S), Rules); + for (auto &P : Config.Propagations) + parseConfig(Option, std::move(P), Rules); + + return Rules; +} + +void GenericTaintChecker::initTaintRules(CheckerContext &C) const { // Check for exact name match for functions without builtin substitutes. // Use qualified name, because these are C functions without namespace. - TaintPropagationRule Rule = - llvm::StringSwitch<TaintPropagationRule>(FData.FullName) - // Source functions - // TODO: Add support for vfscanf & family. - .Case("fdopen", {{}, {ReturnValueIndex}}) - .Case("fopen", {{}, {ReturnValueIndex}}) - .Case("freopen", {{}, {ReturnValueIndex}}) - .Case("getch", {{}, {ReturnValueIndex}}) - .Case("getchar", {{}, {ReturnValueIndex}}) - .Case("getchar_unlocked", {{}, {ReturnValueIndex}}) - .Case("getenv", {{}, {ReturnValueIndex}}) - .Case("gets", {{}, {0, ReturnValueIndex}}) - .Case("scanf", {{}, {}, VariadicType::Dst, 1}) - .Case("socket", {{}, - {ReturnValueIndex}, - VariadicType::None, - InvalidArgIndex, - &TaintPropagationRule::postSocket}) - .Case("wgetch", {{}, {ReturnValueIndex}}) - // Propagating functions - .Case("atoi", {{0}, {ReturnValueIndex}}) - .Case("atol", {{0}, {ReturnValueIndex}}) - .Case("atoll", {{0}, {ReturnValueIndex}}) - .Case("fgetc", {{0}, {ReturnValueIndex}}) - .Case("fgetln", {{0}, {ReturnValueIndex}}) - .Case("fgets", {{2}, {0, ReturnValueIndex}}) - .Case("fscanf", {{0}, {}, VariadicType::Dst, 2}) - .Case("sscanf", {{0}, {}, VariadicType::Dst, 2}) - .Case("getc", {{0}, {ReturnValueIndex}}) - .Case("getc_unlocked", {{0}, {ReturnValueIndex}}) - .Case("getdelim", {{3}, {0}}) - .Case("getline", {{2}, {0}}) - .Case("getw", {{0}, {ReturnValueIndex}}) - .Case("pread", {{0, 1, 2, 3}, {1, ReturnValueIndex}}) - .Case("read", {{0, 2}, {1, ReturnValueIndex}}) - .Case("strchr", {{0}, {ReturnValueIndex}}) - .Case("strrchr", {{0}, {ReturnValueIndex}}) - .Case("tolower", {{0}, {ReturnValueIndex}}) - .Case("toupper", {{0}, {ReturnValueIndex}}) - .Default({}); - - if (!Rule.isNull()) - return Rule; - assert(FData.FDecl); - - // Check if it's one of the memory setting/copying functions. - // This check is specialized but faster then calling isCLibraryFunction. - const FunctionDecl *FDecl = FData.FDecl; - unsigned BId = 0; - if ((BId = FDecl->getMemoryFunctionKind())) { - switch (BId) { - case Builtin::BImemcpy: - case Builtin::BImemmove: - case Builtin::BIstrncpy: - case Builtin::BIstrncat: - return {{1, 2}, {0, ReturnValueIndex}}; - case Builtin::BIstrlcpy: - case Builtin::BIstrlcat: - return {{1, 2}, {0}}; - case Builtin::BIstrndup: - return {{0, 1}, {ReturnValueIndex}}; - - default: - break; - } + + if (StaticTaintRules || DynamicTaintRules) + return; + + using RulesConstructionTy = + std::vector<std::pair<CallDescription, GenericTaintRule>>; + using TR = GenericTaintRule; + + const Builtin::Context &BI = C.getASTContext().BuiltinInfo; + + RulesConstructionTy GlobalCRules{ + // Sources + {{{"fdopen"}}, TR::Source({{ReturnValueIndex}})}, + {{{"fopen"}}, TR::Source({{ReturnValueIndex}})}, + {{{"freopen"}}, TR::Source({{ReturnValueIndex}})}, + {{{"getch"}}, TR::Source({{ReturnValueIndex}})}, + {{{"getchar"}}, TR::Source({{ReturnValueIndex}})}, + {{{"getchar_unlocked"}}, TR::Source({{ReturnValueIndex}})}, + {{{"gets"}}, TR::Source({{0}, ReturnValueIndex})}, + {{{"gets_s"}}, TR::Source({{0}, ReturnValueIndex})}, + {{{"scanf"}}, TR::Source({{}, 1})}, + {{{"scanf_s"}}, TR::Source({{}, {1}})}, + {{{"wgetch"}}, TR::Source({{}, ReturnValueIndex})}, + // Sometimes the line between taint sources and propagators is blurry. + // _IO_getc is choosen to be a source, but could also be a propagator. + // This way it is simpler, as modeling it as a propagator would require + // to model the possible sources of _IO_FILE * values, which the _IO_getc + // function takes as parameters. + {{{"_IO_getc"}}, TR::Source({{ReturnValueIndex}})}, + {{{"getcwd"}}, TR::Source({{0, ReturnValueIndex}})}, + {{{"getwd"}}, TR::Source({{0, ReturnValueIndex}})}, + {{{"readlink"}}, TR::Source({{1, ReturnValueIndex}})}, + {{{"readlinkat"}}, TR::Source({{2, ReturnValueIndex}})}, + {{{"get_current_dir_name"}}, TR::Source({{ReturnValueIndex}})}, + {{{"gethostname"}}, TR::Source({{0}})}, + {{{"getnameinfo"}}, TR::Source({{2, 4}})}, + {{{"getseuserbyname"}}, TR::Source({{1, 2}})}, + {{{"getgroups"}}, TR::Source({{1, ReturnValueIndex}})}, + {{{"getlogin"}}, TR::Source({{ReturnValueIndex}})}, + {{{"getlogin_r"}}, TR::Source({{0}})}, + + // 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})}, + + {{{"getc"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"getc_unlocked"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"getdelim"}}, TR::Prop({{3}}, {{0}})}, + {{{"getline"}}, TR::Prop({{2}}, {{0}})}, + {{{"getw"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"pread"}}, TR::Prop({{0, 1, 2, 3}}, {{1, ReturnValueIndex}})}, + {{{"read"}}, TR::Prop({{0, 2}}, {{1, ReturnValueIndex}})}, + {{{"strchr"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"strrchr"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"tolower"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"toupper"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"fread"}}, TR::Prop({{3}}, {{0, ReturnValueIndex}})}, + {{{"recv"}}, TR::Prop({{0}}, {{1, ReturnValueIndex}})}, + {{{"recvfrom"}}, TR::Prop({{0}}, {{1, ReturnValueIndex}})}, + + {{{"ttyname"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"ttyname_r"}}, TR::Prop({{0}}, {{1, ReturnValueIndex}})}, + + {{{"basename"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"dirname"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"fnmatch"}}, TR::Prop({{1}}, {{ReturnValueIndex}})}, + {{{"memchr"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"memrchr"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"rawmemchr"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + + {{{"mbtowc"}}, TR::Prop({{1}}, {{0, ReturnValueIndex}})}, + {{{"wctomb"}}, TR::Prop({{1}}, {{0, ReturnValueIndex}})}, + {{{"wcwidth"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + + {{{"memcmp"}}, TR::Prop({{0, 1}}, {{ReturnValueIndex}})}, + {{{"memcpy"}}, TR::Prop({{1}}, {{0, ReturnValueIndex}})}, + {{{"memmove"}}, TR::Prop({{1}}, {{0, ReturnValueIndex}})}, + // If memmem was called with a tainted needle and the search was + // successful, that would mean that the value pointed by the return value + // has the same content as the needle. If we choose to go by the policy of + // content equivalence implies taintedness equivalence, that would mean + // haystack should be considered a propagation source argument. + {{{"memmem"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + + // The comment for memmem above also applies to strstr. + {{{"strstr"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"strcasestr"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + + {{{"strchrnul"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + + {{{"index"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"rindex"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + + // FIXME: In case of arrays, only the first element of the array gets + // tainted. + {{{"qsort"}}, TR::Prop({{0}}, {{0}})}, + {{{"qsort_r"}}, TR::Prop({{0}}, {{0}})}, + + {{{"strcmp"}}, TR::Prop({{0, 1}}, {{ReturnValueIndex}})}, + {{{"strcasecmp"}}, TR::Prop({{0, 1}}, {{ReturnValueIndex}})}, + {{{"strncmp"}}, TR::Prop({{0, 1, 2}}, {{ReturnValueIndex}})}, + {{{"strncasecmp"}}, TR::Prop({{0, 1, 2}}, {{ReturnValueIndex}})}, + {{{"strspn"}}, TR::Prop({{0, 1}}, {{ReturnValueIndex}})}, + {{{"strcspn"}}, TR::Prop({{0, 1}}, {{ReturnValueIndex}})}, + {{{"strpbrk"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"strndup"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"strndupa"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + + // strlen, 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}})}, + {{{"strtoull"}}, TR::Prop({{0}}, {{1, ReturnValueIndex}})}, + + {{{"isalnum"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"isalpha"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"isascii"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"isblank"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"iscntrl"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"isdigit"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"isgraph"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"islower"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"isprint"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"ispunct"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"isspace"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"isupper"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + {{{"isxdigit"}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + + {{CDF_MaybeBuiltin, {BI.getName(Builtin::BIstrncat)}}, + TR::Prop({{1, 2}}, {{0, ReturnValueIndex}})}, + {{CDF_MaybeBuiltin, {BI.getName(Builtin::BIstrlcpy)}}, + TR::Prop({{1, 2}}, {{0}})}, + {{CDF_MaybeBuiltin, {BI.getName(Builtin::BIstrlcat)}}, + TR::Prop({{1, 2}}, {{0}})}, + {{CDF_MaybeBuiltin, {{"snprintf"}}}, + TR::Prop({{1}, 3}, {{0, ReturnValueIndex}})}, + {{CDF_MaybeBuiltin, {{"sprintf"}}}, + TR::Prop({{1}, 2}, {{0, ReturnValueIndex}})}, + {{CDF_MaybeBuiltin, {{"strcpy"}}}, + TR::Prop({{1}}, {{0, ReturnValueIndex}})}, + {{CDF_MaybeBuiltin, {{"stpcpy"}}}, + 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}})}, + {{CDF_MaybeBuiltin, {{"wcsdup"}}}, TR::Prop({{0}}, {{ReturnValueIndex}})}, + + // Sinks + {{{"system"}}, TR::Sink({{0}}, MsgSanitizeSystemArgs)}, + {{{"popen"}}, TR::Sink({{0}}, MsgSanitizeSystemArgs)}, + {{{"execl"}}, TR::Sink({{}, {0}}, MsgSanitizeSystemArgs)}, + {{{"execle"}}, TR::Sink({{}, {0}}, MsgSanitizeSystemArgs)}, + {{{"execlp"}}, TR::Sink({{}, {0}}, MsgSanitizeSystemArgs)}, + {{{"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)}, + {{CDF_MaybeBuiltin, {{"alloca"}}}, TR::Sink({{0}}, MsgTaintedBufferSize)}, + {{CDF_MaybeBuiltin, {{"memccpy"}}}, + TR::Sink({{3}}, MsgTaintedBufferSize)}, + {{CDF_MaybeBuiltin, {{"realloc"}}}, + TR::Sink({{1}}, MsgTaintedBufferSize)}, + {{{{"setproctitle"}}}, TR::Sink({{0}, 1}, MsgUncontrolledFormatString)}, + {{{{"setproctitle_fast"}}}, + TR::Sink({{0}, 1}, MsgUncontrolledFormatString)}, + + // SinkProps + {{CDF_MaybeBuiltin, BI.getName(Builtin::BImemcpy)}, + TR::SinkProp({{2}}, {{1, 2}}, {{0, ReturnValueIndex}}, + MsgTaintedBufferSize)}, + {{CDF_MaybeBuiltin, {BI.getName(Builtin::BImemmove)}}, + TR::SinkProp({{2}}, {{1, 2}}, {{0, ReturnValueIndex}}, + MsgTaintedBufferSize)}, + {{CDF_MaybeBuiltin, {BI.getName(Builtin::BIstrncpy)}}, + TR::SinkProp({{2}}, {{1, 2}}, {{0, ReturnValueIndex}}, + MsgTaintedBufferSize)}, + {{CDF_MaybeBuiltin, {BI.getName(Builtin::BIstrndup)}}, + TR::SinkProp({{1}}, {{0, 1}}, {{ReturnValueIndex}}, + MsgTaintedBufferSize)}, + {{CDF_MaybeBuiltin, {{"bcopy"}}}, + TR::SinkProp({{2}}, {{0, 2}}, {{1}}, MsgTaintedBufferSize)}}; + + // `getenv` returns taint only in untrusted environments. + if (TR::UntrustedEnv(C)) { + // void setproctitle_init(int argc, char *argv[], char *envp[]) + GlobalCRules.push_back( + {{{"setproctitle_init"}}, TR::Sink({{1, 2}}, MsgCustomSink)}); + GlobalCRules.push_back({{{"getenv"}}, TR::Source({{ReturnValueIndex}})}); } - // Process all other functions which could be defined as builtins. - if (Rule.isNull()) { - const auto OneOf = [FDecl](const auto &... Name) { - // FIXME: use fold expression in C++17 - using unused = int[]; - bool ret = false; - static_cast<void>(unused{ - 0, (ret |= CheckerContext::isCLibraryFunction(FDecl, Name), 0)...}); - return ret; - }; - if (OneOf("snprintf")) - return {{1}, {0, ReturnValueIndex}, VariadicType::Src, 3}; - if (OneOf("sprintf")) - return {{}, {0, ReturnValueIndex}, VariadicType::Src, 2}; - if (OneOf("strcpy", "stpcpy", "strcat")) - return {{1}, {0, ReturnValueIndex}}; - if (OneOf("bcopy")) - return {{0, 2}, {1}}; - if (OneOf("strdup", "strdupa", "wcsdup")) - return {{0}, {ReturnValueIndex}}; + StaticTaintRules.emplace(std::make_move_iterator(GlobalCRules.begin()), + std::make_move_iterator(GlobalCRules.end())); + + // User-provided taint configuration. + CheckerManager *Mgr = C.getAnalysisManager().getCheckerManager(); + assert(Mgr); + GenericTaintRuleParser ConfigParser{*Mgr}; + std::string Option{"Config"}; + StringRef ConfigFile = + Mgr->getAnalyzerOptions().getCheckerStringOption(this, Option); + std::optional<TaintConfiguration> Config = + getConfiguration<TaintConfiguration>(*Mgr, this, Option, ConfigFile); + if (!Config) { + // We don't have external taint config, no parsing required. + DynamicTaintRules = RuleLookupTy{}; + return; } - // Skipping the following functions, since they might be used for cleansing or - // smart memory copy: - // - memccpy - copying until hitting a special character. + GenericTaintRuleParser::RulesContTy Rules{ + ConfigParser.parseConfiguration(Option, std::move(*Config))}; - auto It = findFunctionInConfig(CustomPropagations, FData); - if (It != CustomPropagations.end()) - return It->second.second; - return {}; + DynamicTaintRules.emplace(std::make_move_iterator(Rules.begin()), + std::make_move_iterator(Rules.end())); } void GenericTaintChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { - Optional<FunctionData> FData = FunctionData::create(Call, C); - if (!FData) - return; - - // Check for taintedness related errors first: system call, uncontrolled - // format string, tainted buffer size. - if (checkPre(Call, *FData, C)) - return; - - // Marks the function's arguments and/or return value tainted if it present in - // the list. - if (addSourcesPre(Call, *FData, C)) - return; - - addFiltersPre(Call, *FData, C); + initTaintRules(C); + + // FIXME: this should be much simpler. + if (const auto *Rule = + Call.isGlobalCFunction() ? StaticTaintRules->lookup(Call) : nullptr) + Rule->process(*this, Call, C); + else if (const auto *Rule = DynamicTaintRules->lookup(Call)) + Rule->process(*this, Call, C); + + // FIXME: These edge cases are to be eliminated from here eventually. + // + // Additional check that is not supported by CallDescription. + // TODO: Make CallDescription be able to match attributes such as printf-like + // arguments. + checkUncontrolledFormatString(Call, C); + + // TODO: Modeling sockets should be done in a specific checker. + // Socket is a source, which taints the return value. + taintUnsafeSocketProtocol(Call, C); } void GenericTaintChecker::checkPostCall(const CallEvent &Call, CheckerContext &C) const { // Set the marked values as tainted. The return value only accessible from // checkPostStmt. - propagateFromPre(Call, C); -} - -void GenericTaintChecker::printState(raw_ostream &Out, ProgramStateRef State, - const char *NL, const char *Sep) const { - printTaint(State, Out, NL, Sep); -} - -bool GenericTaintChecker::addSourcesPre(const CallEvent &Call, - const FunctionData &FData, - CheckerContext &C) const { - // First, try generating a propagation rule for this function. - TaintPropagationRule Rule = TaintPropagationRule::getTaintPropagationRule( - this->CustomPropagations, FData, C); - if (!Rule.isNull()) { - ProgramStateRef State = Rule.process(Call, C); - if (State) { - C.addTransition(State); - return true; - } - } - return false; -} - -bool GenericTaintChecker::addFiltersPre(const CallEvent &Call, - const FunctionData &FData, - CheckerContext &C) const { - auto It = findFunctionInConfig(CustomFilters, FData); - if (It == CustomFilters.end()) - return false; - - ProgramStateRef State = C.getState(); - const auto &Value = It->second; - const ArgVector &Args = Value.second; - for (unsigned ArgNum : Args) { - if (ArgNum >= Call.getNumArgs()) - continue; - - const Expr *Arg = Call.getArgExpr(ArgNum); - Optional<SVal> V = getPointeeOf(C, Arg); - if (V) - State = removeTaint(State, *V); - } - - if (State != C.getState()) { - C.addTransition(State); - return true; - } - return false; -} - -bool GenericTaintChecker::propagateFromPre(const CallEvent &Call, - CheckerContext &C) { ProgramStateRef State = C.getState(); + const StackFrameContext *CurrentFrame = C.getStackFrame(); // Depending on what was tainted at pre-visit, we determined a set of // arguments which should be tainted after the function returns. These are // stored in the state as TaintArgsOnPostVisit set. - TaintArgsOnPostVisitTy TaintArgs = State->get<TaintArgsOnPostVisit>(); - if (TaintArgs.isEmpty()) - return false; + TaintArgsOnPostVisitTy TaintArgsMap = State->get<TaintArgsOnPostVisit>(); - for (unsigned ArgNum : TaintArgs) { + const ImmutableSet<ArgIdxTy> *TaintArgs = TaintArgsMap.lookup(CurrentFrame); + if (!TaintArgs) + return; + assert(!TaintArgs->isEmpty()); + + LLVM_DEBUG(for (ArgIdxTy I + : *TaintArgs) { + llvm::dbgs() << "PostCall<"; + Call.dump(llvm::dbgs()); + llvm::dbgs() << "> actually wants to taint arg index: " << I << '\n'; + }); + + const NoteTag *InjectionTag = nullptr; + std::vector<SymbolRef> TaintedSymbols; + std::vector<ArgIdxTy> TaintedIndexes; + for (ArgIdxTy ArgNum : *TaintArgs) { // Special handling for the tainted return value. if (ArgNum == ReturnValueIndex) { State = addTaint(State, Call.getReturnValue()); + std::vector<SymbolRef> TaintedSyms = + getTaintedSymbols(State, Call.getReturnValue()); + if (!TaintedSyms.empty()) { + TaintedSymbols.push_back(TaintedSyms[0]); + TaintedIndexes.push_back(ArgNum); + } continue; } - // The arguments are pointer arguments. The data they are pointing at is // tainted after the call. - if (Call.getNumArgs() < (ArgNum + 1)) - return false; - const Expr *Arg = Call.getArgExpr(ArgNum); - Optional<SVal> V = getPointeeOf(C, Arg); - if (V) + if (auto V = getPointeeOf(State, Call.getArgSVal(ArgNum))) { State = addTaint(State, *V); + std::vector<SymbolRef> TaintedSyms = getTaintedSymbols(State, *V); + if (!TaintedSyms.empty()) { + TaintedSymbols.push_back(TaintedSyms[0]); + TaintedIndexes.push_back(ArgNum); + } + } } - + // Create a NoteTag callback, which prints to the user where the taintedness + // was propagated to. + InjectionTag = taintPropagationExplainerTag(C, TaintedSymbols, TaintedIndexes, + Call.getCalleeStackFrame(0)); // Clear up the taint info from the state. - State = State->remove<TaintArgsOnPostVisit>(); - - if (State != C.getState()) { - C.addTransition(State); - return true; - } - return false; -} - -bool GenericTaintChecker::checkPre(const CallEvent &Call, - const FunctionData &FData, - CheckerContext &C) const { - if (checkUncontrolledFormatString(Call, C)) - return true; - - if (checkSystemCall(Call, FData.Name, C)) - return true; - - if (checkTaintedBufferSize(Call, C)) - return true; - - return checkCustomSinks(Call, FData, C); + State = State->remove<TaintArgsOnPostVisit>(CurrentFrame); + C.addTransition(State, InjectionTag); } -Optional<SVal> GenericTaintChecker::getPointeeOf(CheckerContext &C, - const Expr *Arg) { - ProgramStateRef State = C.getState(); - SVal AddrVal = C.getSVal(Arg->IgnoreParens()); - if (AddrVal.isUnknownOrUndef()) - return None; - - Optional<Loc> AddrLoc = AddrVal.getAs<Loc>(); - if (!AddrLoc) - return None; - - QualType ArgTy = Arg->getType().getCanonicalType(); - if (!ArgTy->isPointerType()) - return State->getSVal(*AddrLoc); - - QualType ValTy = ArgTy->getPointeeType(); - - // Do not dereference void pointers. Treat them as byte pointers instead. - // FIXME: we might want to consider more than just the first byte. - if (ValTy->isVoidType()) - ValTy = C.getASTContext().CharTy; - - return State->getSVal(*AddrLoc, ValTy); +void GenericTaintChecker::printState(raw_ostream &Out, ProgramStateRef State, + const char *NL, const char *Sep) const { + printTaint(State, Out, NL, Sep); } -ProgramStateRef -GenericTaintChecker::TaintPropagationRule::process(const CallEvent &Call, - CheckerContext &C) const { +void GenericTaintRule::process(const GenericTaintChecker &Checker, + const CallEvent &Call, CheckerContext &C) const { ProgramStateRef State = C.getState(); + const ArgIdxTy CallNumArgs = fromArgumentCount(Call.getNumArgs()); - // Check for taint in arguments. - bool IsTainted = true; - for (unsigned ArgNum : SrcArgs) { - if (ArgNum >= Call.getNumArgs()) - continue; - - if ((IsTainted = - isTaintedOrPointsToTainted(Call.getArgExpr(ArgNum), State, C))) - break; - } - - // Check for taint in variadic arguments. - if (!IsTainted && VariadicType::Src == VarType) { - // Check if any of the arguments is tainted - for (unsigned i = VariadicIndex; i < Call.getNumArgs(); ++i) { - if ((IsTainted = - isTaintedOrPointsToTainted(Call.getArgExpr(i), State, C))) - break; + /// Iterate every call argument, and get their corresponding Expr and SVal. + const auto ForEachCallArg = [&C, &Call, CallNumArgs](auto &&Fun) { + for (ArgIdxTy I = ReturnValueIndex; I < CallNumArgs; ++I) { + const Expr *E = GetArgExpr(I, Call); + Fun(I, E, C.getSVal(E)); } - } + }; - if (PropagationFunc) - IsTainted = PropagationFunc(IsTainted, Call, C); + /// Check for taint sinks. + ForEachCallArg([this, &Checker, &C, &State](ArgIdxTy I, const Expr *E, SVal) { + // Add taintedness to stdin parameters + if (isStdin(C.getSVal(E), C.getASTContext())) { + State = addTaint(State, C.getSVal(E)); + } + if (SinkArgs.contains(I) && isTaintedOrPointsToTainted(State, C.getSVal(E))) + Checker.generateReportIfTainted(E, SinkMsg.value_or(MsgCustomSink), C); + }); + + /// Check for taint filters. + ForEachCallArg([this, &State](ArgIdxTy I, const Expr *E, SVal S) { + if (FilterArgs.contains(I)) { + State = removeTaint(State, S); + if (auto P = getPointeeOf(State, S)) + State = removeTaint(State, *P); + } + }); + + /// Check for taint propagation sources. + /// A rule will make the destination variables tainted if PropSrcArgs + /// is empty (taints the destination + /// arguments unconditionally), or if any of its signified + /// args are tainted in context of the current CallEvent. + bool IsMatching = PropSrcArgs.isEmpty(); + std::vector<SymbolRef> TaintedSymbols; + std::vector<ArgIdxTy> TaintedIndexes; + ForEachCallArg([this, &C, &IsMatching, &State, &TaintedSymbols, + &TaintedIndexes](ArgIdxTy I, const Expr *E, SVal) { + std::optional<SVal> TaintedSVal = + getTaintedPointeeOrPointer(State, C.getSVal(E)); + IsMatching = + IsMatching || (PropSrcArgs.contains(I) && TaintedSVal.has_value()); + + // We track back tainted arguments except for stdin + if (TaintedSVal && !isStdin(*TaintedSVal, C.getASTContext())) { + std::vector<SymbolRef> TaintedArgSyms = + getTaintedSymbols(State, *TaintedSVal); + if (!TaintedArgSyms.empty()) { + llvm::append_range(TaintedSymbols, TaintedArgSyms); + TaintedIndexes.push_back(I); + } + } + }); - if (!IsTainted) - return State; + // Early return for propagation rules which dont match. + // Matching propagations, Sinks and Filters will pass this point. + if (!IsMatching) + return; - // Mark the arguments which should be tainted after the function returns. - for (unsigned ArgNum : DstArgs) { - // Should mark the return value? - if (ArgNum == ReturnValueIndex) { - State = State->add<TaintArgsOnPostVisit>(ReturnValueIndex); - continue; - } + const auto WouldEscape = [](SVal V, QualType Ty) -> bool { + if (!isa<Loc>(V)) + return false; - if (ArgNum >= Call.getNumArgs()) - continue; + const bool IsNonConstRef = Ty->isReferenceType() && !Ty.isConstQualified(); + const bool IsNonConstPtr = + Ty->isPointerType() && !Ty->getPointeeType().isConstQualified(); - // Mark the given argument. - State = State->add<TaintArgsOnPostVisit>(ArgNum); - } + return IsNonConstRef || IsNonConstPtr; + }; - // Mark all variadic arguments tainted if present. - if (VariadicType::Dst == VarType) { - // For all pointer and references that were passed in: - // If they are not pointing to const data, mark data as tainted. - // TODO: So far we are just going one level down; ideally we'd need to - // recurse here. - for (unsigned i = VariadicIndex; i < Call.getNumArgs(); ++i) { - const Expr *Arg = Call.getArgExpr(i); - // Process pointer argument. - const Type *ArgTy = Arg->getType().getTypePtr(); - QualType PType = ArgTy->getPointeeType(); - if ((!PType.isNull() && !PType.isConstQualified()) || - (ArgTy->isReferenceType() && !Arg->getType().isConstQualified())) { - State = State->add<TaintArgsOnPostVisit>(i); - } - } - } + /// Propagate taint where it is necessary. + auto &F = State->getStateManager().get_context<ArgIdxFactory>(); + ImmutableSet<ArgIdxTy> Result = F.getEmptySet(); + ForEachCallArg( + [&](ArgIdxTy I, const Expr *E, SVal V) { + if (PropDstArgs.contains(I)) { + LLVM_DEBUG(llvm::dbgs() << "PreCall<"; Call.dump(llvm::dbgs()); + llvm::dbgs() + << "> prepares tainting arg index: " << I << '\n';); + Result = F.add(Result, I); + } + + // Taint property gets lost if the variable is passed as a + // non-const pointer or reference to a function which is + // not inlined. For matching rules we want to preserve the taintedness. + // TODO: We should traverse all reachable memory regions via the + // escaping parameter. Instead of doing that we simply mark only the + // referred memory region as tainted. + if (WouldEscape(V, E->getType()) && getTaintedPointeeOrPointer(State, V)) { + LLVM_DEBUG(if (!Result.contains(I)) { + llvm::dbgs() << "PreCall<"; + Call.dump(llvm::dbgs()); + llvm::dbgs() << "> prepares tainting arg index: " << I << '\n'; + }); + Result = F.add(Result, I); + } + }); - return State; + if (!Result.isEmpty()) + State = State->set<TaintArgsOnPostVisit>(C.getStackFrame(), Result); + const NoteTag *InjectionTag = taintOriginTrackerTag( + C, std::move(TaintedSymbols), std::move(TaintedIndexes), + Call.getCalleeStackFrame(0)); + C.addTransition(State, InjectionTag); } -// If argument 0(protocol domain) is network, the return value should get taint. -bool GenericTaintChecker::TaintPropagationRule::postSocket( - bool /*IsTainted*/, const CallEvent &Call, CheckerContext &C) { - SourceLocation DomLoc = Call.getArgExpr(0)->getExprLoc(); - StringRef DomName = C.getMacroNameOrSpelling(DomLoc); - // White list the internal communication protocols. - if (DomName.equals("AF_SYSTEM") || DomName.equals("AF_LOCAL") || - DomName.equals("AF_UNIX") || DomName.equals("AF_RESERVED_36")) - return false; - return true; +bool GenericTaintRule::UntrustedEnv(CheckerContext &C) { + return !C.getAnalysisManager() + .getAnalyzerOptions() + .ShouldAssumeControlledEnvironment; } -bool GenericTaintChecker::isStdin(const Expr *E, CheckerContext &C) { - ProgramStateRef State = C.getState(); - SVal Val = C.getSVal(E); - - // stdin is a pointer, so it would be a region. - const MemRegion *MemReg = Val.getAsRegion(); - - // The region should be symbolic, we do not know it's value. - const auto *SymReg = dyn_cast_or_null<SymbolicRegion>(MemReg); - if (!SymReg) - return false; +bool GenericTaintChecker::generateReportIfTainted(const Expr *E, StringRef Msg, + CheckerContext &C) const { + assert(E); + std::optional<SVal> TaintedSVal = + getTaintedPointeeOrPointer(C.getState(), C.getSVal(E)); - // Get it's symbol and find the declaration region it's pointing to. - const auto *Sm = dyn_cast<SymbolRegionValue>(SymReg->getSymbol()); - if (!Sm) - return false; - const auto *DeclReg = dyn_cast_or_null<DeclRegion>(Sm->getRegion()); - if (!DeclReg) + if (!TaintedSVal) return false; - // This region corresponds to a declaration, find out if it's a global/extern - // variable named stdin with the proper type. - if (const auto *D = dyn_cast_or_null<VarDecl>(DeclReg->getDecl())) { - D = D->getCanonicalDecl(); - if ((D->getName().find("stdin") != StringRef::npos) && D->isExternC()) { - const auto *PtrTy = dyn_cast<PointerType>(D->getType().getTypePtr()); - if (PtrTy && PtrTy->getPointeeType().getCanonicalType() == - C.getASTContext().getFILEType().getCanonicalType()) - return true; + // Generate diagnostic. + if (ExplodedNode *N = C.generateNonFatalErrorNode()) { + auto report = std::make_unique<PathSensitiveBugReport>(BT, Msg, N); + report->addRange(E->getSourceRange()); + for (auto TaintedSym : getTaintedSymbols(C.getState(), *TaintedSVal)) { + report->markInteresting(TaintedSym); } + + C.emitReport(std::move(report)); + return true; } return false; } +/// TODO: remove checking for printf format attributes and socket whitelisting +/// from GenericTaintChecker, and that means the following functions: +/// getPrintfFormatArgumentNum, +/// GenericTaintChecker::checkUncontrolledFormatString, +/// GenericTaintChecker::taintUnsafeSocketProtocol + static bool getPrintfFormatArgumentNum(const CallEvent &Call, const CheckerContext &C, - unsigned &ArgNum) { + ArgIdxTy &ArgNum) { // Find if the function contains a format string argument. // Handles: fprintf, printf, sprintf, snprintf, vfprintf, vprintf, vsprintf, // vsnprintf, syslog, custom annotated functions. - const FunctionDecl *FDecl = Call.getDecl()->getAsFunction(); + const Decl *CallDecl = Call.getDecl(); + if (!CallDecl) + return false; + const FunctionDecl *FDecl = CallDecl->getAsFunction(); if (!FDecl) return false; + + const ArgIdxTy CallNumArgs = fromArgumentCount(Call.getNumArgs()); + for (const auto *Format : FDecl->specific_attrs<FormatAttr>()) { ArgNum = Format->getFormatIdx() - 1; - if ((Format->getType()->getName() == "printf") && - Call.getNumArgs() > ArgNum) + if ((Format->getType()->getName() == "printf") && CallNumArgs > ArgNum) return true; } - // Or if a function is named setproctitle (this is a heuristic). - if (C.getCalleeName(FDecl).find("setproctitle") != StringRef::npos) { - ArgNum = 0; - return true; - } - - return false; -} - -bool GenericTaintChecker::generateReportIfTainted(const Expr *E, StringRef Msg, - CheckerContext &C) const { - assert(E); - - // Check for taint. - ProgramStateRef State = C.getState(); - Optional<SVal> PointedToSVal = getPointeeOf(C, E); - SVal TaintedSVal; - if (PointedToSVal && isTainted(State, *PointedToSVal)) - TaintedSVal = *PointedToSVal; - else if (isTainted(State, E, C.getLocationContext())) - TaintedSVal = C.getSVal(E); - else - return false; - - // Generate diagnostic. - if (ExplodedNode *N = C.generateNonFatalErrorNode()) { - initBugType(); - auto report = std::make_unique<PathSensitiveBugReport>(*BT, Msg, N); - report->addRange(E->getSourceRange()); - report->addVisitor(std::make_unique<TaintBugVisitor>(TaintedSVal)); - C.emitReport(std::move(report)); - return true; - } return false; } bool GenericTaintChecker::checkUncontrolledFormatString( const CallEvent &Call, CheckerContext &C) const { // Check if the function contains a format string argument. - unsigned ArgNum = 0; + ArgIdxTy ArgNum = 0; if (!getPrintfFormatArgumentNum(Call, C, ArgNum)) return false; @@ -855,102 +1082,35 @@ bool GenericTaintChecker::checkUncontrolledFormatString( MsgUncontrolledFormatString, C); } -bool GenericTaintChecker::checkSystemCall(const CallEvent &Call, StringRef Name, - CheckerContext &C) const { - // TODO: It might make sense to run this check on demand. In some cases, - // we should check if the environment has been cleansed here. We also might - // need to know if the user was reset before these calls(seteuid). - unsigned ArgNum = llvm::StringSwitch<unsigned>(Name) - .Case("system", 0) - .Case("popen", 0) - .Case("execl", 0) - .Case("execle", 0) - .Case("execlp", 0) - .Case("execv", 0) - .Case("execvp", 0) - .Case("execvP", 0) - .Case("execve", 0) - .Case("dlopen", 0) - .Default(InvalidArgIndex); - - if (ArgNum == InvalidArgIndex || Call.getNumArgs() < (ArgNum + 1)) - return false; - - return generateReportIfTainted(Call.getArgExpr(ArgNum), MsgSanitizeSystemArgs, - C); -} - -// TODO: Should this check be a part of the CString checker? -// If yes, should taint be a global setting? -bool GenericTaintChecker::checkTaintedBufferSize(const CallEvent &Call, - CheckerContext &C) const { - const auto *FDecl = Call.getDecl()->getAsFunction(); - // If the function has a buffer size argument, set ArgNum. - unsigned ArgNum = InvalidArgIndex; - unsigned BId = 0; - if ((BId = FDecl->getMemoryFunctionKind())) { - switch (BId) { - case Builtin::BImemcpy: - case Builtin::BImemmove: - case Builtin::BIstrncpy: - ArgNum = 2; - break; - case Builtin::BIstrndup: - ArgNum = 1; - break; - default: - break; - } - } - - if (ArgNum == InvalidArgIndex) { - using CCtx = CheckerContext; - if (CCtx::isCLibraryFunction(FDecl, "malloc") || - CCtx::isCLibraryFunction(FDecl, "calloc") || - CCtx::isCLibraryFunction(FDecl, "alloca")) - ArgNum = 0; - else if (CCtx::isCLibraryFunction(FDecl, "memccpy")) - ArgNum = 3; - else if (CCtx::isCLibraryFunction(FDecl, "realloc")) - ArgNum = 1; - else if (CCtx::isCLibraryFunction(FDecl, "bcopy")) - ArgNum = 2; - } - - return ArgNum != InvalidArgIndex && Call.getNumArgs() > ArgNum && - generateReportIfTainted(Call.getArgExpr(ArgNum), MsgTaintedBufferSize, - C); -} - -bool GenericTaintChecker::checkCustomSinks(const CallEvent &Call, - const FunctionData &FData, - CheckerContext &C) const { - auto It = findFunctionInConfig(CustomSinks, FData); - if (It == CustomSinks.end()) - return false; - - const auto &Value = It->second; - const GenericTaintChecker::ArgVector &Args = Value.second; - for (unsigned ArgNum : Args) { - if (ArgNum >= Call.getNumArgs()) - continue; +void GenericTaintChecker::taintUnsafeSocketProtocol(const CallEvent &Call, + CheckerContext &C) const { + if (Call.getNumArgs() < 1) + return; + const IdentifierInfo *ID = Call.getCalleeIdentifier(); + if (!ID) + return; + if (!ID->getName().equals("socket")) + return; - if (generateReportIfTainted(Call.getArgExpr(ArgNum), MsgCustomSink, C)) - return true; - } + SourceLocation DomLoc = Call.getArgExpr(0)->getExprLoc(); + StringRef DomName = C.getMacroNameOrSpelling(DomLoc); + // Allow internal communication protocols. + bool SafeProtocol = DomName.equals("AF_SYSTEM") || + DomName.equals("AF_LOCAL") || DomName.equals("AF_UNIX") || + DomName.equals("AF_RESERVED_36"); + if (SafeProtocol) + return; - return false; + ProgramStateRef State = C.getState(); + auto &F = State->getStateManager().get_context<ArgIdxFactory>(); + ImmutableSet<ArgIdxTy> Result = F.add(F.getEmptySet(), ReturnValueIndex); + State = State->set<TaintArgsOnPostVisit>(C.getStackFrame(), Result); + C.addTransition(State); } +/// Checker registration void ento::registerGenericTaintChecker(CheckerManager &Mgr) { - auto *Checker = Mgr.registerChecker<GenericTaintChecker>(); - std::string Option{"Config"}; - StringRef ConfigFile = - Mgr.getAnalyzerOptions().getCheckerStringOption(Checker, Option); - llvm::Optional<TaintConfig> Config = - getConfiguration<TaintConfig>(Mgr, Checker, Option, ConfigFile); - if (Config) - Checker->parseConfiguration(Mgr, Option, std::move(Config.getValue())); + Mgr.registerChecker<GenericTaintChecker>(); } bool ento::shouldRegisterGenericTaintChecker(const CheckerManager &mgr) { diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp index bcae73378028..b673b51c4623 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp @@ -13,11 +13,12 @@ //===----------------------------------------------------------------------===// #include "AllocationState.h" -#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "InterCheckerAPI.h" +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h" #include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" @@ -34,9 +35,9 @@ namespace { class InnerPointerChecker : public Checker<check::DeadSymbols, check::PostCall> { - CallDescription AppendFn, AssignFn, AddressofFn, ClearFn, CStrFn, DataFn, - DataMemberFn, EraseFn, InsertFn, PopBackFn, PushBackFn, ReplaceFn, - ReserveFn, ResizeFn, ShrinkToFitFn, SwapFn; + CallDescription AppendFn, AssignFn, AddressofFn, AddressofFn_, ClearFn, + CStrFn, DataFn, DataMemberFn, EraseFn, InsertFn, PopBackFn, PushBackFn, + ReplaceFn, ReserveFn, ResizeFn, ShrinkToFitFn, SwapFn; public: class InnerPointerBRVisitor : public BugReporterVisitor { @@ -54,9 +55,9 @@ public: ID.AddPointer(getTag()); } - virtual PathDiagnosticPieceRef - VisitNode(const ExplodedNode *N, BugReporterContext &BRC, - PathSensitiveBugReport &BR) override; + PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + PathSensitiveBugReport &BR) override; // FIXME: Scan the map once in the visitor's constructor and do a direct // lookup by region. @@ -73,7 +74,7 @@ public: InnerPointerChecker() : AppendFn({"std", "basic_string", "append"}), AssignFn({"std", "basic_string", "assign"}), - AddressofFn({"std", "addressof"}), + AddressofFn({"std", "addressof"}), AddressofFn_({"std", "__addressof"}), ClearFn({"std", "basic_string", "clear"}), CStrFn({"std", "basic_string", "c_str"}), DataFn({"std", "data"}, 1), DataMemberFn({"std", "basic_string", "data"}), @@ -125,19 +126,15 @@ bool InnerPointerChecker::isInvalidatingMemberFunction( return true; return false; } - return (isa<CXXDestructorCall>(Call) || Call.isCalled(AppendFn) || - Call.isCalled(AssignFn) || Call.isCalled(ClearFn) || - Call.isCalled(EraseFn) || Call.isCalled(InsertFn) || - Call.isCalled(PopBackFn) || Call.isCalled(PushBackFn) || - Call.isCalled(ReplaceFn) || Call.isCalled(ReserveFn) || - Call.isCalled(ResizeFn) || Call.isCalled(ShrinkToFitFn) || - Call.isCalled(SwapFn)); + return isa<CXXDestructorCall>(Call) || + matchesAny(Call, AppendFn, AssignFn, ClearFn, EraseFn, InsertFn, + PopBackFn, PushBackFn, ReplaceFn, ReserveFn, ResizeFn, + ShrinkToFitFn, SwapFn); } bool InnerPointerChecker::isInnerPointerAccessFunction( const CallEvent &Call) const { - return (Call.isCalled(CStrFn) || Call.isCalled(DataFn) || - Call.isCalled(DataMemberFn)); + return matchesAny(Call, CStrFn, DataFn, DataMemberFn); } void InnerPointerChecker::markPtrSymbolsReleased(const CallEvent &Call, @@ -182,9 +179,9 @@ void InnerPointerChecker::checkFunctionArguments(const CallEvent &Call, if (!ArgRegion) continue; - // std::addressof function accepts a non-const reference as an argument, + // std::addressof functions accepts a non-const reference as an argument, // but doesn't modify it. - if (Call.isCalled(AddressofFn)) + if (matchesAny(Call, AddressofFn, AddressofFn_)) continue; markPtrSymbolsReleased(Call, State, ArgRegion, C); @@ -323,8 +320,7 @@ PathDiagnosticPieceRef InnerPointerChecker::InnerPointerBRVisitor::VisitNode( SmallString<256> Buf; llvm::raw_svector_ostream OS(Buf); - OS << "Pointer to inner buffer of '" << ObjTy.getAsString() - << "' obtained here"; + OS << "Pointer to inner buffer of '" << ObjTy << "' obtained here"; PathDiagnosticLocation Pos(S, BRC.getSourceManager(), N->getLocationContext()); return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true); diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/InvalidatedIteratorChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/InvalidatedIteratorChecker.cpp index 6955ba11a28f..3f5856a3efbe 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/InvalidatedIteratorChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/InvalidatedIteratorChecker.cpp @@ -31,14 +31,14 @@ class InvalidatedIteratorChecker check::PreStmt<ArraySubscriptExpr>, check::PreStmt<MemberExpr>> { - std::unique_ptr<BugType> InvalidatedBugType; + const BugType InvalidatedBugType{this, "Iterator invalidated", + "Misuse of STL APIs"}; - void verifyAccess(CheckerContext &C, const SVal &Val) const; - void reportBug(const StringRef &Message, const SVal &Val, - CheckerContext &C, ExplodedNode *ErrNode) const; -public: - InvalidatedIteratorChecker(); + void verifyAccess(CheckerContext &C, SVal Val) const; + void reportBug(StringRef Message, SVal Val, CheckerContext &C, + ExplodedNode *ErrNode) const; +public: void checkPreCall(const CallEvent &Call, CheckerContext &C) const; void checkPreStmt(const UnaryOperator *UO, CheckerContext &C) const; void checkPreStmt(const BinaryOperator *BO, CheckerContext &C) const; @@ -49,11 +49,6 @@ public: } //namespace -InvalidatedIteratorChecker::InvalidatedIteratorChecker() { - InvalidatedBugType.reset( - new BugType(this, "Iterator invalidated", "Misuse of STL APIs")); -} - void InvalidatedIteratorChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { // Check for access of invalidated position @@ -114,7 +109,8 @@ void InvalidatedIteratorChecker::checkPreStmt(const MemberExpr *ME, verifyAccess(C, BaseVal); } -void InvalidatedIteratorChecker::verifyAccess(CheckerContext &C, const SVal &Val) const { +void InvalidatedIteratorChecker::verifyAccess(CheckerContext &C, + SVal Val) const { auto State = C.getState(); const auto *Pos = getIteratorPosition(State, Val); if (Pos && !Pos->isValid()) { @@ -126,11 +122,11 @@ void InvalidatedIteratorChecker::verifyAccess(CheckerContext &C, const SVal &Val } } -void InvalidatedIteratorChecker::reportBug(const StringRef &Message, - const SVal &Val, CheckerContext &C, +void InvalidatedIteratorChecker::reportBug(StringRef Message, SVal Val, + CheckerContext &C, ExplodedNode *ErrNode) const { - auto R = std::make_unique<PathSensitiveBugReport>(*InvalidatedBugType, - Message, ErrNode); + auto R = std::make_unique<PathSensitiveBugReport>(InvalidatedBugType, Message, + ErrNode); R->markInteresting(Val); C.emitReport(std::move(R)); } diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Iterator.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Iterator.cpp index 496190149991..e8d35aac2efd 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Iterator.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Iterator.cpp @@ -29,8 +29,8 @@ bool isIterator(const CXXRecordDecl *CRD) { return false; const auto Name = CRD->getName(); - if (!(Name.endswith_insensitive("iterator") || - Name.endswith_insensitive("iter") || Name.endswith_insensitive("it"))) + if (!(Name.ends_with_insensitive("iterator") || + Name.ends_with_insensitive("iter") || Name.ends_with_insensitive("it"))) return false; bool HasCopyCtor = false, HasCopyAssign = true, HasDtor = false, @@ -181,8 +181,7 @@ const ContainerData *getContainerData(ProgramStateRef State, return State->get<ContainerMap>(Cont); } -const IteratorPosition *getIteratorPosition(ProgramStateRef State, - const SVal &Val) { +const IteratorPosition *getIteratorPosition(ProgramStateRef State, SVal Val) { if (auto Reg = Val.getAsRegion()) { Reg = Reg->getMostDerivedObjectRegion(); return State->get<IteratorRegionMap>(Reg); @@ -194,7 +193,7 @@ const IteratorPosition *getIteratorPosition(ProgramStateRef State, return nullptr; } -ProgramStateRef setIteratorPosition(ProgramStateRef State, const SVal &Val, +ProgramStateRef setIteratorPosition(ProgramStateRef State, SVal Val, const IteratorPosition &Pos) { if (auto Reg = Val.getAsRegion()) { Reg = Reg->getMostDerivedObjectRegion(); @@ -207,8 +206,8 @@ ProgramStateRef setIteratorPosition(ProgramStateRef State, const SVal &Val, return nullptr; } -ProgramStateRef createIteratorPosition(ProgramStateRef State, const SVal &Val, - const MemRegion *Cont, const Stmt* S, +ProgramStateRef createIteratorPosition(ProgramStateRef State, SVal Val, + const MemRegion *Cont, const Stmt *S, const LocationContext *LCtx, unsigned blockCount) { auto &StateMgr = State->getStateManager(); @@ -221,9 +220,8 @@ ProgramStateRef createIteratorPosition(ProgramStateRef State, const SVal &Val, IteratorPosition::getPosition(Cont, Sym)); } -ProgramStateRef advancePosition(ProgramStateRef State, const SVal &Iter, - OverloadedOperatorKind Op, - const SVal &Distance) { +ProgramStateRef advancePosition(ProgramStateRef State, SVal Iter, + OverloadedOperatorKind Op, SVal Distance) { const auto *Pos = getIteratorPosition(State, Iter); if (!Pos) return nullptr; @@ -308,8 +306,8 @@ bool compare(ProgramStateRef State, NonLoc NL1, NonLoc NL2, const auto comparison = SVB.evalBinOp(State, Opc, NL1, NL2, SVB.getConditionType()); - assert(comparison.getAs<DefinedSVal>() && - "Symbol comparison must be a `DefinedSVal`"); + assert(isa<DefinedSVal>(comparison) && + "Symbol comparison must be a `DefinedSVal`"); return !State->assume(comparison.castAs<DefinedSVal>(), false); } diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Iterator.h b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Iterator.h index 37157492fe3e..46de8ea01d77 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Iterator.h +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Iterator.h @@ -63,9 +63,7 @@ public: return Cont == X.Cont && Valid == X.Valid && Offset == X.Offset; } - bool operator!=(const IteratorPosition &X) const { - return Cont != X.Cont || Valid != X.Valid || Offset != X.Offset; - } + bool operator!=(const IteratorPosition &X) const { return !(*this == X); } void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddPointer(Cont); @@ -101,9 +99,7 @@ public: return Begin == X.Begin && End == X.End; } - bool operator!=(const ContainerData &X) const { - return Begin != X.Begin || End != X.End; - } + bool operator!=(const ContainerData &X) const { return !(*this == X); } void Profile(llvm::FoldingSetNodeID &ID) const { ID.Add(Begin); @@ -165,18 +161,15 @@ bool isRandomIncrOrDecrOperator(OverloadedOperatorKind OK); bool isRandomIncrOrDecrOperator(BinaryOperatorKind OK); const ContainerData *getContainerData(ProgramStateRef State, const MemRegion *Cont); -const IteratorPosition *getIteratorPosition(ProgramStateRef State, - const SVal &Val); -ProgramStateRef setIteratorPosition(ProgramStateRef State, const SVal &Val, +const IteratorPosition *getIteratorPosition(ProgramStateRef State, SVal Val); +ProgramStateRef setIteratorPosition(ProgramStateRef State, SVal Val, const IteratorPosition &Pos); -ProgramStateRef createIteratorPosition(ProgramStateRef State, const SVal &Val, - const MemRegion *Cont, const Stmt* S, +ProgramStateRef createIteratorPosition(ProgramStateRef State, SVal Val, + const MemRegion *Cont, const Stmt *S, const LocationContext *LCtx, unsigned blockCount); -ProgramStateRef advancePosition(ProgramStateRef State, - const SVal &Iter, - OverloadedOperatorKind Op, - const SVal &Distance); +ProgramStateRef advancePosition(ProgramStateRef State, SVal Iter, + OverloadedOperatorKind Op, SVal Distance); ProgramStateRef assumeNoOverflow(ProgramStateRef State, SymbolRef Sym, long Scale); bool compare(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2, diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IteratorModeling.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IteratorModeling.cpp index ab5e6a1c9991..a95e811c2a41 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IteratorModeling.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IteratorModeling.cpp @@ -64,13 +64,15 @@ // making an assumption e.g. `S1 + n == S2 + m` we store `S1 - S2 == m - n` as // a constraint which we later retrieve when doing an actual comparison. -#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/AST/DeclTemplate.h" +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.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/DynamicType.h" +#include "llvm/ADT/STLExtras.h" #include "Iterator.h" @@ -98,18 +100,17 @@ class IteratorModeling const AdvanceFn *Handler) const; void handleComparison(CheckerContext &C, const Expr *CE, SVal RetVal, - const SVal &LVal, const SVal &RVal, - OverloadedOperatorKind Op) const; + SVal LVal, SVal RVal, OverloadedOperatorKind Op) const; void processComparison(CheckerContext &C, ProgramStateRef State, - SymbolRef Sym1, SymbolRef Sym2, const SVal &RetVal, + SymbolRef Sym1, SymbolRef Sym2, SVal RetVal, OverloadedOperatorKind Op) const; - void handleIncrement(CheckerContext &C, const SVal &RetVal, const SVal &Iter, + void handleIncrement(CheckerContext &C, SVal RetVal, SVal Iter, bool Postfix) const; - void handleDecrement(CheckerContext &C, const SVal &RetVal, const SVal &Iter, + void handleDecrement(CheckerContext &C, SVal RetVal, SVal Iter, bool Postfix) const; void handleRandomIncrOrDecr(CheckerContext &C, const Expr *CE, - OverloadedOperatorKind Op, const SVal &RetVal, - const SVal &Iterator, const SVal &Amount) const; + OverloadedOperatorKind Op, SVal RetVal, + SVal Iterator, SVal Amount) const; void handlePtrIncrOrDecr(CheckerContext &C, const Expr *Iterator, OverloadedOperatorKind OK, SVal Offset) const; void handleAdvance(CheckerContext &C, const Expr *CE, SVal RetVal, SVal Iter, @@ -118,7 +119,7 @@ class IteratorModeling SVal Amount) const; void handleNext(CheckerContext &C, const Expr *CE, SVal RetVal, SVal Iter, SVal Amount) const; - void assignToContainer(CheckerContext &C, const Expr *CE, const SVal &RetVal, + void assignToContainer(CheckerContext &C, const Expr *CE, SVal RetVal, const MemRegion *Cont) const; bool noChangeInAdvance(CheckerContext &C, SVal Iter, const Expr *CE) const; void printState(raw_ostream &Out, ProgramStateRef State, const char *NL, @@ -150,8 +151,6 @@ public: void checkBind(SVal Loc, SVal Val, const Stmt *S, CheckerContext &C) const; void checkPostStmt(const UnaryOperator *UO, CheckerContext &C) const; void checkPostStmt(const BinaryOperator *BO, CheckerContext &C) const; - void checkPostStmt(const CXXConstructExpr *CCE, CheckerContext &C) const; - void checkPostStmt(const DeclStmt *DS, CheckerContext &C) const; void checkPostStmt(const MaterializeTemporaryExpr *MTE, CheckerContext &C) const; void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const; @@ -160,7 +159,7 @@ public: bool isSimpleComparisonOperator(OverloadedOperatorKind OK); bool isSimpleComparisonOperator(BinaryOperatorKind OK); -ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val); +ProgramStateRef removeIteratorPosition(ProgramStateRef State, SVal Val); ProgramStateRef relateSymbols(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2, bool Equal); bool isBoundThroughLazyCompoundVal(const Environment &Env, @@ -283,7 +282,7 @@ void IteratorModeling::checkPostStmt(const BinaryOperator *BO, // The non-iterator side must have an integral or enumeration type. if (!AmountExpr->getType()->isIntegralOrEnumerationType()) return; - const SVal &AmountVal = IsIterOnLHS ? RVal : LVal; + SVal AmountVal = IsIterOnLHS ? RVal : LVal; handlePtrIncrOrDecr(C, IterExpr, BinaryOperator::getOverloadedOperator(OK), AmountVal); } @@ -304,21 +303,18 @@ void IteratorModeling::checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const { // Keep symbolic expressions of iterator positions alive auto RegionMap = State->get<IteratorRegionMap>(); - for (const auto &Reg : RegionMap) { - const auto Offset = Reg.second.getOffset(); - for (auto i = Offset->symbol_begin(); i != Offset->symbol_end(); ++i) - if (isa<SymbolData>(*i)) - SR.markLive(*i); + for (const IteratorPosition &Pos : llvm::make_second_range(RegionMap)) { + for (SymbolRef Sym : Pos.getOffset()->symbols()) + if (isa<SymbolData>(Sym)) + SR.markLive(Sym); } auto SymbolMap = State->get<IteratorSymbolMap>(); - for (const auto &Sym : SymbolMap) { - const auto Offset = Sym.second.getOffset(); - for (auto i = Offset->symbol_begin(); i != Offset->symbol_end(); ++i) - if (isa<SymbolData>(*i)) - SR.markLive(*i); + for (const IteratorPosition &Pos : llvm::make_second_range(SymbolMap)) { + for (SymbolRef Sym : Pos.getOffset()->symbols()) + if (isa<SymbolData>(Sym)) + SR.markLive(Sym); } - } void IteratorModeling::checkDeadSymbols(SymbolReaper &SR, @@ -391,8 +387,8 @@ IteratorModeling::handleOverloadedOperator(CheckerContext &C, const bool IsIterFirst = FirstType->isStructureOrClassType(); const SVal FirstArg = Call.getArgSVal(0); const SVal SecondArg = Call.getArgSVal(1); - const SVal &Iterator = IsIterFirst ? FirstArg : SecondArg; - const SVal &Amount = IsIterFirst ? SecondArg : FirstArg; + SVal Iterator = IsIterFirst ? FirstArg : SecondArg; + SVal Amount = IsIterFirst ? SecondArg : FirstArg; handleRandomIncrOrDecr(C, OrigExpr, Op, Call.getReturnValue(), Iterator, Amount); @@ -447,14 +443,13 @@ IteratorModeling::handleAdvanceLikeFunction(CheckerContext &C, } void IteratorModeling::handleComparison(CheckerContext &C, const Expr *CE, - SVal RetVal, const SVal &LVal, - const SVal &RVal, - OverloadedOperatorKind Op) const { + SVal RetVal, SVal LVal, SVal RVal, + OverloadedOperatorKind Op) const { // Record the operands and the operator of the comparison for the next // evalAssume, if the result is a symbolic expression. If it is a concrete // value (only one branch is possible), then transfer the state between // the operands according to the operator and the result - auto State = C.getState(); + auto State = C.getState(); const auto *LPos = getIteratorPosition(State, LVal); const auto *RPos = getIteratorPosition(State, RVal); const MemRegion *Cont = nullptr; @@ -507,7 +502,7 @@ void IteratorModeling::handleComparison(CheckerContext &C, const Expr *CE, void IteratorModeling::processComparison(CheckerContext &C, ProgramStateRef State, SymbolRef Sym1, - SymbolRef Sym2, const SVal &RetVal, + SymbolRef Sym2, SVal RetVal, OverloadedOperatorKind Op) const { if (const auto TruthVal = RetVal.getAs<nonloc::ConcreteInt>()) { if ((State = relateSymbols(State, Sym1, Sym2, @@ -535,8 +530,8 @@ void IteratorModeling::processComparison(CheckerContext &C, } } -void IteratorModeling::handleIncrement(CheckerContext &C, const SVal &RetVal, - const SVal &Iter, bool Postfix) const { +void IteratorModeling::handleIncrement(CheckerContext &C, SVal RetVal, + SVal Iter, bool Postfix) const { // Increment the symbolic expressions which represents the position of the // iterator auto State = C.getState(); @@ -561,8 +556,8 @@ void IteratorModeling::handleIncrement(CheckerContext &C, const SVal &RetVal, C.addTransition(State); } -void IteratorModeling::handleDecrement(CheckerContext &C, const SVal &RetVal, - const SVal &Iter, bool Postfix) const { +void IteratorModeling::handleDecrement(CheckerContext &C, SVal RetVal, + SVal Iter, bool Postfix) const { // Decrement the symbolic expressions which represents the position of the // iterator auto State = C.getState(); @@ -589,9 +584,8 @@ void IteratorModeling::handleDecrement(CheckerContext &C, const SVal &RetVal, void IteratorModeling::handleRandomIncrOrDecr(CheckerContext &C, const Expr *CE, OverloadedOperatorKind Op, - const SVal &RetVal, - const SVal &Iterator, - const SVal &Amount) const { + SVal RetVal, SVal Iterator, + SVal Amount) const { // Increment or decrement the symbolic expressions which represents the // position of the iterator auto State = C.getState(); @@ -630,7 +624,7 @@ void IteratorModeling::handlePtrIncrOrDecr(CheckerContext &C, const Expr *Iterator, OverloadedOperatorKind OK, SVal Offset) const { - if (!Offset.getAs<DefinedSVal>()) + if (!isa<DefinedSVal>(Offset)) return; QualType PtrType = Iterator->getType(); @@ -687,7 +681,7 @@ void IteratorModeling::handleNext(CheckerContext &C, const Expr *CE, } void IteratorModeling::assignToContainer(CheckerContext &C, const Expr *CE, - const SVal &RetVal, + SVal RetVal, const MemRegion *Cont) const { Cont = Cont->getMostDerivedObjectRegion(); @@ -775,7 +769,7 @@ bool isSimpleComparisonOperator(BinaryOperatorKind OK) { return OK == BO_EQ || OK == BO_NE; } -ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val) { +ProgramStateRef removeIteratorPosition(ProgramStateRef State, SVal Val) { if (auto Reg = Val.getAsRegion()) { Reg = Reg->getMostDerivedObjectRegion(); return State->remove<IteratorRegionMap>(Reg); @@ -800,8 +794,8 @@ ProgramStateRef relateSymbols(ProgramStateRef State, SymbolRef Sym1, SVB.evalBinOp(State, BO_EQ, nonloc::SymbolVal(Sym1), nonloc::SymbolVal(Sym2), SVB.getConditionType()); - assert(comparison.getAs<DefinedSVal>() && - "Symbol comparison must be a `DefinedSVal`"); + assert(isa<DefinedSVal>(comparison) && + "Symbol comparison must be a `DefinedSVal`"); auto NewState = State->assume(comparison.castAs<DefinedSVal>(), Equal); if (!NewState) diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IteratorRangeChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IteratorRangeChecker.cpp index a47484497771..c8828219dd73 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IteratorRangeChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IteratorRangeChecker.cpp @@ -14,10 +14,10 @@ #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" - #include "Iterator.h" using namespace clang; @@ -32,7 +32,8 @@ class IteratorRangeChecker check::PreStmt<ArraySubscriptExpr>, check::PreStmt<MemberExpr>> { - std::unique_ptr<BugType> OutOfRangeBugType; + const BugType OutOfRangeBugType{this, "Iterator out of range", + "Misuse of STL APIs"}; void verifyDereference(CheckerContext &C, SVal Val) const; void verifyIncrement(CheckerContext &C, SVal Iter) const; @@ -42,12 +43,10 @@ class IteratorRangeChecker void verifyAdvance(CheckerContext &C, SVal LHS, SVal RHS) const; void verifyPrev(CheckerContext &C, SVal LHS, SVal RHS) const; void verifyNext(CheckerContext &C, SVal LHS, SVal RHS) const; - void reportBug(const StringRef &Message, SVal Val, CheckerContext &C, + void reportBug(StringRef Message, SVal Val, CheckerContext &C, ExplodedNode *ErrNode) const; public: - IteratorRangeChecker(); - void checkPreCall(const CallEvent &Call, CheckerContext &C) const; void checkPreStmt(const UnaryOperator *UO, CheckerContext &C) const; void checkPreStmt(const BinaryOperator *BO, CheckerContext &C) const; @@ -67,15 +66,10 @@ public: bool isPastTheEnd(ProgramStateRef State, const IteratorPosition &Pos); bool isAheadOfRange(ProgramStateRef State, const IteratorPosition &Pos); bool isBehindPastTheEnd(ProgramStateRef State, const IteratorPosition &Pos); -bool isZero(ProgramStateRef State, const NonLoc &Val); +bool isZero(ProgramStateRef State, NonLoc Val); } //namespace -IteratorRangeChecker::IteratorRangeChecker() { - OutOfRangeBugType.reset( - new BugType(this, "Iterator out of range", "Misuse of STL APIs")); -} - void IteratorRangeChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { // Check for out of range access @@ -228,7 +222,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. @@ -275,10 +269,10 @@ void IteratorRangeChecker::verifyNext(CheckerContext &C, SVal LHS, verifyRandomIncrOrDecr(C, OO_Plus, LHS, RHS); } -void IteratorRangeChecker::reportBug(const StringRef &Message, SVal Val, +void IteratorRangeChecker::reportBug(StringRef Message, SVal Val, CheckerContext &C, ExplodedNode *ErrNode) const { - auto R = std::make_unique<PathSensitiveBugReport>(*OutOfRangeBugType, Message, + auto R = std::make_unique<PathSensitiveBugReport>(OutOfRangeBugType, Message, ErrNode); const auto *Pos = getIteratorPosition(C.getState(), Val); @@ -295,7 +289,7 @@ bool isLess(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2); bool isGreater(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2); bool isEqual(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2); -bool isZero(ProgramStateRef State, const NonLoc &Val) { +bool isZero(ProgramStateRef State, NonLoc Val) { auto &BVF = State->getBasicVals(); return compare(State, Val, nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))), diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp index 3e6756efe0e6..f0276a57bdf9 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp @@ -27,14 +27,15 @@ // //===----------------------------------------------------------------------===// -#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/AST/Attr.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/StmtVisitor.h" +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" #include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/SmallString.h" @@ -44,9 +45,9 @@ using namespace ento; namespace { struct ChecksFilter { /// Check for missing invalidation method declarations. - DefaultBool check_MissingInvalidationMethod; + bool check_MissingInvalidationMethod = false; /// Check that all ivars are invalidated. - DefaultBool check_InstanceVariableInvalidation; + bool check_InstanceVariableInvalidation = false; CheckerNameRef checkName_MissingInvalidationMethod; CheckerNameRef checkName_InstanceVariableInvalidation; @@ -63,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); } @@ -80,9 +81,8 @@ class IvarInvalidationCheckerImpl { bool hasMethod(const ObjCMethodDecl *MD) { if (IsInvalidated) return true; - for (MethodSet::iterator I = InvalidationMethods.begin(), - E = InvalidationMethods.end(); I != E; ++I) { - if (*I == MD) { + for (const ObjCMethodDecl *Curr : InvalidationMethods) { + if (Curr == MD) { IsInvalidated = true; return true; } @@ -318,9 +318,7 @@ const ObjCIvarDecl *IvarInvalidationCheckerImpl::findPropertyBackingIvar( // Lookup IVars named "_PropName"or "PropName" among the tracked Ivars. StringRef PropName = Prop->getIdentifier()->getName(); - for (IvarSet::const_iterator I = TrackedIvars.begin(), - E = TrackedIvars.end(); I != E; ++I) { - const ObjCIvarDecl *Iv = I->first; + for (const ObjCIvarDecl *Iv : llvm::make_first_range(TrackedIvars)) { StringRef IvarName = Iv->getName(); if (IvarName == PropName) @@ -379,12 +377,9 @@ visit(const ObjCImplementationDecl *ImplD) const { IvarToPropMapTy IvarToPopertyMap; ObjCInterfaceDecl::PropertyMap PropMap; - ObjCInterfaceDecl::PropertyDeclOrder PropOrder; - InterfaceD->collectPropertiesToImplement(PropMap, PropOrder); + InterfaceD->collectPropertiesToImplement(PropMap); - for (ObjCInterfaceDecl::PropertyMap::iterator - I = PropMap.begin(), E = PropMap.end(); I != E; ++I) { - const ObjCPropertyDecl *PD = I->second; + for (const ObjCPropertyDecl *PD : llvm::make_second_range(PropMap)) { if (PD->isClassProperty()) continue; @@ -423,11 +418,7 @@ visit(const ObjCImplementationDecl *ImplD) const { // Remove ivars invalidated by the partial invalidation methods. They do not // need to be invalidated in the regular invalidation methods. bool AtImplementationContainsAtLeastOnePartialInvalidationMethod = false; - for (MethodSet::iterator - I = PartialInfo.InvalidationMethods.begin(), - E = PartialInfo.InvalidationMethods.end(); I != E; ++I) { - const ObjCMethodDecl *InterfD = *I; - + for (const ObjCMethodDecl *InterfD : PartialInfo.InvalidationMethods) { // Get the corresponding method in the @implementation. const ObjCMethodDecl *D = ImplD->getMethod(InterfD->getSelector(), InterfD->isInstanceMethod()); @@ -476,10 +467,7 @@ visit(const ObjCImplementationDecl *ImplD) const { // Check that all ivars are invalidated by the invalidation methods. bool AtImplementationContainsAtLeastOneInvalidationMethod = false; - for (MethodSet::iterator I = Info.InvalidationMethods.begin(), - E = Info.InvalidationMethods.end(); I != E; ++I) { - const ObjCMethodDecl *InterfD = *I; - + for (const ObjCMethodDecl *InterfD : Info.InvalidationMethods) { // Get the corresponding method in the @implementation. const ObjCMethodDecl *D = ImplD->getMethod(InterfD->getSelector(), InterfD->isInstanceMethod()); @@ -502,9 +490,8 @@ visit(const ObjCImplementationDecl *ImplD) const { continue; // Warn on the ivars that were not invalidated by the method. - for (IvarSet::const_iterator - I = IvarsI.begin(), E = IvarsI.end(); I != E; ++I) - reportIvarNeedsInvalidation(I->first, IvarToPopertyMap, D); + for (const ObjCIvarDecl *Ivar : llvm::make_first_range(IvarsI)) + reportIvarNeedsInvalidation(Ivar, IvarToPopertyMap, D); } } @@ -513,9 +500,8 @@ visit(const ObjCImplementationDecl *ImplD) const { if (AtImplementationContainsAtLeastOnePartialInvalidationMethod) { // Warn on the ivars that were not invalidated by the prrtial // invalidation methods. - for (IvarSet::const_iterator - I = Ivars.begin(), E = Ivars.end(); I != E; ++I) - reportIvarNeedsInvalidation(I->first, IvarToPopertyMap, nullptr); + for (const ObjCIvarDecl *Ivar : llvm::make_first_range(Ivars)) + reportIvarNeedsInvalidation(Ivar, IvarToPopertyMap, nullptr); } else { // Otherwise, no invalidation methods were implemented. reportNoInvalidationMethod(Filter.checkName_InstanceVariableInvalidation, diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/LLVMConventionsChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/LLVMConventionsChecker.cpp index 1f3d8844d330..fa51aa80216b 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/LLVMConventionsChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/LLVMConventionsChecker.cpp @@ -273,7 +273,7 @@ void ASTFieldVisitor::ReportError(QualType T) { os << (*I)->getName(); } } - os << " (type " << FieldChain.back()->getType().getAsString() << ")"; + os << " (type " << FieldChain.back()->getType() << ")"; // Note that this will fire for every translation unit that uses this // class. This is suboptimal, but at least scan-build will merge diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp index 28d3e058fee2..812d787e2e37 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp @@ -14,13 +14,13 @@ // //===----------------------------------------------------------------------===// -#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/AST/Attr.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/RecursiveASTVisitor.h" #include "clang/AST/StmtVisitor.h" #include "clang/Lex/Lexer.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" @@ -28,7 +28,9 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/Support/Unicode.h" +#include <optional> using namespace clang; using namespace ento; @@ -60,7 +62,8 @@ class NonLocalizedStringChecker check::PostObjCMessage, check::PostStmt<ObjCStringLiteral>> { - mutable std::unique_ptr<BugType> BT; + const BugType BT{this, "Unlocalizable string", + "Localizability Issue (Apple)"}; // Methods that require a localized string mutable llvm::DenseMap<const IdentifierInfo *, @@ -87,12 +90,10 @@ class NonLocalizedStringChecker Selector S) const; public: - NonLocalizedStringChecker(); - // When this parameter is set to true, the checker assumes all // methods that return NSStrings are unlocalized. Thus, more false // positives will be reported. - DefaultBool IsAggressive; + bool IsAggressive = false; void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const; void checkPostObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const; @@ -106,11 +107,6 @@ public: REGISTER_MAP_WITH_PROGRAMSTATE(LocalizedMemMap, const MemRegion *, LocalizedState) -NonLocalizedStringChecker::NonLocalizedStringChecker() { - BT.reset(new BugType(this, "Unlocalizable string", - "Localizability Issue (Apple)")); -} - namespace { class NonLocalizedStringBRVisitor final : public BugReporterVisitor { @@ -716,7 +712,7 @@ void NonLocalizedStringChecker::setNonLocalizedState(const SVal S, static bool isDebuggingName(std::string name) { - return StringRef(name).lower().find("debug") != StringRef::npos; + return StringRef(name).contains_insensitive("debug"); } /// Returns true when, heuristically, the analyzer may be analyzing debugging @@ -762,7 +758,7 @@ void NonLocalizedStringChecker::reportLocalizationError( // Generate the bug report. auto R = std::make_unique<PathSensitiveBugReport>( - *BT, "User-facing text should use localized string macro", ErrNode); + BT, "User-facing text should use localized string macro", ErrNode); if (argumentNumber) { R->addRange(M.getArgExpr(argumentNumber - 1)->getSourceRange()); } else { @@ -815,9 +811,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(); @@ -846,10 +842,9 @@ void NonLocalizedStringChecker::checkPreObjCMessage(const ObjCMethodCall &msg, if (argumentNumber < 0) { // There was no match in UIMethods if (const Decl *D = msg.getDecl()) { if (const ObjCMethodDecl *OMD = dyn_cast_or_null<ObjCMethodDecl>(D)) { - auto formals = OMD->parameters(); - for (unsigned i = 0, ei = formals.size(); i != ei; ++i) { - if (isAnnotatedAsTakingLocalized(formals[i])) { - argumentNumber = i; + for (auto [Idx, FormalParam] : llvm::enumerate(OMD->parameters())) { + if (isAnnotatedAsTakingLocalized(FormalParam)) { + argumentNumber = Idx; break; } } @@ -949,7 +944,7 @@ void NonLocalizedStringChecker::checkPostCall(const CallEvent &Call, const IdentifierInfo *Identifier = Call.getCalleeIdentifier(); SVal sv = Call.getReturnValue(); - if (isAnnotatedAsReturningLocalized(D) || LSF.count(Identifier) != 0) { + if (isAnnotatedAsReturningLocalized(D) || LSF.contains(Identifier)) { setLocalizedState(sv, C); } else if (isNSStringType(RT, C.getASTContext()) && !hasLocalizedState(sv, C)) { @@ -1004,8 +999,8 @@ NonLocalizedStringBRVisitor::VisitNode(const ExplodedNode *Succ, if (Satisfied) return nullptr; - Optional<StmtPoint> Point = Succ->getLocation().getAs<StmtPoint>(); - if (!Point.hasValue()) + std::optional<StmtPoint> Point = Succ->getLocation().getAs<StmtPoint>(); + if (!Point) return nullptr; auto *LiteralExpr = dyn_cast<ObjCStringLiteral>(Point->getStmt()); @@ -1141,12 +1136,12 @@ void EmptyLocalizationContextChecker::MethodCrawler::VisitObjCMessageExpr( SE = Mgr.getSourceManager().getSLocEntry(SLInfo.first); } - llvm::Optional<llvm::MemoryBufferRef> BF = + std::optional<llvm::MemoryBufferRef> BF = Mgr.getSourceManager().getBufferOrNone(SLInfo.first, SL); if (!BF) return; - - Lexer TheLexer(SL, LangOptions(), BF->getBufferStart(), + LangOptions LangOpts; + Lexer TheLexer(SL, LangOpts, BF->getBufferStart(), BF->getBufferStart() + SLInfo.second, BF->getBufferEnd()); Token I; @@ -1253,8 +1248,8 @@ bool PluralMisuseChecker::MethodCrawler::isCheckingPlurality( BO = B; } } - if (VD->getName().lower().find("plural") != StringRef::npos || - VD->getName().lower().find("singular") != StringRef::npos) { + if (VD->getName().contains_insensitive("plural") || + VD->getName().contains_insensitive("singular")) { return true; } } @@ -1339,7 +1334,10 @@ bool PluralMisuseChecker::MethodCrawler::EndVisitIfStmt(IfStmt *I) { } bool PluralMisuseChecker::MethodCrawler::VisitIfStmt(const IfStmt *I) { - const Expr *Condition = I->getCond()->IgnoreParenImpCasts(); + const Expr *Condition = I->getCond(); + if (!Condition) + return true; + Condition = Condition->IgnoreParenImpCasts(); if (isCheckingPlurality(Condition)) { MatchingStatements.push_back(I); InMatchingStatement = true; diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MIGChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MIGChecker.cpp index b72d72580c28..153a0a51e980 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MIGChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MIGChecker.cpp @@ -27,8 +27,10 @@ #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 <optional> using namespace clang; using namespace ento; @@ -85,7 +87,7 @@ class MIGChecker : public Checker<check::PostCall, check::PreStmt<ReturnStmt>, #undef CALL }; - CallDescription OsRefRetain{"os_ref_retain", 1}; + CallDescription OsRefRetain{{"os_ref_retain"}, 1}; void checkReturnAux(const ReturnStmt *RS, CheckerContext &C) const; @@ -156,10 +158,10 @@ static bool isInMIGCall(CheckerContext &C) { const Decl *D = SFC->getDecl(); - if (Optional<AnyCall> AC = AnyCall::forDecl(D)) { + if (std::optional<AnyCall> AC = AnyCall::forDecl(D)) { // Even though there's a Sema warning when the return type of an annotated // function is not a kern_return_t, this warning isn't an error, so we need - // an extra sanity check here. + // an extra check here. // FIXME: AnyCall doesn't support blocks yet, so they remain unchecked // for now. if (!AC->getReturnType(C.getASTContext()) @@ -180,7 +182,7 @@ static bool isInMIGCall(CheckerContext &C) { } void MIGChecker::checkPostCall(const CallEvent &Call, CheckerContext &C) const { - if (Call.isCalled(OsRefRetain)) { + if (OsRefRetain.matches(Call)) { // If the code is doing reference counting over the parameter, // it opens up an opportunity for safely calling a destructor function. // TODO: We should still check for over-releases. @@ -198,7 +200,7 @@ void MIGChecker::checkPostCall(const CallEvent &Call, CheckerContext &C) const { auto I = llvm::find_if(Deallocators, [&](const std::pair<CallDescription, unsigned> &Item) { - return Call.isCalled(Item.first); + return Item.first.matches(Call); }); if (I == Deallocators.end()) return; diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.cpp index bbf2ddec5762..3e374e6c240e 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.cpp @@ -31,7 +31,7 @@ void MPIBugReporter::reportDoubleNonblocking( RequestRegion->getDescriptiveName() + ". "; auto Report = std::make_unique<PathSensitiveBugReport>( - *DoubleNonblockingBugType, ErrorText, ExplNode); + DoubleNonblockingBugType, ErrorText, ExplNode); Report->addRange(MPICallEvent.getSourceRange()); SourceRange Range = RequestRegion->sourceRange(); @@ -53,7 +53,7 @@ void MPIBugReporter::reportMissingWait( std::string ErrorText{"Request " + RequestRegion->getDescriptiveName() + " has no matching wait. "}; - auto Report = std::make_unique<PathSensitiveBugReport>(*MissingWaitBugType, + auto Report = std::make_unique<PathSensitiveBugReport>(MissingWaitBugType, ErrorText, ExplNode); SourceRange Range = RequestRegion->sourceRange(); @@ -73,7 +73,7 @@ void MPIBugReporter::reportUnmatchedWait( std::string ErrorText{"Request " + RequestRegion->getDescriptiveName() + " has no matching nonblocking call. "}; - auto Report = std::make_unique<PathSensitiveBugReport>(*UnmatchedWaitBugType, + auto Report = std::make_unique<PathSensitiveBugReport>(UnmatchedWaitBugType, ErrorText, ExplNode); Report->addRange(CE.getSourceRange()); diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.h b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.h index 9871da026b04..0222a2120b34 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.h +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.h @@ -17,6 +17,7 @@ #include "MPITypes.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "llvm/ADT/StringRef.h" namespace clang { namespace ento { @@ -24,12 +25,10 @@ namespace mpi { class MPIBugReporter { public: - MPIBugReporter(const CheckerBase &CB) { - UnmatchedWaitBugType.reset(new BugType(&CB, "Unmatched wait", MPIError)); - DoubleNonblockingBugType.reset( - new BugType(&CB, "Double nonblocking", MPIError)); - MissingWaitBugType.reset(new BugType(&CB, "Missing wait", MPIError)); - } + MPIBugReporter(const CheckerBase &CB) + : UnmatchedWaitBugType(&CB, "Unmatched wait", MPIError), + MissingWaitBugType(&CB, "Missing wait", MPIError), + DoubleNonblockingBugType(&CB, "Double nonblocking", MPIError) {} /// Report duplicate request use by nonblocking calls without intermediate /// wait. @@ -68,12 +67,10 @@ public: BugReporter &BReporter) const; private: - const std::string MPIError = "MPI Error"; - - // path-sensitive bug types - std::unique_ptr<BugType> UnmatchedWaitBugType; - std::unique_ptr<BugType> MissingWaitBugType; - std::unique_ptr<BugType> DoubleNonblockingBugType; + const llvm::StringLiteral MPIError = "MPI Error"; + const BugType UnmatchedWaitBugType; + const BugType MissingWaitBugType; + const BugType DoubleNonblockingBugType; /// Bug visitor class to find the node where the request region was previously /// used in order to include it into the BugReport path. diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.cpp index 5d6bd381d3cc..4c0a8ba2c7c0 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.cpp @@ -165,7 +165,7 @@ void MPIChecker::allRegionsUsedByWait( Ctx.getState(), SuperRegion, Ctx.getSValBuilder(), CE.getArgExpr(1)->getType()->getPointeeType()); const llvm::APSInt &ArrSize = - ElementCount.getAs<nonloc::ConcreteInt>()->getValue(); + ElementCount.castAs<nonloc::ConcreteInt>().getValue(); for (size_t i = 0; i < ArrSize; ++i) { const NonLoc Idx = Ctx.getSValBuilder().makeArrayIndex(i); diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp index a157ee2da5df..12bf12a0b232 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp @@ -19,8 +19,10 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/raw_ostream.h" +#include <optional> using namespace clang; using namespace ento; @@ -31,7 +33,8 @@ class MacOSKeychainAPIChecker : public Checker<check::PreStmt<CallExpr>, check::DeadSymbols, check::PointerEscape, eval::Assume> { - mutable std::unique_ptr<BugType> BT; + const BugType BT{this, "Improper use of SecKeychain API", + categories::AppleAPIMisuse}; public: /// AllocationState is a part of the checker specific state together with the @@ -99,12 +102,6 @@ private: /// function. static unsigned getTrackedFunctionIndex(StringRef Name, bool IsAllocator); - inline void initBugType() const { - if (!BT) - BT.reset(new BugType(this, "Improper use of SecKeychain API", - "API Misuse (Apple)")); - } - void generateDeallocatorMismatchReport(const AllocationPair &AP, const Expr *ArgExpr, CheckerContext &C) const; @@ -160,7 +157,7 @@ static bool isEnclosingFunctionParam(const Expr *E) { E = E->IgnoreParenCasts(); if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) { const ValueDecl *VD = DRE->getDecl(); - if (isa<ImplicitParamDecl>(VD) || isa<ParmVarDecl>(VD)) + if (isa<ImplicitParamDecl, ParmVarDecl>(VD)) return true; } return false; @@ -199,8 +196,7 @@ unsigned MacOSKeychainAPIChecker::getTrackedFunctionIndex(StringRef Name, static bool isBadDeallocationArgument(const MemRegion *Arg) { if (!Arg) return false; - return isa<AllocaRegion>(Arg) || isa<BlockDataRegion>(Arg) || - isa<TypedRegion>(Arg); + return isa<AllocaRegion, BlockDataRegion, TypedRegion>(Arg); } /// Given the address expression, retrieve the value it's pointing to. Assume @@ -210,7 +206,7 @@ static SymbolRef getAsPointeeSymbol(const Expr *Expr, ProgramStateRef State = C.getState(); SVal ArgV = C.getSVal(Expr); - if (Optional<loc::MemRegionVal> X = ArgV.getAs<loc::MemRegionVal>()) { + if (std::optional<loc::MemRegionVal> X = ArgV.getAs<loc::MemRegionVal>()) { StoreManager& SM = C.getStoreManager(); SymbolRef sym = SM.getBinding(State->getStore(), *X).getAsLocSymbol(); if (sym) @@ -231,7 +227,6 @@ void MacOSKeychainAPIChecker:: if (!N) return; - initBugType(); SmallString<80> sbuf; llvm::raw_svector_ostream os(sbuf); unsigned int PDeallocIdx = @@ -239,7 +234,7 @@ void MacOSKeychainAPIChecker:: os << "Deallocator doesn't match the allocator: '" << FunctionsToTrack[PDeallocIdx].Name << "' should be used."; - auto Report = std::make_unique<PathSensitiveBugReport>(*BT, os.str(), N); + auto Report = std::make_unique<PathSensitiveBugReport>(BT, os.str(), N); Report->addVisitor(std::make_unique<SecKeychainBugVisitor>(AP.first)); Report->addRange(ArgExpr->getSourceRange()); markInteresting(Report.get(), AP); @@ -275,7 +270,6 @@ void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE, ExplodedNode *N = C.generateNonFatalErrorNode(State); if (!N) return; - initBugType(); SmallString<128> sbuf; llvm::raw_svector_ostream os(sbuf); unsigned int DIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx; @@ -283,8 +277,7 @@ void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE, << "the allocator: missing a call to '" << FunctionsToTrack[DIdx].Name << "'."; - auto Report = - std::make_unique<PathSensitiveBugReport>(*BT, os.str(), N); + auto Report = std::make_unique<PathSensitiveBugReport>(BT, os.str(), N); Report->addVisitor(std::make_unique<SecKeychainBugVisitor>(V)); Report->addRange(ArgExpr->getSourceRange()); Report->markInteresting(AS->Region); @@ -337,9 +330,8 @@ void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE, ExplodedNode *N = C.generateNonFatalErrorNode(State); if (!N) return; - initBugType(); auto Report = std::make_unique<PathSensitiveBugReport>( - *BT, "Trying to free data which has not been allocated.", N); + BT, "Trying to free data which has not been allocated.", N); Report->addRange(ArgExpr->getSourceRange()); if (AS) Report->markInteresting(AS->Region); @@ -473,7 +465,6 @@ std::unique_ptr<PathSensitiveBugReport> MacOSKeychainAPIChecker::generateAllocatedDataNotReleasedReport( const AllocationPair &AP, ExplodedNode *N, CheckerContext &C) const { const ADFunctionInfo &FI = FunctionsToTrack[AP.second->AllocatorIdx]; - initBugType(); SmallString<70> sbuf; llvm::raw_svector_ostream os(sbuf); os << "Allocated data is not released: missing a call to '" @@ -492,7 +483,7 @@ MacOSKeychainAPIChecker::generateAllocatedDataNotReleasedReport( AllocNode->getLocationContext()); auto Report = std::make_unique<PathSensitiveBugReport>( - *BT, os.str(), N, LocUsedForUniqueing, + BT, os.str(), N, LocUsedForUniqueing, AllocNode->getLocationContext()->getDecl()); Report->addVisitor(std::make_unique<SecKeychainBugVisitor>(AP.first)); @@ -530,9 +521,9 @@ ProgramStateRef MacOSKeychainAPIChecker::evalAssume(ProgramStateRef State, } if (ReturnSymbol) - for (auto I = AMap.begin(), E = AMap.end(); I != E; ++I) { - if (ReturnSymbol == I->second.Region) - State = State->remove<AllocatedData>(I->first); + for (auto [Sym, AllocState] : AMap) { + if (ReturnSymbol == AllocState.Region) + State = State->remove<AllocatedData>(Sym); } return State; @@ -547,18 +538,18 @@ void MacOSKeychainAPIChecker::checkDeadSymbols(SymbolReaper &SR, bool Changed = false; AllocationPairVec Errors; - for (auto I = AMap.begin(), E = AMap.end(); I != E; ++I) { - if (!SR.isDead(I->first)) + for (const auto &[Sym, AllocState] : AMap) { + if (!SR.isDead(Sym)) continue; Changed = true; - State = State->remove<AllocatedData>(I->first); + State = State->remove<AllocatedData>(Sym); // If the allocated symbol is null do not report. ConstraintManager &CMgr = State->getConstraintManager(); - ConditionTruthVal AllocFailed = CMgr.isNull(State, I.getKey()); + ConditionTruthVal AllocFailed = CMgr.isNull(State, Sym); if (AllocFailed.isConstrainedTrue()) continue; - Errors.push_back(std::make_pair(I->first, &I->second)); + Errors.push_back(std::make_pair(Sym, &AllocState)); } if (!Changed) { // Generate the new, cleaned up state. @@ -656,8 +647,8 @@ void MacOSKeychainAPIChecker::printState(raw_ostream &Out, if (!AMap.isEmpty()) { Out << Sep << "KeychainAPIChecker :" << NL; - for (auto I = AMap.begin(), E = AMap.end(); I != E; ++I) { - I.getKey()->dumpToStream(Out); + for (SymbolRef Sym : llvm::make_first_range(AMap)) { + Sym->dumpToStream(Out); } } } diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp index 04e7f8dec8d7..754b16764296 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp @@ -18,6 +18,7 @@ #include "clang/Basic/TargetInfo.h" #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" @@ -31,7 +32,8 @@ using namespace ento; namespace { class MacOSXAPIChecker : public Checker< check::PreStmt<CallExpr> > { - mutable std::unique_ptr<BugType> BT_dispatchOnce; + const BugType BT_dispatchOnce{this, "Improper use of 'dispatch_once'", + categories::AppleAPIMisuse}; static const ObjCIvarRegion *getParentIvarRegion(const MemRegion *R); @@ -136,12 +138,8 @@ void MacOSXAPIChecker::CheckDispatchOnce(CheckerContext &C, const CallExpr *CE, if (!N) return; - if (!BT_dispatchOnce) - BT_dispatchOnce.reset(new BugType(this, "Improper use of 'dispatch_once'", - "API Misuse (Apple)")); - auto report = - std::make_unique<PathSensitiveBugReport>(*BT_dispatchOnce, os.str(), N); + std::make_unique<PathSensitiveBugReport>(BT_dispatchOnce, os.str(), N); report->addRange(CE->getArg(0)->getSourceRange()); C.emitReport(std::move(report)); } diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp index a6470da09c45..79ab05f2c786 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -48,9 +48,13 @@ #include "InterCheckerAPI.h" #include "clang/AST/Attr.h" #include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclTemplate.h" #include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/ParentMap.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/ASTMatchers/ASTMatchers.h" +#include "clang/Analysis/ProgramPoint.h" #include "clang/Basic/LLVM.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/TargetInfo.h" @@ -60,22 +64,29 @@ #include "clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.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/CheckerHelpers.h" #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/StoreRef.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SetOperations.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" +#include "llvm/Support/Casting.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_ostream.h" #include <climits> #include <functional> +#include <optional> #include <utility> using namespace clang; @@ -210,10 +221,10 @@ static bool isReleased(SymbolRef Sym, CheckerContext &C); /// Update the RefState to reflect the new memory allocation. /// The optional \p RetVal parameter specifies the newly allocated pointer /// value; if unspecified, the value of expression \p E is used. -static ProgramStateRef MallocUpdateRefState(CheckerContext &C, const Expr *E, - ProgramStateRef State, - AllocationFamily Family, - Optional<SVal> RetVal = None); +static ProgramStateRef +MallocUpdateRefState(CheckerContext &C, const Expr *E, ProgramStateRef State, + AllocationFamily Family, + std::optional<SVal> RetVal = std::nullopt); //===----------------------------------------------------------------------===// // The modeling of memory reallocation. @@ -296,7 +307,9 @@ public: /// functions might free the memory. /// In optimistic mode, the checker assumes that all user-defined functions /// which might free a pointer are annotated. - DefaultBool ShouldIncludeOwnershipAnnotatedFunctions; + bool ShouldIncludeOwnershipAnnotatedFunctions = false; + + bool ShouldRegisterNoOwnershipChangeVisitor = false; /// Many checkers are essentially built into this one, so enabling them will /// make MallocChecker perform additional modeling and reporting. @@ -314,7 +327,7 @@ public: using LeakInfo = std::pair<const ExplodedNode *, const MemRegion *>; - DefaultBool ChecksEnabled[CK_NumCheckKinds]; + bool ChecksEnabled[CK_NumCheckKinds] = {false}; CheckerNameRef CheckNames[CK_NumCheckKinds]; void checkPreCall(const CallEvent &Call, CheckerContext &C) const; @@ -379,70 +392,72 @@ private: const CallEvent &Call, CheckerContext &C)>; const CallDescriptionMap<CheckFn> FreeingMemFnMap{ - {{"free", 1}, &MallocChecker::checkFree}, - {{"if_freenameindex", 1}, &MallocChecker::checkIfFreeNameIndex}, - {{"kfree", 1}, &MallocChecker::checkFree}, - {{"g_free", 1}, &MallocChecker::checkFree}, + {{{"free"}, 1}, &MallocChecker::checkFree}, + {{{"if_freenameindex"}, 1}, &MallocChecker::checkIfFreeNameIndex}, + {{{"kfree"}, 1}, &MallocChecker::checkFree}, + {{{"g_free"}, 1}, &MallocChecker::checkFree}, }; bool isFreeingCall(const CallEvent &Call) const; + static bool isFreeingOwnershipAttrCall(const FunctionDecl *Func); + + friend class NoOwnershipChangeVisitor; CallDescriptionMap<CheckFn> AllocatingMemFnMap{ - {{"alloca", 1}, &MallocChecker::checkAlloca}, - {{"_alloca", 1}, &MallocChecker::checkAlloca}, - {{"malloc", 1}, &MallocChecker::checkBasicAlloc}, - {{"malloc", 3}, &MallocChecker::checkKernelMalloc}, - {{"calloc", 2}, &MallocChecker::checkCalloc}, - {{"valloc", 1}, &MallocChecker::checkBasicAlloc}, - {{CDF_MaybeBuiltin, "strndup", 2}, &MallocChecker::checkStrdup}, - {{CDF_MaybeBuiltin, "strdup", 1}, &MallocChecker::checkStrdup}, - {{"_strdup", 1}, &MallocChecker::checkStrdup}, - {{"kmalloc", 2}, &MallocChecker::checkKernelMalloc}, - {{"if_nameindex", 1}, &MallocChecker::checkIfNameIndex}, - {{CDF_MaybeBuiltin, "wcsdup", 1}, &MallocChecker::checkStrdup}, - {{CDF_MaybeBuiltin, "_wcsdup", 1}, &MallocChecker::checkStrdup}, - {{"g_malloc", 1}, &MallocChecker::checkBasicAlloc}, - {{"g_malloc0", 1}, &MallocChecker::checkGMalloc0}, - {{"g_try_malloc", 1}, &MallocChecker::checkBasicAlloc}, - {{"g_try_malloc0", 1}, &MallocChecker::checkGMalloc0}, - {{"g_memdup", 2}, &MallocChecker::checkGMemdup}, - {{"g_malloc_n", 2}, &MallocChecker::checkGMallocN}, - {{"g_malloc0_n", 2}, &MallocChecker::checkGMallocN0}, - {{"g_try_malloc_n", 2}, &MallocChecker::checkGMallocN}, - {{"g_try_malloc0_n", 2}, &MallocChecker::checkGMallocN0}, + {{{"alloca"}, 1}, &MallocChecker::checkAlloca}, + {{{"_alloca"}, 1}, &MallocChecker::checkAlloca}, + {{{"malloc"}, 1}, &MallocChecker::checkBasicAlloc}, + {{{"malloc"}, 3}, &MallocChecker::checkKernelMalloc}, + {{{"calloc"}, 2}, &MallocChecker::checkCalloc}, + {{{"valloc"}, 1}, &MallocChecker::checkBasicAlloc}, + {{CDF_MaybeBuiltin, {"strndup"}, 2}, &MallocChecker::checkStrdup}, + {{CDF_MaybeBuiltin, {"strdup"}, 1}, &MallocChecker::checkStrdup}, + {{{"_strdup"}, 1}, &MallocChecker::checkStrdup}, + {{{"kmalloc"}, 2}, &MallocChecker::checkKernelMalloc}, + {{{"if_nameindex"}, 1}, &MallocChecker::checkIfNameIndex}, + {{CDF_MaybeBuiltin, {"wcsdup"}, 1}, &MallocChecker::checkStrdup}, + {{CDF_MaybeBuiltin, {"_wcsdup"}, 1}, &MallocChecker::checkStrdup}, + {{{"g_malloc"}, 1}, &MallocChecker::checkBasicAlloc}, + {{{"g_malloc0"}, 1}, &MallocChecker::checkGMalloc0}, + {{{"g_try_malloc"}, 1}, &MallocChecker::checkBasicAlloc}, + {{{"g_try_malloc0"}, 1}, &MallocChecker::checkGMalloc0}, + {{{"g_memdup"}, 2}, &MallocChecker::checkGMemdup}, + {{{"g_malloc_n"}, 2}, &MallocChecker::checkGMallocN}, + {{{"g_malloc0_n"}, 2}, &MallocChecker::checkGMallocN0}, + {{{"g_try_malloc_n"}, 2}, &MallocChecker::checkGMallocN}, + {{{"g_try_malloc0_n"}, 2}, &MallocChecker::checkGMallocN0}, }; CallDescriptionMap<CheckFn> ReallocatingMemFnMap{ - {{"realloc", 2}, + {{{"realloc"}, 2}, std::bind(&MallocChecker::checkRealloc, _1, _2, _3, false)}, - {{"reallocf", 2}, + {{{"reallocf"}, 2}, std::bind(&MallocChecker::checkRealloc, _1, _2, _3, true)}, - {{"g_realloc", 2}, + {{{"g_realloc"}, 2}, std::bind(&MallocChecker::checkRealloc, _1, _2, _3, false)}, - {{"g_try_realloc", 2}, + {{{"g_try_realloc"}, 2}, std::bind(&MallocChecker::checkRealloc, _1, _2, _3, false)}, - {{"g_realloc_n", 3}, &MallocChecker::checkReallocN}, - {{"g_try_realloc_n", 3}, &MallocChecker::checkReallocN}, + {{{"g_realloc_n"}, 3}, &MallocChecker::checkReallocN}, + {{{"g_try_realloc_n"}, 3}, &MallocChecker::checkReallocN}, }; bool isMemCall(const CallEvent &Call) const; // TODO: Remove mutable by moving the initializtaion to the registry function. - mutable Optional<uint64_t> KernelZeroFlagVal; + mutable std::optional<uint64_t> KernelZeroFlagVal; - using KernelZeroSizePtrValueTy = Optional<int>; + using KernelZeroSizePtrValueTy = std::optional<int>; /// Store the value of macro called `ZERO_SIZE_PTR`. /// The value is initialized at first use, before first use the outer /// Optional is empty, afterwards it contains another Optional that indicates /// if the macro value could be determined, and if yes the value itself. - mutable Optional<KernelZeroSizePtrValueTy> KernelZeroSizePtrValue; + mutable std::optional<KernelZeroSizePtrValueTy> KernelZeroSizePtrValue; /// Process C++ operator new()'s allocation, which is the part of C++ /// new-expression that goes before the constructor. - LLVM_NODISCARD - ProgramStateRef processNewAllocation(const CXXAllocatorCall &Call, - CheckerContext &C, - AllocationFamily Family) const; + [[nodiscard]] ProgramStateRef + processNewAllocation(const CXXAllocatorCall &Call, CheckerContext &C, + AllocationFamily Family) const; /// Perform a zero-allocation check. /// @@ -452,11 +467,10 @@ private: /// 0. /// \param [in] RetVal Specifies the newly allocated pointer value; /// if unspecified, the value of expression \p E is used. - LLVM_NODISCARD - static ProgramStateRef ProcessZeroAllocCheck(const CallEvent &Call, - const unsigned IndexOfSizeArg, - ProgramStateRef State, - Optional<SVal> RetVal = None); + [[nodiscard]] static ProgramStateRef + ProcessZeroAllocCheck(const CallEvent &Call, const unsigned IndexOfSizeArg, + ProgramStateRef State, + std::optional<SVal> RetVal = std::nullopt); /// Model functions with the ownership_returns attribute. /// @@ -474,10 +488,9 @@ private: /// \param [in] Att The ownership_returns attribute. /// \param [in] State The \c ProgramState right before allocation. /// \returns The ProgramState right after allocation. - LLVM_NODISCARD - ProgramStateRef MallocMemReturnsAttr(CheckerContext &C, const CallEvent &Call, - const OwnershipAttr *Att, - ProgramStateRef State) const; + [[nodiscard]] ProgramStateRef + MallocMemReturnsAttr(CheckerContext &C, const CallEvent &Call, + const OwnershipAttr *Att, ProgramStateRef State) const; /// Models memory allocation. /// @@ -488,11 +501,9 @@ private: /// malloc leaves it undefined. /// \param [in] State The \c ProgramState right before allocation. /// \returns The ProgramState right after allocation. - LLVM_NODISCARD - static ProgramStateRef MallocMemAux(CheckerContext &C, const CallEvent &Call, - const Expr *SizeEx, SVal Init, - ProgramStateRef State, - AllocationFamily Family); + [[nodiscard]] static ProgramStateRef + MallocMemAux(CheckerContext &C, const CallEvent &Call, const Expr *SizeEx, + SVal Init, ProgramStateRef State, AllocationFamily Family); /// Models memory allocation. /// @@ -503,16 +514,13 @@ private: /// malloc leaves it undefined. /// \param [in] State The \c ProgramState right before allocation. /// \returns The ProgramState right after allocation. - LLVM_NODISCARD - static ProgramStateRef MallocMemAux(CheckerContext &C, const CallEvent &Call, - SVal Size, SVal Init, - ProgramStateRef State, - AllocationFamily Family); + [[nodiscard]] static ProgramStateRef + MallocMemAux(CheckerContext &C, const CallEvent &Call, SVal Size, SVal Init, + ProgramStateRef State, AllocationFamily Family); // Check if this malloc() for special flags. At present that means M_ZERO or // __GFP_ZERO (in which case, treat it like calloc). - LLVM_NODISCARD - llvm::Optional<ProgramStateRef> + [[nodiscard]] std::optional<ProgramStateRef> performKernelMalloc(const CallEvent &Call, CheckerContext &C, const ProgramStateRef &State) const; @@ -533,10 +541,10 @@ private: /// \param [in] Att The ownership_takes or ownership_holds attribute. /// \param [in] State The \c ProgramState right before allocation. /// \returns The ProgramState right after deallocation. - LLVM_NODISCARD - ProgramStateRef FreeMemAttr(CheckerContext &C, const CallEvent &Call, - const OwnershipAttr *Att, - ProgramStateRef State) const; + [[nodiscard]] ProgramStateRef FreeMemAttr(CheckerContext &C, + const CallEvent &Call, + const OwnershipAttr *Att, + ProgramStateRef State) const; /// Models memory deallocation. /// @@ -557,12 +565,10 @@ private: /// \param [in] ReturnsNullOnFailure Whether the memory deallocation function /// we're modeling returns with Null on failure. /// \returns The ProgramState right after deallocation. - LLVM_NODISCARD - ProgramStateRef FreeMemAux(CheckerContext &C, const CallEvent &Call, - ProgramStateRef State, unsigned Num, bool Hold, - bool &IsKnownToBeAllocated, - AllocationFamily Family, - bool ReturnsNullOnFailure = false) const; + [[nodiscard]] ProgramStateRef + FreeMemAux(CheckerContext &C, const CallEvent &Call, ProgramStateRef State, + unsigned Num, bool Hold, bool &IsKnownToBeAllocated, + AllocationFamily Family, bool ReturnsNullOnFailure = false) const; /// Models memory deallocation. /// @@ -583,12 +589,10 @@ private: /// \param [in] ReturnsNullOnFailure Whether the memory deallocation function /// we're modeling returns with Null on failure. /// \returns The ProgramState right after deallocation. - LLVM_NODISCARD - ProgramStateRef FreeMemAux(CheckerContext &C, const Expr *ArgExpr, - const CallEvent &Call, ProgramStateRef State, - bool Hold, bool &IsKnownToBeAllocated, - AllocationFamily Family, - bool ReturnsNullOnFailure = false) const; + [[nodiscard]] ProgramStateRef + FreeMemAux(CheckerContext &C, const Expr *ArgExpr, const CallEvent &Call, + ProgramStateRef State, bool Hold, bool &IsKnownToBeAllocated, + AllocationFamily Family, bool ReturnsNullOnFailure = false) const; // TODO: Needs some refactoring, as all other deallocation modeling // functions are suffering from out parameters and messy code due to how @@ -603,29 +607,27 @@ private: /// \param [in] SuffixWithN Whether the reallocation function we're modeling /// has an '_n' suffix, such as g_realloc_n. /// \returns The ProgramState right after reallocation. - LLVM_NODISCARD - ProgramStateRef ReallocMemAux(CheckerContext &C, const CallEvent &Call, - bool ShouldFreeOnFail, ProgramStateRef State, - AllocationFamily Family, - bool SuffixWithN = false) const; + [[nodiscard]] ProgramStateRef + ReallocMemAux(CheckerContext &C, const CallEvent &Call, bool ShouldFreeOnFail, + ProgramStateRef State, AllocationFamily Family, + bool SuffixWithN = false) const; /// Evaluates the buffer size that needs to be allocated. /// /// \param [in] Blocks The amount of blocks that needs to be allocated. /// \param [in] BlockBytes The size of a block. /// \returns The symbolic value of \p Blocks * \p BlockBytes. - LLVM_NODISCARD - static SVal evalMulForBufferSize(CheckerContext &C, const Expr *Blocks, - const Expr *BlockBytes); + [[nodiscard]] static SVal evalMulForBufferSize(CheckerContext &C, + const Expr *Blocks, + const Expr *BlockBytes); /// Models zero initialized array allocation. /// /// \param [in] Call The expression that reallocated memory /// \param [in] State The \c ProgramState right before reallocation. /// \returns The ProgramState right after allocation. - LLVM_NODISCARD - static ProgramStateRef CallocMem(CheckerContext &C, const CallEvent &Call, - ProgramStateRef State); + [[nodiscard]] static ProgramStateRef + CallocMem(CheckerContext &C, const CallEvent &Call, ProgramStateRef State); /// See if deallocation happens in a suspicious context. If so, escape the /// pointers that otherwise would have been deallocated and return true. @@ -658,12 +660,11 @@ private: SymbolRef &EscapingSymbol) const; /// Implementation of the checkPointerEscape callbacks. - LLVM_NODISCARD - ProgramStateRef checkPointerEscapeAux(ProgramStateRef State, - const InvalidatedSymbols &Escaped, - const CallEvent *Call, - PointerEscapeKind Kind, - bool IsConstPointerEscape) const; + [[nodiscard]] ProgramStateRef + checkPointerEscapeAux(ProgramStateRef State, + const InvalidatedSymbols &Escaped, + const CallEvent *Call, PointerEscapeKind Kind, + bool IsConstPointerEscape) const; // Implementation of the checkPreStmt and checkEndFunction callbacks. void checkEscapeOnReturn(const ReturnStmt *S, CheckerContext &C) const; @@ -672,11 +673,11 @@ private: /// Tells if a given family/call/symbol is tracked by the current checker. /// Sets CheckKind to the kind of the checker responsible for this /// family/call/symbol. - Optional<CheckKind> getCheckIfTracked(AllocationFamily Family, - bool IsALeakCheck = false) const; + std::optional<CheckKind> getCheckIfTracked(AllocationFamily Family, + bool IsALeakCheck = false) const; - Optional<CheckKind> getCheckIfTracked(CheckerContext &C, SymbolRef Sym, - bool IsALeakCheck = false) const; + std::optional<CheckKind> getCheckIfTracked(CheckerContext &C, SymbolRef Sym, + bool IsALeakCheck = false) const; ///@} static bool SummarizeValue(raw_ostream &os, SVal V); static bool SummarizeRegion(raw_ostream &os, const MemRegion *MR); @@ -722,11 +723,204 @@ private: bool isArgZERO_SIZE_PTR(ProgramStateRef State, CheckerContext &C, SVal ArgVal) const; }; +} // end anonymous namespace + +//===----------------------------------------------------------------------===// +// Definition of NoOwnershipChangeVisitor. +//===----------------------------------------------------------------------===// + +namespace { +class NoOwnershipChangeVisitor final : public NoStateChangeFuncVisitor { + // The symbol whose (lack of) ownership change we are interested in. + SymbolRef Sym; + const MallocChecker &Checker; + using OwnerSet = llvm::SmallPtrSet<const MemRegion *, 8>; + + // Collect which entities point to the allocated memory, and could be + // responsible for deallocating it. + class OwnershipBindingsHandler : public StoreManager::BindingsHandler { + SymbolRef Sym; + OwnerSet &Owners; + + public: + OwnershipBindingsHandler(SymbolRef Sym, OwnerSet &Owners) + : Sym(Sym), Owners(Owners) {} + + bool HandleBinding(StoreManager &SMgr, Store Store, const MemRegion *Region, + SVal Val) override { + if (Val.getAsSymbol() == Sym) + Owners.insert(Region); + return true; + } + + LLVM_DUMP_METHOD void dump() const { dumpToStream(llvm::errs()); } + LLVM_DUMP_METHOD void dumpToStream(llvm::raw_ostream &out) const { + out << "Owners: {\n"; + for (const MemRegion *Owner : Owners) { + out << " "; + Owner->dumpToStream(out); + out << ",\n"; + } + out << "}\n"; + } + }; + +protected: + OwnerSet getOwnersAtNode(const ExplodedNode *N) { + OwnerSet Ret; + + ProgramStateRef State = N->getState(); + OwnershipBindingsHandler Handler{Sym, Ret}; + State->getStateManager().getStoreManager().iterBindings(State->getStore(), + Handler); + return Ret; + } + + LLVM_DUMP_METHOD static std::string + getFunctionName(const ExplodedNode *CallEnterN) { + if (const CallExpr *CE = llvm::dyn_cast_or_null<CallExpr>( + CallEnterN->getLocationAs<CallEnter>()->getCallExpr())) + if (const FunctionDecl *FD = CE->getDirectCallee()) + return FD->getQualifiedNameAsString(); + return ""; + } + + /// Syntactically checks whether the callee is a deallocating function. Since + /// we have no path-sensitive information on this call (we would need a + /// CallEvent instead of a CallExpr for that), its possible that a + /// deallocation function was called indirectly through a function pointer, + /// but we are not able to tell, so this is a best effort analysis. + /// See namespace `memory_passed_to_fn_call_free_through_fn_ptr` in + /// clang/test/Analysis/NewDeleteLeaks.cpp. + bool isFreeingCallAsWritten(const CallExpr &Call) const { + if (Checker.FreeingMemFnMap.lookupAsWritten(Call) || + Checker.ReallocatingMemFnMap.lookupAsWritten(Call)) + return true; + + if (const auto *Func = + llvm::dyn_cast_or_null<FunctionDecl>(Call.getCalleeDecl())) + return MallocChecker::isFreeingOwnershipAttrCall(Func); + + return false; + } + + /// Heuristically guess whether the callee intended to free memory. This is + /// done syntactically, because we are trying to argue about alternative + /// paths of execution, and as a consequence we don't have path-sensitive + /// information. + bool doesFnIntendToHandleOwnership(const Decl *Callee, ASTContext &ACtx) { + using namespace clang::ast_matchers; + const FunctionDecl *FD = dyn_cast<FunctionDecl>(Callee); + + // Given that the stack frame was entered, the body should always be + // theoretically obtainable. In case of body farms, the synthesized body + // is not attached to declaration, thus triggering the '!FD->hasBody()' + // branch. That said, would a synthesized body ever intend to handle + // ownership? As of today they don't. And if they did, how would we + // put notes inside it, given that it doesn't match any source locations? + if (!FD || !FD->hasBody()) + return false; + + auto Matches = match(findAll(stmt(anyOf(cxxDeleteExpr().bind("delete"), + callExpr().bind("call")))), + *FD->getBody(), ACtx); + for (BoundNodes Match : Matches) { + if (Match.getNodeAs<CXXDeleteExpr>("delete")) + return true; + + if (const auto *Call = Match.getNodeAs<CallExpr>("call")) + if (isFreeingCallAsWritten(*Call)) + return true; + } + // TODO: Ownership might change with an attempt to store the allocated + // memory, not only through deallocation. Check for attempted stores as + // well. + return false; + } + + bool wasModifiedInFunction(const ExplodedNode *CallEnterN, + const ExplodedNode *CallExitEndN) override { + if (!doesFnIntendToHandleOwnership( + CallExitEndN->getFirstPred()->getLocationContext()->getDecl(), + CallExitEndN->getState()->getAnalysisManager().getASTContext())) + return true; + + if (CallEnterN->getState()->get<RegionState>(Sym) != + CallExitEndN->getState()->get<RegionState>(Sym)) + return true; + + OwnerSet CurrOwners = getOwnersAtNode(CallEnterN); + OwnerSet ExitOwners = getOwnersAtNode(CallExitEndN); + + // Owners in the current set may be purged from the analyzer later on. + // If a variable is dead (is not referenced directly or indirectly after + // some point), it will be removed from the Store before the end of its + // actual lifetime. + // This means that if the ownership status didn't change, CurrOwners + // must be a superset of, but not necessarily equal to ExitOwners. + return !llvm::set_is_subset(ExitOwners, CurrOwners); + } + + static PathDiagnosticPieceRef emitNote(const ExplodedNode *N) { + PathDiagnosticLocation L = PathDiagnosticLocation::create( + N->getLocation(), + N->getState()->getStateManager().getContext().getSourceManager()); + return std::make_shared<PathDiagnosticEventPiece>( + L, "Returning without deallocating memory or storing the pointer for " + "later deallocation"); + } + + PathDiagnosticPieceRef + maybeEmitNoteForObjCSelf(PathSensitiveBugReport &R, + const ObjCMethodCall &Call, + const ExplodedNode *N) override { + // TODO: Implement. + return nullptr; + } + + PathDiagnosticPieceRef + maybeEmitNoteForCXXThis(PathSensitiveBugReport &R, + const CXXConstructorCall &Call, + const ExplodedNode *N) override { + // TODO: Implement. + return nullptr; + } + + PathDiagnosticPieceRef + maybeEmitNoteForParameters(PathSensitiveBugReport &R, const CallEvent &Call, + const ExplodedNode *N) override { + // TODO: Factor the logic of "what constitutes as an entity being passed + // into a function call" out by reusing the code in + // NoStoreFuncVisitor::maybeEmitNoteForParameters, maybe by incorporating + // the printing technology in UninitializedObject's FieldChainInfo. + ArrayRef<ParmVarDecl *> Parameters = Call.parameters(); + for (unsigned I = 0; I < Call.getNumArgs() && I < Parameters.size(); ++I) { + SVal V = Call.getArgSVal(I); + if (V.getAsSymbol() == Sym) + return emitNote(N); + } + return nullptr; + } + +public: + NoOwnershipChangeVisitor(SymbolRef Sym, const MallocChecker *Checker) + : NoStateChangeFuncVisitor(bugreporter::TrackingKind::Thorough), Sym(Sym), + Checker(*Checker) {} + + void Profile(llvm::FoldingSetNodeID &ID) const override { + static int Tag = 0; + ID.AddPointer(&Tag); + ID.AddPointer(Sym); + } +}; + +} // end anonymous namespace //===----------------------------------------------------------------------===// // Definition of MallocBugVisitor. //===----------------------------------------------------------------------===// +namespace { /// The bug visitor which allows us to print extra diagnostics along the /// BugReport path. For example, showing the allocation site of the leaked /// region. @@ -767,7 +961,7 @@ public: /// Did not track -> allocated. Other state (released) -> allocated. static inline bool isAllocated(const RefState *RSCurr, const RefState *RSPrev, const Stmt *Stmt) { - return (Stmt && (isa<CallExpr>(Stmt) || isa<CXXNewExpr>(Stmt)) && + return (isa_and_nonnull<CallExpr, CXXNewExpr>(Stmt) && (RSCurr && (RSCurr->isAllocated() || RSCurr->isAllocatedOfSizeZero())) && (!RSPrev || @@ -780,8 +974,7 @@ public: const Stmt *Stmt) { bool IsReleased = (RSCurr && RSCurr->isReleased()) && (!RSPrev || !RSPrev->isReleased()); - assert(!IsReleased || - (Stmt && (isa<CallExpr>(Stmt) || isa<CXXDeleteExpr>(Stmt))) || + assert(!IsReleased || (isa_and_nonnull<CallExpr, CXXDeleteExpr>(Stmt)) || (!Stmt && RSCurr->getAllocationFamily() == AF_InnerBuffer)); return IsReleased; } @@ -789,11 +982,10 @@ public: /// Did not track -> relinquished. Other state (allocated) -> relinquished. static inline bool isRelinquished(const RefState *RSCurr, const RefState *RSPrev, const Stmt *Stmt) { - return (Stmt && - (isa<CallExpr>(Stmt) || isa<ObjCMessageExpr>(Stmt) || - isa<ObjCPropertyRefExpr>(Stmt)) && - (RSCurr && RSCurr->isRelinquished()) && - (!RSPrev || !RSPrev->isRelinquished())); + return ( + isa_and_nonnull<CallExpr, ObjCMessageExpr, ObjCPropertyRefExpr>(Stmt) && + (RSCurr && RSCurr->isRelinquished()) && + (!RSPrev || !RSPrev->isRelinquished())); } /// If the expression is not a call, and the state change is @@ -803,7 +995,7 @@ public: static inline bool hasReallocFailed(const RefState *RSCurr, const RefState *RSPrev, const Stmt *Stmt) { - return ((!Stmt || !isa<CallExpr>(Stmt)) && + return ((!isa_and_nonnull<CallExpr>(Stmt)) && (RSCurr && (RSCurr->isAllocated() || RSCurr->isAllocatedOfSizeZero())) && (RSPrev && @@ -851,7 +1043,6 @@ private: } }; }; - } // end anonymous namespace // A map from the freed symbol to the symbol representing the return value of @@ -894,12 +1085,8 @@ static bool isStandardNewDelete(const FunctionDecl *FD) { // Methods of MallocChecker and MallocBugVisitor. //===----------------------------------------------------------------------===// -bool MallocChecker::isFreeingCall(const CallEvent &Call) const { - if (FreeingMemFnMap.lookup(Call) || ReallocatingMemFnMap.lookup(Call)) - return true; - - const auto *Func = dyn_cast<FunctionDecl>(Call.getDecl()); - if (Func && Func->hasAttrs()) { +bool MallocChecker::isFreeingOwnershipAttrCall(const FunctionDecl *Func) { + if (Func->hasAttrs()) { for (const auto *I : Func->specific_attrs<OwnershipAttr>()) { OwnershipAttr::OwnershipKind OwnKind = I->getOwnKind(); if (OwnKind == OwnershipAttr::Takes || OwnKind == OwnershipAttr::Holds) @@ -909,6 +1096,16 @@ bool MallocChecker::isFreeingCall(const CallEvent &Call) const { return false; } +bool MallocChecker::isFreeingCall(const CallEvent &Call) const { + if (FreeingMemFnMap.lookup(Call) || ReallocatingMemFnMap.lookup(Call)) + return true; + + if (const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl())) + return isFreeingOwnershipAttrCall(Func); + + return false; +} + bool MallocChecker::isMemCall(const CallEvent &Call) const { if (FreeingMemFnMap.lookup(Call) || AllocatingMemFnMap.lookup(Call) || ReallocatingMemFnMap.lookup(Call)) @@ -921,7 +1118,7 @@ bool MallocChecker::isMemCall(const CallEvent &Call) const { return Func && Func->hasAttr<OwnershipAttr>(); } -llvm::Optional<ProgramStateRef> +std::optional<ProgramStateRef> MallocChecker::performKernelMalloc(const CallEvent &Call, CheckerContext &C, const ProgramStateRef &State) const { // 3-argument malloc(), as commonly used in {Free,Net,Open}BSD Kernels: @@ -943,48 +1140,54 @@ MallocChecker::performKernelMalloc(const CallEvent &Call, CheckerContext &C, ASTContext &Ctx = C.getASTContext(); llvm::Triple::OSType OS = Ctx.getTargetInfo().getTriple().getOS(); - if (!KernelZeroFlagVal.hasValue()) { - if (OS == llvm::Triple::FreeBSD) + if (!KernelZeroFlagVal) { + 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 None; + return std::nullopt; + } } // We treat the last argument as the flags argument, and callers fall-back to // normal malloc on a None return. This works for the FreeBSD kernel malloc // as well as Linux kmalloc. if (Call.getNumArgs() < 2) - return None; + return std::nullopt; const Expr *FlagsEx = Call.getArgExpr(Call.getNumArgs() - 1); const SVal V = C.getSVal(FlagsEx); - if (!V.getAs<NonLoc>()) { + if (!isa<NonLoc>(V)) { // The case where 'V' can be a location can only be due to a bad header, // so in this case bail out. - return None; + return std::nullopt; } NonLoc Flags = V.castAs<NonLoc>(); NonLoc ZeroFlag = C.getSValBuilder() - .makeIntVal(KernelZeroFlagVal.getValue(), FlagsEx->getType()) - .castAs<NonLoc>(); + .makeIntVal(*KernelZeroFlagVal, FlagsEx->getType()) + .castAs<NonLoc>(); SVal MaskedFlagsUC = C.getSValBuilder().evalBinOpNN(State, BO_And, Flags, ZeroFlag, FlagsEx->getType()); if (MaskedFlagsUC.isUnknownOrUndef()) - return None; + return std::nullopt; DefinedSVal MaskedFlags = MaskedFlagsUC.castAs<DefinedSVal>(); // Check if maskedFlags is non-zero. @@ -998,7 +1201,7 @@ MallocChecker::performKernelMalloc(const CallEvent &Call, CheckerContext &C, AF_Malloc); } - return None; + return std::nullopt; } SVal MallocChecker::evalMulForBufferSize(CheckerContext &C, const Expr *Blocks, @@ -1024,10 +1227,10 @@ void MallocChecker::checkBasicAlloc(const CallEvent &Call, void MallocChecker::checkKernelMalloc(const CallEvent &Call, CheckerContext &C) const { ProgramStateRef State = C.getState(); - llvm::Optional<ProgramStateRef> MaybeState = + std::optional<ProgramStateRef> MaybeState = performKernelMalloc(Call, C, State); - if (MaybeState.hasValue()) - State = MaybeState.getValue(); + if (MaybeState) + State = *MaybeState; else State = MallocMemAux(C, Call, Call.getArgExpr(0), UndefinedVal(), State, AF_Malloc); @@ -1191,8 +1394,8 @@ void MallocChecker::checkGMalloc0(const CallEvent &Call, void MallocChecker::checkGMemdup(const CallEvent &Call, CheckerContext &C) const { ProgramStateRef State = C.getState(); - State = MallocMemAux(C, Call, Call.getArgExpr(1), UndefinedVal(), State, - AF_Malloc); + State = + MallocMemAux(C, Call, Call.getArgExpr(1), UnknownVal(), State, AF_Malloc); State = ProcessZeroAllocCheck(Call, 1, State); C.addTransition(State); } @@ -1294,7 +1497,7 @@ void MallocChecker::checkPostCall(const CallEvent &Call, // Performs a 0-sized allocations check. ProgramStateRef MallocChecker::ProcessZeroAllocCheck( const CallEvent &Call, const unsigned IndexOfSizeArg, ProgramStateRef State, - Optional<SVal> RetVal) { + std::optional<SVal> RetVal) { if (!State) return nullptr; @@ -1446,7 +1649,7 @@ static bool isKnownDeallocObjCMethodName(const ObjCMethodCall &Call) { FirstSlot == "initWithCharactersNoCopy"; } -static Optional<bool> getFreeWhenDoneArg(const ObjCMethodCall &Call) { +static std::optional<bool> getFreeWhenDoneArg(const ObjCMethodCall &Call) { Selector S = Call.getSelector(); // FIXME: We should not rely on fully-constrained symbols being folded. @@ -1454,7 +1657,7 @@ static Optional<bool> getFreeWhenDoneArg(const ObjCMethodCall &Call) { if (S.getNameForSlot(i).equals("freeWhenDone")) return !Call.getArgSVal(i).isZeroConstant(); - return None; + return std::nullopt; } void MallocChecker::checkPostObjCMessage(const ObjCMethodCall &Call, @@ -1465,7 +1668,7 @@ void MallocChecker::checkPostObjCMessage(const ObjCMethodCall &Call, if (!isKnownDeallocObjCMethodName(Call)) return; - if (Optional<bool> FreeWhenDone = getFreeWhenDoneArg(Call)) + if (std::optional<bool> FreeWhenDone = getFreeWhenDoneArg(Call)) if (!*FreeWhenDone) return; @@ -1476,7 +1679,7 @@ void MallocChecker::checkPostObjCMessage(const ObjCMethodCall &Call, ProgramStateRef State = FreeMemAux(C, Call.getArgExpr(0), Call, C.getState(), /*Hold=*/true, IsKnownToBeAllocatedMemory, AF_Malloc, - /*RetNullOnFailure=*/true); + /*ReturnsNullOnFailure=*/true); C.addTransition(State); } @@ -1491,9 +1694,9 @@ MallocChecker::MallocMemReturnsAttr(CheckerContext &C, const CallEvent &Call, if (Att->getModule()->getName() != "malloc") return nullptr; - OwnershipAttr::args_iterator I = Att->args_begin(), E = Att->args_end(); - if (I != E) { - return MallocMemAux(C, Call, Call.getArgExpr(I->getASTIndex()), + if (!Att->args().empty()) { + return MallocMemAux(C, Call, + Call.getArgExpr(Att->args_begin()->getASTIndex()), UndefinedVal(), State, AF_Malloc); } return MallocMemAux(C, Call, UnknownVal(), UndefinedVal(), State, AF_Malloc); @@ -1525,21 +1728,27 @@ 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. State = State->bindDefaultInitial(RetVal, Init, LCtx); + // If Size is somehow undefined at this point, this line prevents a crash. + if (Size.isUndef()) + Size = UnknownVal(); + // Set the region's extent. State = setDynamicExtent(State, RetVal.getAsRegion(), - Size.castAs<DefinedOrUnknownSVal>(), svalBuilder); + Size.castAs<DefinedOrUnknownSVal>(), SVB); return MallocUpdateRefState(C, CE, State, Family); } @@ -1547,7 +1756,7 @@ ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C, static ProgramStateRef MallocUpdateRefState(CheckerContext &C, const Expr *E, ProgramStateRef State, AllocationFamily Family, - Optional<SVal> RetVal) { + std::optional<SVal> RetVal) { if (!State) return nullptr; @@ -1695,12 +1904,12 @@ ProgramStateRef MallocChecker::FreeMemAux( return nullptr; SVal ArgVal = C.getSVal(ArgExpr); - if (!ArgVal.getAs<DefinedOrUnknownSVal>()) + if (!isa<DefinedOrUnknownSVal>(ArgVal)) return nullptr; DefinedOrUnknownSVal location = ArgVal.castAs<DefinedOrUnknownSVal>(); // Check for null dereferences. - if (!location.getAs<Loc>()) + if (!isa<Loc>(location)) return nullptr; // The explicit NULL case, no operation is performed. @@ -1753,14 +1962,11 @@ ProgramStateRef MallocChecker::FreeMemAux( // Parameters, locals, statics, globals, and memory returned by // __builtin_alloca() shouldn't be freed. - if (!(isa<UnknownSpaceRegion>(MS) || isa<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. + if (!isa<UnknownSpaceRegion, HeapSpaceRegion>(MS)) { + // 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()); @@ -1862,7 +2068,7 @@ ProgramStateRef MallocChecker::FreeMemAux( RefState::getReleased(Family, ParentExpr)); } -Optional<MallocChecker::CheckKind> +std::optional<MallocChecker::CheckKind> MallocChecker::getCheckIfTracked(AllocationFamily Family, bool IsALeakCheck) const { switch (Family) { @@ -1871,7 +2077,7 @@ MallocChecker::getCheckIfTracked(AllocationFamily Family, case AF_IfNameIndex: { if (ChecksEnabled[CK_MallocChecker]) return CK_MallocChecker; - return None; + return std::nullopt; } case AF_CXXNew: case AF_CXXNewArray: { @@ -1883,12 +2089,12 @@ MallocChecker::getCheckIfTracked(AllocationFamily Family, if (ChecksEnabled[CK_NewDeleteChecker]) return CK_NewDeleteChecker; } - return None; + return std::nullopt; } case AF_InnerBuffer: { if (ChecksEnabled[CK_InnerPointerChecker]) return CK_InnerPointerChecker; - return None; + return std::nullopt; } case AF_None: { llvm_unreachable("no family"); @@ -1897,7 +2103,7 @@ MallocChecker::getCheckIfTracked(AllocationFamily Family, llvm_unreachable("unhandled family"); } -Optional<MallocChecker::CheckKind> +std::optional<MallocChecker::CheckKind> MallocChecker::getCheckIfTracked(CheckerContext &C, SymbolRef Sym, bool IsALeakCheck) const { if (C.getState()->contains<ReallocSizeZeroSymbols>(Sym)) @@ -1909,11 +2115,13 @@ MallocChecker::getCheckIfTracked(CheckerContext &C, SymbolRef Sym, } bool MallocChecker::SummarizeValue(raw_ostream &os, SVal V) { - if (Optional<nonloc::ConcreteInt> IntVal = V.getAs<nonloc::ConcreteInt>()) + if (std::optional<nonloc::ConcreteInt> IntVal = + V.getAs<nonloc::ConcreteInt>()) os << "an integer (" << IntVal->getValue() << ")"; - else if (Optional<loc::ConcreteInt> ConstAddr = V.getAs<loc::ConcreteInt>()) + else if (std::optional<loc::ConcreteInt> ConstAddr = + V.getAs<loc::ConcreteInt>()) os << "a constant address (" << ConstAddr->getValue() << ")"; - else if (Optional<loc::GotoLabel> Label = V.getAs<loc::GotoLabel>()) + else if (std::optional<loc::GotoLabel> Label = V.getAs<loc::GotoLabel>()) os << "the address of the label '" << Label->getLabel()->getName() << "'"; else return false; @@ -2005,8 +2213,8 @@ void MallocChecker::HandleNonHeapDealloc(CheckerContext &C, SVal ArgVal, return; } - Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(Family); - if (!CheckKind.hasValue()) + std::optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(Family); + if (!CheckKind) return; if (ExplodedNode *N = C.generateErrorNode()) { @@ -2046,7 +2254,7 @@ void MallocChecker::HandleNonHeapDealloc(CheckerContext &C, SVal ArgVal, void MallocChecker::HandleFreeAlloca(CheckerContext &C, SVal ArgVal, SourceRange Range) const { - Optional<MallocChecker::CheckKind> CheckKind; + std::optional<MallocChecker::CheckKind> CheckKind; if (ChecksEnabled[CK_MallocChecker]) CheckKind = CK_MallocChecker; @@ -2138,8 +2346,8 @@ void MallocChecker::HandleOffsetFree(CheckerContext &C, SVal ArgVal, return; } - Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(Family); - if (!CheckKind.hasValue()) + std::optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(Family); + if (!CheckKind) return; ExplodedNode *N = C.generateErrorNode(); @@ -2195,8 +2403,8 @@ void MallocChecker::HandleUseAfterFree(CheckerContext &C, SourceRange Range, return; } - Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym); - if (!CheckKind.hasValue()) + std::optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym); + if (!CheckKind) return; if (ExplodedNode *N = C.generateErrorNode()) { @@ -2234,8 +2442,8 @@ void MallocChecker::HandleDoubleFree(CheckerContext &C, SourceRange Range, return; } - Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym); - if (!CheckKind.hasValue()) + std::optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym); + if (!CheckKind) return; if (ExplodedNode *N = C.generateErrorNode()) { @@ -2264,8 +2472,8 @@ void MallocChecker::HandleDoubleDelete(CheckerContext &C, SymbolRef Sym) const { return; } - Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym); - if (!CheckKind.hasValue()) + std::optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym); + if (!CheckKind) return; if (ExplodedNode *N = C.generateErrorNode()) { @@ -2291,9 +2499,9 @@ void MallocChecker::HandleUseZeroAlloc(CheckerContext &C, SourceRange Range, return; } - Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym); + std::optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym); - if (!CheckKind.hasValue()) + if (!CheckKind) return; if (ExplodedNode *N = C.generateErrorNode()) { @@ -2303,7 +2511,8 @@ void MallocChecker::HandleUseZeroAlloc(CheckerContext &C, SourceRange Range, categories::MemoryError)); auto R = std::make_unique<PathSensitiveBugReport>( - *BT_UseZerroAllocated[*CheckKind], "Use of zero-allocated memory", N); + *BT_UseZerroAllocated[*CheckKind], + "Use of memory allocated with size zero", N); R->addRange(Range); if (Sym) { @@ -2323,8 +2532,8 @@ void MallocChecker::HandleFunctionPtrFree(CheckerContext &C, SVal ArgVal, return; } - Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(Family); - if (!CheckKind.hasValue()) + std::optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(Family); + if (!CheckKind) return; if (ExplodedNode *N = C.generateErrorNode()) { @@ -2369,14 +2578,14 @@ MallocChecker::ReallocMemAux(CheckerContext &C, const CallEvent &Call, const Expr *arg0Expr = CE->getArg(0); SVal Arg0Val = C.getSVal(arg0Expr); - if (!Arg0Val.getAs<DefinedOrUnknownSVal>()) + if (!isa<DefinedOrUnknownSVal>(Arg0Val)) return nullptr; DefinedOrUnknownSVal arg0Val = Arg0Val.castAs<DefinedOrUnknownSVal>(); SValBuilder &svalBuilder = C.getSValBuilder(); - DefinedOrUnknownSVal PtrEQ = - svalBuilder.evalEQ(State, arg0Val, svalBuilder.makeNull()); + DefinedOrUnknownSVal PtrEQ = svalBuilder.evalEQ( + State, arg0Val, svalBuilder.makeNullWithType(arg0Expr->getType())); // Get the size argument. const Expr *Arg1 = CE->getArg(1); @@ -2385,13 +2594,14 @@ MallocChecker::ReallocMemAux(CheckerContext &C, const CallEvent &Call, SVal TotalSize = C.getSVal(Arg1); if (SuffixWithN) TotalSize = evalMulForBufferSize(C, Arg1, CE->getArg(2)); - if (!TotalSize.getAs<DefinedOrUnknownSVal>()) + if (!isa<DefinedOrUnknownSVal>(TotalSize)) return nullptr; // Compare the size argument to 0. DefinedOrUnknownSVal SizeZero = - svalBuilder.evalEQ(State, TotalSize.castAs<DefinedOrUnknownSVal>(), - svalBuilder.makeIntValWithPtrWidth(0, false)); + svalBuilder.evalEQ(State, TotalSize.castAs<DefinedOrUnknownSVal>(), + svalBuilder.makeIntValWithWidth( + svalBuilder.getContext().getSizeType(), 0)); ProgramStateRef StatePtrIsNull, StatePtrNotNull; std::tie(StatePtrIsNull, StatePtrNotNull) = State->assume(PtrEQ); @@ -2533,10 +2743,10 @@ void MallocChecker::HandleLeak(SymbolRef Sym, ExplodedNode *N, if (Family == AF_Alloca) return; - Optional<MallocChecker::CheckKind> - CheckKind = getCheckIfTracked(Family, true); + std::optional<MallocChecker::CheckKind> CheckKind = + getCheckIfTracked(Family, true); - if (!CheckKind.hasValue()) + if (!CheckKind) return; assert(N); @@ -2579,6 +2789,8 @@ void MallocChecker::HandleLeak(SymbolRef Sym, ExplodedNode *N, AllocNode->getLocationContext()->getDecl()); R->markInteresting(Sym); R->addVisitor<MallocBugVisitor>(Sym, true); + if (ShouldRegisterNoOwnershipChangeVisitor) + R->addVisitor<NoOwnershipChangeVisitor>(Sym, this); C.emitReport(std::move(R)); } @@ -2591,12 +2803,12 @@ void MallocChecker::checkDeadSymbols(SymbolReaper &SymReaper, RegionStateTy RS = OldRS; SmallVector<SymbolRef, 2> Errors; - for (RegionStateTy::iterator I = RS.begin(), E = RS.end(); I != E; ++I) { - if (SymReaper.isDead(I->first)) { - if (I->second.isAllocated() || I->second.isAllocatedOfSizeZero()) - Errors.push_back(I->first); + for (auto [Sym, State] : RS) { + if (SymReaper.isDead(Sym)) { + if (State.isAllocated() || State.isAllocatedOfSizeZero()) + Errors.push_back(Sym); // Remove the dead symbol from the map. - RS = F.remove(RS, I->first); + RS = F.remove(RS, Sym); } } @@ -2611,19 +2823,17 @@ void MallocChecker::checkDeadSymbols(SymbolReaper &SymReaper, // Cleanup the Realloc Pairs Map. ReallocPairsTy RP = state->get<ReallocPairs>(); - for (ReallocPairsTy::iterator I = RP.begin(), E = RP.end(); I != E; ++I) { - if (SymReaper.isDead(I->first) || - SymReaper.isDead(I->second.ReallocatedSym)) { - state = state->remove<ReallocPairs>(I->first); + for (auto [Sym, ReallocPair] : RP) { + if (SymReaper.isDead(Sym) || SymReaper.isDead(ReallocPair.ReallocatedSym)) { + state = state->remove<ReallocPairs>(Sym); } } // Cleanup the FreeReturnValue Map. FreeReturnValueTy FR = state->get<FreeReturnValue>(); - for (FreeReturnValueTy::iterator I = FR.begin(), E = FR.end(); I != E; ++I) { - if (SymReaper.isDead(I->first) || - SymReaper.isDead(I->second)) { - state = state->remove<FreeReturnValue>(I->first); + for (auto [Sym, RetSym] : FR) { + if (SymReaper.isDead(Sym) || SymReaper.isDead(RetSym)) { + state = state->remove<FreeReturnValue>(Sym); } } @@ -2633,9 +2843,8 @@ void MallocChecker::checkDeadSymbols(SymbolReaper &SymReaper, static CheckerProgramPointTag Tag("MallocChecker", "DeadSymbolsLeak"); N = C.generateNonFatalErrorNode(C.getState(), &Tag); if (N) { - for (SmallVectorImpl<SymbolRef>::iterator - I = Errors.begin(), E = Errors.end(); I != E; ++I) { - HandleLeak(*I, N, C); + for (SymbolRef Sym : Errors) { + HandleLeak(Sym, N, C); } } } @@ -2692,7 +2901,7 @@ void MallocChecker::checkPreCall(const CallEvent &Call, // Check arguments for being used after free. for (unsigned I = 0, E = Call.getNumArgs(); I != E; ++I) { SVal ArgSVal = Call.getArgSVal(I); - if (ArgSVal.getAs<Loc>()) { + if (isa<Loc>(ArgSVal)) { SymbolRef Sym = ArgSVal.getAsSymbol(); if (!Sym) continue; @@ -2733,7 +2942,7 @@ void MallocChecker::checkEscapeOnReturn(const ReturnStmt *S, // the callee could still free the memory. // TODO: This logic should be a part of generic symbol escape callback. if (const MemRegion *MR = RetVal.getAsRegion()) - if (isa<FieldRegion>(MR) || isa<ElementRegion>(MR)) + if (isa<FieldRegion, ElementRegion>(MR)) if (const SymbolicRegion *BMR = dyn_cast<SymbolicRegion>(MR->getBaseRegion())) Sym = BMR->getSymbol(); @@ -2758,18 +2967,16 @@ void MallocChecker::checkPostStmt(const BlockExpr *BE, const BlockDataRegion *R = cast<BlockDataRegion>(C.getSVal(BE).getAsRegion()); - BlockDataRegion::referenced_vars_iterator I = R->referenced_vars_begin(), - E = R->referenced_vars_end(); - - if (I == E) + auto ReferencedVars = R->referenced_vars(); + if (ReferencedVars.empty()) return; SmallVector<const MemRegion*, 10> Regions; const LocationContext *LC = C.getLocationContext(); MemRegionManager &MemMgr = C.getSValBuilder().getRegionManager(); - for ( ; I != E; ++I) { - const VarRegion *VR = I.getCapturedRegion(); + for (const auto &Var : ReferencedVars) { + const VarRegion *VR = Var.getCapturedRegion(); if (VR->getSuperRegion() == R) { VR = MemMgr.getVarRegion(VR->getDecl(), LC); } @@ -2865,28 +3072,28 @@ ProgramStateRef MallocChecker::evalAssume(ProgramStateRef state, SVal Cond, bool Assumption) const { RegionStateTy RS = state->get<RegionState>(); - for (RegionStateTy::iterator I = RS.begin(), E = RS.end(); I != E; ++I) { + for (SymbolRef Sym : llvm::make_first_range(RS)) { // If the symbol is assumed to be NULL, remove it from consideration. ConstraintManager &CMgr = state->getConstraintManager(); - ConditionTruthVal AllocFailed = CMgr.isNull(state, I.getKey()); + ConditionTruthVal AllocFailed = CMgr.isNull(state, Sym); if (AllocFailed.isConstrainedTrue()) - state = state->remove<RegionState>(I.getKey()); + state = state->remove<RegionState>(Sym); } // Realloc returns 0 when reallocation fails, which means that we should // restore the state of the pointer being reallocated. ReallocPairsTy RP = state->get<ReallocPairs>(); - for (ReallocPairsTy::iterator I = RP.begin(), E = RP.end(); I != E; ++I) { + for (auto [Sym, ReallocPair] : RP) { // If the symbol is assumed to be NULL, remove it from consideration. ConstraintManager &CMgr = state->getConstraintManager(); - ConditionTruthVal AllocFailed = CMgr.isNull(state, I.getKey()); + ConditionTruthVal AllocFailed = CMgr.isNull(state, Sym); if (!AllocFailed.isConstrainedTrue()) continue; - SymbolRef ReallocSym = I.getData().ReallocatedSym; + SymbolRef ReallocSym = ReallocPair.ReallocatedSym; if (const RefState *RS = state->get<RegionState>(ReallocSym)) { if (RS->isReleased()) { - switch (I.getData().Kind) { + switch (ReallocPair.Kind) { case OAR_ToBeFreedAfterFailure: state = state->set<RegionState>(ReallocSym, RefState::getAllocated(RS->getAllocationFamily(), RS->getStmt())); @@ -2895,11 +3102,11 @@ ProgramStateRef MallocChecker::evalAssume(ProgramStateRef state, state = state->remove<RegionState>(ReallocSym); break; default: - assert(I.getData().Kind == OAR_FreeOnFailure); + assert(ReallocPair.Kind == OAR_FreeOnFailure); } } } - state = state->remove<ReallocPairs>(I.getKey()); + state = state->remove<ReallocPairs>(Sym); } return state; @@ -2916,7 +3123,7 @@ bool MallocChecker::mayFreeAnyEscapedMemoryOrIsModeledExplicitly( // TODO: If we want to be more optimistic here, we'll need to make sure that // regions escape to C++ containers. They seem to do that even now, but for // mysterious reasons. - if (!(isa<SimpleFunctionCall>(Call) || isa<ObjCMethodCall>(Call))) + if (!isa<SimpleFunctionCall, ObjCMethodCall>(Call)) return true; // Check Objective-C messages by selector name. @@ -2935,7 +3142,7 @@ bool MallocChecker::mayFreeAnyEscapedMemoryOrIsModeledExplicitly( // about, we can't be sure that the object will use free() to deallocate the // memory, so we can't model it explicitly. The best we can do is use it to // decide whether the pointer escapes. - if (Optional<bool> FreeWhenDone = getFreeWhenDoneArg(*Msg)) + if (std::optional<bool> FreeWhenDone = getFreeWhenDoneArg(*Msg)) return *FreeWhenDone; // If the first selector piece ends with "NoCopy", and there is no @@ -2943,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; } @@ -2992,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'. @@ -3024,7 +3231,7 @@ bool MallocChecker::mayFreeAnyEscapedMemoryOrIsModeledExplicitly( const Expr *ArgE = Call->getArgExpr(0)->IgnoreParenCasts(); if (const DeclRefExpr *ArgDRE = dyn_cast<DeclRefExpr>(ArgE)) if (const VarDecl *D = dyn_cast<VarDecl>(ArgDRE->getDecl())) - if (D->getCanonicalDecl()->getName().find("std") != StringRef::npos) + if (D->getCanonicalDecl()->getName().contains("std")) return true; } } @@ -3052,6 +3259,11 @@ bool MallocChecker::mayFreeAnyEscapedMemoryOrIsModeledExplicitly( return true; } + if (FName == "singleShotImpl" && + FD->getQualifiedNameAsString() == "QTimer::singleShotImpl") { + return true; + } + // Handle cases where we know a buffer's /address/ can escape. // Note that the above checks handle some special cases where we know that // even though the address escapes, it's still our responsibility to free the @@ -3100,11 +3312,7 @@ ProgramStateRef MallocChecker::checkPointerEscapeAux( return State; } - for (InvalidatedSymbols::const_iterator I = Escaped.begin(), - E = Escaped.end(); - I != E; ++I) { - SymbolRef sym = *I; - + for (SymbolRef sym : Escaped) { if (EscapingSymbol && EscapingSymbol != sym) continue; @@ -3224,7 +3432,7 @@ PathDiagnosticPieceRef MallocBugVisitor::VisitNode(const ExplodedNode *N, allocation_state::getContainerObjRegion(statePrev, Sym); const auto *TypedRegion = cast<TypedValueRegion>(ObjRegion); QualType ObjTy = TypedRegion->getValueType(); - OS << "Inner buffer of '" << ObjTy.getAsString() << "' "; + OS << "Inner buffer of '" << ObjTy << "' "; if (N->getLocation().getKind() == ProgramPoint::PostImplicitCallKind) { OS << "deallocated by call to destructor"; @@ -3239,7 +3447,8 @@ PathDiagnosticPieceRef MallocBugVisitor::VisitNode(const ExplodedNode *N, OS << OpCallE->getDirectCallee()->getDeclName(); } else if (const auto *CallE = dyn_cast<CallExpr>(S)) { auto &CEMgr = BRC.getStateManager().getCallEventManager(); - CallEventRef<> Call = CEMgr.getSimpleCall(CallE, state, CurrentLC); + CallEventRef<> Call = + CEMgr.getSimpleCall(CallE, state, CurrentLC, {nullptr, 0}); if (const auto *D = dyn_cast_or_null<NamedDecl>(Call->getDecl())) OS << D->getDeclName(); else @@ -3351,17 +3560,18 @@ void MallocChecker::printState(raw_ostream &Out, ProgramStateRef State, if (!RS.isEmpty()) { Out << Sep << "MallocChecker :" << NL; - for (RegionStateTy::iterator I = RS.begin(), E = RS.end(); I != E; ++I) { - const RefState *RefS = State->get<RegionState>(I.getKey()); + for (auto [Sym, Data] : RS) { + const RefState *RefS = State->get<RegionState>(Sym); AllocationFamily Family = RefS->getAllocationFamily(); - Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(Family); - if (!CheckKind.hasValue()) - CheckKind = getCheckIfTracked(Family, true); + std::optional<MallocChecker::CheckKind> CheckKind = + getCheckIfTracked(Family); + if (!CheckKind) + CheckKind = getCheckIfTracked(Family, true); - I.getKey()->dumpToStream(Out); + Sym->dumpToStream(Out); Out << " : "; - I.getData().dump(Out); - if (CheckKind.hasValue()) + Data.dump(Out); + if (CheckKind) Out << " (" << CheckNames[*CheckKind].getName() << ")"; Out << NL; } @@ -3395,6 +3605,9 @@ void ento::registerDynamicMemoryModeling(CheckerManager &mgr) { auto *checker = mgr.registerChecker<MallocChecker>(); checker->ShouldIncludeOwnershipAnnotatedFunctions = mgr.getAnalyzerOptions().getCheckerBooleanOption(checker, "Optimistic"); + checker->ShouldRegisterNoOwnershipChangeVisitor = + mgr.getAnalyzerOptions().getCheckerBooleanOption( + checker, "AddNoOwnershipChangeNotes"); } bool ento::shouldRegisterDynamicMemoryModeling(const CheckerManager &mgr) { diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp index e31630f63b5a..3c8b38973c6b 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp @@ -24,6 +24,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" #include "llvm/ADT/APSInt.h" #include "llvm/ADT/SmallVector.h" +#include <optional> #include <utility> using namespace clang; @@ -32,12 +33,14 @@ using llvm::APSInt; namespace { struct MallocOverflowCheck { + const CallExpr *call; const BinaryOperator *mulop; const Expr *variable; APSInt maxVal; - MallocOverflowCheck(const BinaryOperator *m, const Expr *v, APSInt val) - : mulop(m), variable(v), maxVal(std::move(val)) {} + MallocOverflowCheck(const CallExpr *call, const BinaryOperator *m, + const Expr *v, APSInt val) + : call(call), mulop(m), variable(v), maxVal(std::move(val)) {} }; class MallocOverflowSecurityChecker : public Checker<check::ASTCodeBody> { @@ -46,8 +49,8 @@ public: BugReporter &BR) const; void CheckMallocArgument( - SmallVectorImpl<MallocOverflowCheck> &PossibleMallocOverflows, - const Expr *TheArgument, ASTContext &Context) const; + SmallVectorImpl<MallocOverflowCheck> &PossibleMallocOverflows, + const CallExpr *TheCall, ASTContext &Context) const; void OutputPossibleOverflows( SmallVectorImpl<MallocOverflowCheck> &PossibleMallocOverflows, @@ -62,16 +65,15 @@ static inline bool EvaluatesToZero(APSInt &Val, BinaryOperatorKind op) { } void MallocOverflowSecurityChecker::CheckMallocArgument( - SmallVectorImpl<MallocOverflowCheck> &PossibleMallocOverflows, - const Expr *TheArgument, - ASTContext &Context) const { + SmallVectorImpl<MallocOverflowCheck> &PossibleMallocOverflows, + const CallExpr *TheCall, ASTContext &Context) const { /* Look for a linear combination with a single variable, and at least one multiplication. Reject anything that applies to the variable: an explicit cast, conditional expression, an operation that could reduce the range of the result, or anything too complicated :-). */ - const Expr *e = TheArgument; + const Expr *e = TheCall->getArg(0); const BinaryOperator * mulop = nullptr; APSInt maxVal; @@ -101,8 +103,7 @@ void MallocOverflowSecurityChecker::CheckMallocArgument( e = rhs; } else return; - } - else if (isa<DeclRefExpr>(e) || isa<MemberExpr>(e)) + } else if (isa<DeclRefExpr, MemberExpr>(e)) break; else return; @@ -115,9 +116,8 @@ void MallocOverflowSecurityChecker::CheckMallocArgument( // the data so when the body of the function is completely available // we can check for comparisons. - // TODO: Could push this into the innermost scope where 'e' is - // defined, rather than the whole function. - PossibleMallocOverflows.push_back(MallocOverflowCheck(mulop, e, maxVal)); + PossibleMallocOverflows.push_back( + MallocOverflowCheck(TheCall, mulop, e, maxVal)); } namespace { @@ -153,17 +153,19 @@ private: return getDecl(CheckDR) == getDecl(DR) && Pred(Check); return false; }; - toScanFor.erase(std::remove_if(toScanFor.begin(), toScanFor.end(), P), - toScanFor.end()); + llvm::erase_if(toScanFor, P); } void CheckExpr(const Expr *E_p) { - auto PredTrue = [](const MallocOverflowCheck &) { return true; }; const Expr *E = E_p->IgnoreParenImpCasts(); + const auto PrecedesMalloc = [E, this](const MallocOverflowCheck &c) { + return Context.getSourceManager().isBeforeInTranslationUnit( + E->getExprLoc(), c.call->getExprLoc()); + }; if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(E)) - Erase<DeclRefExpr>(DR, PredTrue); + Erase<DeclRefExpr>(DR, PrecedesMalloc); else if (const auto *ME = dyn_cast<MemberExpr>(E)) { - Erase<MemberExpr>(ME, PredTrue); + Erase<MemberExpr>(ME, PrecedesMalloc); } } @@ -278,17 +280,13 @@ void MallocOverflowSecurityChecker::OutputPossibleOverflows( c.Visit(mgr.getAnalysisDeclContext(D)->getBody()); // Output warnings for all overflows that are left. - for (CheckOverflowOps::theVecType::iterator - i = PossibleMallocOverflows.begin(), - e = PossibleMallocOverflows.end(); - i != e; - ++i) { + for (const MallocOverflowCheck &Check : PossibleMallocOverflows) { BR.EmitBasicReport( D, this, "malloc() size overflow", categories::UnixAPI, "the computation of the size of the memory allocation may overflow", - PathDiagnosticLocation::createOperatorLoc(i->mulop, + PathDiagnosticLocation::createOperatorLoc(Check.mulop, BR.getSourceManager()), - i->mulop->getSourceRange()); + Check.mulop->getSourceRange()); } } @@ -307,26 +305,27 @@ void MallocOverflowSecurityChecker::checkASTCodeBody(const Decl *D, CFGBlock *block = *it; for (CFGBlock::iterator bi = block->begin(), be = block->end(); bi != be; ++bi) { - if (Optional<CFGStmt> CS = bi->getAs<CFGStmt>()) { - if (const CallExpr *TheCall = dyn_cast<CallExpr>(CS->getStmt())) { - // Get the callee. - const FunctionDecl *FD = TheCall->getDirectCallee(); - - if (!FD) - continue; - - // Get the name of the callee. If it's a builtin, strip off the prefix. - IdentifierInfo *FnInfo = FD->getIdentifier(); - if (!FnInfo) - continue; - - if (FnInfo->isStr ("malloc") || FnInfo->isStr ("_MALLOC")) { - if (TheCall->getNumArgs() == 1) - CheckMallocArgument(PossibleMallocOverflows, TheCall->getArg(0), - mgr.getASTContext()); + if (std::optional<CFGStmt> CS = bi->getAs<CFGStmt>()) { + if (const CallExpr *TheCall = dyn_cast<CallExpr>(CS->getStmt())) { + // Get the callee. + const FunctionDecl *FD = TheCall->getDirectCallee(); + + if (!FD) + continue; + + // Get the name of the callee. If it's a builtin, strip off the + // prefix. + IdentifierInfo *FnInfo = FD->getIdentifier(); + if (!FnInfo) + continue; + + if (FnInfo->isStr("malloc") || FnInfo->isStr("_MALLOC")) { + if (TheCall->getNumArgs() == 1) + CheckMallocArgument(PossibleMallocOverflows, TheCall, + mgr.getASTContext()); + } } } - } } } diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp index 4b5206a102b8..9e81a6bd19fc 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp @@ -12,14 +12,15 @@ // //===----------------------------------------------------------------------===// -#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/AST/StmtVisitor.h" #include "clang/AST/TypeLoc.h" +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" #include "llvm/ADT/SmallString.h" +#include "llvm/ADT/iterator_range.h" #include "llvm/Support/raw_ostream.h" using namespace clang; @@ -182,22 +183,20 @@ public: AnalysisDeclContext *ADC = mgr.getAnalysisDeclContext(D); CastedAllocFinder Finder(&BR.getContext()); Finder.Visit(D->getBody()); - for (CastedAllocFinder::CallVec::iterator i = Finder.Calls.begin(), - e = Finder.Calls.end(); i != e; ++i) { - QualType CastedType = i->CastedExpr->getType(); + for (const auto &CallRec : Finder.Calls) { + QualType CastedType = CallRec.CastedExpr->getType(); if (!CastedType->isPointerType()) continue; QualType PointeeType = CastedType->getPointeeType(); if (PointeeType->isVoidType()) continue; - for (CallExpr::const_arg_iterator ai = i->AllocCall->arg_begin(), - ae = i->AllocCall->arg_end(); ai != ae; ++ai) { - if (!(*ai)->getType()->isIntegralOrUnscopedEnumerationType()) + for (const Expr *Arg : CallRec.AllocCall->arguments()) { + if (!Arg->getType()->isIntegralOrUnscopedEnumerationType()) continue; SizeofFinder SFinder; - SFinder.Visit(*ai); + SFinder.Visit(Arg); if (SFinder.Sizeofs.size() != 1) continue; @@ -212,34 +211,33 @@ public: continue; const TypeSourceInfo *TSI = nullptr; - if (i->CastedExprParent.is<const VarDecl *>()) { - TSI = - i->CastedExprParent.get<const VarDecl *>()->getTypeSourceInfo(); + if (CallRec.CastedExprParent.is<const VarDecl *>()) { + TSI = CallRec.CastedExprParent.get<const VarDecl *>() + ->getTypeSourceInfo(); } else { - TSI = i->ExplicitCastType; + TSI = CallRec.ExplicitCastType; } SmallString<64> buf; llvm::raw_svector_ostream OS(buf); OS << "Result of "; - const FunctionDecl *Callee = i->AllocCall->getDirectCallee(); + const FunctionDecl *Callee = CallRec.AllocCall->getDirectCallee(); if (Callee && Callee->getIdentifier()) OS << '\'' << Callee->getIdentifier()->getName() << '\''; else OS << "call"; - OS << " is converted to a pointer of type '" - << PointeeType.getAsString() << "', which is incompatible with " - << "sizeof operand type '" << SizeofType.getAsString() << "'"; + OS << " is converted to a pointer of type '" << PointeeType + << "', which is incompatible with " + << "sizeof operand type '" << SizeofType << "'"; SmallVector<SourceRange, 4> Ranges; - Ranges.push_back(i->AllocCall->getCallee()->getSourceRange()); + Ranges.push_back(CallRec.AllocCall->getCallee()->getSourceRange()); Ranges.push_back(SFinder.Sizeofs[0]->getSourceRange()); if (TSI) Ranges.push_back(TSI->getTypeLoc().getSourceRange()); - PathDiagnosticLocation L = - PathDiagnosticLocation::createBegin(i->AllocCall->getCallee(), - BR.getSourceManager(), ADC); + PathDiagnosticLocation L = PathDiagnosticLocation::createBegin( + CallRec.AllocCall->getCallee(), BR.getSourceManager(), ADC); BR.EmitBasicReport(D, this, "Allocator sizeof operand mismatch", categories::UnixAPI, OS.str(), L, Ranges); diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MismatchedIteratorChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MismatchedIteratorChecker.cpp index 1960873599f7..82a622831817 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MismatchedIteratorChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MismatchedIteratorChecker.cpp @@ -30,22 +30,18 @@ namespace { class MismatchedIteratorChecker : public Checker<check::PreCall, check::PreStmt<BinaryOperator>> { - std::unique_ptr<BugType> MismatchedBugType; - - void verifyMatch(CheckerContext &C, const SVal &Iter, - const MemRegion *Cont) const; - void verifyMatch(CheckerContext &C, const SVal &Iter1, - const SVal &Iter2) const; - void reportBug(const StringRef &Message, const SVal &Val1, - const SVal &Val2, CheckerContext &C, - ExplodedNode *ErrNode) const; - void reportBug(const StringRef &Message, const SVal &Val, - const MemRegion *Reg, CheckerContext &C, + const BugType MismatchedBugType{this, "Iterator(s) mismatched", + "Misuse of STL APIs", + /*SuppressOnSink=*/true}; + + void verifyMatch(CheckerContext &C, SVal Iter, const MemRegion *Cont) const; + void verifyMatch(CheckerContext &C, SVal Iter1, SVal Iter2) const; + void reportBug(StringRef Message, SVal Val1, SVal Val2, CheckerContext &C, ExplodedNode *ErrNode) const; + void reportBug(StringRef Message, SVal Val, const MemRegion *Reg, + CheckerContext &C, ExplodedNode *ErrNode) const; public: - MismatchedIteratorChecker(); - void checkPreCall(const CallEvent &Call, CheckerContext &C) const; void checkPreStmt(const BinaryOperator *BO, CheckerContext &C) const; @@ -53,12 +49,6 @@ public: } // namespace -MismatchedIteratorChecker::MismatchedIteratorChecker() { - MismatchedBugType.reset( - new BugType(this, "Iterator(s) mismatched", "Misuse of STL APIs", - /*SuppressOnSink=*/true)); -} - void MismatchedIteratorChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { // Check for iterator mismatches @@ -176,8 +166,10 @@ void MismatchedIteratorChecker::checkPreCall(const CallEvent &Call, const auto *Param = Func->getParamDecl(J); const auto *ParamType = Param->getType()->getAs<SubstTemplateTypeParmType>(); - if (!ParamType || - ParamType->getReplacedParameter()->getDecl() != TPDecl) + if (!ParamType) + continue; + const TemplateTypeParmDecl *D = ParamType->getReplacedParameter(); + if (D != TPDecl) continue; if (LHS.isUndef()) { LHS = Call.getArgSVal(J); @@ -200,7 +192,7 @@ void MismatchedIteratorChecker::checkPreStmt(const BinaryOperator *BO, verifyMatch(C, LVal, RVal); } -void MismatchedIteratorChecker::verifyMatch(CheckerContext &C, const SVal &Iter, +void MismatchedIteratorChecker::verifyMatch(CheckerContext &C, SVal Iter, const MemRegion *Cont) const { // Verify match between a container and the container of an iterator Cont = Cont->getMostDerivedObjectRegion(); @@ -236,9 +228,8 @@ void MismatchedIteratorChecker::verifyMatch(CheckerContext &C, const SVal &Iter, } } -void MismatchedIteratorChecker::verifyMatch(CheckerContext &C, - const SVal &Iter1, - const SVal &Iter2) const { +void MismatchedIteratorChecker::verifyMatch(CheckerContext &C, SVal Iter1, + SVal Iter2) const { // Verify match between the containers of two iterators auto State = C.getState(); const auto *Pos1 = getIteratorPosition(State, Iter1); @@ -275,23 +266,21 @@ void MismatchedIteratorChecker::verifyMatch(CheckerContext &C, } } -void MismatchedIteratorChecker::reportBug(const StringRef &Message, - const SVal &Val1, - const SVal &Val2, - CheckerContext &C, +void MismatchedIteratorChecker::reportBug(StringRef Message, SVal Val1, + SVal Val2, CheckerContext &C, ExplodedNode *ErrNode) const { - auto R = std::make_unique<PathSensitiveBugReport>(*MismatchedBugType, Message, + auto R = std::make_unique<PathSensitiveBugReport>(MismatchedBugType, Message, ErrNode); R->markInteresting(Val1); R->markInteresting(Val2); C.emitReport(std::move(R)); } -void MismatchedIteratorChecker::reportBug(const StringRef &Message, - const SVal &Val, const MemRegion *Reg, +void MismatchedIteratorChecker::reportBug(StringRef Message, SVal Val, + const MemRegion *Reg, CheckerContext &C, ExplodedNode *ErrNode) const { - auto R = std::make_unique<PathSensitiveBugReport>(*MismatchedBugType, Message, + auto R = std::make_unique<PathSensitiveBugReport>(MismatchedBugType, Message, ErrNode); R->markInteresting(Val); R->markInteresting(Reg); diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp index 5d63d6efd234..2e31c16e457c 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp @@ -15,14 +15,15 @@ #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.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" using namespace clang; using namespace ento; -using llvm::APSInt; namespace { class MmapWriteExecChecker : public Checker<check::PreCall> { @@ -31,9 +32,11 @@ class MmapWriteExecChecker : public Checker<check::PreCall> { static int ProtWrite; static int ProtExec; static int ProtRead; - mutable std::unique_ptr<BugType> BT; + const BugType BT{this, "W^X check fails, Write Exec prot flags set", + "Security"}; + public: - MmapWriteExecChecker() : MmapFn("mmap", 6), MprotectFn("mprotect", 3) {} + MmapWriteExecChecker() : MmapFn({"mmap"}, 6), MprotectFn({"mprotect"}, 3) {} void checkPreCall(const CallEvent &Call, CheckerContext &C) const; int ProtExecOv; int ProtReadOv; @@ -46,9 +49,11 @@ int MmapWriteExecChecker::ProtRead = 0x01; void MmapWriteExecChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { - if (Call.isCalled(MmapFn) || Call.isCalled(MprotectFn)) { + if (matchesAny(Call, MmapFn, MprotectFn)) { SVal ProtVal = Call.getArgSVal(2); - Optional<nonloc::ConcreteInt> ProtLoc = ProtVal.getAs<nonloc::ConcreteInt>(); + auto ProtLoc = ProtVal.getAs<nonloc::ConcreteInt>(); + if (!ProtLoc) + return; int64_t Prot = ProtLoc->getValue().getSExtValue(); if (ProtExecOv != ProtExec) ProtExec = ProtExecOv; @@ -60,17 +65,16 @@ void MmapWriteExecChecker::checkPreCall(const CallEvent &Call, return; if ((Prot & (ProtWrite | ProtExec)) == (ProtWrite | ProtExec)) { - if (!BT) - BT.reset(new BugType(this, "W^X check fails, Write Exec prot flags set", "Security")); - ExplodedNode *N = C.generateNonFatalErrorNode(); if (!N) return; auto Report = std::make_unique<PathSensitiveBugReport>( - *BT, "Both PROT_WRITE and PROT_EXEC flags are set. This can " - "lead to exploitable memory regions, which could be overwritten " - "with malicious code", N); + BT, + "Both PROT_WRITE and PROT_EXEC flags are set. This can " + "lead to exploitable memory regions, which could be overwritten " + "with malicious code", + N); Report->addRange(Call.getArgSourceRange(2)); C.emitReport(std::move(Report)); } diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp index cbe938982000..5240352a9bd2 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp @@ -49,7 +49,6 @@ class MoveChecker : public Checker<check::PreCall, check::PostCall, check::DeadSymbols, check::RegionChanges> { public: - void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const; void checkPreCall(const CallEvent &MC, CheckerContext &C) const; void checkPostCall(const CallEvent &MC, CheckerContext &C) const; void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const; @@ -185,7 +184,7 @@ private: bool Found; }; - AggressivenessKind Aggressiveness; + AggressivenessKind Aggressiveness = AK_KnownsAndLocals; public: void setAggressiveness(StringRef Str, CheckerManager &Mgr) { @@ -214,8 +213,9 @@ private: // Returns the exploded node against which the report was emitted. // The caller *must* add any further transitions against this node. - ExplodedNode *reportBug(const MemRegion *Region, const CXXRecordDecl *RD, - CheckerContext &C, MisuseKind MK) const; + // Returns nullptr and does not report if such node already exists. + ExplodedNode *tryToReportBug(const MemRegion *Region, const CXXRecordDecl *RD, + CheckerContext &C, MisuseKind MK) const; bool isInMoveSafeContext(const LocationContext *LC) const; bool isStateResetMethod(const CXXMethodDecl *MethodDec) const; @@ -310,7 +310,7 @@ MoveChecker::MovedBugVisitor::VisitNode(const ExplodedNode *N, // If it's not a dereference, we don't care if it was reset to null // or that it is even a smart pointer. - LLVM_FALLTHROUGH; + [[fallthrough]]; case SK_NonStd: case SK_Safe: OS << "Object"; @@ -378,19 +378,20 @@ void MoveChecker::modelUse(ProgramStateRef State, const MemRegion *Region, return; } - ExplodedNode *N = reportBug(Region, RD, C, MK); + ExplodedNode *N = tryToReportBug(Region, RD, C, MK); // If the program has already crashed on this path, don't bother. - if (N->isSink()) + if (!N || N->isSink()) return; State = State->set<TrackedRegionMap>(Region, RegionState::getReported()); C.addTransition(State, N); } -ExplodedNode *MoveChecker::reportBug(const MemRegion *Region, - const CXXRecordDecl *RD, CheckerContext &C, - MisuseKind MK) const { +ExplodedNode *MoveChecker::tryToReportBug(const MemRegion *Region, + const CXXRecordDecl *RD, + CheckerContext &C, + MisuseKind MK) const { if (ExplodedNode *N = misuseCausesCrash(MK) ? C.generateErrorNode() : C.generateNonFatalErrorNode()) { // Uniqueing report to the same object. @@ -554,7 +555,8 @@ MoveChecker::classifyObject(const MemRegion *MR, // as not-"STL" types, because that's how the checker treats them. MR = unwrapRValueReferenceIndirection(MR); bool IsLocal = - MR && isa<VarRegion>(MR) && isa<StackSpaceRegion>(MR->getMemorySpace()); + isa_and_nonnull<VarRegion, CXXLifetimeExtendedObjectRegion>(MR) && + isa<StackSpaceRegion>(MR->getMemorySpace()); if (!RD || !RD->getDeclContext()->isStdNamespace()) return { IsLocal, SK_NonStd }; @@ -588,7 +590,7 @@ void MoveChecker::explainObject(llvm::raw_ostream &OS, const MemRegion *MR, break; // We only care about the type if it's a dereference. - LLVM_FALLTHROUGH; + [[fallthrough]]; case SK_Unsafe: OS << " of type '" << RD->getQualifiedNameAsString() << "'"; break; @@ -619,10 +621,6 @@ void MoveChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { if (!IC) return; - // Calling a destructor on a moved object is fine. - if (isa<CXXDestructorCall>(IC)) - return; - const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion(); if (!ThisRegion) return; @@ -632,6 +630,10 @@ void MoveChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { if (!MethodDecl) return; + // Calling a destructor on a moved object is fine. + if (isa<CXXDestructorDecl>(MethodDecl)) + return; + // We want to investigate the whole object, not only sub-object of a parent // class in which the encountered method defined. ThisRegion = ThisRegion->getMostDerivedObjectRegion(); @@ -712,12 +714,9 @@ ProgramStateRef MoveChecker::checkRegionChanges( // directly, but not all of them end up being invalidated. // But when they do, they appear in the InvalidatedRegions array as well. for (const auto *Region : RequestedRegions) { - if (ThisRegion != Region) { - if (llvm::find(InvalidatedRegions, Region) != - std::end(InvalidatedRegions)) { - State = removeFromState(State, Region); - } - } + if (ThisRegion != Region && + llvm::is_contained(InvalidatedRegions, Region)) + State = removeFromState(State, Region); } } else { // For invalidations that aren't caused by calls, assume nothing. In diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp index be17e401fb53..0648084a7d39 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp @@ -31,7 +31,8 @@ using namespace ento; namespace { class NSAutoreleasePoolChecker : public Checker<check::PreObjCMessage> { - mutable std::unique_ptr<BugType> BT; + const BugType BT{this, "Use -drain instead of -release", + "API Upgrade (Apple)"}; mutable Selector releaseS; public: @@ -57,10 +58,6 @@ void NSAutoreleasePoolChecker::checkPreObjCMessage(const ObjCMethodCall &msg, if (msg.getSelector() != releaseS) return; - if (!BT) - BT.reset(new BugType(this, "Use -drain instead of -release", - "API Upgrade (Apple)")); - ExplodedNode *N = C.generateNonFatalErrorNode(); if (!N) { assert(0); @@ -68,7 +65,7 @@ void NSAutoreleasePoolChecker::checkPreObjCMessage(const ObjCMethodCall &msg, } auto Report = std::make_unique<PathSensitiveBugReport>( - *BT, + BT, "Use -drain instead of -release when using NSAutoreleasePool and " "garbage collection", N); @@ -80,7 +77,7 @@ void ento::registerNSAutoreleasePoolChecker(CheckerManager &mgr) { mgr.registerChecker<NSAutoreleasePoolChecker>(); } -bool ento::shouldRegisterNSAutoreleasePoolChecker(const CheckerManager &mgr) { +bool ento::shouldRegisterNSAutoreleasePoolChecker(const CheckerManager &mgr) { const LangOptions &LO = mgr.getLangOpts(); return LO.getGC() != LangOptions::NonGC; } diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp index 90c5583d8969..54870bcb4bb2 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// // -// This file defines a CheckNSError, a flow-insenstive check +// This file defines a CheckNSError, a flow-insensitive check // that determines if an Objective-C class interface correctly returns // a non-void return type. // @@ -24,6 +24,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/raw_ostream.h" +#include <optional> using namespace clang; using namespace ento; @@ -38,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; @@ -118,7 +119,7 @@ void CFErrorFunctionChecker::checkASTDecl(const FunctionDecl *D, II = &D->getASTContext().Idents.get("CFErrorRef"); bool hasCFError = false; - for (auto I : D->parameters()) { + for (auto *I : D->parameters()) { if (IsCFError(I->getType(), II)) { hasCFError = true; break; @@ -166,7 +167,7 @@ class NSOrCFErrorDerefChecker mutable std::unique_ptr<NSErrorDerefBug> NSBT; mutable std::unique_ptr<CFErrorDerefBug> CFBT; public: - DefaultBool ShouldCheckNSError, ShouldCheckCFError; + bool ShouldCheckNSError = false, ShouldCheckCFError = false; CheckerNameRef NSErrorName, CFErrorName; NSOrCFErrorDerefChecker() : NSErrorII(nullptr), CFErrorII(nullptr) {} @@ -197,7 +198,7 @@ static void setFlag(ProgramStateRef state, SVal val, CheckerContext &C) { static QualType parameterTypeFromSVal(SVal val, CheckerContext &C) { const StackFrameContext * SFC = C.getStackFrame(); - if (Optional<loc::MemRegionVal> X = val.getAs<loc::MemRegionVal>()) { + if (std::optional<loc::MemRegionVal> X = val.getAs<loc::MemRegionVal>()) { const MemRegion* R = X->getRegion(); if (const VarRegion *VR = R->getAs<VarRegion>()) if (const StackArgumentsSpaceRegion * @@ -214,7 +215,7 @@ void NSOrCFErrorDerefChecker::checkLocation(SVal loc, bool isLoad, CheckerContext &C) const { if (!isLoad) return; - if (loc.isUndef() || !loc.getAs<Loc>()) + if (loc.isUndef() || !isa<Loc>(loc)) return; ASTContext &Ctx = C.getASTContext(); @@ -266,7 +267,7 @@ void NSOrCFErrorDerefChecker::checkEvent(ImplicitNullDerefEvent event) const { SmallString<128> Buf; llvm::raw_svector_ostream os(Buf); - os << "Potential null dereference. According to coding standards "; + os << "Potential null dereference. According to coding standards "; os << (isNSError ? "in 'Creating and Returning NSError Objects' the parameter" : "documented in CoreFoundation/CFError.h the parameter"); diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp index af208e867318..17c3cb4e9e04 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp @@ -44,9 +44,11 @@ void NoReturnFunctionChecker::checkPostCall(const CallEvent &CE, if (const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(CE.getDecl())) BuildSinks = FD->hasAttr<AnalyzerNoReturnAttr>() || FD->isNoReturn(); - const Expr *Callee = CE.getOriginExpr(); - if (!BuildSinks && Callee) - BuildSinks = getFunctionExtInfo(Callee->getType()).getNoReturn(); + if (const CallExpr *CExpr = dyn_cast_or_null<CallExpr>(CE.getOriginExpr()); + CExpr && !BuildSinks) { + if (const Expr *C = CExpr->getCallee()) + BuildSinks = getFunctionExtInfo(C->getType()).getNoReturn(); + } if (!BuildSinks && CE.isGlobalCFunction()) { if (const IdentifierInfo *II = CE.getCalleeIdentifier()) { diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp index 534b5d68434f..a9002ee7c966 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp @@ -18,6 +18,7 @@ #include "clang/Analysis/AnyCall.h" #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" @@ -31,8 +32,9 @@ namespace { class NonNullParamChecker : public Checker<check::PreCall, check::BeginFunction, EventDispatcher<ImplicitNullDerefEvent>> { - mutable std::unique_ptr<BugType> BTAttrNonNull; - mutable std::unique_ptr<BugType> BTNullRefArg; + const BugType BTAttrNonNull{ + this, "Argument with 'nonnull' attribute passed null", "API"}; + const BugType BTNullRefArg{this, "Dereference of null pointer"}; public: void checkPreCall(const CallEvent &Call, CheckerContext &C) const; @@ -136,10 +138,10 @@ void NonNullParamChecker::checkPreCall(const CallEvent &Call, if (!DV) continue; - assert(!HasRefTypeParam || DV->getAs<Loc>()); + assert(!HasRefTypeParam || isa<Loc>(*DV)); // Process the case when the argument is not a location. - if (ExpectedToBeNonNull && !DV->getAs<Loc>()) { + if (ExpectedToBeNonNull && !isa<Loc>(*DV)) { // If the argument is a union type, we want to handle a potential // transparent_union GCC extension. if (!ArgE) @@ -161,7 +163,7 @@ void NonNullParamChecker::checkPreCall(const CallEvent &Call, assert(++CSV->begin() == CSV->end()); // FIXME: Handle (some_union){ some_other_union_val }, which turns into // a LazyCompoundVal inside a CompoundVal. - if (!V.getAs<Loc>()) + if (!isa<Loc>(V)) continue; // Retrieve the corresponding expression. @@ -278,13 +280,6 @@ std::unique_ptr<PathSensitiveBugReport> NonNullParamChecker::genReportNullAttrNonNull(const ExplodedNode *ErrorNode, const Expr *ArgE, unsigned IdxOfArg) const { - // Lazily allocate the BugType object if it hasn't already been - // created. Ownership is transferred to the BugReporter object once - // the BugReport is passed to 'EmitWarning'. - if (!BTAttrNonNull) - BTAttrNonNull.reset(new BugType( - this, "Argument with 'nonnull' attribute passed null", "API")); - llvm::SmallString<256> SBuf; llvm::raw_svector_ostream OS(SBuf); OS << "Null pointer passed to " @@ -292,7 +287,7 @@ NonNullParamChecker::genReportNullAttrNonNull(const ExplodedNode *ErrorNode, << " parameter expecting 'nonnull'"; auto R = - std::make_unique<PathSensitiveBugReport>(*BTAttrNonNull, SBuf, ErrorNode); + std::make_unique<PathSensitiveBugReport>(BTAttrNonNull, SBuf, ErrorNode); if (ArgE) bugreporter::trackExpressionValue(ErrorNode, ArgE, *R); @@ -302,11 +297,8 @@ NonNullParamChecker::genReportNullAttrNonNull(const ExplodedNode *ErrorNode, std::unique_ptr<PathSensitiveBugReport> NonNullParamChecker::genReportReferenceToNullPointer( const ExplodedNode *ErrorNode, const Expr *ArgE) const { - if (!BTNullRefArg) - BTNullRefArg.reset(new BuiltinBug(this, "Dereference of null pointer")); - auto R = std::make_unique<PathSensitiveBugReport>( - *BTNullRefArg, "Forming reference to null pointer", ErrorNode); + BTNullRefArg, "Forming reference to null pointer", ErrorNode); if (ArgE) { const Expr *ArgEDeref = bugreporter::getDerefExpr(ArgE); if (!ArgEDeref) @@ -314,7 +306,6 @@ NonNullParamChecker::genReportReferenceToNullPointer( bugreporter::trackExpressionValue(ErrorNode, ArgEDeref, *R); } return R; - } void ento::registerNonNullParamChecker(CheckerManager &mgr) { diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NonnullGlobalConstantsChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NonnullGlobalConstantsChecker.cpp index c5437b16c688..72c6a869d225 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NonnullGlobalConstantsChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NonnullGlobalConstantsChecker.cpp @@ -26,6 +26,7 @@ #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" +#include <optional> using namespace clang; using namespace ento; @@ -77,7 +78,8 @@ void NonnullGlobalConstantsChecker::checkLocation(SVal location, bool isLoad, if (isGlobalConstString(location)) { SVal V = State->getSVal(location.castAs<Loc>()); - Optional<DefinedOrUnknownSVal> Constr = V.getAs<DefinedOrUnknownSVal>(); + std::optional<DefinedOrUnknownSVal> Constr = + V.getAs<DefinedOrUnknownSVal>(); if (Constr) { @@ -91,7 +93,7 @@ void NonnullGlobalConstantsChecker::checkLocation(SVal location, bool isLoad, /// \param V loaded lvalue. /// \return whether @c val is a string-like const global. bool NonnullGlobalConstantsChecker::isGlobalConstString(SVal V) const { - Optional<loc::MemRegionVal> RegionVal = V.getAs<loc::MemRegionVal>(); + std::optional<loc::MemRegionVal> RegionVal = V.getAs<loc::MemRegionVal>(); if (!RegionVal) return false; auto *Region = dyn_cast<VarRegion>(RegionVal->getAsRegion()); @@ -109,17 +111,20 @@ bool NonnullGlobalConstantsChecker::isGlobalConstString(SVal V) const { // Look through the typedefs. while (const Type *T = Ty.getTypePtr()) { - if (const auto *TT = dyn_cast<TypedefType>(T)) { + if (const auto *AT = dyn_cast<AttributedType>(T)) { + if (AT->getAttrKind() == attr::TypeNonNull) + return true; + Ty = AT->getModifiedType(); + } else if (const auto *ET = dyn_cast<ElaboratedType>(T)) { + const auto *TT = dyn_cast<TypedefType>(ET->getNamedType()); + if (!TT) + return false; Ty = TT->getDecl()->getUnderlyingType(); // It is sufficient for any intermediate typedef // to be classified const. HasConst = HasConst || Ty.isConstQualified(); if (isNonnullType(Ty) && HasConst) return true; - } else if (const auto *AT = dyn_cast<AttributedType>(T)) { - if (AT->getAttrKind() == attr::TypeNonNull) - return true; - Ty = AT->getModifiedType(); } else { return false; } @@ -136,7 +141,7 @@ bool NonnullGlobalConstantsChecker::isNonnullType(QualType Ty) const { if (auto *T = dyn_cast<ObjCObjectPointerType>(Ty)) { return T->getInterfaceDecl() && T->getInterfaceDecl()->getIdentifier() == NSStringII; - } else if (auto *T = dyn_cast<TypedefType>(Ty)) { + } else if (auto *T = Ty->getAs<TypedefType>()) { IdentifierInfo* II = T->getDecl()->getIdentifier(); return II == CFStringRefII || II == CFBooleanRefII || II == CFNullRefII; } diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp index fe8f7e7bf69e..06f1ad00eaf2 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp @@ -26,13 +26,15 @@ #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/Analysis/AnyCall.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/CheckerHelpers.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/Path.h" @@ -80,8 +82,9 @@ enum class ErrorKind : int { class NullabilityChecker : public Checker<check::Bind, check::PreCall, check::PreStmt<ReturnStmt>, check::PostCall, check::PostStmt<ExplicitCastExpr>, - check::PostObjCMessage, check::DeadSymbols, - check::Location, check::Event<ImplicitNullDerefEvent>> { + check::PostObjCMessage, check::DeadSymbols, eval::Assume, + check::Location, check::Event<ImplicitNullDerefEvent>, + check::BeginFunction> { public: // If true, the checker will not diagnose nullabilility issues for calls @@ -90,7 +93,7 @@ public: // find warnings about nullability annotations that they have explicitly // added themselves higher priority to fix than warnings on calls to system // libraries. - DefaultBool NoDiagnoseCallsToSystemHeaders; + bool NoDiagnoseCallsToSystemHeaders = false; void checkBind(SVal L, SVal V, const Stmt *S, CheckerContext &C) const; void checkPostStmt(const ExplicitCastExpr *CE, CheckerContext &C) const; @@ -102,6 +105,9 @@ public: void checkEvent(ImplicitNullDerefEvent Event) const; void checkLocation(SVal Location, bool IsLoad, const Stmt *S, CheckerContext &C) const; + void checkBeginFunction(CheckerContext &Ctx) const; + ProgramStateRef evalAssume(ProgramStateRef State, SVal Cond, + bool Assumption) const; void printState(raw_ostream &Out, ProgramStateRef State, const char *NL, const char *Sep) const override; @@ -115,7 +121,7 @@ public: CK_NumCheckKinds }; - DefaultBool ChecksEnabled[CK_NumCheckKinds]; + bool ChecksEnabled[CK_NumCheckKinds] = {false}; CheckerNameRef CheckNames[CK_NumCheckKinds]; mutable std::unique_ptr<BugType> BTs[CK_NumCheckKinds]; @@ -129,8 +135,8 @@ public: // When set to false no nullability information will be tracked in // NullabilityMap. It is possible to catch errors like passing a null pointer // to a callee that expects nonnull argument without the information that is - // stroed in the NullabilityMap. This is an optimization. - DefaultBool NeedTracking; + // stored in the NullabilityMap. This is an optimization. + bool NeedTracking = false; private: class NullabilityBugVisitor : public BugReporterVisitor { @@ -230,10 +236,41 @@ bool operator==(NullabilityState Lhs, NullabilityState Rhs) { Lhs.getNullabilitySource() == Rhs.getNullabilitySource(); } +// For the purpose of tracking historical property accesses, the key for lookup +// is an object pointer (could be an instance or a class) paired with the unique +// identifier for the property being invoked on that object. +using ObjectPropPair = std::pair<const MemRegion *, const IdentifierInfo *>; + +// Metadata associated with the return value from a recorded property access. +struct ConstrainedPropertyVal { + // This will reference the conjured return SVal for some call + // of the form [object property] + DefinedOrUnknownSVal Value; + + // If the SVal has been determined to be nonnull, that is recorded here + bool isConstrainedNonnull; + + ConstrainedPropertyVal(DefinedOrUnknownSVal SV) + : Value(SV), isConstrainedNonnull(false) {} + + void Profile(llvm::FoldingSetNodeID &ID) const { + Value.Profile(ID); + ID.AddInteger(isConstrainedNonnull ? 1 : 0); + } +}; + +bool operator==(const ConstrainedPropertyVal &Lhs, + const ConstrainedPropertyVal &Rhs) { + return Lhs.Value == Rhs.Value && + Lhs.isConstrainedNonnull == Rhs.isConstrainedNonnull; +} + } // end anonymous namespace REGISTER_MAP_WITH_PROGRAMSTATE(NullabilityMap, const MemRegion *, NullabilityState) +REGISTER_MAP_WITH_PROGRAMSTATE(PropertyAccessesMap, ObjectPropPair, + ConstrainedPropertyVal) // We say "the nullability type invariant is violated" when a location with a // non-null type contains NULL or a function with a non-null return type returns @@ -273,6 +310,10 @@ static NullConstraint getNullConstraint(DefinedOrUnknownSVal Val, return NullConstraint::Unknown; } +static bool isValidPointerType(QualType T) { + return T->isAnyPointerType() || T->isBlockPointerType(); +} + const SymbolicRegion * NullabilityChecker::getTrackRegion(SVal Val, bool CheckSuperRegion) const { if (!NeedTracking) @@ -285,8 +326,11 @@ NullabilityChecker::getTrackRegion(SVal Val, bool CheckSuperRegion) const { const MemRegion *Region = RegionSVal->getRegion(); if (CheckSuperRegion) { - if (auto FieldReg = Region->getAs<FieldRegion>()) + if (const SubRegion *FieldReg = Region->getAs<FieldRegion>()) { + if (const auto *ER = dyn_cast<ElementRegion>(FieldReg->getSuperRegion())) + FieldReg = ER; return dyn_cast<SymbolicRegion>(FieldReg->getSuperRegion()); + } if (auto ElementReg = Region->getAs<ElementRegion>()) return dyn_cast<SymbolicRegion>(ElementReg->getSuperRegion()); } @@ -455,15 +499,24 @@ void NullabilityChecker::checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const { ProgramStateRef State = C.getState(); NullabilityMapTy Nullabilities = State->get<NullabilityMap>(); - for (NullabilityMapTy::iterator I = Nullabilities.begin(), - E = Nullabilities.end(); - I != E; ++I) { - const auto *Region = I->first->getAs<SymbolicRegion>(); + for (const MemRegion *Reg : llvm::make_first_range(Nullabilities)) { + const auto *Region = Reg->getAs<SymbolicRegion>(); assert(Region && "Non-symbolic region is tracked."); if (SR.isDead(Region->getSymbol())) { - State = State->remove<NullabilityMap>(I->first); + State = State->remove<NullabilityMap>(Reg); } } + + // When an object goes out of scope, we can free the history associated + // with any property accesses on that object + PropertyAccessesMapTy PropertyAccesses = State->get<PropertyAccessesMap>(); + for (ObjectPropPair PropKey : llvm::make_first_range(PropertyAccesses)) { + const MemRegion *ReceiverRegion = PropKey.first; + if (!SR.isLiveRegion(ReceiverRegion)) { + State = State->remove<PropertyAccessesMap>(PropKey); + } + } + // When one of the nonnull arguments are constrained to be null, nullability // preconditions are violated. It is not enough to check this only when we // actually report an error, because at that time interesting symbols might be @@ -510,6 +563,37 @@ void NullabilityChecker::checkEvent(ImplicitNullDerefEvent Event) const { } } +void NullabilityChecker::checkBeginFunction(CheckerContext &C) const { + if (!C.inTopFrame()) + return; + + const LocationContext *LCtx = C.getLocationContext(); + auto AbstractCall = AnyCall::forDecl(LCtx->getDecl()); + if (!AbstractCall || AbstractCall->parameters().empty()) + return; + + ProgramStateRef State = C.getState(); + for (const ParmVarDecl *Param : AbstractCall->parameters()) { + if (!isValidPointerType(Param->getType())) + continue; + + Nullability RequiredNullability = + getNullabilityAnnotation(Param->getType()); + if (RequiredNullability != Nullability::Nullable) + continue; + + const VarRegion *ParamRegion = State->getRegion(Param, LCtx); + const MemRegion *ParamPointeeRegion = + State->getSVal(ParamRegion).getAsRegion(); + if (!ParamPointeeRegion) + continue; + + State = State->set<NullabilityMap>(ParamPointeeRegion, + NullabilityState(RequiredNullability)); + } + C.addTransition(State); +} + // Whenever we see a load from a typed memory region that's been annotated as // 'nonnull', we want to trust the user on that and assume that it is is indeed // non-null. @@ -572,7 +656,7 @@ void NullabilityChecker::checkPreStmt(const ReturnStmt *S, if (!RetExpr) return; - if (!RetExpr->getType()->isAnyPointerType()) + if (!isValidPointerType(RetExpr->getType())) return; ProgramStateRef State = C.getState(); @@ -705,7 +789,7 @@ void NullabilityChecker::checkPreCall(const CallEvent &Call, if (!ArgSVal) continue; - if (!Param->getType()->isAnyPointerType() && + if (!isValidPointerType(Param->getType()) && !Param->getType()->isReferenceType()) continue; @@ -714,7 +798,7 @@ void NullabilityChecker::checkPreCall(const CallEvent &Call, Nullability RequiredNullability = getNullabilityAnnotation(Param->getType()); Nullability ArgExprTypeLevelNullability = - getNullabilityAnnotation(ArgExpr->getType()); + getNullabilityAnnotation(lookThroughImplicitCasts(ArgExpr)->getType()); unsigned ParamIdx = Param->getFunctionScopeIndex() + 1; @@ -792,7 +876,7 @@ void NullabilityChecker::checkPostCall(const CallEvent &Call, if (!FuncType) return; QualType ReturnType = FuncType->getReturnType(); - if (!ReturnType->isAnyPointerType()) + if (!isValidPointerType(ReturnType)) return; ProgramStateRef State = C.getState(); if (State->get<InvariantViolated>()) @@ -806,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; @@ -815,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); @@ -851,6 +943,30 @@ static Nullability getReceiverNullability(const ObjCMethodCall &M, return Nullability::Unspecified; } +// The return value of a property access is typically a temporary value which +// will not be tracked in a persistent manner by the analyzer. We use +// evalAssume() in order to immediately record constraints on those temporaries +// at the time they are imposed (e.g. by a nil-check conditional). +ProgramStateRef NullabilityChecker::evalAssume(ProgramStateRef State, SVal Cond, + bool Assumption) const { + PropertyAccessesMapTy PropertyAccesses = State->get<PropertyAccessesMap>(); + for (auto [PropKey, PropVal] : PropertyAccesses) { + if (!PropVal.isConstrainedNonnull) { + ConditionTruthVal IsNonNull = State->isNonNull(PropVal.Value); + if (IsNonNull.isConstrainedTrue()) { + ConstrainedPropertyVal Replacement = PropVal; + Replacement.isConstrainedNonnull = true; + State = State->set<PropertyAccessesMap>(PropKey, Replacement); + } else if (IsNonNull.isConstrainedFalse()) { + // Space optimization: no point in tracking constrained-null cases + State = State->remove<PropertyAccessesMap>(PropKey); + } + } + } + + return State; +} + /// Calculate the nullability of the result of a message expr based on the /// nullability of the receiver, the nullability of the return value, and the /// constraints. @@ -860,7 +976,7 @@ void NullabilityChecker::checkPostObjCMessage(const ObjCMethodCall &M, if (!Decl) return; QualType RetType = Decl->getReturnType(); - if (!RetType->isAnyPointerType()) + if (!isValidPointerType(RetType)) return; ProgramStateRef State = C.getState(); @@ -876,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 @@ -907,7 +1023,7 @@ void NullabilityChecker::checkPostObjCMessage(const ObjCMethodCall &M, // this class of methods reduced the emitted diagnostics by about 30% on // some projects (and all of that was false positives). if (Name.contains("String")) { - for (auto Param : M.parameters()) { + for (auto *Param : M.parameters()) { if (Param->getName() == "encoding") { State = State->set<NullabilityMap>(ReturnRegion, Nullability::Contradicted); @@ -945,14 +1061,55 @@ 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. For this reason the static analyzer creates a - // new symbol each time an unknown property is read. To avoid false pozitives - // do not treat unknown properties as nullable, even when they explicitly - // marked nullable. - if (M.getMessageKind() == OCM_PropertyAccess && !C.wasInlined) - RetNullability = Nullability::Nonnull; + // Properties might be computed, which means the property value could + // theoretically change between calls even in commonly-observed cases like + // this: + // + // if (foo.prop) { // ok, it's nonnull here... + // [bar doStuffWithNonnullVal:foo.prop]; // ...but what about + // here? + // } + // + // If the property is nullable-annotated, a naive analysis would lead to many + // false positives despite the presence of probably-correct nil-checks. To + // reduce the false positive rate, we maintain a history of the most recently + // observed property value. For each property access, if the prior value has + // been constrained to be not nil then we will conservatively assume that the + // next access can be inferred as nonnull. + if (RetNullability != Nullability::Nonnull && + M.getMessageKind() == OCM_PropertyAccess && !C.wasInlined) { + bool LookupResolved = false; + if (const MemRegion *ReceiverRegion = getTrackRegion(M.getReceiverSVal())) { + if (IdentifierInfo *Ident = M.getSelector().getIdentifierInfoForSlot(0)) { + LookupResolved = true; + ObjectPropPair Key = std::make_pair(ReceiverRegion, Ident); + const ConstrainedPropertyVal *PrevPropVal = + State->get<PropertyAccessesMap>(Key); + if (PrevPropVal && PrevPropVal->isConstrainedNonnull) { + RetNullability = Nullability::Nonnull; + } else { + // If a previous property access was constrained as nonnull, we hold + // on to that constraint (effectively inferring that all subsequent + // accesses on that code path can be inferred as nonnull). If the + // previous property access was *not* constrained as nonnull, then + // let's throw it away in favor of keeping the SVal associated with + // this more recent access. + if (auto ReturnSVal = + M.getReturnValue().getAs<DefinedOrUnknownSVal>()) { + State = State->set<PropertyAccessesMap>( + Key, ConstrainedPropertyVal(*ReturnSVal)); + } + } + } + } + + if (!LookupResolved) { + // Fallback: err on the side of suppressing the false positive. + RetNullability = Nullability::Nonnull; + } + } Nullability ComputedNullab = getMostNullable(RetNullability, SelfNullability); if (ComputedNullab == Nullability::Nullable) { @@ -973,9 +1130,9 @@ void NullabilityChecker::checkPostStmt(const ExplicitCastExpr *CE, CheckerContext &C) const { QualType OriginType = CE->getSubExpr()->getType(); QualType DestType = CE->getType(); - if (!OriginType->isAnyPointerType()) + if (!isValidPointerType(OriginType)) return; - if (!DestType->isAnyPointerType()) + if (!isValidPointerType(DestType)) return; ProgramStateRef State = C.getState(); @@ -1099,7 +1256,7 @@ void NullabilityChecker::checkBind(SVal L, SVal V, const Stmt *S, return; QualType LocType = TVR->getValueType(); - if (!LocType->isAnyPointerType()) + if (!isValidPointerType(LocType)) return; ProgramStateRef State = C.getState(); @@ -1221,9 +1378,9 @@ void NullabilityChecker::printState(raw_ostream &Out, ProgramStateRef State, if (!State->get<InvariantViolated>()) Out << Sep << NL; - for (NullabilityMapTy::iterator I = B.begin(), E = B.end(); I != E; ++I) { - Out << I->first << " : "; - I->second.print(Out); + for (auto [Region, State] : B) { + Out << Region << " : "; + State.print(Out); Out << NL; } } diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NumberObjectConversionChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NumberObjectConversionChecker.cpp index df01cc760e7e..f217520d8f4a 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NumberObjectConversionChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NumberObjectConversionChecker.cpp @@ -143,7 +143,7 @@ void Callback::run(const MatchFinder::MatchResult &Result) { else OS << "Converting "; - OS << "a pointer value of type '" << ObjT.getAsString() << "' to a "; + OS << "a pointer value of type '" << ObjT << "' to a "; std::string EuphemismForPlain = "primitive"; std::string SuggestedApi = IsObjC ? (IsInteger ? "" : "-boolValue") @@ -196,12 +196,10 @@ void NumberObjectConversionChecker::checkASTCodeBody(const Decl *D, AnalysisManager &AM, BugReporter &BR) const { // Currently this matches CoreFoundation opaque pointer typedefs. - auto CSuspiciousNumberObjectExprM = - expr(ignoringParenImpCasts( - expr(hasType( - typedefType(hasDeclaration(anyOf( - typedefDecl(hasName("CFNumberRef")), - typedefDecl(hasName("CFBooleanRef"))))))) + auto CSuspiciousNumberObjectExprM = expr(ignoringParenImpCasts( + expr(hasType(elaboratedType(namesType(typedefType( + hasDeclaration(anyOf(typedefDecl(hasName("CFNumberRef")), + typedefDecl(hasName("CFBooleanRef"))))))))) .bind("c_object"))); // Currently this matches XNU kernel number-object pointers. @@ -240,8 +238,9 @@ void NumberObjectConversionChecker::checkASTCodeBody(const Decl *D, // The .bind here is in order to compose the error message more accurately. auto ObjCSuspiciousScalarBooleanTypeM = - qualType(typedefType(hasDeclaration( - typedefDecl(hasName("BOOL"))))).bind("objc_bool_type"); + qualType(elaboratedType(namesType( + typedefType(hasDeclaration(typedefDecl(hasName("BOOL"))))))) + .bind("objc_bool_type"); // The .bind here is in order to compose the error message more accurately. auto SuspiciousScalarBooleanTypeM = @@ -253,9 +252,9 @@ void NumberObjectConversionChecker::checkASTCodeBody(const Decl *D, // for storing pointers. auto SuspiciousScalarNumberTypeM = qualType(hasCanonicalType(isInteger()), - unless(typedefType(hasDeclaration( - typedefDecl(matchesName("^::u?intptr_t$")))))) - .bind("int_type"); + unless(elaboratedType(namesType(typedefType(hasDeclaration( + typedefDecl(matchesName("^::u?intptr_t$")))))))) + .bind("int_type"); auto SuspiciousScalarTypeM = qualType(anyOf(SuspiciousScalarBooleanTypeM, diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp index 43af4bb14286..552c222a251a 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp @@ -25,8 +25,10 @@ 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; + const BugType BT_null{this, "Nil value used as mutex for @synchronized() " + "(no synchronization will occur)"}; + const BugType BT_undef{this, "Uninitialized value used as mutex " + "for @synchronized"}; public: void checkPreStmt(const ObjCAtSynchronizedStmt *S, CheckerContext &C) const; @@ -41,13 +43,10 @@ void ObjCAtSyncChecker::checkPreStmt(const ObjCAtSynchronizedStmt *S, SVal V = C.getSVal(Ex); // Uninitialized value used for the mutex? - if (V.getAs<UndefinedVal>()) { + 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")); auto report = std::make_unique<PathSensitiveBugReport>( - *BT_undef, BT_undef->getDescription(), N); + BT_undef, BT_undef.getDescription(), N); bugreporter::trackExpressionValue(N, Ex, *report); C.emitReport(std::move(report)); } @@ -66,12 +65,8 @@ void ObjCAtSyncChecker::checkPreStmt(const ObjCAtSynchronizedStmt *S, // Generate an error node. This isn't a sink since // 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)")); auto report = std::make_unique<PathSensitiveBugReport>( - *BT_null, BT_null->getDescription(), N); + BT_null, BT_null.getDescription(), N); bugreporter::trackExpressionValue(N, Ex, *report); C.emitReport(std::move(report)); diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCAutoreleaseWriteChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCAutoreleaseWriteChecker.cpp index c8eab3288094..514f53b4804f 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCAutoreleaseWriteChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCAutoreleaseWriteChecker.cpp @@ -98,11 +98,13 @@ private: }; } -static inline std::vector<llvm::StringRef> toRefs(std::vector<std::string> V) { +static inline std::vector<llvm::StringRef> +toRefs(const std::vector<std::string> &V) { return std::vector<llvm::StringRef>(V.begin(), V.end()); } -static decltype(auto) callsNames(std::vector<std::string> FunctionNames) { +static decltype(auto) +callsNames(const std::vector<std::string> &FunctionNames) { return callee(functionDecl(hasAnyName(toRefs(FunctionNames)))); } diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp index 8428b2294ba6..2b008d1c775a 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp @@ -72,7 +72,7 @@ class WalkAST : public StmtVisitor<WalkAST> { public: WalkAST(BugReporter &br, const CheckerBase *checker, AnalysisDeclContext *ac) : BR(br), Checker(checker), AC(ac), ASTC(AC->getASTContext()), - PtrWidth(ASTC.getTargetInfo().getPointerWidth(0)) {} + PtrWidth(ASTC.getTargetInfo().getPointerWidth(LangAS::Default)) {} // Statement visitor methods. void VisitChildren(Stmt *S); @@ -135,9 +135,9 @@ void WalkAST::VisitCallExpr(CallExpr *CE) { llvm::raw_svector_ostream Os(Buf); // Use "second" and "third" since users will expect 1-based indexing // for parameter names when mentioned in prose. - Os << " The "<< ((ArgNum == 1) ? "second" : "third") << " argument to '" - << Name << "' must be a C array of pointer-sized values, not '" - << Arg->getType().getAsString() << "'"; + Os << " The " << ((ArgNum == 1) ? "second" : "third") << " argument to '" + << Name << "' must be a C array of pointer-sized values, not '" + << Arg->getType() << "'"; PathDiagnosticLocation CELoc = PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp index 13985af76b00..28e88245ca95 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp @@ -30,12 +30,7 @@ namespace { class ObjCContainersChecker : public Checker< check::PreStmt<CallExpr>, check::PostStmt<CallExpr>, check::PointerEscape> { - mutable std::unique_ptr<BugType> BT; - inline void initBugType() const { - if (!BT) - BT.reset(new BugType(this, "CFArray API", - categories::CoreFoundationObjectiveC)); - } + const BugType BT{this, "CFArray API", categories::CoreFoundationObjectiveC}; inline SymbolRef getArraySym(const Expr *E, CheckerContext &C) const { SVal ArrayRef = C.getSVal(E); @@ -47,9 +42,6 @@ class ObjCContainersChecker : public Checker< check::PreStmt<CallExpr>, CheckerContext &C) const; public: - /// A tag to id this checker. - static void *getTag() { static int Tag; return &Tag; } - void checkPostStmt(const CallExpr *CE, CheckerContext &C) const; void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; ProgramStateRef checkPointerEscape(ProgramStateRef State, @@ -137,15 +129,15 @@ void ObjCContainersChecker::checkPreStmt(const CallExpr *CE, // Now, check if 'Idx in [0, Size-1]'. const QualType T = IdxExpr->getType(); - ProgramStateRef StInBound = State->assumeInBound(Idx, *Size, true, T); - ProgramStateRef StOutBound = State->assumeInBound(Idx, *Size, false, T); + ProgramStateRef StInBound, StOutBound; + std::tie(StInBound, StOutBound) = State->assumeInBoundDual(Idx, *Size, T); if (StOutBound && !StInBound) { ExplodedNode *N = C.generateErrorNode(StOutBound); if (!N) return; - initBugType(); + auto R = std::make_unique<PathSensitiveBugReport>( - *BT, "Index is out of bounds", N); + BT, "Index is out of bounds", N); R->addRange(IdxExpr->getSourceRange()); bugreporter::trackExpressionValue(N, IdxExpr, *R, {bugreporter::TrackingKind::Thorough, diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp index 35a600f2d7b8..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; }; } @@ -103,9 +103,7 @@ void ObjCSuperCallChecker::fillSelectors(ASTContext &Ctx, llvm::SmallPtrSet<Selector, 16> &ClassSelectors = SelectorsForClass[ClassName]; // Fill the Selectors SmallSet with all selectors we want to check. - for (ArrayRef<SelectorDescriptor>::iterator I = Sel.begin(), E = Sel.end(); - I != E; ++I) { - SelectorDescriptor Descriptor = *I; + for (SelectorDescriptor Descriptor : Sel) { assert(Descriptor.ArgumentCount <= 1); // No multi-argument selectors yet. // Get the selector. 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/ObjCSelfInitChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp index 17d3c042ac40..217c46451f80 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp @@ -61,13 +61,13 @@ class ObjCSelfInitChecker : public Checker< check::PostObjCMessage, check::PostCall, check::Location, check::Bind > { - mutable std::unique_ptr<BugType> BT; + const BugType BT{this, "Missing \"self = [(super or self) init...]\"", + categories::CoreFoundationObjectiveC}; void checkForInvalidSelf(const Expr *E, CheckerContext &C, const char *errorStr) const; public: - ObjCSelfInitChecker() {} void checkPostObjCMessage(const ObjCMethodCall &Msg, CheckerContext &C) const; void checkPostStmt(const ObjCIvarRefExpr *E, CheckerContext &C) const; void checkPreStmt(const ReturnStmt *S, CheckerContext &C) const; @@ -94,19 +94,19 @@ enum SelfFlagEnum { }; } -REGISTER_MAP_WITH_PROGRAMSTATE(SelfFlag, SymbolRef, unsigned) +REGISTER_MAP_WITH_PROGRAMSTATE(SelfFlag, SymbolRef, SelfFlagEnum) REGISTER_TRAIT_WITH_PROGRAMSTATE(CalledInit, bool) /// A call receiving a reference to 'self' invalidates the object that /// 'self' contains. This keeps the "self flags" assigned to the 'self' /// object before the call so we can assign them to the new object that 'self' /// points to after the call. -REGISTER_TRAIT_WITH_PROGRAMSTATE(PreCallSelfFlags, unsigned) +REGISTER_TRAIT_WITH_PROGRAMSTATE(PreCallSelfFlags, SelfFlagEnum) static SelfFlagEnum getSelfFlags(SVal val, ProgramStateRef state) { if (SymbolRef sym = val.getAsSymbol()) - if (const unsigned *attachedFlags = state->get<SelfFlag>(sym)) - return (SelfFlagEnum)*attachedFlags; + if (const SelfFlagEnum *attachedFlags = state->get<SelfFlag>(sym)) + return *attachedFlags; return SelfFlag_None; } @@ -118,7 +118,8 @@ static void addSelfFlag(ProgramStateRef state, SVal val, SelfFlagEnum flag, CheckerContext &C) { // We tag the symbol that the SVal wraps. if (SymbolRef sym = val.getAsSymbol()) { - state = state->set<SelfFlag>(sym, getSelfFlags(val, state) | flag); + state = state->set<SelfFlag>(sym, + SelfFlagEnum(getSelfFlags(val, state) | flag)); C.addTransition(state); } } @@ -156,10 +157,7 @@ void ObjCSelfInitChecker::checkForInvalidSelf(const Expr *E, CheckerContext &C, if (!N) return; - if (!BT) - BT.reset(new BugType(this, "Missing \"self = [(super or self) init...]\"", - categories::CoreFoundationObjectiveC)); - C.emitReport(std::make_unique<PathSensitiveBugReport>(*BT, errorStr, N)); + C.emitReport(std::make_unique<PathSensitiveBugReport>(BT, errorStr, N)); } void ObjCSelfInitChecker::checkPostObjCMessage(const ObjCMethodCall &Msg, @@ -251,11 +249,12 @@ void ObjCSelfInitChecker::checkPreCall(const CallEvent &CE, for (unsigned i = 0; i < NumArgs; ++i) { SVal argV = CE.getArgSVal(i); if (isSelfVar(argV, C)) { - unsigned selfFlags = getSelfFlags(state->getSVal(argV.castAs<Loc>()), C); + SelfFlagEnum selfFlags = + getSelfFlags(state->getSVal(argV.castAs<Loc>()), C); C.addTransition(state->set<PreCallSelfFlags>(selfFlags)); return; } else if (hasSelfFlag(argV, SelfFlag_Self, C)) { - unsigned selfFlags = getSelfFlags(argV, C); + SelfFlagEnum selfFlags = getSelfFlags(argV, C); C.addTransition(state->set<PreCallSelfFlags>(selfFlags)); return; } @@ -270,7 +269,7 @@ void ObjCSelfInitChecker::checkPostCall(const CallEvent &CE, return; ProgramStateRef state = C.getState(); - SelfFlagEnum prevFlags = (SelfFlagEnum)state->get<PreCallSelfFlags>(); + SelfFlagEnum prevFlags = state->get<PreCallSelfFlags>(); if (!prevFlags) return; state = state->remove<PreCallSelfFlags>(); @@ -338,7 +337,7 @@ void ObjCSelfInitChecker::printState(raw_ostream &Out, ProgramStateRef State, const char *NL, const char *Sep) const { SelfFlagTy FlagMap = State->get<SelfFlag>(); bool DidCallInit = State->get<CalledInit>(); - SelfFlagEnum PreCallFlags = (SelfFlagEnum)State->get<PreCallSelfFlags>(); + SelfFlagEnum PreCallFlags = State->get<PreCallSelfFlags>(); if (FlagMap.isEmpty() && !DidCallInit && !PreCallFlags) return; @@ -360,18 +359,17 @@ void ObjCSelfInitChecker::printState(raw_ostream &Out, ProgramStateRef State, } Out << NL; - for (SelfFlagTy::iterator I = FlagMap.begin(), E = FlagMap.end(); - I != E; ++I) { - Out << I->first << " : "; + for (auto [Sym, Flag] : FlagMap) { + Out << Sym << " : "; - if (I->second == SelfFlag_None) + if (Flag == SelfFlag_None) Out << "none"; - if (I->second & SelfFlag_Self) + if (Flag & SelfFlag_Self) Out << "self variable"; - if (I->second & SelfFlag_InitRes) { - if (I->second != SelfFlag_InitRes) + if (Flag & SelfFlag_InitRes) { + if (Flag != SelfFlag_InitRes) Out << " | "; Out << "result of init method"; } @@ -411,7 +409,7 @@ static bool isSelfVar(SVal location, CheckerContext &C) { AnalysisDeclContext *analCtx = C.getCurrentAnalysisDeclContext(); if (!analCtx->getSelfDecl()) return false; - if (!location.getAs<loc::MemRegionVal>()) + if (!isa<loc::MemRegionVal>(location)) return false; loc::MemRegionVal MRV = location.castAs<loc::MemRegionVal>(); diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp index 3547b7bb61a2..eb40711812e1 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp @@ -26,18 +26,19 @@ namespace { class ObjCSuperDeallocChecker : public Checker<check::PostObjCMessage, check::PreObjCMessage, check::PreCall, check::Location> { - - mutable IdentifierInfo *IIdealloc, *IINSObject; + mutable IdentifierInfo *IIdealloc = nullptr; + mutable IdentifierInfo *IINSObject = nullptr; mutable Selector SELdealloc; - std::unique_ptr<BugType> DoubleSuperDeallocBugType; + const BugType DoubleSuperDeallocBugType{ + this, "[super dealloc] should not be called more than once", + categories::CoreFoundationObjectiveC}; void initIdentifierInfoAndSelectors(ASTContext &Ctx) const; bool isSuperDeallocMessage(const ObjCMethodCall &M) const; public: - ObjCSuperDeallocChecker(); void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; @@ -188,7 +189,7 @@ void ObjCSuperDeallocChecker::reportUseAfterDealloc(SymbolRef Sym, Desc = "Use of 'self' after it has been deallocated"; // Generate the report. - auto BR = std::make_unique<PathSensitiveBugReport>(*DoubleSuperDeallocBugType, + auto BR = std::make_unique<PathSensitiveBugReport>(DoubleSuperDeallocBugType, Desc, ErrNode); BR->addRange(S->getSourceRange()); BR->addVisitor(std::make_unique<SuperDeallocBRVisitor>(Sym)); @@ -213,14 +214,6 @@ void ObjCSuperDeallocChecker::diagnoseCallArguments(const CallEvent &CE, } } -ObjCSuperDeallocChecker::ObjCSuperDeallocChecker() - : IIdealloc(nullptr), IINSObject(nullptr) { - - DoubleSuperDeallocBugType.reset( - new BugType(this, "[super dealloc] should not be called more than once", - categories::CoreFoundationObjectiveC)); -} - void ObjCSuperDeallocChecker::initIdentifierInfoAndSelectors(ASTContext &Ctx) const { if (IIdealloc) diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp index c9828c36a06a..1c2d84254d46 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp @@ -12,16 +12,17 @@ // //===----------------------------------------------------------------------===// -#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" -#include "clang/Analysis/PathDiagnostic.h" #include "clang/AST/Attr.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/Expr.h" #include "clang/AST/ExprObjC.h" +#include "clang/Analysis/PathDiagnostic.h" #include "clang/Basic/LangOptions.h" #include "clang/Basic/SourceManager.h" +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" #include "clang/StaticAnalyzer/Core/Checker.h" +#include "llvm/ADT/STLExtras.h" using namespace clang; using namespace ento; @@ -48,9 +49,7 @@ static void Scan(IvarUsageMap& M, const Stmt *S) { } if (const PseudoObjectExpr *POE = dyn_cast<PseudoObjectExpr>(S)) - for (PseudoObjectExpr::const_semantics_iterator - i = POE->semantics_begin(), e = POE->semantics_end(); i != e; ++i) { - const Expr *sub = *i; + for (const Expr *sub : POE->semantics()) { if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(sub)) sub = OVE->getSourceExpr(); Scan(M, sub); @@ -134,8 +133,8 @@ static void checkObjCUnusedIvar(const ObjCImplementationDecl *D, // Any potentially unused ivars? bool hasUnused = false; - for (IvarUsageMap::iterator I = M.begin(), E = M.end(); I!=E; ++I) - if (I->second == Unused) { + for (IVarState State : llvm::make_second_range(M)) + if (State == Unused) { hasUnused = true; break; } @@ -152,16 +151,16 @@ static void checkObjCUnusedIvar(const ObjCImplementationDecl *D, Scan(M, D->getDeclContext(), SM.getFileID(D->getLocation()), SM); // Find ivars that are unused. - for (IvarUsageMap::iterator I = M.begin(), E = M.end(); I!=E; ++I) - if (I->second == Unused) { + for (auto [Ivar, State] : M) + if (State == Unused) { std::string sbuf; llvm::raw_string_ostream os(sbuf); - os << "Instance variable '" << *I->first << "' in class '" << *ID + os << "Instance variable '" << *Ivar << "' in class '" << *ID << "' is never used by the methods in its @implementation " "(although it may be used by category methods)."; PathDiagnosticLocation L = - PathDiagnosticLocation::create(I->first, BR.getSourceManager()); + PathDiagnosticLocation::create(Ivar, BR.getSourceManager()); BR.EmitBasicReport(D, Checker, "Unused instance variable", "Optimization", os.str(), L); } diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PaddingChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PaddingChecker.cpp index 40472ccfe7e6..eee9449f3180 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PaddingChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PaddingChecker.cpp @@ -32,7 +32,7 @@ using namespace ento; namespace { class PaddingChecker : public Checker<check::ASTDecl<TranslationUnitDecl>> { private: - mutable std::unique_ptr<BugType> PaddingBug; + const BugType PaddingBug{this, "Excessive Padding", "Performance"}; mutable BugReporter *BR; public: @@ -182,7 +182,7 @@ public: return false; }; - if (std::any_of(RD->field_begin(), RD->field_end(), IsTrickyField)) + if (llvm::any_of(RD->fields(), IsTrickyField)) return true; return false; } @@ -273,7 +273,7 @@ public: SmallVector<const FieldDecl *, 20> OptimalFieldsOrder; while (!Fields.empty()) { unsigned TrailingZeros = - llvm::countTrailingZeros((unsigned long long)NewOffset.getQuantity()); + llvm::countr_zero((unsigned long long)NewOffset.getQuantity()); // If NewOffset is zero, then countTrailingZeros will be 64. Shifting // 64 will overflow our unsigned long long. Shifting 63 will turn // our long long (and CharUnits internal type) negative. So shift 62. @@ -310,10 +310,6 @@ public: void reportRecord( const RecordDecl *RD, CharUnits BaselinePad, CharUnits OptimalPad, const SmallVector<const FieldDecl *, 20> &OptimalFieldsOrder) const { - if (!PaddingBug) - PaddingBug = - std::make_unique<BugType>(this, "Excessive Padding", "Performance"); - SmallString<100> Buf; llvm::raw_svector_ostream Os(Buf); Os << "Excessive padding in '"; @@ -332,17 +328,16 @@ public: } Os << " (" << BaselinePad.getQuantity() << " padding bytes, where " - << OptimalPad.getQuantity() << " is optimal). \n" - << "Optimal fields order: \n"; + << OptimalPad.getQuantity() << " is optimal). " + << "Optimal fields order: "; for (const auto *FD : OptimalFieldsOrder) - Os << FD->getName() << ", \n"; + Os << FD->getName() << ", "; Os << "consider reordering the fields or adding explicit padding " "members."; PathDiagnosticLocation CELoc = PathDiagnosticLocation::create(RD, BR->getSourceManager()); - auto Report = - std::make_unique<BasicBugReport>(*PaddingBug, Os.str(), CELoc); + auto Report = std::make_unique<BasicBugReport>(PaddingBug, Os.str(), CELoc); Report->setDeclWithIssue(RD); Report->addRange(RD->getSourceRange()); BR->emitReport(std::move(Report)); diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp index d3e2849a0ce6..1141f07428b4 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; + const BugType BT_pointerArith{this, "Dangerous pointer arithmetic"}; + const BugType BT_polyArray{this, "Dangerous pointer arithmetic"}; mutable llvm::SmallSet<IdentifierInfo *, 8> AllocFunctions; public: @@ -79,10 +80,9 @@ void PointerArithChecker::checkDeadSymbols(SymbolReaper &SR, // see http://reviews.llvm.org/D14203 for further information. /*ProgramStateRef State = C.getState(); RegionStateTy RegionStates = State->get<RegionState>(); - for (RegionStateTy::iterator I = RegionStates.begin(), E = RegionStates.end(); - I != E; ++I) { - if (!SR.isLiveRegion(I->first)) - State = State->remove<RegionState>(I->first); + for (const MemRegion *Reg: llvm::make_first_range(RegionStates)) { + if (!SR.isLiveRegion(Reg)) + State = State->remove<RegionState>(Reg); } C.addTransition(State);*/ } @@ -168,13 +168,10 @@ void PointerArithChecker::reportPointerArithMisuse(const Expr *E, if (!IsPolymorphic) 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); + 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,13 +188,10 @@ void PointerArithChecker::reportPointerArithMisuse(const Expr *E, return; 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); + 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..2438cf30b39b 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; + const BugType BT{this, "Pointer subtraction"}; public: void checkPreStmt(const BinaryOperator *B, CheckerContext &C) const; @@ -58,13 +59,10 @@ void PointerSubChecker::checkPreStmt(const BinaryOperator *B, return; 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); + 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/PthreadLockChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp index ee71b55a39e6..fa8572cf85ed 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp @@ -21,6 +21,7 @@ #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" @@ -77,7 +78,7 @@ public: CK_C11LockChecker, CK_NumCheckKinds }; - DefaultBool ChecksEnabled[CK_NumCheckKinds]; + bool ChecksEnabled[CK_NumCheckKinds] = {false}; CheckerNameRef CheckNames[CK_NumCheckKinds]; private: @@ -86,7 +87,7 @@ private: CheckerKind CheckKind) const; CallDescriptionMap<FnCheck> PThreadCallbacks = { // Init. - {{"pthread_mutex_init", 2}, &PthreadLockChecker::InitAnyLock}, + {{{"pthread_mutex_init"}, 2}, &PthreadLockChecker::InitAnyLock}, // TODO: pthread_rwlock_init(2 arguments). // TODO: lck_mtx_init(3 arguments). // TODO: lck_mtx_alloc_init(2 arguments) => returns the mutex. @@ -94,74 +95,74 @@ private: // TODO: lck_rw_alloc_init(2 arguments) => returns the mutex. // Acquire. - {{"pthread_mutex_lock", 1}, &PthreadLockChecker::AcquirePthreadLock}, - {{"pthread_rwlock_rdlock", 1}, &PthreadLockChecker::AcquirePthreadLock}, - {{"pthread_rwlock_wrlock", 1}, &PthreadLockChecker::AcquirePthreadLock}, - {{"lck_mtx_lock", 1}, &PthreadLockChecker::AcquireXNULock}, - {{"lck_rw_lock_exclusive", 1}, &PthreadLockChecker::AcquireXNULock}, - {{"lck_rw_lock_shared", 1}, &PthreadLockChecker::AcquireXNULock}, + {{{"pthread_mutex_lock"}, 1}, &PthreadLockChecker::AcquirePthreadLock}, + {{{"pthread_rwlock_rdlock"}, 1}, &PthreadLockChecker::AcquirePthreadLock}, + {{{"pthread_rwlock_wrlock"}, 1}, &PthreadLockChecker::AcquirePthreadLock}, + {{{"lck_mtx_lock"}, 1}, &PthreadLockChecker::AcquireXNULock}, + {{{"lck_rw_lock_exclusive"}, 1}, &PthreadLockChecker::AcquireXNULock}, + {{{"lck_rw_lock_shared"}, 1}, &PthreadLockChecker::AcquireXNULock}, // Try. - {{"pthread_mutex_trylock", 1}, &PthreadLockChecker::TryPthreadLock}, - {{"pthread_rwlock_tryrdlock", 1}, &PthreadLockChecker::TryPthreadLock}, - {{"pthread_rwlock_trywrlock", 1}, &PthreadLockChecker::TryPthreadLock}, - {{"lck_mtx_try_lock", 1}, &PthreadLockChecker::TryXNULock}, - {{"lck_rw_try_lock_exclusive", 1}, &PthreadLockChecker::TryXNULock}, - {{"lck_rw_try_lock_shared", 1}, &PthreadLockChecker::TryXNULock}, + {{{"pthread_mutex_trylock"}, 1}, &PthreadLockChecker::TryPthreadLock}, + {{{"pthread_rwlock_tryrdlock"}, 1}, &PthreadLockChecker::TryPthreadLock}, + {{{"pthread_rwlock_trywrlock"}, 1}, &PthreadLockChecker::TryPthreadLock}, + {{{"lck_mtx_try_lock"}, 1}, &PthreadLockChecker::TryXNULock}, + {{{"lck_rw_try_lock_exclusive"}, 1}, &PthreadLockChecker::TryXNULock}, + {{{"lck_rw_try_lock_shared"}, 1}, &PthreadLockChecker::TryXNULock}, // Release. - {{"pthread_mutex_unlock", 1}, &PthreadLockChecker::ReleaseAnyLock}, - {{"pthread_rwlock_unlock", 1}, &PthreadLockChecker::ReleaseAnyLock}, - {{"lck_mtx_unlock", 1}, &PthreadLockChecker::ReleaseAnyLock}, - {{"lck_rw_unlock_exclusive", 1}, &PthreadLockChecker::ReleaseAnyLock}, - {{"lck_rw_unlock_shared", 1}, &PthreadLockChecker::ReleaseAnyLock}, - {{"lck_rw_done", 1}, &PthreadLockChecker::ReleaseAnyLock}, + {{{"pthread_mutex_unlock"}, 1}, &PthreadLockChecker::ReleaseAnyLock}, + {{{"pthread_rwlock_unlock"}, 1}, &PthreadLockChecker::ReleaseAnyLock}, + {{{"lck_mtx_unlock"}, 1}, &PthreadLockChecker::ReleaseAnyLock}, + {{{"lck_rw_unlock_exclusive"}, 1}, &PthreadLockChecker::ReleaseAnyLock}, + {{{"lck_rw_unlock_shared"}, 1}, &PthreadLockChecker::ReleaseAnyLock}, + {{{"lck_rw_done"}, 1}, &PthreadLockChecker::ReleaseAnyLock}, // Destroy. - {{"pthread_mutex_destroy", 1}, &PthreadLockChecker::DestroyPthreadLock}, - {{"lck_mtx_destroy", 2}, &PthreadLockChecker::DestroyXNULock}, + {{{"pthread_mutex_destroy"}, 1}, &PthreadLockChecker::DestroyPthreadLock}, + {{{"lck_mtx_destroy"}, 2}, &PthreadLockChecker::DestroyXNULock}, // TODO: pthread_rwlock_destroy(1 argument). // TODO: lck_rw_destroy(2 arguments). }; CallDescriptionMap<FnCheck> FuchsiaCallbacks = { // Init. - {{"spin_lock_init", 1}, &PthreadLockChecker::InitAnyLock}, + {{{"spin_lock_init"}, 1}, &PthreadLockChecker::InitAnyLock}, // Acquire. - {{"spin_lock", 1}, &PthreadLockChecker::AcquirePthreadLock}, - {{"spin_lock_save", 3}, &PthreadLockChecker::AcquirePthreadLock}, - {{"sync_mutex_lock", 1}, &PthreadLockChecker::AcquirePthreadLock}, - {{"sync_mutex_lock_with_waiter", 1}, + {{{"spin_lock"}, 1}, &PthreadLockChecker::AcquirePthreadLock}, + {{{"spin_lock_save"}, 3}, &PthreadLockChecker::AcquirePthreadLock}, + {{{"sync_mutex_lock"}, 1}, &PthreadLockChecker::AcquirePthreadLock}, + {{{"sync_mutex_lock_with_waiter"}, 1}, &PthreadLockChecker::AcquirePthreadLock}, // Try. - {{"spin_trylock", 1}, &PthreadLockChecker::TryFuchsiaLock}, - {{"sync_mutex_trylock", 1}, &PthreadLockChecker::TryFuchsiaLock}, - {{"sync_mutex_timedlock", 2}, &PthreadLockChecker::TryFuchsiaLock}, + {{{"spin_trylock"}, 1}, &PthreadLockChecker::TryFuchsiaLock}, + {{{"sync_mutex_trylock"}, 1}, &PthreadLockChecker::TryFuchsiaLock}, + {{{"sync_mutex_timedlock"}, 2}, &PthreadLockChecker::TryFuchsiaLock}, // Release. - {{"spin_unlock", 1}, &PthreadLockChecker::ReleaseAnyLock}, - {{"spin_unlock_restore", 3}, &PthreadLockChecker::ReleaseAnyLock}, - {{"sync_mutex_unlock", 1}, &PthreadLockChecker::ReleaseAnyLock}, + {{{"spin_unlock"}, 1}, &PthreadLockChecker::ReleaseAnyLock}, + {{{"spin_unlock_restore"}, 3}, &PthreadLockChecker::ReleaseAnyLock}, + {{{"sync_mutex_unlock"}, 1}, &PthreadLockChecker::ReleaseAnyLock}, }; CallDescriptionMap<FnCheck> C11Callbacks = { // Init. - {{"mtx_init", 2}, &PthreadLockChecker::InitAnyLock}, + {{{"mtx_init"}, 2}, &PthreadLockChecker::InitAnyLock}, // Acquire. - {{"mtx_lock", 1}, &PthreadLockChecker::AcquirePthreadLock}, + {{{"mtx_lock"}, 1}, &PthreadLockChecker::AcquirePthreadLock}, // Try. - {{"mtx_trylock", 1}, &PthreadLockChecker::TryC11Lock}, - {{"mtx_timedlock", 2}, &PthreadLockChecker::TryC11Lock}, + {{{"mtx_trylock"}, 1}, &PthreadLockChecker::TryC11Lock}, + {{{"mtx_timedlock"}, 2}, &PthreadLockChecker::TryC11Lock}, // Release. - {{"mtx_unlock", 1}, &PthreadLockChecker::ReleaseAnyLock}, + {{{"mtx_unlock"}, 1}, &PthreadLockChecker::ReleaseAnyLock}, // Destroy - {{"mtx_destroy", 1}, &PthreadLockChecker::DestroyPthreadLock}, + {{{"mtx_destroy"}, 1}, &PthreadLockChecker::DestroyPthreadLock}, }; ProgramStateRef resolvePossiblyDestroyedMutex(ProgramStateRef state, @@ -290,6 +291,7 @@ ProgramStateRef PthreadLockChecker::resolvePossiblyDestroyedMutex( // Existence in DestroyRetVal ensures existence in LockMap. // Existence in Destroyed also ensures that the lock state for lockR is either // UntouchedAndPossiblyDestroyed or UnlockedAndPossiblyDestroyed. + assert(lstate); assert(lstate->isUntouchedAndPossiblyDestroyed() || lstate->isUnlockedAndPossiblyDestroyed()); @@ -681,9 +683,7 @@ ProgramStateRef PthreadLockChecker::checkRegionChanges( // We assume that system library function wouldn't touch the mutex unless // it takes the mutex explicitly as an argument. // FIXME: This is a bit quadratic. - if (IsLibraryFunction && - std::find(ExplicitRegions.begin(), ExplicitRegions.end(), R) == - ExplicitRegions.end()) + if (IsLibraryFunction && !llvm::is_contained(ExplicitRegions, R)) continue; State = State->remove<LockMap>(R); diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp index 3f3267ff9391..7e74b418b335 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp @@ -14,6 +14,7 @@ #include "RetainCountChecker.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include <optional> using namespace clang; using namespace ento; @@ -45,7 +46,7 @@ static ProgramStateRef removeRefBinding(ProgramStateRef State, SymbolRef Sym) { void RefVal::print(raw_ostream &Out) const { if (!T.isNull()) - Out << "Tracked " << T.getAsString() << " | "; + Out << "Tracked " << T << " | "; switch (getKind()) { default: llvm_unreachable("Invalid RefVal kind"); @@ -154,10 +155,8 @@ void RetainCountChecker::checkPostStmt(const BlockExpr *BE, ProgramStateRef state = C.getState(); auto *R = cast<BlockDataRegion>(C.getSVal(BE).getAsRegion()); - BlockDataRegion::referenced_vars_iterator I = R->referenced_vars_begin(), - E = R->referenced_vars_end(); - - if (I == E) + auto ReferencedVars = R->referenced_vars(); + if (ReferencedVars.empty()) return; // FIXME: For now we invalidate the tracking of all symbols passed to blocks @@ -167,8 +166,8 @@ void RetainCountChecker::checkPostStmt(const BlockExpr *BE, const LocationContext *LC = C.getLocationContext(); MemRegionManager &MemMgr = C.getSValBuilder().getRegionManager(); - for ( ; I != E; ++I) { - const VarRegion *VR = I.getCapturedRegion(); + for (auto Var : ReferencedVars) { + const VarRegion *VR = Var.getCapturedRegion(); if (VR->getSuperRegion() == R) { VR = MemMgr.getVarRegion(VR->getDecl(), LC); } @@ -284,13 +283,13 @@ void RetainCountChecker::checkPostStmt(const ObjCBoxedExpr *Ex, void RetainCountChecker::checkPostStmt(const ObjCIvarRefExpr *IRE, CheckerContext &C) const { - Optional<Loc> IVarLoc = C.getSVal(IRE).getAs<Loc>(); + std::optional<Loc> IVarLoc = C.getSVal(IRE).getAs<Loc>(); if (!IVarLoc) return; ProgramStateRef State = C.getState(); SymbolRef Sym = State->getSVal(*IVarLoc).getAsSymbol(); - if (!Sym || !dyn_cast_or_null<ObjCIvarRegion>(Sym->getOriginRegion())) + if (!Sym || !isa_and_nonnull<ObjCIvarRegion>(Sym->getOriginRegion())) return; // Accessing an ivar directly is unusual. If we've done that, be more @@ -412,15 +411,15 @@ static QualType GetReturnType(const Expr *RetE, ASTContext &Ctx) { return RetTy; } -static Optional<RefVal> refValFromRetEffect(RetEffect RE, - QualType ResultTy) { +static std::optional<RefVal> refValFromRetEffect(RetEffect RE, + QualType ResultTy) { if (RE.isOwned()) { return RefVal::makeOwned(RE.getObjKind(), ResultTy); } else if (RE.notOwned()) { return RefVal::makeNotOwned(RE.getObjKind(), ResultTy); } - return None; + return std::nullopt; } static bool isPointerToObject(QualType QT) { @@ -692,7 +691,7 @@ void RetainCountChecker::checkSummary(const RetainSummary &Summ, assert(Ex); ResultTy = GetReturnType(Ex, C.getASTContext()); } - if (Optional<RefVal> updatedRefVal = refValFromRetEffect(RE, ResultTy)) + if (std::optional<RefVal> updatedRefVal = refValFromRetEffect(RE, ResultTy)) state = setRefBinding(state, Sym, *updatedRefVal); } @@ -767,7 +766,7 @@ ProgramStateRef RetainCountChecker::updateSymbol(ProgramStateRef state, break; } - LLVM_FALLTHROUGH; + [[fallthrough]]; case DoNothing: return state; @@ -907,7 +906,7 @@ bool RetainCountChecker::evalCall(const CallEvent &Call, const LocationContext *LCtx = C.getLocationContext(); using BehaviorSummary = RetainSummaryManager::BehaviorSummary; - Optional<BehaviorSummary> BSmr = + std::optional<BehaviorSummary> BSmr = SmrMgr.canEval(CE, FD, hasTrustedImplementationAnnotation); // See if it's one of the specific functions we know how to eval. @@ -945,7 +944,8 @@ bool RetainCountChecker::evalCall(const CallEvent &Call, // Assume that output is zero on the other branch. NullOutputState = NullOutputState->BindExpr( - CE, LCtx, C.getSValBuilder().makeNull(), /*Invalidate=*/false); + CE, LCtx, C.getSValBuilder().makeNullWithType(ResultTy), + /*Invalidate=*/false); C.addTransition(NullOutputState, &getCastFailTag()); // And on the original branch assume that both input and @@ -1188,14 +1188,14 @@ ProgramStateRef RetainCountChecker::checkRegionChanges( if (!invalidated) return state; - llvm::SmallPtrSet<SymbolRef, 8> WhitelistedSymbols; + llvm::SmallPtrSet<SymbolRef, 8> AllowedSymbols; for (const MemRegion *I : ExplicitRegions) if (const SymbolicRegion *SR = I->StripCasts()->getAs<SymbolicRegion>()) - WhitelistedSymbols.insert(SR->getSymbol()); + AllowedSymbols.insert(SR->getSymbol()); for (SymbolRef sym : *invalidated) { - if (WhitelistedSymbols.count(sym)) + if (AllowedSymbols.count(sym)) continue; // Remove any existing reference-count binding. state = removeRefBinding(state, sym); @@ -1335,7 +1335,7 @@ void RetainCountChecker::checkBeginFunction(CheckerContext &Ctx) const { RetainSummaryManager &SmrMgr = getSummaryManager(Ctx); const LocationContext *LCtx = Ctx.getLocationContext(); const Decl *D = LCtx->getDecl(); - Optional<AnyCall> C = AnyCall::forDecl(D); + std::optional<AnyCall> C = AnyCall::forDecl(D); if (!C || SmrMgr.isTrustedReferenceCountImplementation(D)) return; diff --git a/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 64ac6bc4c06b..c3acb73ba717 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp @@ -15,6 +15,7 @@ #include "RetainCountChecker.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" +#include <optional> using namespace clang; using namespace ento; @@ -73,11 +74,8 @@ RefCountBug::RefCountBug(CheckerNameRef Checker, RefCountBugKind BT) static bool isNumericLiteralExpression(const Expr *E) { // FIXME: This set of cases was copied from SemaExprObjC. - return isa<IntegerLiteral>(E) || - isa<CharacterLiteral>(E) || - isa<FloatingLiteral>(E) || - isa<ObjCBoolLiteralExpr>(E) || - isa<CXXBoolLiteralExpr>(E); + return isa<IntegerLiteral, CharacterLiteral, FloatingLiteral, + ObjCBoolLiteralExpr, CXXBoolLiteralExpr>(E); } /// If type represents a pointer to CXXRecordDecl, @@ -168,13 +166,12 @@ static bool shouldGenerateNote(llvm::raw_string_ostream &os, /// Finds argument index of the out paramter in the call @c S /// corresponding to the symbol @c Sym. -/// If none found, returns None. -static Optional<unsigned> findArgIdxOfSymbol(ProgramStateRef CurrSt, - const LocationContext *LCtx, - SymbolRef &Sym, - Optional<CallEventRef<>> CE) { +/// If none found, returns std::nullopt. +static std::optional<unsigned> +findArgIdxOfSymbol(ProgramStateRef CurrSt, const LocationContext *LCtx, + SymbolRef &Sym, std::optional<CallEventRef<>> CE) { if (!CE) - return None; + return std::nullopt; for (unsigned Idx = 0; Idx < (*CE)->getNumArgs(); Idx++) if (const MemRegion *MR = (*CE)->getArgSVal(Idx).getAsRegion()) @@ -182,25 +179,25 @@ static Optional<unsigned> findArgIdxOfSymbol(ProgramStateRef CurrSt, if (CurrSt->getSVal(MR, TR->getValueType()).getAsSymbol() == Sym) return Idx; - return None; + return std::nullopt; } -static Optional<std::string> findMetaClassAlloc(const Expr *Callee) { +static std::optional<std::string> findMetaClassAlloc(const Expr *Callee) { if (const auto *ME = dyn_cast<MemberExpr>(Callee)) { if (ME->getMemberDecl()->getNameAsString() != "alloc") - return None; + return std::nullopt; const Expr *This = ME->getBase()->IgnoreParenImpCasts(); if (const auto *DRE = dyn_cast<DeclRefExpr>(This)) { const ValueDecl *VD = DRE->getDecl(); if (VD->getNameAsString() != "metaClass") - return None; + return std::nullopt; if (const auto *RD = dyn_cast<CXXRecordDecl>(VD->getDeclContext())) return RD->getNameAsString(); } } - return None; + return std::nullopt; } static std::string findAllocatedObjectName(const Stmt *S, QualType QT) { @@ -237,8 +234,8 @@ static void generateDiagnosticsForCallLike(ProgramStateRef CurrSt, os << "Operator 'new'"; } else { assert(isa<ObjCMessageExpr>(S)); - CallEventRef<ObjCMethodCall> Call = - Mgr.getObjCMethodCall(cast<ObjCMessageExpr>(S), CurrSt, LCtx); + CallEventRef<ObjCMethodCall> Call = Mgr.getObjCMethodCall( + cast<ObjCMessageExpr>(S), CurrSt, LCtx, {nullptr, 0}); switch (Call->getMessageKind()) { case OCM_Message: @@ -253,7 +250,7 @@ static void generateDiagnosticsForCallLike(ProgramStateRef CurrSt, } } - Optional<CallEventRef<>> CE = Mgr.getCall(S, CurrSt, LCtx); + std::optional<CallEventRef<>> CE = Mgr.getCall(S, CurrSt, LCtx, {nullptr, 0}); auto Idx = findArgIdxOfSymbol(CurrSt, LCtx, Sym, CE); // If index is not found, we assume that the symbol was returned. @@ -264,14 +261,12 @@ static void generateDiagnosticsForCallLike(ProgramStateRef CurrSt, } if (CurrV.getObjKind() == ObjKind::CF) { - os << "a Core Foundation object of type '" - << Sym->getType().getAsString() << "' with a "; + os << "a Core Foundation object of type '" << Sym->getType() << "' with a "; } else if (CurrV.getObjKind() == ObjKind::OS) { os << "an OSObject of type '" << findAllocatedObjectName(S, Sym->getType()) << "' with a "; } else if (CurrV.getObjKind() == ObjKind::Generalized) { - os << "an object of type '" << Sym->getType().getAsString() - << "' with a "; + os << "an object of type '" << Sym->getType() << "' with a "; } else { assert(CurrV.getObjKind() == ObjKind::ObjC); QualType T = Sym->getType(); @@ -279,8 +274,7 @@ static void generateDiagnosticsForCallLike(ProgramStateRef CurrSt, os << "an Objective-C object with a "; } else { const ObjCObjectPointerType *PT = cast<ObjCObjectPointerType>(T); - os << "an instance of " << PT->getPointeeType().getAsString() - << " with a "; + os << "an instance of " << PT->getPointeeType() << " with a "; } } @@ -608,16 +602,17 @@ RefCountReportVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC, return std::move(P); } -static Optional<std::string> describeRegion(const MemRegion *MR) { +static std::optional<std::string> describeRegion(const MemRegion *MR) { if (const auto *VR = dyn_cast_or_null<VarRegion>(MR)) return std::string(VR->getDecl()->getName()); // Once we support more storage locations for bindings, // this would need to be improved. - return None; + return std::nullopt; } using Bindings = llvm::SmallVector<std::pair<const MemRegion *, SVal>, 4>; +namespace { class VarBindingsCollector : public StoreManager::BindingsHandler { SymbolRef Sym; Bindings &Result; @@ -638,6 +633,7 @@ public: return true; } }; +} // namespace Bindings getAllVarBindingsForSymbol(ProgramStateManager &Manager, const ExplodedNode *Node, SymbolRef Sym) { @@ -734,7 +730,7 @@ static AllocationInfo GetAllocationSite(ProgramStateManager &StateMgr, const LocationContext *InterestingMethodContext = nullptr; if (InitMethodContext) { const ProgramPoint AllocPP = AllocationNode->getLocation(); - if (Optional<StmtPoint> SP = AllocPP.getAs<StmtPoint>()) + if (std::optional<StmtPoint> SP = AllocPP.getAs<StmtPoint>()) if (const ObjCMessageExpr *ME = SP->getStmtAs<ObjCMessageExpr>()) if (ME->getMethodFamily() == OMF_alloc) InterestingMethodContext = InitMethodContext; @@ -777,7 +773,7 @@ RefLeakReportVisitor::getEndPath(BugReporterContext &BRC, os << "Object leaked: "; - Optional<std::string> RegionDescription = describeRegion(LastBinding); + std::optional<std::string> RegionDescription = describeRegion(LastBinding); if (RegionDescription) { os << "object allocated and stored into '" << *RegionDescription << '\''; } else { @@ -790,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 " @@ -923,7 +916,7 @@ void RefLeakReport::createDescription(CheckerContext &Ctx) { llvm::raw_string_ostream os(Description); os << "Potential leak of an object"; - Optional<std::string> RegionDescription = + std::optional<std::string> RegionDescription = describeRegion(AllocBindingToReport); if (RegionDescription) { os << " stored into '" << *RegionDescription << '\''; @@ -975,7 +968,7 @@ void RefLeakReport::findBindingToReport(CheckerContext &Ctx, // Let's pick one of them at random (if there is something to pick from). AllocBindingToReport = AllVarBindings[0].first; - // Because 'AllocBindingToReport' is not the the same as + // Because 'AllocBindingToReport' is not the same as // 'AllocFirstBinding', we need to explain how the leaking object // got from one to another. // diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp index 885750218b9e..09d82ebabd4c 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" @@ -25,7 +26,9 @@ using namespace ento; namespace { class ReturnPointerRangeChecker : public Checker< check::PreStmt<ReturnStmt> > { - mutable std::unique_ptr<BuiltinBug> BT; + // FIXME: This bug correspond to CWE-466. Eventually we should have bug + // types explicitly reference such exploit categories (when applicable). + const BugType BT{this, "Buffer overflow"}; public: void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const; @@ -40,6 +43,10 @@ void ReturnPointerRangeChecker::checkPreStmt(const ReturnStmt *RS, if (!RetE) return; + // Skip "body farmed" functions. + if (RetE->getSourceRange().isInvalid()) + return; + SVal V = C.getSVal(RetE); const MemRegion *R = V.getAsRegion(); @@ -63,32 +70,55 @@ void ReturnPointerRangeChecker::checkPreStmt(const ReturnStmt *RS, if (Idx == ElementCount) return; - ProgramStateRef StInBound = state->assumeInBound(Idx, ElementCount, true); - ProgramStateRef StOutBound = state->assumeInBound(Idx, ElementCount, false); + ProgramStateRef StInBound, StOutBound; + std::tie(StInBound, StOutBound) = state->assumeInBoundDual(Idx, ElementCount); if (StOutBound && !StInBound) { ExplodedNode *N = C.generateErrorNode(StOutBound); if (!N) return; - // 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)")); - - // 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. + 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); - - report->addRange(RetE->getSourceRange()); - C.emitReport(std::move(report)); + auto Report = std::make_unique<PathSensitiveBugReport>(BT, Msg, N); + Report->addRange(RetE->getSourceRange()); + + const auto ConcreteElementCount = ElementCount.getAs<nonloc::ConcreteInt>(); + const auto ConcreteIdx = Idx.getAs<nonloc::ConcreteInt>(); + + const auto *DeclR = ER->getSuperRegion()->getAs<DeclRegion>(); + + if (DeclR) + Report->addNote("Original object declared here", + {DeclR->getDecl(), C.getSourceManager()}); + + if (ConcreteElementCount) { + SmallString<128> SBuf; + llvm::raw_svector_ostream OS(SBuf); + OS << "Original object "; + if (DeclR) { + OS << "'"; + DeclR->getDecl()->printName(OS); + OS << "' "; + } + OS << "is an array of " << ConcreteElementCount->getValue() << " '"; + ER->getValueType().print(OS, + PrintingPolicy(C.getASTContext().getLangOpts())); + OS << "' objects"; + if (ConcreteIdx) { + OS << ", returned pointer points at index " << ConcreteIdx->getValue(); + } + + Report->addNote(SBuf, + {RetE, C.getSourceManager(), C.getLocationContext()}); + } + + bugreporter::trackExpressionValue(N, RetE, *Report); + + C.emitReport(std::move(Report)); } } diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp index 5266cbf86b44..efffbf2ee755 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; + const BugType BT_Undef{this, "Garbage return value"}; + const BugType BT_NullReference{this, "Returning null reference"}; 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, const 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); @@ -93,11 +92,7 @@ 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); + emitBug(C, BT_Undef, "Undefined or garbage value returned to caller", RetE); } void ReturnUndefChecker::checkReference(CheckerContext &C, const Expr *RetE, @@ -112,10 +107,8 @@ 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")); - - 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/ReturnValueChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ReturnValueChecker.cpp index 14ecede17083..c3112ebe4e79 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ReturnValueChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ReturnValueChecker.cpp @@ -14,10 +14,11 @@ #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.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/Optional.h" #include "llvm/ADT/SmallVector.h" +#include <optional> using namespace clang; using namespace ento; @@ -58,7 +59,7 @@ private: } // namespace static std::string getName(const CallEvent &Call) { - std::string Name = ""; + std::string Name; if (const auto *MD = dyn_cast<CXXMethodDecl>(Call.getDecl())) if (const CXXRecordDecl *RD = MD->getParent()) Name += RD->getNameAsString() + "::"; @@ -69,11 +70,11 @@ static std::string getName(const CallEvent &Call) { // The predefinitions ('CDM') could break due to the ever growing code base. // Check for the expected invariants and see whether they apply. -static Optional<bool> isInvariantBreak(bool ExpectedValue, SVal ReturnV, - CheckerContext &C) { +static std::optional<bool> isInvariantBreak(bool ExpectedValue, SVal ReturnV, + CheckerContext &C) { auto ReturnDV = ReturnV.getAs<DefinedOrUnknownSVal>(); if (!ReturnDV) - return None; + return std::nullopt; if (ExpectedValue) return C.getState()->isNull(*ReturnDV).isConstrainedTrue(); @@ -89,7 +90,8 @@ void ReturnValueChecker::checkPostCall(const CallEvent &Call, SVal ReturnV = Call.getReturnValue(); bool ExpectedValue = *RawExpectedValue; - Optional<bool> IsInvariantBreak = isInvariantBreak(ExpectedValue, ReturnV, C); + std::optional<bool> IsInvariantBreak = + isInvariantBreak(ExpectedValue, ReturnV, C); if (!IsInvariantBreak) return; @@ -136,7 +138,8 @@ void ReturnValueChecker::checkEndFunction(const ReturnStmt *RS, SVal ReturnV = State->getSVal(RS->getRetValue(), C.getLocationContext()); bool ExpectedValue = *RawExpectedValue; - Optional<bool> IsInvariantBreak = isInvariantBreak(ExpectedValue, ReturnV, C); + std::optional<bool> IsInvariantBreak = + isInvariantBreak(ExpectedValue, ReturnV, C); if (!IsInvariantBreak) return; diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/STLAlgorithmModeling.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/STLAlgorithmModeling.cpp index 933e0146ff59..788f2875863c 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/STLAlgorithmModeling.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/STLAlgorithmModeling.cpp @@ -12,6 +12,7 @@ #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" @@ -60,7 +61,7 @@ class STLAlgorithmModeling : public Checker<eval::Call> { public: STLAlgorithmModeling() = default; - bool AggressiveStdFindModeling; + bool AggressiveStdFindModeling = false; bool evalCall(const CallEvent &Call, CheckerContext &C) const; }; // @@ -130,7 +131,7 @@ void STLAlgorithmModeling::Find(CheckerContext &C, const CallExpr *CE, nonloc::SymbolVal(NewPos->getOffset()), nonloc::SymbolVal(Pos->getOffset()), SVB.getConditionType()); - assert(GreaterOrEqual.getAs<DefinedSVal>() && + assert(isa<DefinedSVal>(GreaterOrEqual) && "Symbol comparison must be a `DefinedSVal`"); StateFound = StateFound->assume(GreaterOrEqual.castAs<DefinedSVal>(), true); } @@ -152,7 +153,7 @@ void STLAlgorithmModeling::Find(CheckerContext &C, const CallExpr *CE, nonloc::SymbolVal(NewPos->getOffset()), nonloc::SymbolVal(Pos->getOffset()), SVB.getConditionType()); - assert(Less.getAs<DefinedSVal>() && + assert(isa<DefinedSVal>(Less) && "Symbol comparison must be a `DefinedSVal`"); StateFound = StateFound->assume(Less.castAs<DefinedSVal>(), true); } diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp index 8d380ed1b93d..7cbe271dfbf9 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp @@ -17,6 +17,7 @@ #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include <utility> @@ -51,10 +52,13 @@ class SimpleStreamChecker : public Checker<check::PostCall, check::PreCall, check::DeadSymbols, check::PointerEscape> { - CallDescription OpenFn, CloseFn; + const CallDescription OpenFn{{"fopen"}, 2}; + const CallDescription CloseFn{{"fclose"}, 1}; - std::unique_ptr<BugType> DoubleCloseBugType; - std::unique_ptr<BugType> LeakBugType; + const BugType DoubleCloseBugType{this, "Double fclose", + "Unix Stream API Error"}; + const BugType LeakBugType{this, "Resource Leak", "Unix Stream API Error", + /*SuppressOnSink=*/true}; void reportDoubleClose(SymbolRef FileDescSym, const CallEvent &Call, @@ -66,8 +70,6 @@ class SimpleStreamChecker : public Checker<check::PostCall, bool guaranteedNotToCloseFile(const CallEvent &Call) const; public: - SimpleStreamChecker(); - /// Process fopen. void checkPostCall(const CallEvent &Call, CheckerContext &C) const; /// Process fclose. @@ -88,38 +90,12 @@ public: /// state. Let's store it in the ProgramState. REGISTER_MAP_WITH_PROGRAMSTATE(StreamMap, SymbolRef, StreamState) -namespace { -class StopTrackingCallback final : public SymbolVisitor { - ProgramStateRef state; -public: - StopTrackingCallback(ProgramStateRef st) : state(std::move(st)) {} - ProgramStateRef getState() const { return state; } - - bool VisitSymbol(SymbolRef sym) override { - state = state->remove<StreamMap>(sym); - return true; - } -}; -} // end anonymous namespace - -SimpleStreamChecker::SimpleStreamChecker() - : OpenFn("fopen"), CloseFn("fclose", 1) { - // Initialize the bug types. - DoubleCloseBugType.reset( - new BugType(this, "Double fclose", "Unix Stream API Error")); - - // Sinks are higher importance bugs as well as calls to assert() or exit(0). - LeakBugType.reset( - new BugType(this, "Resource Leak", "Unix Stream API Error", - /*SuppressOnSink=*/true)); -} - void SimpleStreamChecker::checkPostCall(const CallEvent &Call, CheckerContext &C) const { if (!Call.isGlobalCFunction()) return; - if (!Call.isCalled(OpenFn)) + if (!OpenFn.matches(Call)) return; // Get the symbolic value corresponding to the file handle. @@ -138,7 +114,7 @@ void SimpleStreamChecker::checkPreCall(const CallEvent &Call, if (!Call.isGlobalCFunction()) return; - if (!Call.isCalled(CloseFn)) + if (!CloseFn.matches(Call)) return; // Get the symbolic value corresponding to the file handle. @@ -176,13 +152,11 @@ void SimpleStreamChecker::checkDeadSymbols(SymbolReaper &SymReaper, ProgramStateRef State = C.getState(); SymbolVector LeakedStreams; StreamMapTy TrackedStreams = State->get<StreamMap>(); - for (StreamMapTy::iterator I = TrackedStreams.begin(), - E = TrackedStreams.end(); I != E; ++I) { - SymbolRef Sym = I->first; + for (auto [Sym, StreamStatus] : TrackedStreams) { bool IsSymDead = SymReaper.isDead(Sym); // Collect leaked symbols. - if (isLeaked(Sym, I->second, IsSymDead, State)) + if (isLeaked(Sym, StreamStatus, IsSymDead, State)) LeakedStreams.push_back(Sym); // Remove the dead symbol from the streams map. @@ -207,7 +181,7 @@ void SimpleStreamChecker::reportDoubleClose(SymbolRef FileDescSym, // Generate the report. auto R = std::make_unique<PathSensitiveBugReport>( - *DoubleCloseBugType, "Closing a previously closed file stream", ErrNode); + DoubleCloseBugType, "Closing a previously closed file stream", ErrNode); R->addRange(Call.getSourceRange()); R->markInteresting(FileDescSym); C.emitReport(std::move(R)); @@ -220,7 +194,7 @@ void SimpleStreamChecker::reportLeaks(ArrayRef<SymbolRef> LeakedStreams, // TODO: Identify the leaked file descriptor. for (SymbolRef LeakedStream : LeakedStreams) { auto R = std::make_unique<PathSensitiveBugReport>( - *LeakBugType, "Opened file is never closed; potential resource leak", + LeakBugType, "Opened file is never closed; potential resource leak", ErrNode); R->markInteresting(LeakedStream); C.emitReport(std::move(R)); @@ -254,11 +228,7 @@ SimpleStreamChecker::checkPointerEscape(ProgramStateRef State, return State; } - for (InvalidatedSymbols::const_iterator I = Escaped.begin(), - E = Escaped.end(); - I != E; ++I) { - SymbolRef Sym = *I; - + for (SymbolRef Sym : Escaped) { // The symbol escaped. Optimistically, assume that the corresponding file // handle will be closed somewhere else. State = State->remove<StreamMap>(Sym); diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtr.h b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtr.h index 6a40f8eda5fa..b4352b450c7f 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtr.h +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtr.h @@ -25,8 +25,6 @@ bool isStdSmartPtrCall(const CallEvent &Call); bool isStdSmartPtr(const CXXRecordDecl *RD); bool isStdSmartPtr(const Expr *E); -bool isStdSmartPtr(const CXXRecordDecl *RD); - /// Returns whether the smart pointer is null or not. bool isNullSmartPtr(const ProgramStateRef State, const MemRegion *ThisRegion); diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp index 09e885e8133f..268fc742f050 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp @@ -23,6 +23,7 @@ #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/CheckerHelpers.h" @@ -30,8 +31,9 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" -#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/Support/ErrorHandling.h" +#include <optional> #include <string> using namespace clang; @@ -47,9 +49,8 @@ class SmartPtrModeling public: // Whether the checker should model for null dereferences of smart pointers. - DefaultBool ModelSmartPtrDereference; + bool ModelSmartPtrDereference = false; bool evalCall(const CallEvent &Call, CheckerContext &C) const; - void checkPreCall(const CallEvent &Call, CheckerContext &C) const; void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; ProgramStateRef checkRegionChanges(ProgramStateRef State, @@ -70,7 +71,8 @@ private: bool handleMoveCtr(const CallEvent &Call, CheckerContext &C, const MemRegion *ThisRegion) const; bool updateMovedSmartPointers(CheckerContext &C, const MemRegion *ThisRegion, - const MemRegion *OtherSmartPtrRegion) const; + const MemRegion *OtherSmartPtrRegion, + const CallEvent &Call) const; void handleBoolConversion(const CallEvent &Call, CheckerContext &C) const; bool handleComparisionOp(const CallEvent &Call, CheckerContext &C) const; bool handleOstreamOperator(const CallEvent &Call, CheckerContext &C) const; @@ -84,10 +86,10 @@ private: using SmartPtrMethodHandlerFn = void (SmartPtrModeling::*)(const CallEvent &Call, CheckerContext &) const; CallDescriptionMap<SmartPtrMethodHandlerFn> SmartPtrMethodHandlers{ - {{"reset"}, &SmartPtrModeling::handleReset}, - {{"release"}, &SmartPtrModeling::handleRelease}, - {{"swap", 1}, &SmartPtrModeling::handleSwapMethod}, - {{"get"}, &SmartPtrModeling::handleGet}}; + {{{"reset"}}, &SmartPtrModeling::handleReset}, + {{{"release"}}, &SmartPtrModeling::handleRelease}, + {{{"swap"}, 1}, &SmartPtrModeling::handleSwapMethod}, + {{{"get"}}, &SmartPtrModeling::handleGet}}; const CallDescription StdSwapCall{{"std", "swap"}, 2}; const CallDescription StdMakeUniqueCall{{"std", "make_unique"}}; const CallDescription StdMakeUniqueForOverwriteCall{ @@ -102,12 +104,8 @@ static bool hasStdClassWithName(const CXXRecordDecl *RD, ArrayRef<llvm::StringLiteral> Names) { if (!RD || !RD->getDeclContext()->isStdNamespace()) return false; - if (RD->getDeclName().isIdentifier()) { - StringRef Name = RD->getName(); - return llvm::any_of(Names, [&Name](StringRef GivenName) -> bool { - return Name == GivenName; - }); - } + if (RD->getDeclName().isIdentifier()) + return llvm::is_contained(Names, RD->getName()); return false; } @@ -203,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) @@ -289,7 +287,7 @@ bool SmartPtrModeling::evalCall(const CallEvent &Call, if (ModelSmartPtrDereference && isStdOstreamOperatorCall(Call)) return handleOstreamOperator(Call, C); - if (Call.isCalled(StdSwapCall)) { + if (StdSwapCall.matches(Call)) { // Check the first arg, if it is of std::unique_ptr type. assert(Call.getNumArgs() == 2 && "std::swap should have two arguments"); const Expr *FirstArg = Call.getArgExpr(0); @@ -298,12 +296,12 @@ bool SmartPtrModeling::evalCall(const CallEvent &Call, return handleSwap(State, Call.getArgSVal(0), Call.getArgSVal(1), C); } - if (Call.isCalled(StdMakeUniqueCall) || - Call.isCalled(StdMakeUniqueForOverwriteCall)) { + if (matchesAny(Call, StdMakeUniqueCall, StdMakeUniqueForOverwriteCall)) { if (!ModelSmartPtrDereference) return false; - - const Optional<SVal> ThisRegionOpt = Call.getReturnValueUnderConstruction(); + + const std::optional<SVal> ThisRegionOpt = + Call.getReturnValueUnderConstruction(); if (!ThisRegionOpt) return false; @@ -383,11 +381,13 @@ bool SmartPtrModeling::evalCall(const CallEvent &Call, if (!ThisRegion) return false; + QualType ThisType = cast<CXXMethodDecl>(Call.getDecl())->getThisType(); + if (CC->getDecl()->isMoveConstructor()) return handleMoveCtr(Call, C, ThisRegion); if (Call.getNumArgs() == 0) { - auto NullVal = C.getSValBuilder().makeNull(); + auto NullVal = C.getSValBuilder().makeNullWithType(ThisType); State = State->set<TrackedRegionMap>(ThisRegion, NullVal); C.addTransition( @@ -588,10 +588,9 @@ void SmartPtrModeling::checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const { // Marking tracked symbols alive TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>(); - for (auto I = TrackedRegions.begin(), E = TrackedRegions.end(); I != E; ++I) { - SVal Val = I->second; - for (auto si = Val.symbol_begin(), se = Val.symbol_end(); si != se; ++si) { - SR.markLive(*si); + for (SVal Val : llvm::make_second_range(TrackedRegions)) { + for (SymbolRef Sym : Val.symbols()) { + SR.markLive(Sym); } } } @@ -644,7 +643,8 @@ void SmartPtrModeling::handleRelease(const CallEvent &Call, *InnerPointVal); } - auto ValueToUpdate = C.getSValBuilder().makeNull(); + QualType ThisType = cast<CXXMethodDecl>(Call.getDecl())->getThisType(); + auto ValueToUpdate = C.getSValBuilder().makeNullWithType(ThisType); State = State->set<TrackedRegionMap>(ThisRegion, ValueToUpdate); C.addTransition(State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR, @@ -742,13 +742,15 @@ bool SmartPtrModeling::handleAssignOp(const CallEvent &Call, if (!ThisRegion) return false; + QualType ThisType = cast<CXXMethodDecl>(Call.getDecl())->getThisType(); + const MemRegion *OtherSmartPtrRegion = OC->getArgSVal(0).getAsRegion(); // In case of 'nullptr' or '0' assigned if (!OtherSmartPtrRegion) { bool AssignedNull = Call.getArgSVal(0).isZeroConstant(); if (!AssignedNull) return false; - auto NullVal = C.getSValBuilder().makeNull(); + auto NullVal = C.getSValBuilder().makeNullWithType(ThisType); State = State->set<TrackedRegionMap>(ThisRegion, NullVal); C.addTransition(State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR, llvm::raw_ostream &OS) { @@ -762,7 +764,7 @@ bool SmartPtrModeling::handleAssignOp(const CallEvent &Call, return true; } - return updateMovedSmartPointers(C, ThisRegion, OtherSmartPtrRegion); + return updateMovedSmartPointers(C, ThisRegion, OtherSmartPtrRegion, Call); } bool SmartPtrModeling::handleMoveCtr(const CallEvent &Call, CheckerContext &C, @@ -771,17 +773,19 @@ bool SmartPtrModeling::handleMoveCtr(const CallEvent &Call, CheckerContext &C, if (!OtherSmartPtrRegion) return false; - return updateMovedSmartPointers(C, ThisRegion, OtherSmartPtrRegion); + return updateMovedSmartPointers(C, ThisRegion, OtherSmartPtrRegion, Call); } bool SmartPtrModeling::updateMovedSmartPointers( CheckerContext &C, const MemRegion *ThisRegion, - const MemRegion *OtherSmartPtrRegion) const { + const MemRegion *OtherSmartPtrRegion, const CallEvent &Call) const { ProgramStateRef State = C.getState(); + QualType ThisType = cast<CXXMethodDecl>(Call.getDecl())->getThisType(); const auto *OtherInnerPtr = State->get<TrackedRegionMap>(OtherSmartPtrRegion); if (OtherInnerPtr) { State = State->set<TrackedRegionMap>(ThisRegion, *OtherInnerPtr); - auto NullVal = C.getSValBuilder().makeNull(); + + auto NullVal = C.getSValBuilder().makeNullWithType(ThisType); State = State->set<TrackedRegionMap>(OtherSmartPtrRegion, NullVal); bool IsArgValNull = OtherInnerPtr->isZeroConstant(); @@ -807,7 +811,8 @@ bool SmartPtrModeling::updateMovedSmartPointers( } else { // In case we dont know anything about value we are moving from // remove the entry from map for which smart pointer got moved to. - auto NullVal = C.getSValBuilder().makeNull(); + // For unique_ptr<A>, Ty will be 'A*'. + auto NullVal = C.getSValBuilder().makeNullWithType(ThisType); State = State->remove<TrackedRegionMap>(ThisRegion); State = State->set<TrackedRegionMap>(OtherSmartPtrRegion, NullVal); C.addTransition(State, C.getNoteTag([OtherSmartPtrRegion, @@ -834,6 +839,8 @@ void SmartPtrModeling::handleBoolConversion(const CallEvent &Call, const MemRegion *ThisRegion = cast<CXXInstanceCall>(&Call)->getCXXThisVal().getAsRegion(); + QualType ThisType = cast<CXXMethodDecl>(Call.getDecl())->getThisType(); + SVal InnerPointerVal; if (const auto *InnerValPtr = State->get<TrackedRegionMap>(ThisRegion)) { InnerPointerVal = *InnerValPtr; @@ -872,7 +879,7 @@ void SmartPtrModeling::handleBoolConversion(const CallEvent &Call, std::tie(NotNullState, NullState) = State->assume(InnerPointerVal.castAs<DefinedOrUnknownSVal>()); - auto NullVal = C.getSValBuilder().makeNull(); + auto NullVal = C.getSValBuilder().makeNullWithType(ThisType); // Explicitly tracking the region as null. NullState = NullState->set<TrackedRegionMap>(ThisRegion, NullVal); diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp index b5c9356322fc..ea09c43cc5ce 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp @@ -11,9 +11,9 @@ // //===----------------------------------------------------------------------===// -#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/AST/ExprCXX.h" #include "clang/Basic/SourceManager.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" @@ -29,11 +29,11 @@ namespace { class StackAddrEscapeChecker : public Checker<check::PreCall, check::PreStmt<ReturnStmt>, check::EndFunction> { - mutable IdentifierInfo *dispatch_semaphore_tII; - 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 IdentifierInfo *dispatch_semaphore_tII = nullptr; + 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 { @@ -42,7 +42,7 @@ public: CK_NumCheckKinds }; - DefaultBool ChecksEnabled[CK_NumCheckKinds]; + bool ChecksEnabled[CK_NumCheckKinds] = {false}; CheckerNameRef CheckNames[CK_NumCheckKinds]; void checkPreCall(const CallEvent &Call, CheckerContext &C) const; @@ -61,7 +61,6 @@ private: ASTContext &Ctx); static SmallVector<const MemRegion *, 4> getCapturedStackRegions(const BlockDataRegion &B, CheckerContext &C); - static bool isArcManagedBlock(const MemRegion *R, CheckerContext &C); static bool isNotInCurrentFrame(const MemRegion *R, CheckerContext &C); }; } // namespace @@ -97,6 +96,14 @@ SourceRange StackAddrEscapeChecker::genName(raw_ostream &os, const MemRegion *R, os << "stack memory associated with local variable '" << VR->getString() << '\''; range = VR->getDecl()->getSourceRange(); + } else if (const auto *LER = dyn_cast<CXXLifetimeExtendedObjectRegion>(R)) { + QualType Ty = LER->getValueType().getLocalUnqualifiedType(); + os << "stack memory associated with temporary object of type '"; + Ty.print(os, Ctx.getPrintingPolicy()); + os << "' lifetime extended by local variable"; + if (const IdentifierInfo *ID = LER->getExtendingDecl()->getIdentifier()) + os << " '" << ID->getName() << '\''; + range = LER->getExpr()->getSourceRange(); } else if (const auto *TOR = dyn_cast<CXXTempObjectRegion>(R)) { QualType Ty = TOR->getValueType().getLocalUnqualifiedType(); os << "stack memory associated with temporary object of type '"; @@ -110,13 +117,6 @@ SourceRange StackAddrEscapeChecker::genName(raw_ostream &os, const MemRegion *R, return range; } -bool StackAddrEscapeChecker::isArcManagedBlock(const MemRegion *R, - CheckerContext &C) { - assert(R && "MemRegion should not be null"); - return C.getASTContext().getLangOpts().ObjCAutoRefCount && - isa<BlockDataRegion>(R); -} - bool StackAddrEscapeChecker::isNotInCurrentFrame(const MemRegion *R, CheckerContext &C) { const StackSpaceRegion *S = cast<StackSpaceRegion>(R->getMemorySpace()); @@ -138,10 +138,8 @@ SmallVector<const MemRegion *, 4> StackAddrEscapeChecker::getCapturedStackRegions(const BlockDataRegion &B, CheckerContext &C) { SmallVector<const MemRegion *, 4> Regions; - BlockDataRegion::referenced_vars_iterator I = B.referenced_vars_begin(); - BlockDataRegion::referenced_vars_iterator E = B.referenced_vars_end(); - for (; I != E; ++I) { - SVal Val = C.getState()->getSVal(I.getCapturedRegion()); + for (auto Var : B.referenced_vars()) { + SVal Val = C.getState()->getSVal(Var.getCapturedRegion()); const MemRegion *Region = Val.getAsRegion(); if (Region && isa<StackSpaceRegion>(Region->getMemorySpace())) Regions.push_back(Region); @@ -156,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. @@ -196,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; @@ -214,13 +212,13 @@ void StackAddrEscapeChecker::checkAsyncExecutedBlockCaptures( void StackAddrEscapeChecker::checkReturnedBlockCaptures( const BlockDataRegion &B, CheckerContext &C) const { for (const MemRegion *Region : getCapturedStackRegions(B, C)) { - if (isArcManagedBlock(Region, C) || isNotInCurrentFrame(Region, C)) + if (isNotInCurrentFrame(Region, C)) continue; ExplodedNode *N = C.generateNonFatalErrorNode(); if (!N) 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; @@ -267,8 +265,7 @@ void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS, if (const BlockDataRegion *B = dyn_cast<BlockDataRegion>(R)) checkReturnedBlockCaptures(*B, C); - if (!isa<StackSpaceRegion>(R->getMemorySpace()) || - isNotInCurrentFrame(R, C) || isArcManagedBlock(R, C)) + if (!isa<StackSpaceRegion>(R->getMemorySpace()) || isNotInCurrentFrame(R, C)) return; // Returning a record by value is fine. (In this case, the returned @@ -303,21 +300,52 @@ void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS, class CallBack : public StoreManager::BindingsHandler { private: CheckerContext &Ctx; - const StackFrameContext *CurSFC; + const StackFrameContext *PoppedFrame; + + /// Look for stack variables referring to popped stack variables. + /// Returns true only if it found some dangling stack variables + /// referred by an other stack variable from different stack frame. + bool checkForDanglingStackVariable(const MemRegion *Referrer, + const MemRegion *Referred) { + const auto *ReferrerMemSpace = + Referrer->getMemorySpace()->getAs<StackSpaceRegion>(); + const auto *ReferredMemSpace = + Referred->getMemorySpace()->getAs<StackSpaceRegion>(); + + if (!ReferrerMemSpace || !ReferredMemSpace) + return false; + + const auto *ReferrerFrame = ReferrerMemSpace->getStackFrame(); + const auto *ReferredFrame = ReferredMemSpace->getStackFrame(); + + if (ReferrerMemSpace && ReferredMemSpace) { + if (ReferredFrame == PoppedFrame && + ReferrerFrame->isParentOf(PoppedFrame)) { + V.emplace_back(Referrer, Referred); + return true; + } + } + return false; + } public: SmallVector<std::pair<const MemRegion *, const MemRegion *>, 10> V; - CallBack(CheckerContext &CC) : Ctx(CC), CurSFC(CC.getStackFrame()) {} + CallBack(CheckerContext &CC) : Ctx(CC), PoppedFrame(CC.getStackFrame()) {} bool HandleBinding(StoreManager &SMgr, Store S, const MemRegion *Region, SVal Val) override { + const MemRegion *VR = Val.getAsRegion(); + if (!VR) + return true; + if (checkForDanglingStackVariable(Region, VR)) + return true; + + // Check the globals for the same. if (!isa<GlobalsSpaceRegion>(Region->getMemorySpace())) return true; - const MemRegion *VR = Val.getAsRegion(); - if (VR && isa<StackSpaceRegion>(VR->getMemorySpace()) && - !isArcManagedBlock(VR, Ctx) && !isNotInCurrentFrame(VR, Ctx)) + if (VR && VR->hasStackStorage() && !isNotInCurrentFrame(VR, Ctx)) V.emplace_back(Region, VR); return true; } @@ -336,27 +364,54 @@ 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->getBaseRegion(); + const MemRegion *Referred = P.second; + // Generate a report for this bug. + const StringRef CommonSuffix = + "upon returning to the caller. This will be a dangling reference"; SmallString<128> Buf; llvm::raw_svector_ostream Out(Buf); - SourceRange Range = genName(Out, P.second, Ctx.getASTContext()); - Out << " is still referred to by the "; - if (isa<StaticGlobalSpaceRegion>(P.first->getMemorySpace())) - Out << "static"; - else - Out << "global"; - Out << " variable '"; - const VarRegion *VR = cast<VarRegion>(P.first->getBaseRegion()); - Out << *VR->getDecl() - << "' upon returning to the caller. This will be a dangling reference"; + const SourceRange Range = genName(Out, Referred, Ctx.getASTContext()); + + if (isa<CXXTempObjectRegion, CXXLifetimeExtendedObjectRegion>(Referrer)) { + Out << " is still referred to by a temporary object on the stack " + << CommonSuffix; + auto Report = + std::make_unique<PathSensitiveBugReport>(*BT_stackleak, Out.str(), N); + if (Range.isValid()) + Report->addRange(Range); + Ctx.emitReport(std::move(Report)); + return; + } + + const StringRef ReferrerMemorySpace = [](const MemSpaceRegion *Space) { + if (isa<StaticGlobalSpaceRegion>(Space)) + return "static"; + if (isa<GlobalsSpaceRegion>(Space)) + return "global"; + assert(isa<StackSpaceRegion>(Space)); + return "stack"; + }(Referrer->getMemorySpace()); + + // 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(); + + Out << " is still referred to by the " << ReferrerMemorySpace + << " variable '" << ReferrerVarName << "' " << CommonSuffix; auto Report = std::make_unique<PathSensitiveBugReport>(*BT_stackleak, Out.str(), N); if (Range.isValid()) diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp index e758b465af1b..fcd907a9bb0d 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp @@ -38,17 +38,9 @@ // Non-pure functions, for which only partial improvement over the default // behavior is expected, are modeled via check::PostCall, non-intrusively. // -// The following standard C functions are currently supported: -// -// fgetc getline isdigit isupper toascii -// fread isalnum isgraph isxdigit -// fwrite isalpha islower read -// getc isascii isprint write -// getchar isblank ispunct toupper -// getdelim iscntrl isspace tolower -// //===----------------------------------------------------------------------===// +#include "ErrnoModeling.h" #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" @@ -57,9 +49,12 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h" #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" +#include "llvm/Support/FormatVariadic.h" +#include <optional> #include <string> using namespace clang; @@ -72,58 +67,165 @@ class StdLibraryFunctionsChecker class Summary; /// Specify how much the analyzer engine should entrust modeling this function - /// to us. If he doesn't, he performs additional invalidations. - enum InvalidationKind { NoEvalCall, EvalCallAsPure }; + /// to us. + enum InvalidationKind { + /// No \c eval::Call for the function, it can be modeled elsewhere. + /// This checker checks only pre and post conditions. + NoEvalCall, + /// The function is modeled completely in this checker. + EvalCallAsPure + }; + + /// Given a range, should the argument stay inside or outside this range? + enum RangeKind { OutOfRange, WithinRange }; - // The universal integral type to use in value range descriptions. - // Unsigned to make sure overflows are well-defined. + static RangeKind negateKind(RangeKind K) { + switch (K) { + case OutOfRange: + return WithinRange; + case WithinRange: + return OutOfRange; + } + llvm_unreachable("Unknown range kind"); + } + + /// The universal integral type to use in value range descriptions. + /// Unsigned to make sure overflows are well-defined. typedef uint64_t RangeInt; - /// Normally, describes a single range constraint, eg. {{0, 1}, {3, 4}} is - /// a non-negative integer, which less than 5 and not equal to 2. For - /// `ComparesToArgument', holds information about how exactly to compare to - /// the argument. + /// Describes a single range constraint. Eg. {{0, 1}, {3, 4}} is + /// a non-negative integer, which less than 5 and not equal to 2. typedef std::vector<std::pair<RangeInt, RangeInt>> IntRangeVector; /// A reference to an argument or return value by its number. /// ArgNo in CallExpr and CallEvent is defined as Unsigned, but /// obviously uint32_t should be enough for all practical purposes. typedef uint32_t ArgNo; + /// Special argument number for specifying the return value. static const ArgNo Ret; - /// Returns the string representation of an argument index. + /// Get a string representation of an argument index. /// E.g.: (1) -> '1st arg', (2) - > '2nd arg' - static SmallString<8> getArgDesc(ArgNo); + static void printArgDesc(ArgNo, llvm::raw_ostream &Out); + /// Print value X of the argument in form " (which is X)", + /// if the value is a fixed known value, otherwise print nothing. + /// This is used as simple explanation of values if possible. + static void printArgValueInfo(ArgNo ArgN, ProgramStateRef State, + const CallEvent &Call, llvm::raw_ostream &Out); + /// Append textual description of a numeric range [RMin,RMax] to + /// \p Out. + static void appendInsideRangeDesc(llvm::APSInt RMin, llvm::APSInt RMax, + QualType ArgT, BasicValueFactory &BVF, + llvm::raw_ostream &Out); + /// Append textual description of a numeric range out of [RMin,RMax] to + /// \p Out. + static void appendOutOfRangeDesc(llvm::APSInt RMin, llvm::APSInt RMax, + QualType ArgT, BasicValueFactory &BVF, + llvm::raw_ostream &Out); class ValueConstraint; - // Pointer to the ValueConstraint. We need a copyable, polymorphic and - // default initialize able type (vector needs that). A raw pointer was good, - // however, we cannot default initialize that. unique_ptr makes the Summary - // class non-copyable, therefore not an option. Releasing the copyability - // requirement would render the initialization of the Summary map infeasible. + /// Pointer to the ValueConstraint. We need a copyable, polymorphic and + /// default initializable type (vector needs that). A raw pointer was good, + /// however, we cannot default initialize that. unique_ptr makes the Summary + /// class non-copyable, therefore not an option. Releasing the copyability + /// requirement would render the initialization of the Summary map infeasible. + /// Mind that a pointer to a new value constraint is created when the negate + /// function is used. using ValueConstraintPtr = std::shared_ptr<ValueConstraint>; /// Polymorphic base class that represents a constraint on a given argument /// (or return value) of a function. Derived classes implement different kind /// of constraints, e.g range constraints or correlation between two /// arguments. + /// These are used as argument constraints (preconditions) of functions, in + /// which case a bug report may be emitted if the constraint is not satisfied. + /// Another use is as conditions for summary cases, to create different + /// classes of behavior for a function. In this case no description of the + /// constraint is needed because the summary cases have an own (not generated) + /// description string. class ValueConstraint { public: ValueConstraint(ArgNo ArgN) : ArgN(ArgN) {} virtual ~ValueConstraint() {} + /// Apply the effects of the constraint on the given program state. If null /// is returned then the constraint is not feasible. virtual ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, const Summary &Summary, CheckerContext &C) const = 0; + + /// Represents that in which context do we require a description of the + /// constraint. + enum DescriptionKind { + /// Describe a constraint that was violated. + /// Description should start with something like "should be". + Violation, + /// Describe a constraint that was assumed to be true. + /// This can be used when a precondition is satisfied, or when a summary + /// case is applied. + /// Description should start with something like "is". + Assumption + }; + + /// Give a description that explains the constraint to the user. Used when + /// a bug is reported or when the constraint is applied and displayed as a + /// note. The description should not mention the argument (getArgNo). + /// See StdLibraryFunctionsChecker::reportBug about how this function is + /// used (this function is used not only there). + virtual void describe(DescriptionKind DK, const CallEvent &Call, + ProgramStateRef State, const Summary &Summary, + llvm::raw_ostream &Out) const { + // There are some descendant classes that are not used as argument + // constraints, e.g. ComparisonConstraint. In that case we can safely + // ignore the implementation of this function. + llvm_unreachable( + "Description not implemented for summary case constraints"); + } + + /// Give a description that explains the actual argument value (where the + /// current ValueConstraint applies to) to the user. This function should be + /// called only when the current constraint is satisfied by the argument. + /// It should produce a more precise description than the constraint itself. + /// The actual value of the argument and the program state can be used to + /// make the description more precise. In the most simple case, if the + /// argument has a fixed known value this value can be printed into \p Out, + /// this is done by default. + /// The function should return true if a description was printed to \p Out, + /// otherwise false. + /// See StdLibraryFunctionsChecker::reportBug about how this function is + /// used. + virtual bool describeArgumentValue(const CallEvent &Call, + ProgramStateRef State, + const Summary &Summary, + llvm::raw_ostream &Out) const { + if (auto N = getArgSVal(Call, getArgNo()).getAs<NonLoc>()) { + if (const llvm::APSInt *Int = N->getAsInteger()) { + Out << *Int; + return true; + } + } + return false; + } + + /// Return those arguments that should be tracked when we report a bug about + /// argument constraint violation. By default it is the argument that is + /// constrained, however, in some special cases we need to track other + /// arguments as well. E.g. a buffer size might be encoded in another + /// argument. + /// The "return value" argument number can not occur as returned value. + virtual std::vector<ArgNo> getArgsToTrack() const { return {ArgN}; } + + /// Get a constraint that represents exactly the opposite of the current. virtual ValueConstraintPtr negate() const { llvm_unreachable("Not implemented"); }; - // Check whether the constraint is malformed or not. It is malformed if the - // specified argument has a mismatch with the given FunctionDecl (e.g. the - // arg number is out-of-range of the function's argument list). + /// Check whether the constraint is malformed or not. It is malformed if the + /// specified argument has a mismatch with the given FunctionDecl (e.g. the + /// arg number is out-of-range of the function's argument list). + /// This condition can indicate if a probably wrong or unexpected function + /// was found where the constraint is to be applied. bool checkValidity(const FunctionDecl *FD) const { const bool ValidArg = ArgN == Ret || ArgN < FD->getNumParams(); assert(ValidArg && "Arg out of range!"); @@ -132,95 +234,75 @@ class StdLibraryFunctionsChecker // Subclasses may further refine the validation. return checkSpecificValidity(FD); } - ArgNo getArgNo() const { return ArgN; } - // Return those arguments that should be tracked when we report a bug. By - // default it is the argument that is constrained, however, in some special - // cases we need to track other arguments as well. E.g. a buffer size might - // be encoded in another argument. - virtual std::vector<ArgNo> getArgsToTrack() const { return {ArgN}; } - - virtual StringRef getName() const = 0; - - // Give a description that explains the constraint to the user. Used when - // the bug is reported. - virtual std::string describe(ProgramStateRef State, - const Summary &Summary) const { - // There are some descendant classes that are not used as argument - // constraints, e.g. ComparisonConstraint. In that case we can safely - // ignore the implementation of this function. - llvm_unreachable("Not implemented"); - } + /// Return the argument number (may be placeholder for "return value"). + ArgNo getArgNo() const { return ArgN; } protected: - ArgNo ArgN; // Argument to which we apply the constraint. - - /// Do polymorphic sanity check on the constraint. + /// Argument to which to apply the constraint. It can be a real argument of + /// the function to check, or a special value to indicate the return value + /// of the function. + /// Every constraint is assigned to one main argument, even if other + /// arguments are involved. + ArgNo ArgN; + + /// Do constraint-specific validation check. virtual bool checkSpecificValidity(const FunctionDecl *FD) const { return true; } }; - /// Given a range, should the argument stay inside or outside this range? - enum RangeKind { OutOfRange, WithinRange }; - - /// Encapsulates a range on a single symbol. + /// Check if a single argument falls into a specific "range". + /// A range is formed as a set of intervals. + /// E.g. \code {['A', 'Z'], ['a', 'z'], ['_', '_']} \endcode + /// The intervals are closed intervals that contain one or more values. + /// + /// The default constructed RangeConstraint has an empty range, applying + /// such constraint does not involve any assumptions, thus the State remains + /// unchanged. This is meaningful, if the range is dependent on a looked up + /// type (e.g. [0, Socklen_tMax]). If the type is not found, then the range + /// is default initialized to be empty. class RangeConstraint : public ValueConstraint { + /// The constraint can be specified by allowing or disallowing the range. + /// WithinRange indicates allowing the range, OutOfRange indicates + /// disallowing it (allowing the complementary range). RangeKind Kind; - // A range is formed as a set of intervals (sub-ranges). - // E.g. {['A', 'Z'], ['a', 'z']} - // - // The default constructed RangeConstraint has an empty range set, applying - // such constraint does not involve any assumptions, thus the State remains - // unchanged. This is meaningful, if the range is dependent on a looked up - // type (e.g. [0, Socklen_tMax]). If the type is not found, then the range - // is default initialized to be empty. + + /// A set of intervals. IntRangeVector Ranges; - public: - StringRef getName() const override { return "Range"; } - RangeConstraint(ArgNo ArgN, RangeKind Kind, const IntRangeVector &Ranges) - : ValueConstraint(ArgN), Kind(Kind), Ranges(Ranges) {} + /// A textual description of this constraint for the specific case where the + /// constraint is used. If empty a generated description will be used that + /// is built from the range of the constraint. + StringRef Description; - std::string describe(ProgramStateRef State, - const Summary &Summary) const override; + public: + RangeConstraint(ArgNo ArgN, RangeKind Kind, const IntRangeVector &Ranges, + StringRef Desc = "") + : ValueConstraint(ArgN), Kind(Kind), Ranges(Ranges), Description(Desc) { + } const IntRangeVector &getRanges() const { return Ranges; } - private: - ProgramStateRef applyAsOutOfRange(ProgramStateRef State, - const CallEvent &Call, - const Summary &Summary) const; - ProgramStateRef applyAsWithinRange(ProgramStateRef State, - const CallEvent &Call, - const Summary &Summary) const; - - public: ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, const Summary &Summary, - CheckerContext &C) const override { - switch (Kind) { - case OutOfRange: - return applyAsOutOfRange(State, Call, Summary); - case WithinRange: - return applyAsWithinRange(State, Call, Summary); - } - llvm_unreachable("Unknown range kind!"); - } + CheckerContext &C) const override; + + void describe(DescriptionKind DK, const CallEvent &Call, + ProgramStateRef State, const Summary &Summary, + llvm::raw_ostream &Out) const override; + + bool describeArgumentValue(const CallEvent &Call, ProgramStateRef State, + const Summary &Summary, + llvm::raw_ostream &Out) const override; ValueConstraintPtr negate() const override { RangeConstraint Tmp(*this); - switch (Kind) { - case OutOfRange: - Tmp.Kind = WithinRange; - break; - case WithinRange: - Tmp.Kind = OutOfRange; - break; - } + Tmp.Kind = negateKind(Kind); return std::make_shared<RangeConstraint>(Tmp); } + protected: bool checkSpecificValidity(const FunctionDecl *FD) const override { const bool ValidArg = getArgType(FD, ArgN)->isIntegralType(FD->getASTContext()); @@ -228,14 +310,52 @@ class StdLibraryFunctionsChecker "This constraint should be applied on an integral type"); return ValidArg; } + + private: + /// A callback function that is used when iterating over the range + /// intervals. It gets the begin and end (inclusive) of one interval. + /// This is used to make any kind of task possible that needs an iteration + /// over the intervals. + using RangeApplyFunction = + std::function<bool(const llvm::APSInt &Min, const llvm::APSInt &Max)>; + + /// Call a function on the intervals of the range. + /// The function is called with all intervals in the range. + void applyOnWithinRange(BasicValueFactory &BVF, QualType ArgT, + const RangeApplyFunction &F) const; + /// Call a function on all intervals in the complementary range. + /// The function is called with all intervals that fall out of the range. + /// E.g. consider an interval list [A, B] and [C, D] + /// \code + /// -------+--------+------------------+------------+-----------> + /// A B C D + /// \endcode + /// We get the ranges [-inf, A - 1], [D + 1, +inf], [B + 1, C - 1]. + /// The \p ArgT is used to determine the min and max of the type that is + /// used as "-inf" and "+inf". + void applyOnOutOfRange(BasicValueFactory &BVF, QualType ArgT, + const RangeApplyFunction &F) const; + /// Call a function on the intervals of the range or the complementary + /// range. + void applyOnRange(RangeKind Kind, BasicValueFactory &BVF, QualType ArgT, + const RangeApplyFunction &F) const { + switch (Kind) { + case OutOfRange: + applyOnOutOfRange(BVF, ArgT, F); + break; + case WithinRange: + applyOnWithinRange(BVF, ArgT, F); + break; + }; + } }; + /// Check relation of an argument to another. class ComparisonConstraint : public ValueConstraint { BinaryOperator::Opcode Opcode; ArgNo OtherArgN; public: - virtual StringRef getName() const override { return "Comparison"; }; ComparisonConstraint(ArgNo ArgN, BinaryOperator::Opcode Opcode, ArgNo OtherArgN) : ValueConstraint(ArgN), Opcode(Opcode), OtherArgN(OtherArgN) {} @@ -246,28 +366,27 @@ class StdLibraryFunctionsChecker CheckerContext &C) const override; }; + /// Check null or non-null-ness of an argument that is of pointer type. class NotNullConstraint : public ValueConstraint { using ValueConstraint::ValueConstraint; // This variable has a role when we negate the constraint. bool CannotBeNull = true; public: - std::string describe(ProgramStateRef State, - const Summary &Summary) const override; - StringRef getName() const override { return "NonNull"; } + NotNullConstraint(ArgNo ArgN, bool CannotBeNull = true) + : ValueConstraint(ArgN), CannotBeNull(CannotBeNull) {} + ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, const Summary &Summary, - CheckerContext &C) const override { - SVal V = getArgSVal(Call, getArgNo()); - if (V.isUndef()) - return State; + CheckerContext &C) const override; - DefinedOrUnknownSVal L = V.castAs<DefinedOrUnknownSVal>(); - if (!L.getAs<Loc>()) - return State; + void describe(DescriptionKind DK, const CallEvent &Call, + ProgramStateRef State, const Summary &Summary, + llvm::raw_ostream &Out) const override; - return State->assume(L, CannotBeNull); - } + bool describeArgumentValue(const CallEvent &Call, ProgramStateRef State, + const Summary &Summary, + llvm::raw_ostream &Out) const override; ValueConstraintPtr negate() const override { NotNullConstraint Tmp(*this); @@ -275,6 +394,54 @@ class StdLibraryFunctionsChecker return std::make_shared<NotNullConstraint>(Tmp); } + protected: + bool checkSpecificValidity(const FunctionDecl *FD) const override { + const bool ValidArg = getArgType(FD, ArgN)->isPointerType(); + assert(ValidArg && + "This constraint should be applied only on a pointer type"); + return ValidArg; + } + }; + + /// Check null or non-null-ness of an argument that is of pointer type. + /// The argument is meant to be a buffer that has a size constraint, and it + /// is allowed to have a NULL value if the size is 0. The size can depend on + /// 1 or 2 additional arguments, if one of these is 0 the buffer is allowed to + /// be NULL. This is useful for functions like `fread` which have this special + /// property. + class NotNullBufferConstraint : public ValueConstraint { + using ValueConstraint::ValueConstraint; + ArgNo SizeArg1N; + std::optional<ArgNo> SizeArg2N; + // This variable has a role when we negate the constraint. + bool CannotBeNull = true; + + public: + NotNullBufferConstraint(ArgNo ArgN, ArgNo SizeArg1N, + std::optional<ArgNo> SizeArg2N, + bool CannotBeNull = true) + : ValueConstraint(ArgN), SizeArg1N(SizeArg1N), SizeArg2N(SizeArg2N), + CannotBeNull(CannotBeNull) {} + + ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, + const Summary &Summary, + CheckerContext &C) const override; + + void describe(DescriptionKind DK, const CallEvent &Call, + ProgramStateRef State, const Summary &Summary, + llvm::raw_ostream &Out) const override; + + bool describeArgumentValue(const CallEvent &Call, ProgramStateRef State, + const Summary &Summary, + llvm::raw_ostream &Out) const override; + + ValueConstraintPtr negate() const override { + NotNullBufferConstraint Tmp(*this); + Tmp.CannotBeNull = !this->CannotBeNull; + return std::make_shared<NotNullBufferConstraint>(Tmp); + } + + protected: bool checkSpecificValidity(const FunctionDecl *FD) const override { const bool ValidArg = getArgType(FD, ArgN)->isPointerType(); assert(ValidArg && @@ -295,18 +462,17 @@ class StdLibraryFunctionsChecker // // Here, ptr is the buffer, and its minimum size is `size * nmemb`. class BufferSizeConstraint : public ValueConstraint { // The concrete value which is the minimum size for the buffer. - llvm::Optional<llvm::APSInt> ConcreteSize; + std::optional<llvm::APSInt> ConcreteSize; // The argument which holds the size of the buffer. - llvm::Optional<ArgNo> SizeArgN; + std::optional<ArgNo> SizeArgN; // The argument which is a multiplier to size. This is set in case of // `fread` like functions where the size is computed as a multiplication of // two arguments. - llvm::Optional<ArgNo> SizeMultiplierArgN; + std::optional<ArgNo> SizeMultiplierArgN; // The operator we use in apply. This is negated in negate(). BinaryOperator::Opcode Op = BO_LE; public: - StringRef getName() const override { return "BufferSize"; } BufferSizeConstraint(ArgNo Buffer, llvm::APSInt BufMinSize) : ValueConstraint(Buffer), ConcreteSize(BufMinSize) {} BufferSizeConstraint(ArgNo Buffer, ArgNo BufSize) @@ -315,6 +481,18 @@ class StdLibraryFunctionsChecker : ValueConstraint(Buffer), SizeArgN(BufSize), SizeMultiplierArgN(BufSizeMultiplier) {} + ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, + const Summary &Summary, + CheckerContext &C) const override; + + void describe(DescriptionKind DK, const CallEvent &Call, + ProgramStateRef State, const Summary &Summary, + llvm::raw_ostream &Out) const override; + + bool describeArgumentValue(const CallEvent &Call, ProgramStateRef State, + const Summary &Summary, + llvm::raw_ostream &Out) const override; + std::vector<ArgNo> getArgsToTrack() const override { std::vector<ArgNo> Result{ArgN}; if (SizeArgN) @@ -324,57 +502,13 @@ class StdLibraryFunctionsChecker return Result; } - std::string describe(ProgramStateRef State, - const Summary &Summary) const override; - - ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, - const Summary &Summary, - CheckerContext &C) const override { - SValBuilder &SvalBuilder = C.getSValBuilder(); - // The buffer argument. - SVal BufV = getArgSVal(Call, getArgNo()); - - // Get the size constraint. - const SVal SizeV = [this, &State, &Call, &Summary, &SvalBuilder]() { - if (ConcreteSize) { - return SVal(SvalBuilder.makeIntVal(*ConcreteSize)); - } - assert(SizeArgN && "The constraint must be either a concrete value or " - "encoded in an argument."); - // The size argument. - SVal SizeV = getArgSVal(Call, *SizeArgN); - // Multiply with another argument if given. - if (SizeMultiplierArgN) { - SVal SizeMulV = getArgSVal(Call, *SizeMultiplierArgN); - SizeV = SvalBuilder.evalBinOp(State, BO_Mul, SizeV, SizeMulV, - Summary.getArgType(*SizeArgN)); - } - return SizeV; - }(); - - // The dynamic size of the buffer argument, got from the analyzer engine. - SVal BufDynSize = getDynamicExtentWithOffset(State, BufV); - - SVal Feasible = SvalBuilder.evalBinOp(State, Op, SizeV, BufDynSize, - SvalBuilder.getContext().BoolTy); - if (auto F = Feasible.getAs<DefinedOrUnknownSVal>()) - return State->assume(*F, true); - - // We can get here only if the size argument or the dynamic size is - // undefined. But the dynamic size should never be undefined, only - // unknown. So, here, the size of the argument is undefined, i.e. we - // cannot apply the constraint. Actually, other checkers like - // CallAndMessage should catch this situation earlier, because we call a - // function with an uninitialized argument. - llvm_unreachable("Size argument or the dynamic size is Undefined"); - } - ValueConstraintPtr negate() const override { BufferSizeConstraint Tmp(*this); Tmp.Op = BinaryOperator::negateComparisonOp(Op); return std::make_shared<BufferSizeConstraint>(Tmp); } + protected: bool checkSpecificValidity(const FunctionDecl *FD) const override { const bool ValidArg = getArgType(FD, ArgN)->isPointerType(); assert(ValidArg && @@ -384,10 +518,162 @@ class StdLibraryFunctionsChecker }; /// The complete list of constraints that defines a single branch. - typedef std::vector<ValueConstraintPtr> ConstraintSet; + using ConstraintSet = std::vector<ValueConstraintPtr>; + + /// Define how a function affects the system variable 'errno'. + /// This works together with the \c ErrnoModeling and \c ErrnoChecker classes. + /// Currently 3 use cases exist: success, failure, irrelevant. + /// In the future the failure case can be customized to set \c errno to a + /// more specific constraint (for example > 0), or new case can be added + /// for functions which require check of \c errno in both success and failure + /// case. + class ErrnoConstraintBase { + public: + /// Apply specific state changes related to the errno variable. + virtual ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, + const Summary &Summary, + CheckerContext &C) const = 0; + /// Get a description about what 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() {} + + protected: + ErrnoConstraintBase() = default; + + /// This is used for conjure symbol for errno to differentiate from the + /// original call expression (same expression is used for the errno symbol). + static int Tag; + }; + + /// Reset errno constraints to irrelevant. + /// This is applicable to functions that may change 'errno' and are not + /// modeled elsewhere. + class ResetErrnoConstraint : public ErrnoConstraintBase { + public: + ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, + const Summary &Summary, + CheckerContext &C) const override { + return errno_modeling::setErrnoState(State, errno_modeling::Irrelevant); + } + }; + + /// Do not change errno constraints. + /// This is applicable to functions that are modeled in another checker + /// and the already set errno constraints should not be changed in the + /// post-call event. + class NoErrnoConstraint : public ErrnoConstraintBase { + public: + ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, + const Summary &Summary, + CheckerContext &C) const override { + return State; + } + }; + + /// Set errno constraint at failure cases of standard functions. + /// Failure case: 'errno' becomes not equal to 0 and may or may not be checked + /// by the program. \c ErrnoChecker does not emit a bug report after such a + /// function call. + class FailureErrnoConstraint : public ErrnoConstraintBase { + public: + ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, + const Summary &Summary, + CheckerContext &C) const override { + SValBuilder &SVB = C.getSValBuilder(); + NonLoc ErrnoSVal = + SVB.conjureSymbolVal(&Tag, Call.getOriginExpr(), + C.getLocationContext(), C.getASTContext().IntTy, + C.blockCount()) + .castAs<NonLoc>(); + return errno_modeling::setErrnoForStdFailure(State, C, ErrnoSVal); + } + }; + + /// Set errno constraint at success cases of standard functions. + /// 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 { + public: + ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, + const Summary &Summary, + CheckerContext &C) const override { + return errno_modeling::setErrnoForStdSuccess(State, C); + } + + 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, + const Summary &Summary, + CheckerContext &C) const override { + return errno_modeling::setErrnoStdMustBeChecked(State, C, + Call.getOriginExpr()); + } + + const std::string describe(CheckerContext &C) const override { + return "reading 'errno' is required to find out if the call has failed"; + } + }; + + /// A single branch of a function summary. + /// + /// A branch is defined by a series of constraints - "assumptions" - + /// that together form a single possible outcome of invoking the function. + /// When static analyzer considers a branch, it tries to introduce + /// a child node in the Exploded Graph. The child node has to include + /// constraints that define the branch. If the constraints contradict + /// existing constraints in the state, the node is not created and the branch + /// is dropped; otherwise it's queued for future exploration. + /// The branch is accompanied by a note text that may be displayed + /// to the user when a bug is found on a path that takes this branch. + /// + /// For example, consider the branches in `isalpha(x)`: + /// Branch 1) + /// x is in range ['A', 'Z'] or in ['a', 'z'] + /// then the return value is not 0. (I.e. out-of-range [0, 0]) + /// and the note may say "Assuming the character is alphabetical" + /// Branch 2) + /// x is out-of-range ['A', 'Z'] and out-of-range ['a', 'z'] + /// then the return value is 0 + /// and the note may say "Assuming the character is non-alphabetical". + class SummaryCase { + ConstraintSet Constraints; + const ErrnoConstraintBase &ErrnoConstraint; + StringRef Note; + + public: + SummaryCase(ConstraintSet &&Constraints, const ErrnoConstraintBase &ErrnoC, + StringRef Note) + : Constraints(std::move(Constraints)), ErrnoConstraint(ErrnoC), + Note(Note) {} + + SummaryCase(const ConstraintSet &Constraints, + const ErrnoConstraintBase &ErrnoC, StringRef Note) + : Constraints(Constraints), ErrnoConstraint(ErrnoC), Note(Note) {} + + const ConstraintSet &getConstraints() const { return Constraints; } + const ErrnoConstraintBase &getErrnoConstraint() const { + return ErrnoConstraint; + } + StringRef getNote() const { return Note; } + }; - using ArgTypes = std::vector<Optional<QualType>>; - using RetType = Optional<QualType>; + using ArgTypes = std::vector<std::optional<QualType>>; + using RetType = std::optional<QualType>; // A placeholder type, we use it whenever we do not care about the concrete // type in a Signature. @@ -409,7 +695,7 @@ class StdLibraryFunctionsChecker // Construct a signature from optional types. If any of the optional types // are not set then the signature will be invalid. Signature(ArgTypes ArgTys, RetType RetTy) { - for (Optional<QualType> Arg : ArgTys) { + for (std::optional<QualType> Arg : ArgTys) { if (!Arg) { Invalid = true; return; @@ -451,23 +737,12 @@ class StdLibraryFunctionsChecker return T; } - using Cases = std::vector<ConstraintSet>; + using SummaryCases = std::vector<SummaryCase>; /// A summary includes information about /// * function prototype (signature) /// * approach to invalidation, - /// * a list of branches - a list of list of ranges - - /// A branch represents a path in the exploded graph of a function (which - /// is a tree). So, a branch is a series of assumptions. In other words, - /// branches represent split states and additional assumptions on top of - /// the splitting assumption. - /// For example, consider the branches in `isalpha(x)` - /// Branch 1) - /// x is in range ['A', 'Z'] or in ['a', 'z'] - /// then the return value is not 0. (I.e. out-of-range [0, 0]) - /// Branch 2) - /// x is out-of-range ['A', 'Z'] and out-of-range ['a', 'z'] - /// then the return value is 0. + /// * a list of branches - so, a list of list of ranges, /// * a list of argument constraints, that must be true on every branch. /// If these constraints are not satisfied that means a fatal error /// usually resulting in undefined behaviour. @@ -482,7 +757,7 @@ class StdLibraryFunctionsChecker /// signature is matched. class Summary { const InvalidationKind InvalidationKd; - Cases CaseConstraints; + SummaryCases Cases; ConstraintSet ArgConstraints; // The function to which the summary applies. This is set after lookup and @@ -492,12 +767,14 @@ class StdLibraryFunctionsChecker public: Summary(InvalidationKind InvalidationKd) : InvalidationKd(InvalidationKd) {} - Summary &Case(ConstraintSet &&CS) { - CaseConstraints.push_back(std::move(CS)); + Summary &Case(ConstraintSet &&CS, const ErrnoConstraintBase &ErrnoC, + StringRef Note = "") { + Cases.push_back(SummaryCase(std::move(CS), ErrnoC, Note)); return *this; } - Summary &Case(const ConstraintSet &CS) { - CaseConstraints.push_back(CS); + Summary &Case(const ConstraintSet &CS, const ErrnoConstraintBase &ErrnoC, + StringRef Note = "") { + Cases.push_back(SummaryCase(CS, ErrnoC, Note)); return *this; } Summary &ArgConstraint(ValueConstraintPtr VC) { @@ -508,7 +785,7 @@ class StdLibraryFunctionsChecker } InvalidationKind getInvalidationKd() const { return InvalidationKd; } - const Cases &getCaseConstraints() const { return CaseConstraints; } + const SummaryCases &getCases() const { return Cases; } const ConstraintSet &getArgConstraints() const { return ArgConstraints; } QualType getArgType(ArgNo ArgN) const { @@ -527,11 +804,11 @@ class StdLibraryFunctionsChecker } private: - // Once we know the exact type of the function then do sanity check on all - // the given constraints. + // Once we know the exact type of the function then do validation check on + // all the given constraints. bool validateByConstraints(const FunctionDecl *FD) const { - for (const ConstraintSet &Case : CaseConstraints) - for (const ValueConstraintPtr &Constraint : Case) + for (const SummaryCase &Case : Cases) + for (const ValueConstraintPtr &Constraint : Case.getConstraints()) if (!Constraint->checkValidity(FD)) return false; for (const ValueConstraintPtr &Constraint : ArgConstraints) @@ -546,236 +823,310 @@ class StdLibraryFunctionsChecker using FunctionSummaryMapType = llvm::DenseMap<const FunctionDecl *, Summary>; mutable FunctionSummaryMapType FunctionSummaryMap; - mutable std::unique_ptr<BugType> BT_InvalidArg; + const BugType BT_InvalidArg{this, "Function call with invalid argument"}; mutable bool SummariesInitialized = false; static SVal getArgSVal(const CallEvent &Call, ArgNo ArgN) { return ArgN == Ret ? Call.getReturnValue() : Call.getArgSVal(ArgN); } + static std::string getFunctionName(const CallEvent &Call) { + assert(Call.getDecl() && + "Call was found by a summary, should have declaration"); + return cast<NamedDecl>(Call.getDecl())->getNameAsString(); + } public: void checkPreCall(const CallEvent &Call, CheckerContext &C) const; void checkPostCall(const CallEvent &Call, CheckerContext &C) const; bool evalCall(const CallEvent &Call, CheckerContext &C) const; - enum CheckKind { - CK_StdCLibraryFunctionArgsChecker, - CK_StdCLibraryFunctionsTesterChecker, - CK_NumCheckKinds - }; - DefaultBool ChecksEnabled[CK_NumCheckKinds]; - CheckerNameRef CheckNames[CK_NumCheckKinds]; + CheckerNameRef CheckName; + bool AddTestFunctions = false; bool DisplayLoadedSummaries = false; bool ModelPOSIX = false; + bool ShouldAssumeControlledEnvironment = false; private: - Optional<Summary> findFunctionSummary(const FunctionDecl *FD, - CheckerContext &C) const; - Optional<Summary> findFunctionSummary(const CallEvent &Call, - CheckerContext &C) const; + std::optional<Summary> findFunctionSummary(const FunctionDecl *FD, + CheckerContext &C) const; + std::optional<Summary> findFunctionSummary(const CallEvent &Call, + CheckerContext &C) const; void initFunctionSummaries(CheckerContext &C) const; void reportBug(const CallEvent &Call, ExplodedNode *N, - const ValueConstraint *VC, const Summary &Summary, - CheckerContext &C) const { - if (!ChecksEnabled[CK_StdCLibraryFunctionArgsChecker]) - return; - std::string Msg = - (Twine("Function argument constraint is not satisfied, constraint: ") + - VC->getName().data()) - .str(); - if (!BT_InvalidArg) - BT_InvalidArg = std::make_unique<BugType>( - CheckNames[CK_StdCLibraryFunctionArgsChecker], - "Unsatisfied argument constraints", categories::LogicError); - auto R = std::make_unique<PathSensitiveBugReport>(*BT_InvalidArg, Msg, N); - - for (ArgNo ArgN : VC->getArgsToTrack()) + const ValueConstraint *VC, const ValueConstraint *NegatedVC, + const Summary &Summary, CheckerContext &C) const { + assert(Call.getDecl() && + "Function found in summary must have a declaration available"); + SmallString<256> Msg; + llvm::raw_svector_ostream MsgOs(Msg); + + MsgOs << "The "; + printArgDesc(VC->getArgNo(), MsgOs); + MsgOs << " to '" << getFunctionName(Call) << "' "; + bool ValuesPrinted = + NegatedVC->describeArgumentValue(Call, N->getState(), Summary, MsgOs); + if (ValuesPrinted) + MsgOs << " but "; + else + MsgOs << "is out of the accepted range; It "; + VC->describe(ValueConstraint::Violation, Call, C.getState(), Summary, + MsgOs); + Msg[0] = toupper(Msg[0]); + auto R = std::make_unique<PathSensitiveBugReport>(BT_InvalidArg, Msg, N); + + for (ArgNo ArgN : VC->getArgsToTrack()) { bugreporter::trackExpressionValue(N, Call.getArgExpr(ArgN), *R); - - // Highlight the range of the argument that was violated. - R->addRange(Call.getArgSourceRange(VC->getArgNo())); - - // Describe the argument constraint in a note. - R->addNote(VC->describe(C.getState(), Summary), R->getLocation(), - Call.getArgSourceRange(VC->getArgNo())); + R->markInteresting(Call.getArgSVal(ArgN)); + // All tracked arguments are important, highlight them. + R->addRange(Call.getArgSourceRange(ArgN)); + } C.emitReport(std::move(R)); } + + /// These are the errno constraints that can be passed to summary cases. + /// One of these should fit for a single summary case. + /// Usually if a failure return value exists for function, that function + /// needs different cases for success and failure with different errno + /// constraints (and different return value constraints). + const NoErrnoConstraint ErrnoUnchanged{}; + const ResetErrnoConstraint ErrnoIrrelevant{}; + const ErrnoMustBeCheckedConstraint ErrnoMustBeChecked{}; + const SuccessErrnoConstraint ErrnoMustNotBeChecked{}; + const FailureErrnoConstraint ErrnoNEZeroIrrelevant{}; }; +int StdLibraryFunctionsChecker::ErrnoConstraintBase::Tag = 0; + const StdLibraryFunctionsChecker::ArgNo StdLibraryFunctionsChecker::Ret = std::numeric_limits<ArgNo>::max(); -} // end of anonymous namespace - static BasicValueFactory &getBVF(ProgramStateRef State) { ProgramStateManager &Mgr = State->getStateManager(); SValBuilder &SVB = Mgr.getSValBuilder(); return SVB.getBasicValueFactory(); } -std::string StdLibraryFunctionsChecker::NotNullConstraint::describe( - ProgramStateRef State, const Summary &Summary) const { - SmallString<48> Result; - Result += "The "; - Result += getArgDesc(ArgN); - Result += " should not be NULL"; - return Result.c_str(); -} +} // end of anonymous namespace -std::string StdLibraryFunctionsChecker::RangeConstraint::describe( - ProgramStateRef State, const Summary &Summary) const { +void StdLibraryFunctionsChecker::printArgDesc( + StdLibraryFunctionsChecker::ArgNo ArgN, llvm::raw_ostream &Out) { + Out << std::to_string(ArgN + 1); + Out << llvm::getOrdinalSuffix(ArgN + 1); + Out << " argument"; +} - BasicValueFactory &BVF = getBVF(State); +void StdLibraryFunctionsChecker::printArgValueInfo(ArgNo ArgN, + ProgramStateRef State, + const CallEvent &Call, + llvm::raw_ostream &Out) { + if (const llvm::APSInt *Val = + State->getStateManager().getSValBuilder().getKnownValue( + State, getArgSVal(Call, ArgN))) + Out << " (which is " << *Val << ")"; +} - QualType T = Summary.getArgType(getArgNo()); - SmallString<48> Result; - Result += "The "; - Result += getArgDesc(ArgN); - Result += " should be "; - - // Range kind as a string. - Kind == OutOfRange ? Result += "out of" : Result += "within"; - - // Get the range values as a string. - Result += " the range "; - if (Ranges.size() > 1) - Result += "["; - unsigned I = Ranges.size(); - for (const std::pair<RangeInt, RangeInt> &R : Ranges) { - Result += "["; - const llvm::APSInt &Min = BVF.getValue(R.first, T); - const llvm::APSInt &Max = BVF.getValue(R.second, T); - Min.toString(Result); - Result += ", "; - Max.toString(Result); - Result += "]"; - if (--I > 0) - Result += ", "; +void StdLibraryFunctionsChecker::appendInsideRangeDesc(llvm::APSInt RMin, + llvm::APSInt RMax, + QualType ArgT, + BasicValueFactory &BVF, + llvm::raw_ostream &Out) { + if (RMin.isZero() && RMax.isZero()) + Out << "zero"; + else if (RMin == RMax) + Out << RMin; + else if (RMin == BVF.getMinValue(ArgT)) { + if (RMax == -1) + Out << "< 0"; + else + Out << "<= " << RMax; + } else if (RMax == BVF.getMaxValue(ArgT)) { + if (RMin.isOne()) + Out << "> 0"; + else + Out << ">= " << RMin; + } else if (RMin.isNegative() == RMax.isNegative() && + RMin.getLimitedValue() == RMax.getLimitedValue() - 1) { + Out << RMin << " or " << RMax; + } else { + Out << "between " << RMin << " and " << RMax; } - if (Ranges.size() > 1) - Result += "]"; - - return Result.c_str(); } -SmallString<8> -StdLibraryFunctionsChecker::getArgDesc(StdLibraryFunctionsChecker::ArgNo ArgN) { - SmallString<8> Result; - Result += std::to_string(ArgN + 1); - Result += llvm::getOrdinalSuffix(ArgN + 1); - Result += " arg"; - return Result; +void StdLibraryFunctionsChecker::appendOutOfRangeDesc(llvm::APSInt RMin, + llvm::APSInt RMax, + QualType ArgT, + BasicValueFactory &BVF, + llvm::raw_ostream &Out) { + if (RMin.isZero() && RMax.isZero()) + Out << "nonzero"; + else if (RMin == RMax) { + Out << "not equal to " << RMin; + } else if (RMin == BVF.getMinValue(ArgT)) { + if (RMax == -1) + Out << ">= 0"; + else + Out << "> " << RMax; + } else if (RMax == BVF.getMaxValue(ArgT)) { + if (RMin.isOne()) + Out << "<= 0"; + else + Out << "< " << RMin; + } else if (RMin.isNegative() == RMax.isNegative() && + RMin.getLimitedValue() == RMax.getLimitedValue() - 1) { + Out << "not " << RMin << " and not " << RMax; + } else { + Out << "not between " << RMin << " and " << RMax; + } } -std::string StdLibraryFunctionsChecker::BufferSizeConstraint::describe( - ProgramStateRef State, const Summary &Summary) const { - SmallString<96> Result; - Result += "The size of the "; - Result += getArgDesc(ArgN); - Result += " should be equal to or less than the value of "; - if (ConcreteSize) { - ConcreteSize->toString(Result); - } else if (SizeArgN) { - Result += "the "; - Result += getArgDesc(*SizeArgN); - if (SizeMultiplierArgN) { - Result += " times the "; - Result += getArgDesc(*SizeMultiplierArgN); - } +void StdLibraryFunctionsChecker::RangeConstraint::applyOnWithinRange( + BasicValueFactory &BVF, QualType ArgT, const RangeApplyFunction &F) const { + if (Ranges.empty()) + return; + + for (auto [Start, End] : getRanges()) { + const llvm::APSInt &Min = BVF.getValue(Start, ArgT); + const llvm::APSInt &Max = BVF.getValue(End, ArgT); + assert(Min <= Max); + if (!F(Min, Max)) + return; } - return Result.c_str(); } -ProgramStateRef StdLibraryFunctionsChecker::RangeConstraint::applyAsOutOfRange( - ProgramStateRef State, const CallEvent &Call, - const Summary &Summary) const { +void StdLibraryFunctionsChecker::RangeConstraint::applyOnOutOfRange( + BasicValueFactory &BVF, QualType ArgT, const RangeApplyFunction &F) const { if (Ranges.empty()) - return State; + return; - ProgramStateManager &Mgr = State->getStateManager(); - SValBuilder &SVB = Mgr.getSValBuilder(); - BasicValueFactory &BVF = SVB.getBasicValueFactory(); - ConstraintManager &CM = Mgr.getConstraintManager(); - QualType T = Summary.getArgType(getArgNo()); + const IntRangeVector &R = getRanges(); + size_t E = R.size(); + + const llvm::APSInt &MinusInf = BVF.getMinValue(ArgT); + const llvm::APSInt &PlusInf = BVF.getMaxValue(ArgT); + + const llvm::APSInt &RangeLeft = BVF.getValue(R[0].first - 1ULL, ArgT); + const llvm::APSInt &RangeRight = BVF.getValue(R[E - 1].second + 1ULL, ArgT); + + // Iterate over the "holes" between intervals. + for (size_t I = 1; I != E; ++I) { + const llvm::APSInt &Min = BVF.getValue(R[I - 1].second + 1ULL, ArgT); + const llvm::APSInt &Max = BVF.getValue(R[I].first - 1ULL, ArgT); + if (Min <= Max) { + if (!F(Min, Max)) + return; + } + } + // Check the interval [T_MIN, min(R) - 1]. + if (RangeLeft != PlusInf) { + assert(MinusInf <= RangeLeft); + if (!F(MinusInf, RangeLeft)) + return; + } + // Check the interval [max(R) + 1, T_MAX], + if (RangeRight != MinusInf) { + assert(RangeRight <= PlusInf); + if (!F(RangeRight, PlusInf)) + return; + } +} + +ProgramStateRef StdLibraryFunctionsChecker::RangeConstraint::apply( + ProgramStateRef State, const CallEvent &Call, const Summary &Summary, + CheckerContext &C) const { + ConstraintManager &CM = C.getConstraintManager(); SVal V = getArgSVal(Call, getArgNo()); + QualType T = Summary.getArgType(getArgNo()); if (auto N = V.getAs<NonLoc>()) { - const IntRangeVector &R = getRanges(); - size_t E = R.size(); - for (size_t I = 0; I != E; ++I) { - const llvm::APSInt &Min = BVF.getValue(R[I].first, T); - const llvm::APSInt &Max = BVF.getValue(R[I].second, T); - assert(Min <= Max); + auto ExcludeRangeFromArg = [&](const llvm::APSInt &Min, + const llvm::APSInt &Max) { State = CM.assumeInclusiveRange(State, *N, Min, Max, false); - if (!State) - break; - } + return static_cast<bool>(State); + }; + // "OutOfRange R" is handled by excluding all ranges in R. + // "WithinRange R" is treated as "OutOfRange [T_MIN, T_MAX] \ R". + applyOnRange(negateKind(Kind), C.getSValBuilder().getBasicValueFactory(), T, + ExcludeRangeFromArg); } return State; } -ProgramStateRef StdLibraryFunctionsChecker::RangeConstraint::applyAsWithinRange( - ProgramStateRef State, const CallEvent &Call, - const Summary &Summary) const { - if (Ranges.empty()) - return State; +void StdLibraryFunctionsChecker::RangeConstraint::describe( + DescriptionKind DK, const CallEvent &Call, ProgramStateRef State, + const Summary &Summary, llvm::raw_ostream &Out) const { + + BasicValueFactory &BVF = getBVF(State); + QualType T = Summary.getArgType(getArgNo()); + + Out << ((DK == Violation) ? "should be " : "is "); + if (!Description.empty()) { + Out << Description; + } else { + unsigned I = Ranges.size(); + if (Kind == WithinRange) { + for (const std::pair<RangeInt, RangeInt> &R : Ranges) { + appendInsideRangeDesc(BVF.getValue(R.first, T), + BVF.getValue(R.second, T), T, BVF, Out); + if (--I > 0) + Out << " or "; + } + } else { + for (const std::pair<RangeInt, RangeInt> &R : Ranges) { + appendOutOfRangeDesc(BVF.getValue(R.first, T), + BVF.getValue(R.second, T), T, BVF, Out); + if (--I > 0) + Out << " and "; + } + } + } +} + +bool StdLibraryFunctionsChecker::RangeConstraint::describeArgumentValue( + const CallEvent &Call, ProgramStateRef State, const Summary &Summary, + llvm::raw_ostream &Out) const { + unsigned int NRanges = 0; + bool HaveAllRanges = true; ProgramStateManager &Mgr = State->getStateManager(); - SValBuilder &SVB = Mgr.getSValBuilder(); - BasicValueFactory &BVF = SVB.getBasicValueFactory(); + BasicValueFactory &BVF = Mgr.getSValBuilder().getBasicValueFactory(); ConstraintManager &CM = Mgr.getConstraintManager(); - QualType T = Summary.getArgType(getArgNo()); SVal V = getArgSVal(Call, getArgNo()); - // "WithinRange R" is treated as "outside [T_MIN, T_MAX] \ R". - // We cut off [T_MIN, min(R) - 1] and [max(R) + 1, T_MAX] if necessary, - // and then cut away all holes in R one by one. - // - // E.g. consider a range list R as [A, B] and [C, D] - // -------+--------+------------------+------------+-----------> - // A B C D - // Then we assume that the value is not in [-inf, A - 1], - // then not in [D + 1, +inf], then not in [B + 1, C - 1] if (auto N = V.getAs<NonLoc>()) { - const IntRangeVector &R = getRanges(); - size_t E = R.size(); - - const llvm::APSInt &MinusInf = BVF.getMinValue(T); - const llvm::APSInt &PlusInf = BVF.getMaxValue(T); - - const llvm::APSInt &Left = BVF.getValue(R[0].first - 1ULL, T); - if (Left != PlusInf) { - assert(MinusInf <= Left); - State = CM.assumeInclusiveRange(State, *N, MinusInf, Left, false); - if (!State) - return nullptr; - } - - const llvm::APSInt &Right = BVF.getValue(R[E - 1].second + 1ULL, T); - if (Right != MinusInf) { - assert(Right <= PlusInf); - State = CM.assumeInclusiveRange(State, *N, Right, PlusInf, false); - if (!State) - return nullptr; + if (const llvm::APSInt *Int = N->getAsInteger()) { + Out << "is "; + Out << *Int; + return true; } - - for (size_t I = 1; I != E; ++I) { - const llvm::APSInt &Min = BVF.getValue(R[I - 1].second + 1ULL, T); - const llvm::APSInt &Max = BVF.getValue(R[I].first - 1ULL, T); - if (Min <= Max) { - State = CM.assumeInclusiveRange(State, *N, Min, Max, false); - if (!State) - return nullptr; + QualType T = Summary.getArgType(getArgNo()); + SmallString<128> MoreInfo; + llvm::raw_svector_ostream MoreInfoOs(MoreInfo); + auto ApplyF = [&](const llvm::APSInt &Min, const llvm::APSInt &Max) { + if (CM.assumeInclusiveRange(State, *N, Min, Max, true)) { + if (NRanges > 0) + MoreInfoOs << " or "; + appendInsideRangeDesc(Min, Max, T, BVF, MoreInfoOs); + ++NRanges; + } else { + HaveAllRanges = false; } + return true; + }; + + applyOnRange(Kind, BVF, T, ApplyF); + assert(NRanges > 0); + if (!HaveAllRanges || NRanges == 1) { + Out << "is "; + Out << MoreInfo; + return true; } } - - return State; + return false; } ProgramStateRef StdLibraryFunctionsChecker::ComparisonConstraint::apply( @@ -800,9 +1151,165 @@ ProgramStateRef StdLibraryFunctionsChecker::ComparisonConstraint::apply( return State; } +ProgramStateRef StdLibraryFunctionsChecker::NotNullConstraint::apply( + ProgramStateRef State, const CallEvent &Call, const Summary &Summary, + CheckerContext &C) const { + SVal V = getArgSVal(Call, getArgNo()); + if (V.isUndef()) + return State; + + DefinedOrUnknownSVal L = V.castAs<DefinedOrUnknownSVal>(); + if (!isa<Loc>(L)) + return State; + + return State->assume(L, CannotBeNull); +} + +void StdLibraryFunctionsChecker::NotNullConstraint::describe( + DescriptionKind DK, const CallEvent &Call, ProgramStateRef State, + const Summary &Summary, llvm::raw_ostream &Out) const { + assert(CannotBeNull && + "Describe should not be used when the value must be NULL"); + if (DK == Violation) + Out << "should not be NULL"; + else + Out << "is not NULL"; +} + +bool StdLibraryFunctionsChecker::NotNullConstraint::describeArgumentValue( + const CallEvent &Call, ProgramStateRef State, const Summary &Summary, + llvm::raw_ostream &Out) const { + assert(!CannotBeNull && "This function is used when the value is NULL"); + Out << "is NULL"; + return true; +} + +ProgramStateRef StdLibraryFunctionsChecker::NotNullBufferConstraint::apply( + ProgramStateRef State, const CallEvent &Call, const Summary &Summary, + CheckerContext &C) const { + SVal V = getArgSVal(Call, getArgNo()); + if (V.isUndef()) + return State; + DefinedOrUnknownSVal L = V.castAs<DefinedOrUnknownSVal>(); + if (!isa<Loc>(L)) + return State; + + std::optional<DefinedOrUnknownSVal> SizeArg1 = + getArgSVal(Call, SizeArg1N).getAs<DefinedOrUnknownSVal>(); + std::optional<DefinedOrUnknownSVal> SizeArg2; + if (SizeArg2N) + SizeArg2 = getArgSVal(Call, *SizeArg2N).getAs<DefinedOrUnknownSVal>(); + + auto IsArgZero = [State](std::optional<DefinedOrUnknownSVal> Val) { + if (!Val) + return false; + auto [IsNonNull, IsNull] = State->assume(*Val); + return IsNull && !IsNonNull; + }; + + if (IsArgZero(SizeArg1) || IsArgZero(SizeArg2)) + return State; + + return State->assume(L, CannotBeNull); +} + +void StdLibraryFunctionsChecker::NotNullBufferConstraint::describe( + DescriptionKind DK, const CallEvent &Call, ProgramStateRef State, + const Summary &Summary, llvm::raw_ostream &Out) const { + assert(CannotBeNull && + "Describe should not be used when the value must be NULL"); + if (DK == Violation) + Out << "should not be NULL"; + else + Out << "is not NULL"; +} + +bool StdLibraryFunctionsChecker::NotNullBufferConstraint::describeArgumentValue( + const CallEvent &Call, ProgramStateRef State, const Summary &Summary, + llvm::raw_ostream &Out) const { + assert(!CannotBeNull && "This function is used when the value is NULL"); + Out << "is NULL"; + return true; +} + +ProgramStateRef StdLibraryFunctionsChecker::BufferSizeConstraint::apply( + ProgramStateRef State, const CallEvent &Call, const Summary &Summary, + CheckerContext &C) const { + SValBuilder &SvalBuilder = C.getSValBuilder(); + // The buffer argument. + SVal BufV = getArgSVal(Call, getArgNo()); + + // Get the size constraint. + const SVal SizeV = [this, &State, &Call, &Summary, &SvalBuilder]() { + if (ConcreteSize) { + return SVal(SvalBuilder.makeIntVal(*ConcreteSize)); + } + assert(SizeArgN && "The constraint must be either a concrete value or " + "encoded in an argument."); + // The size argument. + SVal SizeV = getArgSVal(Call, *SizeArgN); + // Multiply with another argument if given. + if (SizeMultiplierArgN) { + SVal SizeMulV = getArgSVal(Call, *SizeMultiplierArgN); + SizeV = SvalBuilder.evalBinOp(State, BO_Mul, SizeV, SizeMulV, + Summary.getArgType(*SizeArgN)); + } + return SizeV; + }(); + + // The dynamic size of the buffer argument, got from the analyzer engine. + SVal BufDynSize = getDynamicExtentWithOffset(State, BufV); + + SVal Feasible = SvalBuilder.evalBinOp(State, Op, SizeV, BufDynSize, + SvalBuilder.getContext().BoolTy); + if (auto F = Feasible.getAs<DefinedOrUnknownSVal>()) + return State->assume(*F, true); + + // We can get here only if the size argument or the dynamic size is + // undefined. But the dynamic size should never be undefined, only + // unknown. So, here, the size of the argument is undefined, i.e. we + // cannot apply the constraint. Actually, other checkers like + // CallAndMessage should catch this situation earlier, because we call a + // function with an uninitialized argument. + llvm_unreachable("Size argument or the dynamic size is Undefined"); +} + +void StdLibraryFunctionsChecker::BufferSizeConstraint::describe( + DescriptionKind DK, const CallEvent &Call, ProgramStateRef State, + const Summary &Summary, llvm::raw_ostream &Out) const { + Out << ((DK == Violation) ? "should be " : "is "); + Out << "a buffer with size equal to or greater than "; + if (ConcreteSize) { + Out << *ConcreteSize; + } else if (SizeArgN) { + Out << "the value of the "; + printArgDesc(*SizeArgN, Out); + printArgValueInfo(*SizeArgN, State, Call, Out); + if (SizeMultiplierArgN) { + Out << " times the "; + printArgDesc(*SizeMultiplierArgN, Out); + printArgValueInfo(*SizeMultiplierArgN, State, Call, Out); + } + } +} + +bool StdLibraryFunctionsChecker::BufferSizeConstraint::describeArgumentValue( + const CallEvent &Call, ProgramStateRef State, const Summary &Summary, + llvm::raw_ostream &Out) const { + SVal BufV = getArgSVal(Call, getArgNo()); + SVal BufDynSize = getDynamicExtentWithOffset(State, BufV); + if (const llvm::APSInt *Val = + State->getStateManager().getSValBuilder().getKnownValue(State, + BufDynSize)) { + Out << "is a buffer with size " << *Val; + return true; + } + return false; +} + void StdLibraryFunctionsChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { - Optional<Summary> FoundSummary = findFunctionSummary(Call, C); + std::optional<Summary> FoundSummary = findFunctionSummary(Call, C); if (!FoundSummary) return; @@ -810,55 +1317,155 @@ void StdLibraryFunctionsChecker::checkPreCall(const CallEvent &Call, ProgramStateRef State = C.getState(); ProgramStateRef NewState = State; + ExplodedNode *NewNode = C.getPredecessor(); for (const ValueConstraintPtr &Constraint : Summary.getArgConstraints()) { + ValueConstraintPtr NegatedConstraint = Constraint->negate(); ProgramStateRef SuccessSt = Constraint->apply(NewState, Call, Summary, C); ProgramStateRef FailureSt = - Constraint->negate()->apply(NewState, Call, Summary, C); + NegatedConstraint->apply(NewState, Call, Summary, C); // The argument constraint is not satisfied. if (FailureSt && !SuccessSt) { - if (ExplodedNode *N = C.generateErrorNode(NewState)) - reportBug(Call, N, Constraint.get(), Summary, C); + if (ExplodedNode *N = C.generateErrorNode(State, NewNode)) + reportBug(Call, N, Constraint.get(), NegatedConstraint.get(), Summary, + C); break; - } else { - // We will apply the constraint even if we cannot reason about the - // argument. This means both SuccessSt and FailureSt can be true. If we - // weren't applying the constraint that would mean that symbolic - // execution continues on a code whose behaviour is undefined. - assert(SuccessSt); - NewState = SuccessSt; + } + // We will apply the constraint even if we cannot reason about the + // argument. This means both SuccessSt and FailureSt can be true. If we + // weren't applying the constraint that would mean that symbolic + // execution continues on a code whose behaviour is undefined. + assert(SuccessSt); + NewState = SuccessSt; + if (NewState != State) { + SmallString<128> Msg; + llvm::raw_svector_ostream Os(Msg); + Os << "Assuming that the "; + printArgDesc(Constraint->getArgNo(), Os); + Os << " to '"; + Os << getFunctionName(Call); + Os << "' "; + Constraint->describe(ValueConstraint::Assumption, Call, NewState, Summary, + Os); + const auto ArgSVal = Call.getArgSVal(Constraint->getArgNo()); + NewNode = C.addTransition( + NewState, NewNode, + C.getNoteTag([Msg = std::move(Msg), ArgSVal]( + PathSensitiveBugReport &BR, llvm::raw_ostream &OS) { + if (BR.isInteresting(ArgSVal)) + OS << Msg; + })); } } - if (NewState && NewState != State) - C.addTransition(NewState); } void StdLibraryFunctionsChecker::checkPostCall(const CallEvent &Call, CheckerContext &C) const { - Optional<Summary> FoundSummary = findFunctionSummary(Call, C); + std::optional<Summary> FoundSummary = findFunctionSummary(Call, C); if (!FoundSummary) return; // Now apply the constraints. const Summary &Summary = *FoundSummary; ProgramStateRef State = C.getState(); + ExplodedNode *Node = C.getPredecessor(); // Apply case/branch specifications. - for (const ConstraintSet &Case : Summary.getCaseConstraints()) { + for (const SummaryCase &Case : Summary.getCases()) { ProgramStateRef NewState = State; - for (const ValueConstraintPtr &Constraint : Case) { + for (const ValueConstraintPtr &Constraint : Case.getConstraints()) { NewState = Constraint->apply(NewState, Call, Summary, C); if (!NewState) break; } - if (NewState && NewState != State) + if (NewState) + NewState = Case.getErrnoConstraint().apply(NewState, Call, Summary, C); + + if (!NewState) + continue; + + // 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; + 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, 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 + // (or this) checkers had a chance to add other successors. + // Dereferencing the saved node object is valid because it's part + // of a bug report call sequence. + // FIXME: This check is not exact. We may be here after a state + // split that was performed by another checker (and can not find + // the successors). This is why this check is only used in the + // EvalCallAsPure case. + if (BR.isInteresting(RV) && Node->succ_size() > 1) + return CaseNote; + return ""; + }); + Pred = C.addTransition(NewState, Pred, Tag); + } + } else { + if (!CaseNote.empty() || !ErrnoNote.empty()) { + const NoteTag *Tag = + 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); + } + } + + // Add the transition if no note tag was added. + if (Pred == Node && NewState != State) C.addTransition(NewState); } } bool StdLibraryFunctionsChecker::evalCall(const CallEvent &Call, CheckerContext &C) const { - Optional<Summary> FoundSummary = findFunctionSummary(Call, C); + std::optional<Summary> FoundSummary = findFunctionSummary(Call, C); if (!FoundSummary) return false; @@ -871,7 +1478,9 @@ bool StdLibraryFunctionsChecker::evalCall(const CallEvent &Call, SVal V = C.getSValBuilder().conjureSymbolVal( CE, LC, CE->getType().getCanonicalType(), C.blockCount()); State = State->BindExpr(CE, LC, V); + C.addTransition(State); + return true; } case NoEvalCall: @@ -910,12 +1519,11 @@ bool StdLibraryFunctionsChecker::Signature::matches( } // Check the argument types. - for (size_t I = 0, E = ArgTys.size(); I != E; ++I) { - QualType ArgTy = ArgTys[I]; + for (auto [Idx, ArgTy] : llvm::enumerate(ArgTys)) { if (isIrrelevant(ArgTy)) continue; QualType FDArgTy = - RemoveRestrict(FD->getParamDecl(I)->getType().getCanonicalType()); + RemoveRestrict(FD->getParamDecl(Idx)->getType().getCanonicalType()); if (ArgTy != FDArgTy) return false; } @@ -923,26 +1531,26 @@ bool StdLibraryFunctionsChecker::Signature::matches( return true; } -Optional<StdLibraryFunctionsChecker::Summary> +std::optional<StdLibraryFunctionsChecker::Summary> StdLibraryFunctionsChecker::findFunctionSummary(const FunctionDecl *FD, CheckerContext &C) const { if (!FD) - return None; + return std::nullopt; initFunctionSummaries(C); auto FSMI = FunctionSummaryMap.find(FD->getCanonicalDecl()); if (FSMI == FunctionSummaryMap.end()) - return None; + return std::nullopt; return FSMI->second; } -Optional<StdLibraryFunctionsChecker::Summary> +std::optional<StdLibraryFunctionsChecker::Summary> StdLibraryFunctionsChecker::findFunctionSummary(const CallEvent &Call, CheckerContext &C) const { const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl()); if (!FD) - return None; + return std::nullopt; return findFunctionSummary(FD, C); } @@ -950,10 +1558,12 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( CheckerContext &C) const { if (SummariesInitialized) return; + SummariesInitialized = true; SValBuilder &SVB = C.getSValBuilder(); BasicValueFactory &BVF = SVB.getBasicValueFactory(); const ASTContext &ACtx = BVF.getContext(); + Preprocessor &PP = C.getPreprocessor(); // Helper class to lookup a type by its name. class LookupType { @@ -963,11 +1573,11 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( LookupType(const ASTContext &ACtx) : ACtx(ACtx) {} // Find the type. If not found then the optional is not set. - llvm::Optional<QualType> operator()(StringRef Name) { + std::optional<QualType> operator()(StringRef Name) { IdentifierInfo &II = ACtx.Idents.get(Name); auto LookupRes = ACtx.getTranslationUnitDecl()->lookup(&II); if (LookupRes.empty()) - return None; + return std::nullopt; // Prioritze typedef declarations. // This is needed in case of C struct typedefs. E.g.: @@ -985,7 +1595,7 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( for (Decl *D : LookupRes) if (auto *TD = dyn_cast<TypeDecl>(D)) return ACtx.getTypeDeclType(TD).getCanonicalType(); - return None; + return std::nullopt; } } lookupTy(ACtx); @@ -999,10 +1609,10 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( QualType operator()(QualType Ty) { return ACtx.getLangOpts().C99 ? ACtx.getRestrictType(Ty) : Ty; } - Optional<QualType> operator()(Optional<QualType> Ty) { + std::optional<QualType> operator()(std::optional<QualType> Ty) { if (Ty) return operator()(*Ty); - return None; + return std::nullopt; } } getRestrictTy(ACtx); class GetPointerTy { @@ -1011,16 +1621,16 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( public: GetPointerTy(const ASTContext &ACtx) : ACtx(ACtx) {} QualType operator()(QualType Ty) { return ACtx.getPointerType(Ty); } - Optional<QualType> operator()(Optional<QualType> Ty) { + std::optional<QualType> operator()(std::optional<QualType> Ty) { if (Ty) return operator()(*Ty); - return None; + return std::nullopt; } } getPointerTy(ACtx); class { public: - Optional<QualType> operator()(Optional<QualType> Ty) { - return Ty ? Optional<QualType>(Ty->withConst()) : None; + std::optional<QualType> operator()(std::optional<QualType> Ty) { + return Ty ? std::optional<QualType>(Ty->withConst()) : std::nullopt; } QualType operator()(QualType Ty) { return Ty.withConst(); } } getConstTy; @@ -1029,14 +1639,14 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( public: GetMaxValue(BasicValueFactory &BVF) : BVF(BVF) {} - Optional<RangeInt> operator()(QualType Ty) { + std::optional<RangeInt> operator()(QualType Ty) { return BVF.getMaxValue(Ty).getLimitedValue(); } - Optional<RangeInt> operator()(Optional<QualType> Ty) { + std::optional<RangeInt> operator()(std::optional<QualType> Ty) { if (Ty) { return operator()(*Ty); } - return None; + return std::nullopt; } } getMaxValue(BVF); @@ -1089,14 +1699,11 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( const RangeInt UCharRangeMax = std::min(BVF.getMaxValue(ACtx.UnsignedCharTy).getLimitedValue(), IntMax); - // The platform dependent value of EOF. - // Try our best to parse this from the Preprocessor, otherwise fallback to -1. - const auto EOFv = [&C]() -> RangeInt { - if (const llvm::Optional<int> OptInt = - tryExpandAsInteger("EOF", C.getPreprocessor())) - return *OptInt; - return -1; - }(); + // Get platform dependent values of some macros. + // Try our best to parse this from the Preprocessor, otherwise fallback to a + // default value (what is found in a library header). + const auto EOFv = tryExpandAsInteger("EOF", PP).value_or(-1); + const auto AT_FDCWDv = tryExpandAsInteger("AT_FDCWD", PP).value_or(-100); // Auxiliary class to aid adding summaries to the summary map. struct AddToFunctionSummaryMap { @@ -1146,9 +1753,9 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( } addToFunctionSummaryMap(ACtx, FunctionSummaryMap, DisplayLoadedSummaries); // Below are helpers functions to create the summaries. - auto ArgumentCondition = [](ArgNo ArgN, RangeKind Kind, - IntRangeVector Ranges) { - return std::make_shared<RangeConstraint>(ArgN, Kind, Ranges); + auto ArgumentCondition = [](ArgNo ArgN, RangeKind Kind, IntRangeVector Ranges, + StringRef Desc = "") { + return std::make_shared<RangeConstraint>(ArgN, Kind, Ranges, Desc); }; auto BufferSize = [](auto... Args) { return std::make_shared<BufferSizeConstraint>(Args...); @@ -1165,13 +1772,13 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( auto operator()(RangeInt b, RangeInt e) { return IntRangeVector{std::pair<RangeInt, RangeInt>{b, e}}; } - auto operator()(RangeInt b, Optional<RangeInt> e) { + auto operator()(RangeInt b, std::optional<RangeInt> e) { if (e) return IntRangeVector{std::pair<RangeInt, RangeInt>{b, *e}}; return IntRangeVector{}; } auto operator()(std::pair<RangeInt, RangeInt> i0, - std::pair<RangeInt, Optional<RangeInt>> i1) { + std::pair<RangeInt, std::optional<RangeInt>> i1) { if (i1.second) return IntRangeVector{i0, {i1.first, *(i1.second)}}; return IntRangeVector{i0}; @@ -1184,10 +1791,26 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( auto NotNull = [&](ArgNo ArgN) { return std::make_shared<NotNullConstraint>(ArgN); }; + auto IsNull = [&](ArgNo ArgN) { + return std::make_shared<NotNullConstraint>(ArgN, false); + }; + auto NotNullBuffer = [&](ArgNo ArgN, ArgNo SizeArg1N, ArgNo SizeArg2N) { + return std::make_shared<NotNullBufferConstraint>(ArgN, SizeArg1N, + SizeArg2N); + }; - Optional<QualType> FileTy = lookupTy("FILE"); - Optional<QualType> FilePtrTy = getPointerTy(FileTy); - Optional<QualType> FilePtrRestrictTy = getRestrictTy(FilePtrTy); + std::optional<QualType> FileTy = lookupTy("FILE"); + std::optional<QualType> FilePtrTy = getPointerTy(FileTy); + std::optional<QualType> FilePtrRestrictTy = getRestrictTy(FilePtrTy); + + std::optional<QualType> FPosTTy = lookupTy("fpos_t"); + std::optional<QualType> FPosTPtrTy = getPointerTy(FPosTTy); + std::optional<QualType> ConstFPosTPtrTy = getPointerTy(getConstTy(FPosTTy)); + std::optional<QualType> FPosTPtrRestrictTy = getRestrictTy(FPosTPtrTy); + + constexpr llvm::StringLiteral GenericSuccessMsg( + "Assuming that '{0}' is successful"); + constexpr llvm::StringLiteral GenericFailureMsg("Assuming that '{0}' fails"); // We are finally ready to define specifications for all supported functions. // @@ -1210,163 +1833,227 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( // Boils down to isupper() or islower() or isdigit(). .Case({ArgumentCondition(0U, WithinRange, {{'0', '9'}, {'A', 'Z'}, {'a', 'z'}}), - ReturnValueCondition(OutOfRange, SingleValue(0))}) + ReturnValueCondition(OutOfRange, SingleValue(0))}, + ErrnoIrrelevant, "Assuming the character is alphanumeric") // The locale-specific range. // No post-condition. We are completely unaware of // locale-specific return values. - .Case({ArgumentCondition(0U, WithinRange, {{128, UCharRangeMax}})}) + .Case({ArgumentCondition(0U, WithinRange, {{128, UCharRangeMax}})}, + ErrnoIrrelevant) .Case( {ArgumentCondition( 0U, OutOfRange, {{'0', '9'}, {'A', 'Z'}, {'a', 'z'}, {128, UCharRangeMax}}), - ReturnValueCondition(WithinRange, SingleValue(0))}) - .ArgConstraint(ArgumentCondition( - 0U, WithinRange, {{EOFv, EOFv}, {0, UCharRangeMax}}))); + ReturnValueCondition(WithinRange, SingleValue(0))}, + ErrnoIrrelevant, "Assuming the character is non-alphanumeric") + .ArgConstraint(ArgumentCondition(0U, WithinRange, + {{EOFv, EOFv}, {0, UCharRangeMax}}, + "an unsigned char value or EOF"))); addToFunctionSummaryMap( "isalpha", Signature(ArgTypes{IntTy}, RetType{IntTy}), Summary(EvalCallAsPure) .Case({ArgumentCondition(0U, WithinRange, {{'A', 'Z'}, {'a', 'z'}}), - ReturnValueCondition(OutOfRange, SingleValue(0))}) + ReturnValueCondition(OutOfRange, SingleValue(0))}, + ErrnoIrrelevant, "Assuming the character is alphabetical") // The locale-specific range. - .Case({ArgumentCondition(0U, WithinRange, {{128, UCharRangeMax}})}) + .Case({ArgumentCondition(0U, WithinRange, {{128, UCharRangeMax}})}, + ErrnoIrrelevant) .Case({ArgumentCondition( 0U, OutOfRange, {{'A', 'Z'}, {'a', 'z'}, {128, UCharRangeMax}}), - ReturnValueCondition(WithinRange, SingleValue(0))})); + ReturnValueCondition(WithinRange, SingleValue(0))}, + ErrnoIrrelevant, "Assuming the character is non-alphabetical")); addToFunctionSummaryMap( "isascii", Signature(ArgTypes{IntTy}, RetType{IntTy}), Summary(EvalCallAsPure) .Case({ArgumentCondition(0U, WithinRange, Range(0, 127)), - ReturnValueCondition(OutOfRange, SingleValue(0))}) + ReturnValueCondition(OutOfRange, SingleValue(0))}, + ErrnoIrrelevant, "Assuming the character is an ASCII character") .Case({ArgumentCondition(0U, OutOfRange, Range(0, 127)), - ReturnValueCondition(WithinRange, SingleValue(0))})); + ReturnValueCondition(WithinRange, SingleValue(0))}, + ErrnoIrrelevant, + "Assuming the character is not an ASCII character")); addToFunctionSummaryMap( "isblank", Signature(ArgTypes{IntTy}, RetType{IntTy}), Summary(EvalCallAsPure) .Case({ArgumentCondition(0U, WithinRange, {{'\t', '\t'}, {' ', ' '}}), - ReturnValueCondition(OutOfRange, SingleValue(0))}) + ReturnValueCondition(OutOfRange, SingleValue(0))}, + ErrnoIrrelevant, "Assuming the character is a blank character") .Case({ArgumentCondition(0U, OutOfRange, {{'\t', '\t'}, {' ', ' '}}), - ReturnValueCondition(WithinRange, SingleValue(0))})); + ReturnValueCondition(WithinRange, SingleValue(0))}, + ErrnoIrrelevant, + "Assuming the character is not a blank character")); addToFunctionSummaryMap( "iscntrl", Signature(ArgTypes{IntTy}, RetType{IntTy}), Summary(EvalCallAsPure) .Case({ArgumentCondition(0U, WithinRange, {{0, 32}, {127, 127}}), - ReturnValueCondition(OutOfRange, SingleValue(0))}) + ReturnValueCondition(OutOfRange, SingleValue(0))}, + ErrnoIrrelevant, + "Assuming the character is a control character") .Case({ArgumentCondition(0U, OutOfRange, {{0, 32}, {127, 127}}), - ReturnValueCondition(WithinRange, SingleValue(0))})); + ReturnValueCondition(WithinRange, SingleValue(0))}, + ErrnoIrrelevant, + "Assuming the character is not a control character")); addToFunctionSummaryMap( "isdigit", Signature(ArgTypes{IntTy}, RetType{IntTy}), Summary(EvalCallAsPure) .Case({ArgumentCondition(0U, WithinRange, Range('0', '9')), - ReturnValueCondition(OutOfRange, SingleValue(0))}) + ReturnValueCondition(OutOfRange, SingleValue(0))}, + ErrnoIrrelevant, "Assuming the character is a digit") .Case({ArgumentCondition(0U, OutOfRange, Range('0', '9')), - ReturnValueCondition(WithinRange, SingleValue(0))})); + ReturnValueCondition(WithinRange, SingleValue(0))}, + ErrnoIrrelevant, "Assuming the character is not a digit")); addToFunctionSummaryMap( "isgraph", Signature(ArgTypes{IntTy}, RetType{IntTy}), Summary(EvalCallAsPure) .Case({ArgumentCondition(0U, WithinRange, Range(33, 126)), - ReturnValueCondition(OutOfRange, SingleValue(0))}) - .Case({ArgumentCondition(0U, OutOfRange, Range(33, 126)), - ReturnValueCondition(WithinRange, SingleValue(0))})); + ReturnValueCondition(OutOfRange, SingleValue(0))}, + ErrnoIrrelevant, + "Assuming the character has graphical representation") + .Case( + {ArgumentCondition(0U, OutOfRange, Range(33, 126)), + ReturnValueCondition(WithinRange, SingleValue(0))}, + ErrnoIrrelevant, + "Assuming the character does not have graphical representation")); addToFunctionSummaryMap( "islower", Signature(ArgTypes{IntTy}, RetType{IntTy}), Summary(EvalCallAsPure) // Is certainly lowercase. .Case({ArgumentCondition(0U, WithinRange, Range('a', 'z')), - ReturnValueCondition(OutOfRange, SingleValue(0))}) + ReturnValueCondition(OutOfRange, SingleValue(0))}, + ErrnoIrrelevant, "Assuming the character is a lowercase letter") // Is ascii but not lowercase. .Case({ArgumentCondition(0U, WithinRange, Range(0, 127)), ArgumentCondition(0U, OutOfRange, Range('a', 'z')), - ReturnValueCondition(WithinRange, SingleValue(0))}) + ReturnValueCondition(WithinRange, SingleValue(0))}, + ErrnoIrrelevant, + "Assuming the character is not a lowercase letter") // The locale-specific range. - .Case({ArgumentCondition(0U, WithinRange, {{128, UCharRangeMax}})}) + .Case({ArgumentCondition(0U, WithinRange, {{128, UCharRangeMax}})}, + ErrnoIrrelevant) // Is not an unsigned char. .Case({ArgumentCondition(0U, OutOfRange, Range(0, UCharRangeMax)), - ReturnValueCondition(WithinRange, SingleValue(0))})); + ReturnValueCondition(WithinRange, SingleValue(0))}, + ErrnoIrrelevant)); addToFunctionSummaryMap( "isprint", Signature(ArgTypes{IntTy}, RetType{IntTy}), Summary(EvalCallAsPure) .Case({ArgumentCondition(0U, WithinRange, Range(32, 126)), - ReturnValueCondition(OutOfRange, SingleValue(0))}) + ReturnValueCondition(OutOfRange, SingleValue(0))}, + ErrnoIrrelevant, "Assuming the character is printable") .Case({ArgumentCondition(0U, OutOfRange, Range(32, 126)), - ReturnValueCondition(WithinRange, SingleValue(0))})); + ReturnValueCondition(WithinRange, SingleValue(0))}, + ErrnoIrrelevant, "Assuming the character is non-printable")); addToFunctionSummaryMap( "ispunct", Signature(ArgTypes{IntTy}, RetType{IntTy}), Summary(EvalCallAsPure) .Case({ArgumentCondition( 0U, WithinRange, {{'!', '/'}, {':', '@'}, {'[', '`'}, {'{', '~'}}), - ReturnValueCondition(OutOfRange, SingleValue(0))}) + ReturnValueCondition(OutOfRange, SingleValue(0))}, + ErrnoIrrelevant, "Assuming the character is a punctuation mark") .Case({ArgumentCondition( 0U, OutOfRange, {{'!', '/'}, {':', '@'}, {'[', '`'}, {'{', '~'}}), - ReturnValueCondition(WithinRange, SingleValue(0))})); + ReturnValueCondition(WithinRange, SingleValue(0))}, + ErrnoIrrelevant, + "Assuming the character is not a punctuation mark")); addToFunctionSummaryMap( "isspace", Signature(ArgTypes{IntTy}, RetType{IntTy}), Summary(EvalCallAsPure) // Space, '\f', '\n', '\r', '\t', '\v'. .Case({ArgumentCondition(0U, WithinRange, {{9, 13}, {' ', ' '}}), - ReturnValueCondition(OutOfRange, SingleValue(0))}) + ReturnValueCondition(OutOfRange, SingleValue(0))}, + ErrnoIrrelevant, + "Assuming the character is a whitespace character") // The locale-specific range. - .Case({ArgumentCondition(0U, WithinRange, {{128, UCharRangeMax}})}) + .Case({ArgumentCondition(0U, WithinRange, {{128, UCharRangeMax}})}, + ErrnoIrrelevant) .Case({ArgumentCondition(0U, OutOfRange, {{9, 13}, {' ', ' '}, {128, UCharRangeMax}}), - ReturnValueCondition(WithinRange, SingleValue(0))})); + ReturnValueCondition(WithinRange, SingleValue(0))}, + ErrnoIrrelevant, + "Assuming the character is not a whitespace character")); addToFunctionSummaryMap( "isupper", Signature(ArgTypes{IntTy}, RetType{IntTy}), Summary(EvalCallAsPure) // Is certainly uppercase. .Case({ArgumentCondition(0U, WithinRange, Range('A', 'Z')), - ReturnValueCondition(OutOfRange, SingleValue(0))}) + ReturnValueCondition(OutOfRange, SingleValue(0))}, + ErrnoIrrelevant, + "Assuming the character is an uppercase letter") // The locale-specific range. - .Case({ArgumentCondition(0U, WithinRange, {{128, UCharRangeMax}})}) + .Case({ArgumentCondition(0U, WithinRange, {{128, UCharRangeMax}})}, + ErrnoIrrelevant) // Other. .Case({ArgumentCondition(0U, OutOfRange, {{'A', 'Z'}, {128, UCharRangeMax}}), - ReturnValueCondition(WithinRange, SingleValue(0))})); + ReturnValueCondition(WithinRange, SingleValue(0))}, + ErrnoIrrelevant, + "Assuming the character is not an uppercase letter")); addToFunctionSummaryMap( "isxdigit", Signature(ArgTypes{IntTy}, RetType{IntTy}), Summary(EvalCallAsPure) .Case({ArgumentCondition(0U, WithinRange, {{'0', '9'}, {'A', 'F'}, {'a', 'f'}}), - ReturnValueCondition(OutOfRange, SingleValue(0))}) + ReturnValueCondition(OutOfRange, SingleValue(0))}, + ErrnoIrrelevant, + "Assuming the character is a hexadecimal digit") .Case({ArgumentCondition(0U, OutOfRange, {{'0', '9'}, {'A', 'F'}, {'a', 'f'}}), - ReturnValueCondition(WithinRange, SingleValue(0))})); + ReturnValueCondition(WithinRange, SingleValue(0))}, + ErrnoIrrelevant, + "Assuming the character is not a hexadecimal digit")); addToFunctionSummaryMap( "toupper", Signature(ArgTypes{IntTy}, RetType{IntTy}), Summary(EvalCallAsPure) - .ArgConstraint(ArgumentCondition( - 0U, WithinRange, {{EOFv, EOFv}, {0, UCharRangeMax}}))); + .ArgConstraint(ArgumentCondition(0U, WithinRange, + {{EOFv, EOFv}, {0, UCharRangeMax}}, + "an unsigned char value or EOF"))); addToFunctionSummaryMap( "tolower", Signature(ArgTypes{IntTy}, RetType{IntTy}), Summary(EvalCallAsPure) - .ArgConstraint(ArgumentCondition( - 0U, WithinRange, {{EOFv, EOFv}, {0, UCharRangeMax}}))); + .ArgConstraint(ArgumentCondition(0U, WithinRange, + {{EOFv, EOFv}, {0, UCharRangeMax}}, + "an unsigned char value or EOF"))); addToFunctionSummaryMap( "toascii", Signature(ArgTypes{IntTy}, RetType{IntTy}), Summary(EvalCallAsPure) - .ArgConstraint(ArgumentCondition( - 0U, WithinRange, {{EOFv, EOFv}, {0, UCharRangeMax}}))); + .ArgConstraint(ArgumentCondition(0U, WithinRange, + {{EOFv, EOFv}, {0, UCharRangeMax}}, + "an unsigned char value or EOF"))); // The getc() family of functions that returns either a char or an EOF. addToFunctionSummaryMap( {"getc", "fgetc"}, Signature(ArgTypes{FilePtrTy}, RetType{IntTy}), Summary(NoEvalCall) .Case({ReturnValueCondition(WithinRange, - {{EOFv, EOFv}, {0, UCharRangeMax}})})); + {{EOFv, EOFv}, {0, UCharRangeMax}})}, + ErrnoIrrelevant)); addToFunctionSummaryMap( "getchar", Signature(ArgTypes{}, RetType{IntTy}), Summary(NoEvalCall) .Case({ReturnValueCondition(WithinRange, - {{EOFv, EOFv}, {0, UCharRangeMax}})})); + {{EOFv, EOFv}, {0, UCharRangeMax}})}, + ErrnoIrrelevant)); // read()-like functions that never return more than buffer size. auto FreadSummary = Summary(NoEvalCall) - .Case({ReturnValueCondition(LessThanOrEq, ArgNo(2)), - ReturnValueCondition(WithinRange, Range(0, SizeMax))}) - .ArgConstraint(NotNull(ArgNo(0))) + .Case({ArgumentCondition(1U, WithinRange, Range(1, SizeMax)), + ArgumentCondition(2U, WithinRange, Range(1, SizeMax)), + ReturnValueCondition(BO_LT, ArgNo(2)), + ReturnValueCondition(WithinRange, Range(0, SizeMax))}, + ErrnoNEZeroIrrelevant, GenericFailureMsg) + .Case({ArgumentCondition(1U, WithinRange, Range(1, SizeMax)), + ReturnValueCondition(BO_EQ, ArgNo(2)), + ReturnValueCondition(WithinRange, Range(0, SizeMax))}, + ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case({ArgumentCondition(1U, WithinRange, SingleValue(0)), + ReturnValueCondition(WithinRange, SingleValue(0))}, + 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), /*BufSizeMultiplier=*/ArgNo(2))); @@ -1386,13 +2073,14 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( RetType{SizeTy}), FreadSummary); - Optional<QualType> Ssize_tTy = lookupTy("ssize_t"); - Optional<RangeInt> Ssize_tMax = getMaxValue(Ssize_tTy); + std::optional<QualType> Ssize_tTy = lookupTy("ssize_t"); + std::optional<RangeInt> Ssize_tMax = getMaxValue(Ssize_tTy); auto ReadSummary = Summary(NoEvalCall) .Case({ReturnValueCondition(LessThanOrEq, ArgNo(2)), - ReturnValueCondition(WithinRange, Range(-1, Ssize_tMax))}); + ReturnValueCondition(WithinRange, Range(-1, Ssize_tMax))}, + ErrnoIrrelevant); // FIXME these are actually defined by POSIX and not by the C standard, we // should handle them together with the rest of the POSIX functions. @@ -1409,7 +2097,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( auto GetLineSummary = Summary(NoEvalCall) .Case({ReturnValueCondition(WithinRange, - Range({-1, -1}, {1, Ssize_tMax}))}); + Range({-1, -1}, {1, Ssize_tMax}))}, + ErrnoIrrelevant); QualType CharPtrPtrRestrictTy = getRestrictTy(getPointerTy(CharPtrTy)); @@ -1433,7 +2122,226 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( RetType{Ssize_tTy}), GetLineSummary); + { + Summary GetenvSummary = + Summary(NoEvalCall) + .ArgConstraint(NotNull(ArgNo(0))) + .Case({NotNull(Ret)}, ErrnoIrrelevant, + "Assuming the environment variable exists"); + // In untrusted environments the envvar might not exist. + if (!ShouldAssumeControlledEnvironment) + GetenvSummary.Case({NotNull(Ret)->negate()}, ErrnoIrrelevant, + "Assuming the environment variable does not exist"); + + // char *getenv(const char *name); + addToFunctionSummaryMap( + "getenv", Signature(ArgTypes{ConstCharPtrTy}, RetType{CharPtrTy}), + std::move(GetenvSummary)); + } + if (ModelPOSIX) { + const auto ReturnsZeroOrMinusOne = + ConstraintSet{ReturnValueCondition(WithinRange, Range(-1, 0))}; + const auto ReturnsZero = + ConstraintSet{ReturnValueCondition(WithinRange, SingleValue(0))}; + const auto ReturnsMinusOne = + ConstraintSet{ReturnValueCondition(WithinRange, SingleValue(-1))}; + const auto ReturnsEOF = + ConstraintSet{ReturnValueCondition(WithinRange, SingleValue(EOFv))}; + const auto ReturnsNonnegative = + ConstraintSet{ReturnValueCondition(WithinRange, Range(0, IntMax))}; + const auto ReturnsNonZero = + ConstraintSet{ReturnValueCondition(OutOfRange, SingleValue(0))}; + const auto ReturnsFileDescriptor = + ConstraintSet{ReturnValueCondition(WithinRange, Range(-1, IntMax))}; + const auto &ReturnsValidFileDescriptor = ReturnsNonnegative; + + auto ValidFileDescriptorOrAtFdcwd = [&](ArgNo ArgN) { + return std::make_shared<RangeConstraint>( + ArgN, WithinRange, Range({AT_FDCWDv, AT_FDCWDv}, {0, IntMax}), + "a valid file descriptor or AT_FDCWD"); + }; + + // FILE *fopen(const char *restrict pathname, const char *restrict mode); + addToFunctionSummaryMap( + "fopen", + Signature(ArgTypes{ConstCharPtrRestrictTy, ConstCharPtrRestrictTy}, + RetType{FilePtrTy}), + Summary(NoEvalCall) + .Case({NotNull(Ret)}, ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case({IsNull(Ret)}, ErrnoNEZeroIrrelevant, GenericFailureMsg) + .ArgConstraint(NotNull(ArgNo(0))) + .ArgConstraint(NotNull(ArgNo(1)))); + + // FILE *fdopen(int fd, const char *mode); + addToFunctionSummaryMap( + "fdopen", + Signature(ArgTypes{IntTy, ConstCharPtrTy}, RetType{FilePtrTy}), + Summary(NoEvalCall) + .Case({NotNull(Ret)}, ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case({IsNull(Ret)}, ErrnoNEZeroIrrelevant, GenericFailureMsg) + .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax))) + .ArgConstraint(NotNull(ArgNo(1)))); + + // FILE *tmpfile(void); + addToFunctionSummaryMap( + "tmpfile", Signature(ArgTypes{}, RetType{FilePtrTy}), + Summary(NoEvalCall) + .Case({NotNull(Ret)}, ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case({IsNull(Ret)}, ErrnoNEZeroIrrelevant, GenericFailureMsg)); + + // FILE *freopen(const char *restrict pathname, const char *restrict mode, + // FILE *restrict stream); + addToFunctionSummaryMap( + "freopen", + Signature(ArgTypes{ConstCharPtrRestrictTy, ConstCharPtrRestrictTy, + FilePtrRestrictTy}, + RetType{FilePtrTy}), + Summary(NoEvalCall) + .Case({ReturnValueCondition(BO_EQ, ArgNo(2))}, + ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case({IsNull(Ret)}, ErrnoNEZeroIrrelevant, GenericFailureMsg) + .ArgConstraint(NotNull(ArgNo(1))) + .ArgConstraint(NotNull(ArgNo(2)))); + + // int fclose(FILE *stream); + addToFunctionSummaryMap( + "fclose", Signature(ArgTypes{FilePtrTy}, RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case(ReturnsEOF, ErrnoNEZeroIrrelevant, GenericFailureMsg) + .ArgConstraint(NotNull(ArgNo(0)))); + + // int ungetc(int c, FILE *stream); + addToFunctionSummaryMap( + "ungetc", Signature(ArgTypes{IntTy, FilePtrTy}, RetType{IntTy}), + Summary(NoEvalCall) + .Case({ReturnValueCondition(BO_EQ, ArgNo(0)), + ArgumentCondition(0, WithinRange, {{0, UCharRangeMax}})}, + ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case({ReturnValueCondition(WithinRange, SingleValue(EOFv)), + ArgumentCondition(0, WithinRange, SingleValue(EOFv))}, + ErrnoNEZeroIrrelevant, + "Assuming that 'ungetc' fails because EOF was passed as " + "character") + .Case({ReturnValueCondition(WithinRange, SingleValue(EOFv)), + ArgumentCondition(0, WithinRange, {{0, UCharRangeMax}})}, + ErrnoNEZeroIrrelevant, GenericFailureMsg) + .ArgConstraint(ArgumentCondition( + 0, WithinRange, {{EOFv, EOFv}, {0, UCharRangeMax}})) + .ArgConstraint(NotNull(ArgNo(1)))); + + std::optional<QualType> Off_tTy = lookupTy("off_t"); + std::optional<RangeInt> Off_tMax = getMaxValue(Off_tTy); + + // int fseek(FILE *stream, long offset, int whence); + // FIXME: It can be possible to get the 'SEEK_' values (like EOFv) and use + // these for condition of arg 2. + // Now the range [0,2] is used (the `SEEK_*` constants are usually 0,1,2). + addToFunctionSummaryMap( + "fseek", Signature(ArgTypes{FilePtrTy, LongTy, IntTy}, RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) + .ArgConstraint(NotNull(ArgNo(0))) + .ArgConstraint(ArgumentCondition(2, WithinRange, {{0, 2}}))); + + // int fseeko(FILE *stream, off_t offset, int whence); + addToFunctionSummaryMap( + "fseeko", + Signature(ArgTypes{FilePtrTy, Off_tTy, IntTy}, RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) + .ArgConstraint(NotNull(ArgNo(0))) + .ArgConstraint(ArgumentCondition(2, WithinRange, {{0, 2}}))); + + // int fgetpos(FILE *restrict stream, fpos_t *restrict pos); + // From 'The Open Group Base Specifications Issue 7, 2018 edition': + // "The fgetpos() function shall not change the setting of errno if + // successful." + addToFunctionSummaryMap( + "fgetpos", + Signature(ArgTypes{FilePtrRestrictTy, FPosTPtrRestrictTy}, + RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZero, ErrnoUnchanged, GenericSuccessMsg) + .Case(ReturnsNonZero, ErrnoNEZeroIrrelevant, GenericFailureMsg) + .ArgConstraint(NotNull(ArgNo(0))) + .ArgConstraint(NotNull(ArgNo(1)))); + + // int fsetpos(FILE *stream, const fpos_t *pos); + // From 'The Open Group Base Specifications Issue 7, 2018 edition': + // "The fsetpos() function shall not change the setting of errno if + // successful." + addToFunctionSummaryMap( + "fsetpos", + Signature(ArgTypes{FilePtrTy, ConstFPosTPtrTy}, RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZero, ErrnoUnchanged, GenericSuccessMsg) + .Case(ReturnsNonZero, ErrnoNEZeroIrrelevant, GenericFailureMsg) + .ArgConstraint(NotNull(ArgNo(0))) + .ArgConstraint(NotNull(ArgNo(1)))); + + // int fflush(FILE *stream); + addToFunctionSummaryMap( + "fflush", Signature(ArgTypes{FilePtrTy}, RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case(ReturnsEOF, ErrnoNEZeroIrrelevant, GenericFailureMsg)); + + // long ftell(FILE *stream); + // From 'The Open Group Base Specifications Issue 7, 2018 edition': + // "The ftell() function shall not change the setting of errno if + // successful." + addToFunctionSummaryMap( + "ftell", Signature(ArgTypes{FilePtrTy}, RetType{LongTy}), + Summary(NoEvalCall) + .Case({ReturnValueCondition(WithinRange, Range(0, LongMax))}, + ErrnoUnchanged, GenericSuccessMsg) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) + .ArgConstraint(NotNull(ArgNo(0)))); + + // off_t ftello(FILE *stream); + addToFunctionSummaryMap( + "ftello", Signature(ArgTypes{FilePtrTy}, RetType{Off_tTy}), + Summary(NoEvalCall) + .Case({ReturnValueCondition(WithinRange, Range(0, Off_tMax))}, + ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) + .ArgConstraint(NotNull(ArgNo(0)))); + + // int fileno(FILE *stream); + addToFunctionSummaryMap( + "fileno", Signature(ArgTypes{FilePtrTy}, RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsValidFileDescriptor, ErrnoMustNotBeChecked, + GenericSuccessMsg) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) + .ArgConstraint(NotNull(ArgNo(0)))); + + // void rewind(FILE *stream); + // This function indicates error only by setting of 'errno'. + addToFunctionSummaryMap("rewind", + Signature(ArgTypes{FilePtrTy}, RetType{VoidTy}), + Summary(NoEvalCall) + .Case({}, ErrnoMustBeChecked) + .ArgConstraint(NotNull(ArgNo(0)))); + + // void clearerr(FILE *stream); + addToFunctionSummaryMap( + "clearerr", Signature(ArgTypes{FilePtrTy}, RetType{VoidTy}), + Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); + + // int feof(FILE *stream); + addToFunctionSummaryMap( + "feof", Signature(ArgTypes{FilePtrTy}, RetType{IntTy}), + Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); + + // int ferror(FILE *stream); + addToFunctionSummaryMap( + "ferror", Signature(ArgTypes{FilePtrTy}, RetType{IntTy}), + Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); // long a64l(const char *str64); addToFunctionSummaryMap( @@ -1447,16 +2355,32 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( .ArgConstraint(ArgumentCondition( 0, WithinRange, Range(0, LongMax)))); - const auto ReturnsZeroOrMinusOne = - ConstraintSet{ReturnValueCondition(WithinRange, Range(-1, 0))}; - const auto ReturnsFileDescriptor = - ConstraintSet{ReturnValueCondition(WithinRange, Range(-1, IntMax))}; + // int open(const char *path, int oflag, ...); + addToFunctionSummaryMap( + "open", Signature(ArgTypes{ConstCharPtrTy, IntTy}, RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsValidFileDescriptor, ErrnoMustNotBeChecked, + GenericSuccessMsg) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) + .ArgConstraint(NotNull(ArgNo(0)))); + + // int openat(int fd, const char *path, int oflag, ...); + addToFunctionSummaryMap( + "openat", + Signature(ArgTypes{IntTy, ConstCharPtrTy, IntTy}, RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsValidFileDescriptor, ErrnoMustNotBeChecked, + GenericSuccessMsg) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) + .ArgConstraint(ValidFileDescriptorOrAtFdcwd(ArgNo(0))) + .ArgConstraint(NotNull(ArgNo(1)))); // int access(const char *pathname, int amode); addToFunctionSummaryMap( "access", Signature(ArgTypes{ConstCharPtrTy, IntTy}, RetType{IntTy}), Summary(NoEvalCall) - .Case(ReturnsZeroOrMinusOne) + .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) .ArgConstraint(NotNull(ArgNo(0)))); // int faccessat(int dirfd, const char *pathname, int mode, int flags); @@ -1465,57 +2389,66 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( Signature(ArgTypes{IntTy, ConstCharPtrTy, IntTy, IntTy}, RetType{IntTy}), Summary(NoEvalCall) - .Case(ReturnsZeroOrMinusOne) + .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) + .ArgConstraint(ValidFileDescriptorOrAtFdcwd(ArgNo(0))) .ArgConstraint(NotNull(ArgNo(1)))); // int dup(int fildes); - addToFunctionSummaryMap("dup", Signature(ArgTypes{IntTy}, RetType{IntTy}), - Summary(NoEvalCall) - .Case(ReturnsFileDescriptor) - .ArgConstraint(ArgumentCondition( - 0, WithinRange, Range(0, IntMax)))); + addToFunctionSummaryMap( + "dup", Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsValidFileDescriptor, ErrnoMustNotBeChecked, + GenericSuccessMsg) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) + .ArgConstraint( + ArgumentCondition(0, WithinRange, Range(0, IntMax)))); // int dup2(int fildes1, int filedes2); addToFunctionSummaryMap( "dup2", Signature(ArgTypes{IntTy, IntTy}, RetType{IntTy}), Summary(NoEvalCall) - .Case(ReturnsFileDescriptor) + .Case(ReturnsValidFileDescriptor, ErrnoMustNotBeChecked, + GenericSuccessMsg) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax))) .ArgConstraint( ArgumentCondition(1, WithinRange, Range(0, IntMax)))); // int fdatasync(int fildes); - addToFunctionSummaryMap("fdatasync", - Signature(ArgTypes{IntTy}, RetType{IntTy}), - Summary(NoEvalCall) - .Case(ReturnsZeroOrMinusOne) - .ArgConstraint(ArgumentCondition( - 0, WithinRange, Range(0, IntMax)))); + addToFunctionSummaryMap( + "fdatasync", Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) + .ArgConstraint( + ArgumentCondition(0, WithinRange, Range(0, IntMax)))); // int fnmatch(const char *pattern, const char *string, int flags); addToFunctionSummaryMap( "fnmatch", Signature(ArgTypes{ConstCharPtrTy, ConstCharPtrTy, IntTy}, RetType{IntTy}), - Summary(EvalCallAsPure) + Summary(NoEvalCall) .ArgConstraint(NotNull(ArgNo(0))) .ArgConstraint(NotNull(ArgNo(1)))); // int fsync(int fildes); - addToFunctionSummaryMap("fsync", Signature(ArgTypes{IntTy}, RetType{IntTy}), - Summary(NoEvalCall) - .Case(ReturnsZeroOrMinusOne) - .ArgConstraint(ArgumentCondition( - 0, WithinRange, Range(0, IntMax)))); - - Optional<QualType> Off_tTy = lookupTy("off_t"); + addToFunctionSummaryMap( + "fsync", Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) + .ArgConstraint( + ArgumentCondition(0, WithinRange, Range(0, IntMax)))); // int truncate(const char *path, off_t length); addToFunctionSummaryMap( "truncate", Signature(ArgTypes{ConstCharPtrTy, Off_tTy}, RetType{IntTy}), Summary(NoEvalCall) - .Case(ReturnsZeroOrMinusOne) + .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) .ArgConstraint(NotNull(ArgNo(0)))); // int symlink(const char *oldpath, const char *newpath); @@ -1523,7 +2456,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( "symlink", Signature(ArgTypes{ConstCharPtrTy, ConstCharPtrTy}, RetType{IntTy}), Summary(NoEvalCall) - .Case(ReturnsZeroOrMinusOne) + .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) .ArgConstraint(NotNull(ArgNo(0))) .ArgConstraint(NotNull(ArgNo(1)))); @@ -1533,26 +2467,30 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( Signature(ArgTypes{ConstCharPtrTy, IntTy, ConstCharPtrTy}, RetType{IntTy}), Summary(NoEvalCall) - .Case(ReturnsZeroOrMinusOne) + .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) .ArgConstraint(NotNull(ArgNo(0))) - .ArgConstraint(ArgumentCondition(1, WithinRange, Range(0, IntMax))) + .ArgConstraint(ValidFileDescriptorOrAtFdcwd(ArgNo(1))) .ArgConstraint(NotNull(ArgNo(2)))); // int lockf(int fd, int cmd, off_t len); addToFunctionSummaryMap( "lockf", Signature(ArgTypes{IntTy, IntTy, Off_tTy}, RetType{IntTy}), Summary(NoEvalCall) - .Case(ReturnsZeroOrMinusOne) + .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) .ArgConstraint( ArgumentCondition(0, WithinRange, Range(0, IntMax)))); - Optional<QualType> Mode_tTy = lookupTy("mode_t"); + std::optional<QualType> Mode_tTy = lookupTy("mode_t"); // int creat(const char *pathname, mode_t mode); addToFunctionSummaryMap( "creat", Signature(ArgTypes{ConstCharPtrTy, Mode_tTy}, RetType{IntTy}), Summary(NoEvalCall) - .Case(ReturnsFileDescriptor) + .Case(ReturnsValidFileDescriptor, ErrnoMustNotBeChecked, + GenericSuccessMsg) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) .ArgConstraint(NotNull(ArgNo(0)))); // unsigned int sleep(unsigned int seconds); @@ -1562,15 +2500,17 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( .ArgConstraint( ArgumentCondition(0, WithinRange, Range(0, UnsignedIntMax)))); - Optional<QualType> DirTy = lookupTy("DIR"); - Optional<QualType> DirPtrTy = getPointerTy(DirTy); + std::optional<QualType> DirTy = lookupTy("DIR"); + std::optional<QualType> DirPtrTy = getPointerTy(DirTy); // int dirfd(DIR *dirp); - addToFunctionSummaryMap("dirfd", - Signature(ArgTypes{DirPtrTy}, RetType{IntTy}), - Summary(NoEvalCall) - .Case(ReturnsFileDescriptor) - .ArgConstraint(NotNull(ArgNo(0)))); + addToFunctionSummaryMap( + "dirfd", Signature(ArgTypes{DirPtrTy}, RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsValidFileDescriptor, ErrnoMustNotBeChecked, + GenericSuccessMsg) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) + .ArgConstraint(NotNull(ArgNo(0)))); // unsigned int alarm(unsigned int seconds); addToFunctionSummaryMap( @@ -1580,11 +2520,12 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( ArgumentCondition(0, WithinRange, Range(0, UnsignedIntMax)))); // int closedir(DIR *dir); - addToFunctionSummaryMap("closedir", - Signature(ArgTypes{DirPtrTy}, RetType{IntTy}), - Summary(NoEvalCall) - .Case(ReturnsZeroOrMinusOne) - .ArgConstraint(NotNull(ArgNo(0)))); + addToFunctionSummaryMap( + "closedir", Signature(ArgTypes{DirPtrTy}, RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) + .ArgConstraint(NotNull(ArgNo(0)))); // char *strdup(const char *s); addToFunctionSummaryMap( @@ -1606,21 +2547,39 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); // int mkstemp(char *template); - addToFunctionSummaryMap("mkstemp", - Signature(ArgTypes{CharPtrTy}, RetType{IntTy}), - Summary(NoEvalCall) - .Case(ReturnsFileDescriptor) - .ArgConstraint(NotNull(ArgNo(0)))); + addToFunctionSummaryMap( + "mkstemp", Signature(ArgTypes{CharPtrTy}, RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsValidFileDescriptor, ErrnoMustNotBeChecked, + GenericSuccessMsg) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) + .ArgConstraint(NotNull(ArgNo(0)))); // char *mkdtemp(char *template); addToFunctionSummaryMap( "mkdtemp", Signature(ArgTypes{CharPtrTy}, RetType{CharPtrTy}), - Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); + Summary(NoEvalCall) + .Case({ReturnValueCondition(BO_EQ, ArgNo(0))}, + ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case({IsNull(Ret)}, ErrnoNEZeroIrrelevant, GenericFailureMsg) + .ArgConstraint(NotNull(ArgNo(0)))); // char *getcwd(char *buf, size_t size); addToFunctionSummaryMap( "getcwd", Signature(ArgTypes{CharPtrTy, SizeTy}, RetType{CharPtrTy}), Summary(NoEvalCall) + .Case({ArgumentCondition(1, WithinRange, Range(1, SizeMax)), + ReturnValueCondition(BO_EQ, ArgNo(0))}, + ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case({ArgumentCondition(1, WithinRange, SingleValue(0)), + IsNull(Ret)}, + ErrnoNEZeroIrrelevant, "Assuming that argument 'size' is 0") + .Case({ArgumentCondition(1, WithinRange, Range(1, SizeMax)), + IsNull(Ret)}, + ErrnoNEZeroIrrelevant, GenericFailureMsg) + .ArgConstraint(NotNull(ArgNo(0))) + .ArgConstraint( + BufferSize(/*Buffer*/ ArgNo(0), /*BufSize*/ ArgNo(1))) .ArgConstraint( ArgumentCondition(1, WithinRange, Range(0, SizeMax)))); @@ -1628,7 +2587,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( addToFunctionSummaryMap( "mkdir", Signature(ArgTypes{ConstCharPtrTy, Mode_tTy}, RetType{IntTy}), Summary(NoEvalCall) - .Case(ReturnsZeroOrMinusOne) + .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) .ArgConstraint(NotNull(ArgNo(0)))); // int mkdirat(int dirfd, const char *pathname, mode_t mode); @@ -1636,17 +2596,20 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( "mkdirat", Signature(ArgTypes{IntTy, ConstCharPtrTy, Mode_tTy}, RetType{IntTy}), Summary(NoEvalCall) - .Case(ReturnsZeroOrMinusOne) + .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) + .ArgConstraint(ValidFileDescriptorOrAtFdcwd(ArgNo(0))) .ArgConstraint(NotNull(ArgNo(1)))); - Optional<QualType> Dev_tTy = lookupTy("dev_t"); + std::optional<QualType> Dev_tTy = lookupTy("dev_t"); // int mknod(const char *pathname, mode_t mode, dev_t dev); addToFunctionSummaryMap( "mknod", Signature(ArgTypes{ConstCharPtrTy, Mode_tTy, Dev_tTy}, RetType{IntTy}), Summary(NoEvalCall) - .Case(ReturnsZeroOrMinusOne) + .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) .ArgConstraint(NotNull(ArgNo(0)))); // int mknodat(int dirfd, const char *pathname, mode_t mode, dev_t dev); @@ -1655,14 +2618,17 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( Signature(ArgTypes{IntTy, ConstCharPtrTy, Mode_tTy, Dev_tTy}, RetType{IntTy}), Summary(NoEvalCall) - .Case(ReturnsZeroOrMinusOne) + .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) + .ArgConstraint(ValidFileDescriptorOrAtFdcwd(ArgNo(0))) .ArgConstraint(NotNull(ArgNo(1)))); // int chmod(const char *path, mode_t mode); addToFunctionSummaryMap( "chmod", Signature(ArgTypes{ConstCharPtrTy, Mode_tTy}, RetType{IntTy}), Summary(NoEvalCall) - .Case(ReturnsZeroOrMinusOne) + .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) .ArgConstraint(NotNull(ArgNo(0)))); // int fchmodat(int dirfd, const char *pathname, mode_t mode, int flags); @@ -1671,20 +2637,22 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( Signature(ArgTypes{IntTy, ConstCharPtrTy, Mode_tTy, IntTy}, RetType{IntTy}), Summary(NoEvalCall) - .Case(ReturnsZeroOrMinusOne) - .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax))) + .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) + .ArgConstraint(ValidFileDescriptorOrAtFdcwd(ArgNo(0))) .ArgConstraint(NotNull(ArgNo(1)))); // int fchmod(int fildes, mode_t mode); addToFunctionSummaryMap( "fchmod", Signature(ArgTypes{IntTy, Mode_tTy}, RetType{IntTy}), Summary(NoEvalCall) - .Case(ReturnsZeroOrMinusOne) + .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) .ArgConstraint( ArgumentCondition(0, WithinRange, Range(0, IntMax)))); - Optional<QualType> Uid_tTy = lookupTy("uid_t"); - Optional<QualType> Gid_tTy = lookupTy("gid_t"); + std::optional<QualType> Uid_tTy = lookupTy("uid_t"); + std::optional<QualType> Gid_tTy = lookupTy("gid_t"); // int fchownat(int dirfd, const char *pathname, uid_t owner, gid_t group, // int flags); @@ -1693,8 +2661,9 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( Signature(ArgTypes{IntTy, ConstCharPtrTy, Uid_tTy, Gid_tTy, IntTy}, RetType{IntTy}), Summary(NoEvalCall) - .Case(ReturnsZeroOrMinusOne) - .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax))) + .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) + .ArgConstraint(ValidFileDescriptorOrAtFdcwd(ArgNo(0))) .ArgConstraint(NotNull(ArgNo(1)))); // int chown(const char *path, uid_t owner, gid_t group); @@ -1702,7 +2671,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( "chown", Signature(ArgTypes{ConstCharPtrTy, Uid_tTy, Gid_tTy}, RetType{IntTy}), Summary(NoEvalCall) - .Case(ReturnsZeroOrMinusOne) + .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) .ArgConstraint(NotNull(ArgNo(0)))); // int lchown(const char *path, uid_t owner, gid_t group); @@ -1710,37 +2680,42 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( "lchown", Signature(ArgTypes{ConstCharPtrTy, Uid_tTy, Gid_tTy}, RetType{IntTy}), Summary(NoEvalCall) - .Case(ReturnsZeroOrMinusOne) + .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) .ArgConstraint(NotNull(ArgNo(0)))); // int fchown(int fildes, uid_t owner, gid_t group); addToFunctionSummaryMap( "fchown", Signature(ArgTypes{IntTy, Uid_tTy, Gid_tTy}, RetType{IntTy}), Summary(NoEvalCall) - .Case(ReturnsZeroOrMinusOne) + .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) .ArgConstraint( ArgumentCondition(0, WithinRange, Range(0, IntMax)))); // int rmdir(const char *pathname); - addToFunctionSummaryMap("rmdir", - Signature(ArgTypes{ConstCharPtrTy}, RetType{IntTy}), - Summary(NoEvalCall) - .Case(ReturnsZeroOrMinusOne) - .ArgConstraint(NotNull(ArgNo(0)))); + addToFunctionSummaryMap( + "rmdir", Signature(ArgTypes{ConstCharPtrTy}, RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) + .ArgConstraint(NotNull(ArgNo(0)))); // int chdir(const char *path); - addToFunctionSummaryMap("chdir", - Signature(ArgTypes{ConstCharPtrTy}, RetType{IntTy}), - Summary(NoEvalCall) - .Case(ReturnsZeroOrMinusOne) - .ArgConstraint(NotNull(ArgNo(0)))); + addToFunctionSummaryMap( + "chdir", Signature(ArgTypes{ConstCharPtrTy}, RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) + .ArgConstraint(NotNull(ArgNo(0)))); // int link(const char *oldpath, const char *newpath); addToFunctionSummaryMap( "link", Signature(ArgTypes{ConstCharPtrTy, ConstCharPtrTy}, RetType{IntTy}), Summary(NoEvalCall) - .Case(ReturnsZeroOrMinusOne) + .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) .ArgConstraint(NotNull(ArgNo(0))) .ArgConstraint(NotNull(ArgNo(1)))); @@ -1751,37 +2726,42 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( Signature(ArgTypes{IntTy, ConstCharPtrTy, IntTy, ConstCharPtrTy, IntTy}, RetType{IntTy}), Summary(NoEvalCall) - .Case(ReturnsZeroOrMinusOne) - .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax))) + .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) + .ArgConstraint(ValidFileDescriptorOrAtFdcwd(ArgNo(0))) .ArgConstraint(NotNull(ArgNo(1))) - .ArgConstraint(ArgumentCondition(2, WithinRange, Range(0, IntMax))) + .ArgConstraint(ValidFileDescriptorOrAtFdcwd(ArgNo(2))) .ArgConstraint(NotNull(ArgNo(3)))); // int unlink(const char *pathname); - addToFunctionSummaryMap("unlink", - Signature(ArgTypes{ConstCharPtrTy}, RetType{IntTy}), - Summary(NoEvalCall) - .Case(ReturnsZeroOrMinusOne) - .ArgConstraint(NotNull(ArgNo(0)))); + addToFunctionSummaryMap( + "unlink", Signature(ArgTypes{ConstCharPtrTy}, RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) + .ArgConstraint(NotNull(ArgNo(0)))); // int unlinkat(int fd, const char *path, int flag); addToFunctionSummaryMap( "unlinkat", Signature(ArgTypes{IntTy, ConstCharPtrTy, IntTy}, RetType{IntTy}), Summary(NoEvalCall) - .Case(ReturnsZeroOrMinusOne) - .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax))) + .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) + .ArgConstraint(ValidFileDescriptorOrAtFdcwd(ArgNo(0))) .ArgConstraint(NotNull(ArgNo(1)))); - Optional<QualType> StructStatTy = lookupTy("stat"); - Optional<QualType> StructStatPtrTy = getPointerTy(StructStatTy); - Optional<QualType> StructStatPtrRestrictTy = getRestrictTy(StructStatPtrTy); + std::optional<QualType> StructStatTy = lookupTy("stat"); + std::optional<QualType> StructStatPtrTy = getPointerTy(StructStatTy); + std::optional<QualType> StructStatPtrRestrictTy = + getRestrictTy(StructStatPtrTy); // int fstat(int fd, struct stat *statbuf); addToFunctionSummaryMap( "fstat", Signature(ArgTypes{IntTy, StructStatPtrTy}, RetType{IntTy}), Summary(NoEvalCall) - .Case(ReturnsZeroOrMinusOne) + .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax))) .ArgConstraint(NotNull(ArgNo(1)))); @@ -1791,7 +2771,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( Signature(ArgTypes{ConstCharPtrRestrictTy, StructStatPtrRestrictTy}, RetType{IntTy}), Summary(NoEvalCall) - .Case(ReturnsZeroOrMinusOne) + .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) .ArgConstraint(NotNull(ArgNo(0))) .ArgConstraint(NotNull(ArgNo(1)))); @@ -1801,7 +2782,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( Signature(ArgTypes{ConstCharPtrRestrictTy, StructStatPtrRestrictTy}, RetType{IntTy}), Summary(NoEvalCall) - .Case(ReturnsZeroOrMinusOne) + .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) .ArgConstraint(NotNull(ArgNo(0))) .ArgConstraint(NotNull(ArgNo(1)))); @@ -1813,32 +2795,40 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( StructStatPtrRestrictTy, IntTy}, RetType{IntTy}), Summary(NoEvalCall) - .Case(ReturnsZeroOrMinusOne) - .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax))) + .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) + .ArgConstraint(ValidFileDescriptorOrAtFdcwd(ArgNo(0))) .ArgConstraint(NotNull(ArgNo(1))) .ArgConstraint(NotNull(ArgNo(2)))); // DIR *opendir(const char *name); addToFunctionSummaryMap( "opendir", Signature(ArgTypes{ConstCharPtrTy}, RetType{DirPtrTy}), - Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); + Summary(NoEvalCall) + .Case({NotNull(Ret)}, ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case({IsNull(Ret)}, ErrnoNEZeroIrrelevant, GenericFailureMsg) + .ArgConstraint(NotNull(ArgNo(0)))); // DIR *fdopendir(int fd); - addToFunctionSummaryMap("fdopendir", - Signature(ArgTypes{IntTy}, RetType{DirPtrTy}), - Summary(NoEvalCall) - .ArgConstraint(ArgumentCondition( - 0, WithinRange, Range(0, IntMax)))); + addToFunctionSummaryMap( + "fdopendir", Signature(ArgTypes{IntTy}, RetType{DirPtrTy}), + Summary(NoEvalCall) + .Case({NotNull(Ret)}, ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case({IsNull(Ret)}, ErrnoNEZeroIrrelevant, GenericFailureMsg) + .ArgConstraint( + ArgumentCondition(0, WithinRange, Range(0, IntMax)))); // int isatty(int fildes); addToFunctionSummaryMap( "isatty", Signature(ArgTypes{IntTy}, RetType{IntTy}), Summary(NoEvalCall) - .Case({ReturnValueCondition(WithinRange, Range(0, 1))}) + .Case({ReturnValueCondition(WithinRange, Range(0, 1))}, + ErrnoIrrelevant) .ArgConstraint( ArgumentCondition(0, WithinRange, Range(0, IntMax)))); // FILE *popen(const char *command, const char *type); + // FIXME: Improve for errno modeling. addToFunctionSummaryMap( "popen", Signature(ArgTypes{ConstCharPtrTy, ConstCharPtrTy}, RetType{FilePtrTy}), @@ -1847,16 +2837,19 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( .ArgConstraint(NotNull(ArgNo(1)))); // int pclose(FILE *stream); + // FIXME: Improve for errno modeling. addToFunctionSummaryMap( "pclose", Signature(ArgTypes{FilePtrTy}, RetType{IntTy}), Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); // int close(int fildes); - addToFunctionSummaryMap("close", Signature(ArgTypes{IntTy}, RetType{IntTy}), - Summary(NoEvalCall) - .Case(ReturnsZeroOrMinusOne) - .ArgConstraint(ArgumentCondition( - 0, WithinRange, Range(-1, IntMax)))); + addToFunctionSummaryMap( + "close", Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) + .ArgConstraint( + ArgumentCondition(0, WithinRange, Range(-1, IntMax)))); // long fpathconf(int fildes, int name); addToFunctionSummaryMap("fpathconf", @@ -1870,14 +2863,6 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( "pathconf", Signature(ArgTypes{ConstCharPtrTy, IntTy}, RetType{LongTy}), Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); - // FILE *fdopen(int fd, const char *mode); - addToFunctionSummaryMap( - "fdopen", - Signature(ArgTypes{IntTy, ConstCharPtrTy}, RetType{FilePtrTy}), - Summary(NoEvalCall) - .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax))) - .ArgConstraint(NotNull(ArgNo(1)))); - // void rewinddir(DIR *dir); addToFunctionSummaryMap( "rewinddir", Signature(ArgTypes{DirPtrTy}, RetType{VoidTy}), @@ -1893,28 +2878,9 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( "rand_r", Signature(ArgTypes{UnsignedIntPtrTy}, RetType{IntTy}), Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); - // int fileno(FILE *stream); - addToFunctionSummaryMap("fileno", - Signature(ArgTypes{FilePtrTy}, RetType{IntTy}), - Summary(NoEvalCall) - .Case(ReturnsFileDescriptor) - .ArgConstraint(NotNull(ArgNo(0)))); - - // int fseeko(FILE *stream, off_t offset, int whence); - addToFunctionSummaryMap( - "fseeko", - Signature(ArgTypes{FilePtrTy, Off_tTy, IntTy}, RetType{IntTy}), - Summary(NoEvalCall) - .Case(ReturnsZeroOrMinusOne) - .ArgConstraint(NotNull(ArgNo(0)))); - - // off_t ftello(FILE *stream); - addToFunctionSummaryMap( - "ftello", Signature(ArgTypes{FilePtrTy}, RetType{Off_tTy}), - Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); - // void *mmap(void *addr, size_t length, int prot, int flags, int fd, // off_t offset); + // FIXME: Improve for errno modeling. addToFunctionSummaryMap( "mmap", Signature(ArgTypes{VoidPtrTy, SizeTy, IntTy, IntTy, IntTy, Off_tTy}, @@ -1924,9 +2890,10 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( .ArgConstraint( ArgumentCondition(4, WithinRange, Range(-1, IntMax)))); - Optional<QualType> Off64_tTy = lookupTy("off64_t"); + std::optional<QualType> Off64_tTy = lookupTy("off64_t"); // void *mmap64(void *addr, size_t length, int prot, int flags, int fd, // off64_t offset); + // FIXME: Improve for errno modeling. addToFunctionSummaryMap( "mmap64", Signature(ArgTypes{VoidPtrTy, SizeTy, IntTy, IntTy, IntTy, Off64_tTy}, @@ -1937,16 +2904,23 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( ArgumentCondition(4, WithinRange, Range(-1, IntMax)))); // int pipe(int fildes[2]); - addToFunctionSummaryMap("pipe", - Signature(ArgTypes{IntPtrTy}, RetType{IntTy}), - Summary(NoEvalCall) - .Case(ReturnsZeroOrMinusOne) - .ArgConstraint(NotNull(ArgNo(0)))); + addToFunctionSummaryMap( + "pipe", Signature(ArgTypes{IntPtrTy}, RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) + .ArgConstraint(NotNull(ArgNo(0)))); // off_t lseek(int fildes, off_t offset, int whence); + // In the first case we can not tell for sure if it failed or not. + // A return value different from of the expected offset (that is unknown + // here) may indicate failure. For this reason we do not enforce the errno + // check (can cause false positive). addToFunctionSummaryMap( "lseek", Signature(ArgTypes{IntTy, Off_tTy, IntTy}, RetType{Off_tTy}), Summary(NoEvalCall) + .Case(ReturnsNonnegative, ErrnoIrrelevant) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) .ArgConstraint( ArgumentCondition(0, WithinRange, Range(0, IntMax)))); @@ -1957,8 +2931,15 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( Signature(ArgTypes{ConstCharPtrRestrictTy, CharPtrRestrictTy, SizeTy}, RetType{Ssize_tTy}), Summary(NoEvalCall) - .Case({ReturnValueCondition(LessThanOrEq, ArgNo(2)), - ReturnValueCondition(WithinRange, Range(-1, 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))) .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(1), @@ -1974,9 +2955,16 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( ArgTypes{IntTy, ConstCharPtrRestrictTy, CharPtrRestrictTy, SizeTy}, RetType{Ssize_tTy}), Summary(NoEvalCall) - .Case({ReturnValueCondition(LessThanOrEq, ArgNo(3)), - ReturnValueCondition(WithinRange, Range(-1, Ssize_tMax))}) - .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax))) + .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))) .ArgConstraint(NotNull(ArgNo(2))) .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(2), @@ -1991,12 +2979,16 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( Signature(ArgTypes{IntTy, ConstCharPtrTy, IntTy, ConstCharPtrTy}, RetType{IntTy}), Summary(NoEvalCall) - .Case(ReturnsZeroOrMinusOne) + .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) + .ArgConstraint(ValidFileDescriptorOrAtFdcwd(ArgNo(0))) .ArgConstraint(NotNull(ArgNo(1))) + .ArgConstraint(ValidFileDescriptorOrAtFdcwd(ArgNo(2))) .ArgConstraint(NotNull(ArgNo(3)))); // char *realpath(const char *restrict file_name, // char *restrict resolved_name); + // FIXME: Improve for errno modeling. addToFunctionSummaryMap( "realpath", Signature(ArgTypes{ConstCharPtrRestrictTy, CharPtrRestrictTy}, @@ -2010,7 +3002,7 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( "execv", Signature(ArgTypes{ConstCharPtrTy, CharPtrConstPtr}, RetType{IntTy}), Summary(NoEvalCall) - .Case({ReturnValueCondition(WithinRange, SingleValue(-1))}) + .Case(ReturnsMinusOne, ErrnoIrrelevant) .ArgConstraint(NotNull(ArgNo(0)))); // int execvp(const char *file, char *const argv[]); @@ -2018,7 +3010,7 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( "execvp", Signature(ArgTypes{ConstCharPtrTy, CharPtrConstPtr}, RetType{IntTy}), Summary(NoEvalCall) - .Case({ReturnValueCondition(WithinRange, SingleValue(-1))}) + .Case(ReturnsMinusOne, ErrnoIrrelevant) .ArgConstraint(NotNull(ArgNo(0)))); // int getopt(int argc, char * const argv[], const char *optstring); @@ -2027,23 +3019,26 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( Signature(ArgTypes{IntTy, CharPtrConstPtr, ConstCharPtrTy}, RetType{IntTy}), Summary(NoEvalCall) - .Case({ReturnValueCondition(WithinRange, Range(-1, UCharRangeMax))}) + .Case({ReturnValueCondition(WithinRange, Range(-1, UCharRangeMax))}, + ErrnoIrrelevant) .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax))) .ArgConstraint(NotNull(ArgNo(1))) .ArgConstraint(NotNull(ArgNo(2)))); - Optional<QualType> StructSockaddrTy = lookupTy("sockaddr"); - Optional<QualType> StructSockaddrPtrTy = getPointerTy(StructSockaddrTy); - Optional<QualType> ConstStructSockaddrPtrTy = + std::optional<QualType> StructSockaddrTy = lookupTy("sockaddr"); + std::optional<QualType> StructSockaddrPtrTy = + getPointerTy(StructSockaddrTy); + std::optional<QualType> ConstStructSockaddrPtrTy = getPointerTy(getConstTy(StructSockaddrTy)); - Optional<QualType> StructSockaddrPtrRestrictTy = + std::optional<QualType> StructSockaddrPtrRestrictTy = getRestrictTy(StructSockaddrPtrTy); - Optional<QualType> ConstStructSockaddrPtrRestrictTy = + std::optional<QualType> ConstStructSockaddrPtrRestrictTy = getRestrictTy(ConstStructSockaddrPtrTy); - Optional<QualType> Socklen_tTy = lookupTy("socklen_t"); - Optional<QualType> Socklen_tPtrTy = getPointerTy(Socklen_tTy); - Optional<QualType> Socklen_tPtrRestrictTy = getRestrictTy(Socklen_tPtrTy); - Optional<RangeInt> Socklen_tMax = getMaxValue(Socklen_tTy); + std::optional<QualType> Socklen_tTy = lookupTy("socklen_t"); + std::optional<QualType> Socklen_tPtrTy = getPointerTy(Socklen_tTy); + std::optional<QualType> Socklen_tPtrRestrictTy = + getRestrictTy(Socklen_tPtrTy); + std::optional<RangeInt> Socklen_tMax = getMaxValue(Socklen_tTy); // In 'socket.h' of some libc implementations with C99, sockaddr parameter // is a transparent union of the underlying sockaddr_ family of pointers @@ -2051,9 +3046,20 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( // standardized signature will not match, thus we try to match with another // signature that has the joker Irrelevant type. We also remove those // constraints which require pointer types for the sockaddr param. + + // int socket(int domain, int type, int protocol); + addToFunctionSummaryMap( + "socket", Signature(ArgTypes{IntTy, IntTy, IntTy}, RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsValidFileDescriptor, ErrnoMustNotBeChecked, + GenericSuccessMsg) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)); + auto Accept = Summary(NoEvalCall) - .Case(ReturnsFileDescriptor) + .Case(ReturnsValidFileDescriptor, ErrnoMustNotBeChecked, + GenericSuccessMsg) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax))); if (!addToFunctionSummaryMap( "accept", @@ -2076,7 +3082,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( Signature(ArgTypes{IntTy, ConstStructSockaddrPtrTy, Socklen_tTy}, RetType{IntTy}), Summary(NoEvalCall) - .Case(ReturnsZeroOrMinusOne) + .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) .ArgConstraint( ArgumentCondition(0, WithinRange, Range(0, IntMax))) .ArgConstraint(NotNull(ArgNo(1))) @@ -2089,7 +3096,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( "bind", Signature(ArgTypes{IntTy, Irrelevant, Socklen_tTy}, RetType{IntTy}), Summary(NoEvalCall) - .Case(ReturnsZeroOrMinusOne) + .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) .ArgConstraint( ArgumentCondition(0, WithinRange, Range(0, IntMax))) .ArgConstraint( @@ -2103,7 +3111,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( Socklen_tPtrRestrictTy}, RetType{IntTy}), Summary(NoEvalCall) - .Case(ReturnsZeroOrMinusOne) + .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) .ArgConstraint( ArgumentCondition(0, WithinRange, Range(0, IntMax))) .ArgConstraint(NotNull(ArgNo(1))) @@ -2113,7 +3122,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( Signature(ArgTypes{IntTy, Irrelevant, Socklen_tPtrRestrictTy}, RetType{IntTy}), Summary(NoEvalCall) - .Case(ReturnsZeroOrMinusOne) + .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) .ArgConstraint( ArgumentCondition(0, WithinRange, Range(0, IntMax)))); @@ -2125,7 +3135,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( Socklen_tPtrRestrictTy}, RetType{IntTy}), Summary(NoEvalCall) - .Case(ReturnsZeroOrMinusOne) + .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) .ArgConstraint( ArgumentCondition(0, WithinRange, Range(0, IntMax))) .ArgConstraint(NotNull(ArgNo(1))) @@ -2135,7 +3146,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( Signature(ArgTypes{IntTy, Irrelevant, Socklen_tPtrRestrictTy}, RetType{IntTy}), Summary(NoEvalCall) - .Case(ReturnsZeroOrMinusOne) + .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) .ArgConstraint( ArgumentCondition(0, WithinRange, Range(0, IntMax)))); @@ -2146,7 +3158,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( Signature(ArgTypes{IntTy, ConstStructSockaddrPtrTy, Socklen_tTy}, RetType{IntTy}), Summary(NoEvalCall) - .Case(ReturnsZeroOrMinusOne) + .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) .ArgConstraint( ArgumentCondition(0, WithinRange, Range(0, IntMax))) .ArgConstraint(NotNull(ArgNo(1))))) @@ -2154,14 +3167,20 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( "connect", Signature(ArgTypes{IntTy, Irrelevant, Socklen_tTy}, RetType{IntTy}), Summary(NoEvalCall) - .Case(ReturnsZeroOrMinusOne) + .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) .ArgConstraint( ArgumentCondition(0, WithinRange, Range(0, IntMax)))); auto Recvfrom = Summary(NoEvalCall) .Case({ReturnValueCondition(LessThanOrEq, ArgNo(2)), - ReturnValueCondition(WithinRange, Range(-1, 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))) .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(1), /*BufSize=*/ArgNo(2))); @@ -2186,7 +3205,12 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( auto Sendto = Summary(NoEvalCall) .Case({ReturnValueCondition(LessThanOrEq, ArgNo(2)), - ReturnValueCondition(WithinRange, Range(-1, 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))) .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(1), /*BufSize=*/ArgNo(2))); @@ -2207,12 +3231,13 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( Sendto); // int listen(int sockfd, int backlog); - addToFunctionSummaryMap("listen", - Signature(ArgTypes{IntTy, IntTy}, RetType{IntTy}), - Summary(NoEvalCall) - .Case(ReturnsZeroOrMinusOne) - .ArgConstraint(ArgumentCondition( - 0, WithinRange, Range(0, IntMax)))); + addToFunctionSummaryMap( + "listen", Signature(ArgTypes{IntTy, IntTy}, RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) + .ArgConstraint( + ArgumentCondition(0, WithinRange, Range(0, IntMax)))); // ssize_t recv(int sockfd, void *buf, size_t len, int flags); addToFunctionSummaryMap( @@ -2221,14 +3246,19 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( RetType{Ssize_tTy}), Summary(NoEvalCall) .Case({ReturnValueCondition(LessThanOrEq, ArgNo(2)), - ReturnValueCondition(WithinRange, Range(-1, 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))) .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(1), /*BufSize=*/ArgNo(2)))); - Optional<QualType> StructMsghdrTy = lookupTy("msghdr"); - Optional<QualType> StructMsghdrPtrTy = getPointerTy(StructMsghdrTy); - Optional<QualType> ConstStructMsghdrPtrTy = + std::optional<QualType> StructMsghdrTy = lookupTy("msghdr"); + std::optional<QualType> StructMsghdrPtrTy = getPointerTy(StructMsghdrTy); + std::optional<QualType> ConstStructMsghdrPtrTy = getPointerTy(getConstTy(StructMsghdrTy)); // ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags); @@ -2237,7 +3267,9 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( Signature(ArgTypes{IntTy, StructMsghdrPtrTy, IntTy}, RetType{Ssize_tTy}), Summary(NoEvalCall) - .Case({ReturnValueCondition(WithinRange, Range(-1, Ssize_tMax))}) + .Case({ReturnValueCondition(WithinRange, Range(1, Ssize_tMax))}, + ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) .ArgConstraint( ArgumentCondition(0, WithinRange, Range(0, IntMax)))); @@ -2247,7 +3279,9 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( Signature(ArgTypes{IntTy, ConstStructMsghdrPtrTy, IntTy}, RetType{Ssize_tTy}), Summary(NoEvalCall) - .Case({ReturnValueCondition(WithinRange, Range(-1, Ssize_tMax))}) + .Case({ReturnValueCondition(WithinRange, Range(1, Ssize_tMax))}, + ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) .ArgConstraint( ArgumentCondition(0, WithinRange, Range(0, IntMax)))); @@ -2258,7 +3292,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( Signature(ArgTypes{IntTy, IntTy, IntTy, ConstVoidPtrTy, Socklen_tTy}, RetType{IntTy}), Summary(NoEvalCall) - .Case(ReturnsZeroOrMinusOne) + .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) .ArgConstraint(NotNull(ArgNo(3))) .ArgConstraint( BufferSize(/*Buffer=*/ArgNo(3), /*BufSize=*/ArgNo(4))) @@ -2274,7 +3309,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( Socklen_tPtrRestrictTy}, RetType{IntTy}), Summary(NoEvalCall) - .Case(ReturnsZeroOrMinusOne) + .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) .ArgConstraint(NotNull(ArgNo(3))) .ArgConstraint(NotNull(ArgNo(4)))); @@ -2285,7 +3321,12 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( RetType{Ssize_tTy}), Summary(NoEvalCall) .Case({ReturnValueCondition(LessThanOrEq, ArgNo(2)), - ReturnValueCondition(WithinRange, Range(-1, 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))) .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(1), /*BufSize=*/ArgNo(2)))); @@ -2295,9 +3336,19 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( "socketpair", Signature(ArgTypes{IntTy, IntTy, IntTy, IntPtrTy}, RetType{IntTy}), Summary(NoEvalCall) - .Case(ReturnsZeroOrMinusOne) + .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) .ArgConstraint(NotNull(ArgNo(3)))); + // int shutdown(int socket, int how); + addToFunctionSummaryMap( + "shutdown", Signature(ArgTypes{IntTy, IntTy}, RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) + .ArgConstraint( + ArgumentCondition(0, WithinRange, Range(0, IntMax)))); + // int getnameinfo(const struct sockaddr *restrict sa, socklen_t salen, // char *restrict node, socklen_t nodelen, // char *restrict service, @@ -2325,20 +3376,22 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( .ArgConstraint( ArgumentCondition(5, WithinRange, Range(0, Socklen_tMax)))); - Optional<QualType> StructUtimbufTy = lookupTy("utimbuf"); - Optional<QualType> StructUtimbufPtrTy = getPointerTy(StructUtimbufTy); + std::optional<QualType> StructUtimbufTy = lookupTy("utimbuf"); + std::optional<QualType> StructUtimbufPtrTy = getPointerTy(StructUtimbufTy); // int utime(const char *filename, struct utimbuf *buf); addToFunctionSummaryMap( "utime", Signature(ArgTypes{ConstCharPtrTy, StructUtimbufPtrTy}, RetType{IntTy}), Summary(NoEvalCall) - .Case(ReturnsZeroOrMinusOne) + .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) .ArgConstraint(NotNull(ArgNo(0)))); - Optional<QualType> StructTimespecTy = lookupTy("timespec"); - Optional<QualType> StructTimespecPtrTy = getPointerTy(StructTimespecTy); - Optional<QualType> ConstStructTimespecPtrTy = + std::optional<QualType> StructTimespecTy = lookupTy("timespec"); + std::optional<QualType> StructTimespecPtrTy = + getPointerTy(StructTimespecTy); + std::optional<QualType> ConstStructTimespecPtrTy = getPointerTy(getConstTy(StructTimespecTy)); // int futimens(int fd, const struct timespec times[2]); @@ -2346,22 +3399,25 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( "futimens", Signature(ArgTypes{IntTy, ConstStructTimespecPtrTy}, RetType{IntTy}), Summary(NoEvalCall) - .Case(ReturnsZeroOrMinusOne) + .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) .ArgConstraint( ArgumentCondition(0, WithinRange, Range(0, IntMax)))); // int utimensat(int dirfd, const char *pathname, // const struct timespec times[2], int flags); - addToFunctionSummaryMap("utimensat", - Signature(ArgTypes{IntTy, ConstCharPtrTy, - ConstStructTimespecPtrTy, IntTy}, - RetType{IntTy}), - Summary(NoEvalCall) - .Case(ReturnsZeroOrMinusOne) - .ArgConstraint(NotNull(ArgNo(1)))); + addToFunctionSummaryMap( + "utimensat", + Signature( + ArgTypes{IntTy, ConstCharPtrTy, ConstStructTimespecPtrTy, IntTy}, + RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) + .ArgConstraint(NotNull(ArgNo(1)))); - Optional<QualType> StructTimevalTy = lookupTy("timeval"); - Optional<QualType> ConstStructTimevalPtrTy = + std::optional<QualType> StructTimevalTy = lookupTy("timeval"); + std::optional<QualType> ConstStructTimevalPtrTy = getPointerTy(getConstTy(StructTimevalTy)); // int utimes(const char *filename, const struct timeval times[2]); @@ -2370,7 +3426,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( Signature(ArgTypes{ConstCharPtrTy, ConstStructTimevalPtrTy}, RetType{IntTy}), Summary(NoEvalCall) - .Case(ReturnsZeroOrMinusOne) + .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) .ArgConstraint(NotNull(ArgNo(0)))); // int nanosleep(const struct timespec *rqtp, struct timespec *rmtp); @@ -2379,20 +3436,23 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( Signature(ArgTypes{ConstStructTimespecPtrTy, StructTimespecPtrTy}, RetType{IntTy}), Summary(NoEvalCall) - .Case(ReturnsZeroOrMinusOne) + .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) .ArgConstraint(NotNull(ArgNo(0)))); - Optional<QualType> Time_tTy = lookupTy("time_t"); - Optional<QualType> ConstTime_tPtrTy = getPointerTy(getConstTy(Time_tTy)); - Optional<QualType> ConstTime_tPtrRestrictTy = + std::optional<QualType> Time_tTy = lookupTy("time_t"); + std::optional<QualType> ConstTime_tPtrTy = + getPointerTy(getConstTy(Time_tTy)); + std::optional<QualType> ConstTime_tPtrRestrictTy = getRestrictTy(ConstTime_tPtrTy); - Optional<QualType> StructTmTy = lookupTy("tm"); - Optional<QualType> StructTmPtrTy = getPointerTy(StructTmTy); - Optional<QualType> StructTmPtrRestrictTy = getRestrictTy(StructTmPtrTy); - Optional<QualType> ConstStructTmPtrTy = + std::optional<QualType> StructTmTy = lookupTy("tm"); + std::optional<QualType> StructTmPtrTy = getPointerTy(StructTmTy); + std::optional<QualType> StructTmPtrRestrictTy = + getRestrictTy(StructTmPtrTy); + std::optional<QualType> ConstStructTmPtrTy = getPointerTy(getConstTy(StructTmTy)); - Optional<QualType> ConstStructTmPtrRestrictTy = + std::optional<QualType> ConstStructTmPtrRestrictTy = getRestrictTy(ConstStructTmPtrTy); // struct tm * localtime(const time_t *tp); @@ -2448,46 +3508,54 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( "gmtime", Signature(ArgTypes{ConstTime_tPtrTy}, RetType{StructTmPtrTy}), Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); - Optional<QualType> Clockid_tTy = lookupTy("clockid_t"); + std::optional<QualType> Clockid_tTy = lookupTy("clockid_t"); // int clock_gettime(clockid_t clock_id, struct timespec *tp); addToFunctionSummaryMap( "clock_gettime", Signature(ArgTypes{Clockid_tTy, StructTimespecPtrTy}, RetType{IntTy}), Summary(NoEvalCall) - .Case(ReturnsZeroOrMinusOne) + .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) .ArgConstraint(NotNull(ArgNo(1)))); - Optional<QualType> StructItimervalTy = lookupTy("itimerval"); - Optional<QualType> StructItimervalPtrTy = getPointerTy(StructItimervalTy); + std::optional<QualType> StructItimervalTy = lookupTy("itimerval"); + std::optional<QualType> StructItimervalPtrTy = + getPointerTy(StructItimervalTy); // int getitimer(int which, struct itimerval *curr_value); addToFunctionSummaryMap( "getitimer", Signature(ArgTypes{IntTy, StructItimervalPtrTy}, RetType{IntTy}), Summary(NoEvalCall) - .Case(ReturnsZeroOrMinusOne) + .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) + .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) .ArgConstraint(NotNull(ArgNo(1)))); - Optional<QualType> Pthread_cond_tTy = lookupTy("pthread_cond_t"); - Optional<QualType> Pthread_cond_tPtrTy = getPointerTy(Pthread_cond_tTy); - Optional<QualType> Pthread_tTy = lookupTy("pthread_t"); - Optional<QualType> Pthread_tPtrTy = getPointerTy(Pthread_tTy); - Optional<QualType> Pthread_tPtrRestrictTy = getRestrictTy(Pthread_tPtrTy); - Optional<QualType> Pthread_mutex_tTy = lookupTy("pthread_mutex_t"); - Optional<QualType> Pthread_mutex_tPtrTy = getPointerTy(Pthread_mutex_tTy); - Optional<QualType> Pthread_mutex_tPtrRestrictTy = + std::optional<QualType> Pthread_cond_tTy = lookupTy("pthread_cond_t"); + std::optional<QualType> Pthread_cond_tPtrTy = + getPointerTy(Pthread_cond_tTy); + std::optional<QualType> Pthread_tTy = lookupTy("pthread_t"); + std::optional<QualType> Pthread_tPtrTy = getPointerTy(Pthread_tTy); + std::optional<QualType> Pthread_tPtrRestrictTy = + getRestrictTy(Pthread_tPtrTy); + std::optional<QualType> Pthread_mutex_tTy = lookupTy("pthread_mutex_t"); + std::optional<QualType> Pthread_mutex_tPtrTy = + getPointerTy(Pthread_mutex_tTy); + std::optional<QualType> Pthread_mutex_tPtrRestrictTy = getRestrictTy(Pthread_mutex_tPtrTy); - Optional<QualType> Pthread_attr_tTy = lookupTy("pthread_attr_t"); - Optional<QualType> Pthread_attr_tPtrTy = getPointerTy(Pthread_attr_tTy); - Optional<QualType> ConstPthread_attr_tPtrTy = + std::optional<QualType> Pthread_attr_tTy = lookupTy("pthread_attr_t"); + std::optional<QualType> Pthread_attr_tPtrTy = + getPointerTy(Pthread_attr_tTy); + std::optional<QualType> ConstPthread_attr_tPtrTy = getPointerTy(getConstTy(Pthread_attr_tTy)); - Optional<QualType> ConstPthread_attr_tPtrRestrictTy = + std::optional<QualType> ConstPthread_attr_tPtrRestrictTy = getRestrictTy(ConstPthread_attr_tPtrTy); - Optional<QualType> Pthread_mutexattr_tTy = lookupTy("pthread_mutexattr_t"); - Optional<QualType> ConstPthread_mutexattr_tPtrTy = + std::optional<QualType> Pthread_mutexattr_tTy = + lookupTy("pthread_mutexattr_t"); + std::optional<QualType> ConstPthread_mutexattr_tPtrTy = getPointerTy(getConstTy(Pthread_mutexattr_tTy)); - Optional<QualType> ConstPthread_mutexattr_tPtrRestrictTy = + std::optional<QualType> ConstPthread_mutexattr_tPtrRestrictTy = getRestrictTy(ConstPthread_mutexattr_tPtrTy); QualType PthreadStartRoutineTy = getPointerTy( @@ -2564,12 +3632,24 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( } // Functions for testing. - if (ChecksEnabled[CK_StdCLibraryFunctionsTesterChecker]) { + if (AddTestFunctions) { + const RangeInt IntMin = BVF.getMinValue(IntTy).getLimitedValue(); + addToFunctionSummaryMap( "__not_null", Signature(ArgTypes{IntPtrTy}, RetType{IntTy}), Summary(EvalCallAsPure).ArgConstraint(NotNull(ArgNo(0)))); - // Test range values. + addToFunctionSummaryMap( + "__not_null_buffer", + Signature(ArgTypes{VoidPtrTy, IntTy, IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) + .ArgConstraint(NotNullBuffer(ArgNo(0), ArgNo(1), ArgNo(2)))); + + // Test inside range constraints. + addToFunctionSummaryMap( + "__single_val_0", Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) + .ArgConstraint(ArgumentCondition(0U, WithinRange, SingleValue(0)))); addToFunctionSummaryMap( "__single_val_1", Signature(ArgTypes{IntTy}, RetType{IntTy}), Summary(EvalCallAsPure) @@ -2578,11 +3658,124 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( "__range_1_2", Signature(ArgTypes{IntTy}, RetType{IntTy}), Summary(EvalCallAsPure) .ArgConstraint(ArgumentCondition(0U, WithinRange, Range(1, 2)))); - addToFunctionSummaryMap("__range_1_2__4_5", + addToFunctionSummaryMap( + "__range_m1_1", Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) + .ArgConstraint(ArgumentCondition(0U, WithinRange, Range(-1, 1)))); + addToFunctionSummaryMap( + "__range_m2_m1", Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) + .ArgConstraint(ArgumentCondition(0U, WithinRange, Range(-2, -1)))); + addToFunctionSummaryMap( + "__range_m10_10", Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) + .ArgConstraint(ArgumentCondition(0U, WithinRange, Range(-10, 10)))); + addToFunctionSummaryMap("__range_m1_inf", + Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) + .ArgConstraint(ArgumentCondition( + 0U, WithinRange, Range(-1, IntMax)))); + addToFunctionSummaryMap("__range_0_inf", + Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) + .ArgConstraint(ArgumentCondition( + 0U, WithinRange, Range(0, IntMax)))); + addToFunctionSummaryMap("__range_1_inf", + Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) + .ArgConstraint(ArgumentCondition( + 0U, WithinRange, Range(1, IntMax)))); + addToFunctionSummaryMap("__range_minf_m1", + Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) + .ArgConstraint(ArgumentCondition( + 0U, WithinRange, Range(IntMin, -1)))); + addToFunctionSummaryMap("__range_minf_0", + Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) + .ArgConstraint(ArgumentCondition( + 0U, WithinRange, Range(IntMin, 0)))); + addToFunctionSummaryMap("__range_minf_1", + Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) + .ArgConstraint(ArgumentCondition( + 0U, WithinRange, Range(IntMin, 1)))); + addToFunctionSummaryMap("__range_1_2__4_6", + Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) + .ArgConstraint(ArgumentCondition( + 0U, WithinRange, Range({1, 2}, {4, 6})))); + addToFunctionSummaryMap( + "__range_1_2__4_inf", Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) + .ArgConstraint(ArgumentCondition(0U, WithinRange, + Range({1, 2}, {4, IntMax})))); + + // Test out of range constraints. + addToFunctionSummaryMap( + "__single_val_out_0", Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) + .ArgConstraint(ArgumentCondition(0U, OutOfRange, SingleValue(0)))); + addToFunctionSummaryMap( + "__single_val_out_1", Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) + .ArgConstraint(ArgumentCondition(0U, OutOfRange, SingleValue(1)))); + addToFunctionSummaryMap( + "__range_out_1_2", Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) + .ArgConstraint(ArgumentCondition(0U, OutOfRange, Range(1, 2)))); + addToFunctionSummaryMap( + "__range_out_m1_1", Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) + .ArgConstraint(ArgumentCondition(0U, OutOfRange, Range(-1, 1)))); + addToFunctionSummaryMap( + "__range_out_m2_m1", Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) + .ArgConstraint(ArgumentCondition(0U, OutOfRange, Range(-2, -1)))); + addToFunctionSummaryMap( + "__range_out_m10_10", Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) + .ArgConstraint(ArgumentCondition(0U, OutOfRange, Range(-10, 10)))); + addToFunctionSummaryMap("__range_out_m1_inf", + Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) + .ArgConstraint(ArgumentCondition( + 0U, OutOfRange, Range(-1, IntMax)))); + addToFunctionSummaryMap("__range_out_0_inf", + Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) + .ArgConstraint(ArgumentCondition( + 0U, OutOfRange, Range(0, IntMax)))); + addToFunctionSummaryMap("__range_out_1_inf", + Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) + .ArgConstraint(ArgumentCondition( + 0U, OutOfRange, Range(1, IntMax)))); + addToFunctionSummaryMap("__range_out_minf_m1", + Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) + .ArgConstraint(ArgumentCondition( + 0U, OutOfRange, Range(IntMin, -1)))); + addToFunctionSummaryMap("__range_out_minf_0", + Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) + .ArgConstraint(ArgumentCondition( + 0U, OutOfRange, Range(IntMin, 0)))); + addToFunctionSummaryMap("__range_out_minf_1", + Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) + .ArgConstraint(ArgumentCondition( + 0U, OutOfRange, Range(IntMin, 1)))); + addToFunctionSummaryMap("__range_out_1_2__4_6", Signature(ArgTypes{IntTy}, RetType{IntTy}), Summary(EvalCallAsPure) .ArgConstraint(ArgumentCondition( - 0U, WithinRange, Range({1, 2}, {4, 5})))); + 0U, OutOfRange, Range({1, 2}, {4, 6})))); + addToFunctionSummaryMap( + "__range_out_1_2__4_inf", Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) + .ArgConstraint( + ArgumentCondition(0U, OutOfRange, Range({1, 2}, {4, IntMax})))); // Test range kind. addToFunctionSummaryMap( @@ -2638,18 +3831,48 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( "__test_restrict_param_2"}, Signature(ArgTypes{VoidPtrRestrictTy}, RetType{VoidTy}), Summary(EvalCallAsPure)); - } - SummariesInitialized = true; + // Test the application of cases. + addToFunctionSummaryMap( + "__test_case_note", Signature(ArgTypes{}, RetType{IntTy}), + Summary(EvalCallAsPure) + .Case({ReturnValueCondition(WithinRange, SingleValue(0))}, + ErrnoIrrelevant, "Function returns 0") + .Case({ReturnValueCondition(WithinRange, SingleValue(1))}, + ErrnoIrrelevant, "Function returns 1")); + addToFunctionSummaryMap( + "__test_case_range_1_2__4_6", + Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) + .Case({ArgumentCondition(0U, WithinRange, + IntRangeVector{{IntMin, 0}, {3, 3}}), + ReturnValueCondition(WithinRange, SingleValue(1))}, + ErrnoIrrelevant) + .Case({ArgumentCondition(0U, WithinRange, + IntRangeVector{{3, 3}, {7, IntMax}}), + ReturnValueCondition(WithinRange, SingleValue(2))}, + ErrnoIrrelevant) + .Case({ArgumentCondition(0U, WithinRange, + IntRangeVector{{IntMin, 0}, {7, IntMax}}), + ReturnValueCondition(WithinRange, SingleValue(3))}, + ErrnoIrrelevant) + .Case({ArgumentCondition( + 0U, WithinRange, + IntRangeVector{{IntMin, 0}, {3, 3}, {7, IntMax}}), + ReturnValueCondition(WithinRange, SingleValue(4))}, + ErrnoIrrelevant)); + } } void ento::registerStdCLibraryFunctionsChecker(CheckerManager &mgr) { auto *Checker = mgr.registerChecker<StdLibraryFunctionsChecker>(); + Checker->CheckName = mgr.getCurrentCheckerName(); + const AnalyzerOptions &Opts = mgr.getAnalyzerOptions(); Checker->DisplayLoadedSummaries = - mgr.getAnalyzerOptions().getCheckerBooleanOption( - Checker, "DisplayLoadedSummaries"); - Checker->ModelPOSIX = - mgr.getAnalyzerOptions().getCheckerBooleanOption(Checker, "ModelPOSIX"); + Opts.getCheckerBooleanOption(Checker, "DisplayLoadedSummaries"); + Checker->ModelPOSIX = Opts.getCheckerBooleanOption(Checker, "ModelPOSIX"); + Checker->ShouldAssumeControlledEnvironment = + Opts.ShouldAssumeControlledEnvironment; } bool ento::shouldRegisterStdCLibraryFunctionsChecker( @@ -2657,16 +3880,12 @@ bool ento::shouldRegisterStdCLibraryFunctionsChecker( return true; } -#define REGISTER_CHECKER(name) \ - void ento::register##name(CheckerManager &mgr) { \ - StdLibraryFunctionsChecker *checker = \ - mgr.getChecker<StdLibraryFunctionsChecker>(); \ - checker->ChecksEnabled[StdLibraryFunctionsChecker::CK_##name] = true; \ - checker->CheckNames[StdLibraryFunctionsChecker::CK_##name] = \ - mgr.getCurrentCheckerName(); \ - } \ - \ - bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; } - -REGISTER_CHECKER(StdCLibraryFunctionArgsChecker) -REGISTER_CHECKER(StdCLibraryFunctionsTesterChecker) +void ento::registerStdCLibraryFunctionsTesterChecker(CheckerManager &mgr) { + auto *Checker = mgr.getChecker<StdLibraryFunctionsChecker>(); + Checker->AddTestFunctions = true; +} + +bool ento::shouldRegisterStdCLibraryFunctionsTesterChecker( + const CheckerManager &mgr) { + return true; +} 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 dd65f8c035aa..07727b339d96 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -14,12 +14,15 @@ #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/CheckerHelpers.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" #include <functional> +#include <optional> using namespace clang; using namespace ento; @@ -84,10 +87,10 @@ const StreamErrorState ErrorFError{false, false, true}; /// Full state information about a stream pointer. struct StreamState { /// The last file operation called in the stream. + /// Can be nullptr. const FnDescription *LastOperation; /// State of a stream symbol. - /// FIXME: We need maybe an "escaped" state later. enum KindTy { Opened, /// Stream is opened. Closed, /// Closed stream (an invalid stream pointer after it was closed). @@ -145,7 +148,7 @@ struct StreamState { void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddPointer(LastOperation); ID.AddInteger(State); - ID.AddInteger(ErrorState); + ErrorState.Profile(ID); ID.AddBoolean(FilePositionIndeterminate); } }; @@ -201,7 +204,7 @@ ProgramStateRef bindAndAssumeTrue(ProgramStateRef State, CheckerContext &C, ProgramStateRef bindInt(uint64_t Value, ProgramStateRef State, CheckerContext &C, const CallExpr *CE) { State = State->BindExpr(CE, C.getLocationContext(), - C.getSValBuilder().makeIntVal(Value, false)); + C.getSValBuilder().makeIntVal(Value, CE->getType())); return State; } @@ -235,48 +238,96 @@ public: private: CallDescriptionMap<FnDescription> FnDescriptions = { - {{"fopen"}, {nullptr, &StreamChecker::evalFopen, ArgNone}}, - {{"freopen", 3}, + {{{"fopen"}, 2}, {nullptr, &StreamChecker::evalFopen, ArgNone}}, + {{{"fdopen"}, 2}, {nullptr, &StreamChecker::evalFopen, ArgNone}}, + {{{"freopen"}, 3}, {&StreamChecker::preFreopen, &StreamChecker::evalFreopen, 2}}, - {{"tmpfile"}, {nullptr, &StreamChecker::evalFopen, ArgNone}}, - {{"fclose", 1}, + {{{"tmpfile"}, 0}, {nullptr, &StreamChecker::evalFopen, ArgNone}}, + {{{"fclose"}, 1}, {&StreamChecker::preDefault, &StreamChecker::evalFclose, 0}}, - {{"fread", 4}, - {&StreamChecker::preFread, + {{{"fread"}, 4}, + {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, true), std::bind(&StreamChecker::evalFreadFwrite, _1, _2, _3, _4, true), 3}}, - {{"fwrite", 4}, - {&StreamChecker::preFwrite, + {{{"fwrite"}, 4}, + {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, false), std::bind(&StreamChecker::evalFreadFwrite, _1, _2, _3, _4, false), 3}}, - {{"fseek", 3}, {&StreamChecker::preFseek, &StreamChecker::evalFseek, 0}}, - {{"ftell", 1}, {&StreamChecker::preDefault, nullptr, 0}}, - {{"rewind", 1}, {&StreamChecker::preDefault, nullptr, 0}}, - {{"fgetpos", 2}, {&StreamChecker::preDefault, nullptr, 0}}, - {{"fsetpos", 2}, {&StreamChecker::preDefault, nullptr, 0}}, - {{"clearerr", 1}, + {{{"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}}, + {{{"fprintf"}}, + {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, false), + std::bind(&StreamChecker::evalFprintf, _1, _2, _3, _4), 0}}, + {{{"fscanf"}}, + {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, true), + std::bind(&StreamChecker::evalFscanf, _1, _2, _3, _4), 0}}, + {{{"ungetc"}, 2}, + {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, false), + std::bind(&StreamChecker::evalUngetc, _1, _2, _3, _4), 1}}, + {{{"getdelim"}, 4}, + {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, true), + std::bind(&StreamChecker::evalGetdelim, _1, _2, _3, _4), 3}}, + {{{"getline"}, 3}, + {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, true), + std::bind(&StreamChecker::evalGetdelim, _1, _2, _3, _4), 2}}, + {{{"fseek"}, 3}, + {&StreamChecker::preFseek, &StreamChecker::evalFseek, 0}}, + {{{"fseeko"}, 3}, + {&StreamChecker::preFseek, &StreamChecker::evalFseek, 0}}, + {{{"ftell"}, 1}, + {&StreamChecker::preDefault, &StreamChecker::evalFtell, 0}}, + {{{"ftello"}, 1}, + {&StreamChecker::preDefault, &StreamChecker::evalFtell, 0}}, + {{{"fflush"}, 1}, + {&StreamChecker::preFflush, &StreamChecker::evalFflush, 0}}, + {{{"rewind"}, 1}, + {&StreamChecker::preDefault, &StreamChecker::evalRewind, 0}}, + {{{"fgetpos"}, 2}, + {&StreamChecker::preDefault, &StreamChecker::evalFgetpos, 0}}, + {{{"fsetpos"}, 2}, + {&StreamChecker::preDefault, &StreamChecker::evalFsetpos, 0}}, + {{{"clearerr"}, 1}, {&StreamChecker::preDefault, &StreamChecker::evalClearerr, 0}}, - {{"feof", 1}, + {{{"feof"}, 1}, {&StreamChecker::preDefault, std::bind(&StreamChecker::evalFeofFerror, _1, _2, _3, _4, ErrorFEof), 0}}, - {{"ferror", 1}, + {{{"ferror"}, 1}, {&StreamChecker::preDefault, std::bind(&StreamChecker::evalFeofFerror, _1, _2, _3, _4, ErrorFError), 0}}, - {{"fileno", 1}, {&StreamChecker::preDefault, nullptr, 0}}, + {{{"fileno"}, 1}, {&StreamChecker::preDefault, nullptr, 0}}, }; CallDescriptionMap<FnDescription> FnTestDescriptions = { - {{"StreamTesterChecker_make_feof_stream", 1}, + {{{"StreamTesterChecker_make_feof_stream"}, 1}, {nullptr, std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4, ErrorFEof), 0}}, - {{"StreamTesterChecker_make_ferror_stream", 1}, + {{{"StreamTesterChecker_make_ferror_stream"}, 1}, {nullptr, std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4, ErrorFError), 0}}, }; + /// Expanded value of EOF, empty before initialization. + mutable std::optional<int> EofVal; + /// Expanded value of SEEK_SET, 0 if not found. + mutable int SeekSetVal = 0; + /// Expanded value of SEEK_CUR, 1 if not found. + mutable int SeekCurVal = 1; + /// Expanded value of SEEK_END, 2 if not found. + mutable int SeekEndVal = 2; + void evalFopen(const FnDescription *Desc, const CallEvent &Call, CheckerContext &C) const; @@ -288,20 +339,47 @@ 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 evalFprintf(const FnDescription *Desc, const CallEvent &Call, + CheckerContext &C) const; + + void evalFscanf(const FnDescription *Desc, const CallEvent &Call, + CheckerContext &C) const; + + void evalUngetc(const FnDescription *Desc, const CallEvent &Call, + CheckerContext &C) const; + + void evalGetdelim(const FnDescription *Desc, const CallEvent &Call, + CheckerContext &C) const; + void preFseek(const FnDescription *Desc, const CallEvent &Call, CheckerContext &C) const; void evalFseek(const FnDescription *Desc, const CallEvent &Call, CheckerContext &C) const; + void evalFgetpos(const FnDescription *Desc, const CallEvent &Call, + CheckerContext &C) const; + + void evalFsetpos(const FnDescription *Desc, const CallEvent &Call, + CheckerContext &C) const; + + void evalFtell(const FnDescription *Desc, const CallEvent &Call, + CheckerContext &C) const; + + void evalRewind(const FnDescription *Desc, const CallEvent &Call, + CheckerContext &C) const; + void preDefault(const FnDescription *Desc, const CallEvent &Call, CheckerContext &C) const; @@ -316,6 +394,12 @@ private: CheckerContext &C, const StreamErrorState &ErrorKind) const; + void preFflush(const FnDescription *Desc, const CallEvent &Call, + CheckerContext &C) const; + + void evalFflush(const FnDescription *Desc, const CallEvent &Call, + CheckerContext &C) const; + /// Check that the stream (in StreamVal) is not NULL. /// If it can only be NULL a fatal error is emitted and nullptr returned. /// Otherwise the return value is a new state where the stream is constrained @@ -367,7 +451,7 @@ private: // (and matching name) as stream functions. if (!Call.isGlobalCFunction()) return nullptr; - for (auto P : Call.parameters()) { + for (auto *P : Call.parameters()) { QualType T = P->getType(); if (!T->isIntegralOrEnumerationType() && !T->isPointerType()) return nullptr; @@ -378,23 +462,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, @@ -410,6 +485,26 @@ private: }); } + void initMacroValues(CheckerContext &C) const { + if (EofVal) + return; + + if (const std::optional<int> OptInt = + tryExpandAsInteger("EOF", C.getPreprocessor())) + EofVal = *OptInt; + else + EofVal = -1; + if (const std::optional<int> OptInt = + tryExpandAsInteger("SEEK_SET", C.getPreprocessor())) + SeekSetVal = *OptInt; + if (const std::optional<int> OptInt = + tryExpandAsInteger("SEEK_END", C.getPreprocessor())) + SeekEndVal = *OptInt; + if (const std::optional<int> OptInt = + tryExpandAsInteger("SEEK_CUR", C.getPreprocessor())) + SeekCurVal = *OptInt; + } + /// Searches for the ExplodedNode where the file descriptor was acquired for /// StreamSym. static const ExplodedNode *getAcquisitionSite(const ExplodedNode *N, @@ -425,8 +520,7 @@ private: REGISTER_MAP_WITH_PROGRAMSTATE(StreamMap, SymbolRef, StreamState) inline void assertStreamStateOpened(const StreamState *SS) { - assert(SS->isOpened() && - "Previous create of error node for non-opened stream failed?"); + assert(SS->isOpened() && "Stream is expected to be opened"); } const ExplodedNode *StreamChecker::getAcquisitionSite(const ExplodedNode *N, @@ -456,6 +550,8 @@ const ExplodedNode *StreamChecker::getAcquisitionSite(const ExplodedNode *N, void StreamChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { + initMacroValues(C); + const FnDescription *Desc = lookupFn(Call); if (!Desc || !Desc->PreFn) return; @@ -525,7 +621,7 @@ void StreamChecker::evalFreopen(const FnDescription *Desc, if (!CE) return; - Optional<DefinedSVal> StreamVal = + std::optional<DefinedSVal> StreamVal = getStreamArg(Desc, Call).getAs<DefinedSVal>(); if (!StreamVal) return; @@ -548,8 +644,9 @@ void StreamChecker::evalFreopen(const FnDescription *Desc, State->BindExpr(CE, C.getLocationContext(), *StreamVal); // Generate state for NULL return value. // Stream switches to OpenFailed state. - ProgramStateRef StateRetNull = State->BindExpr(CE, C.getLocationContext(), - C.getSValBuilder().makeNull()); + ProgramStateRef StateRetNull = + State->BindExpr(CE, C.getLocationContext(), + C.getSValBuilder().makeNullWithType(CE->getType())); StateRetNotNull = StateRetNotNull->set<StreamMap>(StreamSym, StreamState::getOpened(Desc)); @@ -572,6 +669,10 @@ void StreamChecker::evalFclose(const FnDescription *Desc, const CallEvent &Call, if (!SS) return; + auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); + if (!CE) + return; + assertStreamStateOpened(SS); // Close the File Descriptor. @@ -579,11 +680,21 @@ void StreamChecker::evalFclose(const FnDescription *Desc, const CallEvent &Call, // and can not be used any more. State = State->set<StreamMap>(Sym, StreamState::getClosed(Desc)); - C.addTransition(State); + // Return 0 on success, EOF on failure. + SValBuilder &SVB = C.getSValBuilder(); + ProgramStateRef StateSuccess = State->BindExpr( + CE, C.getLocationContext(), SVB.makeIntVal(0, C.getASTContext().IntTy)); + ProgramStateRef StateFailure = + State->BindExpr(CE, C.getLocationContext(), + SVB.makeIntVal(*EofVal, C.getASTContext().IntTy)); + + C.addTransition(StateSuccess); + C.addTransition(StateFailure); } -void StreamChecker::preFread(const FnDescription *Desc, const CallEvent &Call, - 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, @@ -597,6 +708,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); @@ -607,24 +723,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 { @@ -637,10 +735,10 @@ void StreamChecker::evalFreadFwrite(const FnDescription *Desc, if (!CE) return; - Optional<NonLoc> SizeVal = Call.getArgSVal(1).getAs<NonLoc>(); + std::optional<NonLoc> SizeVal = Call.getArgSVal(1).getAs<NonLoc>(); if (!SizeVal) return; - Optional<NonLoc> NMembVal = Call.getArgSVal(2).getAs<NonLoc>(); + std::optional<NonLoc> NMembVal = Call.getArgSVal(2).getAs<NonLoc>(); if (!NMembVal) return; @@ -670,24 +768,19 @@ void StreamChecker::evalFreadFwrite(const FnDescription *Desc, if (!IsFread || (OldSS->ErrorState != ErrorFEof)) { ProgramStateRef StateNotFailed = State->BindExpr(CE, C.getLocationContext(), *NMembVal); - if (StateNotFailed) { - StateNotFailed = StateNotFailed->set<StreamMap>( - StreamSym, StreamState::getOpened(Desc)); - C.addTransition(StateNotFailed); - } + StateNotFailed = + StateNotFailed->set<StreamMap>(StreamSym, StreamState::getOpened(Desc)); + C.addTransition(StateNotFailed); } // Add transition for the failed state. - Optional<NonLoc> RetVal = makeRetVal(C, CE).castAs<NonLoc>(); - assert(RetVal && "Value should be NonLoc."); + NonLoc RetVal = makeRetVal(C, CE).castAs<NonLoc>(); ProgramStateRef StateFailed = - State->BindExpr(CE, C.getLocationContext(), *RetVal); - if (!StateFailed) - return; - auto Cond = C.getSValBuilder() - .evalBinOpNN(State, BO_LT, *RetVal, *NMembVal, - C.getASTContext().IntTy) - .getAs<DefinedOrUnknownSVal>(); + State->BindExpr(CE, C.getLocationContext(), RetVal); + SValBuilder &SVB = C.getSValBuilder(); + auto Cond = + SVB.evalBinOpNN(State, BO_LT, RetVal, *NMembVal, SVB.getConditionType()) + .getAs<DefinedOrUnknownSVal>(); if (!Cond) return; StateFailed = StateFailed->assume(*Cond, true); @@ -710,6 +803,351 @@ 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::evalFprintf(const FnDescription *Desc, + const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + if (Call.getNumArgs() < 2) + return; + 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); + + NonLoc RetVal = makeRetVal(C, CE).castAs<NonLoc>(); + State = State->BindExpr(CE, C.getLocationContext(), RetVal); + SValBuilder &SVB = C.getSValBuilder(); + auto &ACtx = C.getASTContext(); + auto Cond = SVB.evalBinOp(State, BO_GE, RetVal, SVB.makeZeroVal(ACtx.IntTy), + SVB.getConditionType()) + .getAs<DefinedOrUnknownSVal>(); + if (!Cond) + return; + ProgramStateRef StateNotFailed, StateFailed; + std::tie(StateNotFailed, StateFailed) = State->assume(*Cond); + + 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. + StateFailed = StateFailed->set<StreamMap>( + StreamSym, StreamState::getOpened(Desc, ErrorFError, true)); + C.addTransition(StateFailed); +} + +void StreamChecker::evalFscanf(const FnDescription *Desc, const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + if (Call.getNumArgs() < 2) + return; + 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); + + SValBuilder &SVB = C.getSValBuilder(); + ASTContext &ACtx = C.getASTContext(); + + // Add the success state. + // In this context "success" means there is not an EOF or other read error + // before any item is matched in 'fscanf'. But there may be match failure, + // therefore return value can be 0 or greater. + // It is not specified what happens if some items (not all) are matched and + // then EOF or read error happens. Now this case is handled like a "success" + // case, and no error flags are set on the stream. This is probably not + // accurate, and the POSIX documentation does not tell more. + if (OldSS->ErrorState != ErrorFEof) { + NonLoc RetVal = makeRetVal(C, CE).castAs<NonLoc>(); + ProgramStateRef StateNotFailed = + State->BindExpr(CE, C.getLocationContext(), RetVal); + auto RetGeZero = + SVB.evalBinOp(StateNotFailed, BO_GE, RetVal, + SVB.makeZeroVal(ACtx.IntTy), SVB.getConditionType()) + .getAs<DefinedOrUnknownSVal>(); + if (!RetGeZero) + return; + StateNotFailed = StateNotFailed->assume(*RetGeZero, true); + + C.addTransition(StateNotFailed); + } + + // Add transition for the failed state. + // Error occurs if nothing is matched yet and reading the input fails. + // Error can be EOF, or other error. At "other error" FERROR or 'errno' can + // be set but it is not further specified if all are required to be set. + // Documentation does not mention, but file position will be set to + // indeterminate similarly as at 'fread'. + ProgramStateRef StateFailed = bindInt(*EofVal, State, C, CE); + StreamErrorState NewES = (OldSS->ErrorState == ErrorFEof) + ? ErrorFEof + : ErrorNone | 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::evalUngetc(const FnDescription *Desc, const CallEvent &Call, + CheckerContext &C) 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); + + // Generate a transition for the success state. + 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); + + // Add transition for the failed state. + // Failure of 'ungetc' does not result in feof or ferror state. + // If the PutVal has value of EofVal the function should "fail", but this is + // the same transition as the success state. + // In this case only one state transition is added by the analyzer (the two + // new states may be similar). + ProgramStateRef StateFailed = bindInt(*EofVal, State, C, CE); + StateFailed = + StateFailed->set<StreamMap>(StreamSym, StreamState::getOpened(Desc)); + C.addTransition(StateFailed); +} + +void StreamChecker::evalGetdelim(const FnDescription *Desc, + const CallEvent &Call, + CheckerContext &C) 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); + + // Upon successful completion, the getline() and getdelim() functions shall + // return the number of bytes written into the buffer. + // If the end-of-file indicator for the stream is set, the function shall + // return -1. + // If an error occurs, the function shall return -1 and set 'errno'. + + // Add transition for the successful state. + if (OldSS->ErrorState != ErrorFEof) { + NonLoc RetVal = makeRetVal(C, CE).castAs<NonLoc>(); + ProgramStateRef StateNotFailed = + State->BindExpr(CE, C.getLocationContext(), RetVal); + SValBuilder &SVB = C.getSValBuilder(); + auto Cond = + SVB.evalBinOp(State, BO_GE, RetVal, SVB.makeZeroVal(CE->getType()), + SVB.getConditionType()) + .getAs<DefinedOrUnknownSVal>(); + if (!Cond) + return; + StateNotFailed = StateNotFailed->assume(*Cond, true); + if (!StateNotFailed) + return; + C.addTransition(StateNotFailed); + } + + // Add transition for the failed state. + // If a (non-EOF) error occurs, the resulting value of the file position + // indicator for the stream is indeterminate. + ProgramStateRef StateFailed = bindInt(-1, State, C, CE); + 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::preFseek(const FnDescription *Desc, const CallEvent &Call, CheckerContext &C) const { ProgramStateRef State = C.getState(); @@ -743,6 +1181,11 @@ void StreamChecker::evalFseek(const FnDescription *Desc, const CallEvent &Call, if (!State->get<StreamMap>(StreamSym)) return; + const llvm::APSInt *PosV = + C.getSValBuilder().getKnownValue(State, Call.getArgSVal(1)); + const llvm::APSInt *WhenceV = + C.getSValBuilder().getKnownValue(State, Call.getArgSVal(2)); + DefinedSVal RetVal = makeRetVal(C, CE); // Make expression result. @@ -761,14 +1204,145 @@ void StreamChecker::evalFseek(const FnDescription *Desc, const CallEvent &Call, // It is possible that fseek fails but sets none of the error flags. // If fseek failed, assume that the file position becomes indeterminate in any // case. + StreamErrorState NewErrS = ErrorNone | ErrorFError; + // Setting the position to start of file never produces EOF error. + if (!(PosV && *PosV == 0 && WhenceV && *WhenceV == SeekSetVal)) + NewErrS = NewErrS | ErrorFEof; StateFailed = StateFailed->set<StreamMap>( - StreamSym, - StreamState::getOpened(Desc, ErrorNone | ErrorFEof | ErrorFError, true)); + StreamSym, StreamState::getOpened(Desc, NewErrS, true)); C.addTransition(StateNotFailed); C.addTransition(StateFailed, constructSetEofNoteTag(C, StreamSym)); } +void StreamChecker::evalFgetpos(const FnDescription *Desc, + const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + SymbolRef Sym = getStreamArg(Desc, Call).getAsSymbol(); + if (!Sym) + return; + + // Do not evaluate if stream is not found. + if (!State->get<StreamMap>(Sym)) + return; + + auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); + if (!CE) + return; + + DefinedSVal RetVal = makeRetVal(C, CE); + State = State->BindExpr(CE, C.getLocationContext(), RetVal); + ProgramStateRef StateNotFailed, StateFailed; + std::tie(StateFailed, StateNotFailed) = + C.getConstraintManager().assumeDual(State, RetVal); + + // This function does not affect the stream state. + // Still we add success and failure state with the appropriate return value. + // StdLibraryFunctionsChecker can change these states (set the 'errno' state). + C.addTransition(StateNotFailed); + C.addTransition(StateFailed); +} + +void StreamChecker::evalFsetpos(const FnDescription *Desc, + const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); + if (!StreamSym) + return; + + const StreamState *SS = State->get<StreamMap>(StreamSym); + if (!SS) + return; + + auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); + if (!CE) + return; + + assertStreamStateOpened(SS); + + DefinedSVal RetVal = makeRetVal(C, CE); + State = State->BindExpr(CE, C.getLocationContext(), RetVal); + ProgramStateRef StateNotFailed, StateFailed; + std::tie(StateFailed, StateNotFailed) = + C.getConstraintManager().assumeDual(State, RetVal); + + StateNotFailed = StateNotFailed->set<StreamMap>( + StreamSym, StreamState::getOpened(Desc, ErrorNone, false)); + + // At failure ferror could be set. + // The standards do not tell what happens with the file position at failure. + // But we can assume that it is dangerous to make a next I/O operation after + // the position was not set correctly (similar to 'fseek'). + StateFailed = StateFailed->set<StreamMap>( + StreamSym, StreamState::getOpened(Desc, ErrorNone | ErrorFError, true)); + + C.addTransition(StateNotFailed); + C.addTransition(StateFailed); +} + +void StreamChecker::evalFtell(const FnDescription *Desc, const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + SymbolRef Sym = getStreamArg(Desc, Call).getAsSymbol(); + if (!Sym) + return; + + if (!State->get<StreamMap>(Sym)) + return; + + auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); + if (!CE) + return; + + SValBuilder &SVB = C.getSValBuilder(); + NonLoc RetVal = makeRetVal(C, CE).castAs<NonLoc>(); + ProgramStateRef StateNotFailed = + State->BindExpr(CE, C.getLocationContext(), RetVal); + auto Cond = + SVB.evalBinOp(State, BO_GE, RetVal, SVB.makeZeroVal(Call.getResultType()), + SVB.getConditionType()) + .getAs<DefinedOrUnknownSVal>(); + if (!Cond) + return; + StateNotFailed = StateNotFailed->assume(*Cond, true); + if (!StateNotFailed) + return; + + ProgramStateRef StateFailed = State->BindExpr( + CE, C.getLocationContext(), SVB.makeIntVal(-1, Call.getResultType())); + + // This function does not affect the stream state. + // Still we add success and failure state with the appropriate return value. + // StdLibraryFunctionsChecker can change these states (set the 'errno' state). + C.addTransition(StateNotFailed); + C.addTransition(StateFailed); +} + +void StreamChecker::evalRewind(const FnDescription *Desc, const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); + if (!StreamSym) + return; + + const StreamState *SS = State->get<StreamMap>(StreamSym); + if (!SS) + return; + + auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); + if (!CE) + return; + + assertStreamStateOpened(SS); + + State = State->set<StreamMap>(StreamSym, + StreamState::getOpened(Desc, ErrorNone, false)); + + C.addTransition(State); +} + void StreamChecker::evalClearerr(const FnDescription *Desc, const CallEvent &Call, CheckerContext &C) const { @@ -858,6 +1432,84 @@ void StreamChecker::evalSetFeofFerror(const FnDescription *Desc, C.addTransition(State); } +void StreamChecker::preFflush(const FnDescription *Desc, const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + SVal StreamVal = getStreamArg(Desc, Call); + std::optional<DefinedSVal> Stream = StreamVal.getAs<DefinedSVal>(); + if (!Stream) + return; + + ProgramStateRef StateNotNull, StateNull; + std::tie(StateNotNull, StateNull) = + C.getConstraintManager().assumeDual(State, *Stream); + if (StateNotNull && !StateNull) + ensureStreamOpened(StreamVal, C, StateNotNull); +} + +void StreamChecker::evalFflush(const FnDescription *Desc, const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + SVal StreamVal = getStreamArg(Desc, Call); + std::optional<DefinedSVal> Stream = StreamVal.getAs<DefinedSVal>(); + if (!Stream) + return; + + // Skip if the stream can be both NULL and non-NULL. + ProgramStateRef StateNotNull, StateNull; + std::tie(StateNotNull, StateNull) = + C.getConstraintManager().assumeDual(State, *Stream); + if (StateNotNull && StateNull) + return; + if (StateNotNull && !StateNull) + State = StateNotNull; + else + State = StateNull; + + const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); + if (!CE) + return; + + // `fflush` returns EOF on failure, otherwise returns 0. + ProgramStateRef StateFailed = bindInt(*EofVal, State, C, CE); + ProgramStateRef StateNotFailed = bindInt(0, State, C, CE); + + // Clear error states if `fflush` returns 0, but retain their EOF flags. + auto ClearErrorInNotFailed = [&StateNotFailed, Desc](SymbolRef Sym, + const StreamState *SS) { + if (SS->ErrorState & ErrorFError) { + StreamErrorState NewES = + (SS->ErrorState & ErrorFEof) ? ErrorFEof : ErrorNone; + StreamState NewSS = StreamState::getOpened(Desc, NewES, false); + StateNotFailed = StateNotFailed->set<StreamMap>(Sym, NewSS); + } + }; + + if (StateNotNull && !StateNull) { + // Skip if the input stream's state is unknown, open-failed or closed. + if (SymbolRef StreamSym = StreamVal.getAsSymbol()) { + const StreamState *SS = State->get<StreamMap>(StreamSym); + if (SS) { + assert(SS->isOpened() && "Stream is expected to be opened"); + ClearErrorInNotFailed(StreamSym, SS); + } else + return; + } + } else { + // Clear error states for all streams. + const StreamMapTy &Map = StateNotFailed->get<StreamMap>(); + for (const auto &I : Map) { + SymbolRef Sym = I.first; + const StreamState &SS = I.second; + if (SS.isOpened()) + ClearErrorInNotFailed(Sym, &SS); + } + } + + C.addTransition(StateNotFailed); + C.addTransition(StateFailed); +} + ProgramStateRef StreamChecker::ensureStreamNonNull(SVal StreamVal, const Expr *StreamE, CheckerContext &C, @@ -869,7 +1521,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)) { @@ -925,7 +1577,6 @@ ProgramStateRef StreamChecker::ensureStreamOpened(SVal StreamVal, N)); return nullptr; } - return State; } return State; @@ -979,12 +1630,13 @@ ProgramStateRef StreamChecker::ensureNoFilePositionIndeterminate( ProgramStateRef StreamChecker::ensureFseekWhenceCorrect(SVal WhenceVal, CheckerContext &C, ProgramStateRef State) const { - Optional<nonloc::ConcreteInt> CI = WhenceVal.getAs<nonloc::ConcreteInt>(); + std::optional<nonloc::ConcreteInt> CI = + WhenceVal.getAs<nonloc::ConcreteInt>(); if (!CI) return State; int64_t X = CI->getValue().getSExtValue(); - if (X >= 0 && X <= 2) + if (X == SeekSetVal || X == SeekCurVal || X == SeekEndVal) return State; if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) { @@ -1035,10 +1687,12 @@ StreamChecker::reportLeaks(const SmallVector<SymbolRef, 2> &LeakedSyms, // FIXME: Add a checker option to turn this uniqueing feature off. const ExplodedNode *StreamOpenNode = getAcquisitionSite(Err, LeakSym, C); assert(StreamOpenNode && "Could not find place of stream opening."); - PathDiagnosticLocation LocUsedForUniqueing = - PathDiagnosticLocation::createBegin( - StreamOpenNode->getStmtForDiagnostics(), C.getSourceManager(), - StreamOpenNode->getLocationContext()); + + PathDiagnosticLocation LocUsedForUniqueing; + if (const Stmt *StreamStmt = StreamOpenNode->getStmtForDiagnostics()) + LocUsedForUniqueing = PathDiagnosticLocation::createBegin( + StreamStmt, C.getSourceManager(), + StreamOpenNode->getLocationContext()); std::unique_ptr<PathSensitiveBugReport> R = std::make_unique<PathSensitiveBugReport>( @@ -1118,4 +1772,4 @@ void ento::registerStreamTesterChecker(CheckerManager &Mgr) { bool ento::shouldRegisterStreamTesterChecker(const CheckerManager &Mgr) { return true; -}
\ No newline at end of file +} diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StringChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StringChecker.cpp new file mode 100644 index 000000000000..2dc9e29ca906 --- /dev/null +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StringChecker.cpp @@ -0,0 +1,105 @@ +//=== StringChecker.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 implements the modeling of the std::basic_string type. +// This involves checking preconditions of the operations and applying the +// effects of the operations, e.g. their post-conditions. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" + +using namespace clang; +using namespace ento; + +namespace { +class StringChecker : public Checker<check::PreCall> { + BugType BT_Null{this, "Dereference of null pointer", categories::LogicError}; + mutable const FunctionDecl *StringConstCharPtrCtor = nullptr; + mutable CanQualType SizeTypeTy; + const CallDescription TwoParamStdStringCtor = { + {"std", "basic_string", "basic_string"}, 2, 2}; + + bool isCharToStringCtor(const CallEvent &Call, const ASTContext &ACtx) const; + +public: + void checkPreCall(const CallEvent &Call, CheckerContext &C) const; +}; + +bool StringChecker::isCharToStringCtor(const CallEvent &Call, + const ASTContext &ACtx) const { + if (!TwoParamStdStringCtor.matches(Call)) + return false; + const auto *FD = dyn_cast<FunctionDecl>(Call.getDecl()); + assert(FD); + + // See if we already cached it. + if (StringConstCharPtrCtor && StringConstCharPtrCtor == FD) + return true; + + // Verify that the parameters have the expected types: + // - arg 1: `const CharT *` + // - arg 2: some allocator - which is definately not `size_t`. + const QualType Arg1Ty = Call.getArgExpr(0)->getType().getCanonicalType(); + const QualType Arg2Ty = Call.getArgExpr(1)->getType().getCanonicalType(); + + if (!Arg1Ty->isPointerType()) + return false; + + // It makes sure that we don't select the `string(const char* p, size_t len)` + // overload accidentally. + if (Arg2Ty.getCanonicalType() == ACtx.getSizeType()) + return false; + + StringConstCharPtrCtor = FD; // Cache the decl of the right overload. + return true; +} + +void StringChecker::checkPreCall(const CallEvent &Call, + CheckerContext &C) const { + if (!isCharToStringCtor(Call, C.getASTContext())) + return; + const auto Param = Call.getArgSVal(0).getAs<Loc>(); + if (!Param) + return; + + // We managed to constrain the parameter to non-null. + ProgramStateRef NotNull, Null; + std::tie(NotNull, Null) = C.getState()->assume(*Param); + + if (NotNull) { + const auto Callback = [Param](PathSensitiveBugReport &BR) -> std::string { + return BR.isInteresting(*Param) ? "Assuming the pointer is not null." + : ""; + }; + + // Emit note only if this operation constrained the pointer to be null. + C.addTransition(NotNull, Null ? C.getNoteTag(Callback) : nullptr); + return; + } + + // We found a path on which the parameter is NULL. + if (ExplodedNode *N = C.generateErrorNode(C.getState())) { + auto R = std::make_unique<PathSensitiveBugReport>( + BT_Null, "The parameter must not be null", N); + bugreporter::trackExpressionValue(N, Call.getArgExpr(0), *R); + C.emitReport(std::move(R)); + } +} + +} // end anonymous namespace + +void ento::registerStringChecker(CheckerManager &Mgr) { + Mgr.registerChecker<StringChecker>(); +} + +bool ento::shouldRegisterStringChecker(const CheckerManager &) { return true; } 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..6de33da107a3 --- /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, + 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/Taint.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Taint.cpp index 71b2ab834a07..6362c82b009d 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Taint.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Taint.cpp @@ -10,9 +10,10 @@ // //===----------------------------------------------------------------------===// -#include "Taint.h" +#include "clang/StaticAnalyzer/Checkers/Taint.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" +#include <optional> using namespace clang; using namespace ento; @@ -37,7 +38,9 @@ void taint::printTaint(ProgramStateRef State, raw_ostream &Out, const char *NL, Out << I.first << " : " << I.second << NL; } -void dumpTaint(ProgramStateRef State) { printTaint(State, llvm::errs()); } +void taint::dumpTaint(ProgramStateRef State) { + printTaint(State, llvm::errs()); +} ProgramStateRef taint::addTaint(ProgramStateRef State, const Stmt *S, const LocationContext *LCtx, @@ -61,7 +64,7 @@ ProgramStateRef taint::addTaint(ProgramStateRef State, SVal V, // their parent region, which is a conjured symbol default-bound to the base // region of the parent region. if (auto LCV = V.getAs<nonloc::LazyCompoundVal>()) { - if (Optional<SVal> binding = + if (std::optional<SVal> binding = State->getStateManager().getStoreManager().getDefaultBinding( *LCV)) { if (SymbolRef Sym = binding->getAsSymbol()) @@ -143,62 +146,140 @@ ProgramStateRef taint::addPartialTaint(ProgramStateRef State, bool taint::isTainted(ProgramStateRef State, const Stmt *S, const LocationContext *LCtx, TaintTagType Kind) { - SVal val = State->getSVal(S, LCtx); - return isTainted(State, val, Kind); + return !getTaintedSymbolsImpl(State, S, LCtx, Kind, /*ReturnFirstOnly=*/true) + .empty(); } bool taint::isTainted(ProgramStateRef State, SVal V, TaintTagType Kind) { - if (SymbolRef Sym = V.getAsSymbol()) - return isTainted(State, Sym, Kind); - if (const MemRegion *Reg = V.getAsRegion()) - return isTainted(State, Reg, Kind); - return false; + return !getTaintedSymbolsImpl(State, V, Kind, /*ReturnFirstOnly=*/true) + .empty(); } bool taint::isTainted(ProgramStateRef State, const MemRegion *Reg, TaintTagType K) { - if (!Reg) - return false; + return !getTaintedSymbolsImpl(State, Reg, K, /*ReturnFirstOnly=*/true) + .empty(); +} - // Element region (array element) is tainted if either the base or the offset - // are tainted. - if (const ElementRegion *ER = dyn_cast<ElementRegion>(Reg)) - return isTainted(State, ER->getSuperRegion(), K) || - isTainted(State, ER->getIndex(), K); +bool taint::isTainted(ProgramStateRef State, SymbolRef Sym, TaintTagType Kind) { + return !getTaintedSymbolsImpl(State, Sym, Kind, /*ReturnFirstOnly=*/true) + .empty(); +} - if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(Reg)) - return isTainted(State, SR->getSymbol(), K); +std::vector<SymbolRef> taint::getTaintedSymbols(ProgramStateRef State, + const Stmt *S, + const LocationContext *LCtx, + TaintTagType Kind) { + return getTaintedSymbolsImpl(State, S, LCtx, Kind, /*ReturnFirstOnly=*/false); +} - if (const SubRegion *ER = dyn_cast<SubRegion>(Reg)) - return isTainted(State, ER->getSuperRegion(), K); +std::vector<SymbolRef> taint::getTaintedSymbols(ProgramStateRef State, SVal V, + TaintTagType Kind) { + return getTaintedSymbolsImpl(State, V, Kind, /*ReturnFirstOnly=*/false); +} - return false; +std::vector<SymbolRef> taint::getTaintedSymbols(ProgramStateRef State, + SymbolRef Sym, + TaintTagType Kind) { + return getTaintedSymbolsImpl(State, Sym, Kind, /*ReturnFirstOnly=*/false); } -bool taint::isTainted(ProgramStateRef State, SymbolRef Sym, TaintTagType Kind) { +std::vector<SymbolRef> taint::getTaintedSymbols(ProgramStateRef State, + const MemRegion *Reg, + TaintTagType Kind) { + return getTaintedSymbolsImpl(State, Reg, Kind, /*ReturnFirstOnly=*/false); +} + +std::vector<SymbolRef> taint::getTaintedSymbolsImpl(ProgramStateRef State, + const Stmt *S, + const LocationContext *LCtx, + TaintTagType Kind, + bool returnFirstOnly) { + SVal val = State->getSVal(S, LCtx); + return getTaintedSymbolsImpl(State, val, Kind, returnFirstOnly); +} + +std::vector<SymbolRef> taint::getTaintedSymbolsImpl(ProgramStateRef State, + SVal V, TaintTagType Kind, + bool returnFirstOnly) { + if (SymbolRef Sym = V.getAsSymbol()) + return getTaintedSymbolsImpl(State, Sym, Kind, returnFirstOnly); + if (const MemRegion *Reg = V.getAsRegion()) + return getTaintedSymbolsImpl(State, Reg, Kind, returnFirstOnly); + return {}; +} + +std::vector<SymbolRef> taint::getTaintedSymbolsImpl(ProgramStateRef State, + const MemRegion *Reg, + TaintTagType K, + bool returnFirstOnly) { + std::vector<SymbolRef> TaintedSymbols; + if (!Reg) + return TaintedSymbols; + + // Element region (array element) is tainted if the offset is tainted. + if (const ElementRegion *ER = dyn_cast<ElementRegion>(Reg)) { + std::vector<SymbolRef> TaintedIndex = + getTaintedSymbolsImpl(State, ER->getIndex(), K, returnFirstOnly); + llvm::append_range(TaintedSymbols, TaintedIndex); + if (returnFirstOnly && !TaintedSymbols.empty()) + return TaintedSymbols; // return early if needed + } + + // Symbolic region is tainted if the corresponding symbol is tainted. + if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(Reg)) { + std::vector<SymbolRef> TaintedRegions = + getTaintedSymbolsImpl(State, SR->getSymbol(), K, returnFirstOnly); + llvm::append_range(TaintedSymbols, TaintedRegions); + if (returnFirstOnly && !TaintedSymbols.empty()) + return TaintedSymbols; // return early if needed + } + + // Any subregion (including Element and Symbolic regions) is tainted if its + // super-region is tainted. + if (const SubRegion *ER = dyn_cast<SubRegion>(Reg)) { + std::vector<SymbolRef> TaintedSubRegions = + getTaintedSymbolsImpl(State, ER->getSuperRegion(), K, returnFirstOnly); + llvm::append_range(TaintedSymbols, TaintedSubRegions); + if (returnFirstOnly && !TaintedSymbols.empty()) + return TaintedSymbols; // return early if needed + } + + return TaintedSymbols; +} + +std::vector<SymbolRef> taint::getTaintedSymbolsImpl(ProgramStateRef State, + SymbolRef Sym, + TaintTagType Kind, + bool returnFirstOnly) { + std::vector<SymbolRef> TaintedSymbols; if (!Sym) - return false; + return TaintedSymbols; // Traverse all the symbols this symbol depends on to see if any are tainted. - for (SymExpr::symbol_iterator SI = Sym->symbol_begin(), - SE = Sym->symbol_end(); - SI != SE; ++SI) { - if (!isa<SymbolData>(*SI)) + for (SymbolRef SubSym : Sym->symbols()) { + if (!isa<SymbolData>(SubSym)) continue; - if (const TaintTagType *Tag = State->get<TaintMap>(*SI)) { - if (*Tag == Kind) - return true; + if (const TaintTagType *Tag = State->get<TaintMap>(SubSym)) { + if (*Tag == Kind) { + TaintedSymbols.push_back(SubSym); + if (returnFirstOnly) + return TaintedSymbols; // return early if needed + } } - if (const auto *SD = dyn_cast<SymbolDerived>(*SI)) { + if (const auto *SD = dyn_cast<SymbolDerived>(SubSym)) { // If this is a SymbolDerived with a tainted parent, it's also tainted. - if (isTainted(State, SD->getParentSymbol(), Kind)) - return true; + std::vector<SymbolRef> TaintedParents = getTaintedSymbolsImpl( + State, SD->getParentSymbol(), Kind, returnFirstOnly); + llvm::append_range(TaintedSymbols, TaintedParents); + if (returnFirstOnly && !TaintedSymbols.empty()) + return TaintedSymbols; // return early if needed // If this is a SymbolDerived with the same parent symbol as another - // tainted SymbolDerived and a region that's a sub-region of that tainted - // symbol, it's also tainted. + // tainted SymbolDerived and a region that's a sub-region of that + // tainted symbol, it's also tainted. if (const TaintedSubRegions *Regs = State->get<DerivedSymTaint>(SD->getParentSymbol())) { const TypedValueRegion *R = SD->getRegion(); @@ -207,46 +288,32 @@ bool taint::isTainted(ProgramStateRef State, SymbolRef Sym, TaintTagType Kind) { // complete. For example, this would not currently identify // overlapping fields in a union as tainted. To identify this we can // check for overlapping/nested byte offsets. - if (Kind == I.second && R->isSubRegionOf(I.first)) - return true; + if (Kind == I.second && R->isSubRegionOf(I.first)) { + TaintedSymbols.push_back(SD->getParentSymbol()); + if (returnFirstOnly && !TaintedSymbols.empty()) + return TaintedSymbols; // return early if needed + } } } } // If memory region is tainted, data is also tainted. - if (const auto *SRV = dyn_cast<SymbolRegionValue>(*SI)) { - if (isTainted(State, SRV->getRegion(), Kind)) - return true; + if (const auto *SRV = dyn_cast<SymbolRegionValue>(SubSym)) { + std::vector<SymbolRef> TaintedRegions = + getTaintedSymbolsImpl(State, SRV->getRegion(), Kind, returnFirstOnly); + llvm::append_range(TaintedSymbols, TaintedRegions); + if (returnFirstOnly && !TaintedSymbols.empty()) + return TaintedSymbols; // return early if needed } // If this is a SymbolCast from a tainted value, it's also tainted. - if (const auto *SC = dyn_cast<SymbolCast>(*SI)) { - if (isTainted(State, SC->getOperand(), Kind)) - return true; + if (const auto *SC = dyn_cast<SymbolCast>(SubSym)) { + std::vector<SymbolRef> TaintedCasts = + getTaintedSymbolsImpl(State, SC->getOperand(), Kind, returnFirstOnly); + llvm::append_range(TaintedSymbols, TaintedCasts); + if (returnFirstOnly && !TaintedSymbols.empty()) + return TaintedSymbols; // return early if needed } } - - return false; -} - -PathDiagnosticPieceRef TaintBugVisitor::VisitNode(const ExplodedNode *N, - BugReporterContext &BRC, - PathSensitiveBugReport &BR) { - - // Find the ExplodedNode where the taint was first introduced - if (!isTainted(N->getState(), V) || - isTainted(N->getFirstPred()->getState(), V)) - return nullptr; - - const Stmt *S = N->getStmtForDiagnostics(); - if (!S) - return nullptr; - - const LocationContext *NCtx = N->getLocationContext(); - PathDiagnosticLocation L = - PathDiagnosticLocation::createBegin(S, BRC.getSourceManager(), NCtx); - if (!L.isValid() || !L.asLocation().isValid()) - return nullptr; - - return std::make_shared<PathDiagnosticEventPiece>(L, "Taint originated here"); + return TaintedSymbols; } diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Taint.h b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Taint.h deleted file mode 100644 index 659a3c898d56..000000000000 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Taint.h +++ /dev/null @@ -1,106 +0,0 @@ -//=== Taint.h - Taint tracking and basic propagation rules. --------*- 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 basic, non-domain-specific mechanisms for tracking tainted values. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_TAINT_H -#define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_TAINT_H - -#include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" - -namespace clang { -namespace ento { -namespace taint { - -/// The type of taint, which helps to differentiate between different types of -/// taint. -using TaintTagType = unsigned; - -static constexpr TaintTagType TaintTagGeneric = 0; - -/// Create a new state in which the value of the statement is marked as tainted. -LLVM_NODISCARD ProgramStateRef addTaint(ProgramStateRef State, const Stmt *S, - const LocationContext *LCtx, - TaintTagType Kind = TaintTagGeneric); - -/// Create a new state in which the value is marked as tainted. -LLVM_NODISCARD ProgramStateRef addTaint(ProgramStateRef State, SVal V, - TaintTagType Kind = TaintTagGeneric); - -/// Create a new state in which the symbol is marked as tainted. -LLVM_NODISCARD ProgramStateRef addTaint(ProgramStateRef State, SymbolRef Sym, - TaintTagType Kind = TaintTagGeneric); - -/// Create a new state in which the pointer represented by the region -/// is marked as tainted. -LLVM_NODISCARD ProgramStateRef addTaint(ProgramStateRef State, - const MemRegion *R, - TaintTagType Kind = TaintTagGeneric); - -LLVM_NODISCARD ProgramStateRef removeTaint(ProgramStateRef State, SVal V); - -LLVM_NODISCARD ProgramStateRef removeTaint(ProgramStateRef State, - const MemRegion *R); - -LLVM_NODISCARD ProgramStateRef removeTaint(ProgramStateRef State, - SymbolRef Sym); - -/// Create a new state in a which a sub-region of a given symbol is tainted. -/// This might be necessary when referring to regions that can not have an -/// individual symbol, e.g. if they are represented by the default binding of -/// a LazyCompoundVal. -LLVM_NODISCARD ProgramStateRef addPartialTaint( - ProgramStateRef State, SymbolRef ParentSym, const SubRegion *SubRegion, - TaintTagType Kind = TaintTagGeneric); - -/// Check if the statement has a tainted value in the given state. -bool isTainted(ProgramStateRef State, const Stmt *S, - const LocationContext *LCtx, - TaintTagType Kind = TaintTagGeneric); - -/// Check if the value is tainted in the given state. -bool isTainted(ProgramStateRef State, SVal V, - TaintTagType Kind = TaintTagGeneric); - -/// Check if the symbol is tainted in the given state. -bool isTainted(ProgramStateRef State, SymbolRef Sym, - TaintTagType Kind = TaintTagGeneric); - -/// Check if the pointer represented by the region is tainted in the given -/// state. -bool isTainted(ProgramStateRef State, const MemRegion *Reg, - TaintTagType Kind = TaintTagGeneric); - -void printTaint(ProgramStateRef State, raw_ostream &Out, const char *nl = "\n", - const char *sep = ""); - -LLVM_DUMP_METHOD void dumpTaint(ProgramStateRef State); - -/// The bug visitor prints a diagnostic message at the location where a given -/// variable was tainted. -class TaintBugVisitor final : public BugReporterVisitor { -private: - const SVal V; - -public: - TaintBugVisitor(const SVal V) : V(V) {} - void Profile(llvm::FoldingSetNodeID &ID) const override { ID.Add(V); } - - PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, - BugReporterContext &BRC, - PathSensitiveBugReport &BR) override; -}; - -} // namespace taint -} // namespace ento -} // namespace clang - -#endif diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/TaintTesterChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/TaintTesterChecker.cpp index 916977c10c0c..acf4e833095b 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/TaintTesterChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/TaintTesterChecker.cpp @@ -10,8 +10,8 @@ // //===----------------------------------------------------------------------===// -#include "Taint.h" #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Checkers/Taint.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" @@ -22,27 +22,14 @@ using namespace ento; using namespace taint; namespace { -class TaintTesterChecker : public Checker< check::PostStmt<Expr> > { - - mutable std::unique_ptr<BugType> BT; - void initBugType() const; - - /// Given a pointer argument, get the symbol of the value it contains - /// (points to). - SymbolRef getPointedToSymbol(CheckerContext &C, - const Expr* Arg, - bool IssueWarning = true) const; +class TaintTesterChecker : public Checker<check::PostStmt<Expr>> { + const BugType BT{this, "Tainted data", "General"}; public: void checkPostStmt(const Expr *E, CheckerContext &C) const; }; } -inline void TaintTesterChecker::initBugType() const { - if (!BT) - BT.reset(new BugType(this, "Tainted data", "General")); -} - void TaintTesterChecker::checkPostStmt(const Expr *E, CheckerContext &C) const { ProgramStateRef State = C.getState(); @@ -51,8 +38,7 @@ void TaintTesterChecker::checkPostStmt(const Expr *E, if (isTainted(State, E, C.getLocationContext())) { if (ExplodedNode *N = C.generateNonFatalErrorNode()) { - initBugType(); - auto report = std::make_unique<PathSensitiveBugReport>(*BT, "tainted", N); + auto report = std::make_unique<PathSensitiveBugReport>(BT, "tainted", N); report->addRange(E->getSourceRange()); C.emitReport(std::move(report)); } diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp index eeec807ccee4..667b19f8120e 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp @@ -17,6 +17,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "llvm/ADT/FoldingSet.h" +#include <optional> using namespace clang; using namespace ento; @@ -77,7 +78,7 @@ public: class TestAfterDivZeroChecker : public Checker<check::PreStmt<BinaryOperator>, check::BranchCondition, check::EndFunction> { - mutable std::unique_ptr<BuiltinBug> DivZeroBug; + const BugType DivZeroBug{this, "Division by zero"}; void reportBug(SVal Val, CheckerContext &C) const; public: @@ -100,7 +101,7 @@ DivisionBRVisitor::VisitNode(const ExplodedNode *Succ, BugReporterContext &BRC, const Expr *E = nullptr; - if (Optional<PostStmt> P = Succ->getLocationAs<PostStmt>()) + if (std::optional<PostStmt> P = Succ->getLocationAs<PostStmt>()) if (const BinaryOperator *BO = P->getStmtAs<BinaryOperator>()) { BinaryOperator::Opcode Op = BO->getOpcode(); if (Op == BO_Div || Op == BO_Rem || Op == BO_DivAssign || @@ -132,7 +133,7 @@ DivisionBRVisitor::VisitNode(const ExplodedNode *Succ, BugReporterContext &BRC, } bool TestAfterDivZeroChecker::isZero(SVal S, CheckerContext &C) const { - Optional<DefinedSVal> DSV = S.getAs<DefinedSVal>(); + std::optional<DefinedSVal> DSV = S.getAs<DefinedSVal>(); if (!DSV) return false; @@ -164,12 +165,10 @@ 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")); - auto R = std::make_unique<PathSensitiveBugReport>( - *DivZeroBug, "Value being compared against zero has already been used " - "for division", + DivZeroBug, + "Value being compared against zero has already been used " + "for division", N); R->addVisitor(std::make_unique<DivisionBRVisitor>(Val.getAsSymbol(), @@ -187,10 +186,7 @@ void TestAfterDivZeroChecker::checkEndFunction(const ReturnStmt *, return; DivZeroMapTy::Factory &F = State->get_context<DivZeroMap>(); - for (llvm::ImmutableSet<ZeroState>::iterator I = DivZeroes.begin(), - E = DivZeroes.end(); - I != E; ++I) { - ZeroState ZS = *I; + for (const ZeroState &ZS : DivZeroes) { if (ZS.getStackFrameContext() == C.getStackFrame()) DivZeroes = F.remove(DivZeroes, ZS); } diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/TrustNonnullChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/TrustNonnullChecker.cpp index 5cc713172527..e2f8bd541c96 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/TrustNonnullChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/TrustNonnullChecker.cpp @@ -69,8 +69,7 @@ public: if (!CondS || CondS->computeComplexity() > ComplexityThreshold) return State; - for (auto B=CondS->symbol_begin(), E=CondS->symbol_end(); B != E; ++B) { - const SymbolRef Antecedent = *B; + for (SymbolRef Antecedent : CondS->symbols()) { State = addImplication(Antecedent, State, true); State = addImplication(Antecedent, State, false); } diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/TrustReturnsNonnullChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/TrustReturnsNonnullChecker.cpp new file mode 100644 index 000000000000..d80559c6a915 --- /dev/null +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/TrustReturnsNonnullChecker.cpp @@ -0,0 +1,60 @@ +//== TrustReturnsNonnullChecker.cpp -- API nullability modeling -*- 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 checker adds nullability-related assumptions to methods annotated with +// returns_nonnull attribute. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/Attr.h" +#include "clang/AST/Decl.h" +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" + +using namespace clang; +using namespace ento; + +namespace { + +class TrustReturnsNonnullChecker : public Checker<check::PostCall> { + +public: + TrustReturnsNonnullChecker(ASTContext &Ctx) {} + + void checkPostCall(const CallEvent &Call, CheckerContext &C) const { + ProgramStateRef State = C.getState(); + + if (isNonNullPtr(Call)) + if (auto L = Call.getReturnValue().getAs<Loc>()) + State = State->assume(*L, /*assumption=*/true); + + C.addTransition(State); + } + +private: + /// \returns Whether the method declaration has the attribute returns_nonnull. + bool isNonNullPtr(const CallEvent &Call) const { + QualType ExprRetType = Call.getResultType(); + const Decl *CallDeclaration = Call.getDecl(); + if (!ExprRetType->isAnyPointerType() || !CallDeclaration) + return false; + + return CallDeclaration->hasAttr<ReturnsNonNullAttr>(); + } +}; + +} // namespace + +void ento::registerTrustReturnsNonnullChecker(CheckerManager &Mgr) { + Mgr.registerChecker<TrustReturnsNonnullChecker>(Mgr.getASTContext()); +} + +bool ento::shouldRegisterTrustReturnsNonnullChecker(const CheckerManager &mgr) { + return true; +} diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp index ebe5ad53cc30..aa478b69aade 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp @@ -18,6 +18,7 @@ #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include <optional> #include <utility> using namespace clang; @@ -26,7 +27,7 @@ using namespace ento; namespace { class UndefBranchChecker : public Checker<check::BranchCondition> { - mutable std::unique_ptr<BuiltinBug> BT; + const BugType BT{this, "Branch condition evaluates to a garbage value"}; struct FindUndefExpr { ProgramStateRef St; @@ -63,52 +64,47 @@ void UndefBranchChecker::checkBranchCondition(const Stmt *Condition, // ObjCForCollection is a loop, but has no actual condition. if (isa<ObjCForCollectionStmt>(Condition)) return; - SVal X = Ctx.getSVal(Condition); - if (X.isUndef()) { - // Generate a sink node, which implicitly marks both outgoing branches as - // infeasible. - ExplodedNode *N = Ctx.generateErrorNode(); - if (N) { - if (!BT) - BT.reset(new BuiltinBug( - 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 - // branch condition." We do a recursive walk of the condition's - // subexpressions and roughly look for the most nested subexpression - // that binds to Undefined. We then highlight that expression's range. - - // Get the predecessor node and check if is a PostStmt with the Stmt - // being the terminator condition. We want to inspect the state - // of that node instead because it will contain main information about - // the subexpressions. - - // Note: any predecessor will do. They should have identical state, - // since all the BlockEdge did was act as an error sink since the value - // had to already be undefined. - assert (!N->pred_empty()); - const Expr *Ex = cast<Expr>(Condition); - ExplodedNode *PrevN = *N->pred_begin(); - ProgramPoint P = PrevN->getLocation(); - ProgramStateRef St = N->getState(); - - if (Optional<PostStmt> PS = P.getAs<PostStmt>()) - if (PS->getStmt() == Ex) - St = PrevN->getState(); - - FindUndefExpr FindIt(St, Ctx.getLocationContext()); - Ex = FindIt.FindExpr(Ex); - - // Emit the bug report. - auto R = std::make_unique<PathSensitiveBugReport>( - *BT, BT->getDescription(), N); - bugreporter::trackExpressionValue(N, Ex, *R); - R->addRange(Ex->getSourceRange()); - - Ctx.emitReport(std::move(R)); - } - } + if (!Ctx.getSVal(Condition).isUndef()) + return; + + // Generate a sink node, which implicitly marks both outgoing branches as + // infeasible. + ExplodedNode *N = Ctx.generateErrorNode(); + if (!N) + return; + // What's going on here: we want to highlight the subexpression of the + // condition that is the most likely source of the "uninitialized + // branch condition." We do a recursive walk of the condition's + // subexpressions and roughly look for the most nested subexpression + // that binds to Undefined. We then highlight that expression's range. + + // Get the predecessor node and check if is a PostStmt with the Stmt + // being the terminator condition. We want to inspect the state + // of that node instead because it will contain main information about + // the subexpressions. + + // Note: any predecessor will do. They should have identical state, + // since all the BlockEdge did was act as an error sink since the value + // had to already be undefined. + assert(!N->pred_empty()); + const Expr *Ex = cast<Expr>(Condition); + ExplodedNode *PrevN = *N->pred_begin(); + ProgramPoint P = PrevN->getLocation(); + ProgramStateRef St = N->getState(); + + if (std::optional<PostStmt> PS = P.getAs<PostStmt>()) + if (PS->getStmt() == Ex) + St = PrevN->getState(); + + FindUndefExpr FindIt(St, Ctx.getLocationContext()); + Ex = FindIt.FindExpr(Ex); + + // Emit the bug report. + auto R = std::make_unique<PathSensitiveBugReport>(BT, BT.getDescription(), N); + bugreporter::trackExpressionValue(N, Ex, *R); + R->addRange(Ex->getSourceRange()); + + Ctx.emitReport(std::move(R)); } void ento::registerUndefBranchChecker(CheckerManager &mgr) { diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp index 816a547cadc3..2839ef0b6d2e 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp @@ -19,6 +19,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/raw_ostream.h" +#include <optional> using namespace clang; using namespace ento; @@ -26,7 +27,7 @@ using namespace ento; namespace { class UndefCapturedBlockVarChecker : public Checker< check::PostStmt<BlockExpr> > { - mutable std::unique_ptr<BugType> BT; + const BugType BT{this, "uninitialized variable captured by block"}; public: void checkPostStmt(const BlockExpr *BE, CheckerContext &C) const; @@ -56,26 +57,19 @@ UndefCapturedBlockVarChecker::checkPostStmt(const BlockExpr *BE, ProgramStateRef state = C.getState(); auto *R = cast<BlockDataRegion>(C.getSVal(BE).getAsRegion()); - BlockDataRegion::referenced_vars_iterator I = R->referenced_vars_begin(), - E = R->referenced_vars_end(); - - for (; I != E; ++I) { + for (auto Var : R->referenced_vars()) { // This VarRegion is the region associated with the block; we need // the one associated with the encompassing context. - const VarRegion *VR = I.getCapturedRegion(); + const VarRegion *VR = Var.getCapturedRegion(); const VarDecl *VD = VR->getDecl(); if (VD->hasAttr<BlocksAttr>() || !VD->hasLocalStorage()) continue; // Get the VarRegion associated with VD in the local stack frame. - if (Optional<UndefinedVal> V = - state->getSVal(I.getOriginalRegion()).getAs<UndefinedVal>()) { + if (std::optional<UndefinedVal> V = + state->getSVal(Var.getOriginalRegion()).getAs<UndefinedVal>()) { if (ExplodedNode *N = C.generateErrorNode()) { - if (!BT) - BT.reset( - new BuiltinBug(this, "uninitialized variable captured by block")); - // Generate a bug report. SmallString<128> buf; llvm::raw_svector_ostream os(buf); @@ -83,7 +77,7 @@ UndefCapturedBlockVarChecker::checkPostStmt(const BlockExpr *BE, os << "Variable '" << VD->getName() << "' is uninitialized when captured by block"; - auto R = std::make_unique<PathSensitiveBugReport>(*BT, os.str(), N); + auto R = std::make_unique<PathSensitiveBugReport>(BT, os.str(), N); if (const Expr *Ex = FindBlockDeclRefExpr(BE->getBody(), VD)) R->addRange(Ex->getSourceRange()); bugreporter::trackStoredValue(*V, VR, *R, diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp index 477d910bc653..4b845bb3ded2 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp @@ -28,7 +28,7 @@ namespace { class UndefResultChecker : public Checker< check::PostStmt<BinaryOperator> > { - mutable std::unique_ptr<BugType> BT; + const BugType BT{this, "Result of operation is garbage or undefined"}; public: void checkPostStmt(const BinaryOperator *B, CheckerContext &C) const; @@ -53,33 +53,17 @@ static bool isArrayIndexOutOfBounds(CheckerContext &C, const Expr *Ex) { DefinedOrUnknownSVal Idx = ER->getIndex().castAs<DefinedOrUnknownSVal>(); DefinedOrUnknownSVal ElementCount = getDynamicElementCount( state, ER->getSuperRegion(), C.getSValBuilder(), ER->getValueType()); - ProgramStateRef StInBound = state->assumeInBound(Idx, ElementCount, true); - ProgramStateRef StOutBound = state->assumeInBound(Idx, ElementCount, false); + ProgramStateRef StInBound, StOutBound; + std::tie(StInBound, StOutBound) = state->assumeInBoundDual(Idx, ElementCount); 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->countLeadingZeros(); -} - 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") @@ -90,10 +74,6 @@ void UndefResultChecker::checkPostStmt(const BinaryOperator *B, if (!N) return; - if (!BT) - BT.reset( - new BuiltinBug(this, "Result of operation is garbage or undefined")); - SmallString<256> sbuf; llvm::raw_svector_ostream OS(sbuf); const Expr *Ex = nullptr; @@ -116,62 +96,11 @@ 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().getAsString() << "'."; - } 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().getAsString() - << "\'"; - 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); + auto report = std::make_unique<PathSensitiveBugReport>(BT, OS.str(), N); if (Ex) { report->addRange(Ex->getSourceRange()); bugreporter::trackExpressionValue(N, Ex, *report); diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp index fdefe75e8201..baa07fa66764 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp @@ -24,7 +24,7 @@ using namespace ento; namespace { class UndefinedArraySubscriptChecker : public Checker< check::PreStmt<ArraySubscriptExpr> > { - mutable std::unique_ptr<BugType> BT; + const BugType BT{this, "Array subscript is undefined"}; public: void checkPreStmt(const ArraySubscriptExpr *A, CheckerContext &C) const; @@ -48,11 +48,8 @@ UndefinedArraySubscriptChecker::checkPreStmt(const ArraySubscriptExpr *A, ExplodedNode *N = C.generateErrorNode(); if (!N) return; - if (!BT) - BT.reset(new BuiltinBug(this, "Array subscript is undefined")); - // Generate a report for this bug. - auto R = std::make_unique<PathSensitiveBugReport>(*BT, BT->getDescription(), N); + auto R = std::make_unique<PathSensitiveBugReport>(BT, BT.getDescription(), N); R->addRange(A->getIdx()->getSourceRange()); bugreporter::trackExpressionValue(N, A->getIdx(), *R); C.emitReport(std::move(R)); diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp index 05f8f6084c0b..ddc6cc9e8202 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp @@ -23,7 +23,7 @@ using namespace ento; namespace { class UndefinedAssignmentChecker : public Checker<check::Bind> { - mutable std::unique_ptr<BugType> BT; + const BugType BT{this, "Assigned value is garbage or undefined"}; public: void checkBind(SVal location, SVal val, const Stmt *S, @@ -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") @@ -50,11 +49,6 @@ void UndefinedAssignmentChecker::checkBind(SVal location, SVal val, if (!N) return; - static const char *const DefaultMsg = - "Assigned value is garbage or undefined"; - if (!BT) - BT.reset(new BuiltinBug(this, DefaultMsg)); - // Generate a report for this bug. llvm::SmallString<128> Str; llvm::raw_svector_ostream OS(Str); @@ -92,7 +86,7 @@ void UndefinedAssignmentChecker::checkBind(SVal location, SVal val, if (const auto *CD = dyn_cast<CXXConstructorDecl>(C.getStackFrame()->getDecl())) { if (CD->isImplicit()) { - for (auto I : CD->inits()) { + for (auto *I : CD->inits()) { if (I->getInit()->IgnoreImpCasts() == StoreE) { OS << "Value assigned to field '" << I->getMember()->getName() << "' in implicit constructor is garbage or undefined"; @@ -106,9 +100,9 @@ void UndefinedAssignmentChecker::checkBind(SVal location, SVal val, } if (OS.str().empty()) - OS << DefaultMsg; + OS << BT.getDescription(); - auto R = std::make_unique<PathSensitiveBugReport>(*BT, OS.str(), N); + auto R = std::make_unique<PathSensitiveBugReport>(BT, OS.str(), N); if (ex) { R->addRange(ex->getSourceRange()); bugreporter::trackExpressionValue(N, ex, *R); diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefinedNewArraySizeChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefinedNewArraySizeChecker.cpp new file mode 100644 index 000000000000..f053ee887a1a --- /dev/null +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefinedNewArraySizeChecker.cpp @@ -0,0 +1,80 @@ +//===--- UndefinedNewArraySizeChecker.cpp -----------------------*- C++ -*--==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This defines UndefinedNewArraySizeChecker, a builtin check in ExprEngine +// that checks if the size of the array in a new[] expression is undefined. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" + +using namespace clang; +using namespace ento; + +namespace { +class UndefinedNewArraySizeChecker : public Checker<check::PreCall> { + +private: + BugType BT{this, "Undefined array element count in new[]", + categories::LogicError}; + +public: + void checkPreCall(const CallEvent &Call, CheckerContext &C) const; + void HandleUndefinedArrayElementCount(CheckerContext &C, SVal ArgVal, + const Expr *Init, + SourceRange Range) const; +}; +} // namespace + +void UndefinedNewArraySizeChecker::checkPreCall(const CallEvent &Call, + CheckerContext &C) const { + if (const auto *AC = dyn_cast<CXXAllocatorCall>(&Call)) { + if (!AC->isArray()) + return; + + auto *SizeEx = *AC->getArraySizeExpr(); + auto SizeVal = AC->getArraySizeVal(); + + if (SizeVal.isUndef()) + HandleUndefinedArrayElementCount(C, SizeVal, SizeEx, + SizeEx->getSourceRange()); + } +} + +void UndefinedNewArraySizeChecker::HandleUndefinedArrayElementCount( + CheckerContext &C, SVal ArgVal, const Expr *Init, SourceRange Range) const { + + if (ExplodedNode *N = C.generateErrorNode()) { + + SmallString<100> buf; + llvm::raw_svector_ostream os(buf); + + os << "Element count in new[] is a garbage value"; + + auto R = std::make_unique<PathSensitiveBugReport>(BT, os.str(), N); + R->markInteresting(ArgVal); + R->addRange(Range); + bugreporter::trackExpressionValue(N, Init, *R); + + C.emitReport(std::move(R)); + } +} + +void ento::registerUndefinedNewArraySizeChecker(CheckerManager &mgr) { + mgr.registerChecker<UndefinedNewArraySizeChecker>(); +} + +bool ento::shouldRegisterUndefinedNewArraySizeChecker( + const CheckerManager &mgr) { + return mgr.getLangOpts().CPlusPlus; +} diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObject.h b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObject.h index 2fcdd6086309..e35778e6480c 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObject.h +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObject.h @@ -299,7 +299,7 @@ private: bool isDereferencableUninit(const FieldRegion *FR, FieldChainInfo LocalChain); /// Returns true if the value of a primitive object is uninitialized. - bool isPrimitiveUninit(const SVal &V); + bool isPrimitiveUninit(SVal V); // Note that we don't have a method for arrays -- the elements of an array are // often left uninitialized intentionally even when it is of a C++ record 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 4182b51c02b0..6e1222fedad3 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp @@ -38,15 +38,12 @@ namespace { class UninitializedObjectChecker : public Checker<check::EndFunction, check::DeadSymbols> { - std::unique_ptr<BuiltinBug> BT_uninitField; + const BugType BT_uninitField{this, "Uninitialized fields"}; public: // The fields of this struct will be initialized when registering the checker. UninitObjCheckerOptions Opts; - UninitializedObjectChecker() - : BT_uninitField(new BuiltinBug(this, "Uninitialized fields")) {} - void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const; void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const; }; @@ -57,19 +54,17 @@ class RegularField final : public FieldNode { public: RegularField(const FieldRegion *FR) : FieldNode(FR) {} - virtual void printNoteMsg(llvm::raw_ostream &Out) const override { + void printNoteMsg(llvm::raw_ostream &Out) const override { Out << "uninitialized field "; } - virtual void printPrefix(llvm::raw_ostream &Out) const override {} + void printPrefix(llvm::raw_ostream &Out) const override {} - virtual void printNode(llvm::raw_ostream &Out) const override { + void printNode(llvm::raw_ostream &Out) const override { Out << getVariableName(getDecl()); } - virtual void printSeparator(llvm::raw_ostream &Out) const override { - Out << '.'; - } + void printSeparator(llvm::raw_ostream &Out) const override { Out << '.'; } }; /// Represents that the FieldNode that comes after this is declared in a base @@ -85,20 +80,20 @@ public: assert(T->getAsCXXRecordDecl()); } - virtual void printNoteMsg(llvm::raw_ostream &Out) const override { + void printNoteMsg(llvm::raw_ostream &Out) const override { llvm_unreachable("This node can never be the final node in the " "fieldchain!"); } - virtual void printPrefix(llvm::raw_ostream &Out) const override {} + void printPrefix(llvm::raw_ostream &Out) const override {} - virtual void printNode(llvm::raw_ostream &Out) const override { + void printNode(llvm::raw_ostream &Out) const override { Out << BaseClassT->getAsCXXRecordDecl()->getName() << "::"; } - virtual void printSeparator(llvm::raw_ostream &Out) const override {} + void printSeparator(llvm::raw_ostream &Out) const override {} - virtual bool isBase() const override { return true; } + bool isBase() const override { return true; } }; } // end of anonymous namespace @@ -188,7 +183,7 @@ void UninitializedObjectChecker::checkEndFunction( for (const auto &Pair : UninitFields) { auto Report = std::make_unique<PathSensitiveBugReport>( - *BT_uninitField, Pair.second, Node, LocUsedForUniqueing, + BT_uninitField, Pair.second, Node, LocUsedForUniqueing, Node->getLocationContext()->getDecl()); Context.emitReport(std::move(Report)); } @@ -202,7 +197,7 @@ void UninitializedObjectChecker::checkEndFunction( << " at the end of the constructor call"; auto Report = std::make_unique<PathSensitiveBugReport>( - *BT_uninitField, WarningOS.str(), Node, LocUsedForUniqueing, + BT_uninitField, WarningOS.str(), Node, LocUsedForUniqueing, Node->getLocationContext()->getDecl()); for (const auto &Pair : UninitFields) { @@ -330,7 +325,7 @@ bool FindUninitializedFields::isNonUnionUninit(const TypedValueRegion *R, SVal V = State->getSVal(FieldVal); - if (isDereferencableType(T) || V.getAs<nonloc::LocAsInteger>()) { + if (isDereferencableType(T) || isa<nonloc::LocAsInteger>(V)) { if (isDereferencableUninit(FR, LocalChain)) ContainsUninitField = true; continue; @@ -381,7 +376,7 @@ bool FindUninitializedFields::isUnionUninit(const TypedValueRegion *R) { return false; } -bool FindUninitializedFields::isPrimitiveUninit(const SVal &V) { +bool FindUninitializedFields::isPrimitiveUninit(SVal V) { if (V.isUndef()) return true; diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp index f0dd0bf813af..54e1e0e11909 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp @@ -19,6 +19,7 @@ #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h" +#include <optional> using namespace clang; using namespace clang::ento; @@ -34,20 +35,20 @@ public: LocField(const FieldRegion *FR, const bool IsDereferenced = true) : FieldNode(FR), IsDereferenced(IsDereferenced) {} - virtual void printNoteMsg(llvm::raw_ostream &Out) const override { + void printNoteMsg(llvm::raw_ostream &Out) const override { if (IsDereferenced) Out << "uninitialized pointee "; else Out << "uninitialized pointer "; } - virtual void printPrefix(llvm::raw_ostream &Out) const override {} + void printPrefix(llvm::raw_ostream &Out) const override {} - virtual void printNode(llvm::raw_ostream &Out) const override { + void printNode(llvm::raw_ostream &Out) const override { Out << getVariableName(getDecl()); } - virtual void printSeparator(llvm::raw_ostream &Out) const override { + void printSeparator(llvm::raw_ostream &Out) const override { if (getDecl()->getType()->isPointerType()) Out << "->"; else @@ -64,11 +65,11 @@ public: NeedsCastLocField(const FieldRegion *FR, const QualType &T) : FieldNode(FR), CastBackType(T) {} - virtual void printNoteMsg(llvm::raw_ostream &Out) const override { + void printNoteMsg(llvm::raw_ostream &Out) const override { Out << "uninitialized pointee "; } - virtual void printPrefix(llvm::raw_ostream &Out) const override { + void printPrefix(llvm::raw_ostream &Out) const override { // If this object is a nonloc::LocAsInteger. if (getDecl()->getType()->isIntegerType()) Out << "reinterpret_cast"; @@ -78,13 +79,11 @@ public: Out << '<' << CastBackType.getAsString() << ">("; } - virtual void printNode(llvm::raw_ostream &Out) const override { + void printNode(llvm::raw_ostream &Out) const override { Out << getVariableName(getDecl()) << ')'; } - virtual void printSeparator(llvm::raw_ostream &Out) const override { - Out << "->"; - } + void printSeparator(llvm::raw_ostream &Out) const override { Out << "->"; } }; /// Represents a Loc field that points to itself. @@ -93,17 +92,17 @@ class CyclicLocField final : public FieldNode { public: CyclicLocField(const FieldRegion *FR) : FieldNode(FR) {} - virtual void printNoteMsg(llvm::raw_ostream &Out) const override { + void printNoteMsg(llvm::raw_ostream &Out) const override { Out << "object references itself "; } - virtual void printPrefix(llvm::raw_ostream &Out) const override {} + void printPrefix(llvm::raw_ostream &Out) const override {} - virtual void printNode(llvm::raw_ostream &Out) const override { + void printNode(llvm::raw_ostream &Out) const override { Out << getVariableName(getDecl()); } - virtual void printSeparator(llvm::raw_ostream &Out) const override { + void printSeparator(llvm::raw_ostream &Out) const override { llvm_unreachable("CyclicLocField objects must be the last node of the " "fieldchain!"); } @@ -123,9 +122,9 @@ struct DereferenceInfo { /// Dereferences \p FR and returns with the pointee's region, and whether it /// needs to be casted back to it's location type. If for whatever reason -/// dereferencing fails, returns with None. -static llvm::Optional<DereferenceInfo> dereference(ProgramStateRef State, - const FieldRegion *FR); +/// dereferencing fails, returns std::nullopt. +static std::optional<DereferenceInfo> dereference(ProgramStateRef State, + const FieldRegion *FR); /// Returns whether \p T can be (transitively) dereferenced to a void pointer /// type (void*, void**, ...). @@ -141,10 +140,10 @@ bool FindUninitializedFields::isDereferencableUninit( SVal V = State->getSVal(FR); assert((isDereferencableType(FR->getDecl()->getType()) || - V.getAs<nonloc::LocAsInteger>()) && + isa<nonloc::LocAsInteger>(V)) && "This method only checks dereferenceable objects!"); - if (V.isUnknown() || V.getAs<loc::ConcreteInt>()) { + if (V.isUnknown() || isa<loc::ConcreteInt>(V)) { IsAnyFieldInitialized = true; return false; } @@ -161,7 +160,7 @@ bool FindUninitializedFields::isDereferencableUninit( // At this point the pointer itself is initialized and points to a valid // location, we'll now check the pointee. - llvm::Optional<DereferenceInfo> DerefInfo = dereference(State, FR); + std::optional<DereferenceInfo> DerefInfo = dereference(State, FR); if (!DerefInfo) { IsAnyFieldInitialized = true; return false; @@ -219,8 +218,8 @@ bool FindUninitializedFields::isDereferencableUninit( // Utility functions. //===----------------------------------------------------------------------===// -static llvm::Optional<DereferenceInfo> dereference(ProgramStateRef State, - const FieldRegion *FR) { +static std::optional<DereferenceInfo> dereference(ProgramStateRef State, + const FieldRegion *FR) { llvm::SmallSet<const TypedValueRegion *, 5> VisitedRegions; @@ -230,13 +229,13 @@ static llvm::Optional<DereferenceInfo> dereference(ProgramStateRef State, // If the static type of the field is a void pointer, or it is a // nonloc::LocAsInteger, we need to cast it back to the dynamic type before // dereferencing. - bool NeedsCastBack = isVoidPointer(FR->getDecl()->getType()) || - V.getAs<nonloc::LocAsInteger>(); + bool NeedsCastBack = + isVoidPointer(FR->getDecl()->getType()) || isa<nonloc::LocAsInteger>(V); // The region we'd like to acquire. const auto *R = V.getAsRegion()->getAs<TypedValueRegion>(); if (!R) - return None; + return std::nullopt; VisitedRegions.insert(R); @@ -247,7 +246,7 @@ static llvm::Optional<DereferenceInfo> dereference(ProgramStateRef State, R = Tmp->getAs<TypedValueRegion>(); if (!R) - return None; + return std::nullopt; // We found a cyclic pointer, like int *ptr = (int *)&ptr. if (!VisitedRegions.insert(R).second) diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp index 381334de068e..b05ce610067c 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp @@ -11,17 +11,18 @@ // //===----------------------------------------------------------------------===// -#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/Basic/TargetInfo.h" +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" -#include "llvm/ADT/Optional.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/raw_ostream.h" +#include <optional> using namespace clang; using namespace ento; @@ -39,12 +40,12 @@ enum class OpenVariant { namespace { class UnixAPIMisuseChecker : public Checker< check::PreStmt<CallExpr> > { - mutable std::unique_ptr<BugType> BT_open, BT_pthreadOnce; - mutable Optional<uint64_t> Val_O_CREAT; + const BugType BT_open{this, "Improper use of 'open'", categories::UnixAPI}; + const BugType BT_pthreadOnce{this, "Improper use of 'pthread_once'", + categories::UnixAPI}; + mutable std::optional<uint64_t> Val_O_CREAT; public: - DefaultBool CheckMisuse, CheckPortability; - void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; void CheckOpen(CheckerContext &C, const CallExpr *CE) const; @@ -66,7 +67,9 @@ public: void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; private: - mutable std::unique_ptr<BugType> BT_mallocZero; + const BugType BT_mallocZero{ + this, "Undefined allocation of 0 bytes (CERT MEM04-C; CWE-131)", + categories::UnixAPI}; void CheckCallocZero(CheckerContext &C, const CallExpr *CE) const; void CheckMallocZero(CheckerContext &C, const CallExpr *CE) const; @@ -89,14 +92,6 @@ private: } //end anonymous namespace -static void LazyInitialize(const CheckerBase *Checker, - std::unique_ptr<BugType> &BT, - const char *name) { - if (BT) - return; - BT.reset(new BugType(Checker, name, categories::UnixAPI)); -} - //===----------------------------------------------------------------------===// // "open" (man 2 open) //===----------------------------------------------------------------------===/ @@ -110,7 +105,7 @@ void UnixAPIMisuseChecker::checkPreStmt(const CallExpr *CE, // Don't treat functions in namespaces with the same name a Unix function // as a call to the Unix function. const DeclContext *NamespaceCtx = FD->getEnclosingNamespaceContext(); - if (NamespaceCtx && isa<NamespaceDecl>(NamespaceCtx)) + if (isa_and_nonnull<NamespaceDecl>(NamespaceCtx)) return; StringRef FName = C.getCalleeName(FD); @@ -134,9 +129,7 @@ void UnixAPIMisuseChecker::ReportOpenBug(CheckerContext &C, if (!N) return; - LazyInitialize(this, BT_open, "Improper use of 'open'"); - - auto Report = std::make_unique<PathSensitiveBugReport>(*BT_open, Msg, N); + auto Report = std::make_unique<PathSensitiveBugReport>(BT_open, Msg, N); Report->addRange(SR); C.emitReport(std::move(Report)); } @@ -182,8 +175,7 @@ void UnixAPIMisuseChecker::CheckOpenVariant(CheckerContext &C, ProgramStateRef state = C.getState(); if (CE->getNumArgs() < MinArgCount) { - // The frontend should issue a warning for this case, so this is a sanity - // check. + // The frontend should issue a warning for this case. Just return. return; } else if (CE->getNumArgs() == MaxArgCount) { const Expr *Arg = CE->getArg(CreateModeArgIndex); @@ -214,7 +206,7 @@ void UnixAPIMisuseChecker::CheckOpenVariant(CheckerContext &C, // The definition of O_CREAT is platform specific. We need a better way // of querying this information from the checking environment. - if (!Val_O_CREAT.hasValue()) { + if (!Val_O_CREAT) { if (C.getASTContext().getTargetInfo().getTriple().getVendor() == llvm::Triple::Apple) Val_O_CREAT = 0x0200; @@ -230,14 +222,15 @@ void UnixAPIMisuseChecker::CheckOpenVariant(CheckerContext &C, // Now check if oflags has O_CREAT set. const Expr *oflagsEx = CE->getArg(FlagsArgIndex); const SVal V = C.getSVal(oflagsEx); - if (!V.getAs<NonLoc>()) { + if (!isa<NonLoc>(V)) { // The case where 'V' can be a location can only be due to a bad header, // so in this case bail out. return; } NonLoc oflags = V.castAs<NonLoc>(); NonLoc ocreateFlag = C.getSValBuilder() - .makeIntVal(Val_O_CREAT.getValue(), oflagsEx->getType()).castAs<NonLoc>(); + .makeIntVal(*Val_O_CREAT, oflagsEx->getType()) + .castAs<NonLoc>(); SVal maskedFlagsUC = C.getSValBuilder().evalBinOpNN(state, BO_And, oflags, ocreateFlag, oflagsEx->getType()); @@ -303,10 +296,8 @@ void UnixAPIMisuseChecker::CheckPthreadOnce(CheckerContext &C, if (isa<VarRegion>(R) && isa<StackLocalsSpaceRegion>(R->getMemorySpace())) os << " Perhaps you intended to declare the variable as 'static'?"; - LazyInitialize(this, BT_pthreadOnce, "Improper use of 'pthread_once'"); - auto report = - std::make_unique<PathSensitiveBugReport>(*BT_pthreadOnce, os.str(), N); + std::make_unique<PathSensitiveBugReport>(BT_pthreadOnce, os.str(), N); report->addRange(CE->getArg(0)->getSourceRange()); C.emitReport(std::move(report)); } @@ -343,14 +334,11 @@ bool UnixAPIPortabilityChecker::ReportZeroByteAllocation( if (!N) return false; - LazyInitialize(this, BT_mallocZero, - "Undefined allocation of 0 bytes (CERT MEM04-C; CWE-131)"); - SmallString<256> S; llvm::raw_svector_ostream os(S); os << "Call to '" << fn_name << "' has an allocation size of 0 bytes"; auto report = - std::make_unique<PathSensitiveBugReport>(*BT_mallocZero, os.str(), N); + std::make_unique<PathSensitiveBugReport>(BT_mallocZero, os.str(), N); report->addRange(arg->getSourceRange()); bugreporter::trackExpressionValue(N, arg, *report); @@ -366,7 +354,7 @@ void UnixAPIPortabilityChecker::BasicAllocationCheck(CheckerContext &C, const unsigned numArgs, const unsigned sizeArg, const char *fn) const { - // Sanity check for the correct number of arguments + // Check for the correct number of arguments. if (CE->getNumArgs() != numArgs) return; @@ -466,7 +454,7 @@ void UnixAPIPortabilityChecker::checkPreStmt(const CallExpr *CE, // Don't treat functions in namespaces with the same name a Unix function // as a call to the Unix function. const DeclContext *NamespaceCtx = FD->getEnclosingNamespaceContext(); - if (NamespaceCtx && isa<NamespaceDecl>(NamespaceCtx)) + if (isa_and_nonnull<NamespaceDecl>(NamespaceCtx)) return; StringRef FName = C.getCalleeName(FD); @@ -504,7 +492,7 @@ void UnixAPIPortabilityChecker::checkPreStmt(const CallExpr *CE, mgr.registerChecker<CHECKERNAME>(); \ } \ \ - bool ento::shouldRegister##CHECKERNAME(const CheckerManager &mgr) { \ + bool ento::shouldRegister##CHECKERNAME(const CheckerManager &mgr) { \ return true; \ } diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp index d231be64c2e1..d24a124f5ffe 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp @@ -24,6 +24,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" #include "llvm/ADT/SmallSet.h" +#include <optional> using namespace clang; using namespace ento; @@ -58,9 +59,8 @@ void UnreachableCodeChecker::checkEndAnalysis(ExplodedGraph &G, const ParentMap *PM = nullptr; const LocationContext *LC = nullptr; // Iterate over ExplodedGraph - for (ExplodedGraph::node_iterator I = G.nodes_begin(), E = G.nodes_end(); - I != E; ++I) { - const ProgramPoint &P = I->getLocation(); + for (const ExplodedNode &N : G.nodes()) { + const ProgramPoint &P = N.getLocation(); LC = P.getLocationContext(); if (!LC->inTopFrame()) continue; @@ -74,7 +74,7 @@ void UnreachableCodeChecker::checkEndAnalysis(ExplodedGraph &G, if (!PM) PM = &LC->getParentMap(); - if (Optional<BlockEntrance> BE = P.getAs<BlockEntrance>()) { + if (std::optional<BlockEntrance> BE = P.getAs<BlockEntrance>()) { const CFGBlock *CB = BE->getBlock(); reachable.insert(CB->getBlockID()); } @@ -92,8 +92,7 @@ void UnreachableCodeChecker::checkEndAnalysis(ExplodedGraph &G, return; // Find CFGBlocks that were not covered by any node - for (CFG::const_iterator I = C->begin(), E = C->end(); I != E; ++I) { - const CFGBlock *CB = *I; + for (const CFGBlock *CB : *C) { // Check if the block is unreachable if (reachable.count(CB->getBlockID())) continue; @@ -129,7 +128,7 @@ void UnreachableCodeChecker::checkEndAnalysis(ExplodedGraph &G, bool foundUnreachable = false; for (CFGBlock::const_iterator ci = CB->begin(), ce = CB->end(); ci != ce; ++ci) { - if (Optional<CFGStmt> S = (*ci).getAs<CFGStmt>()) + if (std::optional<CFGStmt> S = (*ci).getAs<CFGStmt>()) if (const CallExpr *CE = dyn_cast<CallExpr>(S->getStmt())) { if (CE->getBuiltinCallee() == Builtin::BI__builtin_unreachable || CE->isBuiltinAssumeFalse(Eng.getContext())) { @@ -180,34 +179,30 @@ void UnreachableCodeChecker::FindUnreachableEntryPoints(const CFGBlock *CB, CFGBlocksSet &visited) { visited.insert(CB->getBlockID()); - for (CFGBlock::const_pred_iterator I = CB->pred_begin(), E = CB->pred_end(); - I != E; ++I) { - if (!*I) + for (const CFGBlock *PredBlock : CB->preds()) { + if (!PredBlock) continue; - if (!reachable.count((*I)->getBlockID())) { + if (!reachable.count(PredBlock->getBlockID())) { // If we find an unreachable predecessor, mark this block as reachable so // we don't report this block reachable.insert(CB->getBlockID()); - if (!visited.count((*I)->getBlockID())) + if (!visited.count(PredBlock->getBlockID())) // If we haven't previously visited the unreachable predecessor, recurse - FindUnreachableEntryPoints(*I, reachable, visited); + FindUnreachableEntryPoints(PredBlock, reachable, visited); } } } // Find the Stmt* in a CFGBlock for reporting a warning const Stmt *UnreachableCodeChecker::getUnreachableStmt(const CFGBlock *CB) { - for (CFGBlock::const_iterator I = CB->begin(), E = CB->end(); I != E; ++I) { - if (Optional<CFGStmt> S = I->getAs<CFGStmt>()) { + for (const CFGElement &Elem : *CB) { + if (std::optional<CFGStmt> S = Elem.getAs<CFGStmt>()) { if (!isa<DeclStmt>(S->getStmt())) return S->getStmt(); } } - if (const Stmt *S = CB->getTerminatorStmt()) - return S; - else - return nullptr; + return CB->getTerminatorStmt(); } // Determines if the path to this CFGBlock contained an element that infers this diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp index 96501215c689..d76fe4991869 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp @@ -13,9 +13,9 @@ // //===----------------------------------------------------------------------===// -#include "Taint.h" #include "clang/AST/CharUnits.h" #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Checkers/Taint.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" @@ -24,6 +24,7 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/raw_ostream.h" +#include <optional> using namespace clang; using namespace ento; @@ -33,14 +34,11 @@ namespace { class VLASizeChecker : public Checker<check::PreStmt<DeclStmt>, check::PreStmt<UnaryExprOrTypeTraitExpr>> { - mutable std::unique_ptr<BugType> BT; - enum VLASize_Kind { - VLA_Garbage, - VLA_Zero, - VLA_Tainted, - VLA_Negative, - VLA_Overflow - }; + const BugType BT{this, "Dangerous variable-length array (VLA) declaration"}; + const BugType TaintBT{this, + "Dangerous variable-length array (VLA) declaration", + categories::TaintedData}; + enum VLASize_Kind { VLA_Garbage, VLA_Zero, VLA_Negative, VLA_Overflow }; /// Check a VLA for validity. /// Every dimension of the array and the total size is checked for validity. @@ -54,8 +52,10 @@ class VLASizeChecker const Expr *SizeE) const; void reportBug(VLASize_Kind Kind, const Expr *SizeE, ProgramStateRef State, - CheckerContext &C, - std::unique_ptr<BugReporterVisitor> Visitor = nullptr) const; + CheckerContext &C) const; + + void reportTaintBug(const Expr *SizeE, ProgramStateRef State, + CheckerContext &C, SVal TaintedSVal) const; public: void checkPreStmt(const DeclStmt *DS, CheckerContext &C) const; @@ -166,8 +166,7 @@ ProgramStateRef VLASizeChecker::checkVLAIndexSize(CheckerContext &C, // Check if the size is tainted. if (isTainted(State, SizeV)) { - reportBug(VLA_Tainted, SizeE, nullptr, C, - std::make_unique<TaintBugVisitor>(SizeV)); + reportTaintBug(SizeE, State, C, SizeV); return nullptr; } @@ -191,8 +190,9 @@ ProgramStateRef VLASizeChecker::checkVLAIndexSize(CheckerContext &C, QualType SizeTy = SizeE->getType(); DefinedOrUnknownSVal Zero = SVB.makeZeroVal(SizeTy); - SVal LessThanZeroVal = SVB.evalBinOp(State, BO_LT, SizeD, Zero, SizeTy); - if (Optional<DefinedSVal> LessThanZeroDVal = + SVal LessThanZeroVal = + SVB.evalBinOp(State, BO_LT, SizeD, Zero, SVB.getConditionType()); + if (std::optional<DefinedSVal> LessThanZeroDVal = LessThanZeroVal.getAs<DefinedSVal>()) { ConstraintManager &CM = C.getConstraintManager(); ProgramStateRef StatePos, StateNeg; @@ -208,17 +208,34 @@ ProgramStateRef VLASizeChecker::checkVLAIndexSize(CheckerContext &C, return State; } -void VLASizeChecker::reportBug( - VLASize_Kind Kind, const Expr *SizeE, ProgramStateRef State, - CheckerContext &C, std::unique_ptr<BugReporterVisitor> Visitor) const { +void VLASizeChecker::reportTaintBug(const Expr *SizeE, ProgramStateRef State, + CheckerContext &C, SVal TaintedSVal) const { // Generate an error node. ExplodedNode *N = C.generateErrorNode(State); if (!N) return; - if (!BT) - BT.reset(new BuiltinBug( - this, "Dangerous variable-length array (VLA) declaration")); + SmallString<256> buf; + llvm::raw_svector_ostream os(buf); + os << "Declared variable-length array (VLA) "; + os << "has tainted size"; + + auto report = std::make_unique<PathSensitiveBugReport>(TaintBT, os.str(), N); + report->addRange(SizeE->getSourceRange()); + bugreporter::trackExpressionValue(N, SizeE, *report); + // The vla size may be a complex expression where multiple memory locations + // are tainted. + for (auto Sym : getTaintedSymbols(State, TaintedSVal)) + report->markInteresting(Sym); + C.emitReport(std::move(report)); +} + +void VLASizeChecker::reportBug(VLASize_Kind Kind, const Expr *SizeE, + ProgramStateRef State, CheckerContext &C) const { + // Generate an error node. + ExplodedNode *N = C.generateErrorNode(State); + if (!N) + return; SmallString<256> buf; llvm::raw_svector_ostream os(buf); @@ -230,9 +247,6 @@ void VLASizeChecker::reportBug( case VLA_Zero: os << "has zero size"; break; - case VLA_Tainted: - os << "has tainted size"; - break; case VLA_Negative: os << "has negative size"; break; @@ -241,8 +255,7 @@ void VLASizeChecker::reportBug( break; } - auto report = std::make_unique<PathSensitiveBugReport>(*BT, os.str(), N); - report->addVisitor(std::move(Visitor)); + auto report = std::make_unique<PathSensitiveBugReport>(BT, os.str(), N); report->addRange(SizeE->getSourceRange()); bugreporter::trackExpressionValue(N, SizeE, *report); C.emitReport(std::move(report)); @@ -278,8 +291,7 @@ void VLASizeChecker::checkPreStmt(const DeclStmt *DS, CheckerContext &C) const { if (!State) return; - auto ArraySizeNL = ArraySize.getAs<NonLoc>(); - if (!ArraySizeNL) { + if (!isa<NonLoc>(ArraySize)) { // Array size could not be determined but state may contain new assumptions. C.addTransition(State); return; @@ -289,7 +301,7 @@ void VLASizeChecker::checkPreStmt(const DeclStmt *DS, CheckerContext &C) const { if (VD) { State = setDynamicExtent(State, State->getRegion(VD, C.getLocationContext()), - ArraySize.castAs<DefinedOrUnknownSVal>(), SVB); + ArraySize.castAs<NonLoc>(), SVB); } // Remember our assumptions! diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ValistChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ValistChecker.cpp index dde5912b6d6e..2d1b873abf73 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ValistChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ValistChecker.cpp @@ -15,6 +15,7 @@ #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" @@ -45,7 +46,7 @@ public: CK_NumCheckKinds }; - DefaultBool ChecksEnabled[CK_NumCheckKinds]; + bool ChecksEnabled[CK_NumCheckKinds] = {false}; CheckerNameRef CheckNames[CK_NumCheckKinds]; void checkPreStmt(const VAArgExpr *VAA, CheckerContext &C) const; @@ -99,42 +100,41 @@ private: }; const SmallVector<ValistChecker::VAListAccepter, 15> - ValistChecker::VAListAccepters = { - {{"vfprintf", 3}, 2}, - {{"vfscanf", 3}, 2}, - {{"vprintf", 2}, 1}, - {{"vscanf", 2}, 1}, - {{"vsnprintf", 4}, 3}, - {{"vsprintf", 3}, 2}, - {{"vsscanf", 3}, 2}, - {{"vfwprintf", 3}, 2}, - {{"vfwscanf", 3}, 2}, - {{"vwprintf", 2}, 1}, - {{"vwscanf", 2}, 1}, - {{"vswprintf", 4}, 3}, - // vswprintf is the wide version of vsnprintf, - // vsprintf has no wide version - {{"vswscanf", 3}, 2}}; - -const CallDescription - ValistChecker::VaStart("__builtin_va_start", /*Args=*/2, /*Params=*/1), - ValistChecker::VaCopy("__builtin_va_copy", 2), - ValistChecker::VaEnd("__builtin_va_end", 1); + ValistChecker::VAListAccepters = {{{{"vfprintf"}, 3}, 2}, + {{{"vfscanf"}, 3}, 2}, + {{{"vprintf"}, 2}, 1}, + {{{"vscanf"}, 2}, 1}, + {{{"vsnprintf"}, 4}, 3}, + {{{"vsprintf"}, 3}, 2}, + {{{"vsscanf"}, 3}, 2}, + {{{"vfwprintf"}, 3}, 2}, + {{{"vfwscanf"}, 3}, 2}, + {{{"vwprintf"}, 2}, 1}, + {{{"vwscanf"}, 2}, 1}, + {{{"vswprintf"}, 4}, 3}, + // vswprintf is the wide version of + // vsnprintf, vsprintf has no wide version + {{{"vswscanf"}, 3}, 2}}; + +const CallDescription ValistChecker::VaStart({"__builtin_va_start"}, /*Args=*/2, + /*Params=*/1), + ValistChecker::VaCopy({"__builtin_va_copy"}, 2), + ValistChecker::VaEnd({"__builtin_va_end"}, 1); } // end anonymous namespace void ValistChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { if (!Call.isGlobalCFunction()) return; - if (Call.isCalled(VaStart)) + if (VaStart.matches(Call)) checkVAListStartCall(Call, C, false); - else if (Call.isCalled(VaCopy)) + else if (VaCopy.matches(Call)) checkVAListStartCall(Call, C, true); - else if (Call.isCalled(VaEnd)) + else if (VaEnd.matches(Call)) checkVAListEndCall(Call, C); else { for (auto FuncInfo : VAListAccepters) { - if (!Call.isCalled(FuncInfo.Func)) + if (!FuncInfo.Func.matches(Call)) continue; bool Symbolic; const MemRegion *VAList = @@ -177,7 +177,7 @@ const MemRegion *ValistChecker::getVAListAsRegion(SVal SV, const Expr *E, if (isa<ParmVarDecl>(DeclReg->getDecl())) Reg = C.getState()->getSVal(SV.castAs<Loc>()).getAsRegion(); } - IsSymbolic = Reg && Reg->getAs<SymbolicRegion>(); + IsSymbolic = Reg && Reg->getBaseRegion()->getAs<SymbolicRegion>(); // Some VarRegion based VA lists reach here as ElementRegions. const auto *EReg = dyn_cast_or_null<ElementRegion>(Reg); return (EReg && VaListModelledAsArray) ? EReg->getSuperRegion() : Reg; diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/VforkChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/VforkChecker.cpp index 8f147026ae19..cb73ac68edd1 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/VforkChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/VforkChecker.cpp @@ -9,7 +9,7 @@ // This file defines vfork checker which checks for dangerous uses of vfork. // Vforked process shares memory (including stack) with parent so it's // range of actions is significantly limited: can't write variables, -// can't call functions not in whitelist, etc. For more details, see +// can't call functions not in the allowed list, etc. For more details, see // http://man7.org/linux/man-pages/man2/vfork.2.html // // This checker checks for prohibited constructs in vforked process. @@ -35,6 +35,7 @@ #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/AST/ParentMap.h" +#include <optional> using namespace clang; using namespace ento; @@ -43,20 +44,21 @@ namespace { class VforkChecker : public Checker<check::PreCall, check::PostCall, check::Bind, check::PreStmt<ReturnStmt>> { - mutable std::unique_ptr<BuiltinBug> BT; - mutable llvm::SmallSet<const IdentifierInfo *, 10> VforkWhitelist; - mutable const IdentifierInfo *II_vfork; + const BugType BT{this, "Dangerous construct in a vforked process"}; + mutable llvm::SmallSet<const IdentifierInfo *, 10> VforkAllowlist; + mutable const IdentifierInfo *II_vfork = nullptr; static bool isChildProcess(const ProgramStateRef State); bool isVforkCall(const Decl *D, CheckerContext &C) const; - bool isCallWhitelisted(const IdentifierInfo *II, CheckerContext &C) const; + bool isCallExplicitelyAllowed(const IdentifierInfo *II, + CheckerContext &C) const; void reportBug(const char *What, CheckerContext &C, 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; @@ -93,9 +95,9 @@ bool VforkChecker::isVforkCall(const Decl *D, CheckerContext &C) const { } // Returns true iff ok to call function after successful vfork. -bool VforkChecker::isCallWhitelisted(const IdentifierInfo *II, - CheckerContext &C) const { - if (VforkWhitelist.empty()) { +bool VforkChecker::isCallExplicitelyAllowed(const IdentifierInfo *II, + CheckerContext &C) const { + if (VforkAllowlist.empty()) { // According to manpage. const char *ids[] = { "_Exit", @@ -112,19 +114,15 @@ bool VforkChecker::isCallWhitelisted(const IdentifierInfo *II, ASTContext &AC = C.getASTContext(); for (const char **id = ids; *id; ++id) - VforkWhitelist.insert(&AC.Idents.get(*id)); + VforkAllowlist.insert(&AC.Idents.get(*id)); } - return VforkWhitelist.count(II); + return VforkAllowlist.count(II); } 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")); - SmallString<256> buf; llvm::raw_svector_ostream os(buf); @@ -133,7 +131,7 @@ void VforkChecker::reportBug(const char *What, CheckerContext &C, if (Details) os << "; " << Details; - auto Report = std::make_unique<PathSensitiveBugReport>(*BT, os.str(), N); + auto Report = std::make_unique<PathSensitiveBugReport>(BT, os.str(), N); // TODO: mark vfork call in BugReportVisitor C.emitReport(std::move(Report)); } @@ -153,8 +151,8 @@ void VforkChecker::checkPostCall(const CallEvent &Call, // Get return value of vfork. SVal VforkRetVal = Call.getReturnValue(); - Optional<DefinedOrUnknownSVal> DVal = - VforkRetVal.getAs<DefinedOrUnknownSVal>(); + std::optional<DefinedOrUnknownSVal> DVal = + VforkRetVal.getAs<DefinedOrUnknownSVal>(); if (!DVal) return; @@ -179,12 +177,13 @@ void VforkChecker::checkPostCall(const CallEvent &Call, C.addTransition(ChildState); } -// Prohibit calls to non-whitelist functions in child process. +// Prohibit calls to functions in child process which are not explicitly +// allowed. void VforkChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { ProgramStateRef State = C.getState(); - if (isChildProcess(State) - && !isCallWhitelisted(Call.getCalleeIdentifier(), C)) + if (isChildProcess(State) && + !isCallExplicitelyAllowed(Call.getCalleeIdentifier(), C)) reportBug("This function call", C); } diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp index 1c589e3468c2..33a9a07f9d32 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp @@ -116,7 +116,7 @@ void VirtualCallChecker::checkPreCall(const CallEvent &Call, if (!ObState) return; - bool IsPure = MD->isPure(); + bool IsPure = MD->isPureVirtual(); // At this point we're sure that we're calling a virtual method // during construction or destruction, so we'll emit a report. diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp index 9c7a59971763..64028b277021 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp @@ -12,8 +12,8 @@ #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/ExprCXX.h" +#include <optional> -using llvm::Optional; namespace clang { std::pair<const Expr *, bool> @@ -34,8 +34,7 @@ tryToFindPtrOrigin(const Expr *E, bool StopAtFirstRefCountedObj) { } if (auto *call = dyn_cast<CallExpr>(E)) { if (auto *memberCall = dyn_cast<CXXMemberCallExpr>(call)) { - Optional<bool> IsGetterOfRefCt = - isGetterOfRefCounted(memberCall->getMethodDecl()); + std::optional<bool> IsGetterOfRefCt = isGetterOfRefCounted(memberCall->getMethodDecl()); if (IsGetterOfRefCt && *IsGetterOfRefCt) { E = memberCall->getImplicitObjectArgument(); if (StopAtFirstRefCountedObj) { diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.h b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.h index ed4577755457..e35ea4ef05dd 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.h +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.h @@ -17,10 +17,6 @@ #include <utility> namespace clang { -class CXXRecordDecl; -class CXXBaseSpecifier; -class FunctionDecl; -class CXXMethodDecl; class Expr; /// This function de-facto defines a set of transformations that we consider @@ -29,7 +25,7 @@ class Expr; /// values). /// /// For more context see Static Analyzer checkers documentation - specifically -/// webkit.UncountedCallArgsChecker checker. Whitelist of transformations: +/// webkit.UncountedCallArgsChecker checker. Allowed list of transformations: /// - constructors of ref-counted types (including factory methods) /// - getters of ref-counted types /// - member overloaded operators 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 97f75135bf92..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,8 +17,8 @@ #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> using namespace clang; using namespace ento; @@ -69,7 +69,7 @@ public: if (shouldSkipDecl(RD)) return; - for (auto Member : RD->fields()) { + for (auto *Member : RD->fields()) { const Type *MemberType = Member->getType().getTypePtrOrNull(); if (!MemberType) continue; @@ -77,9 +77,9 @@ public: if (auto *MemberCXXRD = MemberType->getPointeeCXXRecordDecl()) { // If we don't see the definition we just don't know. if (MemberCXXRD->hasDefinition()) { - llvm::Optional<bool> isRCAble = isRefCountable(MemberCXXRD); - if (isRCAble && *isRCAble) - reportBug(Member, MemberType, MemberCXXRD, RD); + std::optional<bool> isRCAble = isRefCountable(MemberCXXRD); + if (isRCAble && *isRCAble) + reportBug(Member, MemberType, MemberCXXRD, RD); } } } @@ -103,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 a198943c9433..d2b663410580 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp @@ -12,31 +12,21 @@ #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/ExprCXX.h" -#include "llvm/ADT/Optional.h" +#include <optional> -using llvm::Optional; using namespace clang; namespace { -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; } @@ -45,54 +35,70 @@ bool hasPublicRefAndDeref(const CXXRecordDecl *R) { namespace clang { -llvm::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(); if (!T) - return llvm::None; + return std::nullopt; const CXXRecordDecl *R = T->getAsCXXRecordDecl(); if (!R) - return llvm::None; + return std::nullopt; if (!R->hasDefinition()) - return llvm::None; + return std::nullopt; - return hasPublicRefAndDeref(R) ? R : nullptr; + return hasPublicMethodInBaseClass(R, NameToMatch) ? R : nullptr; } -llvm::Optional<bool> isRefCountable(const CXXRecordDecl *R) { +std::optional<bool> isRefCountable(const CXXRecordDecl* R) +{ assert(R); R = R->getDefinition(); if (!R) - return llvm::None; + return std::nullopt; - if (hasPublicRefAndDeref(R)) + 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 = + const auto hasPublicRefInBase = [&AnyInconclusiveBase](const CXXBaseSpecifier *Base, CXXBasePath &) { - Optional<const clang::CXXRecordDecl *> IsRefCountable = - clang::isRefCountable(Base); - if (!IsRefCountable) { + auto hasRefInBase = clang::hasPublicMethodInBase(Base, "ref"); + if (!hasRefInBase) { AnyInconclusiveBase = true; return false; } - return (*IsRefCountable) != nullptr; + return (*hasRefInBase) != nullptr; }; - bool BasesResult = R->lookupInBases(isRefCountableBase, Paths, + hasRef = hasRef || R->lookupInBases(hasPublicRefInBase, Paths, /*LookupInDependent =*/true); if (AnyInconclusiveBase) - return llvm::None; + 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) { @@ -112,19 +118,21 @@ bool isCtorOfRefCounted(const clang::FunctionDecl *F) { || FunctionName == "Identifier"; } -llvm::Optional<bool> isUncounted(const CXXRecordDecl *Class) { +std::optional<bool> isUncounted(const CXXRecordDecl* Class) +{ // Keep isRefCounted first as it's cheaper. if (isRefCounted(Class)) return false; - llvm::Optional<bool> IsRefCountable = isRefCountable(Class); + std::optional<bool> IsRefCountable = isRefCountable(Class); if (!IsRefCountable) - return llvm::None; + return std::nullopt; return (*IsRefCountable); } -llvm::Optional<bool> isUncountedPtr(const Type *T) { +std::optional<bool> isUncountedPtr(const Type* T) +{ assert(T); if (T->isPointerType() || T->isReferenceType()) { @@ -135,7 +143,8 @@ llvm::Optional<bool> isUncountedPtr(const Type *T) { return false; } -Optional<bool> isGetterOfRefCounted(const CXXMethodDecl *M) { +std::optional<bool> isGetterOfRefCounted(const CXXMethodDecl* M) +{ assert(M); if (isa<CXXMethodDecl>(M)) { @@ -183,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 730a59977175..45b21cc09184 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h @@ -10,12 +10,12 @@ #define LLVM_CLANG_ANALYZER_WEBKIT_PTRTYPESEMANTICS_H #include "llvm/ADT/APInt.h" +#include <optional> namespace clang { class CXXBaseSpecifier; class CXXMethodDecl; class CXXRecordDecl; -class Expr; class FunctionDecl; class Type; @@ -26,32 +26,32 @@ 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, None if inconclusive. -llvm::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, None if +/// \returns true if \p Class is ref-countable, false if not, std::nullopt if /// inconclusive. -llvm::Optional<bool> isRefCountable(const clang::CXXRecordDecl *Class); +std::optional<bool> isRefCountable(const clang::CXXRecordDecl* Class); /// \returns true if \p Class is ref-counted, false if not. bool isRefCounted(const clang::CXXRecordDecl *Class); /// \returns true if \p Class is ref-countable AND not ref-counted, false if -/// not, None if inconclusive. -llvm::Optional<bool> isUncounted(const clang::CXXRecordDecl *Class); +/// not, std::nullopt if inconclusive. +std::optional<bool> isUncounted(const clang::CXXRecordDecl* Class); /// \returns true if \p T is either a raw pointer or reference to an uncounted -/// class, false if not, None if inconclusive. -llvm::Optional<bool> isUncountedPtr(const clang::Type *T); +/// class, false if not, std::nullopt if inconclusive. +std::optional<bool> isUncountedPtr(const clang::Type* T); /// \returns true if \p F creates ref-countable object from uncounted parameter, /// false if not. bool isCtorOfRefCounted(const clang::FunctionDecl *F); /// \returns true if \p M is getter of a ref-counted class, false if not. -llvm::Optional<bool> isGetterOfRefCounted(const clang::CXXMethodDecl *Method); +std::optional<bool> isGetterOfRefCounted(const clang::CXXMethodDecl* Method); /// \returns true if \p F is a conversion between ref-countable or ref-counted /// pointer types. diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp index fa9ece217cc0..d879c110b75d 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp @@ -14,6 +14,7 @@ #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" +#include <optional> using namespace clang; using namespace ento; @@ -76,15 +77,53 @@ public: (AccSpec == AS_none && RD->isClass())) return false; - llvm::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 d70bd9489d2c..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,7 @@ #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; using namespace ento; @@ -68,8 +68,7 @@ public: if (auto *F = CE->getDirectCallee()) { // Skip the first argument for overloaded member operators (e. g. lambda // or std::function call operator). - unsigned ArgIdx = - isa<CXXOperatorCallExpr>(CE) && dyn_cast_or_null<CXXMethodDecl>(F); + unsigned ArgIdx = isa<CXXOperatorCallExpr>(CE) && isa_and_nonnull<CXXMethodDecl>(F); for (auto P = F->param_begin(); // FIXME: Also check variadic function parameters. @@ -86,7 +85,7 @@ public: continue; // FIXME? Should we bail? // FIXME: more complex types (arrays, references to raw pointers, etc) - Optional<bool> IsUncounted = isUncountedPtr(ArgType); + std::optional<bool> IsUncounted = isUncountedPtr(ArgType); if (!IsUncounted || !(*IsUncounted)) continue; @@ -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/UncountedLambdaCapturesChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp index deebbd603b2c..a226a01ec0a5 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp @@ -14,6 +14,7 @@ #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" +#include <optional> using namespace clang; using namespace ento; @@ -24,7 +25,7 @@ class UncountedLambdaCapturesChecker private: BugType Bug{this, "Lambda capture of uncounted variable", "WebKit coding guidelines"}; - mutable BugReporter *BR; + mutable BugReporter *BR = nullptr; public: void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR, @@ -57,18 +58,18 @@ public: void visitLambdaExpr(LambdaExpr *L) const { for (const LambdaCapture &C : L->captures()) { if (C.capturesVariable()) { - VarDecl *CapturedVar = C.getCapturedVar(); + ValueDecl *CapturedVar = C.getCapturedVar(); if (auto *CapturedVarType = CapturedVar->getType().getTypePtrOrNull()) { - Optional<bool> IsUncountedPtr = isUncountedPtr(CapturedVarType); - if (IsUncountedPtr && *IsUncountedPtr) { - reportBug(C, CapturedVar, CapturedVarType); - } + std::optional<bool> IsUncountedPtr = isUncountedPtr(CapturedVarType); + if (IsUncountedPtr && *IsUncountedPtr) { + reportBug(C, CapturedVar, CapturedVarType); + } } } } } - void reportBug(const LambdaCapture &Capture, VarDecl *CapturedVar, + void reportBug(const LambdaCapture &Capture, ValueDecl *CapturedVar, const Type *T) const { assert(CapturedVar); diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLocalVarsChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLocalVarsChecker.cpp index 7e86f28cb70f..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,7 @@ #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; using namespace ento; @@ -169,7 +169,7 @@ public: if (!ArgType) return; - Optional<bool> IsUncountedPtr = isUncountedPtr(ArgType); + std::optional<bool> IsUncountedPtr = isUncountedPtr(ArgType); if (IsUncountedPtr && *IsUncountedPtr) { const Expr *const InitExpr = V->getInit(); if (!InitExpr) diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Yaml.h b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Yaml.h index ec612dde3b8b..b2d17420686e 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Yaml.h +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Yaml.h @@ -17,6 +17,7 @@ #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "llvm/Support/VirtualFileSystem.h" #include "llvm/Support/YAMLTraits.h" +#include <optional> namespace clang { namespace ento { @@ -25,20 +26,20 @@ namespace ento { /// template parameter must have a yaml MappingTraits. /// Emit diagnostic error in case of any failure. template <class T, class Checker> -llvm::Optional<T> getConfiguration(CheckerManager &Mgr, Checker *Chk, - StringRef Option, StringRef ConfigFile) { +std::optional<T> getConfiguration(CheckerManager &Mgr, Checker *Chk, + StringRef Option, StringRef ConfigFile) { if (ConfigFile.trim().empty()) - return None; + return std::nullopt; llvm::vfs::FileSystem *FS = llvm::vfs::getRealFileSystem().get(); llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Buffer = 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) + "'"); - return None; + return std::nullopt; } llvm::yaml::Input Input(Buffer.get()->getBuffer()); @@ -48,7 +49,7 @@ llvm::Optional<T> getConfiguration(CheckerManager &Mgr, Checker *Chk, if (std::error_code ec = Input.error()) { Mgr.reportInvalidCheckerOptionValue(Chk, Option, "a valid yaml file: " + ec.message()); - return None; + return std::nullopt; } return Config; @@ -57,4 +58,4 @@ llvm::Optional<T> getConfiguration(CheckerManager &Mgr, Checker *Chk, } // namespace ento } // namespace clang -#endif // LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MOVE_H +#endif // LLVM_CLANG_LIB_STATICANALYZER_CHECKER_YAML_H diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/cert/InvalidPtrChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/cert/InvalidPtrChecker.cpp new file mode 100644 index 000000000000..b2947f590c4e --- /dev/null +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/cert/InvalidPtrChecker.cpp @@ -0,0 +1,357 @@ +//== InvalidPtrChecker.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 InvalidPtrChecker which finds usages of possibly +// invalidated pointer. +// CERT SEI Rules ENV31-C and ENV34-C +// For more information see: +// https://wiki.sei.cmu.edu/confluence/x/8tYxBQ +// https://wiki.sei.cmu.edu/confluence/x/5NUxBQ +//===----------------------------------------------------------------------===// + +#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" + +using namespace clang; +using namespace ento; + +namespace { + +class InvalidPtrChecker + : public Checker<check::Location, check::BeginFunction, check::PostCall> { +private: + // 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; + + using HandlerFn = void (InvalidPtrChecker::*)(const CallEvent &Call, + 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}, + {{{"putenv"}, 1}, &InvalidPtrChecker::EnvpInvalidatingCall}, + {{{"_putenv_s"}, 2}, &InvalidPtrChecker::EnvpInvalidatingCall}, + {{{"_wputenv_s"}, 2}, &InvalidPtrChecker::EnvpInvalidatingCall}, + }; + + void postPreviousReturnInvalidatingCall(const CallEvent &Call, + CheckerContext &C) const; + + // SEI CERT ENV34-C + const CallDescriptionMap<HandlerFn> PreviousCallInvalidatingFunctions = { + {{{"setlocale"}, 2}, + &InvalidPtrChecker::postPreviousReturnInvalidatingCall}, + {{{"strerror"}, 1}, + &InvalidPtrChecker::postPreviousReturnInvalidatingCall}, + {{{"localeconv"}, 0}, + &InvalidPtrChecker::postPreviousReturnInvalidatingCall}, + {{{"asctime"}, 1}, + &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; + + // Handle functions in EnvpInvalidatingFunctions, that invalidate environment + // pointer from 'main()' + // Handle functions in PreviousCallInvalidatingFunctions. + // Also, check if invalidated region is passed to a + // conservatively evaluated function call as an argument. + void checkPostCall(const CallEvent &Call, CheckerContext &C) const; + + // 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 + +// Set of memory regions that were invalidated +REGISTER_SET_WITH_PROGRAMSTATE(InvalidMemoryRegions, const MemRegion *) + +// Stores the region of the environment pointer of 'main' (if present). +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 { + // This callevent invalidates all previously generated pointers to the + // environment. + ProgramStateRef State = C.getState(); + if (const MemRegion *MainEnvPtr = State->get<MainEnvPtrRegion>()) + State = State->add<InvalidMemoryRegions>(MainEnvPtr); + for (const MemRegion *EnvPtr : State->get<GetenvEnvPtrRegions>()) + State = State->add<InvalidMemoryRegions>(EnvPtr); + + StringRef FunctionName = Call.getCalleeIdentifier()->getName(); + const NoteTag *InvalidationNote = + createEnvInvalidationNote(C, State, FunctionName); + + C.addTransition(State, InvalidationNote); +} + +void InvalidPtrChecker::postPreviousReturnInvalidatingCall( + const CallEvent &Call, CheckerContext &C) const { + ProgramStateRef State = C.getState(); + + const NoteTag *Note = nullptr; + const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl()); + // Invalidate the region of the previously returned pointer - if there was + // one. + if (const MemRegion *const *Reg = State->get<PreviousCallResultMap>(FD)) { + const MemRegion *PrevReg = *Reg; + State = State->add<InvalidMemoryRegions>(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); + Out << "' call may invalidate the result of the previous " << '\''; + FD->getNameForDiagnostic(Out, FD->getASTContext().getLangOpts(), true); + Out << '\''; + }); + } + + const LocationContext *LCtx = C.getLocationContext(); + const auto *CE = cast<CallExpr>(Call.getOriginExpr()); + + // Function call will return a pointer to the new symbolic region. + DefinedOrUnknownSVal RetVal = C.getSValBuilder().conjureSymbolVal( + CE, LCtx, CE->getType(), C.blockCount()); + State = State->BindExpr(CE, LCtx, RetVal); + + const auto *SymRegOfRetVal = + dyn_cast_or_null<SymbolicRegion>(RetVal.getAsRegion()); + if (!SymRegOfRetVal) + return; + + // Remember to this region. + const MemRegion *MR = SymRegOfRetVal->getBaseRegion(); + State = State->set<PreviousCallResultMap>(FD, MR); + + ExplodedNode *Node = C.addTransition(State, Note); + 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"; + }); + + C.addTransition(State, Node, PreviousCallNote); +} + +// TODO: This seems really ugly. Simplify this. +static const MemRegion *findInvalidatedSymbolicBase(ProgramStateRef State, + const MemRegion *Reg) { + while (Reg) { + if (State->contains<InvalidMemoryRegions>(Reg)) + return Reg; + const auto *SymBase = Reg->getSymbolicBase(); + if (!SymBase) + break; + const auto *SRV = dyn_cast<SymbolRegionValue>(SymBase->getSymbol()); + if (!SRV) + break; + Reg = SRV->getRegion(); + if (const auto *VarReg = dyn_cast<VarRegion>(SRV->getRegion())) + Reg = VarReg; + } + return nullptr; +} + +// Handle functions in EnvpInvalidatingFunctions, that invalidate environment +// pointer from 'main()' Also, check if invalidated region is passed to a +// 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); + + // Check if function invalidates the result of previous 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; + + for (unsigned I = 0, NumArgs = Call.getNumArgs(); I < NumArgs; ++I) { + + if (const auto *SR = dyn_cast_or_null<SymbolicRegion>( + Call.getArgSVal(I).getAsRegion())) { + if (const MemRegion *InvalidatedSymbolicBase = + findInvalidatedSymbolicBase(State, SR)) { + ExplodedNode *ErrorNode = C.generateNonFatalErrorNode(); + if (!ErrorNode) + return; + + SmallString<256> Msg; + llvm::raw_svector_ostream Out(Msg); + Out << "use of invalidated pointer '"; + Call.getArgExpr(I)->printPretty(Out, /*Helper=*/nullptr, + C.getASTContext().getPrintingPolicy()); + Out << "' in a function call"; + + auto Report = std::make_unique<PathSensitiveBugReport>( + InvalidPtrBugType, Out.str(), ErrorNode); + Report->markInteresting(InvalidatedSymbolicBase); + Report->addRange(Call.getArgSourceRange(I)); + C.emitReport(std::move(Report)); + } + } + } +} + +// Obtain the environment pointer from 'main()', if present. +void InvalidPtrChecker::checkBeginFunction(CheckerContext &C) const { + if (!C.inTopFrame()) + return; + + const auto *FD = dyn_cast<FunctionDecl>(C.getLocationContext()->getDecl()); + if (!FD || FD->param_size() != 3 || !FD->isMain()) + return; + + ProgramStateRef State = C.getState(); + const MemRegion *EnvpReg = + State->getRegion(FD->parameters()[2], C.getLocationContext()); + + // Save the memory region pointed by the environment pointer parameter of + // 'main'. + C.addTransition(State->set<MainEnvPtrRegion>(EnvpReg)); +} + +// Check if invalidated region is being dereferenced. +void InvalidPtrChecker::checkLocation(SVal Loc, bool isLoad, const Stmt *S, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + + // Ignore memory operations involving 'non-invalidated' locations. + const MemRegion *InvalidatedSymbolicBase = + findInvalidatedSymbolicBase(State, Loc.getAsRegion()); + if (!InvalidatedSymbolicBase) + return; + + ExplodedNode *ErrorNode = C.generateNonFatalErrorNode(); + if (!ErrorNode) + return; + + auto Report = std::make_unique<PathSensitiveBugReport>( + InvalidPtrBugType, "dereferencing an invalid pointer", ErrorNode); + Report->markInteresting(InvalidatedSymbolicBase); + C.emitReport(std::move(Report)); +} + +void ento::registerInvalidPtrChecker(CheckerManager &Mgr) { + auto *Checker = Mgr.registerChecker<InvalidPtrChecker>(); + Checker->InvalidatingGetEnv = + Mgr.getAnalyzerOptions().getCheckerBooleanOption(Checker, + "InvalidatingGetEnv"); +} + +bool ento::shouldRegisterInvalidPtrChecker(const CheckerManager &) { + return true; +} diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/cert/PutenvWithAutoChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/cert/PutenvWithAutoChecker.cpp index 1c67bbd77ec8..eae162cda693 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/cert/PutenvWithAutoChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/cert/PutenvWithAutoChecker.cpp @@ -17,6 +17,7 @@ #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/MemRegion.h" @@ -29,7 +30,7 @@ class PutenvWithAutoChecker : public Checker<check::PostCall> { private: BugType BT{this, "'putenv' function should not be called with auto variables", categories::SecurityError}; - const CallDescription Putenv{"putenv", 1}; + const CallDescription Putenv{{"putenv"}, 1}; public: void checkPostCall(const CallEvent &Call, CheckerContext &C) const; @@ -38,7 +39,7 @@ public: void PutenvWithAutoChecker::checkPostCall(const CallEvent &Call, CheckerContext &C) const { - if (!Call.isCalled(Putenv)) + if (!Putenv.matches(Call)) return; SVal ArgV = Call.getArgSVal(0); |