diff --git a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h index e0d583c735e61..65b79cfc1c2ff 100644 --- a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h +++ b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h @@ -201,14 +201,10 @@ bool anyConflict(const llvm::SmallVectorImpl &FixIts, const SourceManager &SM); } // namespace internal -/// Find unsafe pointers in body/initializer of `D`, if `D` is one of the -/// followings: -/// VarDecl -/// FieldDecl -/// FunctionDecl -/// BlockDecl -/// ObjCMethodDecl -std::set findUnsafePointers(const Decl *D); +/// \return true iff `N` is an unsafe buffer usage and populates the unsafe +/// pointers in `UnsafePointers` +bool matchUnsafePointers(const DynTypedNode &N, ASTContext &Ctx, + std::set &UnsafePointers); } // end namespace clang #endif /* LLVM_CLANG_ANALYSIS_ANALYSES_UNSAFEBUFFERUSAGE_H */ diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h new file mode 100644 index 0000000000000..94bd086793a8b --- /dev/null +++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel.h @@ -0,0 +1,134 @@ +//===- EntityPointerLevel.h -------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_ENTITYPOINTERLEVEL_H +#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_ENTITYPOINTERLEVEL_H + +#include "clang/AST/Decl.h" +#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h" +#include "clang/ScalableStaticAnalysisFramework/Core/Serialization/JSONFormat.h" +#include + +namespace clang::ssaf { + +/// An EntityPointerLevel is associated with a level of the declared +/// pointer/array type of an entity. In the fully-expanded spelling of the +/// declared type, a EntityPointerLevel is associated with a '*' (or a '[]`) in +/// that declaration. +/// +/// For example, for 'int *p[10];', there are two EntityPointerLevels. One +/// is associated with 'int *[10]' of 'p' and the other is associated with 'int +/// *' of 'p'. +/// +/// An EntityPointerLevel can be identified by an EntityId and an unsigned +/// integer indicating the pointer level: '(EntityId, PointerLevel)'. +/// An EntityPointerLevel 'P' is valid iff 'P.EntityId' has a pointer type with +/// at least 'P.PointerLevel' levels (This implies 'P.PointerLevel > 0'). +/// +/// For the same example 'int *p[10];', the EntityPointerLevels below are valid: +/// - '(p, 2)' is associated with the 'int *' part of the declared type of 'p'; +/// - '(p, 1)' is associated with the 'int *[10]' part of the declared type of +/// 'p'. +class EntityPointerLevel { + EntityId Entity; + unsigned PointerLevel; + + friend class EntityPointerLevelTranslator; + friend llvm::Expected + entityPointerLevelFromJSON(const llvm::json::Value &EPLData, + JSONFormat::EntityIdFromJSONFn EntityIdFromJSON); + // For unittests: + friend EntityPointerLevel buildEntityPointerLevel(EntityId, unsigned); + + // EntityPointerLevel(EntityId Entity, unsigned PointerLevel) + // : Entity(Entity), PointerLevel(PointerLevel) {} + EntityPointerLevel(std::pair Pair) + : Entity(Pair.first), PointerLevel(Pair.second) {} + +public: + EntityId getEntity() const { return Entity; } + unsigned getPointerLevel() const { return PointerLevel; } + + bool operator==(const EntityPointerLevel &Other) const { + return std::tie(Entity, PointerLevel) == + std::tie(Other.Entity, Other.PointerLevel); + } + + bool operator!=(const EntityPointerLevel &Other) const { + return !(*this == Other); + } + + bool operator<(const EntityPointerLevel &Other) const { + return std::tie(Entity, PointerLevel) < + std::tie(Other.Entity, Other.PointerLevel); + } + + /// Compares `EntityPointerLevel`s; additionally, partially compares + /// `EntityPointerLevel` with `EntityId`. + struct Comparator { + using is_transparent = void; + bool operator()(const EntityPointerLevel &L, + const EntityPointerLevel &R) const { + return L < R; + } + bool operator()(const EntityId &L, const EntityPointerLevel &R) const { + return L < R.getEntity(); + } + bool operator()(const EntityPointerLevel &L, const EntityId &R) const { + return L.getEntity() < R; + } + }; +}; + +using EntityPointerLevelSet = + std::set; + +/// Translate a pointer/array type expression 'E' to a (set of) +/// EntityPointerLevel(s) associated with the declared type of the base address +/// of `E`. If the base address of `E` is not associated with an entity, the +/// translation result is an empty set. +/// +/// \param E the pointer expression to be translated +/// \param Ctx the AST context of `E` +/// \param AddEntity the callback provided by the caller to convert EntityNames +/// to EntityIds. +llvm::Expected +translateEntityPointerLevel(const Expr *E, ASTContext &Ctx, + std::function AddEntity); + +/// Create an EntityPointerLevel (EPL) from a NamedDecl of a pointer/array type. +/// +/// \param E the pointer expression to be translated +/// \param Ctx the AST context of `E` +/// \param AddEntity the callback provided by the caller to convert EntityNames +/// to EntityIds. +/// \param IsFunRet true iff the created EPL is associated with the return type +/// of a function entity. +llvm::Expected +createEntityPointerLevel(const NamedDecl *ND, ASTContext &Ctx, + std::function AddEntity, + bool IsFunRet = false); + +/// Creates a new EntityPointerLevel (EPL) from `E` by incrementing `E`'s +/// pointer level. +/// \return the EPL that is associated with the pointee (or array element) type +/// of `E`'s associated pointer/array tyoe of the same entity. +EntityPointerLevel incrementPointerLevel(const EntityPointerLevel &E); + +llvm::json::Value +entityPointerLevelToJSON(const EntityPointerLevel &EPL, + JSONFormat::EntityIdToJSONFn EntityId2JSON); + +llvm::Expected +entityPointerLevelFromJSON(const llvm::json::Value &EPLData, + JSONFormat::EntityIdFromJSONFn EntityIdFromJSON); + +/// Proxy function creating EPLs for unit tests: +EntityPointerLevel buildEntityPointerLevel(EntityId, unsigned); +} // namespace clang::ssaf +#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_ENTITYPOINTERLEVEL_H diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h index 52caa52e1120d..0e120a40eebb8 100644 --- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h +++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h @@ -12,17 +12,19 @@ #include "clang/AST/Expr.h" #include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h" #include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.h" +#include "llvm/ADT/STLFunctionalExtras.h" #include namespace clang::ssaf { -/// An EntityPointerLevel represents a level of the declared pointer/array -/// type of an entity. In the fully-expanded spelling of the declared type, a -/// EntityPointerLevel is associated with a '*' (or a '[]`) in that declaration. +/// An EntityPointerLevel is associated with a level of the declared +/// pointer/array type of an entity. In the fully-expanded spelling of the +/// declared type, a EntityPointerLevel is associated with a '*' (or a '[]`) in +/// that declaration. /// -/// For example, for 'int *p[10];', there are two EntityPointerLevels. One -/// is associated with 'int *[10]' of 'p' and the other is associated with 'int -/// *' of 'p'. +/// For example, for 'int *p[10];', there are two EntityPointerLevels. +/// One is associated with 'int *[10]' of 'p' and the other is associated with +/// 'int *' of 'p'. /// /// An EntityPointerLevel can be identified by an EntityId and an unsigned /// integer indicating the pointer level: '(EntityId, PointerLevel)'. @@ -32,16 +34,17 @@ namespace clang::ssaf { /// For the same example 'int *p[10];', the EntityPointerLevels below are valid: /// - '(p, 2)' is associated with the 'int *' part of the declared type of 'p'; /// - '(p, 1)' is associated with the 'int *[10]' part of the declared type of -/// 'p'. +/// 'p'. class EntityPointerLevel { EntityId Entity; unsigned PointerLevel; friend class EntityPointerLevelTranslator; + // For unittests: friend EntityPointerLevel buildEntityPointerLevel(EntityId, unsigned); - EntityPointerLevel(EntityId Entity, unsigned PointerLevel) - : Entity(Entity), PointerLevel(PointerLevel) {} + EntityPointerLevel(std::pair Pair) + : Entity(Pair.first), PointerLevel(Pair.second) {} public: EntityId getEntity() const { return Entity; } @@ -90,10 +93,31 @@ using EntityPointerLevelSet = /// \param Ctx the AST context of `E` /// \param AddEntity the callback provided by the caller to convert EntityNames /// to EntityIds. -llvm::Expected -translateEntityPointerLevel(const Expr *E, ASTContext &Ctx, - std::function AddEntity); +llvm::Expected translateEntityPointerLevel( + const Expr *E, ASTContext &Ctx, + llvm::function_ref AddEntity); +/// Creates a `EntityPointerLevel` from a pair of an EntityId and a pointer +/// level: EntityPointerLevel buildEntityPointerLevel(EntityId, unsigned); + +/// Create an EntityPointerLevel (EPL) from a NamedDecl of a pointer/array type. +/// +/// \param ND the NamedDecl of a pointer/array type. +/// \param AddEntity the callback provided by the caller to convert EntityNames +/// to EntityIds. +/// \param IsFunRet true iff the created EPL is associated with the return type +/// of a function entity. +llvm::Expected +creatEntityPointerLevel(const NamedDecl *ND, + llvm::function_ref AddEntity, + bool IsFunRet = false); + +/// Creates a new EntityPointerLevel (EPL) from `E` by incrementing `E`'s +/// pointer level. +/// \return the EPL that is associated with the pointee (or array element) type +/// of `E`'s associated pointer/array tyoe of the same entity. +EntityPointerLevel incrementPointerLevel(const EntityPointerLevel &E); } // namespace clang::ssaf + #endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_ENTITYPOINTERLEVEL_ENTITYPOINTERLEVEL_H diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h new file mode 100644 index 0000000000000..7b125edb1fb7f --- /dev/null +++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h @@ -0,0 +1,25 @@ +//===- EntityPointerLevelFormat.h -------------------------------*- C++-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_ENTITYPOINTERLEVEL_ENTITYPOINTERLEVELFORMAT_H +#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_ENTITYPOINTERLEVEL_ENTITYPOINTERLEVELFORMAT_H + +#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h" +#include "clang/ScalableStaticAnalysisFramework/Core/Serialization/JSONFormat.h" + +namespace clang::ssaf { +llvm::json::Value +entityPointerLevelToJSON(const EntityPointerLevel &EPL, + JSONFormat::EntityIdToJSONFn EntityId2JSON); + +Expected +entityPointerLevelFromJSON(const llvm::json::Value &EPLData, + JSONFormat::EntityIdFromJSONFn EntityIdFromJSON); +} // namespace clang::ssaf + +#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_ENTITYPOINTERLEVEL_ENTITYPOINTERLEVELFORMAT_H diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h index 250bad5b72f75..27bda9f773085 100644 --- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h +++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h @@ -12,14 +12,10 @@ #include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h" #include "clang/ScalableStaticAnalysisFramework/Core/Model/SummaryName.h" #include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/EntitySummary.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/ADT/iterator_range.h" -#include namespace clang::ssaf { - -/// An UnsafeBufferUsageEntitySummary is an immutable set of unsafe buffers, in -/// the form of EntityPointerLevel. +/// An UnsafeBufferUsageEntitySummary contains a set of EntityPointerLevels +/// extracted from unsafe buffer pointers contributed by an entity. class UnsafeBufferUsageEntitySummary final : public EntitySummary { const EntityPointerLevelSet UnsafeBuffers; diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h deleted file mode 100644 index 13d4e18b4e81f..0000000000000 --- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h +++ /dev/null @@ -1,35 +0,0 @@ -//===- UnsafeBufferUsageExtractor.h -----------------------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGEBUILDER_H -#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGEBUILDER_H - -#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h" -#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h" -#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.h" -#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.h" -#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryExtractor.h" -#include "llvm/Support/Error.h" -#include - -namespace clang::ssaf { -class UnsafeBufferUsageTUSummaryExtractor : public TUSummaryExtractor { -public: - UnsafeBufferUsageTUSummaryExtractor(TUSummaryBuilder &Builder) - : TUSummaryExtractor(Builder) {} - - EntityId addEntity(EntityName EN) { return SummaryBuilder.addEntity(EN); } - - std::unique_ptr - extractEntitySummary(const Decl *Contributor, ASTContext &Ctx, - llvm::Error &Error); - - void HandleTranslationUnit(ASTContext &Ctx) override; -}; -} // namespace clang::ssaf - -#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGEBUILDER_H diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h b/clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h index 2030dc8a12030..916ef1ce783c4 100644 --- a/clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h +++ b/clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h @@ -36,6 +36,11 @@ extern volatile int UnsafeBufferUsageSSAFJSONFormatAnchorSource; [[maybe_unused]] static int UnsafeBufferUsageSSAFJSONFormatAnchorDestination = UnsafeBufferUsageSSAFJSONFormatAnchorSource; +extern volatile int UnsafeBufferUsageTUSummaryExtractorAnchorSource; +[[maybe_unused]] static int + UnsafeBufferUsageTUSummaryExtractorAnchorDestination = + UnsafeBufferUsageTUSummaryExtractorAnchorSource; + // This anchor is used to force the linker to link the CallGraphExtractor. extern volatile int CallGraphExtractorAnchorSource; [[maybe_unused]] static int CallGraphExtractorAnchorDestination = diff --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp b/clang/lib/Analysis/UnsafeBufferUsage.cpp index 3dba15cff3b52..e80dfc82e60ba 100644 --- a/clang/lib/Analysis/UnsafeBufferUsage.cpp +++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp @@ -2975,63 +2975,6 @@ static void populateStmtsForFindingGadgets(SmallVector &Stmts, } } -std::set clang::findUnsafePointers(const Decl *D) { - class MockReporter : public UnsafeBufferUsageHandler { - public: - MockReporter() {} - void handleUnsafeOperation(const Stmt *, bool, ASTContext &) override {} - void handleUnsafeLibcCall(const CallExpr *, unsigned, ASTContext &, - const Expr *UnsafeArg = nullptr) override {} - void handleUnsafeOperationInContainer(const Stmt *, bool, - ASTContext &) override {} - void handleUnsafeVariableGroup(const VarDecl *, - const VariableGroupsManager &, FixItList &&, - const Decl *, - const FixitStrategy &) override {} - void handleUnsafeUniquePtrArrayAccess(const DynTypedNode &Node, - bool IsRelatedToDecl, - ASTContext &Ctx) override {} - bool ignoreUnsafeBufferInContainer(const SourceLocation &) const override { - return false; - } - bool isSafeBufferOptOut(const SourceLocation &) const override { - return false; - } - bool ignoreUnsafeBufferInLibcCall(const SourceLocation &) const override { - return false; - } - bool ignoreUnsafeBufferInStaticSizedArray( - const SourceLocation &Loc) const override { - return false; - } - std::string getUnsafeBufferUsageAttributeTextAt( - SourceLocation, StringRef WSSuffix = "") const override { - return ""; - } - }; - - FixableGadgetList FixableGadgets; - WarningGadgetList WarningGadgets; - DeclUseTracker Tracker; - MockReporter IgnoreHandler; - ASTContext &Ctx = D->getASTContext(); - SmallVector Stmts; - - populateStmtsForFindingGadgets(Stmts, D); - for (auto *Stmt : Stmts) - findGadgets(Stmt, Ctx, IgnoreHandler, false, FixableGadgets, WarningGadgets, - Tracker); - - std::set Result; - for (auto &G : WarningGadgets) { - for (const Expr *E : G->getUnsafePtrs()) { - Result.insert(E); - } - } - - return Result; -} - struct WarningGadgetSets { std::map, // To keep keys sorted by their locations in the map so that the @@ -4749,3 +4692,69 @@ void clang::checkUnsafeBufferUsage(const Decl *D, applyGadgets(D, std::move(FixableGadgets), std::move(WarningGadgets), std::move(Tracker), Handler, EmitSuggestions); } + +bool clang::matchUnsafePointers(const DynTypedNode &N, ASTContext &Ctx, + std::set &UnsafePointers) { + class MockReporter : public UnsafeBufferUsageHandler { + public: + MockReporter() {} + void handleUnsafeOperation(const Stmt *, bool, ASTContext &) override {} + void handleUnsafeLibcCall(const CallExpr *, unsigned, ASTContext &, + const Expr *UnsafeArg = nullptr) override {} + void handleUnsafeOperationInContainer(const Stmt *, bool, + ASTContext &) override {} + void handleUnsafeVariableGroup(const VarDecl *, + const VariableGroupsManager &, FixItList &&, + const Decl *, + const FixitStrategy &) override {} + void handleUnsafeUniquePtrArrayAccess(const DynTypedNode &Node, + bool IsRelatedToDecl, + ASTContext &Ctx) override {} + bool ignoreUnsafeBufferInContainer(const SourceLocation &) const override { + return false; + } + bool isSafeBufferOptOut(const SourceLocation &) const override { + return false; + } + bool ignoreUnsafeBufferInLibcCall(const SourceLocation &) const override { + return false; + } + bool ignoreUnsafeBufferInStaticSizedArray( + const SourceLocation &Loc) const override { + return false; + } + std::string getUnsafeBufferUsageAttributeTextAt( + SourceLocation, StringRef WSSuffix = "") const override { + return ""; + } + } Handler; + + const Stmt *S = N.get(); + if (!S) + return false; + + MatchResult Result; + WarningGadgetList WarningGadgets; + bool Matched = false; + + // FIXME: By design, we don't need MockReporter, and we are supposed to + // only define WARNING_GADGET when we want to treat WARNING_OPTIONAL_GADGET + // the same as WARNING_GADGET. The reason we have to do it this way now is + // that some WARNING_OPTIONAL_GADGETs do not have the 3-argument `matches` + // overload. We need to fix this problem in a separate patch. + +#define WARNING_GADGET(name) \ + if (name##Gadget::matches(S, Ctx, Result)) \ + WarningGadgets.push_back(std::make_unique(Result)); +#define WARNING_OPTIONAL_GADGET(name) \ + if (name##Gadget::matches(S, Ctx, &Handler, Result)) \ + WarningGadgets.push_back(std::make_unique(Result)); +#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def" + + for (auto &WG : WarningGadgets) + for (auto *E : WG->getUnsafePtrs()) { + UnsafePointers.insert(E); + Matched = true; + } + return Matched; +} diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp index d7b8aebef55aa..a6ebe0527f937 100644 --- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp +++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp @@ -5,39 +5,20 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// - #include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h" +#include "SSAFAnalysesCommon.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" #include "clang/AST/StmtVisitor.h" +#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h" #include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h" #include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.h" +#include using namespace clang; using namespace ssaf; -static bool hasPointerType(const Expr *E) { - auto Ty = E->getType(); - return !Ty.isNull() && !Ty->isFunctionPointerType() && - (Ty->isPointerType() || Ty->isArrayType()); -} - -static llvm::Error makeUnsupportedStmtKindError(const Stmt *Unsupported) { - return llvm::createStringError( - "unsupported expression kind for translation to " - "EntityPointerLevel: %s", - Unsupported->getStmtClassName()); -} - -static llvm::Error makeCreateEntityNameError(const NamedDecl *FailedDecl, - ASTContext &Ctx) { - std::string LocStr = FailedDecl->getSourceRange().getBegin().printToString( - Ctx.getSourceManager()); - return llvm::createStringError( - "failed to create entity name for %s declared at %s", - FailedDecl->getNameAsString().c_str(), LocStr.c_str()); -} - namespace clang::ssaf { // Translate a pointer type expression 'E' to a (set of) EntityPointerLevel(s) // associated with the declared type of the base address of `E`. If the base @@ -65,26 +46,19 @@ class EntityPointerLevelTranslator // Fallback method for all unsupported expression kind: llvm::Error fallback(const Stmt *E) { - return makeUnsupportedStmtKindError(E); - } - - static EntityPointerLevel incrementPointerLevel(const EntityPointerLevel &E) { - return EntityPointerLevel(E.getEntity(), E.getPointerLevel() + 1); - } - - static EntityPointerLevel decrementPointerLevel(const EntityPointerLevel &E) { - assert(E.getPointerLevel() > 0); - return EntityPointerLevel(E.getEntity(), E.getPointerLevel() - 1); + return makeErrAtNode(Ctx, *E, + "attempt to translate %s to EntityPointerLevels", + E->getStmtClassName()); } EntityPointerLevel createEntityPointerLevelFor(const EntityName &Name) { - return EntityPointerLevel(AddEntity(Name), 1); + return EntityPointerLevel({AddEntity(Name), 1}); } // The common helper function for Translate(*base): // Translate(*base) -> Translate(base) with .pointerLevel + 1 Expected translateDereferencePointer(const Expr *Ptr) { - assert(hasPointerType(Ptr)); + assert(hasPtrOrArrType(Ptr)); Expected SubResult = Visit(Ptr); if (!SubResult) @@ -103,6 +77,29 @@ class EntityPointerLevelTranslator : AddEntity(AddEntity), Ctx(Ctx) {} Expected translate(const Expr *E) { return Visit(E); } + Expected translate(const NamedDecl *D, bool IsRet) { + if (IsRet && !isa(D)) + return makeErrAtNode( + Ctx, *D, + "attempt to call getEntityNameForReturn on a NamedDecl of %s kind", + D->getDeclKindName()); + + std::optional EN = + IsRet ? getEntityNameForReturn(cast(D)) + : getEntityName(D); + if (EN) + return createEntityPointerLevelFor(*EN); + return makeEntityNameErr(Ctx, *D); + } + + static EntityPointerLevel incrementPointerLevel(const EntityPointerLevel &E) { + return EntityPointerLevel({E.getEntity(), E.getPointerLevel() + 1}); + } + + static EntityPointerLevel decrementPointerLevel(const EntityPointerLevel &E) { + assert(E.getPointerLevel() > 0); + return EntityPointerLevel({E.getEntity(), E.getPointerLevel() - 1}); + } private: Expected VisitStmt(const Stmt *E) { @@ -117,7 +114,7 @@ class EntityPointerLevelTranslator Expected VisitBinaryOperator(const BinaryOperator *E) { switch (E->getOpcode()) { case clang::BO_Add: - if (hasPointerType(E->getLHS())) + if (hasPtrOrArrType(E->getLHS())) return Visit(E->getLHS()); return Visit(E->getRHS()); case clang::BO_Sub: @@ -162,7 +159,7 @@ class EntityPointerLevelTranslator // Translate((T*)base) -> Translate(p) if p has pointer type // -> {} otherwise Expected VisitCastExpr(const CastExpr *E) { - if (hasPointerType(E->getSubExpr())) + if (hasPtrOrArrType(E->getSubExpr())) return Visit(E->getSubExpr()); return EntityPointerLevelSet{}; } @@ -215,14 +212,14 @@ class EntityPointerLevelTranslator Expected VisitDeclRefExpr(const DeclRefExpr *E) { if (auto EntityName = getEntityName(E->getDecl())) return EntityPointerLevelSet{createEntityPointerLevelFor(*EntityName)}; - return makeCreateEntityNameError(E->getDecl(), Ctx); + return makeEntityNameErr(Ctx, *E->getDecl()); } // Translate({., ->}f) -> {(MemberDecl, 1)} Expected VisitMemberExpr(const MemberExpr *E) { if (auto EntityName = getEntityName(E->getMemberDecl())) return EntityPointerLevelSet{createEntityPointerLevelFor(*EntityName)}; - return makeCreateEntityNameError(E->getMemberDecl(), Ctx); + return makeEntityNameErr(Ctx, *E->getMemberDecl()); } Expected @@ -234,13 +231,68 @@ class EntityPointerLevelTranslator Expected clang::ssaf::translateEntityPointerLevel( const Expr *E, ASTContext &Ctx, - std::function AddEntity) { + llvm::function_ref AddEntity) { EntityPointerLevelTranslator Translator(AddEntity, Ctx); return Translator.translate(E); } +/// Create an EntityPointerLevel from a ValueDecl of a pointer type. +Expected clang::ssaf::creatEntityPointerLevel( + const NamedDecl *ND, llvm::function_ref AddEntity, + bool IsFunRet) { + EntityPointerLevelTranslator Translator(AddEntity, ND->getASTContext()); + + return Translator.translate(ND, IsFunRet); +} + +EntityPointerLevel +clang::ssaf::incrementPointerLevel(const EntityPointerLevel &E) { + return EntityPointerLevelTranslator::incrementPointerLevel(E); +} + EntityPointerLevel clang::ssaf::buildEntityPointerLevel(EntityId Id, unsigned PtrLv) { return EntityPointerLevel({Id, PtrLv}); } + +// Writes an EntityPointerLevel as +// Array [ +// Object { "@" : [entity-id]}, +// [pointer-level-integer] +// ] +llvm::json::Value clang::ssaf::entityPointerLevelToJSON( + const EntityPointerLevel &EPL, JSONFormat::EntityIdToJSONFn EntityId2JSON) { + return llvm::json::Array{EntityId2JSON(EPL.getEntity()), + llvm::json::Value(EPL.getPointerLevel())}; +} + +Expected clang::ssaf::entityPointerLevelFromJSON( + const llvm::json::Value &EPLData, + JSONFormat::EntityIdFromJSONFn EntityIdFromJSON) { + auto *AsArr = EPLData.getAsArray(); + + if (!AsArr || AsArr->size() != 2) + return makeSawButExpectedError( + EPLData, "an array with exactly two elements representing " + "EntityId and PointerLevel, respectively"); + + auto *EntityIdObj = (*AsArr)[0].getAsObject(); + + if (!EntityIdObj) + return makeSawButExpectedError((*AsArr)[0], + "an object representing EntityId"); + + Expected Id = EntityIdFromJSON(*EntityIdObj); + + if (!Id) + return Id.takeError(); + + std::optional PtrLv = (*AsArr)[1].getAsInteger(); + + if (!PtrLv) + return makeSawButExpectedError((*AsArr)[1], + "an integer representing PointerLevel"); + + return buildEntityPointerLevel(*Id, *PtrLv); +} diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h b/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h new file mode 100644 index 0000000000000..f308b6dbf9bee --- /dev/null +++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/SSAFAnalysesCommon.h @@ -0,0 +1,142 @@ +//===- SSAFAnalysesCommon.h -------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Common code in SSAF analyses implementations +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_SSAFANALYSESCOMMON_H +#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_SSAFANALYSESCOMMON_H + +#include "clang/AST/ASTTypeTraits.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/DynamicRecursiveASTVisitor.h" +#include "clang/Basic/SourceLocation.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Support/JSON.h" + +using namespace clang; + +template +llvm::Error makeErrAtNode(ASTContext &Ctx, const NodeTy &N, StringRef Fmt, + const Ts &...Args) { + std::string LocStr = N.getBeginLoc().printToString(Ctx.getSourceManager()); + return llvm::createStringError((Fmt + " at %s").str().c_str(), Args..., + LocStr.c_str()); +} + +template +llvm::Error makeSawButExpectedError(const llvm::json::Value &Saw, + llvm::StringRef Expected, + const Ts &...ExpectedArgs) { + std::string Fmt = ("saw %s but expected " + Expected).str(); + std::string SawStr = llvm::formatv("{0:2}", Saw).str(); + + return llvm::createStringError(Fmt.c_str(), SawStr.c_str(), ExpectedArgs...); +} + +template bool hasPtrOrArrType(const DeclOrExpr *E) { + return llvm::isa(E->getType().getCanonicalType()); +} + +inline llvm::Error makeEntityNameErr(ASTContext &Ctx, const NamedDecl &D) { + return makeErrAtNode(Ctx, D, "failed to create entity name for %s", + D.getNameAsString().data()); +} + +inline llvm::Error makeAddEntitySummaryErr(ASTContext &Ctx, + const NamedDecl *D) { + std::string LocStr = D->getBeginLoc().printToString(Ctx.getSourceManager()); + + return llvm::createStringError("failed to add entity summary for %s at %s", + D->getNameAsString().data(), LocStr.c_str()); +} + +namespace clang::ssaf { +/// Traverses the AST and finds contributors: +class ContributorFinder : public DynamicRecursiveASTVisitor { +public: + std::vector Contributors; + + bool VisitFunctionDecl(FunctionDecl *D) override { + Contributors.push_back(D); + return true; + } + + bool VisitRecordDecl(RecordDecl *D) override { + Contributors.push_back(D); + return true; + } + + bool VisitVarDecl(VarDecl *D) override { + DeclContext *DC = D->getDeclContext(); + + if (DC->isFileContext() || DC->isNamespace()) + Contributors.push_back(D); + return true; + } +}; + +// FIXME: move this to a .cpp file + +/// An AST visitor that skips the root node's strict-descendants that are +/// callable Decls and record Decls, because those are separate contributors. +/// +/// Clients need to implement their own `MatchAction`, which is a function that +/// takes a `DynTypedNode`, decides if it matches and performs any further +/// callback actions. +class ContributorFactFinder : public DynamicRecursiveASTVisitor { + std::function MatchAction; + const NamedDecl *RootDecl = nullptr; + + template void match(const NodeTy &Node) { + MatchAction(DynTypedNode::create(Node)); + } + +public: + ContributorFactFinder(std::function MatchAction) + : MatchAction(MatchAction) { + ShouldVisitTemplateInstantiations = true; + ShouldVisitImplicitCode = false; + } + + // The entry point: + void findMatches(const NamedDecl *Contributor) { + RootDecl = Contributor; + TraverseDecl(const_cast(Contributor)); + } + + bool TraverseDecl(Decl *Node) override { + if (!Node) + return true; + // To skip callables: + if (Node != RootDecl && isa(Node)) + return true; + match(*Node); + return DynamicRecursiveASTVisitor::TraverseDecl(Node); + } + + bool TraverseStmt(Stmt *Node) override { + if (!Node) + return true; + match(*Node); + return DynamicRecursiveASTVisitor::TraverseStmt(Node); + } + + bool TraverseLambdaExpr(LambdaExpr *L) override { + // TODO: lambda captures of pointer variables (by copy or by reference) + // are currently not tracked. Each capture initializes an implicit closure + // field from the captured variable, which constitutes a pointer assignment + // edge that should be recorded here. + return true; // Skip lambda as it is a callable. + } +}; +} // namespace clang::ssaf + +#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_SSAFANALYSESCOMMON_H diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp index 84f3f9cbb3852..6bebea52a3768 100644 --- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp +++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp @@ -7,8 +7,10 @@ //===----------------------------------------------------------------------===// #include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h" +#include "SSAFAnalysesCommon.h" +#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h" +#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h" #include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h" -#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h" #include "clang/ScalableStaticAnalysisFramework/Core/Serialization/JSONFormat.h" #include "llvm/Support/Error.h" #include "llvm/Support/JSON.h" @@ -37,8 +39,7 @@ static Object serialize(const EntitySummary &S, Array UnsafeBuffersData; for (const auto &EPL : getUnsafeBuffers(SS)) - UnsafeBuffersData.push_back( - Array{Fn(EPL.getEntity()), EPL.getPointerLevel()}); + UnsafeBuffersData.push_back(entityPointerLevelToJSON(EPL, Fn)); return Object{{SummarySerializationKey.data(), std::move(UnsafeBuffersData)}}; } @@ -48,28 +49,17 @@ deserializeImpl(const Object &Data, JSONFormat::EntityIdFromJSONFn Fn) { Data.getArray(SummarySerializationKey.data()); if (!UnsafeBuffersData) - return llvm::createStringError("expected a json::Object with a key %s", + return makeSawButExpectedError(Object(Data), "an Object with a key %s", SummarySerializationKey.data()); EntityPointerLevelSet EPLs; - for (const auto &EltData : *UnsafeBuffersData) { - const Array *EltDataAsArr = EltData.getAsArray(); + for (auto &EPLData : *UnsafeBuffersData) { + auto EPL = entityPointerLevelFromJSON(EPLData, Fn); - if (!EltDataAsArr || EltDataAsArr->size() != 2) - return llvm::createStringError("expected a json::Array of size 2"); - - const Object *IdData = (*EltDataAsArr)[0].getAsObject(); - std::optional PtrLvData = (*EltDataAsArr)[1].getAsInteger(); - - if (!IdData || !PtrLvData) - return llvm::createStringError("expected a json::Value of integer type"); - - llvm::Expected Id = Fn(*IdData); - - if (!Id) - return Id.takeError(); - EPLs.insert(buildEntityPointerLevel(Id.get(), *PtrLvData)); + if (!EPL) + return EPL.takeError(); + EPLs.insert(*EPL); } return std::make_unique( buildUnsafeBufferUsageEntitySummary(std::move(EPLs))); diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp index b29eaa6b903d0..0c0c8d8fca87e 100644 --- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp +++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp @@ -6,158 +6,93 @@ // //===----------------------------------------------------------------------===// -#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h" +#include "SSAFAnalysesCommon.h" #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" -#include "clang/AST/Decl.h" #include "clang/AST/DynamicRecursiveASTVisitor.h" #include "clang/Analysis/Analyses/UnsafeBufferUsage.h" #include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h" #include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h" #include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h" +#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h" +#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.h" +#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/ExtractorRegistry.h" +#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.h" #include "llvm/ADT/STLExtras.h" -#include "llvm/ADT/iterator_range.h" #include "llvm/Support/Error.h" -#include +#include "llvm/Support/ErrorHandling.h" -namespace { using namespace clang; using namespace ssaf; -static llvm::Error makeCreateEntityNameError(const NamedDecl *FailedDecl, - ASTContext &Ctx) { - std::string LocStr = FailedDecl->getSourceRange().getBegin().printToString( - Ctx.getSourceManager()); - return llvm::createStringError( - "failed to create entity name for %s declared at %s", - FailedDecl->getNameAsString().c_str(), LocStr.c_str()); -} - -static llvm::Error makeAddEntitySummaryError(const NamedDecl *FailedContributor, - ASTContext &Ctx) { - std::string LocStr = - FailedContributor->getSourceRange().getBegin().printToString( - Ctx.getSourceManager()); - return llvm::createStringError( - "failed to add entity summary for contributor %s declared at %s", - FailedContributor->getNameAsString().c_str(), LocStr.c_str()); -} - -Expected -buildEntityPointerLevels(std::set &&UnsafePointers, - UnsafeBufferUsageTUSummaryExtractor &Extractor, - ASTContext &Ctx, - std::function AddEntity) { - EntityPointerLevelSet Result{}; - llvm::Error AllErrors = llvm::ErrorSuccess(); - - for (const Expr *Ptr : UnsafePointers) { - Expected Translation = - translateEntityPointerLevel(Ptr, Ctx, AddEntity); - - if (Translation) { - // Filter out those temporary invalid EntityPointerLevels associated with - // `&E` pointers: - auto FilteredTranslation = llvm::make_filter_range( - *Translation, [](const EntityPointerLevel &E) -> bool { - return E.getPointerLevel() > 0; +class clang::ssaf::UnsafeBufferUsageTUSummaryExtractor + : public TUSummaryExtractor { +public: + UnsafeBufferUsageTUSummaryExtractor(TUSummaryBuilder &Builder) + : TUSummaryExtractor(Builder) {} + + EntityId addEntity(EntityName EN) { return SummaryBuilder.addEntity(EN); } + + Expected> + extractEntitySummary(const NamedDecl *Contributor, ASTContext &Ctx) { + std::set UnsafePointers; + EntityPointerLevelSet Results; + + ContributorFactFinder{[&Ctx, &UnsafePointers](const DynTypedNode &Node) { + return matchUnsafePointers(Node, Ctx, UnsafePointers); + }}.findMatches(Contributor); + for (const Expr *Ptr : UnsafePointers) { + Expected Translation = + translateEntityPointerLevel(Ptr, Ctx, [this](const EntityName &EN) { + return SummaryBuilder.addEntity(EN); }); - Result.insert(FilteredTranslation.begin(), FilteredTranslation.end()); - continue; - } - AllErrors = llvm::joinErrors(std::move(AllErrors), Translation.takeError()); - } - if (AllErrors) - return AllErrors; - return Result; -} -} // namespace - -static std::set findUnsafePointersInContributor(const Decl *D) { - if (isa(D) || isa(D)) - return findUnsafePointers(D); - if (auto *RD = dyn_cast(D)) { - std::set Result; - - for (const FieldDecl *FD : RD->fields()) { - Result.merge(findUnsafePointers(FD)); - } - return Result; - } - return {}; -} - -std::unique_ptr -UnsafeBufferUsageTUSummaryExtractor::extractEntitySummary( - const Decl *Contributor, ASTContext &Ctx, llvm::Error &Error) { - auto AddEntity = [this](EntityName EN) { return addEntity(EN); }; - Expected EPLs = buildEntityPointerLevels( - findUnsafePointersInContributor(Contributor), *this, Ctx, AddEntity); - - if (EPLs) - return std::make_unique( - UnsafeBufferUsageEntitySummary(std::move(*EPLs))); - Error = EPLs.takeError(); - return nullptr; -} - -void UnsafeBufferUsageTUSummaryExtractor::HandleTranslationUnit( - ASTContext &Ctx) { - - // FIXME: I suppose finding contributor Decls is commonly needed by all/many - // extractors - class ContributorFinder : public DynamicRecursiveASTVisitor { - public: - std::vector Contributors; - - bool VisitFunctionDecl(FunctionDecl *D) override { - Contributors.push_back(D); - return true; - } - bool VisitRecordDecl(RecordDecl *D) override { - Contributors.push_back(D); - return true; + if (Translation) { + // Filter out those temporary invalid EntityPointerLevels associated + // with `&E` pointers. They need no transformation of entities: + auto FilteredTranslation = llvm::make_filter_range( + *Translation, [](const EntityPointerLevel &E) -> bool { + return E.getPointerLevel() > 0; + }); + Results.insert(FilteredTranslation.begin(), FilteredTranslation.end()); + continue; + } + return Translation.takeError(); } + return std::make_unique( + UnsafeBufferUsageEntitySummary(std::move(Results))); + } - bool VisitVarDecl(VarDecl *D) override { - DeclContext *DC = D->getDeclContext(); + void HandleTranslationUnit(ASTContext &Ctx) override { + ContributorFinder ContributorFinder; - if (DC->isFileContext() || DC->isNamespace()) - Contributors.push_back(D); - return false; - } - } ContributorFinder; + ContributorFinder.TraverseAST(Ctx); + for (auto *CD : ContributorFinder.Contributors) { + auto EntitySummary = extractEntitySummary(CD, Ctx); - ContributorFinder.VisitTranslationUnitDecl(Ctx.getTranslationUnitDecl()); + if (!EntitySummary) + llvm::reportFatalInternalError(EntitySummary.takeError()); + assert(*EntitySummary); + if ((*EntitySummary)->empty()) + continue; - llvm::Error Errors = llvm::ErrorSuccess(); - auto addError = [&Errors](llvm::Error Err) { - Errors = llvm::joinErrors(std::move(Errors), std::move(Err)); - }; + auto ContributorName = getEntityName(CD); - for (auto *CD : ContributorFinder.Contributors) { - llvm::Error Error = llvm::ErrorSuccess(); - auto EntitySummary = extractEntitySummary(CD, Ctx, Error); + if (!ContributorName) + llvm::reportFatalInternalError(makeEntityNameErr(Ctx, *CD)); - if (Error) - addError(std::move(Error)); - if (EntitySummary->empty()) - continue; + auto [Ignored, InsertionSucceeded] = SummaryBuilder.addSummary( + addEntity(*ContributorName), std::move(*EntitySummary)); - auto ContributorName = getEntityName(CD); - - if (!ContributorName) { - addError(makeCreateEntityNameError(CD, Ctx)); - continue; + assert(InsertionSucceeded && "duplicated contributor extraction"); } + } +}; - auto [EntitySummaryPtr, Success] = SummaryBuilder.addSummary( - addEntity(*ContributorName), std::move(EntitySummary)); +// NOLINTNEXTLINE(misc-use-internal-linkage) +volatile int UnsafeBufferUsageTUSummaryExtractorAnchorSource = 0; - if (!Success) - addError(makeAddEntitySummaryError(CD, Ctx)); - } - // FIXME: handle errors! - llvm::consumeError(std::move(Errors)); -} +static clang::ssaf::TUSummaryExtractorRegistry::Add< + ssaf::UnsafeBufferUsageTUSummaryExtractor> + RegisterExtractor(UnsafeBufferUsageEntitySummary::Name, + "The TUSummaryExtractor for unsafe buffer pointers"); diff --git a/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-serialization.test b/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-serialization.test index 6a12949f3bbb4..ff13aa7f8ca27 100644 --- a/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-serialization.test +++ b/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-serialization.test @@ -5,14 +5,22 @@ // Negative tests: // RUN: not clang-ssaf-format -type=tu %S/Inputs/tu-summary-no-key.json 2>&1 \ -// RUN: | FileCheck %s --check-prefix=CHECK-NO-KEY -// CHECK-NO-KEY: expected a json::Object with a key UnsafeBuffers - +// RUN: | FileCheck %s --check-prefix=CHECK-MISSING-KEY +// CHECK-MISSING-KEY: saw { +// CHECK-MISSING-KEY-NEXT: "NotUnsafeBuffers": [ +// CHECK-MISSING-KEY-NEXT: [ +// CHECK-MISSING-KEY-NEXT: { +// CHECK-MISSING-KEY-NEXT: "@": 0 +// CHECK-MISSING-KEY-NEXT: }, +// CHECK-MISSING-KEY-NEXT: 1 +// CHECK-MISSING-KEY-NEXT: ] +// CHECK-MISSING-KEY-NEXT: ] +// CHECK-MISSING-KEY-NEXT: } but expected an Object with a key UnsafeBuffers // RUN: not clang-ssaf-format -type=tu %S/Inputs/tu-summary-bad-element.json 2>&1 \ // RUN: | FileCheck %s --check-prefix=CHECK-BAD-ELEMENT -// CHECK-BAD-ELEMENT: expected a json::Array of size 2 +// CHECK-BAD-ELEMENT: saw 42 but expected an array with exactly two elements representing EntityId and PointerLevel, respectively // RUN: not clang-ssaf-format -type=tu %S/Inputs/tu-summary-bad-ptr-level.json 2>&1 \ // RUN: | FileCheck %s --check-prefix=CHECK-BAD-PTR-LEVEL -// CHECK-BAD-PTR-LEVEL: expected a json::Value of integer type +// CHECK-BAD-PTR-LEVEL: saw "not-an-integer" but expected an integer representing PointerLevel diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp index d2c513b7c70a2..59c2cd38ffacf 100644 --- a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp +++ b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp @@ -11,20 +11,23 @@ #include "clang/AST/DynamicRecursiveASTVisitor.h" #include "clang/Frontend/ASTUnit.h" #include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h" -#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h" +#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h" #include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h" #include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h" #include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h" #include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityIdTable.h" #include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.h" +#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/ExtractorRegistry.h" #include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummary.h" #include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.h" +#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryExtractor.h" #include "clang/Tooling/Tooling.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Support/Error.h" #include "llvm/Testing/Support/Error.h" #include "gmock/gmock.h" #include "gtest/gtest.h" +#include #include #include @@ -33,7 +36,6 @@ using namespace ssaf; using testing::UnorderedElementsAre; namespace { - template const SomeDecl *findDeclByName(StringRef Name, ASTContext &Ctx) { class NamedDeclFinder : public DynamicRecursiveASTVisitor { @@ -68,52 +70,82 @@ class UnsafeBufferUsageTest : public TestFixture { protected: TUSummary TUSum; TUSummaryBuilder Builder; - UnsafeBufferUsageTUSummaryExtractor Extractor; + std::unique_ptr Extractor; std::unique_ptr AST; UnsafeBufferUsageTest() : TUSum(BuildNamespace(BuildNamespaceKind::CompilationUnit, "Mock.cpp")), - Builder(TUSum), Extractor(Builder) {} + Builder(TUSum), Extractor(nullptr) {} - template - std::unique_ptr - setUpTest(StringRef Code, StringRef ContributorName) { + bool setUpTest(StringRef Code) { AST = tooling::buildASTFromCodeWithArgs( Code, {"-Wno-unused-value", "-Wno-int-to-pointer-cast"}); - const auto *ContributorDefn = - findDeclByName(ContributorName, AST->getASTContext()); + for (auto &E : clang::ssaf::TUSummaryExtractorRegistry::entries()) { + if (E.getName() == UnsafeBufferUsageEntitySummary::Name) { + Extractor = E.instantiate(Builder); + break; + } + } + + if (!Extractor) { + ADD_FAILURE() << "failed to find UnsafeBufferUsageTUSummaryExtractor"; + return false; + } + Extractor->HandleTranslationUnit(AST->getASTContext()); + return true; + } - if (!ContributorDefn) + template + const UnsafeBufferUsageEntitySummary * + getEntitySummary(StringRef ContributorEntityName) { + auto *ContributorDefn = findDeclByName( + ContributorEntityName, AST->getASTContext()); + + if (!ContributorDefn) { + ADD_FAILURE() << "failed to find Decl of \"" << ContributorEntityName + << "\""; return nullptr; + } std::optional EN = getEntityName(ContributorDefn); - if (!EN) + if (!EN) { + ADD_FAILURE() << "failed to get EntityName for contributor \"" + << ContributorEntityName << "\""; return nullptr; + } - llvm::Error Error = llvm::ErrorSuccess(); - auto Sum = Extractor.extractEntitySummary(ContributorDefn, - AST->getASTContext(), Error); + EntityId ContributorEntityId = Builder.addEntity(*EN); + auto &TUSumData = getData(TUSum); + auto EntitiesSumIter = + TUSumData.find(UnsafeBufferUsageEntitySummary::summaryName()); - if (Error) { - llvm::consumeError(std::move(Error)); + // If none entity summary was collected, it may not be an entry in + // `TUSumData`: + if (EntitiesSumIter == TUSumData.end()) return nullptr; - } - return Sum; + + auto EntitySumIter = EntitiesSumIter->second.find(ContributorEntityId); + + // If entity summary is empty, it may not exist: + if (EntitySumIter == EntitiesSumIter->second.end()) + return nullptr; + return static_cast( + EntitySumIter->second.get()); } std::optional getEntityId(StringRef Name) { if (const auto *D = findDeclByName(Name, AST->getASTContext())) if (auto EntityName = getEntityName(D)) - return Extractor.addEntity(*EntityName); + return Builder.addEntity(*EntityName); return std::nullopt; } std::optional getEntityIdForReturn(StringRef FunName) { if (const auto *D = findFnByName(FunName, AST->getASTContext())) if (auto EntityName = getEntityNameForReturn(D)) - return Extractor.addEntity(*EntityName); + return Builder.addEntity(*EntityName); return std::nullopt; } @@ -153,8 +185,8 @@ getSubsetOf(const EntityPointerLevelSet &Set, EntityId Entity) { } TEST_F(UnsafeBufferUsageTest, EntityPointerLevelComparison) { - EntityId E1 = Extractor.addEntity({"c:@F@foo", "", {}}); - EntityId E2 = Extractor.addEntity({"c:@F@bar", "", {}}); + EntityId E1 = Builder.addEntity({"c:@F@foo", "", {}}); + EntityId E2 = Builder.addEntity({"c:@F@bar", "", {}}); auto P1 = buildEntityPointerLevel(E1, 2); auto P2 = buildEntityPointerLevel(E1, 2); @@ -172,9 +204,9 @@ TEST_F(UnsafeBufferUsageTest, EntityPointerLevelComparison) { } TEST_F(UnsafeBufferUsageTest, UnsafeBufferUsageEntityPointerLevelSetTest) { - EntityId E1 = Extractor.addEntity({"c:@F@foo", "", {}}); - EntityId E2 = Extractor.addEntity({"c:@F@bar", "", {}}); - EntityId E3 = Extractor.addEntity({"c:@F@baz", "", {}}); + EntityId E1 = Builder.addEntity({"c:@F@foo", "", {}}); + EntityId E2 = Builder.addEntity({"c:@F@bar", "", {}}); + EntityId E3 = Builder.addEntity({"c:@F@baz", "", {}}); auto P1 = buildEntityPointerLevel(E1, 1); auto P2 = buildEntityPointerLevel(E1, 2); @@ -195,13 +227,14 @@ TEST_F(UnsafeBufferUsageTest, UnsafeBufferUsageEntityPointerLevelSetTest) { ////////////////////////////////////////////////////////////// TEST_F(UnsafeBufferUsageTest, UnsafeBufferUsageSerializeTest) { - auto Sum = setUpTest(R"cpp( + ASSERT_EQ(setUpTest(R"cpp( void foo(int ***p, int ****q, int x) { p[5][5][5]; q[5][5][5][5]; } - )cpp", - "foo"); + )cpp"), + true); + const auto *Sum = getEntitySummary("foo"); ASSERT_NE(Sum, nullptr); EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}, {"p", 2U}, @@ -241,40 +274,43 @@ TEST_F(UnsafeBufferUsageTest, UnsafeBufferUsageSerializeTest) { ////////////////////////////////////////////////////////////// TEST_F(UnsafeBufferUsageTest, SimpleFunctionWithUnsafePointer) { - auto Sum = setUpTest(R"cpp( + ASSERT_EQ(setUpTest(R"cpp( void foo(int *p) { p[5]; } - )cpp", - "foo"); + )cpp"), + true); + const auto *Sum = getEntitySummary("foo"); EXPECT_NE(Sum, nullptr); EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}})); } TEST_F(UnsafeBufferUsageTest, PointerArithmetic) { - auto Sum = setUpTest(R"cpp( + ASSERT_EQ(setUpTest(R"cpp( void foo(int *p, int *q) { *(p + 5); *(q - 3); } - )cpp", - "foo"); + )cpp"), + true); + const auto *Sum = getEntitySummary("foo"); EXPECT_NE(Sum, nullptr); EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}, {"q", 1U}})); } TEST_F(UnsafeBufferUsageTest, PointerIncrementDecrement) { - auto Sum = setUpTest(R"cpp( + ASSERT_EQ(setUpTest(R"cpp( void foo(int *p, int *q, int *r, int *s) { (++p)[5]; (q++)[5]; (--r)[5]; (s--)[5]; } - )cpp", - "foo"); + )cpp"), + true); + const auto *Sum = getEntitySummary("foo"); EXPECT_NE(Sum, nullptr); EXPECT_EQ(*Sum, @@ -282,40 +318,43 @@ TEST_F(UnsafeBufferUsageTest, PointerIncrementDecrement) { } TEST_F(UnsafeBufferUsageTest, PointerAssignment) { - auto Sum = setUpTest(R"cpp( + ASSERT_EQ(setUpTest(R"cpp( void foo(int *p, int *q) { (p = q + 5)[5]; } - )cpp", - "foo"); + )cpp"), + true); + const auto *Sum = getEntitySummary("foo"); EXPECT_NE(Sum, nullptr); EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}, {"q", 1U}})); } TEST_F(UnsafeBufferUsageTest, CompoundAssignment) { - auto Sum = setUpTest(R"cpp( + ASSERT_EQ(setUpTest(R"cpp( void foo(int *p, int *q) { (p += 5)[5]; (q -= 3)[5]; } - )cpp", - "foo"); + )cpp"), + true); + const auto *Sum = getEntitySummary("foo"); EXPECT_NE(Sum, nullptr); EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}, {"q", 1U}})); } TEST_F(UnsafeBufferUsageTest, MultiLevelPointer) { - auto Sum = setUpTest(R"cpp( + ASSERT_EQ(setUpTest(R"cpp( void foo(int **p, int **q, int **r) { (*p)[5]; *(*q); *(q[5]); r[5][5]; } - )cpp", - "foo"); + )cpp"), + true); + const auto *Sum = getEntitySummary("foo"); EXPECT_NE(Sum, nullptr); EXPECT_EQ(*Sum, @@ -323,13 +362,14 @@ TEST_F(UnsafeBufferUsageTest, MultiLevelPointer) { } TEST_F(UnsafeBufferUsageTest, ConditionalOperator) { - auto Sum = setUpTest(R"cpp( + ASSERT_EQ(setUpTest(R"cpp( void foo(int **p, int **q, int cond) { (cond ? *p : *q)[5]; cond ? p[5] : q[5]; } - )cpp", - "foo"); + )cpp"), + true); + const auto *Sum = getEntitySummary("foo"); EXPECT_NE(Sum, nullptr); EXPECT_EQ(*Sum, @@ -337,78 +377,84 @@ TEST_F(UnsafeBufferUsageTest, ConditionalOperator) { } TEST_F(UnsafeBufferUsageTest, CastExpression) { - auto Sum = setUpTest(R"cpp( + ASSERT_EQ(setUpTest(R"cpp( void foo(void *p, int q) { ((int*)p)[5]; ((int*)q)[5]; } - )cpp", - "foo"); + )cpp"), + true); + const auto *Sum = getEntitySummary("foo"); EXPECT_NE(Sum, nullptr); EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}})); } TEST_F(UnsafeBufferUsageTest, CommaOperator) { - auto Sum = setUpTest(R"cpp( + ASSERT_EQ(setUpTest(R"cpp( void foo(int *p, int x) { (x++, p)[5]; } - )cpp", - "foo"); + )cpp"), + true); + const auto *Sum = getEntitySummary("foo"); EXPECT_NE(Sum, nullptr); EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}})); } TEST_F(UnsafeBufferUsageTest, CommaOperator2) { - auto Sum = setUpTest(R"cpp( + ASSERT_EQ(setUpTest(R"cpp( void foo(int **p, int **q, int x) { (p[x] = 0, q[x] = 0)[5]; } - )cpp", - "foo"); + )cpp"), + true); + const auto *Sum = getEntitySummary("foo"); EXPECT_NE(Sum, nullptr); EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}, {"q", 1U}, {"q", 2U}})); } TEST_F(UnsafeBufferUsageTest, ParenthesizedExpression) { - auto Sum = setUpTest(R"cpp( + ASSERT_EQ(setUpTest(R"cpp( void foo(int *p) { (((p)))[5]; } - )cpp", - "foo"); + )cpp"), + true); + const auto *Sum = getEntitySummary("foo"); EXPECT_NE(Sum, nullptr); EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}})); } TEST_F(UnsafeBufferUsageTest, ArrayParameter) { - auto Sum = setUpTest(R"cpp( + ASSERT_EQ(setUpTest(R"cpp( void foo(int arr[], int arr2[][10]) { int n = 5; arr[100]; arr2[5][n]; } - )cpp", - "foo"); + )cpp"), + true); + const auto *Sum = getEntitySummary("foo"); EXPECT_NE(Sum, nullptr); EXPECT_EQ(*Sum, makeSet(__LINE__, {{"arr", 1U}, {"arr2", 1U}, {"arr2", 2U}})); } TEST_F(UnsafeBufferUsageTest, FunctionCall) { - auto Sum = setUpTest(R"cpp( + ASSERT_EQ(setUpTest(R"cpp( int ** (*fp)(); int ** foo() { fp = &foo; foo()[5]; (*fp())[5]; } - )cpp", - "foo"); + )cpp"), + true); + const auto *Sum = getEntitySummary("foo"); EXPECT_NE(Sum, nullptr); // No (foo, 2) becasue indirect calls are ignored. @@ -416,7 +462,7 @@ TEST_F(UnsafeBufferUsageTest, FunctionCall) { } TEST_F(UnsafeBufferUsageTest, StructMemberAccess) { - auto Sum = setUpTest(R"cpp( + ASSERT_EQ(setUpTest(R"cpp( struct S { int *ptr; int (*ptr_to_arr)[10]; @@ -426,206 +472,215 @@ TEST_F(UnsafeBufferUsageTest, StructMemberAccess) { obj.ptr[5]; (*obj.ptr_to_arr)[n]; } - )cpp", - "foo"); + )cpp"), + true); + const auto *Sum = getEntitySummary("foo"); EXPECT_NE(Sum, nullptr); EXPECT_EQ(*Sum, makeSet(__LINE__, {{"ptr", 1U}, {"ptr_to_arr", 2U}})); } TEST_F(UnsafeBufferUsageTest, StringLiteralSubscript) { - auto Sum = setUpTest(R"cpp( + ASSERT_EQ(setUpTest(R"cpp( void foo() { "hello"[5]; } - )cpp", - "foo"); + )cpp"), + true); + const auto *Sum = getEntitySummary("foo"); - EXPECT_NE(Sum, nullptr); // String literals should not generate pointer kind variables - EXPECT_EQ(*Sum, makeSet(__LINE__, {})); + EXPECT_EQ(Sum, nullptr); } TEST_F(UnsafeBufferUsageTest, OpaqueValueExpr) { - auto Sum = setUpTest(R"cpp( + ASSERT_EQ(setUpTest(R"cpp( void foo(int *p, int *q) { (p ?: q)[5]; } - )cpp", - "foo"); + )cpp"), + true); + const auto *Sum = getEntitySummary("foo"); EXPECT_NE(Sum, nullptr); EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}, {"q", 1U}})); } TEST_F(UnsafeBufferUsageTest, AddressOfOperator) { - auto Sum = setUpTest(R"cpp( + ASSERT_EQ(setUpTest(R"cpp( void foo(int x) { (&x)[5]; } - )cpp", - "foo"); - - EXPECT_NE(Sum, nullptr); - // Address-of should not generate pointer kind variables for 'x' - EXPECT_EQ(*Sum, makeSet(__LINE__, {})); + )cpp"), + true); + const auto *Sum = getEntitySummary("foo"); + // Address-of should not generate pointer kind variables for 'x': + EXPECT_EQ(Sum, nullptr); } TEST_F(UnsafeBufferUsageTest, AddressOfThenDereference) { - auto Sum = setUpTest(R"cpp( + ASSERT_EQ(setUpTest(R"cpp( void foo(int *p, int *q) { (*(&p))[5]; (&(*q))[5]; } - )cpp", - "foo"); + )cpp"), + true); + const auto *Sum = getEntitySummary("foo"); EXPECT_NE(Sum, nullptr); EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1}, {"q", 1}})); } TEST_F(UnsafeBufferUsageTest, PointerToArrayOfPointers) { - auto Sum = setUpTest(R"cpp( + ASSERT_EQ(setUpTest(R"cpp( void foo() { int * arr[10]; - int * (*p)[10] = arr; + int * (*p)[10] = &arr; - (*p)[5][5]; // '(*p)[5]' is unsafe + (*p)[5][5]; // '(*p)[5]' is unsafe // '(*p)' is fine because 5 < 10 } - )cpp", - "foo"); + )cpp"), + true); + const auto *Sum = getEntitySummary("foo"); EXPECT_NE(Sum, nullptr); EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 3}})); } TEST_F(UnsafeBufferUsageTest, UnsafePointerInGlobalVariableInitializer) { - auto Sum = setUpTest(R"cpp( + ASSERT_EQ(setUpTest(R"cpp( int *gp; int x = gp[5]; - )cpp", - {"x"}); + )cpp"), + true); + const auto *Sum = getEntitySummary("x"); EXPECT_NE(Sum, nullptr); EXPECT_EQ(*Sum, makeSet(__LINE__, {{"gp", 1U}})); } TEST_F(UnsafeBufferUsageTest, UnsafePointerInFieldInitializer) { - auto Sum = setUpTest(R"cpp( + ASSERT_EQ(setUpTest(R"cpp( int *gp; struct Foo { int field = gp[5]; }; - )cpp", - {"Foo"}); + )cpp"), + true); + const auto *Sum = getEntitySummary("Foo"); EXPECT_NE(Sum, nullptr); EXPECT_EQ(*Sum, makeSet(__LINE__, {{"gp", 1U}})); } TEST_F(UnsafeBufferUsageTest, UnsafePointerInFieldInitializer2) { - auto Sum = setUpTest(R"cpp( + ASSERT_EQ(setUpTest(R"cpp( int *gp; union Foo { int field = gp[5]; int x; }; - )cpp", - {"Foo"}); + )cpp"), + true); + const auto *Sum = getEntitySummary("Foo"); EXPECT_NE(Sum, nullptr); EXPECT_EQ(*Sum, makeSet(__LINE__, {{"gp", 1U}})); } TEST_F(UnsafeBufferUsageTest, InitializerList) { - auto Sum = setUpTest(R"cpp( + ASSERT_EQ(setUpTest(R"cpp( int *gp; struct Foo { int field; int x; }; Foo FooObj{gp[5], 0}; - )cpp", - {"FooObj"}); + )cpp"), + true); + const auto *Sum = getEntitySummary("FooObj"); EXPECT_NE(Sum, nullptr); EXPECT_EQ(*Sum, makeSet(__LINE__, {{"gp", 1U}})); } TEST_F(UnsafeBufferUsageTest, UnsafePointerInCXXCtorInitializer) { - auto Sum = setUpTest(R"cpp( + ASSERT_EQ(setUpTest(R"cpp( struct Foo { int member; Foo(int *p) : member(p[5]) {} }; - )cpp", - {"Foo"}); + )cpp"), + true); + const auto *Sum = getEntitySummary("Foo"); EXPECT_NE(Sum, nullptr); EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}})); } TEST_F(UnsafeBufferUsageTest, UnsafePointerInDefaultArg) { - auto Sum = setUpTest(R"cpp( + ASSERT_EQ(setUpTest(R"cpp( int * gp; void foo(int x = gp[5]); - )cpp", - {"foo"}); + )cpp"), + true); + const auto *Sum = getEntitySummary("foo"); EXPECT_NE(Sum, nullptr); EXPECT_EQ(*Sum, makeSet(__LINE__, {{"gp", 1U}})); } TEST_F(UnsafeBufferUsageTest, NestedDefinitions) { - StringRef Code = R"cpp( + ASSERT_EQ(setUpTest(R"cpp( int * a = [](){ struct Foo { void bar(int * ptr) { ptr[3] = 0; } }; return nullptr; }(); - )cpp"; - auto Sum = setUpTest(Code, {"bar"}); + )cpp"), + true); + const auto *Sum = getEntitySummary("bar"); EXPECT_NE(Sum, nullptr); // The closest contributor owns the fact: EXPECT_EQ(*Sum, makeSet(__LINE__, {{"ptr", 1U}})); - Sum = setUpTest(Code, {"Foo"}); + Sum = getEntitySummary("Foo"); - EXPECT_NE(Sum, nullptr); - EXPECT_TRUE(Sum->empty()); + EXPECT_EQ(Sum, nullptr); - Sum = setUpTest(Code, {"a"}); + Sum = getEntitySummary("a"); - EXPECT_NE(Sum, nullptr); - EXPECT_TRUE(Sum->empty()); + EXPECT_EQ(Sum, nullptr); } TEST_F(UnsafeBufferUsageTest, NestedDefinitions2) { - StringRef Code = R"cpp( + bool SetupSuccess = setUpTest(R"cpp( int main(void) { struct Foo { void bar(int * ptr) { ptr[3] = 0; } }; } - )cpp"; - auto Sum = setUpTest(Code, {"bar"}); + )cpp"); + + ASSERT_EQ(SetupSuccess, true); + + const auto *Sum = getEntitySummary("bar"); EXPECT_NE(Sum, nullptr); // The closest contributor owns the fact: EXPECT_EQ(*Sum, makeSet(__LINE__, {{"ptr", 1U}})); - Sum = setUpTest(Code, {"Foo"}); + Sum = getEntitySummary("Foo"); - EXPECT_NE(Sum, nullptr); - EXPECT_TRUE(Sum->empty()); + EXPECT_EQ(Sum, nullptr); - Sum = setUpTest(Code, {"main"}); + Sum = getEntitySummary("main"); - EXPECT_NE(Sum, nullptr); - EXPECT_TRUE(Sum->empty()); + EXPECT_EQ(Sum, nullptr); } } // namespace