Author: Ziqing Luo Date: 2026-03-11T12:01:12-07:00 New Revision: 53739c75a8720aaef8032628267ed4fd050af038
URL: https://github.com/llvm/llvm-project/commit/53739c75a8720aaef8032628267ed4fd050af038 DIFF: https://github.com/llvm/llvm-project/commit/53739c75a8720aaef8032628267ed4fd050af038.diff LOG: Revert "[clang][ssaf] Add UnsafeBufferUsage summary extractor for functions (#182941)" This reverts commit b7512418d2c1f0ba9ae3016024cb503ded7835d1. There are bots broken by this commit. Added: clang/include/clang/Analysis/Scalable/Analyses/UnsafeBufferUsage/UnsafeBufferUsageBuilder.h Modified: clang/include/clang/Analysis/Scalable/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h clang/lib/Analysis/Scalable/CMakeLists.txt clang/unittests/Analysis/Scalable/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp Removed: clang/include/clang/Analysis/Scalable/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h clang/lib/Analysis/Scalable/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp ################################################################################ diff --git a/clang/include/clang/Analysis/Scalable/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h b/clang/include/clang/Analysis/Scalable/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h index 825fffad49b4a..cae722c837ab0 100644 --- a/clang/include/clang/Analysis/Scalable/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h +++ b/clang/include/clang/Analysis/Scalable/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h @@ -12,6 +12,7 @@ #include "clang/Analysis/Scalable/Model/EntityId.h" #include "clang/Analysis/Scalable/Model/SummaryName.h" #include "clang/Analysis/Scalable/TUSummary/EntitySummary.h" +#include "llvm/ADT/iterator_range.h" #include <set> namespace clang::ssaf { @@ -25,19 +26,23 @@ namespace clang::ssaf { /// *' 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'). +/// 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'); +/// - 'P.EntityId' identifies an lvalue object and 'P.PointerLevel == 0'. +/// The latter case represents address-of expressions. /// /// 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, 1)' is associated with 'int *[10]' of 'p'; +/// '(p, 2)' is associated with 'int *' of 'p'; +/// '(p, 0)' represents '&p'. class EntityPointerLevel { EntityId Entity; unsigned PointerLevel; - friend class UnsafeBufferUsageTUSummaryExtractor; + friend class UnsafeBufferUsageTUSummaryBuilder; + friend class UnsafeBufferUsageEntitySummary; EntityPointerLevel(EntityId Entity, unsigned PointerLevel) : Entity(Entity), PointerLevel(PointerLevel) {} @@ -47,8 +52,7 @@ class EntityPointerLevel { unsigned getPointerLevel() const { return PointerLevel; } bool operator==(const EntityPointerLevel &Other) const { - return std::tie(Entity, PointerLevel) == - std::tie(Other.Entity, Other.PointerLevel); + return Entity == Other.Entity && PointerLevel == Other.PointerLevel; } bool operator!=(const EntityPointerLevel &Other) const { @@ -60,8 +64,7 @@ class EntityPointerLevel { std::tie(Other.Entity, Other.PointerLevel); } - /// Compares `EntityPointerLevel`s; additionally, partially compares - /// `EntityPointerLevel` with `EntityId`. + // Comparator supporting partial comparison against EntityId: struct Comparator { using is_transparent = void; bool operator()(const EntityPointerLevel &L, @@ -85,19 +88,32 @@ using EntityPointerLevelSet = class UnsafeBufferUsageEntitySummary final : public EntitySummary { const EntityPointerLevelSet UnsafeBuffers; - friend class UnsafeBufferUsageTUSummaryExtractor; + friend class UnsafeBufferUsageTUSummaryBuilder; - UnsafeBufferUsageEntitySummary(EntityPointerLevelSet UnsafeBuffers) + UnsafeBufferUsageEntitySummary(EntityPointerLevelSet &&UnsafeBuffers) : EntitySummary(), UnsafeBuffers(std::move(UnsafeBuffers)) {} public: + using const_iterator = EntityPointerLevelSet::const_iterator; + + const_iterator begin() const { return UnsafeBuffers.begin(); } + const_iterator end() const { return UnsafeBuffers.end(); } + + const_iterator find(const EntityPointerLevel &V) const { + return UnsafeBuffers.find(V); + } + + llvm::iterator_range<const_iterator> getSubsetOf(EntityId Entity) const { + return llvm::make_range(UnsafeBuffers.equal_range(Entity)); + } + + /// \return the size of the set of EntityLevelPointers, which represents the + /// set of unsafe buffers + size_t getNumUnsafeBuffers() { return UnsafeBuffers.size(); } + SummaryName getSummaryName() const override { return SummaryName{"UnsafeBufferUsage"}; }; - - bool operator==(const EntityPointerLevelSet &Other) const { - return UnsafeBuffers == Other; - } }; } // namespace clang::ssaf diff --git a/clang/include/clang/Analysis/Scalable/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h b/clang/include/clang/Analysis/Scalable/Analyses/UnsafeBufferUsage/UnsafeBufferUsageBuilder.h similarity index 52% rename from clang/include/clang/Analysis/Scalable/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h rename to clang/include/clang/Analysis/Scalable/Analyses/UnsafeBufferUsage/UnsafeBufferUsageBuilder.h index f4f4221f899cb..1465068ab6840 100644 --- a/clang/include/clang/Analysis/Scalable/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h +++ b/clang/include/clang/Analysis/Scalable/Analyses/UnsafeBufferUsage/UnsafeBufferUsageBuilder.h @@ -1,40 +1,31 @@ -//===- UnsafeBufferUsageExtractor.h -----------------------------*- C++ -*-===// +//===- UnsafeBufferUsageBuilder.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_ANALYSIS_SCALABLE_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGEEXTRACTOR_H -#define LLVM_CLANG_ANALYSIS_SCALABLE_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGEEXTRACTOR_H +#ifndef LLVM_CLANG_ANALYSIS_SCALABLE_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGEBUILDER_H +#define LLVM_CLANG_ANALYSIS_SCALABLE_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGEBUILDER_H #include "clang/Analysis/Scalable/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h" -#include "clang/Analysis/Scalable/Model/EntityId.h" -#include "clang/Analysis/Scalable/Model/EntityName.h" #include "clang/Analysis/Scalable/TUSummary/TUSummaryBuilder.h" -#include "clang/Analysis/Scalable/TUSummary/TUSummaryExtractor.h" -#include "llvm/Support/Error.h" #include <memory> namespace clang::ssaf { -class UnsafeBufferUsageTUSummaryExtractor : public TUSummaryExtractor { +class UnsafeBufferUsageTUSummaryBuilder : public TUSummaryBuilder { public: - UnsafeBufferUsageTUSummaryExtractor(TUSummaryBuilder &Builder) - : TUSummaryExtractor(Builder) {} - static EntityPointerLevel buildEntityPointerLevel(EntityId Entity, unsigned PointerLevel) { return {Entity, PointerLevel}; } - EntityId addEntity(EntityName EN) { return SummaryBuilder.addEntity(EN); } - - std::unique_ptr<UnsafeBufferUsageEntitySummary> - extractEntitySummary(const Decl *Contributor, ASTContext &Ctx, - llvm::Error &Error); - - void HandleTranslationUnit(ASTContext &Ctx) override; + static std::unique_ptr<UnsafeBufferUsageEntitySummary> + buildUnsafeBufferUsageEntitySummary(EntityPointerLevelSet &&UnsafeBuffers) { + return std::make_unique<UnsafeBufferUsageEntitySummary>( + UnsafeBufferUsageEntitySummary(std::move(UnsafeBuffers))); + } }; } // namespace clang::ssaf -#endif // LLVM_CLANG_ANALYSIS_SCALABLE_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGEEXTRACTOR_H +#endif // LLVM_CLANG_ANALYSIS_SCALABLE_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGEBUILDER_H diff --git a/clang/lib/Analysis/Scalable/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp b/clang/lib/Analysis/Scalable/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp deleted file mode 100644 index 440012a2b278b..0000000000000 --- a/clang/lib/Analysis/Scalable/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp +++ /dev/null @@ -1,281 +0,0 @@ -//===- UnsafeBufferUsageExtractor.cpp -------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/Scalable/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h" -#include "clang/AST/ASTConsumer.h" -#include "clang/AST/ASTContext.h" -#include "clang/AST/Decl.h" -#include "clang/AST/DynamicRecursiveASTVisitor.h" -#include "clang/AST/StmtVisitor.h" -#include "clang/Analysis/Analyses/UnsafeBufferUsage.h" -#include "clang/Analysis/Scalable/ASTEntityMapping.h" -#include "clang/Analysis/Scalable/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h" -#include "llvm/ADT/STLExtras.h" -#include "llvm/ADT/iterator_range.h" -#include "llvm/Support/Error.h" -#include <memory> - -namespace { -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()); -} - -constexpr inline auto buildEntityPointerLevel = - UnsafeBufferUsageTUSummaryExtractor::buildEntityPointerLevel; - -// 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 -// address of `E` is not associated with an entity, the translation result is an -// empty set. -// -// The translation is a process of traversing into the pointer 'E' until its -// base address can be represented by an entity, with the number of dereferences -// tracked by incrementing the pointer level. Naturally, taking address of, as -// the inverse operation of dereference, is tracked by decrementing the pointer -// level. -// -// For example, suppose there are pointers and arrays declared as -// int *ptr, **p1, **p2; -// int arr[10][10]; -// , the translation of expressions involving these base addresses will be: -// Translate(ptr + 5) -> {(ptr, 1)} -// Translate(arr[5]) -> {(arr, 2)} -// Translate(cond ? p1[5] : p2) -> {(p1, 2), (p2, 1)} -// Translate(&arr[5]) -> {(arr, 1)} -class EntityPointerLevelTranslator - : ConstStmtVisitor<EntityPointerLevelTranslator, - Expected<EntityPointerLevelSet>> { - friend class StmtVisitorBase; - - // Fallback method for all unsupported expression kind: - llvm::Error fallback(const Stmt *E) { - return llvm::createStringError( - "unsupported expression kind for translation to " - "EntityPointerLevel: %s", - E->getStmtClassName()); - } - - static EntityPointerLevel incrementPointerLevel(const EntityPointerLevel &E) { - return buildEntityPointerLevel(E.getEntity(), E.getPointerLevel() + 1); - } - - static EntityPointerLevel decrementPointerLevel(const EntityPointerLevel &E) { - assert(E.getPointerLevel() > 0); - return buildEntityPointerLevel(E.getEntity(), E.getPointerLevel() - 1); - } - - EntityPointerLevel createEntityPointerLevelFor(const EntityName &Name) { - return buildEntityPointerLevel(Extractor.addEntity(Name), 1); - } - - // The common helper function for Translate(*base): - // Translate(*base) -> Translate(base) with .pointerLevel + 1 - Expected<EntityPointerLevelSet> translateDereferencePointer(const Expr *Ptr) { - assert(hasPointerType(Ptr)); - - Expected<EntityPointerLevelSet> SubResult = Visit(Ptr); - if (!SubResult) - return SubResult.takeError(); - - auto Incremented = llvm::map_range(*SubResult, incrementPointerLevel); - return EntityPointerLevelSet{Incremented.begin(), Incremented.end()}; - } - - UnsafeBufferUsageTUSummaryExtractor &Extractor; - -public: - EntityPointerLevelTranslator(UnsafeBufferUsageTUSummaryExtractor &Extractor) - : Extractor(Extractor) {} - - Expected<EntityPointerLevelSet> translate(const Expr *E) { return Visit(E); } - -private: - Expected<EntityPointerLevelSet> VisitStmt(const Stmt *E) { - return fallback(E); - } - - // Translate(base + x) -> Translate(base) - // Translate(x + base) -> Translate(base) - // Translate(base - x) -> Translate(base) - // Translate(base {+=, -=, =} x) -> Translate(base) - // Translate(x, base) -> Translate(base) - Expected<EntityPointerLevelSet> VisitBinaryOperator(const BinaryOperator *E) { - switch (E->getOpcode()) { - case clang::BO_Add: - if (hasPointerType(E->getLHS())) - return Visit(E->getLHS()); - return Visit(E->getRHS()); - case clang::BO_Sub: - case clang::BO_AddAssign: - case clang::BO_SubAssign: - case clang::BO_Assign: - return Visit(E->getLHS()); - case clang::BO_Comma: - return Visit(E->getRHS()); - default: - return fallback(E); - } - } - - // Translate({++, --}base) -> Translate(base) - // Translate(base{++, --}) -> Translate(base) - // Translate(*base) -> Translate(base) with .pointerLevel += 1 - // Translate(&base) -> {}, if Translate(base) is {} - // -> Translate(base) with .pointerLevel -= 1 - Expected<EntityPointerLevelSet> VisitUnaryOperator(const UnaryOperator *E) { - switch (E->getOpcode()) { - case clang::UO_PostInc: - case clang::UO_PostDec: - case clang::UO_PreInc: - case clang::UO_PreDec: - return Visit(E->getSubExpr()); - case clang::UO_AddrOf: { - Expected<EntityPointerLevelSet> SubResult = Visit(E->getSubExpr()); - if (!SubResult) - return SubResult.takeError(); - - auto Decremented = llvm::map_range(*SubResult, decrementPointerLevel); - return EntityPointerLevelSet{Decremented.begin(), Decremented.end()}; - } - case clang::UO_Deref: - return translateDereferencePointer(E->getSubExpr()); - default: - return fallback(E); - } - } - - // Translate((T*)base) -> Translate(p) if p has pointer type - // -> {} otherwise - Expected<EntityPointerLevelSet> VisitCastExpr(const CastExpr *E) { - if (hasPointerType(E->getSubExpr())) - return Visit(E->getSubExpr()); - return EntityPointerLevelSet{}; - } - - // Translate(f(...)) -> {} if it is an indirect call - // -> {(f_return, 1)}, otherwise - Expected<EntityPointerLevelSet> VisitCallExpr(const CallExpr *E) { - if (auto *FD = E->getDirectCallee()) - if (auto FDEntityName = getEntityNameForReturn(FD)) - return EntityPointerLevelSet{ - createEntityPointerLevelFor(*FDEntityName)}; - return EntityPointerLevelSet{}; - } - - // Translate(base[x]) -> Translate(*base) - Expected<EntityPointerLevelSet> - VisitArraySubscriptExpr(const ArraySubscriptExpr *E) { - return translateDereferencePointer(E->getBase()); - } - - // Translate(cond ? base1 : base2) := Translate(base1) U Translate(base2) - Expected<EntityPointerLevelSet> - VisitAbstractConditionalOperator(const AbstractConditionalOperator *E) { - Expected<EntityPointerLevelSet> ReT = Visit(E->getTrueExpr()); - Expected<EntityPointerLevelSet> ReF = Visit(E->getFalseExpr()); - - if (ReT && ReF) { - ReT->insert(ReF->begin(), ReF->end()); - return ReT; - } - if (!ReF && !ReT) - return llvm::joinErrors(ReT.takeError(), ReF.takeError()); - if (!ReF) - return ReF.takeError(); - return ReT.takeError(); - } - - Expected<EntityPointerLevelSet> VisitParenExpr(const ParenExpr *E) { - return Visit(E->getSubExpr()); - } - - // Translate("string-literal") -> {} - // Buffer accesses on string literals are unsafe, but string literals are not - // entities so there is no EntityPointerLevel associated with it. - Expected<EntityPointerLevelSet> VisitStringLiteral(const StringLiteral *E) { - return EntityPointerLevelSet{}; - } - - // Translate(DRE) -> {(Decl, 1)} - Expected<EntityPointerLevelSet> VisitDeclRefExpr(const DeclRefExpr *E) { - if (auto EntityName = getEntityName(E->getDecl())) - return EntityPointerLevelSet{createEntityPointerLevelFor(*EntityName)}; - return llvm::createStringError( - "failed to create entity name from the Decl of " + - E->getNameInfo().getAsString()); - } - - // Translate({., ->}f) -> {(MemberDecl, 1)} - Expected<EntityPointerLevelSet> VisitMemberExpr(const MemberExpr *E) { - if (auto EntityName = getEntityName(E->getMemberDecl())) - return EntityPointerLevelSet{createEntityPointerLevelFor(*EntityName)}; - return llvm::createStringError( - "failed to create entity name from the MemberDecl of " + - E->getMemberDecl()->getNameAsString()); - } - - Expected<EntityPointerLevelSet> - VisitOpaqueValueExpr(const OpaqueValueExpr *S) { - return Visit(S->getSourceExpr()); - } -}; - -Expected<EntityPointerLevelSet> -buildEntityPointerLevels(std::set<const Expr *> &&UnsafePointers, - UnsafeBufferUsageTUSummaryExtractor &Extractor) { - EntityPointerLevelSet Result{}; - EntityPointerLevelTranslator Translator{Extractor}; - llvm::Error AllErrors = llvm::ErrorSuccess(); - - for (const Expr *Ptr : UnsafePointers) { - Expected<EntityPointerLevelSet> Translation = Translator.translate(Ptr); - - 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; - }); - Result.insert(FilteredTranslation.begin(), FilteredTranslation.end()); - continue; - } - AllErrors = llvm::joinErrors(std::move(AllErrors), Translation.takeError()); - } - if (AllErrors) - return AllErrors; - return Result; -} -} // namespace - -std::unique_ptr<UnsafeBufferUsageEntitySummary> -UnsafeBufferUsageTUSummaryExtractor::extractEntitySummary( - const Decl *Contributor, ASTContext &Ctx, llvm::Error &Error) { - // FIXME: findUnsafePointers should accept more kinds of `Decl`s than just - // `FunctionDecl`: - if (const auto *FD = dyn_cast<FunctionDecl>(Contributor)) { - Expected<EntityPointerLevelSet> EPLs = - buildEntityPointerLevels(findUnsafePointers(FD), *this); - - if (EPLs) - return std::make_unique<UnsafeBufferUsageEntitySummary>( - UnsafeBufferUsageEntitySummary(std::move(*EPLs))); - Error = EPLs.takeError(); - } - return nullptr; -} - -void UnsafeBufferUsageTUSummaryExtractor::HandleTranslationUnit( - ASTContext &Ctx) { - // FIXME: impl me! -} diff --git a/clang/lib/Analysis/Scalable/CMakeLists.txt b/clang/lib/Analysis/Scalable/CMakeLists.txt index 5294a5925b7e7..4593fbcd515b5 100644 --- a/clang/lib/Analysis/Scalable/CMakeLists.txt +++ b/clang/lib/Analysis/Scalable/CMakeLists.txt @@ -3,7 +3,6 @@ set(LLVM_LINK_COMPONENTS ) add_clang_library(clangAnalysisScalable - Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp ASTEntityMapping.cpp EntityLinker/EntityLinker.cpp Model/BuildNamespace.cpp diff --git a/clang/unittests/Analysis/Scalable/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp b/clang/unittests/Analysis/Scalable/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp index 8d65ce51b2926..407dafe1f02fe 100644 --- a/clang/unittests/Analysis/Scalable/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp +++ b/clang/unittests/Analysis/Scalable/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp @@ -7,143 +7,36 @@ //===----------------------------------------------------------------------===// #include "clang/Analysis/Scalable/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h" -#include "clang/AST/DynamicRecursiveASTVisitor.h" -#include "clang/Analysis/Scalable/ASTEntityMapping.h" -#include "clang/Analysis/Scalable/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h" +#include "clang/Analysis/Scalable/Analyses/UnsafeBufferUsage/UnsafeBufferUsageBuilder.h" #include "clang/Analysis/Scalable/Model/EntityId.h" -#include "clang/Analysis/Scalable/Model/EntityName.h" -#include "clang/Analysis/Scalable/TUSummary/TUSummary.h" -#include "clang/Analysis/Scalable/TUSummary/TUSummaryBuilder.h" -#include "clang/Tooling/Tooling.h" -#include "gmock/gmock.h" +#include "clang/Analysis/Scalable/Model/EntityIdTable.h" #include "gtest/gtest.h" using namespace clang; using namespace ssaf; -using testing::UnorderedElementsAre; namespace { -template <typename SomeDecl = NamedDecl> -const SomeDecl *findDeclByName(StringRef Name, ASTContext &Ctx) { - class NamedDeclFinder : public DynamicRecursiveASTVisitor { - public: - StringRef SearchingName; - const NamedDecl *FoundDecl = nullptr; - - NamedDeclFinder(StringRef SearchingName) : SearchingName(SearchingName) {} - - bool VisitDecl(Decl *D) override { - if (const auto *ND = dyn_cast<NamedDecl>(D)) { - if (ND->getNameAsString() == SearchingName) { - FoundDecl = ND; - return false; - } - } - return true; - } - }; - - NamedDeclFinder Finder(Name); - - Finder.TraverseDecl(Ctx.getTranslationUnitDecl()); - return dyn_cast_or_null<SomeDecl>(Finder.FoundDecl); -} - -const FunctionDecl *findFnByName(StringRef Name, ASTContext &Ctx) { - return findDeclByName<FunctionDecl>(Name, Ctx); -} - constexpr inline auto buildEntityPointerLevel = - UnsafeBufferUsageTUSummaryExtractor::buildEntityPointerLevel; + UnsafeBufferUsageTUSummaryBuilder::buildEntityPointerLevel; +constexpr inline auto buildUnsafeBufferUsageEntitySummary = + UnsafeBufferUsageTUSummaryBuilder::buildUnsafeBufferUsageEntitySummary; class UnsafeBufferUsageTest : public testing::Test { protected: - TUSummary TUSummary; - TUSummaryBuilder TUSummaryBuilder; - UnsafeBufferUsageTUSummaryExtractor Extractor; - std::unique_ptr<ASTUnit> AST; - - UnsafeBufferUsageTest() - : TUSummary( - BuildNamespace(BuildNamespaceKind::CompilationUnit, "Mock.cpp")), - TUSummaryBuilder(TUSummary), Extractor(TUSummaryBuilder) {} - - std::unique_ptr<UnsafeBufferUsageEntitySummary> - setUpTest(StringRef Code, StringRef ContributorName) { - AST = tooling::buildASTFromCodeWithArgs( - Code, {"-Wno-unused-value -Wno-int-to-pointer-cast"}); - - const auto *ContributorDefn = - findDeclByName(ContributorName, AST->getASTContext()); - std::optional<EntityName> EN = getEntityName(ContributorDefn); - - if (!ContributorDefn || !EN) - return nullptr; - - llvm::Error Error = llvm::ErrorSuccess(); - auto Sum = Extractor.extractEntitySummary(ContributorDefn, - AST->getASTContext(), Error); - - if (Error) { - llvm::consumeError(std::move(Error)); - return nullptr; - } - return Sum; - } - - std::optional<EntityId> getEntityId(StringRef Name) { - if (const auto *D = findDeclByName(Name, AST->getASTContext())) - if (auto EntityName = getEntityName(D)) - return Extractor.addEntity(*EntityName); - return std::nullopt; - } - - std::optional<EntityId> getEntityIdForReturn(StringRef FunName) { - if (const auto *D = findFnByName(FunName, AST->getASTContext())) - if (auto EntityName = getEntityNameForReturn(D)) - return Extractor.addEntity(*EntityName); - return std::nullopt; - } - - // Same as `std::pair<StringName, unsigned>` for a pair of entity declaration - // name and a pointer level with an extra optional flag for whether the entity - // represents a function return value: - struct EPLPair { - EPLPair(StringRef Name, unsigned Lv, bool isFunRet = false) - : Name(Name), Lv(Lv), isFunRet(isFunRet) {} - - StringRef Name; - unsigned Lv; - bool isFunRet; - }; - - EntityPointerLevelSet makeSet(unsigned Line, ArrayRef<EPLPair> Pairs) { - auto EPLs = llvm::map_range( - Pairs, [this, Line](const EPLPair &Pair) -> EntityPointerLevel { - std::optional<EntityId> Entity = Pair.isFunRet - ? getEntityIdForReturn(Pair.Name) - : getEntityId(Pair.Name); - if (!Entity) - ADD_FAILURE_AT(__FILE__, Line) << "Entity not found: " << Pair.Name; - return buildEntityPointerLevel(*Entity, Pair.Lv); - }); - return EntityPointerLevelSet{EPLs.begin(), EPLs.end()}; - } + EntityIdTable Table; }; ////////////////////////////////////////////////////////////// // Data Structure Tests // ////////////////////////////////////////////////////////////// -static llvm::iterator_range<EntityPointerLevelSet::iterator> -getSubsetOf(const EntityPointerLevelSet &Set, EntityId Entity) { - return llvm::make_range(Set.equal_range(Entity)); -} +#define EXPECT_CONTAINS(Set, Elt) EXPECT_NE((Set).find(Elt), (Set).end()) +#define EXPECT_EXCLUDES(Set, Elt) EXPECT_EQ((Set).find(Elt), (Set).end()) TEST_F(UnsafeBufferUsageTest, EntityPointerLevelComparison) { - EntityId E1 = Extractor.addEntity({"c:@F@foo", "", {}}); - EntityId E2 = Extractor.addEntity({"c:@F@bar", "", {}}); + EntityId E1 = Table.getId({"c:@F@foo", "", {}}); + EntityId E2 = Table.getId({"c:@F@bar", "", {}}); auto P1 = buildEntityPointerLevel(E1, 2); auto P2 = buildEntityPointerLevel(E1, 2); @@ -160,286 +53,48 @@ TEST_F(UnsafeBufferUsageTest, EntityPointerLevelComparison) { EXPECT_FALSE(P2 < P1); } -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", "", {}}); +TEST_F(UnsafeBufferUsageTest, UnsafeBufferUsageEntitySummaryTest) { + EntityId E1 = Table.getId({"c:@F@foo", "", {}}); + EntityId E2 = Table.getId({"c:@F@bar", "", {}}); + EntityId E3 = Table.getId({"c:@F@baz", "", {}}); auto P1 = buildEntityPointerLevel(E1, 1); auto P2 = buildEntityPointerLevel(E1, 2); auto P3 = buildEntityPointerLevel(E2, 1); auto P4 = buildEntityPointerLevel(E2, 2); auto P5 = buildEntityPointerLevel(E3, 1); + auto P6 = buildEntityPointerLevel(E3, 2); EntityPointerLevelSet Set{P1, P2, P3, P4, P5}; + auto ES = buildUnsafeBufferUsageEntitySummary(std::move(Set)); + ASSERT_TRUE(ES); - EXPECT_THAT(Set, UnorderedElementsAre(P1, P2, P3, P4, P5)); - EXPECT_THAT(getSubsetOf(Set, E1), UnorderedElementsAre(P1, P2)); - EXPECT_THAT(getSubsetOf(Set, E2), UnorderedElementsAre(P3, P4)); - EXPECT_THAT(getSubsetOf(Set, E3), UnorderedElementsAre(P5)); -} - -////////////////////////////////////////////////////////////// -// Extractor Tests // -////////////////////////////////////////////////////////////// - -TEST_F(UnsafeBufferUsageTest, SimpleFunctionWithUnsafePointer) { - auto Sum = setUpTest(R"cpp( - void foo(int *p) { - p[5]; - } - )cpp", - "foo"); - - EXPECT_NE(Sum, nullptr); - EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}})); -} + EXPECT_CONTAINS(*ES, P1); + EXPECT_CONTAINS(*ES, P2); + EXPECT_CONTAINS(*ES, P3); + EXPECT_CONTAINS(*ES, P4); + EXPECT_CONTAINS(*ES, P5); + EXPECT_EXCLUDES(*ES, P6); -TEST_F(UnsafeBufferUsageTest, PointerArithmetic) { - auto Sum = setUpTest(R"cpp( - void foo(int *p, int *q) { - *(p + 5); - *(q - 3); - } - )cpp", - "foo"); - - EXPECT_NE(Sum, nullptr); - EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}, {"q", 1U}})); -} - -TEST_F(UnsafeBufferUsageTest, PointerIncrementDecrement) { - auto Sum = setUpTest(R"cpp( - void foo(int *p, int *q, int *r, int *s) { - (++p)[5]; - (q++)[5]; - (--r)[5]; - (s--)[5]; - } - )cpp", - "foo"); - - EXPECT_NE(Sum, nullptr); - EXPECT_EQ(*Sum, - makeSet(__LINE__, {{"p", 1U}, {"q", 1U}, {"r", 1U}, {"s", 1U}})); -} - -TEST_F(UnsafeBufferUsageTest, PointerAssignment) { - auto Sum = setUpTest(R"cpp( - void foo(int *p, int *q) { - (p = q + 5)[5]; - } - )cpp", - "foo"); - - EXPECT_NE(Sum, nullptr); - EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}, {"q", 1U}})); -} - -TEST_F(UnsafeBufferUsageTest, CompoundAssignment) { - auto Sum = setUpTest(R"cpp( - void foo(int *p, int *q) { - (p += 5)[5]; - (q -= 3)[5]; - } - )cpp", - "foo"); - - EXPECT_NE(Sum, nullptr); - EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}, {"q", 1U}})); -} + EntityPointerLevelSet Subset1{ES->getSubsetOf(E1).begin(), + ES->getSubsetOf(E1).end()}; -TEST_F(UnsafeBufferUsageTest, MultiLevelPointer) { - auto Sum = setUpTest(R"cpp( - void foo(int **p, int **q, int **r) { - (*p)[5]; - *(*q); - *(q[5]); - r[5][5]; - } - )cpp", - "foo"); + EXPECT_CONTAINS(Subset1, P1); + EXPECT_CONTAINS(Subset1, P2); + EXPECT_EQ(Subset1.size(), 2U); - EXPECT_NE(Sum, nullptr); - EXPECT_EQ(*Sum, - makeSet(__LINE__, {{"p", 2U}, {"q", 1U}, {"r", 1U}, {"r", 2U}})); -} - -TEST_F(UnsafeBufferUsageTest, ConditionalOperator) { - auto Sum = setUpTest(R"cpp( - void foo(int **p, int **q, int cond) { - (cond ? *p : *q)[5]; - cond ? p[5] : q[5]; - } - )cpp", - "foo"); - - EXPECT_NE(Sum, nullptr); - EXPECT_EQ(*Sum, - makeSet(__LINE__, {{"p", 1U}, {"q", 1U}, {"p", 2U}, {"q", 2U}})); -} - -TEST_F(UnsafeBufferUsageTest, CastExpression) { - auto Sum = setUpTest(R"cpp( - void foo(void *p, int q) { - ((int*)p)[5]; - ((int*)q)[5]; - } - )cpp", - "foo"); - - EXPECT_NE(Sum, nullptr); - EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}})); -} - -TEST_F(UnsafeBufferUsageTest, CommaOperator) { - auto Sum = setUpTest(R"cpp( - void foo(int *p, int x) { - (x++, p)[5]; - } - )cpp", - "foo"); - - EXPECT_NE(Sum, nullptr); - EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}})); -} - -TEST_F(UnsafeBufferUsageTest, CommaOperator2) { - auto Sum = setUpTest(R"cpp( - void foo(int **p, int **q, int x) { - (p[x] = 0, q[x] = 0)[5]; - } - )cpp", - "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( - void foo(int *p) { - (((p)))[5]; - } - )cpp", - "foo"); - - EXPECT_NE(Sum, nullptr); - EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}})); -} - -TEST_F(UnsafeBufferUsageTest, ArrayParameter) { - auto Sum = setUpTest(R"cpp( - void foo(int arr[], int arr2[][10]) { - int n = 5; - arr[100]; - arr2[5][n]; - } - )cpp", - "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( - int ** (*fp)(); - int ** foo() { - fp = &foo; - foo()[5]; - (*fp())[5]; - } - )cpp", - "foo"); - - EXPECT_NE(Sum, nullptr); - // No (foo, 2) becasue indirect calls are ignored. - EXPECT_EQ(*Sum, makeSet(__LINE__, {{"foo", 1U, true}})); -} - -TEST_F(UnsafeBufferUsageTest, StructMemberAccess) { - auto Sum = setUpTest(R"cpp( - struct S { - int *ptr; - int (*ptr_to_arr)[10]; - }; - void foo(struct S obj) { - int n = 5; - obj.ptr[5]; - (*obj.ptr_to_arr)[n]; - } - )cpp", - "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( - void foo() { - "hello"[5]; - } - )cpp", - "foo"); - - EXPECT_NE(Sum, nullptr); - // String literals should not generate pointer kind variables - EXPECT_EQ(*Sum, makeSet(__LINE__, {})); -} - -TEST_F(UnsafeBufferUsageTest, OpaqueValueExpr) { - auto Sum = setUpTest(R"cpp( - void foo(int *p, int *q) { - (p ?: q)[5]; - } - )cpp", - "foo"); - - EXPECT_NE(Sum, nullptr); - EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1U}, {"q", 1U}})); -} - -TEST_F(UnsafeBufferUsageTest, AddressOfOperator) { - auto Sum = 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__, {})); -} - -TEST_F(UnsafeBufferUsageTest, AddressOfThenDereference) { - auto Sum = setUpTest(R"cpp( - void foo(int *p, int *q) { - (*(&p))[5]; - (&(*q))[5]; - } - )cpp", - "foo"); - - EXPECT_NE(Sum, nullptr); - EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 1}, {"q", 1}})); -} + EntityPointerLevelSet Subset2{ES->getSubsetOf(E2).begin(), + ES->getSubsetOf(E2).end()}; -TEST_F(UnsafeBufferUsageTest, PointerToArrayOfPointers) { - auto Sum = setUpTest(R"cpp( - void foo() { - int * arr[10]; - int * (*p)[10] = arr; + EXPECT_CONTAINS(Subset2, P3); + EXPECT_CONTAINS(Subset2, P4); + EXPECT_EQ(Subset2.size(), 2U); - (*p)[5][5]; // '(*p)[5]' is unsafe - // '(*p)' is fine because 5 < 10 - } - )cpp", - "foo"); + EntityPointerLevelSet Subset3{ES->getSubsetOf(E3).begin(), + ES->getSubsetOf(E3).end()}; - EXPECT_NE(Sum, nullptr); - EXPECT_EQ(*Sum, makeSet(__LINE__, {{"p", 3}})); + EXPECT_CONTAINS(Subset3, P5); + EXPECT_EXCLUDES(Subset3, P6); + EXPECT_EQ(Subset3.size(), 1U); } } // namespace _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
