https://github.com/ziqingluo-90 updated https://github.com/llvm/llvm-project/pull/191333
>From a4ccace9e00f6d08f5a00f17a12e1071fff6bd53 Mon Sep 17 00:00:00 2001 From: Ziqing Luo <[email protected]> Date: Thu, 9 Apr 2026 18:43:40 -0700 Subject: [PATCH 1/3] [NFC][SSAF] Move EntityPointerLevel to a separate folder EntityPointerLevel will later be shared with other summaries besides UnsafeBufferUsage. This commit moves it to a separate file. --- .../EntityPointerLevel/EntityPointerLevel.h | 99 +++++++ .../UnsafeBufferUsage/UnsafeBufferUsage.h | 68 +---- .../UnsafeBufferUsageExtractor.h | 5 - .../Analyses/CMakeLists.txt | 1 + .../EntityPointerLevel/EntityPointerLevel.cpp | 244 ++++++++++++++++++ .../UnsafeBufferUsage/UnsafeBufferUsage.cpp | 4 - .../UnsafeBufferUsageExtractor.cpp | 220 +--------------- .../UnsafeBufferUsageTest.cpp | 4 +- 8 files changed, 353 insertions(+), 292 deletions(-) create mode 100644 clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h create mode 100644 clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h new file mode 100644 index 0000000000000..52caa52e1120d --- /dev/null +++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h @@ -0,0 +1,99 @@ +//===- 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_ENTITYPOINTERLEVEL_H +#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_ENTITYPOINTERLEVEL_ENTITYPOINTERLEVEL_H + +#include "clang/AST/Expr.h" +#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h" +#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.h" +#include <set> + +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. +/// +/// 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 EntityPointerLevel buildEntityPointerLevel(EntityId, unsigned); + + EntityPointerLevel(EntityId Entity, unsigned PointerLevel) + : Entity(Entity), PointerLevel(PointerLevel) {} + +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<EntityPointerLevel, EntityPointerLevel::Comparator>; + +/// 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<EntityPointerLevelSet> +translateEntityPointerLevel(const Expr *E, ASTContext &Ctx, + std::function<EntityId(EntityName EN)> AddEntity); + +EntityPointerLevel buildEntityPointerLevel(EntityId, unsigned); +} // namespace clang::ssaf +#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_ENTITYPOINTERLEVEL_ENTITYPOINTERLEVEL_H diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h index 4e217c2eb87ef..250bad5b72f75 100644 --- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h +++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h @@ -9,7 +9,7 @@ #ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGE_H #define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_UNSAFEBUFFERUSAGE_UNSAFEBUFFERUSAGE_H -#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h" +#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" @@ -18,72 +18,6 @@ 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. -/// -/// 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 UnsafeBufferUsageEntitySummary; - friend class UnsafeBufferUsageTUSummaryExtractor; - friend EntityPointerLevel buildEntityPointerLevel(EntityId, unsigned); - - EntityPointerLevel(EntityId Entity, unsigned PointerLevel) - : Entity(Entity), PointerLevel(PointerLevel) {} - -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<EntityPointerLevel, EntityPointerLevel::Comparator>; - /// An UnsafeBufferUsageEntitySummary is an immutable set of unsafe buffers, in /// the form of EntityPointerLevel. class UnsafeBufferUsageEntitySummary final : public EntitySummary { diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h index 765b2c37562ce..13d4e18b4e81f 100644 --- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h +++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.h @@ -22,11 +22,6 @@ class UnsafeBufferUsageTUSummaryExtractor : public TUSummaryExtractor { 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> diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt b/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt index 926b610aa8dee..c15ff3b3c42e7 100644 --- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt +++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt @@ -5,6 +5,7 @@ set(LLVM_LINK_COMPONENTS add_clang_library(clangScalableStaticAnalysisFrameworkAnalyses CallGraph/CallGraphExtractor.cpp CallGraph/CallGraphJSONFormat.cpp + EntityPointerLevel/EntityPointerLevel.cpp UnsafeBufferUsage/UnsafeBufferUsage.cpp UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp new file mode 100644 index 0000000000000..49a135b13877a --- /dev/null +++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp @@ -0,0 +1,244 @@ +//===- EntityPointerLevel.cpp ----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/AST/StmtVisitor.h" +#include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h" +#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.h" + +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()); +} + +// 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 ssaf::EntityPointerLevelTranslator + : ConstStmtVisitor<EntityPointerLevelTranslator, + Expected<EntityPointerLevelSet>> { + friend class StmtVisitorBase; + + // 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); + } + + EntityPointerLevel createEntityPointerLevelFor(const EntityName &Name) { + return EntityPointerLevel(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()}; + } + + std::function<EntityId(EntityName EN)> AddEntity; + ASTContext &Ctx; + +public: + EntityPointerLevelTranslator(std::function<EntityId(EntityName EN)> AddEntity, + ASTContext &Ctx) + : AddEntity(AddEntity), Ctx(Ctx) {} + + 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 makeCreateEntityNameError(E->getDecl(), Ctx); + } + + // Translate({., ->}f) -> {(MemberDecl, 1)} + Expected<EntityPointerLevelSet> VisitMemberExpr(const MemberExpr *E) { + if (auto EntityName = getEntityName(E->getMemberDecl())) + return EntityPointerLevelSet{createEntityPointerLevelFor(*EntityName)}; + return makeCreateEntityNameError(E->getMemberDecl(), Ctx); + } + + Expected<EntityPointerLevelSet> + VisitOpaqueValueExpr(const OpaqueValueExpr *S) { + return Visit(S->getSourceExpr()); + } +}; + +Expected<EntityPointerLevelSet> clang::ssaf::translateEntityPointerLevel( + const Expr *E, ASTContext &Ctx, + std::function<EntityId(EntityName EN)> AddEntity) { + EntityPointerLevelTranslator Translator(AddEntity, Ctx); + + return Translator.translate(E); +} + +EntityPointerLevel clang::ssaf::buildEntityPointerLevel(EntityId Id, + unsigned PtrLv) { + return EntityPointerLevel({Id, PtrLv}); +} diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp index d325e8df79c20..84f3f9cbb3852 100644 --- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp +++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp @@ -21,10 +21,6 @@ using Object = llvm::json::Object; static constexpr llvm::StringLiteral SummarySerializationKey = "UnsafeBuffers"; -EntityPointerLevel ssaf::buildEntityPointerLevel(EntityId Id, unsigned PtrLv) { - return EntityPointerLevel(Id, PtrLv); -} - UnsafeBufferUsageEntitySummary ssaf::buildUnsafeBufferUsageEntitySummary(EntityPointerLevelSet UnsafeBuffers) { return UnsafeBufferUsageEntitySummary(std::move(UnsafeBuffers)); diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp index c609168e4dc7d..b29eaa6b903d0 100644 --- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp +++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp @@ -11,8 +11,8 @@ #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/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h" #include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h" #include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h" #include "llvm/ADT/STLExtras.h" @@ -24,22 +24,6 @@ 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; - -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( @@ -59,208 +43,17 @@ static llvm::Error makeAddEntitySummaryError(const NamedDecl *FailedContributor, FailedContributor->getNameAsString().c_str(), LocStr.c_str()); } -// 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 makeUnsupportedStmtKindError(E); - } - - 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; - ASTContext &Ctx; - -public: - EntityPointerLevelTranslator(UnsafeBufferUsageTUSummaryExtractor &Extractor, - ASTContext &Ctx) - : Extractor(Extractor), Ctx(Ctx) {} - - 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 makeCreateEntityNameError(E->getDecl(), Ctx); - } - - // Translate({., ->}f) -> {(MemberDecl, 1)} - Expected<EntityPointerLevelSet> VisitMemberExpr(const MemberExpr *E) { - if (auto EntityName = getEntityName(E->getMemberDecl())) - return EntityPointerLevelSet{createEntityPointerLevelFor(*EntityName)}; - return makeCreateEntityNameError(E->getMemberDecl(), Ctx); - } - - Expected<EntityPointerLevelSet> - VisitOpaqueValueExpr(const OpaqueValueExpr *S) { - return Visit(S->getSourceExpr()); - } -}; - Expected<EntityPointerLevelSet> buildEntityPointerLevels(std::set<const Expr *> &&UnsafePointers, UnsafeBufferUsageTUSummaryExtractor &Extractor, - ASTContext &Ctx) { + ASTContext &Ctx, + std::function<EntityId(EntityName)> AddEntity) { EntityPointerLevelSet Result{}; - EntityPointerLevelTranslator Translator{Extractor, Ctx}; llvm::Error AllErrors = llvm::ErrorSuccess(); for (const Expr *Ptr : UnsafePointers) { - Expected<EntityPointerLevelSet> Translation = Translator.translate(Ptr); + Expected<EntityPointerLevelSet> Translation = + translateEntityPointerLevel(Ptr, Ctx, AddEntity); if (Translation) { // Filter out those temporary invalid EntityPointerLevels associated with @@ -297,8 +90,9 @@ static std::set<const Expr *> findUnsafePointersInContributor(const Decl *D) { std::unique_ptr<UnsafeBufferUsageEntitySummary> UnsafeBufferUsageTUSummaryExtractor::extractEntitySummary( const Decl *Contributor, ASTContext &Ctx, llvm::Error &Error) { + auto AddEntity = [this](EntityName EN) { return addEntity(EN); }; Expected<EntityPointerLevelSet> EPLs = buildEntityPointerLevels( - findUnsafePointersInContributor(Contributor), *this, Ctx); + findUnsafePointersInContributor(Contributor), *this, Ctx, AddEntity); if (EPLs) return std::make_unique<UnsafeBufferUsageEntitySummary>( diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp index 51d422c1921af..d2c513b7c70a2 100644 --- a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp +++ b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.cpp @@ -10,6 +10,7 @@ #include "TestFixture.h" #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/UnsafeBufferUsageTest.h" #include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h" @@ -63,9 +64,6 @@ const FunctionDecl *findFnByName(StringRef Name, ASTContext &Ctx) { return findDeclByName<FunctionDecl>(Name, Ctx); } -constexpr inline auto buildEntityPointerLevel = - UnsafeBufferUsageTUSummaryExtractor::buildEntityPointerLevel; - class UnsafeBufferUsageTest : public TestFixture { protected: TUSummary TUSum; >From 3994ca430a89be4b890a340fcf4c8652fd04ce7e Mon Sep 17 00:00:00 2001 From: Ziqing Luo <[email protected]> Date: Thu, 9 Apr 2026 19:01:09 -0700 Subject: [PATCH 2/3] [SSAF][UnsafeBufferUsage] Add APIs to the EntityPointerLevel module for UnsafeBufferUsage - UnsafeBufferUsage serialization uses EntityPointerLevel's API to serialize EntityPointerLevels. - Add APIs to EntityPointerLevel for creating EPLs from Decls and incrementing EPL's pointer level. - Improve UnsafeBufferUsage serialization error messages with a test. --- .../EntityPointerLevel/EntityPointerLevel.h | 31 +++- .../EntityPointerLevelFormat.h | 33 ++++ .../UnsafeBufferUsage/UnsafeBufferUsage.h | 8 +- .../EntityPointerLevel/EntityPointerLevel.cpp | 141 +++++++++++++----- .../UnsafeBufferUsage/UnsafeBufferUsage.cpp | 28 ++-- .../UnsafeBufferUsageExtractor.cpp | 4 +- .../tu-summary-serialization.test | 16 +- 7 files changed, 191 insertions(+), 70 deletions(-) create mode 100644 clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h index 52caa52e1120d..6f5687fd186c9 100644 --- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h +++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h @@ -16,9 +16,10 @@ 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 @@ -38,10 +39,11 @@ class EntityPointerLevel { unsigned PointerLevel; friend class EntityPointerLevelTranslator; + // For unittests: friend EntityPointerLevel buildEntityPointerLevel(EntityId, unsigned); - EntityPointerLevel(EntityId Entity, unsigned PointerLevel) - : Entity(Entity), PointerLevel(PointerLevel) {} + EntityPointerLevel(std::pair<EntityId, unsigned> Pair) + : Entity(Pair.first), PointerLevel(Pair.second) {} public: EntityId getEntity() const { return Entity; } @@ -95,5 +97,24 @@ translateEntityPointerLevel(const Expr *E, ASTContext &Ctx, std::function<EntityId(EntityName EN)> AddEntity); EntityPointerLevel buildEntityPointerLevel(EntityId, unsigned); + +/// 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<EntityPointerLevel> +creatEntityPointerLevel(const NamedDecl *ND, ASTContext &Ctx, + std::function<EntityId(EntityName EN)> 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..766e425338e96 --- /dev/null +++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h @@ -0,0 +1,33 @@ +//===- 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" + +template <typename... Ts> +static inline 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...); +} + +namespace clang::ssaf { +llvm::json::Value +entityPointerLevelToJSON(const EntityPointerLevel &EPL, + JSONFormat::EntityIdToJSONFn EntityId2JSON); + +Expected<EntityPointerLevel> +entityPointerLevelFromJSON(const llvm::json::Value &EPLData, + JSONFormat::EntityIdFromJSONFn EntityIdFromJSON); +} // namespace clang::ssaf +#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_ENTITYPOINTERLEVEL_ENTITYPOINTERLEVELFORMAT_H \ No newline at end of file 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 <set> 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/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp index 49a135b13877a..78afdf956eb57 100644 --- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp +++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp @@ -10,32 +10,34 @@ #include "clang/AST/ASTContext.h" #include "clang/AST/Decl.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 <optional> 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()); +template <typename DeclOrExpr> +static bool hasPtrOrArrType(const DeclOrExpr &E) { + return llvm::isa<PointerType>(E.getType().getCanonicalType()) || + llvm::isa<ArrayType>(E.getType().getCanonicalType()); } -static llvm::Error makeUnsupportedStmtKindError(const Stmt *Unsupported) { - return llvm::createStringError( - "unsupported expression kind for translation to " - "EntityPointerLevel: %s", - Unsupported->getStmtClassName()); +template <typename NodeTy, typename... Ts> +static inline llvm::Error makeErrAtNode(ASTContext &Ctx, const NodeTy &N, + StringRef Fmt, const Ts &...Args) { + std::string LocStr = N.getBeginLoc().printToString(Ctx.getSourceManager()); + llvm::SmallVector<char> FmtData; + + (Fmt + " at %s").toStringRef(FmtData); + return llvm::createStringError(FmtData.data(), Args..., LocStr.c_str()); } -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 inline llvm::Error makeEntityNameErr(ASTContext &Ctx, + const NamedDecl &D) { + return makeErrAtNode(Ctx, D, "failed to create entity name for %s", + D.getNameAsString().data()); } // Translate a pointer type expression 'E' to a (set of) EntityPointerLevel(s) @@ -58,32 +60,25 @@ static llvm::Error makeCreateEntityNameError(const NamedDecl *FailedDecl, // Translate(cond ? p1[5] : p2) -> {(p1, 2), (p2, 1)} // Translate(&arr[5]) -> {(arr, 1)} class ssaf::EntityPointerLevelTranslator - : ConstStmtVisitor<EntityPointerLevelTranslator, - Expected<EntityPointerLevelSet>> { + : public ConstStmtVisitor<EntityPointerLevelTranslator, + Expected<EntityPointerLevelSet>> { friend class StmtVisitorBase; // 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<EntityPointerLevelSet> translateDereferencePointer(const Expr *Ptr) { - assert(hasPointerType(Ptr)); + assert(hasPtrOrArrType(*Ptr)); Expected<EntityPointerLevelSet> SubResult = Visit(Ptr); if (!SubResult) @@ -102,6 +97,29 @@ class ssaf::EntityPointerLevelTranslator : AddEntity(AddEntity), Ctx(Ctx) {} Expected<EntityPointerLevelSet> translate(const Expr *E) { return Visit(E); } + Expected<EntityPointerLevel> translate(const NamedDecl *D, bool IsRet) { + if (IsRet && !isa<FunctionDecl>(D)) + return makeErrAtNode( + Ctx, *D, + "attempt to call getEntityNameForReturn on a NamedDecl of %s kind", + D->getDeclKindName()); + + std::optional<EntityName> EN = + IsRet ? getEntityNameForReturn(cast<FunctionDecl>(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<EntityPointerLevelSet> VisitStmt(const Stmt *E) { @@ -116,7 +134,7 @@ class ssaf::EntityPointerLevelTranslator Expected<EntityPointerLevelSet> 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: @@ -161,7 +179,7 @@ class ssaf::EntityPointerLevelTranslator // Translate((T*)base) -> Translate(p) if p has pointer type // -> {} otherwise Expected<EntityPointerLevelSet> VisitCastExpr(const CastExpr *E) { - if (hasPointerType(E->getSubExpr())) + if (hasPtrOrArrType(*E->getSubExpr())) return Visit(E->getSubExpr()); return EntityPointerLevelSet{}; } @@ -214,14 +232,14 @@ class ssaf::EntityPointerLevelTranslator Expected<EntityPointerLevelSet> 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<EntityPointerLevelSet> 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<EntityPointerLevelSet> @@ -238,7 +256,62 @@ Expected<EntityPointerLevelSet> clang::ssaf::translateEntityPointerLevel( return Translator.translate(E); } +/// Create an EntityPointerLevel from a ValueDecl of a pointer type. +Expected<EntityPointerLevel> clang::ssaf::creatEntityPointerLevel( + const NamedDecl *D, ASTContext &Ctx, + std::function<EntityId(EntityName EN)> AddEntity, bool IsFunRet) { + EntityPointerLevelTranslator Translator(AddEntity, Ctx); + + return Translator.translate(D, 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<EntityPointerLevel> 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<EntityId> Id = EntityIdFromJSON(*EntityIdObj); + + if (!Id) + return Id.takeError(); + + std::optional<uint64_t> 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/UnsafeBufferUsage/UnsafeBufferUsage.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp index 84f3f9cbb3852..47e03476d1063 100644 --- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp +++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp @@ -7,8 +7,9 @@ //===----------------------------------------------------------------------===// #include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.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 +38,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 +48,18 @@ 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(); + llvm::Expected<EntityPointerLevel> EPL = + entityPointerLevelFromJSON(EltData, Fn); - if (!EltDataAsArr || EltDataAsArr->size() != 2) - return llvm::createStringError("expected a json::Array of size 2"); - - const Object *IdData = (*EltDataAsArr)[0].getAsObject(); - std::optional<uint64_t> PtrLvData = (*EltDataAsArr)[1].getAsInteger(); - - if (!IdData || !PtrLvData) - return llvm::createStringError("expected a json::Value of integer type"); - - llvm::Expected<EntityId> 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<UnsafeBufferUsageEntitySummary>( buildUnsafeBufferUsageEntitySummary(std::move(EPLs))); diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp index b29eaa6b903d0..c412855028efb 100644 --- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp +++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp @@ -9,14 +9,14 @@ #include "clang/ScalableStaticAnalysisFramework/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/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 "llvm/ADT/STLExtras.h" -#include "llvm/ADT/iterator_range.h" #include "llvm/Support/Error.h" #include <memory> diff --git a/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-serialization.test b/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-serialization.test index 6a12949f3bbb4..55357acb1db08 100644 --- a/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-serialization.test +++ b/clang/test/Analysis/Scalable/UnsafeBufferUsage/tu-summary-serialization.test @@ -6,13 +6,21 @@ // 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 - +// CHECK-NO-KEY: saw { +// CHECK-NO-KEY: "NotUnsafeBuffers": [ +// CHECK-NO-KEY: [ +// CHECK-NO-KEY: { +// CHECK-NO-KEY: "@": 0 +// CHECK-NO-KEY: }, +// CHECK-NO-KEY: 1 +// CHECK-NO-KEY: ] +// CHECK-NO-KEY: ] +// CHECK-NO-KEY: } 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 >From 11bc5e09f34e30a9e1972631afd0071eda080851 Mon Sep 17 00:00:00 2001 From: Ziqing Luo <[email protected]> Date: Mon, 13 Apr 2026 10:49:09 -0700 Subject: [PATCH 3/3] Update clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Balázs Benics <[email protected]> --- .../Analyses/EntityPointerLevel/EntityPointerLevel.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp index 78afdf956eb57..ebbb060605a6c 100644 --- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp +++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp @@ -28,10 +28,7 @@ template <typename NodeTy, typename... Ts> static inline llvm::Error makeErrAtNode(ASTContext &Ctx, const NodeTy &N, StringRef Fmt, const Ts &...Args) { std::string LocStr = N.getBeginLoc().printToString(Ctx.getSourceManager()); - llvm::SmallVector<char> FmtData; - - (Fmt + " at %s").toStringRef(FmtData); - return llvm::createStringError(FmtData.data(), Args..., LocStr.c_str()); + return llvm::createStringError((Fmt + " at %s").str().c_str(), Args..., LocStr.c_str()); } static inline llvm::Error makeEntityNameErr(ASTContext &Ctx, _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
