[clang] 53739c7 - Revert "[clang][ssaf] Add UnsafeBufferUsage summary extractor for functions (#182941)"
Ziqing Luo via cfe-commits
cfe-commits at lists.llvm.org
Wed Mar 11 12:01:25 PDT 2026
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 at foo", "", {}});
- EntityId E2 = Extractor.addEntity({"c:@F at bar", "", {}});
+ EntityId E1 = Table.getId({"c:@F at foo", "", {}});
+ EntityId E2 = Table.getId({"c:@F at 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 at foo", "", {}});
- EntityId E2 = Extractor.addEntity({"c:@F at bar", "", {}});
- EntityId E3 = Extractor.addEntity({"c:@F at baz", "", {}});
+TEST_F(UnsafeBufferUsageTest, UnsafeBufferUsageEntitySummaryTest) {
+ EntityId E1 = Table.getId({"c:@F at foo", "", {}});
+ EntityId E2 = Table.getId({"c:@F at bar", "", {}});
+ EntityId E3 = Table.getId({"c:@F at 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
More information about the cfe-commits
mailing list