diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2021-06-13 19:31:46 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2021-06-13 19:37:19 +0000 |
commit | e8d8bef961a50d4dc22501cde4fb9fb0be1b2532 (patch) | |
tree | 94f04805f47bb7c59ae29690d8952b6074fff602 /contrib/llvm-project/clang/lib/StaticAnalyzer | |
parent | bb130ff39747b94592cb26d71b7cb097b9a4ea6b (diff) | |
parent | b60736ec1405bb0a8dd40989f67ef4c93da068ab (diff) | |
download | src-e8d8bef961a50d4dc22501cde4fb9fb0be1b2532.tar.gz src-e8d8bef961a50d4dc22501cde4fb9fb0be1b2532.zip |
Diffstat (limited to 'contrib/llvm-project/clang/lib/StaticAnalyzer')
62 files changed, 4474 insertions, 1838 deletions
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp index 918c6e361381..a86a410ebcbc 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp @@ -978,8 +978,7 @@ void ObjCLoopChecker::checkPostStmt(const ObjCForCollectionStmt *FCS, ProgramStateRef State = C.getState(); // Check if this is the branch for the end of the loop. - SVal CollectionSentinel = C.getSVal(FCS); - if (CollectionSentinel.isZeroConstant()) { + if (!ExprEngine::hasMoreIteration(State, FCS, C.getLocationContext())) { if (!alreadyExecutedAtLeastOneLoopIteration(C.getPredecessor(), FCS)) State = assumeCollectionNonEmpty(C, State, FCS, /*Assumption*/false); diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp index 528f68c6c429..131c1345af99 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp @@ -119,10 +119,10 @@ static const NoteTag *getNoteTag(CheckerContext &C, Out << "Assuming "; if (const auto *DRE = dyn_cast<DeclRefExpr>(Object)) { - Out << '\'' << DRE->getDecl()->getNameAsString() << '\''; + Out << '\'' << DRE->getDecl()->getDeclName() << '\''; } else if (const auto *ME = dyn_cast<MemberExpr>(Object)) { Out << (IsKnownCast ? "Field '" : "field '") - << ME->getMemberDecl()->getNameAsString() << '\''; + << ME->getMemberDecl()->getDeclName() << '\''; } else { Out << (IsKnownCast ? "The object" : "the object"); } diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp index 13836f08a61e..78b3c209ad6b 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp @@ -406,7 +406,7 @@ ProgramStateRef ObjCDeallocChecker::evalAssume(ProgramStateRef State, SVal Cond, if (State->get<UnreleasedIvarMap>().isEmpty()) return State; - auto *CondBSE = dyn_cast_or_null<BinarySymExpr>(Cond.getAsSymExpr()); + auto *CondBSE = dyn_cast_or_null<BinarySymExpr>(Cond.getAsSymbol()); if (!CondBSE) return State; diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp index 03b7cbd1c833..7cdd78b8adfb 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp @@ -131,21 +131,21 @@ bool ento::shouldRegisterLiveVariablesDumper(const CheckerManager &mgr) { //===----------------------------------------------------------------------===// namespace { -class LiveStatementsDumper : public Checker<check::ASTCodeBody> { +class LiveExpressionsDumper : public Checker<check::ASTCodeBody> { public: void checkASTCodeBody(const Decl *D, AnalysisManager& Mgr, BugReporter &BR) const { if (LiveVariables *L = Mgr.getAnalysis<RelaxedLiveVariables>(D)) - L->dumpStmtLiveness(Mgr.getSourceManager()); + L->dumpExprLiveness(Mgr.getSourceManager()); } }; } -void ento::registerLiveStatementsDumper(CheckerManager &mgr) { - mgr.registerChecker<LiveStatementsDumper>(); +void ento::registerLiveExpressionsDumper(CheckerManager &mgr) { + mgr.registerChecker<LiveExpressionsDumper>(); } -bool ento::shouldRegisterLiveStatementsDumper(const CheckerManager &mgr) { +bool ento::shouldRegisterLiveExpressionsDumper(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 2411f0e2d058..adfc2f8cb8fe 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp @@ -30,10 +30,14 @@ class DereferenceChecker : public Checker< check::Location, check::Bind, EventDispatcher<ImplicitNullDerefEvent> > { - mutable std::unique_ptr<BuiltinBug> BT_null; - mutable std::unique_ptr<BuiltinBug> BT_undef; + enum DerefKind { NullPointer, UndefinedPointerValue }; - void reportBug(ProgramStateRef State, const Stmt *S, CheckerContext &C) const; + BugType BT_Null{this, "Dereference of null pointer", categories::LogicError}; + BugType BT_Undef{this, "Dereference of undefined pointer value", + categories::LogicError}; + + void reportBug(DerefKind K, ProgramStateRef State, const Stmt *S, + CheckerContext &C) const; public: void checkLocation(SVal location, bool isLoad, const Stmt* S, @@ -116,18 +120,29 @@ static bool isDeclRefExprToReference(const Expr *E) { return false; } -void DereferenceChecker::reportBug(ProgramStateRef State, const Stmt *S, - CheckerContext &C) const { +void DereferenceChecker::reportBug(DerefKind K, ProgramStateRef State, + const Stmt *S, CheckerContext &C) const { + const BugType *BT = nullptr; + llvm::StringRef DerefStr1; + llvm::StringRef DerefStr2; + switch (K) { + case DerefKind::NullPointer: + BT = &BT_Null; + DerefStr1 = " results in a null pointer dereference"; + DerefStr2 = " results in a dereference of a null pointer"; + break; + case DerefKind::UndefinedPointerValue: + BT = &BT_Undef; + DerefStr1 = " results in an undefined pointer dereference"; + DerefStr2 = " results in a dereference of an undefined pointer value"; + break; + }; + // Generate an error node. ExplodedNode *N = C.generateErrorNode(State); if (!N) return; - // We know that 'location' cannot be non-null. This is what - // we call an "explicit" null dereference. - if (!BT_null) - BT_null.reset(new BuiltinBug(this, "Dereference of null pointer")); - SmallString<100> buf; llvm::raw_svector_ostream os(buf); @@ -139,7 +154,7 @@ void DereferenceChecker::reportBug(ProgramStateRef State, const Stmt *S, const ArraySubscriptExpr *AE = cast<ArraySubscriptExpr>(S); AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(), State.get(), N->getLocationContext()); - os << " results in a null pointer dereference"; + os << DerefStr1; break; } case Stmt::OMPArraySectionExprClass: { @@ -147,11 +162,11 @@ void DereferenceChecker::reportBug(ProgramStateRef State, const Stmt *S, const OMPArraySectionExpr *AE = cast<OMPArraySectionExpr>(S); AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(), State.get(), N->getLocationContext()); - os << " results in a null pointer dereference"; + os << DerefStr1; break; } case Stmt::UnaryOperatorClass: { - os << "Dereference of null pointer"; + os << BT->getDescription(); const UnaryOperator *U = cast<UnaryOperator>(S); AddDerefSource(os, Ranges, U->getSubExpr()->IgnoreParens(), State.get(), N->getLocationContext(), true); @@ -160,8 +175,7 @@ void DereferenceChecker::reportBug(ProgramStateRef State, const Stmt *S, case Stmt::MemberExprClass: { const MemberExpr *M = cast<MemberExpr>(S); if (M->isArrow() || isDeclRefExprToReference(M->getBase())) { - os << "Access to field '" << M->getMemberNameInfo() - << "' results in a dereference of a null pointer"; + os << "Access to field '" << M->getMemberNameInfo() << "'" << DerefStr2; AddDerefSource(os, Ranges, M->getBase()->IgnoreParenCasts(), State.get(), N->getLocationContext(), true); } @@ -169,8 +183,7 @@ void DereferenceChecker::reportBug(ProgramStateRef State, const Stmt *S, } case Stmt::ObjCIvarRefExprClass: { const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(S); - os << "Access to instance variable '" << *IV->getDecl() - << "' results in a dereference of a null pointer"; + os << "Access to instance variable '" << *IV->getDecl() << "'" << DerefStr2; AddDerefSource(os, Ranges, IV->getBase()->IgnoreParenCasts(), State.get(), N->getLocationContext(), true); break; @@ -180,7 +193,7 @@ void DereferenceChecker::reportBug(ProgramStateRef State, const Stmt *S, } auto report = std::make_unique<PathSensitiveBugReport>( - *BT_null, buf.empty() ? BT_null->getDescription() : StringRef(buf), N); + *BT, buf.empty() ? BT->getDescription() : StringRef(buf), N); bugreporter::trackExpressionValue(N, bugreporter::getDerefExpr(S), *report); @@ -195,16 +208,9 @@ void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S, CheckerContext &C) const { // Check for dereference of an undefined value. if (l.isUndef()) { - if (ExplodedNode *N = C.generateErrorNode()) { - if (!BT_undef) - BT_undef.reset( - new BuiltinBug(this, "Dereference of undefined pointer value")); - - auto report = std::make_unique<PathSensitiveBugReport>( - *BT_undef, BT_undef->getDescription(), N); - bugreporter::trackExpressionValue(N, bugreporter::getDerefExpr(S), *report); - C.emitReport(std::move(report)); - } + const Expr *DerefExpr = getDereferenceExpr(S); + if (!suppressReport(DerefExpr)) + reportBug(DerefKind::UndefinedPointerValue, C.getState(), DerefExpr, C); return; } @@ -219,12 +225,13 @@ void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S, ProgramStateRef notNullState, nullState; std::tie(notNullState, nullState) = state->assume(location); - // The explicit NULL case. if (nullState) { if (!notNullState) { + // 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)) { - reportBug(nullState, expr, C); + reportBug(DerefKind::NullPointer, nullState, expr, C); return; } } @@ -266,7 +273,7 @@ void DereferenceChecker::checkBind(SVal L, SVal V, const Stmt *S, if (!StNonNull) { const Expr *expr = getDereferenceExpr(S, /*IsBind=*/true); if (!suppressReport(expr)) { - reportBug(StNull, expr, C); + reportBug(DerefKind::NullPointer, StNull, expr, C); return; } } diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp index 4225d890c47a..c0167b53ae26 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp @@ -7,11 +7,11 @@ //===----------------------------------------------------------------------===// #include "Taint.h" +#include "clang/Analysis/IssueHash.h" #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/StaticAnalyzer/Checkers/SValExplainer.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" -#include "clang/StaticAnalyzer/Core/IssueHash.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicSize.h" @@ -326,7 +326,7 @@ void ExprInspectionChecker::analyzerHashDump(const CallExpr *CE, const SourceManager &SM = C.getSourceManager(); FullSourceLoc FL(CE->getArg(0)->getBeginLoc(), SM); std::string HashContent = - GetIssueString(SM, FL, getCheckerName().getName(), "Category", + getIssueString(FL, getCheckerName().getName(), "Category", C.getLocationContext()->getDecl(), Opts); reportBug(HashContent, C); diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/FuchsiaHandleChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/FuchsiaHandleChecker.cpp index fc35082705fa..e3f4be0726c8 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/FuchsiaHandleChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/FuchsiaHandleChecker.cpp @@ -20,29 +20,39 @@ // Art: // // -// +-+---------v-+ +------------+ -// acquire_func succeeded | | Escape | | -// +-----------------> Allocated +---------> Escaped <--+ -// | | | | | | -// | +-----+------++ +------------+ | -// | | | | -// | release_func | +--+ | -// | | | handle +--------+ | -// | | | dies | | | -// | +----v-----+ +---------> Leaked | | -// | | | |(REPORT)| | -// +----------+--+ | Released | Escape +--------+ | -// | | | +---------------------------+ -// | Not tracked <--+ +----+---+-+ -// | | | | | As argument by value -// +------+------+ | release_func | +------+ in function call -// | | | | or by reference in -// | | | | use_func call -// +---------+ +----v-----+ | +-----------+ -// acquire_func failed | Double | +-----> Use after | -// | released | | released | -// | (REPORT) | | (REPORT) | -// +----------+ +-----------+ +// +-------------+ +------------+ +// acquire_func succeeded | | Escape | | +// +-----------------> Allocated +---------> Escaped <--+ +// | | | | | | +// | +-----+------++ +------------+ | +// | | | | +// acquire_func | release_func | +--+ | +// failed | | | handle +--------+ | +// +---------+ | | | dies | | | +// | | | +----v-----+ +---------> Leaked | | +// | | | | | |(REPORT)| | +// | +----------+--+ | Released | Escape +--------+ | +// | | | | +---------------------------+ +// +--> Not tracked | +----+---+-+ +// | | | | As argument by value +// +----------+--+ release_func | +------+ in function call +// | | | or by reference in +// | | | use_func call +// unowned | +----v-----+ | +-----------+ +// acquire_func | | Double | +-----> Use after | +// succeeded | | released | | released | +// | | (REPORT) | | (REPORT) | +// +---------------+ +----------+ +-----------+ +// | Allocated | +// | Unowned | release_func +// | +---------+ +// +---------------+ | +// | +// +-----v----------+ +// | Release of | +// | unowned handle | +// | (REPORT) | +// +----------------+ // // acquire_func represents the functions or syscalls that may acquire a handle. // release_func represents the functions or syscalls that may release a handle. @@ -53,7 +63,7 @@ // // Note that, the analyzer does not always know for sure if a function failed // or succeeded. In those cases we use the state MaybeAllocated. -// Thus, the diagramm above captures the intent, not implementation details. +// Thus, the diagram above captures the intent, not implementation details. // // Due to the fact that the number of handle related syscalls in Fuchsia // is large, we adopt the annotation attributes to descript syscalls' @@ -102,7 +112,7 @@ static const StringRef ErrorTypeName = "zx_status_t"; class HandleState { private: - enum class Kind { MaybeAllocated, Allocated, Released, Escaped } K; + enum class Kind { MaybeAllocated, Allocated, Released, Escaped, Unowned } K; SymbolRef ErrorSym; HandleState(Kind K, SymbolRef ErrorSym) : K(K), ErrorSym(ErrorSym) {} @@ -114,6 +124,7 @@ public: bool maybeAllocated() const { return K == Kind::MaybeAllocated; } bool isReleased() const { return K == Kind::Released; } bool isEscaped() const { return K == Kind::Escaped; } + bool isUnowned() const { return K == Kind::Unowned; } static HandleState getMaybeAllocated(SymbolRef ErrorSym) { return HandleState(Kind::MaybeAllocated, ErrorSym); @@ -131,6 +142,9 @@ public: static HandleState getEscaped() { return HandleState(Kind::Escaped, nullptr); } + static HandleState getUnowned() { + return HandleState(Kind::Unowned, nullptr); + } SymbolRef getErrorSym() const { return ErrorSym; } @@ -149,6 +163,7 @@ public: CASE(Kind::Allocated) CASE(Kind::Released) CASE(Kind::Escaped) + CASE(Kind::Unowned) } if (ErrorSym) { OS << " ErrorSym: "; @@ -163,6 +178,11 @@ template <typename Attr> static bool hasFuchsiaAttr(const Decl *D) { return D->hasAttr<Attr>() && D->getAttr<Attr>()->getHandleType() == "Fuchsia"; } +template <typename Attr> static bool hasFuchsiaUnownedAttr(const Decl *D) { + return D->hasAttr<Attr>() && + D->getAttr<Attr>()->getHandleType() == "FuchsiaUnowned"; +} + class FuchsiaHandleChecker : public Checker<check::PostCall, check::PreCall, check::DeadSymbols, check::PointerEscape, eval::Assume> { @@ -172,6 +192,8 @@ class FuchsiaHandleChecker "Fuchsia Handle Error"}; BugType UseAfterReleaseBugType{this, "Fuchsia handle use after release", "Fuchsia Handle Error"}; + BugType ReleaseUnownedBugType{ + this, "Fuchsia handle release of unowned handle", "Fuchsia Handle Error"}; public: void checkPreCall(const CallEvent &Call, CheckerContext &C) const; @@ -190,6 +212,9 @@ public: void reportDoubleRelease(SymbolRef HandleSym, const SourceRange &Range, CheckerContext &C) const; + void reportUnownedRelease(SymbolRef HandleSym, const SourceRange &Range, + CheckerContext &C) const; + void reportUseAfterFree(SymbolRef HandleSym, const SourceRange &Range, CheckerContext &C) const; @@ -226,32 +251,70 @@ static const ExplodedNode *getAcquireSite(const ExplodedNode *N, SymbolRef Sym, return nullptr; } -/// Returns the symbols extracted from the argument or null if it cannot be -/// found. -static SymbolRef getFuchsiaHandleSymbol(QualType QT, SVal Arg, - ProgramStateRef State) { +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) + Symbols.push_back(S); + return true; + } + + SmallVector<SymbolRef, 1024> GetSymbols() { return Symbols; } + +private: + SmallVector<SymbolRef, 1024> Symbols; + ProgramStateRef State; +}; +} // end anonymous namespace + +/// Returns the symbols extracted from the argument or empty vector if it cannot +/// be found. It is unlikely to have over 1024 symbols in one argument. +static SmallVector<SymbolRef, 1024> +getFuchsiaHandleSymbols(QualType QT, SVal Arg, ProgramStateRef State) { int PtrToHandleLevel = 0; while (QT->isAnyPointerType() || QT->isReferenceType()) { ++PtrToHandleLevel; QT = QT->getPointeeType(); } + if (QT->isStructureType()) { + // If we see a structure, see if there is any handle referenced by the + // structure. + FuchsiaHandleSymbolVisitor Visitor(State); + State->scanReachableSymbols(Arg, Visitor); + return Visitor.GetSymbols(); + } if (const auto *HandleType = QT->getAs<TypedefType>()) { if (HandleType->getDecl()->getName() != HandleTypeName) - return nullptr; - if (PtrToHandleLevel > 1) { + return {}; + if (PtrToHandleLevel > 1) // Not supported yet. - return nullptr; - } + return {}; if (PtrToHandleLevel == 0) { - return Arg.getAsSymbol(); + SymbolRef Sym = Arg.getAsSymbol(); + if (Sym) { + return {Sym}; + } else { + return {}; + } } else { assert(PtrToHandleLevel == 1); - if (Optional<Loc> ArgLoc = Arg.getAs<Loc>()) - return State->getSVal(*ArgLoc).getAsSymbol(); + if (Optional<Loc> ArgLoc = Arg.getAs<Loc>()) { + SymbolRef Sym = State->getSVal(*ArgLoc).getAsSymbol(); + if (Sym) { + return {Sym}; + } else { + return {}; + } + } } } - return nullptr; + return {}; } void FuchsiaHandleChecker::checkPreCall(const CallEvent &Call, @@ -273,31 +336,27 @@ void FuchsiaHandleChecker::checkPreCall(const CallEvent &Call, if (Arg >= FuncDecl->getNumParams()) break; const ParmVarDecl *PVD = FuncDecl->getParamDecl(Arg); - SymbolRef Handle = - getFuchsiaHandleSymbol(PVD->getType(), Call.getArgSVal(Arg), State); - if (!Handle) - continue; + SmallVector<SymbolRef, 1024> Handles = + getFuchsiaHandleSymbols(PVD->getType(), Call.getArgSVal(Arg), State); // Handled in checkPostCall. if (hasFuchsiaAttr<ReleaseHandleAttr>(PVD) || hasFuchsiaAttr<AcquireHandleAttr>(PVD)) continue; - const HandleState *HState = State->get<HStateMap>(Handle); - if (!HState || HState->isEscaped()) - continue; + for (SymbolRef Handle : Handles) { + const HandleState *HState = State->get<HStateMap>(Handle); + if (!HState || HState->isEscaped()) + continue; - if (hasFuchsiaAttr<UseHandleAttr>(PVD) || PVD->getType()->isIntegerType()) { - if (HState->isReleased()) { - reportUseAfterFree(Handle, Call.getArgSourceRange(Arg), C); - return; + if (hasFuchsiaAttr<UseHandleAttr>(PVD) || + PVD->getType()->isIntegerType()) { + if (HState->isReleased()) { + reportUseAfterFree(Handle, Call.getArgSourceRange(Arg), C); + return; + } } } - if (!hasFuchsiaAttr<UseHandleAttr>(PVD) && - PVD->getType()->isIntegerType()) { - // Working around integer by-value escapes. - State = State->set<HStateMap>(Handle, HandleState::getEscaped()); - } } C.addTransition(State); } @@ -308,6 +367,10 @@ void FuchsiaHandleChecker::checkPostCall(const CallEvent &Call, if (!FuncDecl) return; + // If we analyzed the function body, then ignore the annotations. + if (C.wasInlined) + return; + ProgramStateRef State = C.getState(); std::vector<std::function<std::string(BugReport & BR)>> Notes; @@ -324,7 +387,7 @@ void FuchsiaHandleChecker::checkPostCall(const CallEvent &Call, if (auto IsInteresting = PathBR->getInterestingnessKind(RetSym)) { std::string SBuf; llvm::raw_string_ostream OS(SBuf); - OS << "Function '" << FuncDecl->getNameAsString() + OS << "Function '" << FuncDecl->getDeclName() << "' returns an open handle"; return OS.str(); } else @@ -332,6 +395,21 @@ void FuchsiaHandleChecker::checkPostCall(const CallEvent &Call, }); State = State->set<HStateMap>(RetSym, HandleState::getMaybeAllocated(nullptr)); + } else if (hasFuchsiaUnownedAttr<AcquireHandleAttr>(FuncDecl)) { + // Function returns an unowned handle + 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)) { + std::string SBuf; + llvm::raw_string_ostream OS(SBuf); + OS << "Function '" << FuncDecl->getDeclName() + << "' returns an unowned handle"; + return OS.str(); + } else + return ""; + }); + State = State->set<HStateMap>(RetSym, HandleState::getUnowned()); } for (unsigned Arg = 0; Arg < Call.getNumArgs(); ++Arg) { @@ -339,63 +417,88 @@ void FuchsiaHandleChecker::checkPostCall(const CallEvent &Call, break; const ParmVarDecl *PVD = FuncDecl->getParamDecl(Arg); unsigned ParamDiagIdx = PVD->getFunctionScopeIndex() + 1; - SymbolRef Handle = - getFuchsiaHandleSymbol(PVD->getType(), Call.getArgSVal(Arg), State); - if (!Handle) - continue; + SmallVector<SymbolRef, 1024> Handles = + getFuchsiaHandleSymbols(PVD->getType(), Call.getArgSVal(Arg), State); - const HandleState *HState = State->get<HStateMap>(Handle); - if (HState && HState->isEscaped()) - continue; - if (hasFuchsiaAttr<ReleaseHandleAttr>(PVD)) { - if (HState && HState->isReleased()) { - reportDoubleRelease(Handle, Call.getArgSourceRange(Arg), C); - return; - } else { + for (SymbolRef Handle : Handles) { + const HandleState *HState = State->get<HStateMap>(Handle); + if (HState && HState->isEscaped()) + continue; + if (hasFuchsiaAttr<ReleaseHandleAttr>(PVD)) { + if (HState && HState->isReleased()) { + reportDoubleRelease(Handle, Call.getArgSourceRange(Arg), C); + return; + } else if (HState && HState->isUnowned()) { + reportUnownedRelease(Handle, Call.getArgSourceRange(Arg), C); + return; + } else { + Notes.push_back([Handle, ParamDiagIdx](BugReport &BR) -> std::string { + auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR); + if (auto IsInteresting = PathBR->getInterestingnessKind(Handle)) { + std::string SBuf; + llvm::raw_string_ostream OS(SBuf); + OS << "Handle released through " << ParamDiagIdx + << llvm::getOrdinalSuffix(ParamDiagIdx) << " parameter"; + return OS.str(); + } else + return ""; + }); + State = State->set<HStateMap>(Handle, HandleState::getReleased()); + } + } 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)) { std::string SBuf; llvm::raw_string_ostream OS(SBuf); - OS << "Handle released through " << ParamDiagIdx + OS << "Handle allocated through " << ParamDiagIdx << llvm::getOrdinalSuffix(ParamDiagIdx) << " parameter"; return OS.str(); } else return ""; }); - State = State->set<HStateMap>(Handle, HandleState::getReleased()); + State = State->set<HStateMap>( + Handle, HandleState::getMaybeAllocated(ResultSymbol)); + } 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)) { + std::string SBuf; + llvm::raw_string_ostream OS(SBuf); + OS << "Unowned handle allocated through " << ParamDiagIdx + << llvm::getOrdinalSuffix(ParamDiagIdx) << " parameter"; + return OS.str(); + } else + return ""; + }); + State = State->set<HStateMap>(Handle, HandleState::getUnowned()); + } else if (!hasFuchsiaAttr<UseHandleAttr>(PVD) && + PVD->getType()->isIntegerType()) { + // Working around integer by-value escapes. + // The by-value escape would not be captured in checkPointerEscape. + // If the function was not analyzed (otherwise wasInlined should be + // true) and there is no annotation on the handle, we assume the handle + // is escaped. + State = State->set<HStateMap>(Handle, HandleState::getEscaped()); } - } 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)) { - std::string SBuf; - llvm::raw_string_ostream OS(SBuf); - OS << "Handle allocated through " << ParamDiagIdx - << llvm::getOrdinalSuffix(ParamDiagIdx) << " parameter"; - return OS.str(); - } else - return ""; - }); - State = State->set<HStateMap>( - Handle, HandleState::getMaybeAllocated(ResultSymbol)); } } const NoteTag *T = nullptr; if (!Notes.empty()) { T = C.getNoteTag([this, Notes{std::move(Notes)}]( PathSensitiveBugReport &BR) -> std::string { - if (&BR.getBugType() != &UseAfterReleaseBugType && - &BR.getBugType() != &LeakBugType && - &BR.getBugType() != &DoubleReleaseBugType) - return ""; - for (auto &Note : Notes) { - std::string Text = Note(BR); - if (!Text.empty()) - return Text; - } - return ""; - }); + if (&BR.getBugType() != &UseAfterReleaseBugType && + &BR.getBugType() != &LeakBugType && + &BR.getBugType() != &DoubleReleaseBugType && + &BR.getBugType() != &ReleaseUnownedBugType) + return ""; + for (auto &Note : Notes) { + std::string Text = Note(BR); + if (!Text.empty()) + return Text; + } + return ""; + }); } C.addTransition(State, T); } @@ -481,13 +584,14 @@ ProgramStateRef FuchsiaHandleChecker::checkPointerEscape( if (Arg >= FuncDecl->getNumParams()) break; const ParmVarDecl *PVD = FuncDecl->getParamDecl(Arg); - SymbolRef Handle = - getFuchsiaHandleSymbol(PVD->getType(), Call->getArgSVal(Arg), State); - if (!Handle) - continue; - if (hasFuchsiaAttr<UseHandleAttr>(PVD) || - hasFuchsiaAttr<ReleaseHandleAttr>(PVD)) - UnEscaped.insert(Handle); + SmallVector<SymbolRef, 1024> Handles = + getFuchsiaHandleSymbols(PVD->getType(), Call->getArgSVal(Arg), State); + for (SymbolRef Handle : Handles) { + if (hasFuchsiaAttr<UseHandleAttr>(PVD) || + hasFuchsiaAttr<ReleaseHandleAttr>(PVD)) { + UnEscaped.insert(Handle); + } + } } } @@ -525,6 +629,14 @@ void FuchsiaHandleChecker::reportDoubleRelease(SymbolRef HandleSym, "Releasing a previously released handle"); } +void FuchsiaHandleChecker::reportUnownedRelease(SymbolRef HandleSym, + const SourceRange &Range, + CheckerContext &C) const { + ExplodedNode *ErrNode = C.generateErrorNode(C.getState()); + reportBug(HandleSym, ErrNode, C, &Range, ReleaseUnownedBugType, + "Releasing an unowned handle"); +} + void FuchsiaHandleChecker::reportUseAfterFree(SymbolRef HandleSym, const SourceRange &Range, CheckerContext &C) const { diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp index c06d2fcd8e7d..42c777eb2c52 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp @@ -103,6 +103,9 @@ private: 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; @@ -123,7 +126,7 @@ private: if (Name.empty() || FullName.empty()) return None; - return FunctionData{FDecl, Name, FullName}; + return FunctionData{FDecl, Name, std::move(FullName)}; } bool isInScope(StringRef Scope) const { diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IteratorModeling.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IteratorModeling.cpp index fd8cbd694b24..ab5e6a1c9991 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IteratorModeling.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IteratorModeling.cpp @@ -109,7 +109,7 @@ class IteratorModeling bool Postfix) const; void handleRandomIncrOrDecr(CheckerContext &C, const Expr *CE, OverloadedOperatorKind Op, const SVal &RetVal, - const SVal &LHS, const SVal &RHS) const; + const SVal &Iterator, const 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, @@ -262,18 +262,30 @@ void IteratorModeling::checkPostStmt(const UnaryOperator *UO, void IteratorModeling::checkPostStmt(const BinaryOperator *BO, CheckerContext &C) const { - ProgramStateRef State = C.getState(); - BinaryOperatorKind OK = BO->getOpcode(); - SVal RVal = State->getSVal(BO->getRHS(), C.getLocationContext()); + const ProgramStateRef State = C.getState(); + const BinaryOperatorKind OK = BO->getOpcode(); + const Expr *const LHS = BO->getLHS(); + const Expr *const RHS = BO->getRHS(); + const SVal LVal = State->getSVal(LHS, C.getLocationContext()); + const SVal RVal = State->getSVal(RHS, C.getLocationContext()); if (isSimpleComparisonOperator(BO->getOpcode())) { - SVal LVal = State->getSVal(BO->getLHS(), C.getLocationContext()); SVal Result = State->getSVal(BO, C.getLocationContext()); handleComparison(C, BO, Result, LVal, RVal, BinaryOperator::getOverloadedOperator(OK)); } else if (isRandomIncrOrDecrOperator(OK)) { - handlePtrIncrOrDecr(C, BO->getLHS(), - BinaryOperator::getOverloadedOperator(OK), RVal); + // In case of operator+ the iterator can be either on the LHS (eg.: it + 1), + // or on the RHS (eg.: 1 + it). Both cases are modeled. + const bool IsIterOnLHS = BO->getLHS()->getType()->isPointerType(); + const Expr *const &IterExpr = IsIterOnLHS ? LHS : RHS; + const Expr *const &AmountExpr = IsIterOnLHS ? RHS : LHS; + + // The non-iterator side must have an integral or enumeration type. + if (!AmountExpr->getType()->isIntegralOrEnumerationType()) + return; + const SVal &AmountVal = IsIterOnLHS ? RVal : LVal; + handlePtrIncrOrDecr(C, IterExpr, BinaryOperator::getOverloadedOperator(OK), + AmountVal); } } @@ -366,11 +378,24 @@ IteratorModeling::handleOverloadedOperator(CheckerContext &C, InstCall->getCXXThisVal(), Call.getArgSVal(0)); return; } - } else { - if (Call.getNumArgs() >= 2 && - Call.getArgExpr(1)->getType()->isIntegralOrEnumerationType()) { + } else if (Call.getNumArgs() >= 2) { + const Expr *FirstArg = Call.getArgExpr(0); + const Expr *SecondArg = Call.getArgExpr(1); + const QualType FirstType = FirstArg->getType(); + const QualType SecondType = SecondArg->getType(); + + if (FirstType->isIntegralOrEnumerationType() || + SecondType->isIntegralOrEnumerationType()) { + // In case of operator+ the iterator can be either on the LHS (eg.: + // it + 1), or on the RHS (eg.: 1 + it). Both cases are modeled. + 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; + handleRandomIncrOrDecr(C, OrigExpr, Op, Call.getReturnValue(), - Call.getArgSVal(0), Call.getArgSVal(1)); + Iterator, Amount); return; } } @@ -461,6 +486,12 @@ void IteratorModeling::handleComparison(CheckerContext &C, const Expr *CE, RPos = getIteratorPosition(State, RVal); } + // If the value for which we just tried to set a new iterator position is + // an `SVal`for which no iterator position can be set then the setting was + // unsuccessful. We cannot handle the comparison in this case. + if (!LPos || !RPos) + return; + // We cannot make assumptions on `UnknownVal`. Let us conjure a symbol // instead. if (RetVal.isUnknown()) { @@ -556,35 +587,35 @@ void IteratorModeling::handleDecrement(CheckerContext &C, const SVal &RetVal, C.addTransition(State); } -void IteratorModeling::handleRandomIncrOrDecr(CheckerContext &C, - const Expr *CE, +void IteratorModeling::handleRandomIncrOrDecr(CheckerContext &C, const Expr *CE, OverloadedOperatorKind Op, const SVal &RetVal, - const SVal &LHS, - const SVal &RHS) const { + const SVal &Iterator, + const SVal &Amount) const { // Increment or decrement the symbolic expressions which represents the // position of the iterator auto State = C.getState(); - const auto *Pos = getIteratorPosition(State, LHS); + const auto *Pos = getIteratorPosition(State, Iterator); if (!Pos) return; - const auto *value = &RHS; - SVal val; - if (auto loc = RHS.getAs<Loc>()) { - val = State->getRawSVal(*loc); - value = &val; + const auto *Value = &Amount; + SVal Val; + if (auto LocAmount = Amount.getAs<Loc>()) { + Val = State->getRawSVal(*LocAmount); + Value = &Val; } - auto &TgtVal = (Op == OO_PlusEqual || Op == OO_MinusEqual) ? LHS : RetVal; + const auto &TgtVal = + (Op == OO_PlusEqual || Op == OO_MinusEqual) ? Iterator : RetVal; // `AdvancedState` is a state where the position of `LHS` is advanced. We // only need this state to retrieve the new position, but we do not want // to change the position of `LHS` (in every case). - auto AdvancedState = advancePosition(State, LHS, Op, *value); + auto AdvancedState = advancePosition(State, Iterator, Op, *Value); if (AdvancedState) { - const auto *NewPos = getIteratorPosition(AdvancedState, LHS); + const auto *NewPos = getIteratorPosition(AdvancedState, Iterator); assert(NewPos && "Iterator should have position after successful advancement"); @@ -599,6 +630,9 @@ void IteratorModeling::handlePtrIncrOrDecr(CheckerContext &C, const Expr *Iterator, OverloadedOperatorKind OK, SVal Offset) const { + if (!Offset.getAs<DefinedSVal>()) + return; + QualType PtrType = Iterator->getType(); if (!PtrType->isPointerType()) return; @@ -612,13 +646,11 @@ void IteratorModeling::handlePtrIncrOrDecr(CheckerContext &C, return; SVal NewVal; - if (OK == OO_Plus || OK == OO_PlusEqual) + if (OK == OO_Plus || OK == OO_PlusEqual) { NewVal = State->getLValue(ElementType, Offset, OldVal); - else { - const llvm::APSInt &OffsetInt = - Offset.castAs<nonloc::ConcreteInt>().getValue(); - auto &BVF = C.getSymbolManager().getBasicVals(); - SVal NegatedOffset = nonloc::ConcreteInt(BVF.getValue(-OffsetInt)); + } else { + auto &SVB = C.getSValBuilder(); + SVal NegatedOffset = SVB.evalMinus(Offset.castAs<NonLoc>()); NewVal = State->getLValue(ElementType, NegatedOffset, OldVal); } @@ -684,9 +716,14 @@ bool IteratorModeling::noChangeInAdvance(CheckerContext &C, SVal Iter, const auto StateBefore = N->getState(); const auto *PosBefore = getIteratorPosition(StateBefore, Iter); - - assert(PosBefore && "`std::advance() should not create new iterator " - "position but change existing ones"); + // FIXME: `std::advance()` should not create a new iterator position but + // change existing ones. However, in case of iterators implemented as + // pointers the handling of parameters in `std::advance()`-like + // functions is still incomplete which may result in cases where + // the new position is assigned to the wrong pointer. This causes + // crash if we use an assertion here. + if (!PosBefore) + return false; return PosBefore->getOffset() == PosAfter->getOffset(); } diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IteratorRangeChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IteratorRangeChecker.cpp index df8e379d1f20..dd014648eb6f 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IteratorRangeChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IteratorRangeChecker.cpp @@ -169,6 +169,8 @@ void IteratorRangeChecker::checkPreStmt(const BinaryOperator *BO, verifyDereference(C, LVal); } else if (isRandomIncrOrDecrOperator(OK)) { SVal RVal = State->getSVal(BO->getRHS(), C.getLocationContext()); + if (!BO->getRHS()->getType()->isIntegralOrEnumerationType()) + return; verifyRandomIncrOrDecr(C, BinaryOperator::getOverloadedOperator(OK), LVal, RVal); } diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp index 252377f24bd7..28d3e058fee2 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp @@ -1141,10 +1141,9 @@ void EmptyLocalizationContextChecker::MethodCrawler::VisitObjCMessageExpr( SE = Mgr.getSourceManager().getSLocEntry(SLInfo.first); } - bool Invalid = false; - const llvm::MemoryBuffer *BF = - Mgr.getSourceManager().getBuffer(SLInfo.first, SL, &Invalid); - if (Invalid) + llvm::Optional<llvm::MemoryBufferRef> BF = + Mgr.getSourceManager().getBufferOrNone(SLInfo.first, SL); + if (!BF) return; Lexer TheLexer(SL, LangOptions(), BF->getBufferStart(), diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp index 87477e96d2d1..a157ee2da5df 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp @@ -509,7 +509,7 @@ ProgramStateRef MacOSKeychainAPIChecker::evalAssume(ProgramStateRef State, if (AMap.isEmpty()) return State; - auto *CondBSE = dyn_cast_or_null<BinarySymExpr>(Cond.getAsSymExpr()); + auto *CondBSE = dyn_cast_or_null<BinarySymExpr>(Cond.getAsSymbol()); if (!CondBSE) return State; BinaryOperator::Opcode OpCode = CondBSE->getOpcode(); diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp index d5b0a5b2220f..f117d5505ecb 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -3110,11 +3110,6 @@ bool MallocChecker::mayFreeAnyEscapedMemoryOrIsModeledExplicitly( return true; } - if (FName == "postEvent" && - FD->getQualifiedNameAsString() == "QCoreApplication::postEvent") { - return true; - } - if (FName == "connectImpl" && FD->getQualifiedNameAsString() == "QObject::connectImpl") { return true; @@ -3301,14 +3296,16 @@ PathDiagnosticPieceRef MallocBugVisitor::VisitNode(const ExplodedNode *N, OS << "reallocated by call to '"; const Stmt *S = RSCurr->getStmt(); if (const auto *MemCallE = dyn_cast<CXXMemberCallExpr>(S)) { - OS << MemCallE->getMethodDecl()->getNameAsString(); + OS << MemCallE->getMethodDecl()->getDeclName(); } else if (const auto *OpCallE = dyn_cast<CXXOperatorCallExpr>(S)) { - OS << OpCallE->getDirectCallee()->getNameAsString(); + 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); - const auto *D = dyn_cast_or_null<NamedDecl>(Call->getDecl()); - OS << (D ? D->getNameAsString() : "unknown"); + if (const auto *D = dyn_cast_or_null<NamedDecl>(Call->getDecl())) + OS << D->getDeclName(); + else + OS << "unknown"; } OS << "'"; StackHint = std::make_unique<StackHintGeneratorForSymbol>( diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp index 7f0519c695b0..a38298a7abed 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp @@ -104,7 +104,7 @@ private: "basic_ios", "future", "optional", - "packaged_task" + "packaged_task", "promise", "shared_future", "shared_lock", @@ -580,7 +580,7 @@ void MoveChecker::explainObject(llvm::raw_ostream &OS, const MemRegion *MR, if (const auto DR = dyn_cast_or_null<DeclRegion>(unwrapRValueReferenceIndirection(MR))) { const auto *RegionDecl = cast<NamedDecl>(DR->getDecl()); - OS << " '" << RegionDecl->getNameAsString() << "'"; + OS << " '" << RegionDecl->getDeclName() << "'"; } ObjectKind OK = classifyObject(MR, RD); diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/OSObjectCStyleCast.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/OSObjectCStyleCast.cpp index 53ed0e187a4c..270b66dab020 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/OSObjectCStyleCast.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/OSObjectCStyleCast.cpp @@ -24,32 +24,36 @@ using namespace ento; using namespace ast_matchers; namespace { - -const char *WarnAtNode = "OSObjCast"; +static constexpr const char *const WarnAtNode = "WarnAtNode"; +static constexpr const char *const WarnRecordDecl = "WarnRecordDecl"; class OSObjectCStyleCastChecker : public Checker<check::ASTCodeBody> { public: - void checkASTCodeBody(const Decl *D, - AnalysisManager &AM, + void checkASTCodeBody(const Decl *D, AnalysisManager &AM, BugReporter &BR) const; }; +} static void emitDiagnostics(const BoundNodes &Nodes, BugReporter &BR, AnalysisDeclContext *ADC, const OSObjectCStyleCastChecker *Checker) { const auto *CE = Nodes.getNodeAs<CastExpr>(WarnAtNode); - assert(CE); + const CXXRecordDecl *RD = Nodes.getNodeAs<CXXRecordDecl>(WarnRecordDecl); + assert(CE && RD); std::string Diagnostics; llvm::raw_string_ostream OS(Diagnostics); - OS << "C-style cast of OSObject. Use OSDynamicCast instead."; + OS << "C-style cast of an OSObject is prone to type confusion attacks; " + << "use 'OSRequiredCast' if the object is definitely of type '" + << RD->getNameAsString() << "', or 'OSDynamicCast' followed by " + << "a null check if unsure", BR.EmitBasicReport( ADC->getDecl(), Checker, /*Name=*/"OSObject C-Style Cast", - /*BugCategory=*/"Security", + categories::SecurityError, OS.str(), PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), ADC), CE->getSourceRange()); @@ -68,7 +72,7 @@ void OSObjectCStyleCastChecker::checkASTCodeBody(const Decl *D, AnalysisManager auto OSObjTypeM = hasTypePointingTo(cxxRecordDecl(isDerivedFrom("OSMetaClassBase"))); auto OSObjSubclassM = hasTypePointingTo( - cxxRecordDecl(isDerivedFrom("OSObject"))); + cxxRecordDecl(isDerivedFrom("OSObject")).bind(WarnRecordDecl)); auto CastM = cStyleCastExpr( allOf(hasSourceExpression(allOf(OSObjTypeM, unless(DynamicCastM))), @@ -78,7 +82,6 @@ void OSObjectCStyleCastChecker::checkASTCodeBody(const Decl *D, AnalysisManager for (BoundNodes Match : Matches) emitDiagnostics(Match, BR, ADC, this); } -} void ento::registerOSObjectCStyleCast(CheckerManager &Mgr) { Mgr.registerChecker<OSObjectCStyleCastChecker>(); diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp index 24e2a4dea922..35a600f2d7b8 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp @@ -21,7 +21,7 @@ #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" -#include "llvm/ADT/SmallSet.h" +#include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/raw_ostream.h" @@ -74,7 +74,7 @@ private: void initializeSelectors(ASTContext &Ctx) const; void fillSelectors(ASTContext &Ctx, ArrayRef<SelectorDescriptor> Sel, StringRef ClassName) const; - mutable llvm::StringMap<llvm::SmallSet<Selector, 16> > SelectorsForClass; + mutable llvm::StringMap<llvm::SmallPtrSet<Selector, 16>> SelectorsForClass; mutable bool IsInitialized; }; @@ -100,7 +100,8 @@ bool ObjCSuperCallChecker::isCheckableClass(const ObjCImplementationDecl *D, void ObjCSuperCallChecker::fillSelectors(ASTContext &Ctx, ArrayRef<SelectorDescriptor> Sel, StringRef ClassName) const { - llvm::SmallSet<Selector, 16> &ClassSelectors = SelectorsForClass[ClassName]; + 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) { diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PaddingChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PaddingChecker.cpp index 0b00664c7c10..96f0d9bb3c3d 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PaddingChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PaddingChecker.cpp @@ -248,8 +248,9 @@ public: FieldInfo RetVal; RetVal.Field = FD; auto &Ctx = FD->getASTContext(); - std::tie(RetVal.Size, RetVal.Align) = - Ctx.getTypeInfoInChars(FD->getType()); + auto Info = Ctx.getTypeInfoInChars(FD->getType()); + RetVal.Size = Info.Width; + RetVal.Align = Info.Align; assert(llvm::isPowerOf2_64(RetVal.Align.getQuantity())); if (auto Max = FD->getMaxAlignment()) RetVal.Align = std::max(Ctx.toCharUnitsFromBits(Max), RetVal.Align); diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp index 285d2da104f1..88e80c481a5a 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp @@ -83,7 +83,7 @@ public: private: typedef void (PthreadLockChecker::*FnCheck)(const CallEvent &Call, CheckerContext &C, - CheckerKind checkkind) const; + CheckerKind CheckKind) const; CallDescriptionMap<FnCheck> PThreadCallbacks = { // Init. {{"pthread_mutex_init", 2}, &PthreadLockChecker::InitAnyLock}, @@ -167,46 +167,49 @@ private: ProgramStateRef resolvePossiblyDestroyedMutex(ProgramStateRef state, const MemRegion *lockR, const SymbolRef *sym) const; - void reportUseDestroyedBug(const CallEvent &Call, CheckerContext &C, - unsigned ArgNo, CheckerKind checkKind) const; + void reportBug(CheckerContext &C, std::unique_ptr<BugType> BT[], + const Expr *MtxExpr, CheckerKind CheckKind, + StringRef Desc) const; // Init. void InitAnyLock(const CallEvent &Call, CheckerContext &C, - CheckerKind checkkind) const; - void InitLockAux(const CallEvent &Call, CheckerContext &C, unsigned ArgNo, - SVal Lock, CheckerKind checkkind) const; + CheckerKind CheckKind) const; + void InitLockAux(const CallEvent &Call, CheckerContext &C, + const Expr *MtxExpr, SVal MtxVal, + CheckerKind CheckKind) const; // Lock, Try-lock. void AcquirePthreadLock(const CallEvent &Call, CheckerContext &C, - CheckerKind checkkind) const; + CheckerKind CheckKind) const; void AcquireXNULock(const CallEvent &Call, CheckerContext &C, - CheckerKind checkkind) const; + CheckerKind CheckKind) const; void TryPthreadLock(const CallEvent &Call, CheckerContext &C, - CheckerKind checkkind) const; + CheckerKind CheckKind) const; void TryXNULock(const CallEvent &Call, CheckerContext &C, - CheckerKind checkkind) const; + CheckerKind CheckKind) const; void TryFuchsiaLock(const CallEvent &Call, CheckerContext &C, - CheckerKind checkkind) const; + CheckerKind CheckKind) const; void TryC11Lock(const CallEvent &Call, CheckerContext &C, - CheckerKind checkkind) const; - void AcquireLockAux(const CallEvent &Call, CheckerContext &C, unsigned ArgNo, - SVal lock, bool isTryLock, LockingSemantics semantics, - CheckerKind checkkind) const; + CheckerKind CheckKind) const; + void AcquireLockAux(const CallEvent &Call, CheckerContext &C, + const Expr *MtxExpr, SVal MtxVal, bool IsTryLock, + LockingSemantics Semantics, CheckerKind CheckKind) const; // Release. void ReleaseAnyLock(const CallEvent &Call, CheckerContext &C, - CheckerKind checkkind) const; - void ReleaseLockAux(const CallEvent &Call, CheckerContext &C, unsigned ArgNo, - SVal lock, CheckerKind checkkind) const; + CheckerKind CheckKind) const; + void ReleaseLockAux(const CallEvent &Call, CheckerContext &C, + const Expr *MtxExpr, SVal MtxVal, + CheckerKind CheckKind) const; // Destroy. void DestroyPthreadLock(const CallEvent &Call, CheckerContext &C, - CheckerKind checkkind) const; + CheckerKind CheckKind) const; void DestroyXNULock(const CallEvent &Call, CheckerContext &C, - CheckerKind checkkind) const; - void DestroyLockAux(const CallEvent &Call, CheckerContext &C, unsigned ArgNo, - SVal Lock, LockingSemantics semantics, - CheckerKind checkkind) const; + CheckerKind CheckKind) const; + void DestroyLockAux(const CallEvent &Call, CheckerContext &C, + const Expr *MtxExpr, SVal MtxVal, + LockingSemantics Semantics, CheckerKind CheckKind) const; public: void checkPostCall(const CallEvent &Call, CheckerContext &C) const; @@ -226,18 +229,18 @@ private: mutable std::unique_ptr<BugType> BT_initlock[CK_NumCheckKinds]; mutable std::unique_ptr<BugType> BT_lor[CK_NumCheckKinds]; - void initBugType(CheckerKind checkKind) const { - if (BT_doublelock[checkKind]) + void initBugType(CheckerKind CheckKind) const { + if (BT_doublelock[CheckKind]) return; - BT_doublelock[checkKind].reset( - new BugType{CheckNames[checkKind], "Double locking", "Lock checker"}); - BT_doubleunlock[checkKind].reset( - new BugType{CheckNames[checkKind], "Double unlocking", "Lock checker"}); - BT_destroylock[checkKind].reset(new BugType{ - CheckNames[checkKind], "Use destroyed lock", "Lock checker"}); - BT_initlock[checkKind].reset(new BugType{ - CheckNames[checkKind], "Init invalid lock", "Lock checker"}); - BT_lor[checkKind].reset(new BugType{CheckNames[checkKind], + BT_doublelock[CheckKind].reset( + new BugType{CheckNames[CheckKind], "Double locking", "Lock checker"}); + BT_doubleunlock[CheckKind].reset( + new BugType{CheckNames[CheckKind], "Double unlocking", "Lock checker"}); + BT_destroylock[CheckKind].reset(new BugType{ + CheckNames[CheckKind], "Use destroyed lock", "Lock checker"}); + BT_initlock[CheckKind].reset(new BugType{ + CheckNames[CheckKind], "Init invalid lock", "Lock checker"}); + BT_lor[CheckKind].reset(new BugType{CheckNames[CheckKind], "Lock order reversal", "Lock checker"}); } }; @@ -341,53 +344,53 @@ void PthreadLockChecker::printState(raw_ostream &Out, ProgramStateRef State, void PthreadLockChecker::AcquirePthreadLock(const CallEvent &Call, CheckerContext &C, - CheckerKind checkKind) const { - AcquireLockAux(Call, C, 0, Call.getArgSVal(0), false, PthreadSemantics, - checkKind); + CheckerKind CheckKind) const { + AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), false, + PthreadSemantics, CheckKind); } void PthreadLockChecker::AcquireXNULock(const CallEvent &Call, CheckerContext &C, - CheckerKind checkKind) const { - AcquireLockAux(Call, C, 0, Call.getArgSVal(0), false, XNUSemantics, - checkKind); + CheckerKind CheckKind) const { + AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), false, + XNUSemantics, CheckKind); } void PthreadLockChecker::TryPthreadLock(const CallEvent &Call, CheckerContext &C, - CheckerKind checkKind) const { - AcquireLockAux(Call, C, 0, Call.getArgSVal(0), true, PthreadSemantics, - checkKind); + CheckerKind CheckKind) const { + AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), true, + PthreadSemantics, CheckKind); } void PthreadLockChecker::TryXNULock(const CallEvent &Call, CheckerContext &C, - CheckerKind checkKind) const { - AcquireLockAux(Call, C, 0, Call.getArgSVal(0), true, PthreadSemantics, - checkKind); + CheckerKind CheckKind) const { + AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), true, + PthreadSemantics, CheckKind); } void PthreadLockChecker::TryFuchsiaLock(const CallEvent &Call, CheckerContext &C, - CheckerKind checkKind) const { - AcquireLockAux(Call, C, 0, Call.getArgSVal(0), true, PthreadSemantics, - checkKind); + CheckerKind CheckKind) const { + AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), true, + PthreadSemantics, CheckKind); } void PthreadLockChecker::TryC11Lock(const CallEvent &Call, CheckerContext &C, - CheckerKind checkKind) const { - AcquireLockAux(Call, C, 0, Call.getArgSVal(0), true, PthreadSemantics, - checkKind); + CheckerKind CheckKind) const { + AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), true, + PthreadSemantics, CheckKind); } void PthreadLockChecker::AcquireLockAux(const CallEvent &Call, - CheckerContext &C, unsigned ArgNo, - SVal lock, bool isTryLock, - enum LockingSemantics semantics, - CheckerKind checkKind) const { - if (!ChecksEnabled[checkKind]) + CheckerContext &C, const Expr *MtxExpr, + SVal MtxVal, bool IsTryLock, + enum LockingSemantics Semantics, + CheckerKind CheckKind) const { + if (!ChecksEnabled[CheckKind]) return; - const MemRegion *lockR = lock.getAsRegion(); + const MemRegion *lockR = MtxVal.getAsRegion(); if (!lockR) return; @@ -398,28 +401,23 @@ void PthreadLockChecker::AcquireLockAux(const CallEvent &Call, if (const LockState *LState = state->get<LockMap>(lockR)) { if (LState->isLocked()) { - ExplodedNode *N = C.generateErrorNode(); - if (!N) - return; - initBugType(checkKind); - auto report = std::make_unique<PathSensitiveBugReport>( - *BT_doublelock[checkKind], "This lock has already been acquired", N); - report->addRange(Call.getArgExpr(ArgNo)->getSourceRange()); - C.emitReport(std::move(report)); + reportBug(C, BT_doublelock, MtxExpr, CheckKind, + "This lock has already been acquired"); return; } else if (LState->isDestroyed()) { - reportUseDestroyedBug(Call, C, ArgNo, checkKind); + reportBug(C, BT_destroylock, MtxExpr, CheckKind, + "This lock has already been destroyed"); return; } } ProgramStateRef lockSucc = state; - if (isTryLock) { + if (IsTryLock) { // Bifurcate the state, and allow a mode where the lock acquisition fails. SVal RetVal = Call.getReturnValue(); if (auto DefinedRetVal = RetVal.getAs<DefinedSVal>()) { ProgramStateRef lockFail; - switch (semantics) { + switch (Semantics) { case PthreadSemantics: std::tie(lockFail, lockSucc) = state->assume(*DefinedRetVal); break; @@ -434,7 +432,7 @@ void PthreadLockChecker::AcquireLockAux(const CallEvent &Call, } // We might want to handle the case when the mutex lock function was inlined // and returned an Unknown or Undefined value. - } else if (semantics == PthreadSemantics) { + } else if (Semantics == PthreadSemantics) { // Assume that the return value was 0. SVal RetVal = Call.getReturnValue(); if (auto DefinedRetVal = RetVal.getAs<DefinedSVal>()) { @@ -447,7 +445,7 @@ void PthreadLockChecker::AcquireLockAux(const CallEvent &Call, // and returned an Unknown or Undefined value. } else { // XNU locking semantics return void on non-try locks - assert((semantics == XNUSemantics) && "Unknown locking semantics"); + assert((Semantics == XNUSemantics) && "Unknown locking semantics"); lockSucc = state; } @@ -459,18 +457,18 @@ void PthreadLockChecker::AcquireLockAux(const CallEvent &Call, void PthreadLockChecker::ReleaseAnyLock(const CallEvent &Call, CheckerContext &C, - CheckerKind checkKind) const { - ReleaseLockAux(Call, C, 0, Call.getArgSVal(0), checkKind); + CheckerKind CheckKind) const { + ReleaseLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), CheckKind); } void PthreadLockChecker::ReleaseLockAux(const CallEvent &Call, - CheckerContext &C, unsigned ArgNo, - SVal lock, - CheckerKind checkKind) const { - if (!ChecksEnabled[checkKind]) + CheckerContext &C, const Expr *MtxExpr, + SVal MtxVal, + CheckerKind CheckKind) const { + if (!ChecksEnabled[CheckKind]) return; - const MemRegion *lockR = lock.getAsRegion(); + const MemRegion *lockR = MtxVal.getAsRegion(); if (!lockR) return; @@ -481,18 +479,12 @@ void PthreadLockChecker::ReleaseLockAux(const CallEvent &Call, if (const LockState *LState = state->get<LockMap>(lockR)) { if (LState->isUnlocked()) { - ExplodedNode *N = C.generateErrorNode(); - if (!N) - return; - initBugType(checkKind); - auto Report = std::make_unique<PathSensitiveBugReport>( - *BT_doubleunlock[checkKind], "This lock has already been unlocked", - N); - Report->addRange(Call.getArgExpr(ArgNo)->getSourceRange()); - C.emitReport(std::move(Report)); + reportBug(C, BT_doubleunlock, MtxExpr, CheckKind, + "This lock has already been unlocked"); return; } else if (LState->isDestroyed()) { - reportUseDestroyedBug(Call, C, ArgNo, checkKind); + reportBug(C, BT_destroylock, MtxExpr, CheckKind, + "This lock has already been destroyed"); return; } } @@ -502,17 +494,9 @@ void PthreadLockChecker::ReleaseLockAux(const CallEvent &Call, if (!LS.isEmpty()) { const MemRegion *firstLockR = LS.getHead(); if (firstLockR != lockR) { - ExplodedNode *N = C.generateErrorNode(); - if (!N) - return; - initBugType(checkKind); - auto report = std::make_unique<PathSensitiveBugReport>( - *BT_lor[checkKind], - "This was not the most recently acquired lock. Possible " - "lock order reversal", - N); - report->addRange(Call.getArgExpr(ArgNo)->getSourceRange()); - C.emitReport(std::move(report)); + reportBug(C, BT_lor, MtxExpr, CheckKind, + "This was not the most recently acquired lock. Possible lock " + "order reversal"); return; } // Record that the lock was released. @@ -525,25 +509,27 @@ void PthreadLockChecker::ReleaseLockAux(const CallEvent &Call, void PthreadLockChecker::DestroyPthreadLock(const CallEvent &Call, CheckerContext &C, - CheckerKind checkKind) const { - DestroyLockAux(Call, C, 0, Call.getArgSVal(0), PthreadSemantics, checkKind); + CheckerKind CheckKind) const { + DestroyLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), + PthreadSemantics, CheckKind); } void PthreadLockChecker::DestroyXNULock(const CallEvent &Call, CheckerContext &C, - CheckerKind checkKind) const { - DestroyLockAux(Call, C, 0, Call.getArgSVal(0), XNUSemantics, checkKind); + CheckerKind CheckKind) const { + DestroyLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), XNUSemantics, + CheckKind); } void PthreadLockChecker::DestroyLockAux(const CallEvent &Call, - CheckerContext &C, unsigned ArgNo, - SVal Lock, - enum LockingSemantics semantics, - CheckerKind checkKind) const { - if (!ChecksEnabled[checkKind]) + CheckerContext &C, const Expr *MtxExpr, + SVal MtxVal, + enum LockingSemantics Semantics, + CheckerKind CheckKind) const { + if (!ChecksEnabled[CheckKind]) return; - const MemRegion *LockR = Lock.getAsRegion(); + const MemRegion *LockR = MtxVal.getAsRegion(); if (!LockR) return; @@ -556,7 +542,7 @@ void PthreadLockChecker::DestroyLockAux(const CallEvent &Call, const LockState *LState = State->get<LockMap>(LockR); // Checking the return value of the destroy method only in the case of // PthreadSemantics - if (semantics == PthreadSemantics) { + if (Semantics == PthreadSemantics) { if (!LState || LState->isUnlocked()) { SymbolRef sym = Call.getReturnValue().getAsSymbol(); if (!sym) { @@ -581,36 +567,26 @@ void PthreadLockChecker::DestroyLockAux(const CallEvent &Call, return; } } - StringRef Message; - if (LState->isLocked()) { - Message = "This lock is still locked"; - } else { - Message = "This lock has already been destroyed"; - } + StringRef Message = LState->isLocked() + ? "This lock is still locked" + : "This lock has already been destroyed"; - ExplodedNode *N = C.generateErrorNode(); - if (!N) - return; - initBugType(checkKind); - auto Report = std::make_unique<PathSensitiveBugReport>( - *BT_destroylock[checkKind], Message, N); - Report->addRange(Call.getArgExpr(ArgNo)->getSourceRange()); - C.emitReport(std::move(Report)); + reportBug(C, BT_destroylock, MtxExpr, CheckKind, Message); } void PthreadLockChecker::InitAnyLock(const CallEvent &Call, CheckerContext &C, - CheckerKind checkKind) const { - InitLockAux(Call, C, 0, Call.getArgSVal(0), checkKind); + CheckerKind CheckKind) const { + InitLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), CheckKind); } void PthreadLockChecker::InitLockAux(const CallEvent &Call, CheckerContext &C, - unsigned ArgNo, SVal Lock, - CheckerKind checkKind) const { - if (!ChecksEnabled[checkKind]) + const Expr *MtxExpr, SVal MtxVal, + CheckerKind CheckKind) const { + if (!ChecksEnabled[CheckKind]) return; - const MemRegion *LockR = Lock.getAsRegion(); + const MemRegion *LockR = MtxVal.getAsRegion(); if (!LockR) return; @@ -627,35 +603,24 @@ void PthreadLockChecker::InitLockAux(const CallEvent &Call, CheckerContext &C, return; } - StringRef Message; - - if (LState->isLocked()) { - Message = "This lock is still being held"; - } else { - Message = "This lock has already been initialized"; - } + StringRef Message = LState->isLocked() + ? "This lock is still being held" + : "This lock has already been initialized"; - ExplodedNode *N = C.generateErrorNode(); - if (!N) - return; - initBugType(checkKind); - auto Report = std::make_unique<PathSensitiveBugReport>( - *BT_initlock[checkKind], Message, N); - Report->addRange(Call.getArgExpr(ArgNo)->getSourceRange()); - C.emitReport(std::move(Report)); + reportBug(C, BT_initlock, MtxExpr, CheckKind, Message); } -void PthreadLockChecker::reportUseDestroyedBug(const CallEvent &Call, - CheckerContext &C, - unsigned ArgNo, - CheckerKind checkKind) const { +void PthreadLockChecker::reportBug(CheckerContext &C, + std::unique_ptr<BugType> BT[], + const Expr *MtxExpr, CheckerKind CheckKind, + StringRef Desc) const { ExplodedNode *N = C.generateErrorNode(); if (!N) return; - initBugType(checkKind); - auto Report = std::make_unique<PathSensitiveBugReport>( - *BT_destroylock[checkKind], "This lock has already been destroyed", N); - Report->addRange(Call.getArgExpr(ArgNo)->getSourceRange()); + initBugType(CheckKind); + auto Report = + std::make_unique<PathSensitiveBugReport>(*BT[CheckKind], Desc, N); + Report->addRange(MtxExpr->getSourceRange()); C.emitReport(std::move(Report)); } 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 1d8ed90f7590..1d903530201f 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp @@ -177,7 +177,7 @@ static Optional<unsigned> findArgIdxOfSymbol(ProgramStateRef CurrSt, for (unsigned Idx = 0; Idx < (*CE)->getNumArgs(); Idx++) if (const MemRegion *MR = (*CE)->getArgSVal(Idx).getAsRegion()) if (const auto *TR = dyn_cast<TypedValueRegion>(MR)) - if (CurrSt->getSVal(MR, TR->getValueType()).getAsSymExpr() == Sym) + if (CurrSt->getSVal(MR, TR->getValueType()).getAsSymbol() == Sym) return Idx; return None; @@ -439,7 +439,7 @@ annotateStartParameter(const ExplodedNode *N, SymbolRef Sym, std::string s; llvm::raw_string_ostream os(s); - os << "Parameter '" << PVD->getNameAsString() << "' starts at +"; + os << "Parameter '" << PVD->getDeclName() << "' starts at +"; if (CurrT->getCount() == 1) { os << "1, as it is marked as consuming"; } else { diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp index 599d4f306aa1..1a94ccdc2825 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp @@ -58,6 +58,11 @@ void ReturnPointerRangeChecker::checkPreStmt(const ReturnStmt *RS, DefinedOrUnknownSVal ElementCount = getDynamicElementCount( state, ER->getSuperRegion(), C.getSValBuilder(), ER->getValueType()); + // We assume that the location after the last element in the array is used as + // end() iterator. Reporting on these would return too many false positives. + if (Idx == ElementCount) + return; + ProgramStateRef StInBound = state->assumeInBound(Idx, ElementCount, true); ProgramStateRef StOutBound = state->assumeInBound(Idx, ElementCount, false); if (StOutBound && !StInBound) { @@ -70,7 +75,7 @@ void ReturnPointerRangeChecker::checkPreStmt(const ReturnStmt *RS, // types explicitly reference such exploit categories (when applicable). if (!BT) BT.reset(new BuiltinBug( - this, "Return of pointer value outside of expected range", + this, "Buffer overflow", "Returned pointer value points outside the original object " "(potential buffer overflow)")); diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtr.h b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtr.h index ec43a23e30a9..92c386bbb2b0 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtr.h +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtr.h @@ -26,6 +26,8 @@ bool isStdSmartPtrCall(const CallEvent &Call); /// Returns whether the smart pointer is null or not. bool isNullSmartPtr(const ProgramStateRef State, const MemRegion *ThisRegion); +const BugType *getNullDereferenceBugType(); + } // namespace smartptr } // namespace ento } // namespace clang diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtrChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtrChecker.cpp index 7bb25f397d01..8a85d454856b 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtrChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtrChecker.cpp @@ -23,23 +23,40 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" +#include "llvm/ADT/StringRef.h" using namespace clang; using namespace ento; namespace { -class SmartPtrChecker : public Checker<check::PreCall> { - BugType NullDereferenceBugType{this, "Null SmartPtr dereference", - "C++ Smart Pointer"}; +static const BugType *NullDereferenceBugTypePtr; + +class SmartPtrChecker : public Checker<check::PreCall> { public: void checkPreCall(const CallEvent &Call, CheckerContext &C) const; + BugType NullDereferenceBugType{this, "Null SmartPtr dereference", + "C++ Smart Pointer"}; private: - void reportBug(CheckerContext &C, const CallEvent &Call) const; + void reportBug(CheckerContext &C, const MemRegion *DerefRegion, + const CallEvent &Call) const; + void explainDereference(llvm::raw_ostream &OS, const MemRegion *DerefRegion, + const CallEvent &Call) const; }; } // end of anonymous namespace +// Define the inter-checker API. +namespace clang { +namespace ento { +namespace smartptr { + +const BugType *getNullDereferenceBugType() { return NullDereferenceBugTypePtr; } + +} // namespace smartptr +} // namespace ento +} // namespace clang + void SmartPtrChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { if (!smartptr::isStdSmartPtrCall(Call)) @@ -55,23 +72,34 @@ void SmartPtrChecker::checkPreCall(const CallEvent &Call, OverloadedOperatorKind OOK = OC->getOverloadedOperator(); if (OOK == OO_Star || OOK == OO_Arrow) { if (smartptr::isNullSmartPtr(State, ThisRegion)) - reportBug(C, Call); + reportBug(C, ThisRegion, Call); } } -void SmartPtrChecker::reportBug(CheckerContext &C, +void SmartPtrChecker::reportBug(CheckerContext &C, const MemRegion *DerefRegion, const CallEvent &Call) const { ExplodedNode *ErrNode = C.generateErrorNode(); if (!ErrNode) return; - - auto R = std::make_unique<PathSensitiveBugReport>( - NullDereferenceBugType, "Dereference of null smart pointer", ErrNode); + llvm::SmallString<128> Str; + llvm::raw_svector_ostream OS(Str); + explainDereference(OS, DerefRegion, Call); + auto R = std::make_unique<PathSensitiveBugReport>(NullDereferenceBugType, + OS.str(), ErrNode); + R->markInteresting(DerefRegion); C.emitReport(std::move(R)); } +void SmartPtrChecker::explainDereference(llvm::raw_ostream &OS, + const MemRegion *DerefRegion, + const CallEvent &Call) const { + OS << "Dereference of null smart pointer "; + DerefRegion->printPretty(OS); +} + void ento::registerSmartPtrChecker(CheckerManager &Mgr) { - Mgr.registerChecker<SmartPtrChecker>(); + SmartPtrChecker *Checker = Mgr.registerChecker<SmartPtrChecker>(); + NullDereferenceBugTypePtr = &Checker->NullDereferenceBugType; } bool ento::shouldRegisterSmartPtrChecker(const CheckerManager &mgr) { diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp index bcc7d4103c1c..6ee7bd9252b3 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp @@ -15,24 +15,31 @@ #include "SmartPtr.h" #include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclarationName.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/Type.h" +#include "clang/Basic/LLVM.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/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" +#include <string> using namespace clang; using namespace ento; namespace { -class SmartPtrModeling : public Checker<eval::Call, check::DeadSymbols> { +class SmartPtrModeling + : public Checker<eval::Call, check::DeadSymbols, check::RegionChanges, + check::LiveSymbols> { - bool isNullAfterMoveMethod(const CallEvent &Call) const; + bool isBoolConversionMethod(const CallEvent &Call) const; public: // Whether the checker should model for null dereferences of smart pointers. @@ -40,20 +47,35 @@ public: 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, + const InvalidatedSymbols *Invalidated, + ArrayRef<const MemRegion *> ExplicitRegions, + ArrayRef<const MemRegion *> Regions, + const LocationContext *LCtx, const CallEvent *Call) const; + void printState(raw_ostream &Out, ProgramStateRef State, const char *NL, + const char *Sep) const override; + void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const; private: - ProgramStateRef updateTrackedRegion(const CallEvent &Call, CheckerContext &C, - const MemRegion *ThisValRegion) const; void handleReset(const CallEvent &Call, CheckerContext &C) const; void handleRelease(const CallEvent &Call, CheckerContext &C) const; void handleSwap(const CallEvent &Call, CheckerContext &C) const; + void handleGet(const CallEvent &Call, CheckerContext &C) const; + bool handleAssignOp(const CallEvent &Call, CheckerContext &C) const; + bool handleMoveCtr(const CallEvent &Call, CheckerContext &C, + const MemRegion *ThisRegion) const; + bool updateMovedSmartPointers(CheckerContext &C, const MemRegion *ThisRegion, + const MemRegion *OtherSmartPtrRegion) const; + void handleBoolConversion(const CallEvent &Call, CheckerContext &C) const; using SmartPtrMethodHandlerFn = void (SmartPtrModeling::*)(const CallEvent &Call, CheckerContext &) const; CallDescriptionMap<SmartPtrMethodHandlerFn> SmartPtrMethodHandlers{ {{"reset"}, &SmartPtrModeling::handleReset}, {{"release"}, &SmartPtrModeling::handleRelease}, - {{"swap", 1}, &SmartPtrModeling::handleSwap}}; + {{"swap", 1}, &SmartPtrModeling::handleSwap}, + {{"get"}, &SmartPtrModeling::handleGet}}; }; } // end of anonymous namespace @@ -81,13 +103,70 @@ bool isStdSmartPtrCall(const CallEvent &Call) { bool isNullSmartPtr(const ProgramStateRef State, const MemRegion *ThisRegion) { const auto *InnerPointVal = State->get<TrackedRegionMap>(ThisRegion); - return InnerPointVal && InnerPointVal->isZeroConstant(); + return InnerPointVal && + !State->assume(InnerPointVal->castAs<DefinedOrUnknownSVal>(), true); } } // namespace smartptr } // namespace ento } // namespace clang -bool SmartPtrModeling::isNullAfterMoveMethod(const CallEvent &Call) const { +// If a region is removed all of the subregions need to be removed too. +static TrackedRegionMapTy +removeTrackedSubregions(TrackedRegionMapTy RegionMap, + TrackedRegionMapTy::Factory &RegionMapFactory, + const MemRegion *Region) { + if (!Region) + return RegionMap; + for (const auto &E : RegionMap) { + if (E.first->isSubRegionOf(Region)) + RegionMap = RegionMapFactory.remove(RegionMap, E.first); + } + return RegionMap; +} + +static ProgramStateRef updateSwappedRegion(ProgramStateRef State, + const MemRegion *Region, + const SVal *RegionInnerPointerVal) { + if (RegionInnerPointerVal) { + State = State->set<TrackedRegionMap>(Region, *RegionInnerPointerVal); + } else { + State = State->remove<TrackedRegionMap>(Region); + } + return State; +} + +// Helper method to get the inner pointer type of specialized smart pointer +// Returns empty type if not found valid inner pointer type. +static QualType getInnerPointerType(const CallEvent &Call, CheckerContext &C) { + const auto *MethodDecl = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl()); + if (!MethodDecl || !MethodDecl->getParent()) + return {}; + + const auto *RecordDecl = MethodDecl->getParent(); + if (!RecordDecl || !RecordDecl->isInStdNamespace()) + return {}; + + const auto *TSD = dyn_cast<ClassTemplateSpecializationDecl>(RecordDecl); + if (!TSD) + return {}; + + auto TemplateArgs = TSD->getTemplateArgs().asArray(); + if (TemplateArgs.size() == 0) + return {}; + auto InnerValueType = TemplateArgs[0].getAsType(); + return C.getASTContext().getPointerType(InnerValueType.getCanonicalType()); +} + +// Helper method to pretty print region and avoid extra spacing. +static void checkAndPrettyPrintRegion(llvm::raw_ostream &OS, + const MemRegion *Region) { + if (Region->canPrintPretty()) { + OS << " "; + Region->printPretty(OS); + } +} + +bool SmartPtrModeling::isBoolConversionMethod(const CallEvent &Call) const { // TODO: Update CallDescription to support anonymous calls? // TODO: Handle other methods, such as .get() or .release(). // But once we do, we'd need a visitor to explain null dereferences @@ -98,43 +177,93 @@ bool SmartPtrModeling::isNullAfterMoveMethod(const CallEvent &Call) const { bool SmartPtrModeling::evalCall(const CallEvent &Call, CheckerContext &C) const { - + ProgramStateRef State = C.getState(); if (!smartptr::isStdSmartPtrCall(Call)) return false; - if (isNullAfterMoveMethod(Call)) { - ProgramStateRef State = C.getState(); + if (isBoolConversionMethod(Call)) { const MemRegion *ThisR = cast<CXXInstanceCall>(&Call)->getCXXThisVal().getAsRegion(); - if (!move::isMovedFrom(State, ThisR)) { - // TODO: Model this case as well. At least, avoid invalidation of globals. - return false; + if (ModelSmartPtrDereference) { + // The check for the region is moved is duplicated in handleBoolOperation + // method. + // FIXME: Once we model std::move for smart pointers clean up this and use + // that modeling. + handleBoolConversion(Call, C); + return true; + } else { + if (!move::isMovedFrom(State, ThisR)) { + // TODO: Model this case as well. At least, avoid invalidation of + // globals. + return false; + } + + // TODO: Add a note to bug reports describing this decision. + C.addTransition(State->BindExpr( + Call.getOriginExpr(), C.getLocationContext(), + C.getSValBuilder().makeZeroVal(Call.getResultType()))); + + return true; } - - // TODO: Add a note to bug reports describing this decision. - C.addTransition( - State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), - C.getSValBuilder().makeZeroVal(Call.getResultType()))); - return true; } if (!ModelSmartPtrDereference) return false; if (const auto *CC = dyn_cast<CXXConstructorCall>(&Call)) { - if (CC->getDecl()->isCopyOrMoveConstructor()) + if (CC->getDecl()->isCopyConstructor()) return false; - const MemRegion *ThisValRegion = CC->getCXXThisVal().getAsRegion(); - if (!ThisValRegion) + const MemRegion *ThisRegion = CC->getCXXThisVal().getAsRegion(); + if (!ThisRegion) return false; - auto State = updateTrackedRegion(Call, C, ThisValRegion); - C.addTransition(State); + if (CC->getDecl()->isMoveConstructor()) + return handleMoveCtr(Call, C, ThisRegion); + + if (Call.getNumArgs() == 0) { + auto NullVal = C.getSValBuilder().makeNull(); + State = State->set<TrackedRegionMap>(ThisRegion, NullVal); + + C.addTransition( + State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR, + llvm::raw_ostream &OS) { + if (&BR.getBugType() != smartptr::getNullDereferenceBugType() || + !BR.isInteresting(ThisRegion)) + return; + OS << "Default constructed smart pointer"; + checkAndPrettyPrintRegion(OS, ThisRegion); + OS << " is null"; + })); + } else { + const auto *TrackingExpr = Call.getArgExpr(0); + assert(TrackingExpr->getType()->isPointerType() && + "Adding a non pointer value to TrackedRegionMap"); + auto ArgVal = Call.getArgSVal(0); + State = State->set<TrackedRegionMap>(ThisRegion, ArgVal); + + C.addTransition(State, C.getNoteTag([ThisRegion, TrackingExpr, + ArgVal](PathSensitiveBugReport &BR, + llvm::raw_ostream &OS) { + if (&BR.getBugType() != smartptr::getNullDereferenceBugType() || + !BR.isInteresting(ThisRegion)) + return; + bugreporter::trackExpressionValue(BR.getErrorNode(), TrackingExpr, BR); + OS << "Smart pointer"; + checkAndPrettyPrintRegion(OS, ThisRegion); + if (ArgVal.isZeroConstant()) + OS << " is constructed using a null value"; + else + OS << " is constructed"; + })); + } return true; } + if (handleAssignOp(Call, C)) + return true; + const SmartPtrMethodHandlerFn *Handler = SmartPtrMethodHandlers.lookup(Call); if (!Handler) return false; @@ -158,66 +287,351 @@ void SmartPtrModeling::checkDeadSymbols(SymbolReaper &SymReaper, C.addTransition(State); } +void SmartPtrModeling::printState(raw_ostream &Out, ProgramStateRef State, + const char *NL, const char *Sep) const { + TrackedRegionMapTy RS = State->get<TrackedRegionMap>(); + + if (!RS.isEmpty()) { + Out << Sep << "Smart ptr regions :" << NL; + for (auto I : RS) { + I.first->dumpToStream(Out); + if (smartptr::isNullSmartPtr(State, I.first)) + Out << ": Null"; + else + Out << ": Non Null"; + Out << NL; + } + } +} + +ProgramStateRef SmartPtrModeling::checkRegionChanges( + ProgramStateRef State, const InvalidatedSymbols *Invalidated, + ArrayRef<const MemRegion *> ExplicitRegions, + ArrayRef<const MemRegion *> Regions, const LocationContext *LCtx, + const CallEvent *Call) const { + TrackedRegionMapTy RegionMap = State->get<TrackedRegionMap>(); + TrackedRegionMapTy::Factory &RegionMapFactory = + State->get_context<TrackedRegionMap>(); + for (const auto *Region : Regions) + RegionMap = removeTrackedSubregions(RegionMap, RegionMapFactory, + Region->getBaseRegion()); + return State->set<TrackedRegionMap>(RegionMap); +} + +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); + } + } +} + void SmartPtrModeling::handleReset(const CallEvent &Call, CheckerContext &C) const { + ProgramStateRef State = C.getState(); const auto *IC = dyn_cast<CXXInstanceCall>(&Call); if (!IC) return; - const MemRegion *ThisValRegion = IC->getCXXThisVal().getAsRegion(); - if (!ThisValRegion) + const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion(); + if (!ThisRegion) return; - auto State = updateTrackedRegion(Call, C, ThisValRegion); - C.addTransition(State); - // TODO: Make sure to ivalidate the the region in the Store if we don't have + + assert(Call.getArgExpr(0)->getType()->isPointerType() && + "Adding a non pointer value to TrackedRegionMap"); + State = State->set<TrackedRegionMap>(ThisRegion, Call.getArgSVal(0)); + const auto *TrackingExpr = Call.getArgExpr(0); + C.addTransition( + State, C.getNoteTag([ThisRegion, TrackingExpr](PathSensitiveBugReport &BR, + llvm::raw_ostream &OS) { + if (&BR.getBugType() != smartptr::getNullDereferenceBugType() || + !BR.isInteresting(ThisRegion)) + return; + bugreporter::trackExpressionValue(BR.getErrorNode(), TrackingExpr, BR); + OS << "Smart pointer"; + checkAndPrettyPrintRegion(OS, ThisRegion); + OS << " reset using a null value"; + })); + // TODO: Make sure to ivalidate the region in the Store if we don't have // time to model all methods. } void SmartPtrModeling::handleRelease(const CallEvent &Call, CheckerContext &C) const { + ProgramStateRef State = C.getState(); const auto *IC = dyn_cast<CXXInstanceCall>(&Call); if (!IC) return; - const MemRegion *ThisValRegion = IC->getCXXThisVal().getAsRegion(); - if (!ThisValRegion) + const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion(); + if (!ThisRegion) return; - auto State = updateTrackedRegion(Call, C, ThisValRegion); + const auto *InnerPointVal = State->get<TrackedRegionMap>(ThisRegion); - const auto *InnerPointVal = State->get<TrackedRegionMap>(ThisValRegion); if (InnerPointVal) { State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), *InnerPointVal); } - C.addTransition(State); + + auto ValueToUpdate = C.getSValBuilder().makeNull(); + State = State->set<TrackedRegionMap>(ThisRegion, ValueToUpdate); + + C.addTransition(State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR, + llvm::raw_ostream &OS) { + if (&BR.getBugType() != smartptr::getNullDereferenceBugType() || + !BR.isInteresting(ThisRegion)) + return; + + OS << "Smart pointer"; + checkAndPrettyPrintRegion(OS, ThisRegion); + OS << " is released and set to null"; + })); // TODO: Add support to enable MallocChecker to start tracking the raw // pointer. } void SmartPtrModeling::handleSwap(const CallEvent &Call, CheckerContext &C) const { - // TODO: Add support to handle swap method. + // To model unique_ptr::swap() method. + const auto *IC = dyn_cast<CXXInstanceCall>(&Call); + if (!IC) + return; + + const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion(); + if (!ThisRegion) + return; + + const auto *ArgRegion = Call.getArgSVal(0).getAsRegion(); + if (!ArgRegion) + return; + + auto State = C.getState(); + const auto *ThisRegionInnerPointerVal = + State->get<TrackedRegionMap>(ThisRegion); + const auto *ArgRegionInnerPointerVal = + State->get<TrackedRegionMap>(ArgRegion); + + // Swap the tracked region values. + State = updateSwappedRegion(State, ThisRegion, ArgRegionInnerPointerVal); + State = updateSwappedRegion(State, ArgRegion, ThisRegionInnerPointerVal); + + C.addTransition( + State, C.getNoteTag([ThisRegion, ArgRegion](PathSensitiveBugReport &BR, + llvm::raw_ostream &OS) { + if (&BR.getBugType() != smartptr::getNullDereferenceBugType() || + !BR.isInteresting(ThisRegion)) + return; + BR.markInteresting(ArgRegion); + OS << "Swapped null smart pointer"; + checkAndPrettyPrintRegion(OS, ArgRegion); + OS << " with smart pointer"; + checkAndPrettyPrintRegion(OS, ThisRegion); + })); } -ProgramStateRef -SmartPtrModeling::updateTrackedRegion(const CallEvent &Call, CheckerContext &C, - const MemRegion *ThisValRegion) const { - // TODO: Refactor and clean up handling too many things. +void SmartPtrModeling::handleGet(const CallEvent &Call, + CheckerContext &C) const { ProgramStateRef State = C.getState(); - auto NumArgs = Call.getNumArgs(); - - if (NumArgs == 0) { - auto NullSVal = C.getSValBuilder().makeNull(); - State = State->set<TrackedRegionMap>(ThisValRegion, NullSVal); - } else if (NumArgs == 1) { - auto ArgVal = Call.getArgSVal(0); - assert(Call.getArgExpr(0)->getType()->isPointerType() && - "Adding a non pointer value to TrackedRegionMap"); - State = State->set<TrackedRegionMap>(ThisValRegion, ArgVal); + const auto *IC = dyn_cast<CXXInstanceCall>(&Call); + if (!IC) + return; + + const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion(); + if (!ThisRegion) + return; + + SVal InnerPointerVal; + if (const auto *InnerValPtr = State->get<TrackedRegionMap>(ThisRegion)) { + InnerPointerVal = *InnerValPtr; + } else { + const auto *CallExpr = Call.getOriginExpr(); + InnerPointerVal = C.getSValBuilder().conjureSymbolVal( + CallExpr, C.getLocationContext(), Call.getResultType(), C.blockCount()); + State = State->set<TrackedRegionMap>(ThisRegion, InnerPointerVal); } - return State; + State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), + InnerPointerVal); + // TODO: Add NoteTag, for how the raw pointer got using 'get' method. + C.addTransition(State); +} + +bool SmartPtrModeling::handleAssignOp(const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + const auto *OC = dyn_cast<CXXMemberOperatorCall>(&Call); + if (!OC) + return false; + OverloadedOperatorKind OOK = OC->getOverloadedOperator(); + if (OOK != OO_Equal) + return false; + const MemRegion *ThisRegion = OC->getCXXThisVal().getAsRegion(); + if (!ThisRegion) + return false; + + 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(); + State = State->set<TrackedRegionMap>(ThisRegion, NullVal); + C.addTransition(State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR, + llvm::raw_ostream &OS) { + if (&BR.getBugType() != smartptr::getNullDereferenceBugType() || + !BR.isInteresting(ThisRegion)) + return; + OS << "Smart pointer"; + checkAndPrettyPrintRegion(OS, ThisRegion); + OS << " is assigned to null"; + })); + return true; + } + + return updateMovedSmartPointers(C, ThisRegion, OtherSmartPtrRegion); +} + +bool SmartPtrModeling::handleMoveCtr(const CallEvent &Call, CheckerContext &C, + const MemRegion *ThisRegion) const { + const auto *OtherSmartPtrRegion = Call.getArgSVal(0).getAsRegion(); + if (!OtherSmartPtrRegion) + return false; + + return updateMovedSmartPointers(C, ThisRegion, OtherSmartPtrRegion); +} + +bool SmartPtrModeling::updateMovedSmartPointers( + CheckerContext &C, const MemRegion *ThisRegion, + const MemRegion *OtherSmartPtrRegion) const { + ProgramStateRef State = C.getState(); + const auto *OtherInnerPtr = State->get<TrackedRegionMap>(OtherSmartPtrRegion); + if (OtherInnerPtr) { + State = State->set<TrackedRegionMap>(ThisRegion, *OtherInnerPtr); + auto NullVal = C.getSValBuilder().makeNull(); + State = State->set<TrackedRegionMap>(OtherSmartPtrRegion, NullVal); + bool IsArgValNull = OtherInnerPtr->isZeroConstant(); + + C.addTransition( + State, + C.getNoteTag([ThisRegion, OtherSmartPtrRegion, IsArgValNull]( + PathSensitiveBugReport &BR, llvm::raw_ostream &OS) { + if (&BR.getBugType() != smartptr::getNullDereferenceBugType()) + return; + if (BR.isInteresting(OtherSmartPtrRegion)) { + OS << "Smart pointer"; + checkAndPrettyPrintRegion(OS, OtherSmartPtrRegion); + OS << " is null after being moved to"; + checkAndPrettyPrintRegion(OS, ThisRegion); + } + if (BR.isInteresting(ThisRegion) && IsArgValNull) { + OS << "A null pointer value is moved to"; + checkAndPrettyPrintRegion(OS, ThisRegion); + BR.markInteresting(OtherSmartPtrRegion); + } + })); + return true; + } 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(); + State = State->remove<TrackedRegionMap>(ThisRegion); + State = State->set<TrackedRegionMap>(OtherSmartPtrRegion, NullVal); + C.addTransition(State, C.getNoteTag([OtherSmartPtrRegion, + ThisRegion](PathSensitiveBugReport &BR, + llvm::raw_ostream &OS) { + if (&BR.getBugType() != smartptr::getNullDereferenceBugType() || + !BR.isInteresting(OtherSmartPtrRegion)) + return; + OS << "Smart pointer"; + checkAndPrettyPrintRegion(OS, OtherSmartPtrRegion); + OS << " is null after; previous value moved to"; + checkAndPrettyPrintRegion(OS, ThisRegion); + })); + return true; + } + return false; +} + +void SmartPtrModeling::handleBoolConversion(const CallEvent &Call, + CheckerContext &C) const { + // To model unique_ptr::operator bool + ProgramStateRef State = C.getState(); + const Expr *CallExpr = Call.getOriginExpr(); + const MemRegion *ThisRegion = + cast<CXXInstanceCall>(&Call)->getCXXThisVal().getAsRegion(); + + SVal InnerPointerVal; + if (const auto *InnerValPtr = State->get<TrackedRegionMap>(ThisRegion)) { + InnerPointerVal = *InnerValPtr; + } else { + // In case of inner pointer SVal is not available we create + // conjureSymbolVal for inner pointer value. + auto InnerPointerType = getInnerPointerType(Call, C); + if (InnerPointerType.isNull()) + return; + + const LocationContext *LC = C.getLocationContext(); + InnerPointerVal = C.getSValBuilder().conjureSymbolVal( + CallExpr, LC, InnerPointerType, C.blockCount()); + State = State->set<TrackedRegionMap>(ThisRegion, InnerPointerVal); + } + + if (State->isNull(InnerPointerVal).isConstrainedTrue()) { + State = State->BindExpr(CallExpr, C.getLocationContext(), + C.getSValBuilder().makeTruthVal(false)); + + C.addTransition(State); + return; + } else if (State->isNonNull(InnerPointerVal).isConstrainedTrue()) { + State = State->BindExpr(CallExpr, C.getLocationContext(), + C.getSValBuilder().makeTruthVal(true)); + + C.addTransition(State); + return; + } else if (move::isMovedFrom(State, ThisRegion)) { + C.addTransition( + State->BindExpr(CallExpr, C.getLocationContext(), + C.getSValBuilder().makeZeroVal(Call.getResultType()))); + return; + } else { + ProgramStateRef NotNullState, NullState; + std::tie(NotNullState, NullState) = + State->assume(InnerPointerVal.castAs<DefinedOrUnknownSVal>()); + + auto NullVal = C.getSValBuilder().makeNull(); + // Explicitly tracking the region as null. + NullState = NullState->set<TrackedRegionMap>(ThisRegion, NullVal); + + NullState = NullState->BindExpr(CallExpr, C.getLocationContext(), + C.getSValBuilder().makeTruthVal(false)); + C.addTransition(NullState, C.getNoteTag( + [ThisRegion](PathSensitiveBugReport &BR, + llvm::raw_ostream &OS) { + OS << "Assuming smart pointer"; + checkAndPrettyPrintRegion(OS, ThisRegion); + OS << " is null"; + }, + /*IsPrunable=*/true)); + NotNullState = + NotNullState->BindExpr(CallExpr, C.getLocationContext(), + C.getSValBuilder().makeTruthVal(true)); + C.addTransition( + NotNullState, + C.getNoteTag( + [ThisRegion](PathSensitiveBugReport &BR, llvm::raw_ostream &OS) { + OS << "Assuming smart pointer"; + checkAndPrettyPrintRegion(OS, ThisRegion); + OS << " is non-null"; + }, + /*IsPrunable=*/true)); + return; + } } void ento::registerSmartPtrModeling(CheckerManager &Mgr) { diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp index 8b575f4f4759..d1c366a94fac 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp @@ -40,12 +40,12 @@ // // The following standard C functions are currently supported: // -// fgetc getline isdigit isupper +// fgetc getline isdigit isupper toascii // fread isalnum isgraph isxdigit // fwrite isalpha islower read // getc isascii isprint write -// getchar isblank ispunct -// getdelim iscntrl isspace +// getchar isblank ispunct toupper +// getdelim iscntrl isspace tolower // //===----------------------------------------------------------------------===// @@ -126,6 +126,8 @@ class StdLibraryFunctionsChecker } ArgNo getArgNo() const { return ArgN; } + virtual StringRef getName() const = 0; + protected: ArgNo ArgN; // Argument to which we apply the constraint. @@ -138,18 +140,25 @@ class StdLibraryFunctionsChecker /// Given a range, should the argument stay inside or outside this range? enum RangeKind { OutOfRange, WithinRange }; - /// Encapsulates a single range on a single symbol within a branch. + /// Encapsulates a range on a single symbol. class RangeConstraint : public ValueConstraint { - RangeKind Kind; // Kind of range definition. - IntRangeVector Args; // Polymorphic arguments. + 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. + IntRangeVector Ranges; public: - RangeConstraint(ArgNo ArgN, RangeKind Kind, const IntRangeVector &Args) - : ValueConstraint(ArgN), Kind(Kind), Args(Args) {} + StringRef getName() const override { return "Range"; } + RangeConstraint(ArgNo ArgN, RangeKind Kind, const IntRangeVector &Ranges) + : ValueConstraint(ArgN), Kind(Kind), Ranges(Ranges) {} - const IntRangeVector &getRanges() const { - return Args; - } + const IntRangeVector &getRanges() const { return Ranges; } private: ProgramStateRef applyAsOutOfRange(ProgramStateRef State, @@ -158,6 +167,7 @@ class StdLibraryFunctionsChecker ProgramStateRef applyAsWithinRange(ProgramStateRef State, const CallEvent &Call, const Summary &Summary) const; + public: ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, const Summary &Summary, @@ -198,6 +208,7 @@ class StdLibraryFunctionsChecker ArgNo OtherArgN; public: + virtual StringRef getName() const override { return "Comparison"; }; ComparisonConstraint(ArgNo ArgN, BinaryOperator::Opcode Opcode, ArgNo OtherArgN) : ValueConstraint(ArgN), Opcode(Opcode), OtherArgN(OtherArgN) {} @@ -214,6 +225,7 @@ class StdLibraryFunctionsChecker bool CannotBeNull = true; public: + StringRef getName() const override { return "NonNull"; } ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, const Summary &Summary, CheckerContext &C) const override { @@ -242,15 +254,21 @@ class StdLibraryFunctionsChecker } }; - // Represents a buffer argument with an additional size argument. - // E.g. the first two arguments here: + // Represents a buffer argument with an additional size constraint. The + // constraint may be a concrete value, or a symbolic value in an argument. + // Example 1. Concrete value as the minimum buffer size. + // char *asctime_r(const struct tm *restrict tm, char *restrict buf); + // // `buf` size must be at least 26 bytes according the POSIX standard. + // Example 2. Argument as a buffer size. // ctime_s(char *buffer, rsize_t bufsz, const time_t *time); - // Another example: + // Example 3. The size is computed as a multiplication of other args. // size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream); // // 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; // The argument which holds the size of the buffer. - ArgNo SizeArgN; + llvm::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. @@ -259,9 +277,11 @@ class StdLibraryFunctionsChecker 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) : ValueConstraint(Buffer), SizeArgN(BufSize) {} - BufferSizeConstraint(ArgNo Buffer, ArgNo BufSize, ArgNo BufSizeMultiplier) : ValueConstraint(Buffer), SizeArgN(BufSize), SizeMultiplierArgN(BufSizeMultiplier) {} @@ -272,14 +292,27 @@ class StdLibraryFunctionsChecker SValBuilder &SvalBuilder = C.getSValBuilder(); // The buffer argument. SVal BufV = getArgSVal(Call, getArgNo()); - // 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)); - } + + // Get the size constraint. + const SVal SizeV = [this, &State, &Call, &Summary, &SvalBuilder]() { + if (ConcreteSize) { + return SVal(SvalBuilder.makeIntVal(*ConcreteSize)); + } else if (SizeArgN) { + // 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; + } else { + llvm_unreachable("The constraint must be either a concrete value or " + "encoded in an arguement."); + } + }(); + // The dynamic size of the buffer argument, got from the analyzer engine. SVal BufDynSize = getDynamicSizeWithOffset(State, BufV); @@ -302,12 +335,20 @@ class StdLibraryFunctionsChecker Tmp.Op = BinaryOperator::negateComparisonOp(Op); return std::make_shared<BufferSizeConstraint>(Tmp); } + + 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; + } }; /// The complete list of constraints that defines a single branch. typedef std::vector<ValueConstraintPtr> ConstraintSet; - using ArgTypes = std::vector<QualType>; + using ArgTypes = std::vector<Optional<QualType>>; + using RetType = Optional<QualType>; // A placeholder type, we use it whenever we do not care about the concrete // type in a Signature. @@ -317,16 +358,37 @@ class StdLibraryFunctionsChecker // The signature of a function we want to describe with a summary. This is a // concessive signature, meaning there may be irrelevant types in the // signature which we do not check against a function with concrete types. - struct Signature { - const ArgTypes ArgTys; - const QualType RetTy; - Signature(ArgTypes ArgTys, QualType RetTy) : ArgTys(ArgTys), RetTy(RetTy) { - assertRetTypeSuitableForSignature(RetTy); - for (size_t I = 0, E = ArgTys.size(); I != E; ++I) { - QualType ArgTy = ArgTys[I]; - assertArgTypeSuitableForSignature(ArgTy); + // All types in the spec need to be canonical. + class Signature { + using ArgQualTypes = std::vector<QualType>; + ArgQualTypes ArgTys; + QualType RetTy; + // True if any component type is not found by lookup. + bool Invalid = false; + + public: + // 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) { + if (!Arg) { + Invalid = true; + return; + } else { + assertArgTypeSuitableForSignature(*Arg); + this->ArgTys.push_back(*Arg); + } + } + if (!RetTy) { + Invalid = true; + return; + } else { + assertRetTypeSuitableForSignature(*RetTy); + this->RetTy = *RetTy; } } + + bool isInvalid() const { return Invalid; } bool matches(const FunctionDecl *FD) const; private: @@ -380,7 +442,6 @@ class StdLibraryFunctionsChecker /// rules for the given parameter's type, those rules are checked once the /// signature is matched. class Summary { - const Signature Sign; const InvalidationKind InvalidationKd; Cases CaseConstraints; ConstraintSet ArgConstraints; @@ -390,14 +451,19 @@ class StdLibraryFunctionsChecker const FunctionDecl *FD = nullptr; public: - Summary(ArgTypes ArgTys, QualType RetTy, InvalidationKind InvalidationKd) - : Sign(ArgTys, RetTy), InvalidationKd(InvalidationKd) {} + Summary(InvalidationKind InvalidationKd) : InvalidationKd(InvalidationKd) {} - Summary &Case(ConstraintSet&& CS) { + Summary &Case(ConstraintSet &&CS) { CaseConstraints.push_back(std::move(CS)); return *this; } + Summary &Case(const ConstraintSet &CS) { + CaseConstraints.push_back(CS); + return *this; + } Summary &ArgConstraint(ValueConstraintPtr VC) { + assert(VC->getArgNo() != Ret && + "Arg constraint should not refer to the return value"); ArgConstraints.push_back(VC); return *this; } @@ -412,7 +478,7 @@ class StdLibraryFunctionsChecker // Returns true if the summary should be applied to the given function. // And if yes then store the function declaration. - bool matchesAndSet(const FunctionDecl *FD) { + bool matchesAndSet(const Signature &Sign, const FunctionDecl *FD) { bool Result = Sign.matches(FD) && validateByConstraints(FD); if (Result) { assert(!this->FD && "FD must not be set more than once"); @@ -472,17 +538,24 @@ private: void initFunctionSummaries(CheckerContext &C) const; void reportBug(const CallEvent &Call, ExplodedNode *N, - CheckerContext &C) const { + const ValueConstraint *VC, CheckerContext &C) const { if (!ChecksEnabled[CK_StdCLibraryFunctionArgsChecker]) return; - // TODO Add detailed diagnostic. - StringRef Msg = "Function argument constraint is not satisfied"; + // TODO Add more detailed diagnostic. + std::string Msg = + (Twine("Function argument constraint is not satisfied, constraint: ") + + VC->getName().data() + ", ArgN: " + Twine(VC->getArgNo())) + .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); - bugreporter::trackExpressionValue(N, Call.getArgExpr(0), *R); + bugreporter::trackExpressionValue(N, Call.getArgExpr(VC->getArgNo()), *R); + + // Highlight the range of the argument that was violated. + R->addRange(Call.getArgSourceRange(VC->getArgNo())); + C.emitReport(std::move(R)); } }; @@ -495,6 +568,8 @@ const StdLibraryFunctionsChecker::ArgNo StdLibraryFunctionsChecker::Ret = ProgramStateRef StdLibraryFunctionsChecker::RangeConstraint::applyAsOutOfRange( ProgramStateRef State, const CallEvent &Call, const Summary &Summary) const { + if (Ranges.empty()) + return State; ProgramStateManager &Mgr = State->getStateManager(); SValBuilder &SVB = Mgr.getSValBuilder(); @@ -522,6 +597,8 @@ ProgramStateRef StdLibraryFunctionsChecker::RangeConstraint::applyAsOutOfRange( ProgramStateRef StdLibraryFunctionsChecker::RangeConstraint::applyAsWithinRange( ProgramStateRef State, const CallEvent &Call, const Summary &Summary) const { + if (Ranges.empty()) + return State; ProgramStateManager &Mgr = State->getStateManager(); SValBuilder &SVB = Mgr.getSValBuilder(); @@ -615,7 +692,7 @@ void StdLibraryFunctionsChecker::checkPreCall(const CallEvent &Call, // The argument constraint is not satisfied. if (FailureSt && !SuccessSt) { if (ExplodedNode *N = C.generateErrorNode(NewState)) - reportBug(Call, N, C); + reportBug(Call, N, Constraint.get(), C); break; } else { // We will apply the constraint even if we cannot reason about the @@ -665,7 +742,7 @@ bool StdLibraryFunctionsChecker::evalCall(const CallEvent &Call, case EvalCallAsPure: { ProgramStateRef State = C.getState(); const LocationContext *LC = C.getLocationContext(); - const auto *CE = cast_or_null<CallExpr>(Call.getOriginExpr()); + const auto *CE = cast<CallExpr>(Call.getOriginExpr()); SVal V = C.getSValBuilder().conjureSymbolVal( CE, LC, CE->getType().getCanonicalType(), C.blockCount()); State = State->BindExpr(CE, LC, V); @@ -682,21 +759,39 @@ bool StdLibraryFunctionsChecker::evalCall(const CallEvent &Call, bool StdLibraryFunctionsChecker::Signature::matches( const FunctionDecl *FD) const { - // Check number of arguments: + assert(!isInvalid()); + // Check the number of arguments. if (FD->param_size() != ArgTys.size()) return false; - // Check return type. - if (!isIrrelevant(RetTy)) - if (RetTy != FD->getReturnType().getCanonicalType()) + // The "restrict" keyword is illegal in C++, however, many libc + // implementations use the "__restrict" compiler intrinsic in functions + // prototypes. The "__restrict" keyword qualifies a type as a restricted type + // even in C++. + // In case of any non-C99 languages, we don't want to match based on the + // restrict qualifier because we cannot know if the given libc implementation + // qualifies the paramter type or not. + auto RemoveRestrict = [&FD](QualType T) { + if (!FD->getASTContext().getLangOpts().C99) + T.removeLocalRestrict(); + return T; + }; + + // Check the return type. + if (!isIrrelevant(RetTy)) { + QualType FDRetTy = RemoveRestrict(FD->getReturnType().getCanonicalType()); + if (RetTy != FDRetTy) return false; + } - // Check argument types. + // Check the argument types. for (size_t I = 0, E = ArgTys.size(); I != E; ++I) { QualType ArgTy = ArgTys[I]; if (isIrrelevant(ArgTy)) continue; - if (ArgTy != FD->getParamDecl(I)->getType().getCanonicalType()) + QualType FDArgTy = + RemoveRestrict(FD->getParamDecl(I)->getType().getCanonicalType()); + if (ArgTy != FDArgTy) return false; } @@ -726,32 +821,6 @@ StdLibraryFunctionsChecker::findFunctionSummary(const CallEvent &Call, return findFunctionSummary(FD, C); } -static llvm::Optional<QualType> lookupType(StringRef Name, - const ASTContext &ACtx) { - IdentifierInfo &II = ACtx.Idents.get(Name); - auto LookupRes = ACtx.getTranslationUnitDecl()->lookup(&II); - if (LookupRes.size() == 0) - return None; - - // Prioritze typedef declarations. - // This is needed in case of C struct typedefs. E.g.: - // typedef struct FILE FILE; - // In this case, we have a RecordDecl 'struct FILE' with the name 'FILE' and - // we have a TypedefDecl with the name 'FILE'. - for (Decl *D : LookupRes) - if (auto *TD = dyn_cast<TypedefNameDecl>(D)) - return ACtx.getTypeDeclType(TD).getCanonicalType(); - - // Find the first TypeDecl. - // There maybe cases when a function has the same name as a struct. - // E.g. in POSIX: `struct stat` and the function `stat()`: - // int stat(const char *restrict path, struct stat *restrict buf); - for (Decl *D : LookupRes) - if (auto *TD = dyn_cast<TypeDecl>(D)) - return ACtx.getTypeDeclType(TD).getCanonicalType(); - return None; -} - void StdLibraryFunctionsChecker::initFunctionSummaries( CheckerContext &C) const { if (!FunctionSummaryMap.empty()) @@ -761,6 +830,91 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( BasicValueFactory &BVF = SVB.getBasicValueFactory(); const ASTContext &ACtx = BVF.getContext(); + // Helper class to lookup a type by its name. + class LookupType { + const ASTContext &ACtx; + + public: + LookupType(const ASTContext &ACtx) : ACtx(ACtx) {} + + // Find the type. If not found then the optional is not set. + llvm::Optional<QualType> operator()(StringRef Name) { + IdentifierInfo &II = ACtx.Idents.get(Name); + auto LookupRes = ACtx.getTranslationUnitDecl()->lookup(&II); + if (LookupRes.size() == 0) + return None; + + // Prioritze typedef declarations. + // This is needed in case of C struct typedefs. E.g.: + // typedef struct FILE FILE; + // In this case, we have a RecordDecl 'struct FILE' with the name 'FILE' + // and we have a TypedefDecl with the name 'FILE'. + for (Decl *D : LookupRes) + if (auto *TD = dyn_cast<TypedefNameDecl>(D)) + return ACtx.getTypeDeclType(TD).getCanonicalType(); + + // Find the first TypeDecl. + // There maybe cases when a function has the same name as a struct. + // E.g. in POSIX: `struct stat` and the function `stat()`: + // int stat(const char *restrict path, struct stat *restrict buf); + for (Decl *D : LookupRes) + if (auto *TD = dyn_cast<TypeDecl>(D)) + return ACtx.getTypeDeclType(TD).getCanonicalType(); + return None; + } + } lookupTy(ACtx); + + // Below are auxiliary classes to handle optional types that we get as a + // result of the lookup. + class GetRestrictTy { + const ASTContext &ACtx; + + public: + GetRestrictTy(const ASTContext &ACtx) : ACtx(ACtx) {} + QualType operator()(QualType Ty) { + return ACtx.getLangOpts().C99 ? ACtx.getRestrictType(Ty) : Ty; + } + Optional<QualType> operator()(Optional<QualType> Ty) { + if (Ty) + return operator()(*Ty); + return None; + } + } getRestrictTy(ACtx); + class GetPointerTy { + const ASTContext &ACtx; + + public: + GetPointerTy(const ASTContext &ACtx) : ACtx(ACtx) {} + QualType operator()(QualType Ty) { return ACtx.getPointerType(Ty); } + Optional<QualType> operator()(Optional<QualType> Ty) { + if (Ty) + return operator()(*Ty); + return None; + } + } getPointerTy(ACtx); + class { + public: + Optional<QualType> operator()(Optional<QualType> Ty) { + return Ty ? Optional<QualType>(Ty->withConst()) : None; + } + QualType operator()(QualType Ty) { return Ty.withConst(); } + } getConstTy; + class GetMaxValue { + BasicValueFactory &BVF; + + public: + GetMaxValue(BasicValueFactory &BVF) : BVF(BVF) {} + Optional<RangeInt> operator()(QualType Ty) { + return BVF.getMaxValue(Ty).getLimitedValue(); + } + Optional<RangeInt> operator()(Optional<QualType> Ty) { + if (Ty) { + return operator()(*Ty); + } + return None; + } + } getMaxValue(BVF); + // These types are useful for writing specifications quickly, // New specifications should probably introduce more types. // Some types are hard to obtain from the AST, eg. "ssize_t". @@ -769,44 +923,36 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( // or long long, so three summary variants would be enough). // Of course, function variants are also useful for C++ overloads. const QualType VoidTy = ACtx.VoidTy; + const QualType CharTy = ACtx.CharTy; + const QualType WCharTy = ACtx.WCharTy; const QualType IntTy = ACtx.IntTy; const QualType UnsignedIntTy = ACtx.UnsignedIntTy; const QualType LongTy = ACtx.LongTy; - const QualType LongLongTy = ACtx.LongLongTy; const QualType SizeTy = ACtx.getSizeType(); - const QualType VoidPtrTy = ACtx.VoidPtrTy; // void * - const QualType IntPtrTy = ACtx.getPointerType(IntTy); // int * + const QualType VoidPtrTy = getPointerTy(VoidTy); // void * + const QualType IntPtrTy = getPointerTy(IntTy); // int * const QualType UnsignedIntPtrTy = - ACtx.getPointerType(UnsignedIntTy); // unsigned int * - const QualType VoidPtrRestrictTy = - ACtx.getLangOpts().C99 ? ACtx.getRestrictType(VoidPtrTy) // void *restrict - : VoidPtrTy; + getPointerTy(UnsignedIntTy); // unsigned int * + const QualType VoidPtrRestrictTy = getRestrictTy(VoidPtrTy); const QualType ConstVoidPtrTy = - ACtx.getPointerType(ACtx.VoidTy.withConst()); // const void * - const QualType CharPtrTy = ACtx.getPointerType(ACtx.CharTy); // char * - const QualType CharPtrRestrictTy = - ACtx.getLangOpts().C99 ? ACtx.getRestrictType(CharPtrTy) // char *restrict - : CharPtrTy; + getPointerTy(getConstTy(VoidTy)); // const void * + const QualType CharPtrTy = getPointerTy(CharTy); // char * + const QualType CharPtrRestrictTy = getRestrictTy(CharPtrTy); const QualType ConstCharPtrTy = - ACtx.getPointerType(ACtx.CharTy.withConst()); // const char * - const QualType ConstCharPtrRestrictTy = - ACtx.getLangOpts().C99 - ? ACtx.getRestrictType(ConstCharPtrTy) // const char *restrict - : ConstCharPtrTy; - const QualType Wchar_tPtrTy = ACtx.getPointerType(ACtx.WCharTy); // wchar_t * + getPointerTy(getConstTy(CharTy)); // const char * + const QualType ConstCharPtrRestrictTy = getRestrictTy(ConstCharPtrTy); + const QualType Wchar_tPtrTy = getPointerTy(WCharTy); // wchar_t * const QualType ConstWchar_tPtrTy = - ACtx.getPointerType(ACtx.WCharTy.withConst()); // const wchar_t * - const QualType ConstVoidPtrRestrictTy = - ACtx.getLangOpts().C99 - ? ACtx.getRestrictType(ConstVoidPtrTy) // const void *restrict - : ConstVoidPtrTy; + getPointerTy(getConstTy(WCharTy)); // const wchar_t * + const QualType ConstVoidPtrRestrictTy = getRestrictTy(ConstVoidPtrTy); + const QualType SizePtrTy = getPointerTy(SizeTy); + const QualType SizePtrRestrictTy = getRestrictTy(SizePtrTy); const RangeInt IntMax = BVF.getMaxValue(IntTy).getLimitedValue(); const RangeInt UnsignedIntMax = BVF.getMaxValue(UnsignedIntTy).getLimitedValue(); const RangeInt LongMax = BVF.getMaxValue(LongTy).getLimitedValue(); - const RangeInt LongLongMax = BVF.getMaxValue(LongLongTy).getLimitedValue(); const RangeInt SizeMax = BVF.getMaxValue(SizeTy).getLimitedValue(); // Set UCharRangeMax to min of int or uchar maximum value. @@ -840,15 +986,19 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( // Add a summary to a FunctionDecl found by lookup. The lookup is performed // by the given Name, and in the global scope. The summary will be attached // to the found FunctionDecl only if the signatures match. - void operator()(StringRef Name, Summary S) { + // + // Returns true if the summary has been added, false otherwise. + bool operator()(StringRef Name, Signature Sign, Summary Sum) { + if (Sign.isInvalid()) + return false; IdentifierInfo &II = ACtx.Idents.get(Name); auto LookupRes = ACtx.getTranslationUnitDecl()->lookup(&II); if (LookupRes.size() == 0) - return; + return false; for (Decl *D : LookupRes) { if (auto *FD = dyn_cast<FunctionDecl>(D)) { - if (S.matchesAndSet(FD)) { - auto Res = Map.insert({FD->getCanonicalDecl(), S}); + if (Sum.matchesAndSet(Sign, FD)) { + auto Res = Map.insert({FD->getCanonicalDecl(), Sum}); assert(Res.second && "Function already has a summary set!"); (void)Res; if (DisplayLoadedSummaries) { @@ -856,44 +1006,20 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( FD->print(llvm::errs()); llvm::errs() << "\n"; } - return; + return true; } } } + return false; } - // Add several summaries for the given name. - void operator()(StringRef Name, const std::vector<Summary> &Summaries) { - for (const Summary &S : Summaries) - operator()(Name, S); + // Add the same summary for different names with the Signature explicitly + // given. + void operator()(std::vector<StringRef> Names, Signature Sign, Summary Sum) { + for (StringRef Name : Names) + operator()(Name, Sign, Sum); } } addToFunctionSummaryMap(ACtx, FunctionSummaryMap, DisplayLoadedSummaries); - // We are finally ready to define specifications for all supported functions. - // - // The signature needs to have the correct number of arguments. - // However, we insert `Irrelevant' when the type is insignificant. - // - // Argument ranges should always cover all variants. If return value - // is completely unknown, omit it from the respective range set. - // - // All types in the spec need to be canonical. - // - // Every item in the list of range sets represents a particular - // execution path the analyzer would need to explore once - // the call is modeled - a new program state is constructed - // for every range set, and each range line in the range set - // corresponds to a specific constraint within this state. - // - // Upon comparing to another argument, the other argument is casted - // to the current argument's type. This avoids proper promotion but - // seems useful. For example, read() receives size_t argument, - // and its return value, which is of type ssize_t, cannot be greater - // than this argument. If we made a promotion, and the size argument - // is equal to, say, 10, then we'd impose a range of [0, 10] on the - // return value, however the correct range is [-1, 10]. - // - // Please update the list of functions in the header after editing! - // Below are helpers functions to create the summaries. auto ArgumentCondition = [](ArgNo ArgN, RangeKind Kind, IntRangeVector Ranges) { @@ -910,9 +1036,22 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( return std::make_shared<ComparisonConstraint>(Ret, Op, OtherArgN); } } ReturnValueCondition; - auto Range = [](RangeInt b, RangeInt e) { - return IntRangeVector{std::pair<RangeInt, RangeInt>{b, e}}; - }; + struct { + auto operator()(RangeInt b, RangeInt e) { + return IntRangeVector{std::pair<RangeInt, RangeInt>{b, e}}; + } + auto operator()(RangeInt b, 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) { + if (i1.second) + return IntRangeVector{i0, {i1.first, *(i1.second)}}; + return IntRangeVector{i0}; + } + } Range; auto SingleValue = [](RangeInt v) { return IntRangeVector{std::pair<RangeInt, RangeInt>{v, v}}; }; @@ -921,60 +1060,28 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( return std::make_shared<NotNullConstraint>(ArgN); }; - Optional<QualType> FileTy = lookupType("FILE", ACtx); - Optional<QualType> FilePtrTy, FilePtrRestrictTy; - if (FileTy) { - // FILE * - FilePtrTy = ACtx.getPointerType(*FileTy); - // FILE *restrict - FilePtrRestrictTy = - ACtx.getLangOpts().C99 ? ACtx.getRestrictType(*FilePtrTy) : *FilePtrTy; - } + Optional<QualType> FileTy = lookupTy("FILE"); + Optional<QualType> FilePtrTy = getPointerTy(FileTy); + Optional<QualType> FilePtrRestrictTy = getRestrictTy(FilePtrTy); - using RetType = QualType; - // Templates for summaries that are reused by many functions. - auto Getc = [&]() { - return Summary(ArgTypes{*FilePtrTy}, RetType{IntTy}, NoEvalCall) - .Case({ReturnValueCondition(WithinRange, - {{EOFv, EOFv}, {0, UCharRangeMax}})}); - }; - auto Read = [&](RetType R, RangeInt Max) { - return Summary(ArgTypes{Irrelevant, Irrelevant, SizeTy}, RetType{R}, - NoEvalCall) - .Case({ReturnValueCondition(LessThanOrEq, ArgNo(2)), - ReturnValueCondition(WithinRange, Range(-1, Max))}); - }; - auto Fread = [&]() { - return Summary( - ArgTypes{VoidPtrRestrictTy, SizeTy, SizeTy, *FilePtrRestrictTy}, - RetType{SizeTy}, NoEvalCall) - .Case({ - ReturnValueCondition(LessThanOrEq, ArgNo(2)), - }) - .ArgConstraint(NotNull(ArgNo(0))); - }; - auto Fwrite = [&]() { - return Summary(ArgTypes{ConstVoidPtrRestrictTy, SizeTy, SizeTy, - *FilePtrRestrictTy}, - RetType{SizeTy}, NoEvalCall) - .Case({ - ReturnValueCondition(LessThanOrEq, ArgNo(2)), - }) - .ArgConstraint(NotNull(ArgNo(0))); - }; - auto Getline = [&](RetType R, RangeInt Max) { - return Summary(ArgTypes{Irrelevant, Irrelevant, Irrelevant}, RetType{R}, - NoEvalCall) - .Case({ReturnValueCondition(WithinRange, {{-1, -1}, {1, Max}})}); - }; + // We are finally ready to define specifications for all supported functions. + // + // Argument ranges should always cover all variants. If return value + // is completely unknown, omit it from the respective range set. + // + // Every item in the list of range sets represents a particular + // execution path the analyzer would need to explore once + // the call is modeled - a new program state is constructed + // for every range set, and each range line in the range set + // corresponds to a specific constraint within this state. // The isascii() family of functions. // The behavior is undefined if the value of the argument is not // representable as unsigned char or is not equal to EOF. See e.g. C99 // 7.4.1.2 The isalpha function (p: 181-182). addToFunctionSummaryMap( - "isalnum", - Summary(ArgTypes{IntTy}, RetType{IntTy}, EvalCallAsPure) + "isalnum", Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) // Boils down to isupper() or islower() or isdigit(). .Case({ArgumentCondition(0U, WithinRange, {{'0', '9'}, {'A', 'Z'}, {'a', 'z'}}), @@ -991,8 +1098,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( .ArgConstraint(ArgumentCondition( 0U, WithinRange, {{EOFv, EOFv}, {0, UCharRangeMax}}))); addToFunctionSummaryMap( - "isalpha", - Summary(ArgTypes{IntTy}, RetType{IntTy}, EvalCallAsPure) + "isalpha", Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) .Case({ArgumentCondition(0U, WithinRange, {{'A', 'Z'}, {'a', 'z'}}), ReturnValueCondition(OutOfRange, SingleValue(0))}) // The locale-specific range. @@ -1002,43 +1109,43 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( {{'A', 'Z'}, {'a', 'z'}, {128, UCharRangeMax}}), ReturnValueCondition(WithinRange, SingleValue(0))})); addToFunctionSummaryMap( - "isascii", - Summary(ArgTypes{IntTy}, RetType{IntTy}, EvalCallAsPure) + "isascii", Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) .Case({ArgumentCondition(0U, WithinRange, Range(0, 127)), ReturnValueCondition(OutOfRange, SingleValue(0))}) .Case({ArgumentCondition(0U, OutOfRange, Range(0, 127)), ReturnValueCondition(WithinRange, SingleValue(0))})); addToFunctionSummaryMap( - "isblank", - Summary(ArgTypes{IntTy}, RetType{IntTy}, EvalCallAsPure) + "isblank", Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) .Case({ArgumentCondition(0U, WithinRange, {{'\t', '\t'}, {' ', ' '}}), ReturnValueCondition(OutOfRange, SingleValue(0))}) .Case({ArgumentCondition(0U, OutOfRange, {{'\t', '\t'}, {' ', ' '}}), ReturnValueCondition(WithinRange, SingleValue(0))})); addToFunctionSummaryMap( - "iscntrl", - Summary(ArgTypes{IntTy}, RetType{IntTy}, EvalCallAsPure) + "iscntrl", Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) .Case({ArgumentCondition(0U, WithinRange, {{0, 32}, {127, 127}}), ReturnValueCondition(OutOfRange, SingleValue(0))}) .Case({ArgumentCondition(0U, OutOfRange, {{0, 32}, {127, 127}}), ReturnValueCondition(WithinRange, SingleValue(0))})); addToFunctionSummaryMap( - "isdigit", - Summary(ArgTypes{IntTy}, RetType{IntTy}, EvalCallAsPure) + "isdigit", Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) .Case({ArgumentCondition(0U, WithinRange, Range('0', '9')), ReturnValueCondition(OutOfRange, SingleValue(0))}) .Case({ArgumentCondition(0U, OutOfRange, Range('0', '9')), ReturnValueCondition(WithinRange, SingleValue(0))})); addToFunctionSummaryMap( - "isgraph", - Summary(ArgTypes{IntTy}, RetType{IntTy}, EvalCallAsPure) + "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))})); addToFunctionSummaryMap( - "islower", - Summary(ArgTypes{IntTy}, RetType{IntTy}, EvalCallAsPure) + "islower", Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) // Is certainly lowercase. .Case({ArgumentCondition(0U, WithinRange, Range('a', 'z')), ReturnValueCondition(OutOfRange, SingleValue(0))}) @@ -1052,15 +1159,15 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( .Case({ArgumentCondition(0U, OutOfRange, Range(0, UCharRangeMax)), ReturnValueCondition(WithinRange, SingleValue(0))})); addToFunctionSummaryMap( - "isprint", - Summary(ArgTypes{IntTy}, RetType{IntTy}, EvalCallAsPure) + "isprint", Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) .Case({ArgumentCondition(0U, WithinRange, Range(32, 126)), ReturnValueCondition(OutOfRange, SingleValue(0))}) .Case({ArgumentCondition(0U, OutOfRange, Range(32, 126)), ReturnValueCondition(WithinRange, SingleValue(0))})); addToFunctionSummaryMap( - "ispunct", - Summary(ArgTypes{IntTy}, RetType{IntTy}, EvalCallAsPure) + "ispunct", Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) .Case({ArgumentCondition( 0U, WithinRange, {{'!', '/'}, {':', '@'}, {'[', '`'}, {'{', '~'}}), @@ -1070,8 +1177,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( {{'!', '/'}, {':', '@'}, {'[', '`'}, {'{', '~'}}), ReturnValueCondition(WithinRange, SingleValue(0))})); addToFunctionSummaryMap( - "isspace", - Summary(ArgTypes{IntTy}, RetType{IntTy}, EvalCallAsPure) + "isspace", Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) // Space, '\f', '\n', '\r', '\t', '\v'. .Case({ArgumentCondition(0U, WithinRange, {{9, 13}, {' ', ' '}}), ReturnValueCondition(OutOfRange, SingleValue(0))}) @@ -1081,8 +1188,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( {{9, 13}, {' ', ' '}, {128, UCharRangeMax}}), ReturnValueCondition(WithinRange, SingleValue(0))})); addToFunctionSummaryMap( - "isupper", - Summary(ArgTypes{IntTy}, RetType{IntTy}, EvalCallAsPure) + "isupper", Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) // Is certainly uppercase. .Case({ArgumentCondition(0U, WithinRange, Range('A', 'Z')), ReturnValueCondition(OutOfRange, SingleValue(0))}) @@ -1093,650 +1200,1290 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( {{'A', 'Z'}, {128, UCharRangeMax}}), ReturnValueCondition(WithinRange, SingleValue(0))})); addToFunctionSummaryMap( - "isxdigit", - Summary(ArgTypes{IntTy}, RetType{IntTy}, EvalCallAsPure) + "isxdigit", Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) .Case({ArgumentCondition(0U, WithinRange, {{'0', '9'}, {'A', 'F'}, {'a', 'f'}}), ReturnValueCondition(OutOfRange, SingleValue(0))}) .Case({ArgumentCondition(0U, OutOfRange, {{'0', '9'}, {'A', 'F'}, {'a', 'f'}}), ReturnValueCondition(WithinRange, SingleValue(0))})); + addToFunctionSummaryMap( + "toupper", Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) + .ArgConstraint(ArgumentCondition( + 0U, WithinRange, {{EOFv, EOFv}, {0, UCharRangeMax}}))); + addToFunctionSummaryMap( + "tolower", Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) + .ArgConstraint(ArgumentCondition( + 0U, WithinRange, {{EOFv, EOFv}, {0, UCharRangeMax}}))); + addToFunctionSummaryMap( + "toascii", Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) + .ArgConstraint(ArgumentCondition( + 0U, WithinRange, {{EOFv, EOFv}, {0, UCharRangeMax}}))); // The getc() family of functions that returns either a char or an EOF. - if (FilePtrTy) { - addToFunctionSummaryMap("getc", Getc()); - addToFunctionSummaryMap("fgetc", Getc()); - } addToFunctionSummaryMap( - "getchar", Summary(ArgTypes{}, RetType{IntTy}, NoEvalCall) - .Case({ReturnValueCondition( - WithinRange, {{EOFv, EOFv}, {0, UCharRangeMax}})})); + {"getc", "fgetc"}, Signature(ArgTypes{FilePtrTy}, RetType{IntTy}), + Summary(NoEvalCall) + .Case({ReturnValueCondition(WithinRange, + {{EOFv, EOFv}, {0, UCharRangeMax}})})); + addToFunctionSummaryMap( + "getchar", Signature(ArgTypes{}, RetType{IntTy}), + Summary(NoEvalCall) + .Case({ReturnValueCondition(WithinRange, + {{EOFv, EOFv}, {0, UCharRangeMax}})})); // read()-like functions that never return more than buffer size. - if (FilePtrRestrictTy) { - addToFunctionSummaryMap("fread", Fread()); - addToFunctionSummaryMap("fwrite", Fwrite()); - } + auto FreadSummary = + Summary(NoEvalCall) + .Case({ReturnValueCondition(LessThanOrEq, ArgNo(2)), + ReturnValueCondition(WithinRange, Range(0, SizeMax))}) + .ArgConstraint(NotNull(ArgNo(0))) + .ArgConstraint(NotNull(ArgNo(3))) + .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(0), /*BufSize=*/ArgNo(1), + /*BufSizeMultiplier=*/ArgNo(2))); + + // size_t fread(void *restrict ptr, size_t size, size_t nitems, + // FILE *restrict stream); + addToFunctionSummaryMap( + "fread", + Signature(ArgTypes{VoidPtrRestrictTy, SizeTy, SizeTy, FilePtrRestrictTy}, + RetType{SizeTy}), + FreadSummary); + // size_t fwrite(const void *restrict ptr, size_t size, size_t nitems, + // FILE *restrict stream); + addToFunctionSummaryMap("fwrite", + Signature(ArgTypes{ConstVoidPtrRestrictTy, SizeTy, + SizeTy, FilePtrRestrictTy}, + RetType{SizeTy}), + FreadSummary); + + Optional<QualType> Ssize_tTy = lookupTy("ssize_t"); + Optional<RangeInt> Ssize_tMax = getMaxValue(Ssize_tTy); + + auto ReadSummary = + Summary(NoEvalCall) + .Case({ReturnValueCondition(LessThanOrEq, ArgNo(2)), + ReturnValueCondition(WithinRange, Range(-1, Ssize_tMax))}); - // We are not sure how ssize_t is defined on every platform, so we - // provide three variants that should cover common cases. // 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. - addToFunctionSummaryMap("read", {Read(IntTy, IntMax), Read(LongTy, LongMax), - Read(LongLongTy, LongLongMax)}); - addToFunctionSummaryMap("write", {Read(IntTy, IntMax), Read(LongTy, LongMax), - Read(LongLongTy, LongLongMax)}); + // ssize_t read(int fildes, void *buf, size_t nbyte); + addToFunctionSummaryMap( + "read", Signature(ArgTypes{IntTy, VoidPtrTy, SizeTy}, RetType{Ssize_tTy}), + ReadSummary); + // ssize_t write(int fildes, const void *buf, size_t nbyte); + addToFunctionSummaryMap( + "write", + Signature(ArgTypes{IntTy, ConstVoidPtrTy, SizeTy}, RetType{Ssize_tTy}), + ReadSummary); + + auto GetLineSummary = + Summary(NoEvalCall) + .Case({ReturnValueCondition(WithinRange, + Range({-1, -1}, {1, Ssize_tMax}))}); + + QualType CharPtrPtrRestrictTy = getRestrictTy(getPointerTy(CharPtrTy)); // getline()-like functions either fail or read at least the delimiter. // 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. - addToFunctionSummaryMap("getline", - {Getline(IntTy, IntMax), Getline(LongTy, LongMax), - Getline(LongLongTy, LongLongMax)}); - addToFunctionSummaryMap("getdelim", - {Getline(IntTy, IntMax), Getline(LongTy, LongMax), - Getline(LongLongTy, LongLongMax)}); + // ssize_t getline(char **restrict lineptr, size_t *restrict n, + // FILE *restrict stream); + addToFunctionSummaryMap( + "getline", + Signature( + ArgTypes{CharPtrPtrRestrictTy, SizePtrRestrictTy, FilePtrRestrictTy}, + RetType{Ssize_tTy}), + GetLineSummary); + // ssize_t getdelim(char **restrict lineptr, size_t *restrict n, + // int delimiter, FILE *restrict stream); + addToFunctionSummaryMap( + "getdelim", + Signature(ArgTypes{CharPtrPtrRestrictTy, SizePtrRestrictTy, IntTy, + FilePtrRestrictTy}, + RetType{Ssize_tTy}), + GetLineSummary); if (ModelPOSIX) { // long a64l(const char *str64); addToFunctionSummaryMap( - "a64l", Summary(ArgTypes{ConstCharPtrTy}, RetType{LongTy}, NoEvalCall) - .ArgConstraint(NotNull(ArgNo(0)))); + "a64l", Signature(ArgTypes{ConstCharPtrTy}, RetType{LongTy}), + Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); // char *l64a(long value); - addToFunctionSummaryMap( - "l64a", Summary(ArgTypes{LongTy}, RetType{CharPtrTy}, NoEvalCall) - .ArgConstraint( - ArgumentCondition(0, WithinRange, Range(0, LongMax)))); + addToFunctionSummaryMap("l64a", + Signature(ArgTypes{LongTy}, RetType{CharPtrTy}), + Summary(NoEvalCall) + .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 access(const char *pathname, int amode); - addToFunctionSummaryMap("access", Summary(ArgTypes{ConstCharPtrTy, IntTy}, - RetType{IntTy}, NoEvalCall) - .ArgConstraint(NotNull(ArgNo(0)))); + addToFunctionSummaryMap( + "access", Signature(ArgTypes{ConstCharPtrTy, IntTy}, RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZeroOrMinusOne) + .ArgConstraint(NotNull(ArgNo(0)))); // int faccessat(int dirfd, const char *pathname, int mode, int flags); addToFunctionSummaryMap( - "faccessat", Summary(ArgTypes{IntTy, ConstCharPtrTy, IntTy, IntTy}, - RetType{IntTy}, NoEvalCall) - .ArgConstraint(NotNull(ArgNo(1)))); + "faccessat", + Signature(ArgTypes{IntTy, ConstCharPtrTy, IntTy, IntTy}, + RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZeroOrMinusOne) + .ArgConstraint(NotNull(ArgNo(1)))); // int dup(int fildes); - addToFunctionSummaryMap( - "dup", Summary(ArgTypes{IntTy}, RetType{IntTy}, NoEvalCall) - .ArgConstraint( - ArgumentCondition(0, WithinRange, Range(0, IntMax)))); + addToFunctionSummaryMap("dup", Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsFileDescriptor) + .ArgConstraint(ArgumentCondition( + 0, WithinRange, Range(0, IntMax)))); // int dup2(int fildes1, int filedes2); addToFunctionSummaryMap( - "dup2", - Summary(ArgTypes{IntTy, IntTy}, RetType{IntTy}, NoEvalCall) + "dup2", Signature(ArgTypes{IntTy, IntTy}, RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsFileDescriptor) .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax))) .ArgConstraint( ArgumentCondition(1, WithinRange, Range(0, IntMax)))); // int fdatasync(int fildes); - addToFunctionSummaryMap( - "fdatasync", Summary(ArgTypes{IntTy}, RetType{IntTy}, NoEvalCall) - .ArgConstraint(ArgumentCondition(0, WithinRange, - Range(0, IntMax)))); + addToFunctionSummaryMap("fdatasync", + Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZeroOrMinusOne) + .ArgConstraint(ArgumentCondition( + 0, WithinRange, Range(0, IntMax)))); // int fnmatch(const char *pattern, const char *string, int flags); addToFunctionSummaryMap( - "fnmatch", Summary(ArgTypes{ConstCharPtrTy, ConstCharPtrTy, IntTy}, - RetType{IntTy}, EvalCallAsPure) - .ArgConstraint(NotNull(ArgNo(0))) - .ArgConstraint(NotNull(ArgNo(1)))); + "fnmatch", + Signature(ArgTypes{ConstCharPtrTy, ConstCharPtrTy, IntTy}, + RetType{IntTy}), + Summary(EvalCallAsPure) + .ArgConstraint(NotNull(ArgNo(0))) + .ArgConstraint(NotNull(ArgNo(1)))); // int fsync(int fildes); - addToFunctionSummaryMap( - "fsync", Summary(ArgTypes{IntTy}, RetType{IntTy}, NoEvalCall) - .ArgConstraint( - ArgumentCondition(0, WithinRange, Range(0, IntMax)))); + addToFunctionSummaryMap("fsync", Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZeroOrMinusOne) + .ArgConstraint(ArgumentCondition( + 0, WithinRange, Range(0, IntMax)))); - Optional<QualType> Off_tTy = lookupType("off_t", ACtx); + Optional<QualType> Off_tTy = lookupTy("off_t"); - if (Off_tTy) - // int truncate(const char *path, off_t length); - addToFunctionSummaryMap("truncate", - Summary(ArgTypes{ConstCharPtrTy, *Off_tTy}, - RetType{IntTy}, NoEvalCall) - .ArgConstraint(NotNull(ArgNo(0)))); + // int truncate(const char *path, off_t length); + addToFunctionSummaryMap( + "truncate", + Signature(ArgTypes{ConstCharPtrTy, Off_tTy}, RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZeroOrMinusOne) + .ArgConstraint(NotNull(ArgNo(0)))); // int symlink(const char *oldpath, const char *newpath); - addToFunctionSummaryMap("symlink", - Summary(ArgTypes{ConstCharPtrTy, ConstCharPtrTy}, - RetType{IntTy}, NoEvalCall) - .ArgConstraint(NotNull(ArgNo(0))) - .ArgConstraint(NotNull(ArgNo(1)))); + addToFunctionSummaryMap( + "symlink", + Signature(ArgTypes{ConstCharPtrTy, ConstCharPtrTy}, RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZeroOrMinusOne) + .ArgConstraint(NotNull(ArgNo(0))) + .ArgConstraint(NotNull(ArgNo(1)))); // int symlinkat(const char *oldpath, int newdirfd, const char *newpath); addToFunctionSummaryMap( "symlinkat", - Summary(ArgTypes{ConstCharPtrTy, IntTy, ConstCharPtrTy}, RetType{IntTy}, - NoEvalCall) + Signature(ArgTypes{ConstCharPtrTy, IntTy, ConstCharPtrTy}, + RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZeroOrMinusOne) .ArgConstraint(NotNull(ArgNo(0))) .ArgConstraint(ArgumentCondition(1, WithinRange, Range(0, IntMax))) .ArgConstraint(NotNull(ArgNo(2)))); - if (Off_tTy) - // int lockf(int fd, int cmd, off_t len); - addToFunctionSummaryMap( - "lockf", - Summary(ArgTypes{IntTy, IntTy, *Off_tTy}, RetType{IntTy}, NoEvalCall) - .ArgConstraint( - ArgumentCondition(0, WithinRange, Range(0, IntMax)))); + // int lockf(int fd, int cmd, off_t len); + addToFunctionSummaryMap( + "lockf", Signature(ArgTypes{IntTy, IntTy, Off_tTy}, RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZeroOrMinusOne) + .ArgConstraint( + ArgumentCondition(0, WithinRange, Range(0, IntMax)))); - Optional<QualType> Mode_tTy = lookupType("mode_t", ACtx); + Optional<QualType> Mode_tTy = lookupTy("mode_t"); - if (Mode_tTy) - // int creat(const char *pathname, mode_t mode); - addToFunctionSummaryMap("creat", - Summary(ArgTypes{ConstCharPtrTy, *Mode_tTy}, - RetType{IntTy}, NoEvalCall) - .ArgConstraint(NotNull(ArgNo(0)))); + // int creat(const char *pathname, mode_t mode); + addToFunctionSummaryMap( + "creat", Signature(ArgTypes{ConstCharPtrTy, Mode_tTy}, RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsFileDescriptor) + .ArgConstraint(NotNull(ArgNo(0)))); // unsigned int sleep(unsigned int seconds); addToFunctionSummaryMap( - "sleep", - Summary(ArgTypes{UnsignedIntTy}, RetType{UnsignedIntTy}, NoEvalCall) + "sleep", Signature(ArgTypes{UnsignedIntTy}, RetType{UnsignedIntTy}), + Summary(NoEvalCall) .ArgConstraint( ArgumentCondition(0, WithinRange, Range(0, UnsignedIntMax)))); - Optional<QualType> DirTy = lookupType("DIR", ACtx); - Optional<QualType> DirPtrTy; - if (DirTy) - DirPtrTy = ACtx.getPointerType(*DirTy); + Optional<QualType> DirTy = lookupTy("DIR"); + Optional<QualType> DirPtrTy = getPointerTy(DirTy); - if (DirPtrTy) - // int dirfd(DIR *dirp); - addToFunctionSummaryMap( - "dirfd", Summary(ArgTypes{*DirPtrTy}, RetType{IntTy}, NoEvalCall) - .ArgConstraint(NotNull(ArgNo(0)))); + // int dirfd(DIR *dirp); + addToFunctionSummaryMap("dirfd", + Signature(ArgTypes{DirPtrTy}, RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsFileDescriptor) + .ArgConstraint(NotNull(ArgNo(0)))); // unsigned int alarm(unsigned int seconds); addToFunctionSummaryMap( - "alarm", - Summary(ArgTypes{UnsignedIntTy}, RetType{UnsignedIntTy}, NoEvalCall) + "alarm", Signature(ArgTypes{UnsignedIntTy}, RetType{UnsignedIntTy}), + Summary(NoEvalCall) .ArgConstraint( ArgumentCondition(0, WithinRange, Range(0, UnsignedIntMax)))); - if (DirPtrTy) - // int closedir(DIR *dir); - addToFunctionSummaryMap( - "closedir", Summary(ArgTypes{*DirPtrTy}, RetType{IntTy}, NoEvalCall) - .ArgConstraint(NotNull(ArgNo(0)))); + // int closedir(DIR *dir); + addToFunctionSummaryMap("closedir", + Signature(ArgTypes{DirPtrTy}, RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZeroOrMinusOne) + .ArgConstraint(NotNull(ArgNo(0)))); // char *strdup(const char *s); - addToFunctionSummaryMap("strdup", Summary(ArgTypes{ConstCharPtrTy}, - RetType{CharPtrTy}, NoEvalCall) - .ArgConstraint(NotNull(ArgNo(0)))); + addToFunctionSummaryMap( + "strdup", Signature(ArgTypes{ConstCharPtrTy}, RetType{CharPtrTy}), + Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); // char *strndup(const char *s, size_t n); addToFunctionSummaryMap( - "strndup", Summary(ArgTypes{ConstCharPtrTy, SizeTy}, RetType{CharPtrTy}, - NoEvalCall) - .ArgConstraint(NotNull(ArgNo(0))) - .ArgConstraint(ArgumentCondition(1, WithinRange, - Range(0, SizeMax)))); + "strndup", + Signature(ArgTypes{ConstCharPtrTy, SizeTy}, RetType{CharPtrTy}), + Summary(NoEvalCall) + .ArgConstraint(NotNull(ArgNo(0))) + .ArgConstraint( + ArgumentCondition(1, WithinRange, Range(0, SizeMax)))); // wchar_t *wcsdup(const wchar_t *s); - addToFunctionSummaryMap("wcsdup", Summary(ArgTypes{ConstWchar_tPtrTy}, - RetType{Wchar_tPtrTy}, NoEvalCall) - .ArgConstraint(NotNull(ArgNo(0)))); + addToFunctionSummaryMap( + "wcsdup", Signature(ArgTypes{ConstWchar_tPtrTy}, RetType{Wchar_tPtrTy}), + Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); // int mkstemp(char *template); - addToFunctionSummaryMap( - "mkstemp", Summary(ArgTypes{CharPtrTy}, RetType{IntTy}, NoEvalCall) - .ArgConstraint(NotNull(ArgNo(0)))); + addToFunctionSummaryMap("mkstemp", + Signature(ArgTypes{CharPtrTy}, RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsFileDescriptor) + .ArgConstraint(NotNull(ArgNo(0)))); // char *mkdtemp(char *template); addToFunctionSummaryMap( - "mkdtemp", Summary(ArgTypes{CharPtrTy}, RetType{CharPtrTy}, NoEvalCall) - .ArgConstraint(NotNull(ArgNo(0)))); + "mkdtemp", Signature(ArgTypes{CharPtrTy}, RetType{CharPtrTy}), + Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); // char *getcwd(char *buf, size_t size); addToFunctionSummaryMap( - "getcwd", - Summary(ArgTypes{CharPtrTy, SizeTy}, RetType{CharPtrTy}, NoEvalCall) + "getcwd", Signature(ArgTypes{CharPtrTy, SizeTy}, RetType{CharPtrTy}), + Summary(NoEvalCall) .ArgConstraint( ArgumentCondition(1, WithinRange, Range(0, SizeMax)))); - if (Mode_tTy) { - // int mkdir(const char *pathname, mode_t mode); - addToFunctionSummaryMap("mkdir", - Summary(ArgTypes{ConstCharPtrTy, *Mode_tTy}, - RetType{IntTy}, NoEvalCall) - .ArgConstraint(NotNull(ArgNo(0)))); + // int mkdir(const char *pathname, mode_t mode); + addToFunctionSummaryMap( + "mkdir", Signature(ArgTypes{ConstCharPtrTy, Mode_tTy}, RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZeroOrMinusOne) + .ArgConstraint(NotNull(ArgNo(0)))); - // int mkdirat(int dirfd, const char *pathname, mode_t mode); - addToFunctionSummaryMap( - "mkdirat", Summary(ArgTypes{IntTy, ConstCharPtrTy, *Mode_tTy}, - RetType{IntTy}, NoEvalCall) - .ArgConstraint(NotNull(ArgNo(1)))); - } + // int mkdirat(int dirfd, const char *pathname, mode_t mode); + addToFunctionSummaryMap( + "mkdirat", + Signature(ArgTypes{IntTy, ConstCharPtrTy, Mode_tTy}, RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZeroOrMinusOne) + .ArgConstraint(NotNull(ArgNo(1)))); - Optional<QualType> Dev_tTy = lookupType("dev_t", ACtx); + Optional<QualType> Dev_tTy = lookupTy("dev_t"); - if (Mode_tTy && Dev_tTy) { - // int mknod(const char *pathname, mode_t mode, dev_t dev); - addToFunctionSummaryMap( - "mknod", Summary(ArgTypes{ConstCharPtrTy, *Mode_tTy, *Dev_tTy}, - RetType{IntTy}, NoEvalCall) - .ArgConstraint(NotNull(ArgNo(0)))); - - // int mknodat(int dirfd, const char *pathname, mode_t mode, dev_t dev); - addToFunctionSummaryMap("mknodat", Summary(ArgTypes{IntTy, ConstCharPtrTy, - *Mode_tTy, *Dev_tTy}, - RetType{IntTy}, NoEvalCall) - .ArgConstraint(NotNull(ArgNo(1)))); - } + // 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) + .ArgConstraint(NotNull(ArgNo(0)))); - if (Mode_tTy) { - // int chmod(const char *path, mode_t mode); - addToFunctionSummaryMap("chmod", - Summary(ArgTypes{ConstCharPtrTy, *Mode_tTy}, - RetType{IntTy}, NoEvalCall) - .ArgConstraint(NotNull(ArgNo(0)))); + // int mknodat(int dirfd, const char *pathname, mode_t mode, dev_t dev); + addToFunctionSummaryMap( + "mknodat", + Signature(ArgTypes{IntTy, ConstCharPtrTy, Mode_tTy, Dev_tTy}, + RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZeroOrMinusOne) + .ArgConstraint(NotNull(ArgNo(1)))); - // int fchmodat(int dirfd, const char *pathname, mode_t mode, int flags); - addToFunctionSummaryMap( - "fchmodat", Summary(ArgTypes{IntTy, ConstCharPtrTy, *Mode_tTy, IntTy}, - RetType{IntTy}, NoEvalCall) - .ArgConstraint(ArgumentCondition(0, WithinRange, - Range(0, IntMax))) - .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) + .ArgConstraint(NotNull(ArgNo(0)))); - // int fchmod(int fildes, mode_t mode); - addToFunctionSummaryMap( - "fchmod", - Summary(ArgTypes{IntTy, *Mode_tTy}, RetType{IntTy}, NoEvalCall) - .ArgConstraint( - ArgumentCondition(0, WithinRange, Range(0, IntMax)))); - } + // int fchmodat(int dirfd, const char *pathname, mode_t mode, int flags); + addToFunctionSummaryMap( + "fchmodat", + Signature(ArgTypes{IntTy, ConstCharPtrTy, Mode_tTy, IntTy}, + RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZeroOrMinusOne) + .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax))) + .ArgConstraint(NotNull(ArgNo(1)))); - Optional<QualType> Uid_tTy = lookupType("uid_t", ACtx); - Optional<QualType> Gid_tTy = lookupType("gid_t", ACtx); + // int fchmod(int fildes, mode_t mode); + addToFunctionSummaryMap( + "fchmod", Signature(ArgTypes{IntTy, Mode_tTy}, RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZeroOrMinusOne) + .ArgConstraint( + ArgumentCondition(0, WithinRange, Range(0, IntMax)))); - if (Uid_tTy && Gid_tTy) { - // int fchownat(int dirfd, const char *pathname, uid_t owner, gid_t group, - // int flags); - addToFunctionSummaryMap( - "fchownat", - Summary(ArgTypes{IntTy, ConstCharPtrTy, *Uid_tTy, *Gid_tTy, IntTy}, - RetType{IntTy}, NoEvalCall) - .ArgConstraint( - ArgumentCondition(0, WithinRange, Range(0, IntMax))) - .ArgConstraint(NotNull(ArgNo(1)))); + Optional<QualType> Uid_tTy = lookupTy("uid_t"); + Optional<QualType> Gid_tTy = lookupTy("gid_t"); - // int chown(const char *path, uid_t owner, gid_t group); - addToFunctionSummaryMap( - "chown", Summary(ArgTypes{ConstCharPtrTy, *Uid_tTy, *Gid_tTy}, - RetType{IntTy}, NoEvalCall) - .ArgConstraint(NotNull(ArgNo(0)))); + // int fchownat(int dirfd, const char *pathname, uid_t owner, gid_t group, + // int flags); + addToFunctionSummaryMap( + "fchownat", + Signature(ArgTypes{IntTy, ConstCharPtrTy, Uid_tTy, Gid_tTy, IntTy}, + RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZeroOrMinusOne) + .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax))) + .ArgConstraint(NotNull(ArgNo(1)))); - // int lchown(const char *path, uid_t owner, gid_t group); - addToFunctionSummaryMap( - "lchown", Summary(ArgTypes{ConstCharPtrTy, *Uid_tTy, *Gid_tTy}, - RetType{IntTy}, NoEvalCall) - .ArgConstraint(NotNull(ArgNo(0)))); + // int chown(const char *path, uid_t owner, gid_t group); + addToFunctionSummaryMap( + "chown", + Signature(ArgTypes{ConstCharPtrTy, Uid_tTy, Gid_tTy}, RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZeroOrMinusOne) + .ArgConstraint(NotNull(ArgNo(0)))); - // int fchown(int fildes, uid_t owner, gid_t group); - addToFunctionSummaryMap( - "fchown", Summary(ArgTypes{IntTy, *Uid_tTy, *Gid_tTy}, RetType{IntTy}, - NoEvalCall) - .ArgConstraint(ArgumentCondition(0, WithinRange, - Range(0, IntMax)))); - } + // int lchown(const char *path, uid_t owner, gid_t group); + addToFunctionSummaryMap( + "lchown", + Signature(ArgTypes{ConstCharPtrTy, Uid_tTy, Gid_tTy}, RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZeroOrMinusOne) + .ArgConstraint(NotNull(ArgNo(0)))); - // int rmdir(const char *pathname); + // int fchown(int fildes, uid_t owner, gid_t group); addToFunctionSummaryMap( - "rmdir", Summary(ArgTypes{ConstCharPtrTy}, RetType{IntTy}, NoEvalCall) - .ArgConstraint(NotNull(ArgNo(0)))); + "fchown", Signature(ArgTypes{IntTy, Uid_tTy, Gid_tTy}, RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZeroOrMinusOne) + .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)))); // int chdir(const char *path); - addToFunctionSummaryMap( - "chdir", Summary(ArgTypes{ConstCharPtrTy}, RetType{IntTy}, NoEvalCall) - .ArgConstraint(NotNull(ArgNo(0)))); + addToFunctionSummaryMap("chdir", + Signature(ArgTypes{ConstCharPtrTy}, RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZeroOrMinusOne) + .ArgConstraint(NotNull(ArgNo(0)))); // int link(const char *oldpath, const char *newpath); - addToFunctionSummaryMap("link", - Summary(ArgTypes{ConstCharPtrTy, ConstCharPtrTy}, - RetType{IntTy}, NoEvalCall) - .ArgConstraint(NotNull(ArgNo(0))) - .ArgConstraint(NotNull(ArgNo(1)))); + addToFunctionSummaryMap( + "link", + Signature(ArgTypes{ConstCharPtrTy, ConstCharPtrTy}, RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZeroOrMinusOne) + .ArgConstraint(NotNull(ArgNo(0))) + .ArgConstraint(NotNull(ArgNo(1)))); // int linkat(int fd1, const char *path1, int fd2, const char *path2, // int flag); addToFunctionSummaryMap( "linkat", - Summary(ArgTypes{IntTy, ConstCharPtrTy, IntTy, ConstCharPtrTy, IntTy}, - RetType{IntTy}, NoEvalCall) + Signature(ArgTypes{IntTy, ConstCharPtrTy, IntTy, ConstCharPtrTy, IntTy}, + RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZeroOrMinusOne) .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax))) .ArgConstraint(NotNull(ArgNo(1))) .ArgConstraint(ArgumentCondition(2, WithinRange, Range(0, IntMax))) .ArgConstraint(NotNull(ArgNo(3)))); // int unlink(const char *pathname); - addToFunctionSummaryMap( - "unlink", Summary(ArgTypes{ConstCharPtrTy}, RetType{IntTy}, NoEvalCall) - .ArgConstraint(NotNull(ArgNo(0)))); + addToFunctionSummaryMap("unlink", + Signature(ArgTypes{ConstCharPtrTy}, RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZeroOrMinusOne) + .ArgConstraint(NotNull(ArgNo(0)))); // int unlinkat(int fd, const char *path, int flag); addToFunctionSummaryMap( "unlinkat", - Summary(ArgTypes{IntTy, ConstCharPtrTy, IntTy}, RetType{IntTy}, - NoEvalCall) + Signature(ArgTypes{IntTy, ConstCharPtrTy, IntTy}, RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZeroOrMinusOne) .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax))) .ArgConstraint(NotNull(ArgNo(1)))); - Optional<QualType> StructStatTy = lookupType("stat", ACtx); - Optional<QualType> StructStatPtrTy, StructStatPtrRestrictTy; - if (StructStatTy) { - StructStatPtrTy = ACtx.getPointerType(*StructStatTy); - StructStatPtrRestrictTy = ACtx.getLangOpts().C99 - ? ACtx.getRestrictType(*StructStatPtrTy) - : *StructStatPtrTy; - } + Optional<QualType> StructStatTy = lookupTy("stat"); + Optional<QualType> StructStatPtrTy = getPointerTy(StructStatTy); + Optional<QualType> StructStatPtrRestrictTy = getRestrictTy(StructStatPtrTy); - if (StructStatPtrTy) - // int fstat(int fd, struct stat *statbuf); - addToFunctionSummaryMap( - "fstat", - Summary(ArgTypes{IntTy, *StructStatPtrTy}, RetType{IntTy}, NoEvalCall) - .ArgConstraint( - ArgumentCondition(0, WithinRange, Range(0, IntMax))) - .ArgConstraint(NotNull(ArgNo(1)))); + // int fstat(int fd, struct stat *statbuf); + addToFunctionSummaryMap( + "fstat", Signature(ArgTypes{IntTy, StructStatPtrTy}, RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZeroOrMinusOne) + .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax))) + .ArgConstraint(NotNull(ArgNo(1)))); - if (StructStatPtrRestrictTy) { - // int stat(const char *restrict path, struct stat *restrict buf); - addToFunctionSummaryMap( - "stat", - Summary(ArgTypes{ConstCharPtrRestrictTy, *StructStatPtrRestrictTy}, - RetType{IntTy}, NoEvalCall) - .ArgConstraint(NotNull(ArgNo(0))) - .ArgConstraint(NotNull(ArgNo(1)))); + // int stat(const char *restrict path, struct stat *restrict buf); + addToFunctionSummaryMap( + "stat", + Signature(ArgTypes{ConstCharPtrRestrictTy, StructStatPtrRestrictTy}, + RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZeroOrMinusOne) + .ArgConstraint(NotNull(ArgNo(0))) + .ArgConstraint(NotNull(ArgNo(1)))); - // int lstat(const char *restrict path, struct stat *restrict buf); - addToFunctionSummaryMap( - "lstat", - Summary(ArgTypes{ConstCharPtrRestrictTy, *StructStatPtrRestrictTy}, - RetType{IntTy}, NoEvalCall) - .ArgConstraint(NotNull(ArgNo(0))) - .ArgConstraint(NotNull(ArgNo(1)))); - - // int fstatat(int fd, const char *restrict path, - // struct stat *restrict buf, int flag); - addToFunctionSummaryMap( - "fstatat", Summary(ArgTypes{IntTy, ConstCharPtrRestrictTy, - *StructStatPtrRestrictTy, IntTy}, - RetType{IntTy}, NoEvalCall) - .ArgConstraint(ArgumentCondition(0, WithinRange, - Range(0, IntMax))) - .ArgConstraint(NotNull(ArgNo(1))) - .ArgConstraint(NotNull(ArgNo(2)))); - } + // int lstat(const char *restrict path, struct stat *restrict buf); + addToFunctionSummaryMap( + "lstat", + Signature(ArgTypes{ConstCharPtrRestrictTy, StructStatPtrRestrictTy}, + RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZeroOrMinusOne) + .ArgConstraint(NotNull(ArgNo(0))) + .ArgConstraint(NotNull(ArgNo(1)))); - if (DirPtrTy) { - // DIR *opendir(const char *name); - addToFunctionSummaryMap("opendir", Summary(ArgTypes{ConstCharPtrTy}, - RetType{*DirPtrTy}, NoEvalCall) - .ArgConstraint(NotNull(ArgNo(0)))); + // int fstatat(int fd, const char *restrict path, + // struct stat *restrict buf, int flag); + addToFunctionSummaryMap( + "fstatat", + Signature(ArgTypes{IntTy, ConstCharPtrRestrictTy, + StructStatPtrRestrictTy, IntTy}, + RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZeroOrMinusOne) + .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax))) + .ArgConstraint(NotNull(ArgNo(1))) + .ArgConstraint(NotNull(ArgNo(2)))); - // DIR *fdopendir(int fd); - addToFunctionSummaryMap( - "fdopendir", Summary(ArgTypes{IntTy}, RetType{*DirPtrTy}, NoEvalCall) - .ArgConstraint(ArgumentCondition(0, WithinRange, - Range(0, IntMax)))); - } + // DIR *opendir(const char *name); + addToFunctionSummaryMap( + "opendir", Signature(ArgTypes{ConstCharPtrTy}, RetType{DirPtrTy}), + Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); + + // DIR *fdopendir(int fd); + addToFunctionSummaryMap("fdopendir", + Signature(ArgTypes{IntTy}, RetType{DirPtrTy}), + Summary(NoEvalCall) + .ArgConstraint(ArgumentCondition( + 0, WithinRange, Range(0, IntMax)))); // int isatty(int fildes); addToFunctionSummaryMap( - "isatty", Summary(ArgTypes{IntTy}, RetType{IntTy}, NoEvalCall) - .ArgConstraint( - ArgumentCondition(0, WithinRange, Range(0, IntMax)))); + "isatty", Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(NoEvalCall) + .Case({ReturnValueCondition(WithinRange, Range(0, 1))}) + .ArgConstraint( + ArgumentCondition(0, WithinRange, Range(0, IntMax)))); - if (FilePtrTy) { - // FILE *popen(const char *command, const char *type); - addToFunctionSummaryMap("popen", - Summary(ArgTypes{ConstCharPtrTy, ConstCharPtrTy}, - RetType{*FilePtrTy}, NoEvalCall) - .ArgConstraint(NotNull(ArgNo(0))) - .ArgConstraint(NotNull(ArgNo(1)))); + // FILE *popen(const char *command, const char *type); + addToFunctionSummaryMap( + "popen", + Signature(ArgTypes{ConstCharPtrTy, ConstCharPtrTy}, RetType{FilePtrTy}), + Summary(NoEvalCall) + .ArgConstraint(NotNull(ArgNo(0))) + .ArgConstraint(NotNull(ArgNo(1)))); - // int pclose(FILE *stream); - addToFunctionSummaryMap( - "pclose", Summary(ArgTypes{*FilePtrTy}, RetType{IntTy}, NoEvalCall) - .ArgConstraint(NotNull(ArgNo(0)))); - } + // int pclose(FILE *stream); + addToFunctionSummaryMap( + "pclose", Signature(ArgTypes{FilePtrTy}, RetType{IntTy}), + Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); // int close(int fildes); - addToFunctionSummaryMap( - "close", Summary(ArgTypes{IntTy}, RetType{IntTy}, NoEvalCall) - .ArgConstraint( - ArgumentCondition(0, WithinRange, Range(0, IntMax)))); + addToFunctionSummaryMap("close", Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZeroOrMinusOne) + .ArgConstraint(ArgumentCondition( + 0, WithinRange, Range(-1, IntMax)))); // long fpathconf(int fildes, int name); - addToFunctionSummaryMap( - "fpathconf", - Summary(ArgTypes{IntTy, IntTy}, RetType{LongTy}, NoEvalCall) - .ArgConstraint( - ArgumentCondition(0, WithinRange, Range(0, IntMax)))); + addToFunctionSummaryMap("fpathconf", + Signature(ArgTypes{IntTy, IntTy}, RetType{LongTy}), + Summary(NoEvalCall) + .ArgConstraint(ArgumentCondition( + 0, WithinRange, Range(0, IntMax)))); // long pathconf(const char *path, int name); - addToFunctionSummaryMap("pathconf", Summary(ArgTypes{ConstCharPtrTy, IntTy}, - RetType{LongTy}, NoEvalCall) - .ArgConstraint(NotNull(ArgNo(0)))); + addToFunctionSummaryMap( + "pathconf", Signature(ArgTypes{ConstCharPtrTy, IntTy}, RetType{LongTy}), + Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); - if (FilePtrTy) - // FILE *fdopen(int fd, const char *mode); - addToFunctionSummaryMap( - "fdopen", Summary(ArgTypes{IntTy, ConstCharPtrTy}, - RetType{*FilePtrTy}, NoEvalCall) - .ArgConstraint( - ArgumentCondition(0, WithinRange, Range(0, IntMax))) - .ArgConstraint(NotNull(ArgNo(1)))); - - if (DirPtrTy) { - // void rewinddir(DIR *dir); - addToFunctionSummaryMap( - "rewinddir", Summary(ArgTypes{*DirPtrTy}, RetType{VoidTy}, 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 seekdir(DIR *dirp, long loc); - addToFunctionSummaryMap("seekdir", Summary(ArgTypes{*DirPtrTy, LongTy}, - RetType{VoidTy}, NoEvalCall) - .ArgConstraint(NotNull(ArgNo(0)))); - } + // void rewinddir(DIR *dir); + addToFunctionSummaryMap( + "rewinddir", Signature(ArgTypes{DirPtrTy}, RetType{VoidTy}), + Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); + + // void seekdir(DIR *dirp, long loc); + addToFunctionSummaryMap( + "seekdir", Signature(ArgTypes{DirPtrTy, LongTy}, RetType{VoidTy}), + Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); // int rand_r(unsigned int *seedp); - addToFunctionSummaryMap("rand_r", Summary(ArgTypes{UnsignedIntPtrTy}, - RetType{IntTy}, NoEvalCall) - .ArgConstraint(NotNull(ArgNo(0)))); - - // int strcasecmp(const char *s1, const char *s2); - addToFunctionSummaryMap("strcasecmp", - Summary(ArgTypes{ConstCharPtrTy, ConstCharPtrTy}, - RetType{IntTy}, EvalCallAsPure) - .ArgConstraint(NotNull(ArgNo(0))) - .ArgConstraint(NotNull(ArgNo(1)))); + addToFunctionSummaryMap( + "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 strncasecmp(const char *s1, const char *s2, size_t n); + // int fseeko(FILE *stream, off_t offset, int whence); addToFunctionSummaryMap( - "strncasecmp", Summary(ArgTypes{ConstCharPtrTy, ConstCharPtrTy, SizeTy}, - RetType{IntTy}, EvalCallAsPure) - .ArgConstraint(NotNull(ArgNo(0))) - .ArgConstraint(NotNull(ArgNo(1))) - .ArgConstraint(ArgumentCondition( - 2, WithinRange, Range(0, SizeMax)))); + "fseeko", + Signature(ArgTypes{FilePtrTy, Off_tTy, IntTy}, RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZeroOrMinusOne) + .ArgConstraint(NotNull(ArgNo(0)))); - if (FilePtrTy && Off_tTy) { + // off_t ftello(FILE *stream); + addToFunctionSummaryMap( + "ftello", Signature(ArgTypes{FilePtrTy}, RetType{Off_tTy}), + Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); - // int fileno(FILE *stream); - addToFunctionSummaryMap( - "fileno", Summary(ArgTypes{*FilePtrTy}, RetType{IntTy}, NoEvalCall) - .ArgConstraint(NotNull(ArgNo(0)))); + // void *mmap(void *addr, size_t length, int prot, int flags, int fd, + // off_t offset); + addToFunctionSummaryMap( + "mmap", + Signature(ArgTypes{VoidPtrTy, SizeTy, IntTy, IntTy, IntTy, Off_tTy}, + RetType{VoidPtrTy}), + Summary(NoEvalCall) + .ArgConstraint(ArgumentCondition(1, WithinRange, Range(1, SizeMax))) + .ArgConstraint( + ArgumentCondition(4, WithinRange, Range(-1, IntMax)))); - // int fseeko(FILE *stream, off_t offset, int whence); - addToFunctionSummaryMap("fseeko", - Summary(ArgTypes{*FilePtrTy, *Off_tTy, IntTy}, - RetType{IntTy}, NoEvalCall) - .ArgConstraint(NotNull(ArgNo(0)))); + Optional<QualType> Off64_tTy = lookupTy("off64_t"); + // void *mmap64(void *addr, size_t length, int prot, int flags, int fd, + // off64_t offset); + addToFunctionSummaryMap( + "mmap64", + Signature(ArgTypes{VoidPtrTy, SizeTy, IntTy, IntTy, IntTy, Off64_tTy}, + RetType{VoidPtrTy}), + Summary(NoEvalCall) + .ArgConstraint(ArgumentCondition(1, WithinRange, Range(1, SizeMax))) + .ArgConstraint( + ArgumentCondition(4, WithinRange, Range(-1, IntMax)))); - // off_t ftello(FILE *stream); - addToFunctionSummaryMap( - "ftello", Summary(ArgTypes{*FilePtrTy}, RetType{*Off_tTy}, NoEvalCall) - .ArgConstraint(NotNull(ArgNo(0)))); - } + // int pipe(int fildes[2]); + addToFunctionSummaryMap("pipe", + Signature(ArgTypes{IntPtrTy}, RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZeroOrMinusOne) + .ArgConstraint(NotNull(ArgNo(0)))); + + // off_t lseek(int fildes, off_t offset, int whence); + addToFunctionSummaryMap( + "lseek", Signature(ArgTypes{IntTy, Off_tTy, IntTy}, RetType{Off_tTy}), + Summary(NoEvalCall) + .ArgConstraint( + ArgumentCondition(0, WithinRange, Range(0, IntMax)))); + + // ssize_t readlink(const char *restrict path, char *restrict buf, + // size_t bufsize); + addToFunctionSummaryMap( + "readlink", + Signature(ArgTypes{ConstCharPtrRestrictTy, CharPtrRestrictTy, SizeTy}, + RetType{Ssize_tTy}), + Summary(NoEvalCall) + .Case({ReturnValueCondition(LessThanOrEq, ArgNo(2)), + ReturnValueCondition(WithinRange, Range(-1, Ssize_tMax))}) + .ArgConstraint(NotNull(ArgNo(0))) + .ArgConstraint(NotNull(ArgNo(1))) + .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(1), + /*BufSize=*/ArgNo(2))) + .ArgConstraint( + ArgumentCondition(2, WithinRange, Range(0, SizeMax)))); + + // ssize_t readlinkat(int fd, const char *restrict path, + // char *restrict buf, size_t bufsize); + addToFunctionSummaryMap( + "readlinkat", + Signature( + 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))) + .ArgConstraint(NotNull(ArgNo(1))) + .ArgConstraint(NotNull(ArgNo(2))) + .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(2), + /*BufSize=*/ArgNo(3))) + .ArgConstraint( + ArgumentCondition(3, WithinRange, Range(0, SizeMax)))); + + // int renameat(int olddirfd, const char *oldpath, int newdirfd, const char + // *newpath); + addToFunctionSummaryMap( + "renameat", + Signature(ArgTypes{IntTy, ConstCharPtrTy, IntTy, ConstCharPtrTy}, + RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZeroOrMinusOne) + .ArgConstraint(NotNull(ArgNo(1))) + .ArgConstraint(NotNull(ArgNo(3)))); + + // char *realpath(const char *restrict file_name, + // char *restrict resolved_name); + addToFunctionSummaryMap( + "realpath", + Signature(ArgTypes{ConstCharPtrRestrictTy, CharPtrRestrictTy}, + RetType{CharPtrTy}), + Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); + + QualType CharPtrConstPtr = getPointerTy(getConstTy(CharPtrTy)); + + // int execv(const char *path, char *const argv[]); + addToFunctionSummaryMap( + "execv", + Signature(ArgTypes{ConstCharPtrTy, CharPtrConstPtr}, RetType{IntTy}), + Summary(NoEvalCall) + .Case({ReturnValueCondition(WithinRange, SingleValue(-1))}) + .ArgConstraint(NotNull(ArgNo(0)))); + + // int execvp(const char *file, char *const argv[]); + addToFunctionSummaryMap( + "execvp", + Signature(ArgTypes{ConstCharPtrTy, CharPtrConstPtr}, RetType{IntTy}), + Summary(NoEvalCall) + .Case({ReturnValueCondition(WithinRange, SingleValue(-1))}) + .ArgConstraint(NotNull(ArgNo(0)))); - if (Off_tTy) { - Optional<RangeInt> Off_tMax = BVF.getMaxValue(*Off_tTy).getLimitedValue(); + // int getopt(int argc, char * const argv[], const char *optstring); + addToFunctionSummaryMap( + "getopt", + Signature(ArgTypes{IntTy, CharPtrConstPtr, ConstCharPtrTy}, + RetType{IntTy}), + Summary(NoEvalCall) + .Case({ReturnValueCondition(WithinRange, Range(-1, UCharRangeMax))}) + .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax))) + .ArgConstraint(NotNull(ArgNo(1))) + .ArgConstraint(NotNull(ArgNo(2)))); - // void *mmap(void *addr, size_t length, int prot, int flags, int fd, - // off_t offset); + Optional<QualType> StructSockaddrTy = lookupTy("sockaddr"); + Optional<QualType> StructSockaddrPtrTy = getPointerTy(StructSockaddrTy); + Optional<QualType> ConstStructSockaddrPtrTy = + getPointerTy(getConstTy(StructSockaddrTy)); + Optional<QualType> StructSockaddrPtrRestrictTy = + getRestrictTy(StructSockaddrPtrTy); + 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); + + // In 'socket.h' of some libc implementations with C99, sockaddr parameter + // is a transparent union of the underlying sockaddr_ family of pointers + // instead of being a pointer to struct sockaddr. In these cases, the + // 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. + auto Accept = + Summary(NoEvalCall) + .Case(ReturnsFileDescriptor) + .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax))); + if (!addToFunctionSummaryMap( + "accept", + // int accept(int socket, struct sockaddr *restrict address, + // socklen_t *restrict address_len); + Signature(ArgTypes{IntTy, StructSockaddrPtrRestrictTy, + Socklen_tPtrRestrictTy}, + RetType{IntTy}), + Accept)) + addToFunctionSummaryMap( + "accept", + Signature(ArgTypes{IntTy, Irrelevant, Socklen_tPtrRestrictTy}, + RetType{IntTy}), + Accept); + + // int bind(int socket, const struct sockaddr *address, socklen_t + // address_len); + if (!addToFunctionSummaryMap( + "bind", + Signature(ArgTypes{IntTy, ConstStructSockaddrPtrTy, Socklen_tTy}, + RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZeroOrMinusOne) + .ArgConstraint( + ArgumentCondition(0, WithinRange, Range(0, IntMax))) + .ArgConstraint(NotNull(ArgNo(1))) + .ArgConstraint( + BufferSize(/*Buffer=*/ArgNo(1), /*BufSize=*/ArgNo(2))) + .ArgConstraint( + ArgumentCondition(2, WithinRange, Range(0, Socklen_tMax))))) + // Do not add constraints on sockaddr. addToFunctionSummaryMap( - "mmap", - Summary(ArgTypes{VoidPtrTy, SizeTy, IntTy, IntTy, IntTy, *Off_tTy}, - RetType{VoidPtrTy}, NoEvalCall) + "bind", + Signature(ArgTypes{IntTy, Irrelevant, Socklen_tTy}, RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZeroOrMinusOne) .ArgConstraint( - ArgumentCondition(1, WithinRange, Range(1, SizeMax))) + ArgumentCondition(0, WithinRange, Range(0, IntMax))) .ArgConstraint( - ArgumentCondition(4, WithinRange, Range(0, *Off_tMax)))); - } + ArgumentCondition(2, WithinRange, Range(0, Socklen_tMax)))); + + // int getpeername(int socket, struct sockaddr *restrict address, + // socklen_t *restrict address_len); + if (!addToFunctionSummaryMap( + "getpeername", + Signature(ArgTypes{IntTy, StructSockaddrPtrRestrictTy, + Socklen_tPtrRestrictTy}, + RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZeroOrMinusOne) + .ArgConstraint( + ArgumentCondition(0, WithinRange, Range(0, IntMax))) + .ArgConstraint(NotNull(ArgNo(1))) + .ArgConstraint(NotNull(ArgNo(2))))) + addToFunctionSummaryMap( + "getpeername", + Signature(ArgTypes{IntTy, Irrelevant, Socklen_tPtrRestrictTy}, + RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZeroOrMinusOne) + .ArgConstraint( + ArgumentCondition(0, WithinRange, Range(0, IntMax)))); - Optional<QualType> Off64_tTy = lookupType("off64_t", ACtx); - Optional<RangeInt> Off64_tMax; - if (Off64_tTy) { - Off64_tMax = BVF.getMaxValue(*Off_tTy).getLimitedValue(); - // void *mmap64(void *addr, size_t length, int prot, int flags, int fd, - // off64_t offset); + // int getsockname(int socket, struct sockaddr *restrict address, + // socklen_t *restrict address_len); + if (!addToFunctionSummaryMap( + "getsockname", + Signature(ArgTypes{IntTy, StructSockaddrPtrRestrictTy, + Socklen_tPtrRestrictTy}, + RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZeroOrMinusOne) + .ArgConstraint( + ArgumentCondition(0, WithinRange, Range(0, IntMax))) + .ArgConstraint(NotNull(ArgNo(1))) + .ArgConstraint(NotNull(ArgNo(2))))) addToFunctionSummaryMap( - "mmap64", - Summary(ArgTypes{VoidPtrTy, SizeTy, IntTy, IntTy, IntTy, *Off64_tTy}, - RetType{VoidPtrTy}, NoEvalCall) + "getsockname", + Signature(ArgTypes{IntTy, Irrelevant, Socklen_tPtrRestrictTy}, + RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZeroOrMinusOne) .ArgConstraint( - ArgumentCondition(1, WithinRange, Range(1, SizeMax))) + ArgumentCondition(0, WithinRange, Range(0, IntMax)))); + + // int connect(int socket, const struct sockaddr *address, socklen_t + // address_len); + if (!addToFunctionSummaryMap( + "connect", + Signature(ArgTypes{IntTy, ConstStructSockaddrPtrTy, Socklen_tTy}, + RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZeroOrMinusOne) + .ArgConstraint( + ArgumentCondition(0, WithinRange, Range(0, IntMax))) + .ArgConstraint(NotNull(ArgNo(1))))) + addToFunctionSummaryMap( + "connect", + Signature(ArgTypes{IntTy, Irrelevant, Socklen_tTy}, RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZeroOrMinusOne) .ArgConstraint( - ArgumentCondition(4, WithinRange, Range(0, *Off64_tMax)))); - } + ArgumentCondition(0, WithinRange, Range(0, IntMax)))); - // int pipe(int fildes[2]); + auto Recvfrom = + Summary(NoEvalCall) + .Case({ReturnValueCondition(LessThanOrEq, ArgNo(2)), + ReturnValueCondition(WithinRange, Range(-1, Ssize_tMax))}) + .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax))) + .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(1), + /*BufSize=*/ArgNo(2))); + if (!addToFunctionSummaryMap( + "recvfrom", + // ssize_t recvfrom(int socket, void *restrict buffer, + // size_t length, + // int flags, struct sockaddr *restrict address, + // socklen_t *restrict address_len); + Signature(ArgTypes{IntTy, VoidPtrRestrictTy, SizeTy, IntTy, + StructSockaddrPtrRestrictTy, + Socklen_tPtrRestrictTy}, + RetType{Ssize_tTy}), + Recvfrom)) + addToFunctionSummaryMap( + "recvfrom", + Signature(ArgTypes{IntTy, VoidPtrRestrictTy, SizeTy, IntTy, + Irrelevant, Socklen_tPtrRestrictTy}, + RetType{Ssize_tTy}), + Recvfrom); + + auto Sendto = + Summary(NoEvalCall) + .Case({ReturnValueCondition(LessThanOrEq, ArgNo(2)), + ReturnValueCondition(WithinRange, Range(-1, Ssize_tMax))}) + .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax))) + .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(1), + /*BufSize=*/ArgNo(2))); + if (!addToFunctionSummaryMap( + "sendto", + // ssize_t sendto(int socket, const void *message, size_t length, + // int flags, const struct sockaddr *dest_addr, + // socklen_t dest_len); + Signature(ArgTypes{IntTy, ConstVoidPtrTy, SizeTy, IntTy, + ConstStructSockaddrPtrTy, Socklen_tTy}, + RetType{Ssize_tTy}), + Sendto)) + addToFunctionSummaryMap( + "sendto", + Signature(ArgTypes{IntTy, ConstVoidPtrTy, SizeTy, IntTy, Irrelevant, + Socklen_tTy}, + RetType{Ssize_tTy}), + 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)))); + + // ssize_t recv(int sockfd, void *buf, size_t len, int flags); addToFunctionSummaryMap( - "pipe", Summary(ArgTypes{IntPtrTy}, RetType{IntTy}, NoEvalCall) - .ArgConstraint(NotNull(ArgNo(0)))); + "recv", + Signature(ArgTypes{IntTy, VoidPtrTy, SizeTy, IntTy}, + RetType{Ssize_tTy}), + Summary(NoEvalCall) + .Case({ReturnValueCondition(LessThanOrEq, ArgNo(2)), + ReturnValueCondition(WithinRange, Range(-1, Ssize_tMax))}) + .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax))) + .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(1), + /*BufSize=*/ArgNo(2)))); - if (Off_tTy) - // off_t lseek(int fildes, off_t offset, int whence); - addToFunctionSummaryMap( - "lseek", Summary(ArgTypes{IntTy, *Off_tTy, IntTy}, RetType{*Off_tTy}, - NoEvalCall) - .ArgConstraint(ArgumentCondition(0, WithinRange, - Range(0, IntMax)))); + Optional<QualType> StructMsghdrTy = lookupTy("msghdr"); + Optional<QualType> StructMsghdrPtrTy = getPointerTy(StructMsghdrTy); + Optional<QualType> ConstStructMsghdrPtrTy = + getPointerTy(getConstTy(StructMsghdrTy)); - Optional<QualType> Ssize_tTy = lookupType("ssize_t", ACtx); + // ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags); + addToFunctionSummaryMap( + "recvmsg", + Signature(ArgTypes{IntTy, StructMsghdrPtrTy, IntTy}, + RetType{Ssize_tTy}), + Summary(NoEvalCall) + .Case({ReturnValueCondition(WithinRange, Range(-1, Ssize_tMax))}) + .ArgConstraint( + ArgumentCondition(0, WithinRange, Range(0, IntMax)))); - if (Ssize_tTy) { - // ssize_t readlink(const char *restrict path, char *restrict buf, - // size_t bufsize); - addToFunctionSummaryMap( - "readlink", - Summary(ArgTypes{ConstCharPtrRestrictTy, CharPtrRestrictTy, SizeTy}, - RetType{*Ssize_tTy}, NoEvalCall) - .ArgConstraint(NotNull(ArgNo(0))) - .ArgConstraint(NotNull(ArgNo(1))) - .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(1), - /*BufSize=*/ArgNo(2))) - .ArgConstraint( - ArgumentCondition(2, WithinRange, Range(0, SizeMax)))); + // ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags); + addToFunctionSummaryMap( + "sendmsg", + Signature(ArgTypes{IntTy, ConstStructMsghdrPtrTy, IntTy}, + RetType{Ssize_tTy}), + Summary(NoEvalCall) + .Case({ReturnValueCondition(WithinRange, Range(-1, Ssize_tMax))}) + .ArgConstraint( + ArgumentCondition(0, WithinRange, Range(0, IntMax)))); - // ssize_t readlinkat(int fd, const char *restrict path, - // char *restrict buf, size_t bufsize); - addToFunctionSummaryMap( - "readlinkat", Summary(ArgTypes{IntTy, ConstCharPtrRestrictTy, - CharPtrRestrictTy, SizeTy}, - RetType{*Ssize_tTy}, NoEvalCall) - .ArgConstraint(ArgumentCondition(0, WithinRange, - Range(0, IntMax))) - .ArgConstraint(NotNull(ArgNo(1))) - .ArgConstraint(NotNull(ArgNo(2))) - .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(2), - /*BufSize=*/ArgNo(3))) - .ArgConstraint(ArgumentCondition( - 3, WithinRange, Range(0, SizeMax)))); - } + // int setsockopt(int socket, int level, int option_name, + // const void *option_value, socklen_t option_len); + addToFunctionSummaryMap( + "setsockopt", + Signature(ArgTypes{IntTy, IntTy, IntTy, ConstVoidPtrTy, Socklen_tTy}, + RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZeroOrMinusOne) + .ArgConstraint(NotNull(ArgNo(3))) + .ArgConstraint( + BufferSize(/*Buffer=*/ArgNo(3), /*BufSize=*/ArgNo(4))) + .ArgConstraint( + ArgumentCondition(4, WithinRange, Range(0, Socklen_tMax)))); - // int renameat(int olddirfd, const char *oldpath, int newdirfd, const char - // *newpath); - addToFunctionSummaryMap("renameat", Summary(ArgTypes{IntTy, ConstCharPtrTy, - IntTy, ConstCharPtrTy}, - RetType{IntTy}, NoEvalCall) - .ArgConstraint(NotNull(ArgNo(1))) - .ArgConstraint(NotNull(ArgNo(3)))); + // int getsockopt(int socket, int level, int option_name, + // void *restrict option_value, + // socklen_t *restrict option_len); + addToFunctionSummaryMap( + "getsockopt", + Signature(ArgTypes{IntTy, IntTy, IntTy, VoidPtrRestrictTy, + Socklen_tPtrRestrictTy}, + RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZeroOrMinusOne) + .ArgConstraint(NotNull(ArgNo(3))) + .ArgConstraint(NotNull(ArgNo(4)))); + + // ssize_t send(int sockfd, const void *buf, size_t len, int flags); + addToFunctionSummaryMap( + "send", + Signature(ArgTypes{IntTy, ConstVoidPtrTy, SizeTy, IntTy}, + RetType{Ssize_tTy}), + Summary(NoEvalCall) + .Case({ReturnValueCondition(LessThanOrEq, ArgNo(2)), + ReturnValueCondition(WithinRange, Range(-1, Ssize_tMax))}) + .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax))) + .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(1), + /*BufSize=*/ArgNo(2)))); - // char *realpath(const char *restrict file_name, - // char *restrict resolved_name); + // int socketpair(int domain, int type, int protocol, int sv[2]); + addToFunctionSummaryMap( + "socketpair", + Signature(ArgTypes{IntTy, IntTy, IntTy, IntPtrTy}, RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZeroOrMinusOne) + .ArgConstraint(NotNull(ArgNo(3)))); + + // int getnameinfo(const struct sockaddr *restrict sa, socklen_t salen, + // char *restrict node, socklen_t nodelen, + // char *restrict service, + // socklen_t servicelen, int flags); + // + // This is defined in netdb.h. And contrary to 'socket.h', the sockaddr + // parameter is never handled as a transparent union in netdb.h addToFunctionSummaryMap( - "realpath", Summary(ArgTypes{ConstCharPtrRestrictTy, CharPtrRestrictTy}, - RetType{CharPtrTy}, NoEvalCall) - .ArgConstraint(NotNull(ArgNo(0)))); + "getnameinfo", + Signature(ArgTypes{ConstStructSockaddrPtrRestrictTy, Socklen_tTy, + CharPtrRestrictTy, Socklen_tTy, CharPtrRestrictTy, + Socklen_tTy, IntTy}, + RetType{IntTy}), + Summary(NoEvalCall) + .ArgConstraint( + BufferSize(/*Buffer=*/ArgNo(0), /*BufSize=*/ArgNo(1))) + .ArgConstraint( + ArgumentCondition(1, WithinRange, Range(0, Socklen_tMax))) + .ArgConstraint( + BufferSize(/*Buffer=*/ArgNo(2), /*BufSize=*/ArgNo(3))) + .ArgConstraint( + ArgumentCondition(3, WithinRange, Range(0, Socklen_tMax))) + .ArgConstraint( + BufferSize(/*Buffer=*/ArgNo(4), /*BufSize=*/ArgNo(5))) + .ArgConstraint( + ArgumentCondition(5, WithinRange, Range(0, Socklen_tMax)))); - QualType CharPtrConstPtr = ACtx.getPointerType(CharPtrTy.withConst()); + Optional<QualType> StructUtimbufTy = lookupTy("utimbuf"); + Optional<QualType> StructUtimbufPtrTy = getPointerTy(StructUtimbufTy); - // int execv(const char *path, char *const argv[]); - addToFunctionSummaryMap("execv", - Summary(ArgTypes{ConstCharPtrTy, CharPtrConstPtr}, - RetType{IntTy}, NoEvalCall) - .ArgConstraint(NotNull(ArgNo(0)))); + // int utime(const char *filename, struct utimbuf *buf); + addToFunctionSummaryMap( + "utime", + Signature(ArgTypes{ConstCharPtrTy, StructUtimbufPtrTy}, RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZeroOrMinusOne) + .ArgConstraint(NotNull(ArgNo(0)))); - // int execvp(const char *file, char *const argv[]); - addToFunctionSummaryMap("execvp", - Summary(ArgTypes{ConstCharPtrTy, CharPtrConstPtr}, - RetType{IntTy}, NoEvalCall) - .ArgConstraint(NotNull(ArgNo(0)))); + Optional<QualType> StructTimespecTy = lookupTy("timespec"); + Optional<QualType> StructTimespecPtrTy = getPointerTy(StructTimespecTy); + Optional<QualType> ConstStructTimespecPtrTy = + getPointerTy(getConstTy(StructTimespecTy)); - // int getopt(int argc, char * const argv[], const char *optstring); + // int futimens(int fd, const struct timespec times[2]); addToFunctionSummaryMap( - "getopt", - Summary(ArgTypes{IntTy, CharPtrConstPtr, ConstCharPtrTy}, - RetType{IntTy}, NoEvalCall) - .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax))) + "futimens", + Signature(ArgTypes{IntTy, ConstStructTimespecPtrTy}, RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZeroOrMinusOne) + .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)))); + + Optional<QualType> StructTimevalTy = lookupTy("timeval"); + Optional<QualType> ConstStructTimevalPtrTy = + getPointerTy(getConstTy(StructTimevalTy)); + + // int utimes(const char *filename, const struct timeval times[2]); + addToFunctionSummaryMap( + "utimes", + Signature(ArgTypes{ConstCharPtrTy, ConstStructTimevalPtrTy}, + RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZeroOrMinusOne) + .ArgConstraint(NotNull(ArgNo(0)))); + + // int nanosleep(const struct timespec *rqtp, struct timespec *rmtp); + addToFunctionSummaryMap( + "nanosleep", + Signature(ArgTypes{ConstStructTimespecPtrTy, StructTimespecPtrTy}, + RetType{IntTy}), + Summary(NoEvalCall) + .Case(ReturnsZeroOrMinusOne) + .ArgConstraint(NotNull(ArgNo(0)))); + + Optional<QualType> Time_tTy = lookupTy("time_t"); + Optional<QualType> ConstTime_tPtrTy = getPointerTy(getConstTy(Time_tTy)); + 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 = + getPointerTy(getConstTy(StructTmTy)); + Optional<QualType> ConstStructTmPtrRestrictTy = + getRestrictTy(ConstStructTmPtrTy); + + // struct tm * localtime(const time_t *tp); + addToFunctionSummaryMap( + "localtime", + Signature(ArgTypes{ConstTime_tPtrTy}, RetType{StructTmPtrTy}), + Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); + + // struct tm *localtime_r(const time_t *restrict timer, + // struct tm *restrict result); + addToFunctionSummaryMap( + "localtime_r", + Signature(ArgTypes{ConstTime_tPtrRestrictTy, StructTmPtrRestrictTy}, + RetType{StructTmPtrTy}), + Summary(NoEvalCall) + .ArgConstraint(NotNull(ArgNo(0))) + .ArgConstraint(NotNull(ArgNo(1)))); + + // char *asctime_r(const struct tm *restrict tm, char *restrict buf); + addToFunctionSummaryMap( + "asctime_r", + Signature(ArgTypes{ConstStructTmPtrRestrictTy, CharPtrRestrictTy}, + RetType{CharPtrTy}), + Summary(NoEvalCall) + .ArgConstraint(NotNull(ArgNo(0))) + .ArgConstraint(NotNull(ArgNo(1))) + .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(1), + /*MinBufSize=*/BVF.getValue(26, IntTy)))); + + // char *ctime_r(const time_t *timep, char *buf); + addToFunctionSummaryMap( + "ctime_r", + Signature(ArgTypes{ConstTime_tPtrTy, CharPtrTy}, RetType{CharPtrTy}), + Summary(NoEvalCall) + .ArgConstraint(NotNull(ArgNo(0))) .ArgConstraint(NotNull(ArgNo(1))) + .ArgConstraint(BufferSize( + /*Buffer=*/ArgNo(1), + /*MinBufSize=*/BVF.getValue(26, IntTy)))); + + // struct tm *gmtime_r(const time_t *restrict timer, + // struct tm *restrict result); + addToFunctionSummaryMap( + "gmtime_r", + Signature(ArgTypes{ConstTime_tPtrRestrictTy, StructTmPtrRestrictTy}, + RetType{StructTmPtrTy}), + Summary(NoEvalCall) + .ArgConstraint(NotNull(ArgNo(0))) + .ArgConstraint(NotNull(ArgNo(1)))); + + // struct tm * gmtime(const time_t *tp); + addToFunctionSummaryMap( + "gmtime", Signature(ArgTypes{ConstTime_tPtrTy}, RetType{StructTmPtrTy}), + Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); + + 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) + .ArgConstraint(NotNull(ArgNo(1)))); + + Optional<QualType> StructItimervalTy = lookupTy("itimerval"); + 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) + .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 = + 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 = + getPointerTy(getConstTy(Pthread_attr_tTy)); + Optional<QualType> ConstPthread_attr_tPtrRestrictTy = + getRestrictTy(ConstPthread_attr_tPtrTy); + Optional<QualType> Pthread_mutexattr_tTy = lookupTy("pthread_mutexattr_t"); + Optional<QualType> ConstPthread_mutexattr_tPtrTy = + getPointerTy(getConstTy(Pthread_mutexattr_tTy)); + Optional<QualType> ConstPthread_mutexattr_tPtrRestrictTy = + getRestrictTy(ConstPthread_mutexattr_tPtrTy); + + QualType PthreadStartRoutineTy = getPointerTy( + ACtx.getFunctionType(/*ResultTy=*/VoidPtrTy, /*Args=*/VoidPtrTy, + FunctionProtoType::ExtProtoInfo())); + + // int pthread_cond_signal(pthread_cond_t *cond); + // int pthread_cond_broadcast(pthread_cond_t *cond); + addToFunctionSummaryMap( + {"pthread_cond_signal", "pthread_cond_broadcast"}, + Signature(ArgTypes{Pthread_cond_tPtrTy}, RetType{IntTy}), + Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); + + // int pthread_create(pthread_t *restrict thread, + // const pthread_attr_t *restrict attr, + // void *(*start_routine)(void*), void *restrict arg); + addToFunctionSummaryMap( + "pthread_create", + Signature(ArgTypes{Pthread_tPtrRestrictTy, + ConstPthread_attr_tPtrRestrictTy, + PthreadStartRoutineTy, VoidPtrRestrictTy}, + RetType{IntTy}), + Summary(NoEvalCall) + .ArgConstraint(NotNull(ArgNo(0))) .ArgConstraint(NotNull(ArgNo(2)))); + + // int pthread_attr_destroy(pthread_attr_t *attr); + // int pthread_attr_init(pthread_attr_t *attr); + addToFunctionSummaryMap( + {"pthread_attr_destroy", "pthread_attr_init"}, + Signature(ArgTypes{Pthread_attr_tPtrTy}, RetType{IntTy}), + Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); + + // int pthread_attr_getstacksize(const pthread_attr_t *restrict attr, + // size_t *restrict stacksize); + // int pthread_attr_getguardsize(const pthread_attr_t *restrict attr, + // size_t *restrict guardsize); + addToFunctionSummaryMap( + {"pthread_attr_getstacksize", "pthread_attr_getguardsize"}, + Signature(ArgTypes{ConstPthread_attr_tPtrRestrictTy, SizePtrRestrictTy}, + RetType{IntTy}), + Summary(NoEvalCall) + .ArgConstraint(NotNull(ArgNo(0))) + .ArgConstraint(NotNull(ArgNo(1)))); + + // int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize); + // int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize); + addToFunctionSummaryMap( + {"pthread_attr_setstacksize", "pthread_attr_setguardsize"}, + Signature(ArgTypes{Pthread_attr_tPtrTy, SizeTy}, RetType{IntTy}), + Summary(NoEvalCall) + .ArgConstraint(NotNull(ArgNo(0))) + .ArgConstraint( + ArgumentCondition(1, WithinRange, Range(0, SizeMax)))); + + // int pthread_mutex_init(pthread_mutex_t *restrict mutex, const + // pthread_mutexattr_t *restrict attr); + addToFunctionSummaryMap( + "pthread_mutex_init", + Signature(ArgTypes{Pthread_mutex_tPtrRestrictTy, + ConstPthread_mutexattr_tPtrRestrictTy}, + RetType{IntTy}), + Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); + + // int pthread_mutex_destroy(pthread_mutex_t *mutex); + // int pthread_mutex_lock(pthread_mutex_t *mutex); + // int pthread_mutex_trylock(pthread_mutex_t *mutex); + // int pthread_mutex_unlock(pthread_mutex_t *mutex); + addToFunctionSummaryMap( + {"pthread_mutex_destroy", "pthread_mutex_lock", "pthread_mutex_trylock", + "pthread_mutex_unlock"}, + Signature(ArgTypes{Pthread_mutex_tPtrTy}, RetType{IntTy}), + Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); } // Functions for testing. if (ChecksEnabled[CK_StdCLibraryFunctionsTesterChecker]) { addToFunctionSummaryMap( "__two_constrained_args", - Summary(ArgTypes{IntTy, IntTy}, RetType{IntTy}, EvalCallAsPure) + Signature(ArgTypes{IntTy, IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) .ArgConstraint(ArgumentCondition(0U, WithinRange, SingleValue(1))) .ArgConstraint(ArgumentCondition(1U, WithinRange, SingleValue(1)))); addToFunctionSummaryMap( - "__arg_constrained_twice", - Summary(ArgTypes{IntTy}, RetType{IntTy}, EvalCallAsPure) + "__arg_constrained_twice", Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) .ArgConstraint(ArgumentCondition(0U, OutOfRange, SingleValue(1))) .ArgConstraint(ArgumentCondition(0U, OutOfRange, SingleValue(2)))); addToFunctionSummaryMap( "__defaultparam", - Summary(ArgTypes{Irrelevant, IntTy}, RetType{IntTy}, EvalCallAsPure) - .ArgConstraint(NotNull(ArgNo(0)))); - addToFunctionSummaryMap("__variadic", - Summary(ArgTypes{VoidPtrTy, ConstCharPtrTy}, - RetType{IntTy}, EvalCallAsPure) - .ArgConstraint(NotNull(ArgNo(0))) - .ArgConstraint(NotNull(ArgNo(1)))); + Signature(ArgTypes{Irrelevant, IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure).ArgConstraint(NotNull(ArgNo(0)))); + addToFunctionSummaryMap( + "__variadic", + Signature(ArgTypes{VoidPtrTy, ConstCharPtrTy}, RetType{IntTy}), + Summary(EvalCallAsPure) + .ArgConstraint(NotNull(ArgNo(0))) + .ArgConstraint(NotNull(ArgNo(1)))); addToFunctionSummaryMap( "__buf_size_arg_constraint", - Summary(ArgTypes{ConstVoidPtrTy, SizeTy}, RetType{IntTy}, - EvalCallAsPure) + Signature(ArgTypes{ConstVoidPtrTy, SizeTy}, RetType{IntTy}), + Summary(EvalCallAsPure) .ArgConstraint( BufferSize(/*Buffer=*/ArgNo(0), /*BufSize=*/ArgNo(1)))); addToFunctionSummaryMap( "__buf_size_arg_constraint_mul", - Summary(ArgTypes{ConstVoidPtrTy, SizeTy, SizeTy}, RetType{IntTy}, - EvalCallAsPure) + Signature(ArgTypes{ConstVoidPtrTy, SizeTy, SizeTy}, RetType{IntTy}), + Summary(EvalCallAsPure) .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(0), /*BufSize=*/ArgNo(1), /*BufSizeMultiplier=*/ArgNo(2)))); + addToFunctionSummaryMap( + "__buf_size_arg_constraint_concrete", + Signature(ArgTypes{ConstVoidPtrTy}, RetType{IntTy}), + Summary(EvalCallAsPure) + .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(0), + /*BufSize=*/BVF.getValue(10, IntTy)))); + addToFunctionSummaryMap( + {"__test_restrict_param_0", "__test_restrict_param_1", + "__test_restrict_param_2"}, + Signature(ArgTypes{VoidPtrRestrictTy}, RetType{VoidTy}), + Summary(EvalCallAsPure)); } } @@ -1749,7 +2496,8 @@ void ento::registerStdCLibraryFunctionsChecker(CheckerManager &mgr) { mgr.getAnalyzerOptions().getCheckerBooleanOption(Checker, "ModelPOSIX"); } -bool ento::shouldRegisterStdCLibraryFunctionsChecker(const CheckerManager &mgr) { +bool ento::shouldRegisterStdCLibraryFunctionsChecker( + const CheckerManager &mgr) { return true; } diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index f6abbe4f8f03..6b176b3c4e2b 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -204,7 +204,8 @@ class StreamChecker : public Checker<check::PreCall, eval::Call, BugType BT_IllegalWhence{this, "Illegal whence argument", "Stream handling error"}; BugType BT_StreamEof{this, "Stream already in EOF", "Stream handling error"}; - BugType BT_ResourceLeak{this, "Resource leak", "Stream handling error"}; + BugType BT_ResourceLeak{this, "Resource leak", "Stream handling error", + /*SuppressOnSink =*/true}; public: void checkPreCall(const CallEvent &Call, CheckerContext &C) const; @@ -337,6 +338,12 @@ private: /// to ensure uniform handling. void reportFEofWarning(CheckerContext &C, ProgramStateRef State) const; + /// Emit resource leak warnings for the given symbols. + /// Createn a non-fatal error node for these, and returns it (if any warnings + /// were generated). Return value is non-null. + ExplodedNode *reportLeaks(const SmallVector<SymbolRef, 2> &LeakedSyms, + CheckerContext &C, ExplodedNode *Pred) const; + /// Find the description data of the function called by a call event. /// Returns nullptr if no function is recognized. const FnDescription *lookupFn(const CallEvent &Call) const { @@ -956,28 +963,14 @@ void StreamChecker::reportFEofWarning(CheckerContext &C, C.addTransition(State); } -void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper, - CheckerContext &C) const { - ProgramStateRef State = C.getState(); - - // TODO: Clean up the state. - const StreamMapTy &Map = State->get<StreamMap>(); - for (const auto &I : Map) { - SymbolRef Sym = I.first; - const StreamState &SS = I.second; - if (!SymReaper.isDead(Sym) || !SS.isOpened()) - continue; - - ExplodedNode *N = C.generateErrorNode(); - if (!N) - continue; - - // Do not warn for non-closed stream at program exit. - ExplodedNode *Pred = C.getPredecessor(); - if (Pred && Pred->getCFGBlock() && - Pred->getCFGBlock()->hasNoReturnElement()) - continue; +ExplodedNode * +StreamChecker::reportLeaks(const SmallVector<SymbolRef, 2> &LeakedSyms, + CheckerContext &C, ExplodedNode *Pred) const { + ExplodedNode *Err = C.generateNonFatalErrorNode(C.getState(), Pred); + if (!Err) + return Pred; + for (SymbolRef LeakSym : LeakedSyms) { // Resource leaks can result in multiple warning that describe the same kind // of programming error: // void f() { @@ -989,8 +982,7 @@ void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper, // from a different kinds of errors), the reduction in redundant reports // makes this a worthwhile heuristic. // FIXME: Add a checker option to turn this uniqueing feature off. - - const ExplodedNode *StreamOpenNode = getAcquisitionSite(N, Sym, C); + const ExplodedNode *StreamOpenNode = getAcquisitionSite(Err, LeakSym, C); assert(StreamOpenNode && "Could not find place of stream opening."); PathDiagnosticLocation LocUsedForUniqueing = PathDiagnosticLocation::createBegin( @@ -1000,12 +992,38 @@ void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper, std::unique_ptr<PathSensitiveBugReport> R = std::make_unique<PathSensitiveBugReport>( BT_ResourceLeak, - "Opened stream never closed. Potential resource leak.", N, + "Opened stream never closed. Potential resource leak.", Err, LocUsedForUniqueing, StreamOpenNode->getLocationContext()->getDecl()); - R->markInteresting(Sym); + R->markInteresting(LeakSym); C.emitReport(std::move(R)); } + + return Err; +} + +void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + + llvm::SmallVector<SymbolRef, 2> LeakedSyms; + + const StreamMapTy &Map = State->get<StreamMap>(); + for (const auto &I : Map) { + SymbolRef Sym = I.first; + const StreamState &SS = I.second; + if (!SymReaper.isDead(Sym)) + continue; + if (SS.isOpened()) + LeakedSyms.push_back(Sym); + State = State->remove<StreamMap>(Sym); + } + + ExplodedNode *N = C.getPredecessor(); + if (!LeakedSyms.empty()) + N = reportLeaks(LeakedSyms, C, N); + + C.addTransition(State, N); } ProgramStateRef StreamChecker::checkPointerEscape( diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Taint.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Taint.cpp index 5b46ffb656cf..71b2ab834a07 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Taint.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Taint.cpp @@ -148,7 +148,7 @@ bool taint::isTainted(ProgramStateRef State, const Stmt *S, } bool taint::isTainted(ProgramStateRef State, SVal V, TaintTagType Kind) { - if (const SymExpr *Sym = V.getAsSymExpr()) + if (SymbolRef Sym = V.getAsSymbol()) return isTainted(State, Sym, Kind); if (const MemRegion *Reg = V.getAsRegion()) return isTainted(State, Reg, Kind); diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp index 3e0caaf79ca0..ebe5ad53cc30 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp @@ -11,6 +11,8 @@ // //===----------------------------------------------------------------------===// +#include "clang/AST/StmtObjC.h" +#include "clang/AST/Type.h" #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" @@ -54,10 +56,13 @@ public: void checkBranchCondition(const Stmt *Condition, CheckerContext &Ctx) const; }; -} +} // namespace void UndefBranchChecker::checkBranchCondition(const Stmt *Condition, CheckerContext &Ctx) const { + // 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 diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp index f49ee5fa5ad3..1c589e3468c2 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp @@ -125,8 +125,8 @@ void VirtualCallChecker::checkPreCall(const CallEvent &Call, OS << "Call to "; if (IsPure) OS << "pure "; - OS << "virtual method '" << MD->getParent()->getNameAsString() - << "::" << MD->getNameAsString() << "' during "; + OS << "virtual method '" << MD->getParent()->getDeclName() + << "::" << MD->getDeclName() << "' during "; if (*ObState == ObjectState::CtorCalled) OS << "construction "; else 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 34c072ac2241..9c7a59971763 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp @@ -34,7 +34,9 @@ tryToFindPtrOrigin(const Expr *E, bool StopAtFirstRefCountedObj) { } if (auto *call = dyn_cast<CallExpr>(E)) { if (auto *memberCall = dyn_cast<CXXMemberCallExpr>(call)) { - if (isGetterOfRefCounted(memberCall->getMethodDecl())) { + Optional<bool> IsGetterOfRefCt = + isGetterOfRefCounted(memberCall->getMethodDecl()); + if (IsGetterOfRefCt && *IsGetterOfRefCt) { E = memberCall->getImplicitObjectArgument(); if (StopAtFirstRefCountedObj) { return {E, true}; 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 3956db933b35..97f75135bf92 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/NoUncountedMembersChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/NoUncountedMembersChecker.cpp @@ -76,8 +76,11 @@ public: if (auto *MemberCXXRD = MemberType->getPointeeCXXRecordDecl()) { // If we don't see the definition we just don't know. - if (MemberCXXRD->hasDefinition() && isRefCountable(MemberCXXRD)) - reportBug(Member, MemberType, MemberCXXRD, RD); + if (MemberCXXRD->hasDefinition()) { + llvm::Optional<bool> isRCAble = isRefCountable(MemberCXXRD); + if (isRCAble && *isRCAble) + reportBug(Member, MemberType, MemberCXXRD, RD); + } } } } 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 168cfd511170..a198943c9433 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp @@ -12,6 +12,7 @@ #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/ExprCXX.h" +#include "llvm/ADT/Optional.h" using llvm::Optional; using namespace clang; @@ -20,6 +21,7 @@ namespace { bool hasPublicRefAndDeref(const CXXRecordDecl *R) { assert(R); + assert(R->hasDefinition()); bool hasRef = false; bool hasDeref = false; @@ -43,25 +45,29 @@ bool hasPublicRefAndDeref(const CXXRecordDecl *R) { namespace clang { -const CXXRecordDecl *isRefCountable(const CXXBaseSpecifier *Base) { +llvm::Optional<const clang::CXXRecordDecl *> +isRefCountable(const CXXBaseSpecifier *Base) { assert(Base); const Type *T = Base->getType().getTypePtrOrNull(); if (!T) - return nullptr; + return llvm::None; const CXXRecordDecl *R = T->getAsCXXRecordDecl(); if (!R) - return nullptr; + return llvm::None; + if (!R->hasDefinition()) + return llvm::None; return hasPublicRefAndDeref(R) ? R : nullptr; } -bool isRefCountable(const CXXRecordDecl *R) { +llvm::Optional<bool> isRefCountable(const CXXRecordDecl *R) { assert(R); R = R->getDefinition(); - assert(R); + if (!R) + return llvm::None; if (hasPublicRefAndDeref(R)) return true; @@ -69,13 +75,24 @@ bool isRefCountable(const CXXRecordDecl *R) { CXXBasePaths Paths; Paths.setOrigin(const_cast<CXXRecordDecl *>(R)); - const auto isRefCountableBase = [](const CXXBaseSpecifier *Base, - CXXBasePath &) { - return clang::isRefCountable(Base); - }; + bool AnyInconclusiveBase = false; + const auto isRefCountableBase = + [&AnyInconclusiveBase](const CXXBaseSpecifier *Base, CXXBasePath &) { + Optional<const clang::CXXRecordDecl *> IsRefCountable = + clang::isRefCountable(Base); + if (!IsRefCountable) { + AnyInconclusiveBase = true; + return false; + } + return (*IsRefCountable) != nullptr; + }; + + bool BasesResult = R->lookupInBases(isRefCountableBase, Paths, + /*LookupInDependent =*/true); + if (AnyInconclusiveBase) + return llvm::None; - return R->lookupInBases(isRefCountableBase, Paths, - /*LookupInDependent =*/true); + return BasesResult; } bool isCtorOfRefCounted(const clang::FunctionDecl *F) { @@ -95,12 +112,19 @@ bool isCtorOfRefCounted(const clang::FunctionDecl *F) { || FunctionName == "Identifier"; } -bool isUncounted(const CXXRecordDecl *Class) { +llvm::Optional<bool> isUncounted(const CXXRecordDecl *Class) { // Keep isRefCounted first as it's cheaper. - return !isRefCounted(Class) && isRefCountable(Class); + if (isRefCounted(Class)) + return false; + + llvm::Optional<bool> IsRefCountable = isRefCountable(Class); + if (!IsRefCountable) + return llvm::None; + + return (*IsRefCountable); } -bool isUncountedPtr(const Type *T) { +llvm::Optional<bool> isUncountedPtr(const Type *T) { assert(T); if (T->isPointerType() || T->isReferenceType()) { @@ -111,7 +135,7 @@ bool isUncountedPtr(const Type *T) { return false; } -bool isGetterOfRefCounted(const CXXMethodDecl *M) { +Optional<bool> isGetterOfRefCounted(const CXXMethodDecl *M) { assert(M); if (isa<CXXMethodDecl>(M)) { @@ -133,9 +157,7 @@ bool isGetterOfRefCounted(const CXXMethodDecl *M) { if (auto *maybeRefToRawOperator = dyn_cast<CXXConversionDecl>(M)) { if (auto *targetConversionType = maybeRefToRawOperator->getConversionType().getTypePtrOrNull()) { - if (isUncountedPtr(targetConversionType)) { - return true; - } + return isUncountedPtr(targetConversionType); } } } 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 83d9c0bcc13b..730a59977175 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h @@ -9,6 +9,8 @@ #ifndef LLVM_CLANG_ANALYZER_WEBKIT_PTRTYPESEMANTICS_H #define LLVM_CLANG_ANALYZER_WEBKIT_PTRTYPESEMANTICS_H +#include "llvm/ADT/APInt.h" + namespace clang { class CXXBaseSpecifier; class CXXMethodDecl; @@ -25,30 +27,31 @@ class Type; // Ref<T>. /// \returns CXXRecordDecl of the base if the type is ref-countable, nullptr if -/// not. -const clang::CXXRecordDecl *isRefCountable(const clang::CXXBaseSpecifier *Base); +/// not, None if inconclusive. +llvm::Optional<const clang::CXXRecordDecl *> +isRefCountable(const clang::CXXBaseSpecifier *Base); -/// \returns true if \p Class is ref-countable, false if not. -/// Asserts that \p Class IS a definition. -bool isRefCountable(const clang::CXXRecordDecl *Class); +/// \returns true if \p Class is ref-countable, false if not, None if +/// inconclusive. +llvm::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. Asserts that \p Class IS a definition. -bool isUncounted(const clang::CXXRecordDecl *Class); +/// not, None if inconclusive. +llvm::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. -bool isUncountedPtr(const clang::Type *T); +/// class, false if not, None if inconclusive. +llvm::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. -bool isGetterOfRefCounted(const clang::CXXMethodDecl *Method); +llvm::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 81ce284c2dc7..fa9ece217cc0 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp @@ -76,19 +76,15 @@ public: (AccSpec == AS_none && RD->isClass())) return false; - llvm::Optional<const clang::CXXRecordDecl *> MaybeRefCntblBaseRD = + llvm::Optional<const CXXRecordDecl *> RefCntblBaseRD = isRefCountable(Base); - if (!MaybeRefCntblBaseRD.hasValue()) + if (!RefCntblBaseRD || !(*RefCntblBaseRD)) return false; - const CXXRecordDecl *RefCntblBaseRD = MaybeRefCntblBaseRD.getValue(); - if (!RefCntblBaseRD) - return false; - - const auto *Dtor = RefCntblBaseRD->getDestructor(); + const auto *Dtor = (*RefCntblBaseRD)->getDestructor(); if (!Dtor || !Dtor->isVirtual()) { ProblematicBaseSpecifier = Base; - ProblematicBaseClass = RefCntblBaseRD; + ProblematicBaseClass = *RefCntblBaseRD; return true; } 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 940a1f349831..d70bd9489d2c 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp @@ -86,7 +86,8 @@ public: continue; // FIXME? Should we bail? // FIXME: more complex types (arrays, references to raw pointers, etc) - if (!isUncountedPtr(ArgType)) + Optional<bool> IsUncounted = isUncountedPtr(ArgType); + if (!IsUncounted || !(*IsUncounted)) continue; const auto *Arg = CE->getArg(ArgIdx); diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp new file mode 100644 index 000000000000..deebbd603b2c --- /dev/null +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp @@ -0,0 +1,107 @@ +//=======- UncountedLambdaCapturesChecker.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 "DiagOutputUtils.h" +#include "PtrTypesSemantics.h" +#include "clang/AST/CXXInheritance.h" +#include "clang/AST/RecursiveASTVisitor.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" + +using namespace clang; +using namespace ento; + +namespace { +class UncountedLambdaCapturesChecker + : public Checker<check::ASTDecl<TranslationUnitDecl>> { +private: + BugType Bug{this, "Lambda capture of uncounted variable", + "WebKit coding guidelines"}; + mutable BugReporter *BR; + +public: + void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR, + BugReporter &BRArg) const { + BR = &BRArg; + + // The calls to checkAST* from AnalysisConsumer don't + // visit template instantiations or lambda classes. We + // want to visit those, so we make our own RecursiveASTVisitor. + struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> { + const UncountedLambdaCapturesChecker *Checker; + explicit LocalVisitor(const UncountedLambdaCapturesChecker *Checker) + : Checker(Checker) { + assert(Checker); + } + + bool shouldVisitTemplateInstantiations() const { return true; } + bool shouldVisitImplicitCode() const { return false; } + + bool VisitLambdaExpr(LambdaExpr *L) { + Checker->visitLambdaExpr(L); + return true; + } + }; + + LocalVisitor visitor(this); + visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD)); + } + + void visitLambdaExpr(LambdaExpr *L) const { + for (const LambdaCapture &C : L->captures()) { + if (C.capturesVariable()) { + VarDecl *CapturedVar = C.getCapturedVar(); + if (auto *CapturedVarType = CapturedVar->getType().getTypePtrOrNull()) { + Optional<bool> IsUncountedPtr = isUncountedPtr(CapturedVarType); + if (IsUncountedPtr && *IsUncountedPtr) { + reportBug(C, CapturedVar, CapturedVarType); + } + } + } + } + } + + void reportBug(const LambdaCapture &Capture, VarDecl *CapturedVar, + const Type *T) const { + assert(CapturedVar); + + SmallString<100> Buf; + llvm::raw_svector_ostream Os(Buf); + + if (Capture.isExplicit()) { + Os << "Captured "; + } else { + Os << "Implicitly captured "; + } + if (T->isPointerType()) { + Os << "raw-pointer "; + } else { + assert(T->isReferenceType()); + Os << "reference "; + } + + printQuotedQualifiedName(Os, Capture.getCapturedVar()); + Os << " to uncounted type is unsafe."; + + PathDiagnosticLocation BSLoc(Capture.getLocation(), BR->getSourceManager()); + auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc); + BR->emitReport(std::move(Report)); + } +}; +} // namespace + +void ento::registerUncountedLambdaCapturesChecker(CheckerManager &Mgr) { + Mgr.registerChecker<UncountedLambdaCapturesChecker>(); +} + +bool ento::shouldRegisterUncountedLambdaCapturesChecker( + const CheckerManager &mgr) { + return true; +} diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLocalVarsChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLocalVarsChecker.cpp new file mode 100644 index 000000000000..7e86f28cb70f --- /dev/null +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLocalVarsChecker.cpp @@ -0,0 +1,251 @@ +//=======- UncountedLocalVarsChecker.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 "ASTUtils.h" +#include "DiagOutputUtils.h" +#include "PtrTypesSemantics.h" +#include "clang/AST/CXXInheritance.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/ParentMapContext.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Basic/SourceLocation.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 "llvm/ADT/DenseSet.h" + +using namespace clang; +using namespace ento; + +namespace { + +// for ( int a = ...) ... true +// for ( int a : ...) ... true +// if ( int* a = ) ... true +// anything else ... false +bool isDeclaredInForOrIf(const VarDecl *Var) { + assert(Var); + auto &ASTCtx = Var->getASTContext(); + auto parent = ASTCtx.getParents(*Var); + + if (parent.size() == 1) { + if (auto *DS = parent.begin()->get<DeclStmt>()) { + DynTypedNodeList grandParent = ASTCtx.getParents(*DS); + if (grandParent.size() == 1) { + return grandParent.begin()->get<ForStmt>() || + grandParent.begin()->get<IfStmt>() || + grandParent.begin()->get<CXXForRangeStmt>(); + } + } + } + return false; +} + +// FIXME: should be defined by anotations in the future +bool isRefcountedStringsHack(const VarDecl *V) { + assert(V); + auto safeClass = [](const std::string &className) { + return className == "String" || className == "AtomString" || + className == "UniquedString" || className == "Identifier"; + }; + QualType QT = V->getType(); + auto *T = QT.getTypePtr(); + if (auto *CXXRD = T->getAsCXXRecordDecl()) { + if (safeClass(safeGetName(CXXRD))) + return true; + } + if (T->isPointerType() || T->isReferenceType()) { + if (auto *CXXRD = T->getPointeeCXXRecordDecl()) { + if (safeClass(safeGetName(CXXRD))) + return true; + } + } + return false; +} + +bool isGuardedScopeEmbeddedInGuardianScope(const VarDecl *Guarded, + const VarDecl *MaybeGuardian) { + assert(Guarded); + assert(MaybeGuardian); + + if (!MaybeGuardian->isLocalVarDecl()) + return false; + + const CompoundStmt *guardiansClosestCompStmtAncestor = nullptr; + + ASTContext &ctx = MaybeGuardian->getASTContext(); + + for (DynTypedNodeList guardianAncestors = ctx.getParents(*MaybeGuardian); + !guardianAncestors.empty(); + guardianAncestors = ctx.getParents( + *guardianAncestors + .begin()) // FIXME - should we handle all of the parents? + ) { + for (auto &guardianAncestor : guardianAncestors) { + if (auto *CStmtParentAncestor = guardianAncestor.get<CompoundStmt>()) { + guardiansClosestCompStmtAncestor = CStmtParentAncestor; + break; + } + } + if (guardiansClosestCompStmtAncestor) + break; + } + + if (!guardiansClosestCompStmtAncestor) + return false; + + // We need to skip the first CompoundStmt to avoid situation when guardian is + // defined in the same scope as guarded variable. + bool HaveSkippedFirstCompoundStmt = false; + for (DynTypedNodeList guardedVarAncestors = ctx.getParents(*Guarded); + !guardedVarAncestors.empty(); + guardedVarAncestors = ctx.getParents( + *guardedVarAncestors + .begin()) // FIXME - should we handle all of the parents? + ) { + for (auto &guardedVarAncestor : guardedVarAncestors) { + if (auto *CStmtAncestor = guardedVarAncestor.get<CompoundStmt>()) { + if (!HaveSkippedFirstCompoundStmt) { + HaveSkippedFirstCompoundStmt = true; + continue; + } + if (CStmtAncestor == guardiansClosestCompStmtAncestor) + return true; + } + } + } + + return false; +} + +class UncountedLocalVarsChecker + : public Checker<check::ASTDecl<TranslationUnitDecl>> { + BugType Bug{this, + "Uncounted raw pointer or reference not provably backed by " + "ref-counted variable", + "WebKit coding guidelines"}; + mutable BugReporter *BR; + +public: + void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR, + BugReporter &BRArg) const { + BR = &BRArg; + + // The calls to checkAST* from AnalysisConsumer don't + // visit template instantiations or lambda classes. We + // want to visit those, so we make our own RecursiveASTVisitor. + struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> { + const UncountedLocalVarsChecker *Checker; + explicit LocalVisitor(const UncountedLocalVarsChecker *Checker) + : Checker(Checker) { + assert(Checker); + } + + bool shouldVisitTemplateInstantiations() const { return true; } + bool shouldVisitImplicitCode() const { return false; } + + bool VisitVarDecl(VarDecl *V) { + Checker->visitVarDecl(V); + return true; + } + }; + + LocalVisitor visitor(this); + visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD)); + } + + void visitVarDecl(const VarDecl *V) const { + if (shouldSkipVarDecl(V)) + return; + + const auto *ArgType = V->getType().getTypePtr(); + if (!ArgType) + return; + + Optional<bool> IsUncountedPtr = isUncountedPtr(ArgType); + if (IsUncountedPtr && *IsUncountedPtr) { + const Expr *const InitExpr = V->getInit(); + if (!InitExpr) + return; // FIXME: later on we might warn on uninitialized vars too + + const clang::Expr *const InitArgOrigin = + tryToFindPtrOrigin(InitExpr, /*StopAtFirstRefCountedObj=*/false) + .first; + if (!InitArgOrigin) + return; + + if (isa<CXXThisExpr>(InitArgOrigin)) + return; + + if (auto *Ref = llvm::dyn_cast<DeclRefExpr>(InitArgOrigin)) { + if (auto *MaybeGuardian = + dyn_cast_or_null<VarDecl>(Ref->getFoundDecl())) { + const auto *MaybeGuardianArgType = + MaybeGuardian->getType().getTypePtr(); + if (!MaybeGuardianArgType) + return; + const CXXRecordDecl *const MaybeGuardianArgCXXRecord = + MaybeGuardianArgType->getAsCXXRecordDecl(); + if (!MaybeGuardianArgCXXRecord) + return; + + if (MaybeGuardian->isLocalVarDecl() && + (isRefCounted(MaybeGuardianArgCXXRecord) || + isRefcountedStringsHack(MaybeGuardian)) && + isGuardedScopeEmbeddedInGuardianScope(V, MaybeGuardian)) { + return; + } + + // Parameters are guaranteed to be safe for the duration of the call + // by another checker. + if (isa<ParmVarDecl>(MaybeGuardian)) + return; + } + } + + reportBug(V); + } + } + + bool shouldSkipVarDecl(const VarDecl *V) const { + assert(V); + if (!V->isLocalVarDecl()) + return true; + + if (isDeclaredInForOrIf(V)) + return true; + + return false; + } + + void reportBug(const VarDecl *V) const { + assert(V); + SmallString<100> Buf; + llvm::raw_svector_ostream Os(Buf); + + Os << "Local variable "; + printQuotedQualifiedName(Os, V); + Os << " is uncounted and unsafe."; + + PathDiagnosticLocation BSLoc(V->getLocation(), BR->getSourceManager()); + auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc); + Report->addRange(V->getSourceRange()); + BR->emitReport(std::move(Report)); + } +}; +} // namespace + +void ento::registerUncountedLocalVarsChecker(CheckerManager &Mgr) { + Mgr.registerChecker<UncountedLocalVarsChecker>(); +} + +bool ento::shouldRegisterUncountedLocalVarsChecker(const CheckerManager &) { + return true; +} diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp index 01ac2bc83bb6..8cd7f75e4e38 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp @@ -40,7 +40,7 @@ void AnalyzerOptions::printFormattedEntry( const size_t PadForDesc = InitialPad + EntryWidth; FOut.PadToColumn(InitialPad) << EntryDescPair.first; - // If the buffer's length is greater then PadForDesc, print a newline. + // If the buffer's length is greater than PadForDesc, print a newline. if (FOut.getColumn() > PadForDesc) FOut << '\n'; diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp index 73f057f09550..d1f5ac02278f 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp @@ -42,7 +42,7 @@ void LazyCompoundValData::Profile(llvm::FoldingSetNodeID& ID, } void PointerToMemberData::Profile( - llvm::FoldingSetNodeID& ID, const DeclaratorDecl *D, + llvm::FoldingSetNodeID &ID, const NamedDecl *D, llvm::ImmutableList<const CXXBaseSpecifier *> L) { ID.AddPointer(D); ID.AddPointer(L.getInternalPointer()); @@ -159,17 +159,17 @@ BasicValueFactory::getLazyCompoundValData(const StoreRef &store, } const PointerToMemberData *BasicValueFactory::getPointerToMemberData( - const DeclaratorDecl *DD, llvm::ImmutableList<const CXXBaseSpecifier *> L) { + const NamedDecl *ND, llvm::ImmutableList<const CXXBaseSpecifier *> L) { llvm::FoldingSetNodeID ID; - PointerToMemberData::Profile(ID, DD, L); + PointerToMemberData::Profile(ID, ND, L); void *InsertPos; PointerToMemberData *D = PointerToMemberDataSet.FindNodeOrInsertPos(ID, InsertPos); if (!D) { - D = (PointerToMemberData*) BPAlloc.Allocate<PointerToMemberData>(); - new (D) PointerToMemberData(DD, L); + D = (PointerToMemberData *)BPAlloc.Allocate<PointerToMemberData>(); + new (D) PointerToMemberData(ND, L); PointerToMemberDataSet.InsertNode(D, InsertPos); } @@ -180,25 +180,24 @@ const PointerToMemberData *BasicValueFactory::accumCXXBase( llvm::iterator_range<CastExpr::path_const_iterator> PathRange, const nonloc::PointerToMember &PTM) { nonloc::PointerToMember::PTMDataType PTMDT = PTM.getPTMData(); - const DeclaratorDecl *DD = nullptr; + const NamedDecl *ND = nullptr; llvm::ImmutableList<const CXXBaseSpecifier *> PathList; - if (PTMDT.isNull() || PTMDT.is<const DeclaratorDecl *>()) { - if (PTMDT.is<const DeclaratorDecl *>()) - DD = PTMDT.get<const DeclaratorDecl *>(); + if (PTMDT.isNull() || PTMDT.is<const NamedDecl *>()) { + if (PTMDT.is<const NamedDecl *>()) + ND = PTMDT.get<const NamedDecl *>(); PathList = CXXBaseListFactory.getEmptyList(); } else { // const PointerToMemberData * - const PointerToMemberData *PTMD = - PTMDT.get<const PointerToMemberData *>(); - DD = PTMD->getDeclaratorDecl(); + const PointerToMemberData *PTMD = PTMDT.get<const PointerToMemberData *>(); + ND = PTMD->getDeclaratorDecl(); PathList = PTMD->getCXXBaseList(); } for (const auto &I : llvm::reverse(PathRange)) PathList = prependCXXBase(I, PathList); - return getPointerToMemberData(DD, PathList); + return getPointerToMemberData(ND, PathList); } const llvm::APSInt* diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BugReporter.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BugReporter.cpp index 72be4e81c83d..bf38891b370a 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BugReporter.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BugReporter.cpp @@ -1570,9 +1570,8 @@ static Optional<size_t> getLengthOnSingleLine(const SourceManager &SM, if (FID != SM.getFileID(ExpansionRange.getEnd())) return None; - bool Invalid; - const llvm::MemoryBuffer *Buffer = SM.getBuffer(FID, &Invalid); - if (Invalid) + Optional<MemoryBufferRef> Buffer = SM.getBufferOrNone(FID); + if (!Buffer) return None; unsigned BeginOffset = SM.getFileOffset(ExpansionRange.getBegin()); @@ -2194,8 +2193,8 @@ void BasicBugReport::Profile(llvm::FoldingSetNodeID& hash) const { for (SourceRange range : Ranges) { if (!range.isValid()) continue; - hash.AddInteger(range.getBegin().getRawEncoding()); - hash.AddInteger(range.getEnd().getRawEncoding()); + hash.Add(range.getBegin()); + hash.Add(range.getEnd()); } } @@ -2217,8 +2216,8 @@ void PathSensitiveBugReport::Profile(llvm::FoldingSetNodeID &hash) const { for (SourceRange range : Ranges) { if (!range.isValid()) continue; - hash.AddInteger(range.getBegin().getRawEncoding()); - hash.AddInteger(range.getEnd().getRawEncoding()); + hash.Add(range.getBegin()); + hash.Add(range.getEnd()); } } diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp index ef4d38ff498f..bc72f4f8c1e3 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp @@ -2813,7 +2813,7 @@ UndefOrNullArgVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC, //===----------------------------------------------------------------------===// FalsePositiveRefutationBRVisitor::FalsePositiveRefutationBRVisitor() - : Constraints(ConstraintRangeTy::Factory().getEmptyMap()) {} + : Constraints(ConstraintMap::Factory().getEmptyMap()) {} void FalsePositiveRefutationBRVisitor::finalizeVisitor( BugReporterContext &BRC, const ExplodedNode *EndPathNode, @@ -2855,9 +2855,8 @@ void FalsePositiveRefutationBRVisitor::finalizeVisitor( void FalsePositiveRefutationBRVisitor::addConstraints( const ExplodedNode *N, bool OverwriteConstraintsOnExistingSyms) { // Collect new constraints - const ConstraintRangeTy &NewCs = N->getState()->get<ConstraintRange>(); - ConstraintRangeTy::Factory &CF = - N->getState()->get_context<ConstraintRange>(); + ConstraintMap NewCs = getConstraintMap(N->getState()); + ConstraintMap::Factory &CF = N->getState()->get_context<ConstraintMap>(); // Add constraints if we don't have them yet for (auto const &C : NewCs) { diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CallEvent.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CallEvent.cpp index 78d13ddfb773..a55d9302ca58 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CallEvent.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CallEvent.cpp @@ -687,7 +687,7 @@ void CXXInstanceCall::getExtraInvalidatedValues( // base class decl, rather than the class of the instance which needs to be // checked for mutable fields. // TODO: We might as well look at the dynamic type of the object. - const Expr *Ex = getCXXThisExpr()->ignoreParenBaseCasts(); + const Expr *Ex = getCXXThisExpr()->IgnoreParenBaseCasts(); QualType T = Ex->getType(); if (T->isPointerType()) // Arrow or implicit-this syntax? T = T->getPointeeType(); diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CheckerContext.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CheckerContext.cpp index 725ff1002e29..3d44d2cbc069 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CheckerContext.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CheckerContext.cpp @@ -93,7 +93,7 @@ StringRef CheckerContext::getMacroNameOrSpelling(SourceLocation &Loc) { if (Loc.isMacroID()) return Lexer::getImmediateMacroName(Loc, getSourceManager(), getLangOpts()); - SmallVector<char, 16> buf; + SmallString<16> buf; return Lexer::getSpelling(Loc, buf, getSourceManager(), getLangOpts()); } diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/Environment.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/Environment.cpp index 9e6d79bb7dcc..ee7474592528 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/Environment.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/Environment.cpp @@ -15,6 +15,7 @@ #include "clang/AST/ExprCXX.h" #include "clang/AST/PrettyPrinter.h" #include "clang/AST/Stmt.h" +#include "clang/AST/StmtObjC.h" #include "clang/Analysis/AnalysisDeclContext.h" #include "clang/Basic/LLVM.h" #include "clang/Basic/LangOptions.h" @@ -85,6 +86,12 @@ SVal Environment::lookupExpr(const EnvironmentEntry &E) const { SVal Environment::getSVal(const EnvironmentEntry &Entry, SValBuilder& svalBuilder) const { const Stmt *S = Entry.getStmt(); + assert(!isa<ObjCForCollectionStmt>(S) && + "Use ExprEngine::hasMoreIteration()!"); + assert((isa<Expr>(S) || isa<ReturnStmt>(S)) && + "Environment can only argue about Exprs, since only they express " + "a value! Any non-expression statement stored in Environment is a " + "result of a hack!"); const LocationContext *LCtx = Entry.getLocationContext(); switch (S->getStmtClass()) { @@ -109,6 +116,7 @@ SVal Environment::getSVal(const EnvironmentEntry &Entry, case Stmt::StringLiteralClass: case Stmt::TypeTraitExprClass: case Stmt::SizeOfPackExprClass: + case Stmt::PredefinedExprClass: // Known constants; defer to SValBuilder. return svalBuilder.getConstantVal(cast<Expr>(S)).getValue(); @@ -183,18 +191,15 @@ EnvironmentManager::removeDeadBindings(Environment Env, F.getTreeFactory()); // Iterate over the block-expr bindings. - for (Environment::iterator I = Env.begin(), E = Env.end(); I != E; ++I) { + for (Environment::iterator I = Env.begin(), End = Env.end(); I != End; ++I) { const EnvironmentEntry &BlkExpr = I.getKey(); const SVal &X = I.getData(); - const bool IsBlkExprLive = - SymReaper.isLive(BlkExpr.getStmt(), BlkExpr.getLocationContext()); + const Expr *E = dyn_cast<Expr>(BlkExpr.getStmt()); + if (!E) + continue; - assert((isa<Expr>(BlkExpr.getStmt()) || !IsBlkExprLive) && - "Only Exprs can be live, LivenessAnalysis argues about the liveness " - "of *values*!"); - - if (IsBlkExprLive) { + if (SymReaper.isLive(E, BlkExpr.getLocationContext())) { // Copy the binding to the new map. EBMapRef = EBMapRef.add(BlkExpr, X); diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index 265dcd134213..f285b652c175 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -169,7 +169,7 @@ public: if (S) { S->printJson(Out, Helper, PP, /*AddQuotes=*/true); } else { - Out << '\"' << I->getAnyMember()->getNameAsString() << '\"'; + Out << '\"' << I->getAnyMember()->getDeclName() << '\"'; } } @@ -2129,6 +2129,83 @@ static const Stmt *ResolveCondition(const Stmt *Condition, llvm_unreachable("could not resolve condition"); } +using ObjCForLctxPair = + std::pair<const ObjCForCollectionStmt *, const LocationContext *>; + +REGISTER_MAP_WITH_PROGRAMSTATE(ObjCForHasMoreIterations, ObjCForLctxPair, bool) + +ProgramStateRef ExprEngine::setWhetherHasMoreIteration( + ProgramStateRef State, const ObjCForCollectionStmt *O, + const LocationContext *LC, bool HasMoreIteraton) { + assert(!State->contains<ObjCForHasMoreIterations>({O, LC})); + return State->set<ObjCForHasMoreIterations>({O, LC}, HasMoreIteraton); +} + +ProgramStateRef +ExprEngine::removeIterationState(ProgramStateRef State, + const ObjCForCollectionStmt *O, + const LocationContext *LC) { + assert(State->contains<ObjCForHasMoreIterations>({O, LC})); + return State->remove<ObjCForHasMoreIterations>({O, LC}); +} + +bool ExprEngine::hasMoreIteration(ProgramStateRef State, + const ObjCForCollectionStmt *O, + const LocationContext *LC) { + assert(State->contains<ObjCForHasMoreIterations>({O, LC})); + return *State->get<ObjCForHasMoreIterations>({O, LC}); +} + +/// Split the state on whether there are any more iterations left for this loop. +/// Returns a (HasMoreIteration, HasNoMoreIteration) pair, or None when the +/// acquisition of the loop condition value failed. +static Optional<std::pair<ProgramStateRef, ProgramStateRef>> +assumeCondition(const Stmt *Condition, ExplodedNode *N) { + ProgramStateRef State = N->getState(); + if (const auto *ObjCFor = dyn_cast<ObjCForCollectionStmt>(Condition)) { + bool HasMoreIteraton = + ExprEngine::hasMoreIteration(State, ObjCFor, N->getLocationContext()); + // Checkers have already ran on branch conditions, so the current + // information as to whether the loop has more iteration becomes outdated + // after this point. + State = ExprEngine::removeIterationState(State, ObjCFor, + N->getLocationContext()); + if (HasMoreIteraton) + return std::pair<ProgramStateRef, ProgramStateRef>{State, nullptr}; + else + return std::pair<ProgramStateRef, ProgramStateRef>{nullptr, State}; + } + SVal X = State->getSVal(Condition, N->getLocationContext()); + + if (X.isUnknownOrUndef()) { + // Give it a chance to recover from unknown. + if (const auto *Ex = dyn_cast<Expr>(Condition)) { + if (Ex->getType()->isIntegralOrEnumerationType()) { + // Try to recover some path-sensitivity. Right now casts of symbolic + // integers that promote their values are currently not tracked well. + // If 'Condition' is such an expression, try and recover the + // underlying value and use that instead. + SVal recovered = + RecoverCastedSymbol(State, Condition, N->getLocationContext(), + N->getState()->getStateManager().getContext()); + + if (!recovered.isUnknown()) { + X = recovered; + } + } + } + } + + // If the condition is still unknown, give up. + if (X.isUnknownOrUndef()) + return None; + + DefinedSVal V = X.castAs<DefinedSVal>(); + + ProgramStateRef StTrue, StFalse; + return State->assume(V); +} + void ExprEngine::processBranch(const Stmt *Condition, NodeBuilderContext& BldCtx, ExplodedNode *Pred, @@ -2165,48 +2242,28 @@ void ExprEngine::processBranch(const Stmt *Condition, return; BranchNodeBuilder builder(CheckersOutSet, Dst, BldCtx, DstT, DstF); - for (const auto PredI : CheckersOutSet) { - if (PredI->isSink()) + for (ExplodedNode *PredN : CheckersOutSet) { + if (PredN->isSink()) continue; - ProgramStateRef PrevState = PredI->getState(); - SVal X = PrevState->getSVal(Condition, PredI->getLocationContext()); - - if (X.isUnknownOrUndef()) { - // Give it a chance to recover from unknown. - if (const auto *Ex = dyn_cast<Expr>(Condition)) { - if (Ex->getType()->isIntegralOrEnumerationType()) { - // Try to recover some path-sensitivity. Right now casts of symbolic - // integers that promote their values are currently not tracked well. - // If 'Condition' is such an expression, try and recover the - // underlying value and use that instead. - SVal recovered = RecoverCastedSymbol(PrevState, Condition, - PredI->getLocationContext(), - getContext()); - - if (!recovered.isUnknown()) { - X = recovered; - } - } - } - } + ProgramStateRef PrevState = PredN->getState(); - // If the condition is still unknown, give up. - if (X.isUnknownOrUndef()) { - builder.generateNode(PrevState, true, PredI); - builder.generateNode(PrevState, false, PredI); + ProgramStateRef StTrue, StFalse; + if (const auto KnownCondValueAssumption = assumeCondition(Condition, PredN)) + std::tie(StTrue, StFalse) = *KnownCondValueAssumption; + else { + assert(!isa<ObjCForCollectionStmt>(Condition)); + builder.generateNode(PrevState, true, PredN); + builder.generateNode(PrevState, false, PredN); continue; } - - DefinedSVal V = X.castAs<DefinedSVal>(); - - ProgramStateRef StTrue, StFalse; - std::tie(StTrue, StFalse) = PrevState->assume(V); + if (StTrue && StFalse) + assert(!isa<ObjCForCollectionStmt>(Condition));; // Process the true branch. if (builder.isFeasible(true)) { if (StTrue) - builder.generateNode(StTrue, true, PredI); + builder.generateNode(StTrue, true, PredN); else builder.markInfeasible(true); } @@ -2214,7 +2271,7 @@ void ExprEngine::processBranch(const Stmt *Condition, // Process the false branch. if (builder.isFeasible(false)) { if (StFalse) - builder.generateNode(StFalse, false, PredI); + builder.generateNode(StFalse, false, PredN); else builder.markInfeasible(false); } @@ -2530,16 +2587,8 @@ void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D, return; } if (isa<FieldDecl>(D) || isa<IndirectFieldDecl>(D)) { - // FIXME: Compute lvalue of field pointers-to-member. - // Right now we just use a non-null void pointer, so that it gives proper - // results in boolean contexts. - // FIXME: Maybe delegate this to the surrounding operator&. - // Note how this expression is lvalue, however pointer-to-member is NonLoc. - SVal V = svalBuilder.conjureSymbolVal(Ex, LCtx, getContext().VoidPtrTy, - currBldrCtx->blockCount()); - state = state->assume(V.castAs<DefinedOrUnknownSVal>(), true); - Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V), nullptr, - ProgramPoint::PostLValueKind); + // Delegate all work related to pointer to members to the surrounding + // operator&. return; } if (isa<BindingDecl>(D)) { @@ -3100,7 +3149,7 @@ struct DOTGraphTraits<ExplodedGraph*> : public DefaultDOTGraphTraits { if (Stop(N)) return true; - if (N->succ_size() != 1 || !isNodeHidden(N->getFirstSucc())) + if (N->succ_size() != 1 || !isNodeHidden(N->getFirstSucc(), nullptr)) break; PostCallback(N); @@ -3109,7 +3158,7 @@ struct DOTGraphTraits<ExplodedGraph*> : public DefaultDOTGraphTraits { return false; } - static bool isNodeHidden(const ExplodedNode *N) { + static bool isNodeHidden(const ExplodedNode *N, const ExplodedGraph *G) { return N->isTrivial(); } @@ -3162,8 +3211,9 @@ void ExprEngine::ViewGraph(bool trim) { #ifndef NDEBUG std::string Filename = DumpGraph(trim); llvm::DisplayGraph(Filename, false, llvm::GraphProgram::DOT); -#endif +#else llvm::errs() << "Warning: viewing graph requires assertions" << "\n"; +#endif } @@ -3171,8 +3221,9 @@ void ExprEngine::ViewGraph(ArrayRef<const ExplodedNode*> Nodes) { #ifndef NDEBUG std::string Filename = DumpGraph(Nodes); llvm::DisplayGraph(Filename, false, llvm::GraphProgram::DOT); -#endif +#else llvm::errs() << "Warning: viewing graph requires assertions" << "\n"; +#endif } std::string ExprEngine::DumpGraph(bool trim, StringRef Filename) { @@ -3209,15 +3260,17 @@ std::string ExprEngine::DumpGraph(ArrayRef<const ExplodedNode*> Nodes, if (!TrimmedG.get()) { llvm::errs() << "warning: Trimmed ExplodedGraph is empty.\n"; + return ""; } else { return llvm::WriteGraph(TrimmedG.get(), "TrimmedExprEngine", /*ShortNames=*/false, /*Title=*/"Trimmed Exploded Graph", /*Filename=*/std::string(Filename)); } -#endif +#else llvm::errs() << "Warning: dumping graph requires assertions" << "\n"; return ""; +#endif } void *ProgramStateTrait<ReplayWithoutInlining>::GDMIndex() { diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp index c5e38cc7423d..18d1b2169eed 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp @@ -418,6 +418,8 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, case CK_ZeroToOCLOpaqueType: case CK_IntToOCLSampler: case CK_LValueBitCast: + case CK_FloatingToFixedPoint: + case CK_FixedPointToFloating: case CK_FixedPointCast: case CK_FixedPointToBoolean: case CK_FixedPointToIntegral: @@ -991,10 +993,11 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, ExplodedNode *Pred, if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Ex)) { const ValueDecl *VD = DRE->getDecl(); - if (isa<CXXMethodDecl>(VD) || isa<FieldDecl>(VD)) { + if (isa<CXXMethodDecl>(VD) || isa<FieldDecl>(VD) || + isa<IndirectFieldDecl>(VD)) { ProgramStateRef State = (*I)->getState(); const LocationContext *LCtx = (*I)->getLocationContext(); - SVal SV = svalBuilder.getMemberPointer(cast<DeclaratorDecl>(VD)); + SVal SV = svalBuilder.getMemberPointer(cast<NamedDecl>(VD)); Bldr.generateNode(U, *I, State->BindExpr(U, LCtx, SV)); break; } diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp index 38a680eb04c0..cab65687444b 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -132,10 +132,20 @@ SVal ExprEngine::computeObjectUnderConstruction( case ConstructionContext::SimpleConstructorInitializerKind: { const auto *ICC = cast<ConstructorInitializerConstructionContext>(CC); const auto *Init = ICC->getCXXCtorInitializer(); - assert(Init->isAnyMemberInitializer()); const CXXMethodDecl *CurCtor = cast<CXXMethodDecl>(LCtx->getDecl()); Loc ThisPtr = SVB.getCXXThis(CurCtor, LCtx->getStackFrame()); SVal ThisVal = State->getSVal(ThisPtr); + if (Init->isBaseInitializer()) { + const auto *ThisReg = cast<SubRegion>(ThisVal.getAsRegion()); + const CXXRecordDecl *BaseClass = + Init->getBaseClass()->getAsCXXRecordDecl(); + const auto *BaseReg = + MRMgr.getCXXBaseObjectRegion(BaseClass, ThisReg, + Init->isBaseVirtual()); + return SVB.makeLoc(BaseReg); + } + if (Init->isDelegatingInitializer()) + return ThisVal; const ValueDecl *Field; SVal FieldVal; @@ -364,8 +374,12 @@ ProgramStateRef ExprEngine::updateObjectsUnderConstruction( case ConstructionContext::CXX17ElidedCopyConstructorInitializerKind: case ConstructionContext::SimpleConstructorInitializerKind: { const auto *ICC = cast<ConstructorInitializerConstructionContext>(CC); - return addObjectUnderConstruction(State, ICC->getCXXCtorInitializer(), - LCtx, V); + const auto *Init = ICC->getCXXCtorInitializer(); + // Base and delegating initializers handled above + assert(Init->isAnyMemberInitializer() && + "Base and delegating initializers should have been handled by" + "computeObjectUnderConstruction()"); + return addObjectUnderConstruction(State, Init, LCtx, V); } case ConstructionContext::NewAllocatedObjectKind: { return State; @@ -602,11 +616,11 @@ void ExprEngine::handleConstructor(const Expr *E, *Call, *this); ExplodedNodeSet DstEvaluated; - StmtNodeBuilder Bldr(DstPreCall, DstEvaluated, *currBldrCtx); if (CE && CE->getConstructor()->isTrivial() && CE->getConstructor()->isCopyOrMoveConstructor() && !CallOpts.IsArrayCtorOrDtor) { + StmtNodeBuilder Bldr(DstPreCall, DstEvaluated, *currBldrCtx); // FIXME: Handle other kinds of trivial constructors as well. for (ExplodedNodeSet::iterator I = DstPreCall.begin(), E = DstPreCall.end(); I != E; ++I) @@ -626,6 +640,8 @@ void ExprEngine::handleConstructor(const Expr *E, // in the CFG, would be called at the end of the full expression or // later (for life-time extended temporaries) -- but avoids infeasible // paths when no-return temporary destructors are used for assertions. + ExplodedNodeSet DstEvaluatedPostProcessed; + StmtNodeBuilder Bldr(DstEvaluated, DstEvaluatedPostProcessed, *currBldrCtx); const AnalysisDeclContext *ADC = LCtx->getAnalysisDeclContext(); if (!ADC->getCFGBuildOptions().AddTemporaryDtors) { if (llvm::isa_and_nonnull<CXXTempObjectRegion>(TargetRegion) && @@ -655,7 +671,7 @@ void ExprEngine::handleConstructor(const Expr *E, } ExplodedNodeSet DstPostArgumentCleanup; - for (ExplodedNode *I : DstEvaluated) + for (ExplodedNode *I : DstEvaluatedPostProcessed) finishArgumentConstruction(DstPostArgumentCleanup, I, *Call); // If there were other constructors called for object-type arguments diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp index 52ba17d59ae0..996d3644e018 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -842,19 +842,7 @@ ExprEngine::mayInlineCallKind(const CallEvent &Call, const ExplodedNode *Pred, static bool hasMember(const ASTContext &Ctx, const CXXRecordDecl *RD, StringRef Name) { const IdentifierInfo &II = Ctx.Idents.get(Name); - DeclarationName DeclName = Ctx.DeclarationNames.getIdentifier(&II); - if (!RD->lookup(DeclName).empty()) - return true; - - CXXBasePaths Paths(false, false, false); - if (RD->lookupInBases( - [DeclName](const CXXBaseSpecifier *Specifier, CXXBasePath &Path) { - return CXXRecordDecl::FindOrdinaryMember(Specifier, Path, DeclName); - }, - Paths)) - return true; - - return false; + return RD->hasMemberName(Ctx.DeclarationNames.getIdentifier(&II)); } /// Returns true if the given C++ class is a container or iterator. diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp index eb9a0be2e5d6..5a55e81497b0 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp @@ -53,10 +53,8 @@ static void populateObjCForDestinationSet( ProgramStateRef state = Pred->getState(); const LocationContext *LCtx = Pred->getLocationContext(); - SVal hasElementsV = svalBuilder.makeTruthVal(hasElements); - - // FIXME: S is not an expression. We should not be binding values to it. - ProgramStateRef nextState = state->BindExpr(S, LCtx, hasElementsV); + ProgramStateRef nextState = + ExprEngine::setWhetherHasMoreIteration(state, S, LCtx, hasElements); if (auto MV = elementV.getAs<loc::MemRegionVal>()) if (const auto *R = dyn_cast<TypedValueRegion>(MV->getRegion())) { @@ -93,10 +91,9 @@ void ExprEngine::VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S, // (1) binds the next container value to 'element'. This creates a new // node in the ExplodedGraph. // - // (2) binds the value 0/1 to the ObjCForCollectionStmt* itself, indicating - // whether or not the container has any more elements. This value - // will be tested in ProcessBranch. We need to explicitly bind - // this value because a container can contain nil elements. + // (2) note whether the collection has any more elements (or in other words, + // whether the loop has more iterations). This will be tested in + // processBranch. // // FIXME: Eventually this logic should actually do dispatches to // 'countByEnumeratingWithState:objects:count:' (NSFastEnumeration). diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp index bc7c41d039c4..149459cf986a 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +#include "clang/Analysis/IssueHash.h" #include "clang/Analysis/PathDiagnostic.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclBase.h" @@ -23,8 +24,6 @@ #include "clang/Lex/Token.h" #include "clang/Rewrite/Core/HTMLRewrite.h" #include "clang/Rewrite/Core/Rewriter.h" -#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" -#include "clang/StaticAnalyzer/Core/IssueHash.h" #include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/SmallString.h" @@ -58,17 +57,18 @@ using namespace ento; namespace { class HTMLDiagnostics : public PathDiagnosticConsumer { + PathDiagnosticConsumerOptions DiagOpts; std::string Directory; bool createdDir = false; bool noDir = false; const Preprocessor &PP; - AnalyzerOptions &AnalyzerOpts; const bool SupportsCrossFileDiagnostics; public: - HTMLDiagnostics(AnalyzerOptions &AnalyzerOpts, const std::string &OutputDir, - const Preprocessor &pp, bool supportsMultipleFiles) - : Directory(OutputDir), PP(pp), AnalyzerOpts(AnalyzerOpts), + HTMLDiagnostics(PathDiagnosticConsumerOptions DiagOpts, + const std::string &OutputDir, const Preprocessor &pp, + bool supportsMultipleFiles) + : DiagOpts(std::move(DiagOpts)), Directory(OutputDir), PP(pp), SupportsCrossFileDiagnostics(supportsMultipleFiles) {} ~HTMLDiagnostics() override { FlushDiagnostics(nullptr); } @@ -133,7 +133,7 @@ private: } // namespace void ento::createHTMLDiagnosticConsumer( - AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C, + PathDiagnosticConsumerOptions DiagOpts, PathDiagnosticConsumers &C, const std::string &OutputDir, const Preprocessor &PP, const cross_tu::CrossTranslationUnitContext &CTU) { @@ -142,37 +142,38 @@ void ento::createHTMLDiagnosticConsumer( // output mode. This doesn't make much sense, we should have the minimal text // as our default. In the case of backward compatibility concerns, this could // be preserved with -analyzer-config-compatibility-mode=true. - createTextMinimalPathDiagnosticConsumer(AnalyzerOpts, C, OutputDir, PP, CTU); + createTextMinimalPathDiagnosticConsumer(DiagOpts, C, OutputDir, PP, CTU); // TODO: Emit an error here. if (OutputDir.empty()) return; - C.push_back(new HTMLDiagnostics(AnalyzerOpts, OutputDir, PP, true)); + C.push_back(new HTMLDiagnostics(std::move(DiagOpts), OutputDir, PP, true)); } void ento::createHTMLSingleFileDiagnosticConsumer( - AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C, + PathDiagnosticConsumerOptions DiagOpts, PathDiagnosticConsumers &C, const std::string &OutputDir, const Preprocessor &PP, const cross_tu::CrossTranslationUnitContext &CTU) { + createTextMinimalPathDiagnosticConsumer(DiagOpts, C, OutputDir, PP, CTU); // TODO: Emit an error here. if (OutputDir.empty()) return; - C.push_back(new HTMLDiagnostics(AnalyzerOpts, OutputDir, PP, false)); - createTextMinimalPathDiagnosticConsumer(AnalyzerOpts, C, OutputDir, PP, CTU); + C.push_back(new HTMLDiagnostics(std::move(DiagOpts), OutputDir, PP, false)); } void ento::createPlistHTMLDiagnosticConsumer( - AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C, + PathDiagnosticConsumerOptions DiagOpts, PathDiagnosticConsumers &C, const std::string &prefix, const Preprocessor &PP, const cross_tu::CrossTranslationUnitContext &CTU) { createHTMLDiagnosticConsumer( - AnalyzerOpts, C, std::string(llvm::sys::path::parent_path(prefix)), PP, + DiagOpts, C, std::string(llvm::sys::path::parent_path(prefix)), PP, CTU); - createPlistMultiFileDiagnosticConsumer(AnalyzerOpts, C, prefix, PP, CTU); - createTextMinimalPathDiagnosticConsumer(AnalyzerOpts, C, prefix, PP, CTU); + createPlistMultiFileDiagnosticConsumer(DiagOpts, C, prefix, PP, CTU); + createTextMinimalPathDiagnosticConsumer(std::move(DiagOpts), C, prefix, PP, + CTU); } //===----------------------------------------------------------------------===// @@ -245,7 +246,7 @@ void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D, int FD; SmallString<128> Model, ResultPath; - if (!AnalyzerOpts.ShouldWriteStableReportFilename) { + if (!DiagOpts.ShouldWriteStableReportFilename) { llvm::sys::path::append(Model, Directory, "report-%%%%%%.html"); if (std::error_code EC = llvm::sys::fs::make_absolute(Model)) { @@ -535,7 +536,7 @@ void HTMLDiagnostics::FinalizeHTML(const PathDiagnostic& D, Rewriter &R, <input type="checkbox" class="spoilerhider" id="showinvocation" /> <label for="showinvocation" >Show analyzer invocation</label> <div class="spoiler">clang -cc1 )<<<"; - os << html::EscapeText(AnalyzerOpts.FullCompilerInvocation); + os << html::EscapeText(DiagOpts.ToolInvocation); os << R"<<<( </div> <div id='tooltiphint' hidden="true"> @@ -582,8 +583,8 @@ void HTMLDiagnostics::FinalizeHTML(const PathDiagnostic& D, Rewriter &R, os << "\n<!-- FUNCTIONNAME " << declName << " -->\n"; os << "\n<!-- ISSUEHASHCONTENTOFLINEINCONTEXT " - << GetIssueHash(SMgr, L, D.getCheckerName(), D.getBugType(), - DeclWithIssue, PP.getLangOpts()) + << getIssueHash(L, D.getCheckerName(), D.getBugType(), DeclWithIssue, + PP.getLangOpts()) << " -->\n"; os << "\n<!-- BUGLINE " @@ -786,8 +787,8 @@ void HTMLDiagnostics::HandlePiece(Rewriter &R, FileID BugFileID, if (LPosInfo.first != BugFileID) return; - const llvm::MemoryBuffer *Buf = SM.getBuffer(LPosInfo.first); - const char* FileStart = Buf->getBufferStart(); + llvm::MemoryBufferRef Buf = SM.getBufferOrFake(LPosInfo.first); + const char *FileStart = Buf.getBufferStart(); // Compute the column number. Rewind from the current position to the start // of the line. @@ -797,7 +798,7 @@ void HTMLDiagnostics::HandlePiece(Rewriter &R, FileID BugFileID, // Compute LineEnd. const char *LineEnd = TokInstantiationPtr; - const char* FileEnd = Buf->getBufferEnd(); + const char *FileEnd = Buf.getBufferEnd(); while (*LineEnd != '\n' && LineEnd != FileEnd) ++LineEnd; diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/IssueHash.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/IssueHash.cpp deleted file mode 100644 index e7497f3fbdaa..000000000000 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/IssueHash.cpp +++ /dev/null @@ -1,204 +0,0 @@ -//===---------- IssueHash.cpp - Generate identification hashes --*- 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/StaticAnalyzer/Core/IssueHash.h" -#include "clang/AST/ASTContext.h" -#include "clang/AST/Decl.h" -#include "clang/AST/DeclCXX.h" -#include "clang/Basic/SourceManager.h" -#include "clang/Basic/Specifiers.h" -#include "clang/Lex/Lexer.h" -#include "llvm/ADT/StringExtras.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/ADT/Twine.h" -#include "llvm/Support/LineIterator.h" -#include "llvm/Support/MD5.h" -#include "llvm/Support/Path.h" - -#include <functional> -#include <sstream> -#include <string> - -using namespace clang; - -// Get a string representation of the parts of the signature that can be -// overloaded on. -static std::string GetSignature(const FunctionDecl *Target) { - if (!Target) - return ""; - std::string Signature; - - // When a flow sensitive bug happens in templated code we should not generate - // distinct hash value for every instantiation. Use the signature from the - // primary template. - if (const FunctionDecl *InstantiatedFrom = - Target->getTemplateInstantiationPattern()) - Target = InstantiatedFrom; - - if (!isa<CXXConstructorDecl>(Target) && !isa<CXXDestructorDecl>(Target) && - !isa<CXXConversionDecl>(Target)) - Signature.append(Target->getReturnType().getAsString()).append(" "); - Signature.append(Target->getQualifiedNameAsString()).append("("); - - for (int i = 0, paramsCount = Target->getNumParams(); i < paramsCount; ++i) { - if (i) - Signature.append(", "); - Signature.append(Target->getParamDecl(i)->getType().getAsString()); - } - - if (Target->isVariadic()) - Signature.append(", ..."); - Signature.append(")"); - - const auto *TargetT = - llvm::dyn_cast_or_null<FunctionType>(Target->getType().getTypePtr()); - - if (!TargetT || !isa<CXXMethodDecl>(Target)) - return Signature; - - if (TargetT->isConst()) - Signature.append(" const"); - if (TargetT->isVolatile()) - Signature.append(" volatile"); - if (TargetT->isRestrict()) - Signature.append(" restrict"); - - if (const auto *TargetPT = - dyn_cast_or_null<FunctionProtoType>(Target->getType().getTypePtr())) { - switch (TargetPT->getRefQualifier()) { - case RQ_LValue: - Signature.append(" &"); - break; - case RQ_RValue: - Signature.append(" &&"); - break; - default: - break; - } - } - - return Signature; -} - -static std::string GetEnclosingDeclContextSignature(const Decl *D) { - if (!D) - return ""; - - if (const auto *ND = dyn_cast<NamedDecl>(D)) { - std::string DeclName; - - switch (ND->getKind()) { - case Decl::Namespace: - case Decl::Record: - case Decl::CXXRecord: - case Decl::Enum: - DeclName = ND->getQualifiedNameAsString(); - break; - case Decl::CXXConstructor: - case Decl::CXXDestructor: - case Decl::CXXConversion: - case Decl::CXXMethod: - case Decl::Function: - DeclName = GetSignature(dyn_cast_or_null<FunctionDecl>(ND)); - break; - case Decl::ObjCMethod: - // ObjC Methods can not be overloaded, qualified name uniquely identifies - // the method. - DeclName = ND->getQualifiedNameAsString(); - break; - default: - break; - } - - return DeclName; - } - - return ""; -} - -static StringRef GetNthLineOfFile(const llvm::MemoryBuffer *Buffer, int Line) { - if (!Buffer) - return ""; - - llvm::line_iterator LI(*Buffer, false); - for (; !LI.is_at_eof() && LI.line_number() != Line; ++LI) - ; - - return *LI; -} - -static std::string NormalizeLine(const SourceManager &SM, FullSourceLoc &L, - const LangOptions &LangOpts) { - static StringRef Whitespaces = " \t\n"; - - StringRef Str = GetNthLineOfFile(SM.getBuffer(L.getFileID(), L), - L.getExpansionLineNumber()); - StringRef::size_type col = Str.find_first_not_of(Whitespaces); - if (col == StringRef::npos) - col = 1; // The line only contains whitespace. - else - col++; - SourceLocation StartOfLine = - SM.translateLineCol(SM.getFileID(L), L.getExpansionLineNumber(), col); - const llvm::MemoryBuffer *Buffer = - SM.getBuffer(SM.getFileID(StartOfLine), StartOfLine); - if (!Buffer) - return {}; - - const char *BufferPos = SM.getCharacterData(StartOfLine); - - Token Token; - Lexer Lexer(SM.getLocForStartOfFile(SM.getFileID(StartOfLine)), LangOpts, - Buffer->getBufferStart(), BufferPos, Buffer->getBufferEnd()); - - size_t NextStart = 0; - std::ostringstream LineBuff; - while (!Lexer.LexFromRawLexer(Token) && NextStart < 2) { - if (Token.isAtStartOfLine() && NextStart++ > 0) - continue; - LineBuff << std::string(SM.getCharacterData(Token.getLocation()), - Token.getLength()); - } - - return LineBuff.str(); -} - -static llvm::SmallString<32> GetHashOfContent(StringRef Content) { - llvm::MD5 Hash; - llvm::MD5::MD5Result MD5Res; - SmallString<32> Res; - - Hash.update(Content); - Hash.final(MD5Res); - llvm::MD5::stringifyResult(MD5Res, Res); - - return Res; -} - -std::string clang::GetIssueString(const SourceManager &SM, - FullSourceLoc &IssueLoc, - StringRef CheckerName, StringRef BugType, - const Decl *D, - const LangOptions &LangOpts) { - static StringRef Delimiter = "$"; - - return (llvm::Twine(CheckerName) + Delimiter + - GetEnclosingDeclContextSignature(D) + Delimiter + - Twine(IssueLoc.getExpansionColumnNumber()) + Delimiter + - NormalizeLine(SM, IssueLoc, LangOpts) + Delimiter + BugType) - .str(); -} - -SmallString<32> clang::GetIssueHash(const SourceManager &SM, - FullSourceLoc &IssueLoc, - StringRef CheckerName, StringRef BugType, - const Decl *D, - const LangOptions &LangOpts) { - - return GetHashOfContent( - GetIssueString(SM, IssueLoc, CheckerName, BugType, D, LangOpts)); -} diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp index ed62778623a8..35e320c7755f 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +#include "clang/Analysis/IssueHash.h" #include "clang/Analysis/PathDiagnostic.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/PlistSupport.h" @@ -20,13 +21,12 @@ #include "clang/Lex/Preprocessor.h" #include "clang/Lex/TokenConcatenation.h" #include "clang/Rewrite/Core/HTMLRewrite.h" -#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" -#include "clang/StaticAnalyzer/Core/IssueHash.h" #include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/Statistic.h" #include "llvm/Support/Casting.h" +#include <memory> using namespace clang; using namespace ento; @@ -39,13 +39,17 @@ using namespace markup; namespace { class PlistDiagnostics : public PathDiagnosticConsumer { + PathDiagnosticConsumerOptions DiagOpts; const std::string OutputFile; const Preprocessor &PP; const cross_tu::CrossTranslationUnitContext &CTU; - AnalyzerOptions &AnOpts; const bool SupportsCrossFileDiagnostics; + + void printBugPath(llvm::raw_ostream &o, const FIDMap &FM, + const PathPieces &Path); + public: - PlistDiagnostics(AnalyzerOptions &AnalyzerOpts, + PlistDiagnostics(PathDiagnosticConsumerOptions DiagOpts, const std::string &OutputFile, const Preprocessor &PP, const cross_tu::CrossTranslationUnitContext &CTU, bool supportsMultipleFiles); @@ -74,23 +78,19 @@ namespace { /// A helper class for emitting a single report. class PlistPrinter { const FIDMap& FM; - AnalyzerOptions &AnOpts; const Preprocessor &PP; const cross_tu::CrossTranslationUnitContext &CTU; llvm::SmallVector<const PathDiagnosticMacroPiece *, 0> MacroPieces; public: - PlistPrinter(const FIDMap& FM, AnalyzerOptions &AnOpts, + PlistPrinter(const FIDMap& FM, const Preprocessor &PP, const cross_tu::CrossTranslationUnitContext &CTU) - : FM(FM), AnOpts(AnOpts), PP(PP), CTU(CTU) { + : FM(FM), PP(PP), CTU(CTU) { } void ReportDiag(raw_ostream &o, const PathDiagnosticPiece& P) { ReportPiece(o, P, /*indent*/ 4, /*depth*/ 0, /*includeControlFlow*/ true); - - // Don't emit a warning about an unused private field. - (void)AnOpts; } /// Print the expansions of the collected macro pieces. @@ -165,11 +165,6 @@ struct ExpansionInfo { } // end of anonymous namespace -static void printBugPath(llvm::raw_ostream &o, const FIDMap& FM, - AnalyzerOptions &AnOpts, const Preprocessor &PP, - const cross_tu::CrossTranslationUnitContext &CTU, - const PathPieces &Path); - /// Print coverage information to output stream {@code o}. /// May modify the used list of files {@code Fids} by inserting new ones. static void printCoverage(const PathDiagnostic *D, @@ -520,11 +515,53 @@ static void printCoverage(const PathDiagnostic *D, assert(IndentLevel == InputIndentLevel); } -static void printBugPath(llvm::raw_ostream &o, const FIDMap& FM, - AnalyzerOptions &AnOpts, const Preprocessor &PP, - const cross_tu::CrossTranslationUnitContext &CTU, - const PathPieces &Path) { - PlistPrinter Printer(FM, AnOpts, PP, CTU); +//===----------------------------------------------------------------------===// +// Methods of PlistDiagnostics. +//===----------------------------------------------------------------------===// + +PlistDiagnostics::PlistDiagnostics( + PathDiagnosticConsumerOptions DiagOpts, const std::string &output, + const Preprocessor &PP, const cross_tu::CrossTranslationUnitContext &CTU, + bool supportsMultipleFiles) + : DiagOpts(std::move(DiagOpts)), OutputFile(output), PP(PP), CTU(CTU), + SupportsCrossFileDiagnostics(supportsMultipleFiles) { + // FIXME: Will be used by a later planned change. + (void)this->CTU; +} + +void ento::createPlistDiagnosticConsumer( + PathDiagnosticConsumerOptions DiagOpts, PathDiagnosticConsumers &C, + const std::string &OutputFile, const Preprocessor &PP, + const cross_tu::CrossTranslationUnitContext &CTU) { + + // TODO: Emit an error here. + if (OutputFile.empty()) + return; + + C.push_back(new PlistDiagnostics(DiagOpts, OutputFile, PP, CTU, + /*supportsMultipleFiles=*/false)); + createTextMinimalPathDiagnosticConsumer(std::move(DiagOpts), C, OutputFile, + PP, CTU); +} + +void ento::createPlistMultiFileDiagnosticConsumer( + PathDiagnosticConsumerOptions DiagOpts, PathDiagnosticConsumers &C, + const std::string &OutputFile, const Preprocessor &PP, + const cross_tu::CrossTranslationUnitContext &CTU) { + + // TODO: Emit an error here. + if (OutputFile.empty()) + return; + + C.push_back(new PlistDiagnostics(DiagOpts, OutputFile, PP, CTU, + /*supportsMultipleFiles=*/true)); + createTextMinimalPathDiagnosticConsumer(std::move(DiagOpts), C, OutputFile, + PP, CTU); +} + +void PlistDiagnostics::printBugPath(llvm::raw_ostream &o, const FIDMap &FM, + const PathPieces &Path) { + PlistPrinter Printer(FM, PP, CTU); assert(std::is_partitioned(Path.begin(), Path.end(), [](const PathDiagnosticPieceRef &E) { return E->getKind() == PathDiagnosticPiece::Note; @@ -557,7 +594,7 @@ static void printBugPath(llvm::raw_ostream &o, const FIDMap& FM, o << " </array>\n"; - if (!AnOpts.ShouldDisplayMacroExpansions) + if (!DiagOpts.ShouldDisplayMacroExpansions) return; o << " <key>macro_expansions</key>\n" @@ -566,48 +603,6 @@ static void printBugPath(llvm::raw_ostream &o, const FIDMap& FM, o << " </array>\n"; } -//===----------------------------------------------------------------------===// -// Methods of PlistDiagnostics. -//===----------------------------------------------------------------------===// - -PlistDiagnostics::PlistDiagnostics( - AnalyzerOptions &AnalyzerOpts, const std::string &output, - const Preprocessor &PP, const cross_tu::CrossTranslationUnitContext &CTU, - bool supportsMultipleFiles) - : OutputFile(output), PP(PP), CTU(CTU), AnOpts(AnalyzerOpts), - SupportsCrossFileDiagnostics(supportsMultipleFiles) { - // FIXME: Will be used by a later planned change. - (void)this->CTU; -} - -void ento::createPlistDiagnosticConsumer( - AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C, - const std::string &OutputFile, const Preprocessor &PP, - const cross_tu::CrossTranslationUnitContext &CTU) { - - // TODO: Emit an error here. - if (OutputFile.empty()) - return; - - C.push_back(new PlistDiagnostics(AnalyzerOpts, OutputFile, PP, CTU, - /*supportsMultipleFiles*/ false)); - createTextMinimalPathDiagnosticConsumer(AnalyzerOpts, C, OutputFile, PP, CTU); -} - -void ento::createPlistMultiFileDiagnosticConsumer( - AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C, - const std::string &OutputFile, const Preprocessor &PP, - const cross_tu::CrossTranslationUnitContext &CTU) { - - // TODO: Emit an error here. - if (OutputFile.empty()) - return; - - C.push_back(new PlistDiagnostics(AnalyzerOpts, OutputFile, PP, CTU, - /*supportsMultipleFiles*/ true)); - createTextMinimalPathDiagnosticConsumer(AnalyzerOpts, C, OutputFile, PP, CTU); -} - void PlistDiagnostics::FlushDiagnosticsImpl( std::vector<const PathDiagnostic *> &Diags, FilesMade *filesMade) { @@ -682,7 +677,7 @@ void PlistDiagnostics::FlushDiagnosticsImpl( o << " <dict>\n"; const PathDiagnostic *D = *DI; - printBugPath(o, FM, AnOpts, PP, CTU, D->path); + printBugPath(o, FM, D->path); // Output the bug type and bug category. o << " <key>description</key>"; @@ -702,7 +697,7 @@ void PlistDiagnostics::FlushDiagnosticsImpl( : D->getLocation().asLocation()), SM); const Decl *DeclWithIssue = D->getDeclWithIssue(); - EmitString(o, GetIssueHash(SM, L, D->getCheckerName(), D->getBugType(), + EmitString(o, getIssueHash(L, D->getCheckerName(), D->getBugType(), DeclWithIssue, LangOpts)) << '\n'; @@ -806,7 +801,7 @@ void PlistDiagnostics::FlushDiagnosticsImpl( EmitString(o << " ", SM.getFileEntryForID(FID)->getName()) << '\n'; o << " </array>\n"; - if (llvm::AreStatisticsEnabled() && AnOpts.ShouldSerializeStats) { + if (llvm::AreStatisticsEnabled() && DiagOpts.ShouldSerializeStats) { o << " <key>statistics</key>\n"; std::string stats; llvm::raw_string_ostream os(stats); @@ -825,22 +820,36 @@ void PlistDiagnostics::FlushDiagnosticsImpl( namespace { -using ExpArgTokens = llvm::SmallVector<Token, 2>; +using ArgTokensTy = llvm::SmallVector<Token, 2>; + +} // end of anonymous namespace + +LLVM_DUMP_METHOD static void dumpArgTokensToStream(llvm::raw_ostream &Out, + const Preprocessor &PP, + const ArgTokensTy &Toks); -/// Maps unexpanded macro arguments to expanded arguments. A macro argument may +namespace { +/// Maps unexpanded macro parameters to expanded arguments. A macro argument may /// need to expanded further when it is nested inside another macro. -class MacroArgMap : public std::map<const IdentifierInfo *, ExpArgTokens> { +class MacroParamMap : public std::map<const IdentifierInfo *, ArgTokensTy> { public: - void expandFromPrevMacro(const MacroArgMap &Super); + void expandFromPrevMacro(const MacroParamMap &Super); + + LLVM_DUMP_METHOD void dump(const Preprocessor &PP) const { + dumpToStream(llvm::errs(), PP); + } + + LLVM_DUMP_METHOD void dumpToStream(llvm::raw_ostream &Out, + const Preprocessor &PP) const; }; -struct MacroNameAndArgs { +struct MacroExpansionInfo { std::string Name; const MacroInfo *MI = nullptr; - MacroArgMap Args; + MacroParamMap ParamMap; - MacroNameAndArgs(std::string N, const MacroInfo *MI, MacroArgMap M) - : Name(std::move(N)), MI(MI), Args(std::move(M)) {} + MacroExpansionInfo(std::string N, const MacroInfo *MI, MacroParamMap M) + : Name(std::move(N)), MI(MI), ParamMap(std::move(M)) {} }; class TokenPrinter { @@ -860,6 +869,46 @@ public: void printToken(const Token &Tok); }; +/// Wrapper around a Lexer object that can lex tokens one-by-one. Its possible +/// to "inject" a range of tokens into the stream, in which case the next token +/// is retrieved from the next element of the range, until the end of the range +/// is reached. +class TokenStream { +public: + TokenStream(SourceLocation ExpanLoc, const SourceManager &SM, + const LangOptions &LangOpts) + : ExpanLoc(ExpanLoc) { + FileID File; + unsigned Offset; + std::tie(File, Offset) = SM.getDecomposedLoc(ExpanLoc); + llvm::MemoryBufferRef MB = SM.getBufferOrFake(File); + const char *MacroNameTokenPos = MB.getBufferStart() + Offset; + + RawLexer = std::make_unique<Lexer>(SM.getLocForStartOfFile(File), LangOpts, + MB.getBufferStart(), MacroNameTokenPos, + MB.getBufferEnd()); + } + + void next(Token &Result) { + if (CurrTokenIt == TokenRange.end()) { + RawLexer->LexFromRawLexer(Result); + return; + } + Result = *CurrTokenIt; + CurrTokenIt++; + } + + void injectRange(const ArgTokensTy &Range) { + TokenRange = Range; + CurrTokenIt = TokenRange.begin(); + } + + std::unique_ptr<Lexer> RawLexer; + ArgTokensTy TokenRange; + ArgTokensTy::iterator CurrTokenIt = TokenRange.begin(); + SourceLocation ExpanLoc; +}; + } // end of anonymous namespace /// The implementation method of getMacroExpansion: It prints the expansion of @@ -878,7 +927,7 @@ public: /// /// As we expand the last line, we'll immediately replace PRINT(str) with /// print(x). The information that both 'str' and 'x' refers to the same string -/// is an information we have to forward, hence the argument \p PrevArgs. +/// is an information we have to forward, hence the argument \p PrevParamMap. /// /// To avoid infinite recursion we maintain the already processed tokens in /// a set. This is carried as a parameter through the recursive calls. The set @@ -888,13 +937,11 @@ public: /// #define f(y) x /// #define x f(x) static std::string getMacroNameAndPrintExpansion( - TokenPrinter &Printer, - SourceLocation MacroLoc, - const Preprocessor &PP, - const MacroArgMap &PrevArgs, + TokenPrinter &Printer, SourceLocation MacroLoc, const Preprocessor &PP, + const MacroParamMap &PrevParamMap, llvm::SmallPtrSet<IdentifierInfo *, 8> &AlreadyProcessedTokens); -/// Retrieves the name of the macro and what it's arguments expand into +/// Retrieves the name of the macro and what it's parameters expand into /// at \p ExpanLoc. /// /// For example, for the following macro expansion: @@ -916,8 +963,9 @@ static std::string getMacroNameAndPrintExpansion( /// When \p ExpanLoc references "SET_TO_NULL(a)" within the definition of /// "NOT_SUSPICOUS", the macro name "SET_TO_NULL" and the MacroArgMap map /// { (x, a) } will be returned. -static MacroNameAndArgs getMacroNameAndArgs(SourceLocation ExpanLoc, - const Preprocessor &PP); +static MacroExpansionInfo +getMacroExpansionInfo(const MacroParamMap &PrevParamMap, + SourceLocation ExpanLoc, const Preprocessor &PP); /// Retrieves the ')' token that matches '(' \p It points to. static MacroInfo::tokens_iterator getMatchingRParen( @@ -951,21 +999,20 @@ getExpandedMacro(SourceLocation MacroLoc, const Preprocessor &PP, llvm::SmallPtrSet<IdentifierInfo*, 8> AlreadyProcessedTokens; std::string MacroName = getMacroNameAndPrintExpansion( - Printer, MacroLoc, *PPToUse, MacroArgMap{}, AlreadyProcessedTokens); + Printer, MacroLoc, *PPToUse, MacroParamMap{}, AlreadyProcessedTokens); return {MacroName, std::string(OS.str())}; } static std::string getMacroNameAndPrintExpansion( - TokenPrinter &Printer, - SourceLocation MacroLoc, - const Preprocessor &PP, - const MacroArgMap &PrevArgs, + TokenPrinter &Printer, SourceLocation MacroLoc, const Preprocessor &PP, + const MacroParamMap &PrevParamMap, llvm::SmallPtrSet<IdentifierInfo *, 8> &AlreadyProcessedTokens) { const SourceManager &SM = PP.getSourceManager(); - MacroNameAndArgs Info = getMacroNameAndArgs(SM.getExpansionLoc(MacroLoc), PP); - IdentifierInfo* IDInfo = PP.getIdentifierInfo(Info.Name); + MacroExpansionInfo MExpInfo = + getMacroExpansionInfo(PrevParamMap, SM.getExpansionLoc(MacroLoc), PP); + IdentifierInfo *MacroNameII = PP.getIdentifierInfo(MExpInfo.Name); // TODO: If the macro definition contains another symbol then this function is // called recursively. In case this symbol is the one being defined, it will @@ -973,18 +1020,18 @@ static std::string getMacroNameAndPrintExpansion( // in this case we don't get the full expansion text in the Plist file. See // the test file where "value" is expanded to "garbage_" instead of // "garbage_value". - if (!AlreadyProcessedTokens.insert(IDInfo).second) - return Info.Name; + if (!AlreadyProcessedTokens.insert(MacroNameII).second) + return MExpInfo.Name; - if (!Info.MI) - return Info.Name; + if (!MExpInfo.MI) + return MExpInfo.Name; // Manually expand its arguments from the previous macro. - Info.Args.expandFromPrevMacro(PrevArgs); + MExpInfo.ParamMap.expandFromPrevMacro(PrevParamMap); // Iterate over the macro's tokens and stringify them. - for (auto It = Info.MI->tokens_begin(), E = Info.MI->tokens_end(); It != E; - ++It) { + for (auto It = MExpInfo.MI->tokens_begin(), E = MExpInfo.MI->tokens_end(); + It != E; ++It) { Token T = *It; // If this token is not an identifier, we only need to print it. @@ -1000,8 +1047,8 @@ static std::string getMacroNameAndPrintExpansion( // If this token is a macro that should be expanded inside the current // macro. if (getMacroInfoForLocation(PP, SM, II, T.getLocation())) { - getMacroNameAndPrintExpansion(Printer, T.getLocation(), PP, Info.Args, - AlreadyProcessedTokens); + getMacroNameAndPrintExpansion(Printer, T.getLocation(), PP, + MExpInfo.ParamMap, AlreadyProcessedTokens); // If this is a function-like macro, skip its arguments, as // getExpandedMacro() already printed them. If this is the case, let's @@ -1013,10 +1060,10 @@ static std::string getMacroNameAndPrintExpansion( } // If this token is the current macro's argument, we should expand it. - auto ArgMapIt = Info.Args.find(II); - if (ArgMapIt != Info.Args.end()) { - for (MacroInfo::tokens_iterator ArgIt = ArgMapIt->second.begin(), - ArgEnd = ArgMapIt->second.end(); + auto ParamToArgIt = MExpInfo.ParamMap.find(II); + if (ParamToArgIt != MExpInfo.ParamMap.end()) { + for (MacroInfo::tokens_iterator ArgIt = ParamToArgIt->second.begin(), + ArgEnd = ParamToArgIt->second.end(); ArgIt != ArgEnd; ++ArgIt) { // These tokens may still be macros, if that is the case, handle it the @@ -1034,7 +1081,8 @@ static std::string getMacroNameAndPrintExpansion( } getMacroNameAndPrintExpansion(Printer, ArgIt->getLocation(), PP, - Info.Args, AlreadyProcessedTokens); + MExpInfo.ParamMap, + AlreadyProcessedTokens); // Peek the next token if it is a tok::l_paren. This way we can decide // if this is the application or just a reference to a function maxro // symbol: @@ -1055,34 +1103,30 @@ static std::string getMacroNameAndPrintExpansion( Printer.printToken(T); } - AlreadyProcessedTokens.erase(IDInfo); + AlreadyProcessedTokens.erase(MacroNameII); - return Info.Name; + return MExpInfo.Name; } -static MacroNameAndArgs getMacroNameAndArgs(SourceLocation ExpanLoc, - const Preprocessor &PP) { +static MacroExpansionInfo +getMacroExpansionInfo(const MacroParamMap &PrevParamMap, + SourceLocation ExpanLoc, const Preprocessor &PP) { const SourceManager &SM = PP.getSourceManager(); const LangOptions &LangOpts = PP.getLangOpts(); // First, we create a Lexer to lex *at the expansion location* the tokens // referring to the macro's name and its arguments. - std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(ExpanLoc); - const llvm::MemoryBuffer *MB = SM.getBuffer(LocInfo.first); - const char *MacroNameTokenPos = MB->getBufferStart() + LocInfo.second; - - Lexer RawLexer(SM.getLocForStartOfFile(LocInfo.first), LangOpts, - MB->getBufferStart(), MacroNameTokenPos, MB->getBufferEnd()); + TokenStream TStream(ExpanLoc, SM, LangOpts); // Acquire the macro's name. Token TheTok; - RawLexer.LexFromRawLexer(TheTok); + TStream.next(TheTok); std::string MacroName = PP.getSpelling(TheTok); const auto *II = PP.getIdentifierInfo(MacroName); - assert(II && "Failed to acquire the IndetifierInfo for the macro!"); + assert(II && "Failed to acquire the IdentifierInfo for the macro!"); const MacroInfo *MI = getMacroInfoForLocation(PP, SM, II, ExpanLoc); // assert(MI && "The macro must've been defined at it's expansion location!"); @@ -1094,18 +1138,18 @@ static MacroNameAndArgs getMacroNameAndArgs(SourceLocation ExpanLoc, if (!MI) return { MacroName, MI, {} }; - // Acquire the macro's arguments. + // Acquire the macro's arguments at the expansion point. // // The rough idea here is to lex from the first left parentheses to the last - // right parentheses, and map the macro's unexpanded arguments to what they - // will be expanded to. An expanded macro argument may contain several tokens - // (like '3 + 4'), so we'll lex until we find a tok::comma or tok::r_paren, at - // which point we start lexing the next argument or finish. - ArrayRef<const IdentifierInfo *> MacroArgs = MI->params(); - if (MacroArgs.empty()) + // right parentheses, and map the macro's parameter to what they will be + // expanded to. A macro argument may contain several token (like '3 + 4'), so + // we'll lex until we find a tok::comma or tok::r_paren, at which point we + // start lexing the next argument or finish. + ArrayRef<const IdentifierInfo *> MacroParams = MI->params(); + if (MacroParams.empty()) return { MacroName, MI, {} }; - RawLexer.LexFromRawLexer(TheTok); + TStream.next(TheTok); // When this is a token which expands to another macro function then its // parentheses are not at its expansion locaiton. For example: // @@ -1117,9 +1161,9 @@ static MacroNameAndArgs getMacroNameAndArgs(SourceLocation ExpanLoc, if (TheTok.isNot(tok::l_paren)) return { MacroName, MI, {} }; - MacroArgMap Args; + MacroParamMap ParamMap; - // When the macro's argument is a function call, like + // When the argument is a function call, like // CALL_FN(someFunctionName(param1, param2)) // we will find tok::l_paren, tok::r_paren, and tok::comma that do not divide // actual macro arguments, or do not represent the macro argument's closing @@ -1130,12 +1174,19 @@ static MacroNameAndArgs getMacroNameAndArgs(SourceLocation ExpanLoc, // * > 1, then tok::comma is a part of the current arg. int ParenthesesDepth = 1; - // If we encounter __VA_ARGS__, we will lex until the closing tok::r_paren, - // even if we lex a tok::comma and ParanthesesDepth == 1. - const IdentifierInfo *__VA_ARGS__II = PP.getIdentifierInfo("__VA_ARGS__"); + // If we encounter the variadic arg, we will lex until the closing + // tok::r_paren, even if we lex a tok::comma and ParanthesesDepth == 1. + const IdentifierInfo *VariadicParamII = PP.getIdentifierInfo("__VA_ARGS__"); + if (MI->isGNUVarargs()) { + // If macro uses GNU-style variadic args, the param name is user-supplied, + // an not "__VA_ARGS__". E.g.: + // #define FOO(a, b, myvargs...) + // In this case, just use the last parameter: + VariadicParamII = *(MacroParams.rbegin()); + } - for (const IdentifierInfo *UnexpArgII : MacroArgs) { - MacroArgMap::mapped_type ExpandedArgTokens; + for (const IdentifierInfo *CurrParamII : MacroParams) { + MacroParamMap::mapped_type ArgTokens; // One could also simply not supply a single argument to __VA_ARGS__ -- this // results in a preprocessor warning, but is not an error: @@ -1149,10 +1200,10 @@ static MacroNameAndArgs getMacroNameAndArgs(SourceLocation ExpanLoc, if (ParenthesesDepth != 0) { // Lex the first token of the next macro parameter. - RawLexer.LexFromRawLexer(TheTok); + TStream.next(TheTok); - while (!(ParenthesesDepth == 1 && - (UnexpArgII == __VA_ARGS__II ? false : TheTok.is(tok::comma)))) { + while (CurrParamII == VariadicParamII || ParenthesesDepth != 1 || + !TheTok.is(tok::comma)) { assert(TheTok.isNot(tok::eof) && "EOF encountered while looking for expanded macro args!"); @@ -1165,24 +1216,51 @@ static MacroNameAndArgs getMacroNameAndArgs(SourceLocation ExpanLoc, if (ParenthesesDepth == 0) break; - if (TheTok.is(tok::raw_identifier)) + if (TheTok.is(tok::raw_identifier)) { PP.LookUpIdentifierInfo(TheTok); + // This token is a variadic parameter: + // + // #define PARAMS_RESOLVE_TO_VA_ARGS(i, fmt) foo(i, fmt); \ + // i = 0; + // #define DISPATCH(...) \ + // PARAMS_RESOLVE_TO_VA_ARGS(__VA_ARGS__); + // // ^~~~~~~~~~~ Variadic parameter here + // + // void multipleParamsResolveToVA_ARGS(void) { + // int x = 1; + // DISPATCH(x, "LF1M healer"); // Multiple arguments are mapped to + // // a single __VA_ARGS__ parameter. + // (void)(10 / x); + // } + // + // We will stumble across this while trying to expand + // PARAMS_RESOLVE_TO_VA_ARGS. By this point, we already noted during + // the processing of DISPATCH what __VA_ARGS__ maps to, so we'll + // retrieve the next series of tokens from that. + if (TheTok.getIdentifierInfo() == VariadicParamII) { + TStream.injectRange(PrevParamMap.at(VariadicParamII)); + TStream.next(TheTok); + continue; + } + } - ExpandedArgTokens.push_back(TheTok); - RawLexer.LexFromRawLexer(TheTok); + ArgTokens.push_back(TheTok); + TStream.next(TheTok); } } else { - assert(UnexpArgII == __VA_ARGS__II); + assert(CurrParamII == VariadicParamII && + "No more macro arguments are found, but the current parameter " + "isn't the variadic arg!"); } - Args.emplace(UnexpArgII, std::move(ExpandedArgTokens)); + ParamMap.emplace(CurrParamII, std::move(ArgTokens)); } assert(TheTok.is(tok::r_paren) && "Expanded macro argument acquisition failed! After the end of the loop" " this token should be ')'!"); - return { MacroName, MI, Args }; + return {MacroName, MI, ParamMap}; } static MacroInfo::tokens_iterator getMatchingRParen( @@ -1222,14 +1300,14 @@ static const MacroInfo *getMacroInfoForLocation(const Preprocessor &PP, return MD->findDirectiveAtLoc(Loc, SM).getMacroInfo(); } -void MacroArgMap::expandFromPrevMacro(const MacroArgMap &Super) { +void MacroParamMap::expandFromPrevMacro(const MacroParamMap &Super) { for (value_type &Pair : *this) { - ExpArgTokens &CurrExpArgTokens = Pair.second; + ArgTokensTy &CurrArgTokens = Pair.second; // For each token in the expanded macro argument. - auto It = CurrExpArgTokens.begin(); - while (It != CurrExpArgTokens.end()) { + auto It = CurrArgTokens.begin(); + while (It != CurrArgTokens.end()) { if (It->isNot(tok::identifier)) { ++It; continue; @@ -1244,17 +1322,43 @@ void MacroArgMap::expandFromPrevMacro(const MacroArgMap &Super) { continue; } - const ExpArgTokens &SuperExpArgTokens = Super.at(II); + const ArgTokensTy &SuperArgTokens = Super.at(II); - It = CurrExpArgTokens.insert( - It, SuperExpArgTokens.begin(), SuperExpArgTokens.end()); - std::advance(It, SuperExpArgTokens.size()); - It = CurrExpArgTokens.erase(It); + It = CurrArgTokens.insert(It, SuperArgTokens.begin(), + SuperArgTokens.end()); + std::advance(It, SuperArgTokens.size()); + It = CurrArgTokens.erase(It); } } } +void MacroParamMap::dumpToStream(llvm::raw_ostream &Out, + const Preprocessor &PP) const { + for (const std::pair<const IdentifierInfo *, ArgTokensTy> Pair : *this) { + Out << Pair.first->getName() << " -> "; + dumpArgTokensToStream(Out, PP, Pair.second); + Out << '\n'; + } +} + +static void dumpArgTokensToStream(llvm::raw_ostream &Out, + const Preprocessor &PP, + const ArgTokensTy &Toks) { + TokenPrinter Printer(Out, PP); + for (Token Tok : Toks) + Printer.printToken(Tok); +} + void TokenPrinter::printToken(const Token &Tok) { + // TODO: Handle GNU extensions where hash and hashhash occurs right before + // __VA_ARGS__. + // cppreference.com: "some compilers offer an extension that allows ## to + // appear after a comma and before __VA_ARGS__, in which case the ## does + // nothing when the variable arguments are present, but removes the comma when + // the variable arguments are not present: this makes it possible to define + // macros such as fprintf (stderr, format, ##__VA_ARGS__)" + // FIXME: Handle named variadic macro parameters (also a GNU extension). + // If this is the first token to be printed, don't print space. if (PrevTok.isNot(tok::unknown)) { // If the tokens were already space separated, or if they must be to avoid diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ProgramState.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ProgramState.cpp index 006a4006b7fc..1ccb0de92fba 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ProgramState.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ProgramState.cpp @@ -582,9 +582,6 @@ bool ScanReachableSymbols::scan(SVal val) { if (SymbolRef Sym = val.getAsSymbol()) return scan(Sym); - if (const SymExpr *Sym = val.getAsSymbolicExpression()) - return scan(Sym); - if (Optional<nonloc::CompoundVal> X = val.getAs<nonloc::CompoundVal>()) return scan(*X); diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp index cb6f61e86ae3..a481bde1651b 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp @@ -89,7 +89,7 @@ public: } TriStateKind getCmpOpState(BinaryOperatorKind CurrentOP, - BinaryOperatorKind QueriedOP) const { + BinaryOperatorKind QueriedOP) const { return CmpOpTable[getIndexFromOp(CurrentOP)][getIndexFromOp(QueriedOP)]; } @@ -364,6 +364,18 @@ RangeSet RangeSet::Negate(BasicValueFactory &BV, Factory &F) const { return newRanges; } +RangeSet RangeSet::Delete(BasicValueFactory &BV, Factory &F, + const llvm::APSInt &Point) const { + llvm::APSInt Upper = Point; + llvm::APSInt Lower = Point; + + ++Upper; + --Lower; + + // Notice that the lower bound is greater than the upper bound. + return Intersect(BV, F, Upper, Lower); +} + void RangeSet::print(raw_ostream &os) const { bool isFirst = true; os << "{ "; @@ -379,7 +391,315 @@ void RangeSet::print(raw_ostream &os) const { os << " }"; } +REGISTER_SET_FACTORY_WITH_PROGRAMSTATE(SymbolSet, SymbolRef) + namespace { +class EquivalenceClass; +} // end anonymous namespace + +REGISTER_MAP_WITH_PROGRAMSTATE(ClassMap, SymbolRef, EquivalenceClass) +REGISTER_MAP_WITH_PROGRAMSTATE(ClassMembers, EquivalenceClass, SymbolSet) +REGISTER_MAP_WITH_PROGRAMSTATE(ConstraintRange, EquivalenceClass, RangeSet) + +REGISTER_SET_FACTORY_WITH_PROGRAMSTATE(ClassSet, EquivalenceClass) +REGISTER_MAP_WITH_PROGRAMSTATE(DisequalityMap, EquivalenceClass, ClassSet) + +namespace { +/// This class encapsulates a set of symbols equal to each other. +/// +/// The main idea of the approach requiring such classes is in narrowing +/// and sharing constraints between symbols within the class. Also we can +/// conclude that there is no practical need in storing constraints for +/// every member of the class separately. +/// +/// Main terminology: +/// +/// * "Equivalence class" is an object of this class, which can be efficiently +/// compared to other classes. It represents the whole class without +/// storing the actual in it. The members of the class however can be +/// retrieved from the state. +/// +/// * "Class members" are the symbols corresponding to the class. This means +/// that A == B for every member symbols A and B from the class. Members of +/// each class are stored in the state. +/// +/// * "Trivial class" is a class that has and ever had only one same symbol. +/// +/// * "Merge operation" merges two classes into one. It is the main operation +/// to produce non-trivial classes. +/// If, at some point, we can assume that two symbols from two distinct +/// classes are equal, we can merge these classes. +class EquivalenceClass : public llvm::FoldingSetNode { +public: + /// Find equivalence class for the given symbol in the given state. + LLVM_NODISCARD static inline EquivalenceClass find(ProgramStateRef State, + SymbolRef Sym); + + /// Merge classes for the given symbols and return a new state. + LLVM_NODISCARD static inline ProgramStateRef + merge(BasicValueFactory &BV, RangeSet::Factory &F, ProgramStateRef State, + SymbolRef First, SymbolRef Second); + // Merge this class with the given class and return a new state. + LLVM_NODISCARD inline ProgramStateRef merge(BasicValueFactory &BV, + RangeSet::Factory &F, + ProgramStateRef State, + EquivalenceClass Other); + + /// Return a set of class members for the given state. + LLVM_NODISCARD inline SymbolSet getClassMembers(ProgramStateRef State); + /// Return true if the current class is trivial in the given state. + LLVM_NODISCARD inline bool isTrivial(ProgramStateRef State); + /// Return true if the current class is trivial and its only member is dead. + LLVM_NODISCARD inline bool isTriviallyDead(ProgramStateRef State, + SymbolReaper &Reaper); + + LLVM_NODISCARD static inline ProgramStateRef + markDisequal(BasicValueFactory &BV, RangeSet::Factory &F, + ProgramStateRef State, SymbolRef First, SymbolRef Second); + LLVM_NODISCARD static inline ProgramStateRef + markDisequal(BasicValueFactory &BV, RangeSet::Factory &F, + ProgramStateRef State, EquivalenceClass First, + EquivalenceClass Second); + LLVM_NODISCARD inline ProgramStateRef + markDisequal(BasicValueFactory &BV, RangeSet::Factory &F, + ProgramStateRef State, EquivalenceClass Other) const; + LLVM_NODISCARD static inline ClassSet + getDisequalClasses(ProgramStateRef State, SymbolRef Sym); + LLVM_NODISCARD inline ClassSet + getDisequalClasses(ProgramStateRef State) const; + LLVM_NODISCARD inline ClassSet + getDisequalClasses(DisequalityMapTy Map, ClassSet::Factory &Factory) const; + + LLVM_NODISCARD static inline Optional<bool> + areEqual(ProgramStateRef State, SymbolRef First, SymbolRef Second); + + /// Check equivalence data for consistency. + LLVM_NODISCARD LLVM_ATTRIBUTE_UNUSED static bool + isClassDataConsistent(ProgramStateRef State); + + LLVM_NODISCARD QualType getType() const { + return getRepresentativeSymbol()->getType(); + } + + EquivalenceClass() = delete; + EquivalenceClass(const EquivalenceClass &) = default; + EquivalenceClass &operator=(const EquivalenceClass &) = delete; + EquivalenceClass(EquivalenceClass &&) = default; + EquivalenceClass &operator=(EquivalenceClass &&) = delete; + + bool operator==(const EquivalenceClass &Other) const { + return ID == Other.ID; + } + bool operator<(const EquivalenceClass &Other) const { return ID < Other.ID; } + bool operator!=(const EquivalenceClass &Other) const { + return !operator==(Other); + } + + static void Profile(llvm::FoldingSetNodeID &ID, uintptr_t CID) { + ID.AddInteger(CID); + } + + void Profile(llvm::FoldingSetNodeID &ID) const { Profile(ID, this->ID); } + +private: + /* implicit */ EquivalenceClass(SymbolRef Sym) + : ID(reinterpret_cast<uintptr_t>(Sym)) {} + + /// This function is intended to be used ONLY within the class. + /// The fact that ID is a pointer to a symbol is an implementation detail + /// and should stay that way. + /// In the current implementation, we use it to retrieve the only member + /// of the trivial class. + SymbolRef getRepresentativeSymbol() const { + return reinterpret_cast<SymbolRef>(ID); + } + static inline SymbolSet::Factory &getMembersFactory(ProgramStateRef State); + + inline ProgramStateRef mergeImpl(BasicValueFactory &BV, RangeSet::Factory &F, + ProgramStateRef State, SymbolSet Members, + EquivalenceClass Other, + SymbolSet OtherMembers); + static inline void + addToDisequalityInfo(DisequalityMapTy &Info, ConstraintRangeTy &Constraints, + BasicValueFactory &BV, RangeSet::Factory &F, + ProgramStateRef State, EquivalenceClass First, + EquivalenceClass Second); + + /// This is a unique identifier of the class. + uintptr_t ID; +}; + +//===----------------------------------------------------------------------===// +// Constraint functions +//===----------------------------------------------------------------------===// + +LLVM_NODISCARD inline const RangeSet *getConstraint(ProgramStateRef State, + EquivalenceClass Class) { + return State->get<ConstraintRange>(Class); +} + +LLVM_NODISCARD inline const RangeSet *getConstraint(ProgramStateRef State, + SymbolRef Sym) { + return getConstraint(State, EquivalenceClass::find(State, Sym)); +} + +//===----------------------------------------------------------------------===// +// Equality/diseqiality abstraction +//===----------------------------------------------------------------------===// + +/// A small helper structure representing symbolic equality. +/// +/// Equality check can have different forms (like a == b or a - b) and this +/// class encapsulates those away if the only thing the user wants to check - +/// whether it's equality/diseqiality or not and have an easy access to the +/// compared symbols. +struct EqualityInfo { +public: + SymbolRef Left, Right; + // true for equality and false for disequality. + bool IsEquality = true; + + void invert() { IsEquality = !IsEquality; } + /// Extract equality information from the given symbol and the constants. + /// + /// This function assumes the following expression Sym + Adjustment != Int. + /// It is a default because the most widespread case of the equality check + /// is (A == B) + 0 != 0. + static Optional<EqualityInfo> extract(SymbolRef Sym, const llvm::APSInt &Int, + const llvm::APSInt &Adjustment) { + // As of now, the only equality form supported is Sym + 0 != 0. + if (!Int.isNullValue() || !Adjustment.isNullValue()) + return llvm::None; + + return extract(Sym); + } + /// Extract equality information from the given symbol. + static Optional<EqualityInfo> extract(SymbolRef Sym) { + return EqualityExtractor().Visit(Sym); + } + +private: + class EqualityExtractor + : public SymExprVisitor<EqualityExtractor, Optional<EqualityInfo>> { + public: + Optional<EqualityInfo> VisitSymSymExpr(const SymSymExpr *Sym) const { + switch (Sym->getOpcode()) { + case BO_Sub: + // This case is: A - B != 0 -> disequality check. + return EqualityInfo{Sym->getLHS(), Sym->getRHS(), false}; + case BO_EQ: + // This case is: A == B != 0 -> equality check. + return EqualityInfo{Sym->getLHS(), Sym->getRHS(), true}; + case BO_NE: + // This case is: A != B != 0 -> diseqiality check. + return EqualityInfo{Sym->getLHS(), Sym->getRHS(), false}; + default: + return llvm::None; + } + } + }; +}; + +//===----------------------------------------------------------------------===// +// Intersection functions +//===----------------------------------------------------------------------===// + +template <class SecondTy, class... RestTy> +LLVM_NODISCARD inline RangeSet intersect(BasicValueFactory &BV, + RangeSet::Factory &F, RangeSet Head, + SecondTy Second, RestTy... Tail); + +template <class... RangeTy> struct IntersectionTraits; + +template <class... TailTy> struct IntersectionTraits<RangeSet, TailTy...> { + // Found RangeSet, no need to check any further + using Type = RangeSet; +}; + +template <> struct IntersectionTraits<> { + // We ran out of types, and we didn't find any RangeSet, so the result should + // be optional. + using Type = Optional<RangeSet>; +}; + +template <class OptionalOrPointer, class... TailTy> +struct IntersectionTraits<OptionalOrPointer, TailTy...> { + // If current type is Optional or a raw pointer, we should keep looking. + using Type = typename IntersectionTraits<TailTy...>::Type; +}; + +template <class EndTy> +LLVM_NODISCARD inline EndTy intersect(BasicValueFactory &BV, + RangeSet::Factory &F, EndTy End) { + // If the list contains only RangeSet or Optional<RangeSet>, simply return + // that range set. + return End; +} + +LLVM_NODISCARD LLVM_ATTRIBUTE_UNUSED inline Optional<RangeSet> +intersect(BasicValueFactory &BV, RangeSet::Factory &F, const RangeSet *End) { + // This is an extraneous conversion from a raw pointer into Optional<RangeSet> + if (End) { + return *End; + } + return llvm::None; +} + +template <class... RestTy> +LLVM_NODISCARD inline RangeSet intersect(BasicValueFactory &BV, + RangeSet::Factory &F, RangeSet Head, + RangeSet Second, RestTy... Tail) { + // Here we call either the <RangeSet,RangeSet,...> or <RangeSet,...> version + // of the function and can be sure that the result is RangeSet. + return intersect(BV, F, Head.Intersect(BV, F, Second), Tail...); +} + +template <class SecondTy, class... RestTy> +LLVM_NODISCARD inline RangeSet intersect(BasicValueFactory &BV, + RangeSet::Factory &F, RangeSet Head, + SecondTy Second, RestTy... Tail) { + if (Second) { + // Here we call the <RangeSet,RangeSet,...> version of the function... + return intersect(BV, F, Head, *Second, Tail...); + } + // ...and here it is either <RangeSet,RangeSet,...> or <RangeSet,...>, which + // means that the result is definitely RangeSet. + return intersect(BV, F, Head, Tail...); +} + +/// Main generic intersect function. +/// It intersects all of the given range sets. If some of the given arguments +/// don't hold a range set (nullptr or llvm::None), the function will skip them. +/// +/// Available representations for the arguments are: +/// * RangeSet +/// * Optional<RangeSet> +/// * RangeSet * +/// Pointer to a RangeSet is automatically assumed to be nullable and will get +/// checked as well as the optional version. If this behaviour is undesired, +/// please dereference the pointer in the call. +/// +/// Return type depends on the arguments' types. If we can be sure in compile +/// time that there will be a range set as a result, the returning type is +/// simply RangeSet, in other cases we have to back off to Optional<RangeSet>. +/// +/// Please, prefer optional range sets to raw pointers. If the last argument is +/// a raw pointer and all previous arguments are None, it will cost one +/// additional check to convert RangeSet * into Optional<RangeSet>. +template <class HeadTy, class SecondTy, class... RestTy> +LLVM_NODISCARD inline + typename IntersectionTraits<HeadTy, SecondTy, RestTy...>::Type + intersect(BasicValueFactory &BV, RangeSet::Factory &F, HeadTy Head, + SecondTy Second, RestTy... Tail) { + if (Head) { + return intersect(BV, F, *Head, Second, Tail...); + } + return intersect(BV, F, Second, Tail...); +} + +//===----------------------------------------------------------------------===// +// Symbolic reasoning logic +//===----------------------------------------------------------------------===// /// A little component aggregating all of the reasoning we have about /// the ranges of symbolic expressions. @@ -389,10 +709,11 @@ namespace { class SymbolicRangeInferrer : public SymExprVisitor<SymbolicRangeInferrer, RangeSet> { public: + template <class SourceType> static RangeSet inferRange(BasicValueFactory &BV, RangeSet::Factory &F, - ProgramStateRef State, SymbolRef Sym) { + ProgramStateRef State, SourceType Origin) { SymbolicRangeInferrer Inferrer(BV, F, State); - return Inferrer.infer(Sym); + return Inferrer.infer(Origin); } RangeSet VisitSymExpr(SymbolRef Sym) { @@ -442,37 +763,35 @@ private: } RangeSet infer(SymbolRef Sym) { - const RangeSet *AssociatedRange = State->get<ConstraintRange>(Sym); - - // If Sym is a difference of symbols A - B, then maybe we have range set - // stored for B - A. - const RangeSet *RangeAssociatedWithNegatedSym = - getRangeForMinusSymbol(State, Sym); - - // If we have range set stored for both A - B and B - A then calculate the - // effective range set by intersecting the range set for A - B and the - // negated range set of B - A. - if (AssociatedRange && RangeAssociatedWithNegatedSym) - return AssociatedRange->Intersect( - ValueFactory, RangeFactory, - RangeAssociatedWithNegatedSym->Negate(ValueFactory, RangeFactory)); - - if (AssociatedRange) - return *AssociatedRange; - - if (RangeAssociatedWithNegatedSym) - return RangeAssociatedWithNegatedSym->Negate(ValueFactory, RangeFactory); + if (Optional<RangeSet> ConstraintBasedRange = intersect( + ValueFactory, RangeFactory, getConstraint(State, Sym), + // If Sym is a difference of symbols A - B, then maybe we have range + // set stored for B - A. + // + // If we have range set stored for both A - B and B - A then + // calculate the effective range set by intersecting the range set + // for A - B and the negated range set of B - A. + getRangeForNegatedSub(Sym), getRangeForEqualities(Sym))) { + return *ConstraintBasedRange; + } // If Sym is a comparison expression (except <=>), // find any other comparisons with the same operands. // See function description. - const RangeSet CmpRangeSet = getRangeForComparisonSymbol(State, Sym); - if (!CmpRangeSet.isEmpty()) - return CmpRangeSet; + if (Optional<RangeSet> CmpRangeSet = getRangeForComparisonSymbol(Sym)) { + return *CmpRangeSet; + } return Visit(Sym); } + RangeSet infer(EquivalenceClass Class) { + if (const RangeSet *AssociatedConstraint = getConstraint(State, Class)) + return *AssociatedConstraint; + + return infer(Class.getType()); + } + /// Infer range information solely from the type. RangeSet infer(QualType T) { // Lazily generate a new RangeSet representing all possible values for the @@ -621,8 +940,7 @@ private: /// Return a range set subtracting zero from \p Domain. RangeSet assumeNonZero(RangeSet Domain, QualType T) { APSIntType IntType = ValueFactory.getAPSIntType(T); - return Domain.Intersect(ValueFactory, RangeFactory, - ++IntType.getZeroValue(), --IntType.getZeroValue()); + return Domain.Delete(ValueFactory, RangeFactory, IntType.getZeroValue()); } // FIXME: Once SValBuilder supports unary minus, we should use SValBuilder to @@ -630,23 +948,26 @@ private: // symbol manually. This will allow us to support finding ranges of not // only negated SymSymExpr-type expressions, but also of other, simpler // expressions which we currently do not know how to negate. - const RangeSet *getRangeForMinusSymbol(ProgramStateRef State, SymbolRef Sym) { + Optional<RangeSet> getRangeForNegatedSub(SymbolRef Sym) { if (const SymSymExpr *SSE = dyn_cast<SymSymExpr>(Sym)) { if (SSE->getOpcode() == BO_Sub) { QualType T = Sym->getType(); + + // Do not negate unsigned ranges + if (!T->isUnsignedIntegerOrEnumerationType() && + !T->isSignedIntegerOrEnumerationType()) + return llvm::None; + SymbolManager &SymMgr = State->getSymbolManager(); - SymbolRef negSym = + SymbolRef NegatedSym = SymMgr.getSymSymExpr(SSE->getRHS(), BO_Sub, SSE->getLHS(), T); - if (const RangeSet *negV = State->get<ConstraintRange>(negSym)) { - // Unsigned range set cannot be negated, unless it is [0, 0]. - if (T->isUnsignedIntegerOrEnumerationType() || - T->isSignedIntegerOrEnumerationType()) - return negV; + if (const RangeSet *NegatedRange = getConstraint(State, NegatedSym)) { + return NegatedRange->Negate(ValueFactory, RangeFactory); } } } - return nullptr; + return llvm::None; } // Returns ranges only for binary comparison operators (except <=>) @@ -659,18 +980,16 @@ private: // It covers all possible combinations (see CmpOpTable description). // Note that `x` and `y` can also stand for subexpressions, // not only for actual symbols. - RangeSet getRangeForComparisonSymbol(ProgramStateRef State, SymbolRef Sym) { - const RangeSet EmptyRangeSet = RangeFactory.getEmptySet(); - - auto SSE = dyn_cast<SymSymExpr>(Sym); + Optional<RangeSet> getRangeForComparisonSymbol(SymbolRef Sym) { + const auto *SSE = dyn_cast<SymSymExpr>(Sym); if (!SSE) - return EmptyRangeSet; + return llvm::None; BinaryOperatorKind CurrentOP = SSE->getOpcode(); // We currently do not support <=> (C++20). if (!BinaryOperator::isComparisonOp(CurrentOP) || (CurrentOP == BO_Cmp)) - return EmptyRangeSet; + return llvm::None; static const OperatorRelationsTable CmpOpTable{}; @@ -679,10 +998,6 @@ private: QualType T = SSE->getType(); SymbolManager &SymMgr = State->getSymbolManager(); - const llvm::APSInt &Zero = ValueFactory.getValue(0, T); - const llvm::APSInt &One = ValueFactory.getValue(1, T); - const RangeSet TrueRangeSet(RangeFactory, One, One); - const RangeSet FalseRangeSet(RangeFactory, Zero, Zero); int UnknownStates = 0; @@ -693,7 +1008,7 @@ private: // Let's find an expression e.g. (x < y). BinaryOperatorKind QueriedOP = OperatorRelationsTable::getOpFromIndex(i); const SymSymExpr *SymSym = SymMgr.getSymSymExpr(LHS, QueriedOP, RHS, T); - const RangeSet *QueriedRangeSet = State->get<ConstraintRange>(SymSym); + const RangeSet *QueriedRangeSet = getConstraint(State, SymSym); // If ranges were not previously found, // try to find a reversed expression (y > x). @@ -701,7 +1016,7 @@ private: const BinaryOperatorKind ROP = BinaryOperator::reverseComparisonOp(QueriedOP); SymSym = SymMgr.getSymSymExpr(RHS, ROP, LHS, T); - QueriedRangeSet = State->get<ConstraintRange>(SymSym); + QueriedRangeSet = getConstraint(State, SymSym); } if (!QueriedRangeSet || QueriedRangeSet->isEmpty()) @@ -732,11 +1047,38 @@ private: continue; } - return (BranchState == OperatorRelationsTable::True) ? TrueRangeSet - : FalseRangeSet; + return (BranchState == OperatorRelationsTable::True) ? getTrueRange(T) + : getFalseRange(T); + } + + return llvm::None; + } + + Optional<RangeSet> getRangeForEqualities(SymbolRef Sym) { + Optional<EqualityInfo> Equality = EqualityInfo::extract(Sym); + + if (!Equality) + return llvm::None; + + if (Optional<bool> AreEqual = EquivalenceClass::areEqual( + State, Equality->Left, Equality->Right)) { + if (*AreEqual == Equality->IsEquality) { + return getTrueRange(Sym->getType()); + } + return getFalseRange(Sym->getType()); } - return EmptyRangeSet; + return llvm::None; + } + + RangeSet getTrueRange(QualType T) { + RangeSet TypeRange = infer(T); + return assumeNonZero(TypeRange, T); + } + + RangeSet getFalseRange(QualType T) { + const llvm::APSInt &Zero = ValueFactory.getValue(0, T); + return RangeSet(RangeFactory, Zero); } BasicValueFactory &ValueFactory; @@ -744,6 +1086,10 @@ private: ProgramStateRef State; }; +//===----------------------------------------------------------------------===// +// Range-based reasoning about symbolic operations +//===----------------------------------------------------------------------===// + template <> RangeSet SymbolicRangeInferrer::VisitBinaryOperator<BO_Or>(Range LHS, Range RHS, QualType T) { @@ -904,6 +1250,10 @@ RangeSet SymbolicRangeInferrer::VisitBinaryOperator<BO_Rem>(Range LHS, return {RangeFactory, ValueFactory.getValue(Min), ValueFactory.getValue(Max)}; } +//===----------------------------------------------------------------------===// +// Constraint manager implementation details +//===----------------------------------------------------------------------===// + class RangeConstraintManager : public RangedConstraintManager { public: RangeConstraintManager(ExprEngine *EE, SValBuilder &SVB) @@ -915,7 +1265,11 @@ public: bool haveEqualConstraints(ProgramStateRef S1, ProgramStateRef S2) const override { - return S1->get<ConstraintRange>() == S2->get<ConstraintRange>(); + // NOTE: ClassMembers are as simple as back pointers for ClassMap, + // so comparing constraint ranges and class maps should be + // sufficient. + return S1->get<ConstraintRange>() == S2->get<ConstraintRange>() && + S1->get<ClassMap>() == S2->get<ClassMap>(); } bool canReasonAbout(SVal X) const override; @@ -971,6 +1325,7 @@ private: RangeSet::Factory F; RangeSet getRange(ProgramStateRef State, SymbolRef Sym); + RangeSet getRange(ProgramStateRef State, EquivalenceClass Class); RangeSet getSymLTRange(ProgramStateRef St, SymbolRef Sym, const llvm::APSInt &Int, @@ -987,6 +1342,87 @@ private: RangeSet getSymGERange(ProgramStateRef St, SymbolRef Sym, const llvm::APSInt &Int, const llvm::APSInt &Adjustment); + + //===------------------------------------------------------------------===// + // Equality tracking implementation + //===------------------------------------------------------------------===// + + ProgramStateRef trackEQ(RangeSet NewConstraint, ProgramStateRef State, + SymbolRef Sym, const llvm::APSInt &Int, + const llvm::APSInt &Adjustment) { + return track<true>(NewConstraint, State, Sym, Int, Adjustment); + } + + ProgramStateRef trackNE(RangeSet NewConstraint, ProgramStateRef State, + SymbolRef Sym, const llvm::APSInt &Int, + const llvm::APSInt &Adjustment) { + return track<false>(NewConstraint, State, Sym, Int, Adjustment); + } + + template <bool EQ> + ProgramStateRef track(RangeSet NewConstraint, ProgramStateRef State, + SymbolRef Sym, const llvm::APSInt &Int, + const llvm::APSInt &Adjustment) { + if (NewConstraint.isEmpty()) + // This is an infeasible assumption. + return nullptr; + + ProgramStateRef NewState = setConstraint(State, Sym, NewConstraint); + if (auto Equality = EqualityInfo::extract(Sym, Int, Adjustment)) { + // If the original assumption is not Sym + Adjustment !=/</> Int, + // we should invert IsEquality flag. + Equality->IsEquality = Equality->IsEquality != EQ; + return track(NewState, *Equality); + } + + return NewState; + } + + ProgramStateRef track(ProgramStateRef State, EqualityInfo ToTrack) { + if (ToTrack.IsEquality) { + return trackEquality(State, ToTrack.Left, ToTrack.Right); + } + return trackDisequality(State, ToTrack.Left, ToTrack.Right); + } + + ProgramStateRef trackDisequality(ProgramStateRef State, SymbolRef LHS, + SymbolRef RHS) { + return EquivalenceClass::markDisequal(getBasicVals(), F, State, LHS, RHS); + } + + ProgramStateRef trackEquality(ProgramStateRef State, SymbolRef LHS, + SymbolRef RHS) { + return EquivalenceClass::merge(getBasicVals(), F, State, LHS, RHS); + } + + LLVM_NODISCARD inline ProgramStateRef setConstraint(ProgramStateRef State, + EquivalenceClass Class, + RangeSet Constraint) { + ConstraintRangeTy Constraints = State->get<ConstraintRange>(); + ConstraintRangeTy::Factory &CF = State->get_context<ConstraintRange>(); + + // Add new constraint. + Constraints = CF.add(Constraints, Class, Constraint); + + // There is a chance that we might need to update constraints for the + // classes that are known to be disequal to Class. + // + // In order for this to be even possible, the new constraint should + // be simply a constant because we can't reason about range disequalities. + if (const llvm::APSInt *Point = Constraint.getConcreteValue()) + for (EquivalenceClass DisequalClass : Class.getDisequalClasses(State)) { + RangeSet UpdatedConstraint = + getRange(State, DisequalClass).Delete(getBasicVals(), F, *Point); + Constraints = CF.add(Constraints, DisequalClass, UpdatedConstraint); + } + + return State->set<ConstraintRange>(Constraints); + } + + LLVM_NODISCARD inline ProgramStateRef + setConstraint(ProgramStateRef State, SymbolRef Sym, RangeSet Constraint) { + return setConstraint(State, EquivalenceClass::find(State, Sym), Constraint); + } }; } // end anonymous namespace @@ -997,6 +1433,372 @@ ento::CreateRangeConstraintManager(ProgramStateManager &StMgr, return std::make_unique<RangeConstraintManager>(Eng, StMgr.getSValBuilder()); } +ConstraintMap ento::getConstraintMap(ProgramStateRef State) { + ConstraintMap::Factory &F = State->get_context<ConstraintMap>(); + ConstraintMap Result = F.getEmptyMap(); + + ConstraintRangeTy Constraints = State->get<ConstraintRange>(); + for (std::pair<EquivalenceClass, RangeSet> ClassConstraint : Constraints) { + EquivalenceClass Class = ClassConstraint.first; + SymbolSet ClassMembers = Class.getClassMembers(State); + assert(!ClassMembers.isEmpty() && + "Class must always have at least one member!"); + + SymbolRef Representative = *ClassMembers.begin(); + Result = F.add(Result, Representative, ClassConstraint.second); + } + + return Result; +} + +//===----------------------------------------------------------------------===// +// EqualityClass implementation details +//===----------------------------------------------------------------------===// + +inline EquivalenceClass EquivalenceClass::find(ProgramStateRef State, + SymbolRef Sym) { + // We store far from all Symbol -> Class mappings + if (const EquivalenceClass *NontrivialClass = State->get<ClassMap>(Sym)) + return *NontrivialClass; + + // This is a trivial class of Sym. + return Sym; +} + +inline ProgramStateRef EquivalenceClass::merge(BasicValueFactory &BV, + RangeSet::Factory &F, + ProgramStateRef State, + SymbolRef First, + SymbolRef Second) { + EquivalenceClass FirstClass = find(State, First); + EquivalenceClass SecondClass = find(State, Second); + + return FirstClass.merge(BV, F, State, SecondClass); +} + +inline ProgramStateRef EquivalenceClass::merge(BasicValueFactory &BV, + RangeSet::Factory &F, + ProgramStateRef State, + EquivalenceClass Other) { + // It is already the same class. + if (*this == Other) + return State; + + // FIXME: As of now, we support only equivalence classes of the same type. + // This limitation is connected to the lack of explicit casts in + // our symbolic expression model. + // + // That means that for `int x` and `char y` we don't distinguish + // between these two very different cases: + // * `x == y` + // * `(char)x == y` + // + // The moment we introduce symbolic casts, this restriction can be + // lifted. + if (getType() != Other.getType()) + return State; + + SymbolSet Members = getClassMembers(State); + SymbolSet OtherMembers = Other.getClassMembers(State); + + // We estimate the size of the class by the height of tree containing + // its members. Merging is not a trivial operation, so it's easier to + // merge the smaller class into the bigger one. + if (Members.getHeight() >= OtherMembers.getHeight()) { + return mergeImpl(BV, F, State, Members, Other, OtherMembers); + } else { + return Other.mergeImpl(BV, F, State, OtherMembers, *this, Members); + } +} + +inline ProgramStateRef +EquivalenceClass::mergeImpl(BasicValueFactory &ValueFactory, + RangeSet::Factory &RangeFactory, + ProgramStateRef State, SymbolSet MyMembers, + EquivalenceClass Other, SymbolSet OtherMembers) { + // Essentially what we try to recreate here is some kind of union-find + // data structure. It does have certain limitations due to persistence + // and the need to remove elements from classes. + // + // In this setting, EquialityClass object is the representative of the class + // or the parent element. ClassMap is a mapping of class members to their + // parent. Unlike the union-find structure, they all point directly to the + // class representative because we don't have an opportunity to actually do + // path compression when dealing with immutability. This means that we + // compress paths every time we do merges. It also means that we lose + // the main amortized complexity benefit from the original data structure. + ConstraintRangeTy Constraints = State->get<ConstraintRange>(); + ConstraintRangeTy::Factory &CRF = State->get_context<ConstraintRange>(); + + // 1. If the merged classes have any constraints associated with them, we + // need to transfer them to the class we have left. + // + // Intersection here makes perfect sense because both of these constraints + // must hold for the whole new class. + if (Optional<RangeSet> NewClassConstraint = + intersect(ValueFactory, RangeFactory, getConstraint(State, *this), + getConstraint(State, Other))) { + // NOTE: Essentially, NewClassConstraint should NEVER be infeasible because + // range inferrer shouldn't generate ranges incompatible with + // equivalence classes. However, at the moment, due to imperfections + // in the solver, it is possible and the merge function can also + // return infeasible states aka null states. + if (NewClassConstraint->isEmpty()) + // Infeasible state + return nullptr; + + // No need in tracking constraints of a now-dissolved class. + Constraints = CRF.remove(Constraints, Other); + // Assign new constraints for this class. + Constraints = CRF.add(Constraints, *this, *NewClassConstraint); + + State = State->set<ConstraintRange>(Constraints); + } + + // 2. Get ALL equivalence-related maps + ClassMapTy Classes = State->get<ClassMap>(); + ClassMapTy::Factory &CMF = State->get_context<ClassMap>(); + + ClassMembersTy Members = State->get<ClassMembers>(); + ClassMembersTy::Factory &MF = State->get_context<ClassMembers>(); + + DisequalityMapTy DisequalityInfo = State->get<DisequalityMap>(); + DisequalityMapTy::Factory &DF = State->get_context<DisequalityMap>(); + + ClassSet::Factory &CF = State->get_context<ClassSet>(); + SymbolSet::Factory &F = getMembersFactory(State); + + // 2. Merge members of the Other class into the current class. + SymbolSet NewClassMembers = MyMembers; + for (SymbolRef Sym : OtherMembers) { + NewClassMembers = F.add(NewClassMembers, Sym); + // *this is now the class for all these new symbols. + Classes = CMF.add(Classes, Sym, *this); + } + + // 3. Adjust member mapping. + // + // No need in tracking members of a now-dissolved class. + Members = MF.remove(Members, Other); + // Now only the current class is mapped to all the symbols. + Members = MF.add(Members, *this, NewClassMembers); + + // 4. Update disequality relations + ClassSet DisequalToOther = Other.getDisequalClasses(DisequalityInfo, CF); + if (!DisequalToOther.isEmpty()) { + ClassSet DisequalToThis = getDisequalClasses(DisequalityInfo, CF); + DisequalityInfo = DF.remove(DisequalityInfo, Other); + + for (EquivalenceClass DisequalClass : DisequalToOther) { + DisequalToThis = CF.add(DisequalToThis, DisequalClass); + + // Disequality is a symmetric relation meaning that if + // DisequalToOther not null then the set for DisequalClass is not + // empty and has at least Other. + ClassSet OriginalSetLinkedToOther = + *DisequalityInfo.lookup(DisequalClass); + + // Other will be eliminated and we should replace it with the bigger + // united class. + ClassSet NewSet = CF.remove(OriginalSetLinkedToOther, Other); + NewSet = CF.add(NewSet, *this); + + DisequalityInfo = DF.add(DisequalityInfo, DisequalClass, NewSet); + } + + DisequalityInfo = DF.add(DisequalityInfo, *this, DisequalToThis); + State = State->set<DisequalityMap>(DisequalityInfo); + } + + // 5. Update the state + State = State->set<ClassMap>(Classes); + State = State->set<ClassMembers>(Members); + + return State; +} + +inline SymbolSet::Factory & +EquivalenceClass::getMembersFactory(ProgramStateRef State) { + return State->get_context<SymbolSet>(); +} + +SymbolSet EquivalenceClass::getClassMembers(ProgramStateRef State) { + if (const SymbolSet *Members = State->get<ClassMembers>(*this)) + return *Members; + + // This class is trivial, so we need to construct a set + // with just that one symbol from the class. + SymbolSet::Factory &F = getMembersFactory(State); + return F.add(F.getEmptySet(), getRepresentativeSymbol()); +} + +bool EquivalenceClass::isTrivial(ProgramStateRef State) { + return State->get<ClassMembers>(*this) == nullptr; +} + +bool EquivalenceClass::isTriviallyDead(ProgramStateRef State, + SymbolReaper &Reaper) { + return isTrivial(State) && Reaper.isDead(getRepresentativeSymbol()); +} + +inline ProgramStateRef EquivalenceClass::markDisequal(BasicValueFactory &VF, + RangeSet::Factory &RF, + ProgramStateRef State, + SymbolRef First, + SymbolRef Second) { + return markDisequal(VF, RF, State, find(State, First), find(State, Second)); +} + +inline ProgramStateRef EquivalenceClass::markDisequal(BasicValueFactory &VF, + RangeSet::Factory &RF, + ProgramStateRef State, + EquivalenceClass First, + EquivalenceClass Second) { + return First.markDisequal(VF, RF, State, Second); +} + +inline ProgramStateRef +EquivalenceClass::markDisequal(BasicValueFactory &VF, RangeSet::Factory &RF, + ProgramStateRef State, + EquivalenceClass Other) const { + // If we know that two classes are equal, we can only produce an infeasible + // state. + if (*this == Other) { + return nullptr; + } + + DisequalityMapTy DisequalityInfo = State->get<DisequalityMap>(); + ConstraintRangeTy Constraints = State->get<ConstraintRange>(); + + // Disequality is a symmetric relation, so if we mark A as disequal to B, + // we should also mark B as disequalt to A. + addToDisequalityInfo(DisequalityInfo, Constraints, VF, RF, State, *this, + Other); + addToDisequalityInfo(DisequalityInfo, Constraints, VF, RF, State, Other, + *this); + + State = State->set<DisequalityMap>(DisequalityInfo); + State = State->set<ConstraintRange>(Constraints); + + return State; +} + +inline void EquivalenceClass::addToDisequalityInfo( + DisequalityMapTy &Info, ConstraintRangeTy &Constraints, + BasicValueFactory &VF, RangeSet::Factory &RF, ProgramStateRef State, + EquivalenceClass First, EquivalenceClass Second) { + + // 1. Get all of the required factories. + DisequalityMapTy::Factory &F = State->get_context<DisequalityMap>(); + ClassSet::Factory &CF = State->get_context<ClassSet>(); + ConstraintRangeTy::Factory &CRF = State->get_context<ConstraintRange>(); + + // 2. Add Second to the set of classes disequal to First. + const ClassSet *CurrentSet = Info.lookup(First); + ClassSet NewSet = CurrentSet ? *CurrentSet : CF.getEmptySet(); + NewSet = CF.add(NewSet, Second); + + Info = F.add(Info, First, NewSet); + + // 3. If Second is known to be a constant, we can delete this point + // from the constraint asociated with First. + // + // So, if Second == 10, it means that First != 10. + // At the same time, the same logic does not apply to ranges. + if (const RangeSet *SecondConstraint = Constraints.lookup(Second)) + if (const llvm::APSInt *Point = SecondConstraint->getConcreteValue()) { + + RangeSet FirstConstraint = SymbolicRangeInferrer::inferRange( + VF, RF, State, First.getRepresentativeSymbol()); + + FirstConstraint = FirstConstraint.Delete(VF, RF, *Point); + Constraints = CRF.add(Constraints, First, FirstConstraint); + } +} + +inline Optional<bool> EquivalenceClass::areEqual(ProgramStateRef State, + SymbolRef FirstSym, + SymbolRef SecondSym) { + EquivalenceClass First = find(State, FirstSym); + EquivalenceClass Second = find(State, SecondSym); + + // The same equivalence class => symbols are equal. + if (First == Second) + return true; + + // Let's check if we know anything about these two classes being not equal to + // each other. + ClassSet DisequalToFirst = First.getDisequalClasses(State); + if (DisequalToFirst.contains(Second)) + return false; + + // It is not clear. + return llvm::None; +} + +inline ClassSet EquivalenceClass::getDisequalClasses(ProgramStateRef State, + SymbolRef Sym) { + return find(State, Sym).getDisequalClasses(State); +} + +inline ClassSet +EquivalenceClass::getDisequalClasses(ProgramStateRef State) const { + return getDisequalClasses(State->get<DisequalityMap>(), + State->get_context<ClassSet>()); +} + +inline ClassSet +EquivalenceClass::getDisequalClasses(DisequalityMapTy Map, + ClassSet::Factory &Factory) const { + if (const ClassSet *DisequalClasses = Map.lookup(*this)) + return *DisequalClasses; + + return Factory.getEmptySet(); +} + +bool EquivalenceClass::isClassDataConsistent(ProgramStateRef State) { + ClassMembersTy Members = State->get<ClassMembers>(); + + for (std::pair<EquivalenceClass, SymbolSet> ClassMembersPair : Members) { + for (SymbolRef Member : ClassMembersPair.second) { + // Every member of the class should have a mapping back to the class. + if (find(State, Member) == ClassMembersPair.first) { + continue; + } + + return false; + } + } + + DisequalityMapTy Disequalities = State->get<DisequalityMap>(); + for (std::pair<EquivalenceClass, ClassSet> DisequalityInfo : Disequalities) { + EquivalenceClass Class = DisequalityInfo.first; + ClassSet DisequalClasses = DisequalityInfo.second; + + // There is no use in keeping empty sets in the map. + if (DisequalClasses.isEmpty()) + return false; + + // Disequality is symmetrical, i.e. for every Class A and B that A != B, + // B != A should also be true. + for (EquivalenceClass DisequalClass : DisequalClasses) { + const ClassSet *DisequalToDisequalClasses = + Disequalities.lookup(DisequalClass); + + // It should be a set of at least one element: Class + if (!DisequalToDisequalClasses || + !DisequalToDisequalClasses->contains(Class)) + return false; + } + } + + return true; +} + +//===----------------------------------------------------------------------===// +// RangeConstraintManager implementation +//===----------------------------------------------------------------------===// + bool RangeConstraintManager::canReasonAbout(SVal X) const { Optional<nonloc::SymbolVal> SymVal = X.getAs<nonloc::SymbolVal>(); if (SymVal && SymVal->isExpression()) { @@ -1045,7 +1847,7 @@ bool RangeConstraintManager::canReasonAbout(SVal X) const { ConditionTruthVal RangeConstraintManager::checkNull(ProgramStateRef State, SymbolRef Sym) { - const RangeSet *Ranges = State->get<ConstraintRange>(Sym); + const RangeSet *Ranges = getConstraint(State, Sym); // If we don't have any information about this symbol, it's underconstrained. if (!Ranges) @@ -1069,28 +1871,148 @@ ConditionTruthVal RangeConstraintManager::checkNull(ProgramStateRef State, const llvm::APSInt *RangeConstraintManager::getSymVal(ProgramStateRef St, SymbolRef Sym) const { - const ConstraintRangeTy::data_type *T = St->get<ConstraintRange>(Sym); + const RangeSet *T = getConstraint(St, Sym); return T ? T->getConcreteValue() : nullptr; } +//===----------------------------------------------------------------------===// +// Remove dead symbols from existing constraints +//===----------------------------------------------------------------------===// + /// Scan all symbols referenced by the constraints. If the symbol is not alive /// as marked in LSymbols, mark it as dead in DSymbols. ProgramStateRef RangeConstraintManager::removeDeadBindings(ProgramStateRef State, SymbolReaper &SymReaper) { - bool Changed = false; - ConstraintRangeTy CR = State->get<ConstraintRange>(); - ConstraintRangeTy::Factory &CRFactory = State->get_context<ConstraintRange>(); + ClassMembersTy ClassMembersMap = State->get<ClassMembers>(); + ClassMembersTy NewClassMembersMap = ClassMembersMap; + ClassMembersTy::Factory &EMFactory = State->get_context<ClassMembers>(); + SymbolSet::Factory &SetFactory = State->get_context<SymbolSet>(); + + ConstraintRangeTy Constraints = State->get<ConstraintRange>(); + ConstraintRangeTy NewConstraints = Constraints; + ConstraintRangeTy::Factory &ConstraintFactory = + State->get_context<ConstraintRange>(); + + ClassMapTy Map = State->get<ClassMap>(); + ClassMapTy NewMap = Map; + ClassMapTy::Factory &ClassFactory = State->get_context<ClassMap>(); + + DisequalityMapTy Disequalities = State->get<DisequalityMap>(); + DisequalityMapTy::Factory &DisequalityFactory = + State->get_context<DisequalityMap>(); + ClassSet::Factory &ClassSetFactory = State->get_context<ClassSet>(); + + bool ClassMapChanged = false; + bool MembersMapChanged = false; + bool ConstraintMapChanged = false; + bool DisequalitiesChanged = false; + + auto removeDeadClass = [&](EquivalenceClass Class) { + // Remove associated constraint ranges. + Constraints = ConstraintFactory.remove(Constraints, Class); + ConstraintMapChanged = true; + + // Update disequality information to not hold any information on the + // removed class. + ClassSet DisequalClasses = + Class.getDisequalClasses(Disequalities, ClassSetFactory); + if (!DisequalClasses.isEmpty()) { + for (EquivalenceClass DisequalClass : DisequalClasses) { + ClassSet DisequalToDisequalSet = + DisequalClass.getDisequalClasses(Disequalities, ClassSetFactory); + // DisequalToDisequalSet is guaranteed to be non-empty for consistent + // disequality info. + assert(!DisequalToDisequalSet.isEmpty()); + ClassSet NewSet = ClassSetFactory.remove(DisequalToDisequalSet, Class); + + // No need in keeping an empty set. + if (NewSet.isEmpty()) { + Disequalities = + DisequalityFactory.remove(Disequalities, DisequalClass); + } else { + Disequalities = + DisequalityFactory.add(Disequalities, DisequalClass, NewSet); + } + } + // Remove the data for the class + Disequalities = DisequalityFactory.remove(Disequalities, Class); + DisequalitiesChanged = true; + } + }; + + // 1. Let's see if dead symbols are trivial and have associated constraints. + for (std::pair<EquivalenceClass, RangeSet> ClassConstraintPair : + Constraints) { + EquivalenceClass Class = ClassConstraintPair.first; + if (Class.isTriviallyDead(State, SymReaper)) { + // If this class is trivial, we can remove its constraints right away. + removeDeadClass(Class); + } + } + + // 2. We don't need to track classes for dead symbols. + for (std::pair<SymbolRef, EquivalenceClass> SymbolClassPair : Map) { + SymbolRef Sym = SymbolClassPair.first; - for (ConstraintRangeTy::iterator I = CR.begin(), E = CR.end(); I != E; ++I) { - SymbolRef Sym = I.getKey(); if (SymReaper.isDead(Sym)) { - Changed = true; - CR = CRFactory.remove(CR, Sym); + ClassMapChanged = true; + NewMap = ClassFactory.remove(NewMap, Sym); } } - return Changed ? State->set<ConstraintRange>(CR) : State; + // 3. Remove dead members from classes and remove dead non-trivial classes + // and their constraints. + for (std::pair<EquivalenceClass, SymbolSet> ClassMembersPair : + ClassMembersMap) { + EquivalenceClass Class = ClassMembersPair.first; + SymbolSet LiveMembers = ClassMembersPair.second; + bool MembersChanged = false; + + for (SymbolRef Member : ClassMembersPair.second) { + if (SymReaper.isDead(Member)) { + MembersChanged = true; + LiveMembers = SetFactory.remove(LiveMembers, Member); + } + } + + // Check if the class changed. + if (!MembersChanged) + continue; + + MembersMapChanged = true; + + if (LiveMembers.isEmpty()) { + // The class is dead now, we need to wipe it out of the members map... + NewClassMembersMap = EMFactory.remove(NewClassMembersMap, Class); + + // ...and remove all of its constraints. + removeDeadClass(Class); + } else { + // We need to change the members associated with the class. + NewClassMembersMap = + EMFactory.add(NewClassMembersMap, Class, LiveMembers); + } + } + + // 4. Update the state with new maps. + // + // Here we try to be humble and update a map only if it really changed. + if (ClassMapChanged) + State = State->set<ClassMap>(NewMap); + + if (MembersMapChanged) + State = State->set<ClassMembers>(NewClassMembersMap); + + if (ConstraintMapChanged) + State = State->set<ConstraintRange>(Constraints); + + if (DisequalitiesChanged) + State = State->set<DisequalityMap>(Disequalities); + + assert(EquivalenceClass::isClassDataConsistent(State)); + + return State; } RangeSet RangeConstraintManager::getRange(ProgramStateRef State, @@ -1098,6 +2020,11 @@ RangeSet RangeConstraintManager::getRange(ProgramStateRef State, return SymbolicRangeInferrer::inferRange(getBasicVals(), F, State, Sym); } +RangeSet RangeConstraintManager::getRange(ProgramStateRef State, + EquivalenceClass Class) { + return SymbolicRangeInferrer::inferRange(getBasicVals(), F, State, Class); +} + //===------------------------------------------------------------------------=== // assumeSymX methods: protected interface for RangeConstraintManager. //===------------------------------------------------------------------------===/ @@ -1119,15 +2046,11 @@ RangeConstraintManager::assumeSymNE(ProgramStateRef St, SymbolRef Sym, if (AdjustmentType.testInRange(Int, true) != APSIntType::RTR_Within) return St; - llvm::APSInt Lower = AdjustmentType.convert(Int) - Adjustment; - llvm::APSInt Upper = Lower; - --Lower; - ++Upper; + llvm::APSInt Point = AdjustmentType.convert(Int) - Adjustment; - // [Int-Adjustment+1, Int-Adjustment-1] - // Notice that the lower bound is greater than the upper bound. - RangeSet New = getRange(St, Sym).Intersect(getBasicVals(), F, Upper, Lower); - return New.isEmpty() ? nullptr : St->set<ConstraintRange>(Sym, New); + RangeSet New = getRange(St, Sym).Delete(getBasicVals(), F, Point); + + return trackNE(New, St, Sym, Int, Adjustment); } ProgramStateRef @@ -1142,7 +2065,8 @@ RangeConstraintManager::assumeSymEQ(ProgramStateRef St, SymbolRef Sym, // [Int-Adjustment, Int-Adjustment] llvm::APSInt AdjInt = AdjustmentType.convert(Int) - Adjustment; RangeSet New = getRange(St, Sym).Intersect(getBasicVals(), F, AdjInt, AdjInt); - return New.isEmpty() ? nullptr : St->set<ConstraintRange>(Sym, New); + + return trackEQ(New, St, Sym, Int, Adjustment); } RangeSet RangeConstraintManager::getSymLTRange(ProgramStateRef St, @@ -1178,7 +2102,7 @@ RangeConstraintManager::assumeSymLT(ProgramStateRef St, SymbolRef Sym, const llvm::APSInt &Int, const llvm::APSInt &Adjustment) { RangeSet New = getSymLTRange(St, Sym, Int, Adjustment); - return New.isEmpty() ? nullptr : St->set<ConstraintRange>(Sym, New); + return trackNE(New, St, Sym, Int, Adjustment); } RangeSet RangeConstraintManager::getSymGTRange(ProgramStateRef St, @@ -1214,7 +2138,7 @@ RangeConstraintManager::assumeSymGT(ProgramStateRef St, SymbolRef Sym, const llvm::APSInt &Int, const llvm::APSInt &Adjustment) { RangeSet New = getSymGTRange(St, Sym, Int, Adjustment); - return New.isEmpty() ? nullptr : St->set<ConstraintRange>(Sym, New); + return trackNE(New, St, Sym, Int, Adjustment); } RangeSet RangeConstraintManager::getSymGERange(ProgramStateRef St, @@ -1250,13 +2174,13 @@ RangeConstraintManager::assumeSymGE(ProgramStateRef St, SymbolRef Sym, const llvm::APSInt &Int, const llvm::APSInt &Adjustment) { RangeSet New = getSymGERange(St, Sym, Int, Adjustment); - return New.isEmpty() ? nullptr : St->set<ConstraintRange>(Sym, New); + return New.isEmpty() ? nullptr : setConstraint(St, Sym, New); } -RangeSet RangeConstraintManager::getSymLERange( - llvm::function_ref<RangeSet()> RS, - const llvm::APSInt &Int, - const llvm::APSInt &Adjustment) { +RangeSet +RangeConstraintManager::getSymLERange(llvm::function_ref<RangeSet()> RS, + const llvm::APSInt &Int, + const llvm::APSInt &Adjustment) { // Before we do any real work, see if the value can even show up. APSIntType AdjustmentType(Adjustment); switch (AdjustmentType.testInRange(Int, true)) { @@ -1293,7 +2217,7 @@ RangeConstraintManager::assumeSymLE(ProgramStateRef St, SymbolRef Sym, const llvm::APSInt &Int, const llvm::APSInt &Adjustment) { RangeSet New = getSymLERange(St, Sym, Int, Adjustment); - return New.isEmpty() ? nullptr : St->set<ConstraintRange>(Sym, New); + return New.isEmpty() ? nullptr : setConstraint(St, Sym, New); } ProgramStateRef RangeConstraintManager::assumeSymWithinInclusiveRange( @@ -1303,7 +2227,7 @@ ProgramStateRef RangeConstraintManager::assumeSymWithinInclusiveRange( if (New.isEmpty()) return nullptr; RangeSet Out = getSymLERange([&] { return New; }, To, Adjustment); - return Out.isEmpty() ? nullptr : State->set<ConstraintRange>(Sym, Out); + return Out.isEmpty() ? nullptr : setConstraint(State, Sym, Out); } ProgramStateRef RangeConstraintManager::assumeSymOutsideInclusiveRange( @@ -1312,7 +2236,7 @@ ProgramStateRef RangeConstraintManager::assumeSymOutsideInclusiveRange( RangeSet RangeLT = getSymLTRange(State, Sym, From, Adjustment); RangeSet RangeGT = getSymGTRange(State, Sym, To, Adjustment); RangeSet New(RangeLT.addRange(F, RangeGT)); - return New.isEmpty() ? nullptr : State->set<ConstraintRange>(Sym, New); + return New.isEmpty() ? nullptr : setConstraint(State, Sym, New); } //===----------------------------------------------------------------------===// @@ -1332,17 +2256,25 @@ void RangeConstraintManager::printJson(raw_ostream &Out, ProgramStateRef State, ++Space; Out << '[' << NL; - for (ConstraintRangeTy::iterator I = Constraints.begin(); - I != Constraints.end(); ++I) { - Indent(Out, Space, IsDot) - << "{ \"symbol\": \"" << I.getKey() << "\", \"range\": \""; - I.getData().print(Out); - Out << "\" }"; - - if (std::next(I) != Constraints.end()) - Out << ','; - Out << NL; + bool First = true; + for (std::pair<EquivalenceClass, RangeSet> P : Constraints) { + SymbolSet ClassMembers = P.first.getClassMembers(State); + + // We can print the same constraint for every class member. + for (SymbolRef ClassMember : ClassMembers) { + if (First) { + First = false; + } else { + Out << ','; + Out << NL; + } + Indent(Out, Space, IsDot) + << "{ \"symbol\": \"" << ClassMember << "\", \"range\": \""; + P.second.print(Out); + Out << "\" }"; + } } + Out << NL; --Space; Indent(Out, Space, IsDot) << "]," << NL; diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/RangedConstraintManager.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/RangedConstraintManager.cpp index 4748c106eb55..e7a03e6ed582 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/RangedConstraintManager.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/RangedConstraintManager.cpp @@ -40,19 +40,20 @@ ProgramStateRef RangedConstraintManager::assumeSym(ProgramStateRef State, } } else if (const SymSymExpr *SSE = dyn_cast<SymSymExpr>(Sym)) { - // Translate "a != b" to "(b - a) != 0". - // We invert the order of the operands as a heuristic for how loop - // conditions are usually written ("begin != end") as compared to length - // calculations ("end - begin"). The more correct thing to do would be to - // canonicalize "a - b" and "b - a", which would allow us to treat - // "a != b" and "b != a" the same. - SymbolManager &SymMgr = getSymbolManager(); BinaryOperator::Opcode Op = SSE->getOpcode(); assert(BinaryOperator::isComparisonOp(Op)); - // For now, we only support comparing pointers. + // We convert equality operations for pointers only. if (Loc::isLocType(SSE->getLHS()->getType()) && Loc::isLocType(SSE->getRHS()->getType())) { + // Translate "a != b" to "(b - a) != 0". + // We invert the order of the operands as a heuristic for how loop + // conditions are usually written ("begin != end") as compared to length + // calculations ("end - begin"). The more correct thing to do would be to + // canonicalize "a - b" and "b - a", which would allow us to treat + // "a != b" and "b != a" the same. + + SymbolManager &SymMgr = getSymbolManager(); QualType DiffTy = SymMgr.getContext().getPointerDiffType(); SymbolRef Subtraction = SymMgr.getSymSymExpr(SSE->getRHS(), BO_Sub, SSE->getLHS(), DiffTy); @@ -63,6 +64,25 @@ ProgramStateRef RangedConstraintManager::assumeSym(ProgramStateRef State, Op = BinaryOperator::negateComparisonOp(Op); return assumeSymRel(State, Subtraction, Op, Zero); } + + if (BinaryOperator::isEqualityOp(Op)) { + SymbolManager &SymMgr = getSymbolManager(); + + QualType ExprType = SSE->getType(); + SymbolRef CanonicalEquality = + SymMgr.getSymSymExpr(SSE->getLHS(), BO_EQ, SSE->getRHS(), ExprType); + + bool WasEqual = SSE->getOpcode() == BO_EQ; + bool IsExpectedEqual = WasEqual == Assumption; + + const llvm::APSInt &Zero = getBasicVals().getValue(0, ExprType); + + if (IsExpectedEqual) { + return assumeSymNE(State, CanonicalEquality, Zero, Zero); + } + + return assumeSymEQ(State, CanonicalEquality, Zero, Zero); + } } // If we get here, there's nothing else we can do but treat the symbol as @@ -199,11 +219,6 @@ void RangedConstraintManager::computeAdjustment(SymbolRef &Sym, } } -void *ProgramStateTrait<ConstraintRange>::GDMIndex() { - static int Index; - return &Index; -} - } // end of namespace ento } // end of namespace clang diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp index c00a2c8ba8a2..72b8ada1dfab 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp @@ -236,10 +236,11 @@ SValBuilder::getDerivedRegionValueSymbolVal(SymbolRef parentSymbol, return nonloc::SymbolVal(sym); } -DefinedSVal SValBuilder::getMemberPointer(const DeclaratorDecl *DD) { - assert(!DD || isa<CXXMethodDecl>(DD) || isa<FieldDecl>(DD)); +DefinedSVal SValBuilder::getMemberPointer(const NamedDecl *ND) { + assert(!ND || isa<CXXMethodDecl>(ND) || isa<FieldDecl>(ND) || + isa<IndirectFieldDecl>(ND)); - if (const auto *MD = dyn_cast_or_null<CXXMethodDecl>(DD)) { + if (const auto *MD = dyn_cast_or_null<CXXMethodDecl>(ND)) { // Sema treats pointers to static member functions as have function pointer // type, so return a function pointer for the method. // We don't need to play a similar trick for static member fields @@ -249,7 +250,7 @@ DefinedSVal SValBuilder::getMemberPointer(const DeclaratorDecl *DD) { return getFunctionPointer(MD); } - return nonloc::PointerToMember(DD); + return nonloc::PointerToMember(ND); } DefinedSVal SValBuilder::getFunctionPointer(const FunctionDecl *func) { @@ -305,6 +306,14 @@ Optional<SVal> SValBuilder::getConstantVal(const Expr *E) { return makeLoc(getRegionManager().getStringRegion(SL)); } + case Stmt::PredefinedExprClass: { + const auto *PE = cast<PredefinedExpr>(E); + assert(PE->getFunctionName() && + "Since we analyze only instantiated functions, PredefinedExpr " + "should have a function name."); + return makeLoc(getRegionManager().getStringRegion(PE->getFunctionName())); + } + // Fast-path some expressions to avoid the overhead of going through the AST's // constant evaluator case Stmt::CharacterLiteralClass: { @@ -377,8 +386,8 @@ Optional<SVal> SValBuilder::getConstantVal(const Expr *E) { SVal SValBuilder::makeSymExprValNN(BinaryOperator::Opcode Op, NonLoc LHS, NonLoc RHS, QualType ResultTy) { - const SymExpr *symLHS = LHS.getAsSymExpr(); - const SymExpr *symRHS = RHS.getAsSymExpr(); + SymbolRef symLHS = LHS.getAsSymbol(); + SymbolRef symRHS = RHS.getAsSymbol(); // TODO: When the Max Complexity is reached, we should conjure a symbol // instead of generating an Unknown value and propagate the taint info to it. @@ -492,7 +501,7 @@ SVal SValBuilder::evalIntegralCast(ProgramStateRef state, SVal val, if (getContext().getTypeSize(castTy) >= getContext().getTypeSize(originalTy)) return evalCast(val, castTy, originalTy); - const SymExpr *se = val.getAsSymbolicExpression(); + SymbolRef se = val.getAsSymbol(); if (!se) // Let evalCast handle non symbolic expressions. return evalCast(val, castTy, originalTy); diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SVals.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SVals.cpp index 9b5de6c3eb92..252596887e4f 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SVals.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SVals.cpp @@ -84,16 +84,12 @@ const FunctionDecl *SVal::getAsFunctionDecl() const { /// the first symbolic parent region is returned. SymbolRef SVal::getAsLocSymbol(bool IncludeBaseRegions) const { // FIXME: should we consider SymbolRef wrapped in CodeTextRegion? - if (Optional<nonloc::LocAsInteger> X = getAs<nonloc::LocAsInteger>()) - return X->getLoc().getAsLocSymbol(IncludeBaseRegions); - - if (Optional<loc::MemRegionVal> X = getAs<loc::MemRegionVal>()) { - const MemRegion *R = X->getRegion(); - if (const SymbolicRegion *SymR = IncludeBaseRegions ? - R->getSymbolicBase() : - dyn_cast<SymbolicRegion>(R->StripCasts())) + if (const MemRegion *R = getAsRegion()) + if (const SymbolicRegion *SymR = + IncludeBaseRegions ? R->getSymbolicBase() + : dyn_cast<SymbolicRegion>(R->StripCasts())) return SymR->getSymbol(); - } + return nullptr; } @@ -116,8 +112,6 @@ SymbolRef SVal::getLocSymbolInBase() const { return nullptr; } -// TODO: The next 3 functions have to be simplified. - /// If this SVal wraps a symbol return that SymbolRef. /// Otherwise, return 0. /// @@ -132,22 +126,6 @@ SymbolRef SVal::getAsSymbol(bool IncludeBaseRegions) const { return getAsLocSymbol(IncludeBaseRegions); } -/// getAsSymbolicExpression - If this Sval wraps a symbolic expression then -/// return that expression. Otherwise return NULL. -const SymExpr *SVal::getAsSymbolicExpression() const { - if (Optional<nonloc::SymbolVal> X = getAs<nonloc::SymbolVal>()) - return X->getSymbol(); - - return getAsSymbol(); -} - -const SymExpr* SVal::getAsSymExpr() const { - const SymExpr* Sym = getAsSymbol(); - if (!Sym) - Sym = getAsSymbolicExpression(); - return Sym; -} - const MemRegion *SVal::getAsRegion() const { if (Optional<loc::MemRegionVal> X = getAs<loc::MemRegionVal>()) return X->getRegion(); @@ -175,18 +153,18 @@ bool nonloc::PointerToMember::isNullMemberPointer() const { return getPTMData().isNull(); } -const DeclaratorDecl *nonloc::PointerToMember::getDecl() const { +const NamedDecl *nonloc::PointerToMember::getDecl() const { const auto PTMD = this->getPTMData(); if (PTMD.isNull()) return nullptr; - const DeclaratorDecl *DD = nullptr; - if (PTMD.is<const DeclaratorDecl *>()) - DD = PTMD.get<const DeclaratorDecl *>(); + const NamedDecl *ND = nullptr; + if (PTMD.is<const NamedDecl *>()) + ND = PTMD.get<const NamedDecl *>(); else - DD = PTMD.get<const PointerToMemberData *>()->getDeclaratorDecl(); + ND = PTMD.get<const PointerToMemberData *>()->getDeclaratorDecl(); - return DD; + return ND; } //===----------------------------------------------------------------------===// @@ -203,14 +181,14 @@ nonloc::CompoundVal::iterator nonloc::CompoundVal::end() const { nonloc::PointerToMember::iterator nonloc::PointerToMember::begin() const { const PTMDataType PTMD = getPTMData(); - if (PTMD.is<const DeclaratorDecl *>()) + if (PTMD.is<const NamedDecl *>()) return {}; return PTMD.get<const PointerToMemberData *>()->begin(); } nonloc::PointerToMember::iterator nonloc::PointerToMember::end() const { const PTMDataType PTMD = getPTMData(); - if (PTMD.is<const DeclaratorDecl *>()) + if (PTMD.is<const NamedDecl *>()) return {}; return PTMD.get<const PointerToMemberData *>()->end(); } diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp index 8c2e85601576..f93d04ccd61a 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp @@ -14,7 +14,6 @@ #include "clang/Basic/FileManager.h" #include "clang/Basic/Version.h" #include "clang/Lex/Preprocessor.h" -#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" #include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringMap.h" @@ -32,8 +31,7 @@ class SarifDiagnostics : public PathDiagnosticConsumer { const LangOptions &LO; public: - SarifDiagnostics(AnalyzerOptions &, const std::string &Output, - const LangOptions &LO) + SarifDiagnostics(const std::string &Output, const LangOptions &LO) : OutputFile(Output), LO(LO) {} ~SarifDiagnostics() override = default; @@ -48,7 +46,7 @@ public: } // end anonymous namespace void ento::createSarifDiagnosticConsumer( - AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C, + PathDiagnosticConsumerOptions DiagOpts, PathDiagnosticConsumers &C, const std::string &Output, const Preprocessor &PP, const cross_tu::CrossTranslationUnitContext &CTU) { @@ -56,8 +54,9 @@ void ento::createSarifDiagnosticConsumer( if (Output.empty()) return; - C.push_back(new SarifDiagnostics(AnalyzerOpts, Output, PP.getLangOpts())); - createTextMinimalPathDiagnosticConsumer(AnalyzerOpts, C, Output, PP, CTU); + C.push_back(new SarifDiagnostics(Output, PP.getLangOpts())); + createTextMinimalPathDiagnosticConsumer(std::move(DiagOpts), C, Output, PP, + CTU); } static StringRef getFileName(const FileEntry &FE) { @@ -160,9 +159,8 @@ static unsigned int adjustColumnPos(const SourceManager &SM, SourceLocation Loc, assert(LocInfo.second > SM.getExpansionColumnNumber(Loc) && "position in file is before column number?"); - bool InvalidBuffer = false; - const MemoryBuffer *Buf = SM.getBuffer(LocInfo.first, &InvalidBuffer); - assert(!InvalidBuffer && "got an invalid buffer for the location's file"); + Optional<MemoryBufferRef> Buf = SM.getBufferOrNone(LocInfo.first); + assert(Buf && "got an invalid buffer for the location's file"); assert(Buf->getBufferSize() >= (LocInfo.second + TokenLen) && "token extends past end of buffer?"); diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp index 3709106ad44c..f96974f97dcc 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp @@ -57,7 +57,7 @@ ProgramStateRef SimpleConstraintManager::assumeAux(ProgramStateRef State, // SymIntExprs. if (!canReasonAbout(Cond)) { // Just add the constraint to the expression without trying to simplify. - SymbolRef Sym = Cond.getAsSymExpr(); + SymbolRef Sym = Cond.getAsSymbol(); assert(Sym); return assumeSymUnsupported(State, Sym, Assumption); } @@ -101,7 +101,7 @@ ProgramStateRef SimpleConstraintManager::assumeInclusiveRange( if (!canReasonAbout(Value)) { // Just add the constraint to the expression without trying to simplify. - SymbolRef Sym = Value.getAsSymExpr(); + SymbolRef Sym = Value.getAsSymbol(); assert(Sym); return assumeSymInclusiveRange(State, Sym, From, To, InRange); } diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp index 2e269f6a596e..facadaf1225f 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp @@ -86,7 +86,7 @@ SVal SimpleSValBuilder::evalCastFromNonLoc(NonLoc val, QualType castTy) { return makeLocAsInteger(LI->getLoc(), castSize); } - if (const SymExpr *se = val.getAsSymbolicExpression()) { + if (SymbolRef se = val.getAsSymbol()) { QualType T = Context.getCanonicalType(se->getType()); // If types are the same or both are integers, ignore the cast. // FIXME: Remove this hack when we support symbolic truncation/extension. @@ -1106,19 +1106,28 @@ SVal SimpleSValBuilder::evalBinOpLL(ProgramStateRef state, } SVal SimpleSValBuilder::evalBinOpLN(ProgramStateRef state, - BinaryOperator::Opcode op, - Loc lhs, NonLoc rhs, QualType resultTy) { + BinaryOperator::Opcode op, Loc lhs, + NonLoc rhs, QualType resultTy) { if (op >= BO_PtrMemD && op <= BO_PtrMemI) { if (auto PTMSV = rhs.getAs<nonloc::PointerToMember>()) { if (PTMSV->isNullMemberPointer()) return UndefinedVal(); - if (const FieldDecl *FD = PTMSV->getDeclAs<FieldDecl>()) { + + auto getFieldLValue = [&](const auto *FD) -> SVal { SVal Result = lhs; for (const auto &I : *PTMSV) Result = StateMgr.getStoreManager().evalDerivedToBase( - Result, I->getType(),I->isVirtual()); + Result, I->getType(), I->isVirtual()); + return state->getLValue(FD, Result); + }; + + if (const auto *FD = PTMSV->getDeclAs<FieldDecl>()) { + return getFieldLValue(FD); + } + if (const auto *FD = PTMSV->getDeclAs<IndirectFieldDecl>()) { + return getFieldLValue(FD); } } diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp index 6ca7aec9caec..79a8eef30576 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp @@ -14,6 +14,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Expr.h" +#include "clang/AST/StmtObjC.h" #include "clang/Analysis/Analyses/LiveVariables.h" #include "clang/Analysis/AnalysisDeclContext.h" #include "clang/Basic/LLVM.h" @@ -34,6 +35,12 @@ using namespace ento; void SymExpr::anchor() {} +StringRef SymbolConjured::getKindStr() const { return "conj_$"; } +StringRef SymbolDerived::getKindStr() const { return "derived_$"; } +StringRef SymbolExtent::getKindStr() const { return "extent_$"; } +StringRef SymbolMetadata::getKindStr() const { return "meta_$"; } +StringRef SymbolRegionValue::getKindStr() const { return "reg_$"; } + LLVM_DUMP_METHOD void SymExpr::dump() const { dumpToStream(llvm::errs()); } void BinarySymExpr::dumpToStreamImpl(raw_ostream &OS, const SymExpr *Sym) { @@ -64,7 +71,7 @@ void SymbolCast::dumpToStream(raw_ostream &os) const { } void SymbolConjured::dumpToStream(raw_ostream &os) const { - os << "conj_$" << getSymbolID() << '{' << T.getAsString() << ", LC" + os << getKindStr() << getSymbolID() << '{' << T.getAsString() << ", LC" << LCtx->getID(); if (S) os << ", S" << S->getID(LCtx->getDecl()->getASTContext()); @@ -74,24 +81,24 @@ void SymbolConjured::dumpToStream(raw_ostream &os) const { } void SymbolDerived::dumpToStream(raw_ostream &os) const { - os << "derived_$" << getSymbolID() << '{' - << getParentSymbol() << ',' << getRegion() << '}'; + os << getKindStr() << getSymbolID() << '{' << getParentSymbol() << ',' + << getRegion() << '}'; } void SymbolExtent::dumpToStream(raw_ostream &os) const { - os << "extent_$" << getSymbolID() << '{' << getRegion() << '}'; + os << getKindStr() << getSymbolID() << '{' << getRegion() << '}'; } void SymbolMetadata::dumpToStream(raw_ostream &os) const { - os << "meta_$" << getSymbolID() << '{' - << getRegion() << ',' << T.getAsString() << '}'; + os << getKindStr() << getSymbolID() << '{' << getRegion() << ',' + << T.getAsString() << '}'; } void SymbolData::anchor() {} void SymbolRegionValue::dumpToStream(raw_ostream &os) const { - os << "reg_$" << getSymbolID() - << '<' << getType().getAsString() << ' ' << R << '>'; + os << getKindStr() << getSymbolID() << '<' << getType().getAsString() << ' ' + << R << '>'; } bool SymExpr::symbol_iterator::operator==(const symbol_iterator &X) const { @@ -482,7 +489,7 @@ bool SymbolReaper::isLive(SymbolRef sym) { } bool -SymbolReaper::isLive(const Stmt *ExprVal, const LocationContext *ELCtx) const { +SymbolReaper::isLive(const Expr *ExprVal, const LocationContext *ELCtx) const { if (LCtx == nullptr) return false; @@ -494,7 +501,8 @@ SymbolReaper::isLive(const Stmt *ExprVal, const LocationContext *ELCtx) const { return true; } - // If no statement is provided, everything is this and parent contexts is live. + // If no statement is provided, everything in this and parent contexts is + // live. if (!Loc) return true; diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/TextDiagnostics.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/TextDiagnostics.cpp index f4c7e5978e19..ae2bad7ee77c 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/TextDiagnostics.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/TextDiagnostics.cpp @@ -34,20 +34,17 @@ namespace { /// type to the standard error, or to to compliment many others. Emits detailed /// diagnostics in textual format for the 'text' output type. class TextDiagnostics : public PathDiagnosticConsumer { + PathDiagnosticConsumerOptions DiagOpts; DiagnosticsEngine &DiagEng; const LangOptions &LO; - const bool IncludePath = false; - const bool ShouldEmitAsError = false; - const bool ApplyFixIts = false; - const bool ShouldDisplayCheckerName = false; + bool ShouldDisplayPathNotes; public: - TextDiagnostics(DiagnosticsEngine &DiagEng, const LangOptions &LO, - bool ShouldIncludePath, const AnalyzerOptions &AnOpts) - : DiagEng(DiagEng), LO(LO), IncludePath(ShouldIncludePath), - ShouldEmitAsError(AnOpts.AnalyzerWerror), - ApplyFixIts(AnOpts.ShouldApplyFixIts), - ShouldDisplayCheckerName(AnOpts.ShouldDisplayCheckerNameForText) {} + TextDiagnostics(PathDiagnosticConsumerOptions DiagOpts, + DiagnosticsEngine &DiagEng, const LangOptions &LO, + bool ShouldDisplayPathNotes) + : DiagOpts(std::move(DiagOpts)), DiagEng(DiagEng), LO(LO), + ShouldDisplayPathNotes(ShouldDisplayPathNotes) {} ~TextDiagnostics() override {} StringRef getName() const override { return "TextDiagnostics"; } @@ -56,13 +53,13 @@ public: bool supportsCrossFileDiagnostics() const override { return true; } PathGenerationScheme getGenerationScheme() const override { - return IncludePath ? Minimal : None; + return ShouldDisplayPathNotes ? Minimal : None; } void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags, FilesMade *filesMade) override { unsigned WarnID = - ShouldEmitAsError + DiagOpts.ShouldDisplayWarningsAsErrors ? DiagEng.getCustomDiagID(DiagnosticsEngine::Error, "%0") : DiagEng.getCustomDiagID(DiagnosticsEngine::Warning, "%0"); unsigned NoteID = DiagEng.getCustomDiagID(DiagnosticsEngine::Note, "%0"); @@ -72,7 +69,7 @@ public: auto reportPiece = [&](unsigned ID, FullSourceLoc Loc, StringRef String, ArrayRef<SourceRange> Ranges, ArrayRef<FixItHint> Fixits) { - if (!ApplyFixIts) { + if (!DiagOpts.ShouldApplyFixIts) { DiagEng.Report(Loc, ID) << String << Ranges << Fixits; return; } @@ -92,9 +89,10 @@ public: E = Diags.end(); I != E; ++I) { const PathDiagnostic *PD = *I; - std::string WarningMsg = - (ShouldDisplayCheckerName ? " [" + PD->getCheckerName() + "]" : "") - .str(); + std::string WarningMsg = (DiagOpts.ShouldDisplayDiagnosticName + ? " [" + PD->getCheckerName() + "]" + : "") + .str(); reportPiece(WarnID, PD->getLocation().asLocation(), (PD->getShortDescription() + WarningMsg).str(), @@ -110,7 +108,7 @@ public: Piece->getFixits()); } - if (!IncludePath) + if (!ShouldDisplayPathNotes) continue; // Then, add the path notes if necessary. @@ -125,7 +123,7 @@ public: } } - if (!ApplyFixIts || Repls.empty()) + if (Repls.empty()) return; Rewriter Rewrite(SM, LO); @@ -139,18 +137,19 @@ public: } // end anonymous namespace void ento::createTextPathDiagnosticConsumer( - AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C, + PathDiagnosticConsumerOptions DiagOpts, PathDiagnosticConsumers &C, const std::string &Prefix, const clang::Preprocessor &PP, const cross_tu::CrossTranslationUnitContext &CTU) { - C.emplace_back(new TextDiagnostics(PP.getDiagnostics(), PP.getLangOpts(), - /*ShouldIncludePath*/ true, AnalyzerOpts)); + C.emplace_back(new TextDiagnostics(std::move(DiagOpts), PP.getDiagnostics(), + PP.getLangOpts(), + /*ShouldDisplayPathNotes=*/true)); } void ento::createTextMinimalPathDiagnosticConsumer( - AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C, + PathDiagnosticConsumerOptions DiagOpts, PathDiagnosticConsumers &C, const std::string &Prefix, const clang::Preprocessor &PP, const cross_tu::CrossTranslationUnitContext &CTU) { - C.emplace_back(new TextDiagnostics(PP.getDiagnostics(), PP.getLangOpts(), - /*ShouldIncludePath*/ false, - AnalyzerOpts)); + C.emplace_back(new TextDiagnostics(std::move(DiagOpts), PP.getDiagnostics(), + PP.getLangOpts(), + /*ShouldDisplayPathNotes=*/false)); } diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp index 392049e21c6e..f2a19b2ccc90 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp @@ -150,7 +150,7 @@ public: break; #define ANALYSIS_DIAGNOSTICS(NAME, CMDFLAG, DESC, CREATEFN) \ case PD_##NAME: \ - CREATEFN(*Opts.get(), PathConsumers, OutDir, PP, CTU); \ + CREATEFN(Opts->getDiagOpts(), PathConsumers, OutDir, PP, CTU); \ break; #include "clang/StaticAnalyzer/Core/Analyses.def" default: @@ -476,7 +476,7 @@ void AnalysisConsumer::HandleDeclsCallGraph(const unsigned LocalTUDeclsSize) { static bool isBisonFile(ASTContext &C) { const SourceManager &SM = C.getSourceManager(); FileID FID = SM.getMainFileID(); - StringRef Buffer = SM.getBuffer(FID)->getBuffer(); + StringRef Buffer = SM.getBufferOrFake(FID).getBuffer(); if (Buffer.startswith("/* A Bison parser, made by")) return true; return false; |