Index: include/clang/StaticAnalyzer/Checkers/CheckerBase.td =================================================================== --- include/clang/StaticAnalyzer/Checkers/CheckerBase.td +++ include/clang/StaticAnalyzer/Checkers/CheckerBase.td @@ -37,8 +37,23 @@ /// Note that a checker has a name (e.g.: 'NullDereference'), and a fullname, /// that is autogenerated with the help of the ParentPackage field, that also /// includes package names (e.g.: 'core.NullDereference'). +/// Example: +/// def DereferenceChecker : Checker<"NullDereference">, +/// HelpText<"Check for dereferences of null pointers">; class Checker { - string CheckerName = name; - string HelpText; - Package ParentPackage; + string CheckerName = name; + string HelpText; + Package ParentPackage; + list Dependencies; +} + +/// Describes dependencies in between checkers. For example, InnerPointerChecker +/// relies on information MallocBase gathers. +/// Example: +/// def InnerPointerChecker : Checker<"InnerPointer">, +/// HelpText<"Check for inner pointers of C++ containers used after " +/// "re/deallocation">, +/// Dependencies<[MallocBase]>; +class Dependencies Deps = []> { + list Dependencies = Deps; } Index: include/clang/StaticAnalyzer/Checkers/Checkers.td =================================================================== --- include/clang/StaticAnalyzer/Checkers/Checkers.td +++ include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -254,22 +254,111 @@ } // end "core.uninitialized" +//===----------------------------------------------------------------------===// +// Unix API checkers. +//===----------------------------------------------------------------------===// + +let ParentPackage = UnixAlpha in { + +def ChrootChecker : Checker<"Chroot">, + HelpText<"Check improper use of chroot">; + +def PthreadLockChecker : Checker<"PthreadLock">, + HelpText<"Simple lock -> unlock checker">; + +def StreamChecker : Checker<"Stream">, + HelpText<"Check stream handling functions">; + +def SimpleStreamChecker : Checker<"SimpleStream">, + HelpText<"Check for misuses of stream APIs">; + +def BlockInCriticalSectionChecker : Checker<"BlockInCriticalSection">, + HelpText<"Check for calls to blocking functions inside a critical section">; + +} // end "alpha.unix" + +let ParentPackage = CString in { + +def CStringBase : Checker<"CStringBase">, + HelpText<"The base of several CString related checkers. On it's own it emits " + "no reports, but adds valuable information to the analysis when " + "enabled.">; + +def CStringNullArg : Checker<"NullArg">, + HelpText<"Check for null pointers being passed as arguments to C string " + "functions">, + Dependencies<[CStringBase]>; + +def CStringSyntaxChecker : Checker<"BadSizeArg">, + HelpText<"Check the size argument passed into C string functions for common " + "erroneous patterns">, + Dependencies<[CStringBase]>; + +} // end "unix.cstring" + +let ParentPackage = CStringAlpha in { + +def CStringOutOfBounds : Checker<"OutOfBounds">, + HelpText<"Check for out-of-bounds access in string functions">, + Dependencies<[CStringBase]>; + +def CStringBufferOverlap : Checker<"BufferOverlap">, + HelpText<"Checks for overlap in two buffer arguments">, + Dependencies<[CStringBase]>; + +def CStringNotNullTerm : Checker<"NotNullTerminated">, + HelpText<"Check for arguments which are not null-terminating strings">, + Dependencies<[CStringBase]>; + +} // end "alpha.unix.cstring" + +let ParentPackage = Unix in { + +def UnixAPIMisuseChecker : Checker<"API">, + HelpText<"Check calls to various UNIX/Posix functions">; + +def MallocBase: Checker<"MallocBase">, + HelpText<"The base of several malloc() related checkers. On it's own it " + "emits no reports, but adds valuable information to the analysis " + "when enabled.">, + Dependencies<[CStringBase]>; + +def MallocChecker: Checker<"Malloc">, + HelpText<"Check for memory leaks, double free, and use-after-free problems. " + "Traces memory managed by malloc()/free().">, + Dependencies<[MallocBase]>; + +def MallocSizeofChecker : Checker<"MallocSizeof">, + HelpText<"Check for dubious malloc arguments involving sizeof">; + +def MismatchedDeallocatorChecker : Checker<"MismatchedDeallocator">, + HelpText<"Check for mismatched deallocators.">, + Dependencies<[MallocBase]>; + +def VforkChecker : Checker<"Vfork">, + HelpText<"Check for proper usage of vfork">; + +} // end "unix" + //===----------------------------------------------------------------------===// // C++ checkers. //===----------------------------------------------------------------------===// let ParentPackage = Cplusplus in { -def InnerPointerChecker : Checker<"InnerPointer">, - HelpText<"Check for inner pointers of C++ containers used after " - "re/deallocation">; - def NewDeleteChecker : Checker<"NewDelete">, HelpText<"Check for double-free and use-after-free problems. Traces memory " - "managed by new/delete.">; + "managed by new/delete.">, + Dependencies<[MallocBase]>; + +def InnerPointerChecker : Checker<"InnerPointer">, + HelpText<"Check for inner pointers of C++ containers used after " + "re/deallocation">, + Dependencies<[MallocBase]>; def NewDeleteLeaksChecker : Checker<"NewDeleteLeaks">, - HelpText<"Check for memory leaks. Traces memory managed by new/delete.">; + HelpText<"Check for memory leaks. Traces memory managed by new/delete.">, + Dependencies<[NewDeleteChecker]>; def CXXSelfAssignmentChecker : Checker<"SelfAssignment">, HelpText<"Checks C++ copy and move assignment operators for self assignment">; @@ -430,74 +519,6 @@ } // end "alpha.security.taint" -//===----------------------------------------------------------------------===// -// Unix API checkers. -//===----------------------------------------------------------------------===// - -let ParentPackage = Unix in { - -def UnixAPIMisuseChecker : Checker<"API">, - HelpText<"Check calls to various UNIX/Posix functions">; - -def MallocChecker: Checker<"Malloc">, - HelpText<"Check for memory leaks, double free, and use-after-free problems. " - "Traces memory managed by malloc()/free().">; - -def MallocSizeofChecker : Checker<"MallocSizeof">, - HelpText<"Check for dubious malloc arguments involving sizeof">; - -def MismatchedDeallocatorChecker : Checker<"MismatchedDeallocator">, - HelpText<"Check for mismatched deallocators.">; - -def VforkChecker : Checker<"Vfork">, - HelpText<"Check for proper usage of vfork">; - -} // end "unix" - -let ParentPackage = UnixAlpha in { - -def ChrootChecker : Checker<"Chroot">, - HelpText<"Check improper use of chroot">; - -def PthreadLockChecker : Checker<"PthreadLock">, - HelpText<"Simple lock -> unlock checker">; - -def StreamChecker : Checker<"Stream">, - HelpText<"Check stream handling functions">; - -def SimpleStreamChecker : Checker<"SimpleStream">, - HelpText<"Check for misuses of stream APIs">; - -def BlockInCriticalSectionChecker : Checker<"BlockInCriticalSection">, - HelpText<"Check for calls to blocking functions inside a critical section">; - -} // end "alpha.unix" - -let ParentPackage = CString in { - -def CStringNullArg : Checker<"NullArg">, - HelpText<"Check for null pointers being passed as arguments to C string " - "functions">; - -def CStringSyntaxChecker : Checker<"BadSizeArg">, - HelpText<"Check the size argument passed into C string functions for common " - "erroneous patterns">; - -} // end "unix.cstring" - -let ParentPackage = CStringAlpha in { - -def CStringOutOfBounds : Checker<"OutOfBounds">, - HelpText<"Check for out-of-bounds access in string functions">; - -def CStringBufferOverlap : Checker<"BufferOverlap">, - HelpText<"Checks for overlap in two buffer arguments">; - -def CStringNotNullTerm : Checker<"NotNullTerminated">, - HelpText<"Check for arguments which are not null-terminating strings">; - -} // end "alpha.unix.cstring" - //===----------------------------------------------------------------------===// // Mac OS X, Cocoa, and Core Foundation checkers. //===----------------------------------------------------------------------===// Index: include/clang/StaticAnalyzer/Frontend/CheckerRegistry.h =================================================================== --- include/clang/StaticAnalyzer/Frontend/CheckerRegistry.h +++ include/clang/StaticAnalyzer/Frontend/CheckerRegistry.h @@ -91,6 +91,7 @@ InitializationFunction Initialize; StringRef FullName; StringRef Desc; + llvm::SmallVector Dependencies; CheckerInfo(InitializationFunction fn, StringRef name, StringRef desc) : Initialize(fn), FullName(name), Desc(desc) {} @@ -120,6 +121,28 @@ addChecker(&CheckerRegistry::initializeManager, fullName, desc); } + /// Makes the checker with the full name \p fullName depends on the checker + /// called \p dependency. + void addDependency(StringRef fullName, StringRef dependency) { + auto CheckerThatNeedsDeps = + [&fullName](const CheckerInfo &Chk) { return Chk.FullName == fullName; }; + auto Dependency = + [&dependency](const CheckerInfo &Chk) { + return Chk.FullName == dependency; + }; + + auto CheckerIt = llvm::find_if(Checkers, CheckerThatNeedsDeps); + assert(CheckerIt != Checkers.end() && + "Failed to find the checker while attempting to set up it's " + "dependencies!"); + + auto DependencyIt = llvm::find_if(Checkers, Dependency); + assert(DependencyIt != Checkers.end() && + "Failed to find the dependency of a checker!"); + + CheckerIt->Dependencies.push_back(&*DependencyIt); + } + /// Initializes a CheckerManager by calling the initialization functions for /// all checkers specified by the given CheckerOptInfo list. The order of this /// list is significant; later options can be used to reverse earlier ones. @@ -138,8 +161,9 @@ private: CheckerInfoSet getEnabledCheckers(const AnalyzerOptions &Opts) const; - mutable CheckerInfoList Checkers; - mutable llvm::StringMap Packages; + CheckerInfoList Checkers; + llvm::StringMap Packages; + DiagnosticsEngine &Diags; }; Index: lib/StaticAnalyzer/Checkers/CMakeLists.txt =================================================================== --- lib/StaticAnalyzer/Checkers/CMakeLists.txt +++ lib/StaticAnalyzer/Checkers/CMakeLists.txt @@ -39,7 +39,7 @@ GenericTaintChecker.cpp GTestChecker.cpp IdenticalExprChecker.cpp - InnerPointerChecker.cpp + InnerPointer/InnerPointerChecker.cpp IteratorChecker.cpp IvarInvalidationChecker.cpp LLVMConventionsChecker.cpp Index: lib/StaticAnalyzer/Checkers/CStringChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/CStringChecker.cpp +++ lib/StaticAnalyzer/Checkers/CStringChecker.cpp @@ -13,7 +13,6 @@ //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" -#include "InterCheckerAPI.h" #include "clang/Basic/CharInfo.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" @@ -2419,6 +2418,6 @@ REGISTER_CHECKER(CStringBufferOverlap) REGISTER_CHECKER(CStringNotNullTerm) - void ento::registerCStringCheckerBasic(CheckerManager &Mgr) { - Mgr.registerChecker(); - } +void ento::registerCStringBase(CheckerManager &Mgr) { + Mgr.registerChecker(); +} Index: lib/StaticAnalyzer/Checkers/InnerPointer/InnerPointerChecker.h =================================================================== --- /dev/null +++ lib/StaticAnalyzer/Checkers/InnerPointer/InnerPointerChecker.h @@ -0,0 +1,70 @@ +//=== InnerPointerChecker.cpp -------------------------------------*- C++ -*--// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// For a documentation, refer to InnerPointerChecker.cpp. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" + +namespace clang { +namespace ento { + +class InnerPointerChecker + : public Checker { + + CallDescription AppendFn, AssignFn, ClearFn, CStrFn, DataFn, EraseFn, + InsertFn, PopBackFn, PushBackFn, ReplaceFn, ReserveFn, ResizeFn, + ShrinkToFitFn, SwapFn; + +public: + InnerPointerChecker() + : AppendFn({"std", "basic_string", "append"}), + AssignFn({"std", "basic_string", "assign"}), + ClearFn({"std", "basic_string", "clear"}), + CStrFn({"std", "basic_string", "c_str"}), + DataFn({"std", "basic_string", "data"}), + EraseFn({"std", "basic_string", "erase"}), + InsertFn({"std", "basic_string", "insert"}), + PopBackFn({"std", "basic_string", "pop_back"}), + PushBackFn({"std", "basic_string", "push_back"}), + ReplaceFn({"std", "basic_string", "replace"}), + ReserveFn({"std", "basic_string", "reserve"}), + ResizeFn({"std", "basic_string", "resize"}), + ShrinkToFitFn({"std", "basic_string", "shrink_to_fit"}), + SwapFn({"std", "basic_string", "swap"}) {} + + /// Check whether the called member function potentially invalidates + /// pointers referring to the container object's inner buffer. + bool isInvalidatingMemberFunction(const CallEvent &Call) const; + + /// Mark pointer symbols associated with the given memory region released + /// in the program state. + void markPtrSymbolsReleased(const CallEvent &Call, ProgramStateRef State, + const MemRegion *ObjRegion, + CheckerContext &C) const; + + /// Standard library functions that take a non-const `basic_string` argument by + /// reference may invalidate its inner pointers. Check for these cases and + /// mark the pointers released. + void checkFunctionArguments(const CallEvent &Call, ProgramStateRef State, + CheckerContext &C) const; + + /// Record the connection between raw pointers referring to a container + /// object's inner buffer and the object's memory region in the program state. + /// Mark potentially invalidated pointers released. + void checkPostCall(const CallEvent &Call, CheckerContext &C) const; + + /// Clean up the program state map. + void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; +}; + +} // end of namespace ento +} // end of namespace clang Index: lib/StaticAnalyzer/Checkers/InnerPointer/InnerPointerChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/InnerPointer/InnerPointerChecker.cpp +++ lib/StaticAnalyzer/Checkers/InnerPointer/InnerPointerChecker.cpp @@ -13,10 +13,9 @@ // //===----------------------------------------------------------------------===// -#include "AllocationState.h" +#include "InnerPointerChecker.h" +#include "../AllocationState.h" #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" -#include "InterCheckerAPI.h" -#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" @@ -32,84 +31,35 @@ namespace { -class InnerPointerChecker - : public Checker { - - CallDescription AppendFn, AssignFn, ClearFn, CStrFn, DataFn, EraseFn, - InsertFn, PopBackFn, PushBackFn, ReplaceFn, ReserveFn, ResizeFn, - ShrinkToFitFn, SwapFn; +class InnerPointerBRVisitor : public BugReporterVisitor { + SymbolRef PtrToBuf; public: - class InnerPointerBRVisitor : public BugReporterVisitor { - SymbolRef PtrToBuf; - - public: - InnerPointerBRVisitor(SymbolRef Sym) : PtrToBuf(Sym) {} + InnerPointerBRVisitor(SymbolRef Sym) : PtrToBuf(Sym) {} - static void *getTag() { - static int Tag = 0; - return &Tag; - } + static void *getTag() { + static int Tag = 0; + return &Tag; + } - void Profile(llvm::FoldingSetNodeID &ID) const override { - ID.AddPointer(getTag()); - } + void Profile(llvm::FoldingSetNodeID &ID) const override { + ID.AddPointer(getTag()); + } - virtual std::shared_ptr VisitNode(const ExplodedNode *N, - BugReporterContext &BRC, - BugReport &BR) override; - - // FIXME: Scan the map once in the visitor's constructor and do a direct - // lookup by region. - bool isSymbolTracked(ProgramStateRef State, SymbolRef Sym) { - RawPtrMapTy Map = State->get(); - for (const auto Entry : Map) { - if (Entry.second.contains(Sym)) - return true; - } - return false; + virtual std::shared_ptr VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + BugReport &BR) override; + + // FIXME: Scan the map once in the visitor's constructor and do a direct + // lookup by region. + bool isSymbolTracked(ProgramStateRef State, SymbolRef Sym) { + RawPtrMapTy Map = State->get(); + for (const auto Entry : Map) { + if (Entry.second.contains(Sym)) + return true; } - }; - - InnerPointerChecker() - : AppendFn({"std", "basic_string", "append"}), - AssignFn({"std", "basic_string", "assign"}), - ClearFn({"std", "basic_string", "clear"}), - CStrFn({"std", "basic_string", "c_str"}), - DataFn({"std", "basic_string", "data"}), - EraseFn({"std", "basic_string", "erase"}), - InsertFn({"std", "basic_string", "insert"}), - PopBackFn({"std", "basic_string", "pop_back"}), - PushBackFn({"std", "basic_string", "push_back"}), - ReplaceFn({"std", "basic_string", "replace"}), - ReserveFn({"std", "basic_string", "reserve"}), - ResizeFn({"std", "basic_string", "resize"}), - ShrinkToFitFn({"std", "basic_string", "shrink_to_fit"}), - SwapFn({"std", "basic_string", "swap"}) {} - - /// Check whether the called member function potentially invalidates - /// pointers referring to the container object's inner buffer. - bool isInvalidatingMemberFunction(const CallEvent &Call) const; - - /// Mark pointer symbols associated with the given memory region released - /// in the program state. - void markPtrSymbolsReleased(const CallEvent &Call, ProgramStateRef State, - const MemRegion *ObjRegion, - CheckerContext &C) const; - - /// Standard library functions that take a non-const `basic_string` argument by - /// reference may invalidate its inner pointers. Check for these cases and - /// mark the pointers released. - void checkFunctionArguments(const CallEvent &Call, ProgramStateRef State, - CheckerContext &C) const; - - /// Record the connection between raw pointers referring to a container - /// object's inner buffer and the object's memory region in the program state. - /// Mark potentially invalidated pointers released. - void checkPostCall(const CallEvent &Call, CheckerContext &C) const; - - /// Clean up the program state map. - void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; + return false; + } }; } // end anonymous namespace @@ -262,7 +212,7 @@ namespace allocation_state { std::unique_ptr getInnerPointerBRVisitor(SymbolRef Sym) { - return llvm::make_unique(Sym); + return llvm::make_unique(Sym); } const MemRegion *getContainerObjRegion(ProgramStateRef State, SymbolRef Sym) { @@ -280,9 +230,8 @@ } // end namespace clang std::shared_ptr -InnerPointerChecker::InnerPointerBRVisitor::VisitNode(const ExplodedNode *N, - BugReporterContext &BRC, - BugReport &) { +InnerPointerBRVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC, + BugReport &) { if (!isSymbolTracked(N->getState(), PtrToBuf) || isSymbolTracked(N->getFirstPred()->getState(), PtrToBuf)) return nullptr; @@ -306,7 +255,5 @@ nullptr); } -void ento::registerInnerPointerChecker(CheckerManager &Mgr) { - registerInnerPointerCheckerAux(Mgr); - Mgr.registerChecker(); -} +// The register function for this checker is in MallocChecker.cpp, as it's +// closely tied to it. Index: lib/StaticAnalyzer/Checkers/InterCheckerAPI.h =================================================================== --- lib/StaticAnalyzer/Checkers/InterCheckerAPI.h +++ /dev/null @@ -1,27 +0,0 @@ -//==--- InterCheckerAPI.h ---------------------------------------*- C++ -*-==// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// This file allows introduction of checker dependencies. It contains APIs for -// inter-checker communications. -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_INTERCHECKERAPI_H -#define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_INTERCHECKERAPI_H -namespace clang { -class CheckerManager; - -namespace ento { - -/// Register the checker which evaluates CString API calls. -void registerCStringCheckerBasic(CheckerManager &Mgr); - -/// Register the part of MallocChecker connected to InnerPointerChecker. -void registerInnerPointerCheckerAux(CheckerManager &Mgr); - -}} -#endif /* INTERCHECKERAPI_H_ */ Index: lib/StaticAnalyzer/Checkers/MallocChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -13,8 +13,6 @@ //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" -#include "InterCheckerAPI.h" -#include "clang/AST/Attr.h" #include "clang/AST/ParentMap.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/TargetInfo.h" @@ -31,6 +29,7 @@ #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" #include "AllocationState.h" +#include "InnerPointer/InnerPointerChecker.h" #include #include @@ -190,6 +189,7 @@ /// In pessimistic mode, the checker assumes that it does not know which /// functions might free the memory. enum CheckKind { + CK_MallocBase, CK_MallocChecker, CK_NewDeleteChecker, CK_NewDeleteLeaksChecker, @@ -3081,40 +3081,8 @@ } // end namespace ento } // end namespace clang -void ento::registerNewDeleteLeaksChecker(CheckerManager &mgr) { - registerCStringCheckerBasic(mgr); - MallocChecker *checker = mgr.registerChecker(); - checker->IsOptimistic = mgr.getAnalyzerOptions().getCheckerBooleanOption( - "Optimistic", false, checker); - checker->ChecksEnabled[MallocChecker::CK_NewDeleteLeaksChecker] = true; - checker->CheckNames[MallocChecker::CK_NewDeleteLeaksChecker] = - mgr.getCurrentCheckName(); - // We currently treat NewDeleteLeaks checker as a subchecker of NewDelete - // checker. - if (!checker->ChecksEnabled[MallocChecker::CK_NewDeleteChecker]) { - checker->ChecksEnabled[MallocChecker::CK_NewDeleteChecker] = true; - // FIXME: This does not set the correct name, but without this workaround - // no name will be set at all. - checker->CheckNames[MallocChecker::CK_NewDeleteChecker] = - mgr.getCurrentCheckName(); - } -} - -// Intended to be used in InnerPointerChecker to register the part of -// MallocChecker connected to it. -void ento::registerInnerPointerCheckerAux(CheckerManager &mgr) { - registerCStringCheckerBasic(mgr); - MallocChecker *checker = mgr.registerChecker(); - checker->IsOptimistic = mgr.getAnalyzerOptions().getCheckerBooleanOption( - "Optimistic", false, checker); - checker->ChecksEnabled[MallocChecker::CK_InnerPointerChecker] = true; - checker->CheckNames[MallocChecker::CK_InnerPointerChecker] = - mgr.getCurrentCheckName(); -} - #define REGISTER_CHECKER(name) \ void ento::register##name(CheckerManager &mgr) { \ - registerCStringCheckerBasic(mgr); \ MallocChecker *checker = mgr.registerChecker(); \ checker->IsOptimistic = mgr.getAnalyzerOptions().getCheckerBooleanOption( \ "Optimistic", false, checker); \ @@ -3122,6 +3090,20 @@ checker->CheckNames[MallocChecker::CK_##name] = mgr.getCurrentCheckName(); \ } +REGISTER_CHECKER(MallocBase) REGISTER_CHECKER(MallocChecker) REGISTER_CHECKER(NewDeleteChecker) +REGISTER_CHECKER(NewDeleteLeaksChecker) REGISTER_CHECKER(MismatchedDeallocatorChecker) + +void ento::registerInnerPointerChecker(CheckerManager &mgr) { + // This checker first registers itself as well, which is why this registry + // function isn't generated. + mgr.registerChecker(); + MallocChecker *checker = mgr.registerChecker(); + checker->IsOptimistic = mgr.getAnalyzerOptions().getCheckerBooleanOption( + "Optimistic", false, checker); + checker->ChecksEnabled[MallocChecker::CK_InnerPointerChecker] = true; + checker->CheckNames[MallocChecker::CK_InnerPointerChecker] = + mgr.getCurrentCheckName(); +} Index: lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp =================================================================== --- lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp +++ lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp @@ -38,11 +38,18 @@ return strcmp(versionString, CLANG_ANALYZER_API_VERSION_STRING) == 0; } +static bool checkerNameLT(const CheckerRegistry::CheckerInfo &a, + const CheckerRegistry::CheckerInfo &b) { + return a.FullName < b.FullName; +} + CheckerRegistry::CheckerRegistry(ArrayRef plugins, DiagnosticsEngine &diags) : Diags(diags) { #define GET_CHECKERS + #define CHECKER(FULLNAME, CLASS, HELPTEXT) \ addChecker(register##CLASS, FULLNAME, HELPTEXT); + #include "clang/StaticAnalyzer/Checkers/Checkers.inc" #undef CHECKER #undef GET_CHECKERS @@ -76,15 +83,21 @@ if (registerPluginCheckers) registerPluginCheckers(*this); } -} -static constexpr char PackageSeparator = '.'; + llvm::sort(Checkers, checkerNameLT); -static bool checkerNameLT(const CheckerRegistry::CheckerInfo &a, - const CheckerRegistry::CheckerInfo &b) { - return a.FullName < b.FullName; +#define GET_CHECKER_DEPENDENCIES + +#define CHECKER_DEPENDENCY(FULLNAME, DEPENDENCY) \ + addDependency(FULLNAME, DEPENDENCY); + +#include "clang/StaticAnalyzer/Checkers/Checkers.inc" +#undef CHECKER_DEPENDENCY +#undef GET_CHECKER_DEPENDENCIES } +static constexpr char PackageSeparator = '.'; + static bool isInPackage(const CheckerRegistry::CheckerInfo &checker, StringRef packageName) { // Does the checker's full name have the package as a prefix? @@ -102,9 +115,17 @@ return false; } +static void collectDependencies( + CheckerRegistry::CheckerInfoSet &enabledCheckers, + const llvm::SmallVector &Deps) { + for (const CheckerRegistry::CheckerInfo *Dependency : Deps) { + enabledCheckers.insert(Dependency); + collectDependencies(enabledCheckers, Dependency->Dependencies); + } +} + CheckerRegistry::CheckerInfoSet CheckerRegistry::getEnabledCheckers( const AnalyzerOptions &Opts) const { - assert(std::is_sorted(Checkers.begin(), Checkers.end(), checkerNameLT) && "In order to efficiently gather checkers, this function expects them " "to be already sorted!"); @@ -114,7 +135,7 @@ for (const std::pair &opt : Opts.CheckersControlList) { // Use a binary search to find the possible start of the package. - CheckerRegistry::CheckerInfo packageInfo(nullptr, opt.first, ""); + CheckerInfo packageInfo(nullptr, opt.first, ""); auto firstRelatedChecker = std::lower_bound(Checkers.cbegin(), end, packageInfo, checkerNameLT); @@ -135,11 +156,18 @@ // Step through all the checkers in the package. for (auto lastRelatedChecker = firstRelatedChecker+size; - firstRelatedChecker != lastRelatedChecker; ++firstRelatedChecker) - if (opt.second) + firstRelatedChecker != lastRelatedChecker; ++firstRelatedChecker) { + if (opt.second) { + // Enable the checker. enabledCheckers.insert(&*firstRelatedChecker); - else + + // Recursively enable it's dependencies. + collectDependencies(enabledCheckers, firstRelatedChecker->Dependencies); + } else { + // Disable the checker. enabledCheckers.remove(&*firstRelatedChecker); + } + } } return enabledCheckers; @@ -160,9 +188,6 @@ void CheckerRegistry::initializeManager(CheckerManager &checkerMgr, const AnalyzerOptions &Opts) const { - // Sort checkers for efficient collection. - llvm::sort(Checkers, checkerNameLT); - // Collect checkers enabled by the options. CheckerInfoSet enabledCheckers = getEnabledCheckers(Opts); @@ -199,8 +224,6 @@ // FIXME: Alphabetical sort puts 'experimental' in the middle. // Would it be better to name it '~experimental' or something else // that's ASCIIbetically last? - llvm::sort(Checkers, checkerNameLT); - // FIXME: Print available packages. out << "CHECKERS:\n"; @@ -234,9 +257,6 @@ void CheckerRegistry::printList(raw_ostream &out, const AnalyzerOptions &opts) const { - // Sort checkers for efficient collection. - llvm::sort(Checkers, checkerNameLT); - // Collect checkers enabled by the options. CheckerInfoSet enabledCheckers = getEnabledCheckers(opts); Index: test/Analysis/NewDelete+MismatchedDeallocator_intersections.cpp =================================================================== --- test/Analysis/NewDelete+MismatchedDeallocator_intersections.cpp +++ test/Analysis/NewDelete+MismatchedDeallocator_intersections.cpp @@ -1,5 +1,14 @@ -// RUN: %clang_analyze_cc1 -analyzer-checker=core,cplusplus.NewDelete,unix.MismatchedDeallocator -std=c++11 -verify %s -// RUN: %clang_analyze_cc1 -analyzer-checker=core,cplusplus.NewDelete,cplusplus.NewDeleteLeaks,unix.MismatchedDeallocator -DLEAKS -std=c++11 -verify %s +// RUN: %clang_analyze_cc1 -std=c++11 -verify %s \ +// RUN: -analyzer-checker=core \ +// RUN: -analyzer-checker=cplusplus.NewDelete \ +// RUN: -analyzer-checker=unix.MismatchedDeallocator +// +// RUN: %clang_analyze_cc1 -std=c++11 -verify %s \ +// RUN: -analyzer-checker=core \ +// RUN: -analyzer-checker=cplusplus.NewDelete \ +// RUN: -analyzer-checker=cplusplus.NewDeleteLeaks \ +// RUN: -analyzer-checker=unix.MismatchedDeallocator + // expected-no-diagnostics typedef __typeof(sizeof(int)) size_t; Index: test/Analysis/NewDelete-checker-test.cpp =================================================================== --- test/Analysis/NewDelete-checker-test.cpp +++ test/Analysis/NewDelete-checker-test.cpp @@ -1,11 +1,42 @@ -// RUN: %clang_analyze_cc1 -analyzer-checker=core,cplusplus.NewDelete -std=c++11 -fblocks -verify %s -// RUN: %clang_analyze_cc1 -analyzer-checker=core,cplusplus.NewDeleteLeaks -DLEAKS -std=c++11 -fblocks -verify %s -// RUN: %clang_analyze_cc1 -analyzer-checker=core,cplusplus.NewDelete -std=c++11 -fblocks -analyzer-config c++-allocator-inlining=true -verify %s -// RUN: %clang_analyze_cc1 -analyzer-checker=core,cplusplus.NewDeleteLeaks -DLEAKS -std=c++11 -fblocks -analyzer-config c++-allocator-inlining=true -verify %s -// RUN: %clang_analyze_cc1 -analyzer-checker=core,cplusplus.NewDelete -std=c++11 -fblocks -DTEST_INLINABLE_ALLOCATORS -verify %s -// RUN: %clang_analyze_cc1 -analyzer-checker=core,cplusplus.NewDeleteLeaks -DLEAKS -std=c++11 -fblocks -DTEST_INLINABLE_ALLOCATORS -verify %s -// RUN: %clang_analyze_cc1 -analyzer-checker=core,cplusplus.NewDelete -std=c++11 -fblocks -analyzer-config c++-allocator-inlining=true -DTEST_INLINABLE_ALLOCATORS -verify %s -// RUN: %clang_analyze_cc1 -analyzer-checker=core,cplusplus.NewDeleteLeaks -DLEAKS -std=c++11 -fblocks -analyzer-config c++-allocator-inlining=true -DTEST_INLINABLE_ALLOCATORS -verify %s +// RUN: %clang_analyze_cc1 -std=c++11 -fblocks -verify %s \ +// RUN: -analyzer-checker=core \ +// RUN: -analyzer-checker=cplusplus.NewDelete +// +// RUN: %clang_analyze_cc1 -DLEAKS -std=c++11 -fblocks -verify %s \ +// RUN: -analyzer-checker=core \ +// RUN: -analyzer-checker=cplusplus.NewDeleteLeaks +// +// RUN: %clang_analyze_cc1 -std=c++11 -fblocks -verify %s \ +// RUN: -analyzer-checker=core \ +// RUN: -analyzer-checker=cplusplus.NewDelete \ +// RUN: -analyzer-config c++-allocator-inlining=true +// +// RUN: %clang_analyze_cc1 -DLEAKS -std=c++11 -fblocks -verify %s \ +// RUN: -analyzer-checker=core \ +// RUN: -analyzer-checker=cplusplus.NewDeleteLeaks \ +// RUN: -analyzer-config c++-allocator-inlining=true +// +// RUN: %clang_analyze_cc1 -DTEST_INLINABLE_ALLOCATORS \ +// RUN: -std=c++11 -fblocks -verify %s \ +// RUN: -analyzer-checker=core \ +// RUN: -analyzer-checker=cplusplus.NewDelete +// +// RUN: %clang_analyze_cc1 -DLEAKS -DTEST_INLINABLE_ALLOCATORS \ +// RUN: -std=c++11 -fblocks -verify %s \ +// RUN: -analyzer-checker=core \ +// RUN: -analyzer-checker=cplusplus.NewDeleteLeaks +// +// RUN: %clang_analyze_cc1 -DTEST_INLINABLE_ALLOCATORS \ +// RUN: -std=c++11 -fblocks -verify %s \ +// RUN: -analyzer-checker=core \ +// RUN: -analyzer-checker=cplusplus.NewDelete \ +// RUN: -analyzer-config c++-allocator-inlining=true +// +// RUN: %clang_analyze_cc1 -DLEAKS -DTEST_INLINABLE_ALLOCATORS \ +// RUN: -std=c++11 -fblocks -verify %s \ +// RUN: -analyzer-checker=core \ +// RUN: -analyzer-checker=cplusplus.NewDeleteLeaks \ +// RUN: -analyzer-config c++-allocator-inlining=true #include "Inputs/system-header-simulator-cxx.h" Index: test/Analysis/inner-pointer.cpp =================================================================== --- test/Analysis/inner-pointer.cpp +++ test/Analysis/inner-pointer.cpp @@ -1,4 +1,5 @@ -//RUN: %clang_analyze_cc1 -analyzer-checker=cplusplus.InnerPointer %s -analyzer-output=text -verify +// RUN: %clang_analyze_cc1 -analyzer-checker=core,cplusplus.InnerPointer \ +// RUN: -analyzer-output=text -verify %s -analyzer-list-enabled-checkers namespace std { Index: utils/TableGen/ClangSACheckersEmitter.cpp =================================================================== --- utils/TableGen/ClangSACheckersEmitter.cpp +++ utils/TableGen/ClangSACheckersEmitter.cpp @@ -57,6 +57,13 @@ return std::string(); } +static void printChecker(llvm::raw_ostream &OS, const Record &R) { + OS << '\"'; + OS.write_escaped(getCheckerFullName(&R)) << "\", "; + OS << R.getName() << ", \""; + OS.write_escaped(getStringValue(R, "HelpText")) << '\"'; +} + namespace clang { void EmitClangSACheckers(RecordKeeper &Records, raw_ostream &OS) { std::vector checkers = Records.getAllDerivedDefinitions("Checker"); @@ -64,7 +71,12 @@ using SortedRecords = llvm::StringMap; - OS << "\n#ifdef GET_PACKAGES\n"; + // Emit packages. + // + // PACKAGE(PACKAGENAME) + // - PACKAGENAME: The name of the package. + OS << "\n" + "#ifdef GET_PACKAGES\n"; { SortedRecords sortedPackages; for (unsigned i = 0, e = packages.size(); i != e; ++i) @@ -79,19 +91,51 @@ OS << ")\n"; } } - OS << "#endif // GET_PACKAGES\n\n"; - - OS << "\n#ifdef GET_CHECKERS\n"; - for (unsigned i = 0, e = checkers.size(); i != e; ++i) { - const Record &R = *checkers[i]; + OS << "#endif // GET_PACKAGES\n" + "\n"; - OS << "CHECKER(" << "\""; - OS.write_escaped(getCheckerFullName(&R)) << "\", "; - OS << R.getName() << ", "; - OS << "\""; - OS.write_escaped(getStringValue(R, "HelpText")) << '\"'; + // Emit checkers. + // + // CHECKER(FULLNAME, CLASS, HELPTEXT) + // - FULLNAME: The full name of the checker, including packages, e.g.: + // alpha.cplusplus.UninitializedObject + // - CLASS: The name of the checker, with "Checker" appended, e.g.: + // UninitializedObjectChecker + // - HELPTEXT: The description of the checker. + OS << "\n" + "#ifdef GET_CHECKERS\n" + "\n"; + for (const Record *checker : checkers) { + OS << "CHECKER("; + printChecker(OS, *checker); OS << ")\n"; } - OS << "#endif // GET_CHECKERS\n\n"; + OS << "\n" + "#endif // GET_CHECKERS\n" + "\n"; + + // Emit dependencies. + // + // CHECKER_DEPENDENCY(FULLNAME, DEPENDENCY) + // - FULLNAME: The full name of the checker that depends on another checker. + // - DEPENDENCY: The full name of the checker FULLNAME depends on. + OS << "\n" + "#ifdef GET_CHECKER_DEPENDENCIES\n"; + for (const Record *checker : checkers) { + if (checker->isValueUnset("Dependencies")) + continue; + + for (const Record *Dependency : + checker->getValueAsListOfDefs("Dependencies")) { + OS << "CHECKER_DEPENDENCY("; + OS << '\"'; + OS.write_escaped(getCheckerFullName(checker)) << "\", "; + OS << '\"'; + OS.write_escaped(getCheckerFullName(Dependency)) << '\"'; + OS << ")\n"; + } + } + OS << "\n" + "#endif // GET_CHECKER_DEPENDENCIES\n"; } } // end namespace clang