Skip to content

Commit 75790bf

Browse files
[SSAF][UnsafeBufferUsage] Add APIs to the EntityPointerLevel module for UnsafeBufferUsage (#191333)
- 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. --------- Co-authored-by: Balázs Benics <benicsbalazs@gmail.com>
1 parent 13e18f8 commit 75790bf

7 files changed

Lines changed: 193 additions & 73 deletions

File tree

clang/include/clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,15 @@
1212
#include "clang/AST/Expr.h"
1313
#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
1414
#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.h"
15+
#include "llvm/ADT/STLFunctionalExtras.h"
1516
#include <set>
1617

1718
namespace clang::ssaf {
1819

19-
/// An EntityPointerLevel represents a level of the declared pointer/array
20-
/// type of an entity. In the fully-expanded spelling of the declared type, a
21-
/// EntityPointerLevel is associated with a '*' (or a '[]`) in that declaration.
20+
/// An EntityPointerLevel is associated with a level of the declared
21+
/// pointer/array type of an entity. In the fully-expanded spelling of the
22+
/// declared type, a EntityPointerLevel is associated with a '*' (or a '[]`) in
23+
/// that declaration.
2224
///
2325
/// For example, for 'int *p[10];', there are two EntityPointerLevels. One
2426
/// is associated with 'int *[10]' of 'p' and the other is associated with 'int
@@ -38,10 +40,11 @@ class EntityPointerLevel {
3840
unsigned PointerLevel;
3941

4042
friend class EntityPointerLevelTranslator;
43+
// For unittests:
4144
friend EntityPointerLevel buildEntityPointerLevel(EntityId, unsigned);
4245

43-
EntityPointerLevel(EntityId Entity, unsigned PointerLevel)
44-
: Entity(Entity), PointerLevel(PointerLevel) {}
46+
EntityPointerLevel(std::pair<EntityId, unsigned> Pair)
47+
: Entity(Pair.first), PointerLevel(Pair.second) {}
4548

4649
public:
4750
EntityId getEntity() const { return Entity; }
@@ -90,10 +93,28 @@ using EntityPointerLevelSet =
9093
/// \param Ctx the AST context of `E`
9194
/// \param AddEntity the callback provided by the caller to convert EntityNames
9295
/// to EntityIds.
93-
llvm::Expected<EntityPointerLevelSet>
94-
translateEntityPointerLevel(const Expr *E, ASTContext &Ctx,
95-
std::function<EntityId(EntityName EN)> AddEntity);
96+
llvm::Expected<EntityPointerLevelSet> translateEntityPointerLevel(
97+
const Expr *E, ASTContext &Ctx,
98+
llvm::function_ref<EntityId(EntityName EN)> AddEntity);
9699

97100
EntityPointerLevel buildEntityPointerLevel(EntityId, unsigned);
101+
102+
/// Create an EntityPointerLevel (EPL) from a NamedDecl of a pointer/array type.
103+
///
104+
/// \param ND the NamedDecl of a pointer/array type.
105+
/// \param AddEntity the callback provided by the caller to convert EntityNames
106+
/// to EntityIds.
107+
/// \param IsFunRet true iff the created EPL is associated with the return type
108+
/// of a function entity.
109+
llvm::Expected<EntityPointerLevel>
110+
creatEntityPointerLevel(const NamedDecl *ND,
111+
llvm::function_ref<EntityId(EntityName EN)> AddEntity,
112+
bool IsFunRet = false);
113+
114+
/// Creates a new EntityPointerLevel (EPL) from `E` by incrementing `E`'s
115+
/// pointer level.
116+
/// \return the EPL that is associated with the pointee (or array element) type
117+
/// of `E`'s associated pointer/array tyoe of the same entity.
118+
EntityPointerLevel incrementPointerLevel(const EntityPointerLevel &E);
98119
} // namespace clang::ssaf
99120
#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_ENTITYPOINTERLEVEL_ENTITYPOINTERLEVEL_H
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
//===- EntityPointerLevelFormat.h -------------------------------*- C++-*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_ENTITYPOINTERLEVEL_ENTITYPOINTERLEVELFORMAT_H
10+
#define LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_ENTITYPOINTERLEVEL_ENTITYPOINTERLEVELFORMAT_H
11+
12+
#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h"
13+
#include "clang/ScalableStaticAnalysisFramework/Core/Serialization/JSONFormat.h"
14+
15+
template <typename... Ts>
16+
llvm::Error makeSawButExpectedError(const llvm::json::Value &Saw,
17+
llvm::StringRef Expected,
18+
const Ts &...ExpectedArgs) {
19+
std::string Fmt = ("saw %s but expected " + Expected).str();
20+
std::string SawStr = llvm::formatv("{0:2}", Saw).str();
21+
22+
return llvm::createStringError(Fmt.c_str(), SawStr.c_str(), ExpectedArgs...);
23+
}
24+
25+
namespace clang::ssaf {
26+
llvm::json::Value
27+
entityPointerLevelToJSON(const EntityPointerLevel &EPL,
28+
JSONFormat::EntityIdToJSONFn EntityId2JSON);
29+
30+
Expected<EntityPointerLevel>
31+
entityPointerLevelFromJSON(const llvm::json::Value &EPLData,
32+
JSONFormat::EntityIdFromJSONFn EntityIdFromJSON);
33+
} // namespace clang::ssaf
34+
35+
#endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_ANALYSES_ENTITYPOINTERLEVEL_ENTITYPOINTERLEVELFORMAT_H

clang/include/clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,10 @@
1212
#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h"
1313
#include "clang/ScalableStaticAnalysisFramework/Core/Model/SummaryName.h"
1414
#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/EntitySummary.h"
15-
#include "llvm/ADT/StringRef.h"
16-
#include "llvm/ADT/iterator_range.h"
17-
#include <set>
1815

1916
namespace clang::ssaf {
20-
21-
/// An UnsafeBufferUsageEntitySummary is an immutable set of unsafe buffers, in
22-
/// the form of EntityPointerLevel.
17+
/// An UnsafeBufferUsageEntitySummary contains a set of EntityPointerLevels
18+
/// extracted from unsafe buffer pointers contributed by an entity.
2319
class UnsafeBufferUsageEntitySummary final : public EntitySummary {
2420
const EntityPointerLevelSet UnsafeBuffers;
2521

clang/lib/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.cpp

Lines changed: 103 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -10,33 +10,32 @@
1010
#include "clang/AST/ASTContext.h"
1111
#include "clang/AST/Decl.h"
1212
#include "clang/AST/StmtVisitor.h"
13+
#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h"
1314
#include "clang/ScalableStaticAnalysisFramework/Core/ASTEntityMapping.h"
1415
#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityName.h"
16+
#include <optional>
1517

1618
using namespace clang;
1719
using namespace ssaf;
1820

19-
static bool hasPointerType(const Expr *E) {
20-
auto Ty = E->getType();
21-
return !Ty.isNull() && !Ty->isFunctionPointerType() &&
22-
(Ty->isPointerType() || Ty->isArrayType());
21+
namespace {
22+
template <typename DeclOrExpr> bool hasPtrOrArrType(const DeclOrExpr *E) {
23+
return llvm::isa<PointerType, ArrayType>(E->getType().getCanonicalType());
2324
}
2425

25-
static llvm::Error makeUnsupportedStmtKindError(const Stmt *Unsupported) {
26-
return llvm::createStringError(
27-
"unsupported expression kind for translation to "
28-
"EntityPointerLevel: %s",
29-
Unsupported->getStmtClassName());
26+
template <typename NodeTy, typename... Ts>
27+
llvm::Error makeErrAtNode(ASTContext &Ctx, const NodeTy &N, StringRef Fmt,
28+
const Ts &...Args) {
29+
std::string LocStr = N.getBeginLoc().printToString(Ctx.getSourceManager());
30+
return llvm::createStringError((Fmt + " at %s").str().c_str(), Args...,
31+
LocStr.c_str());
3032
}
3133

32-
static llvm::Error makeCreateEntityNameError(const NamedDecl *FailedDecl,
33-
ASTContext &Ctx) {
34-
std::string LocStr = FailedDecl->getSourceRange().getBegin().printToString(
35-
Ctx.getSourceManager());
36-
return llvm::createStringError(
37-
"failed to create entity name for %s declared at %s",
38-
FailedDecl->getNameAsString().c_str(), LocStr.c_str());
34+
llvm::Error makeEntityNameErr(ASTContext &Ctx, const NamedDecl &D) {
35+
return makeErrAtNode(Ctx, D, "failed to create entity name for %s",
36+
D.getNameAsString().data());
3937
}
38+
} // namespace
4039

4140
namespace clang::ssaf {
4241
// Translate a pointer type expression 'E' to a (set of) EntityPointerLevel(s)
@@ -65,26 +64,19 @@ class EntityPointerLevelTranslator
6564

6665
// Fallback method for all unsupported expression kind:
6766
llvm::Error fallback(const Stmt *E) {
68-
return makeUnsupportedStmtKindError(E);
69-
}
70-
71-
static EntityPointerLevel incrementPointerLevel(const EntityPointerLevel &E) {
72-
return EntityPointerLevel(E.getEntity(), E.getPointerLevel() + 1);
73-
}
74-
75-
static EntityPointerLevel decrementPointerLevel(const EntityPointerLevel &E) {
76-
assert(E.getPointerLevel() > 0);
77-
return EntityPointerLevel(E.getEntity(), E.getPointerLevel() - 1);
67+
return makeErrAtNode(Ctx, *E,
68+
"attempt to translate %s to EntityPointerLevels",
69+
E->getStmtClassName());
7870
}
7971

8072
EntityPointerLevel createEntityPointerLevelFor(const EntityName &Name) {
81-
return EntityPointerLevel(AddEntity(Name), 1);
73+
return EntityPointerLevel({AddEntity(Name), 1});
8274
}
8375

8476
// The common helper function for Translate(*base):
8577
// Translate(*base) -> Translate(base) with .pointerLevel + 1
8678
Expected<EntityPointerLevelSet> translateDereferencePointer(const Expr *Ptr) {
87-
assert(hasPointerType(Ptr));
79+
assert(hasPtrOrArrType(Ptr));
8880

8981
Expected<EntityPointerLevelSet> SubResult = Visit(Ptr);
9082
if (!SubResult)
@@ -103,6 +95,29 @@ class EntityPointerLevelTranslator
10395
: AddEntity(AddEntity), Ctx(Ctx) {}
10496

10597
Expected<EntityPointerLevelSet> translate(const Expr *E) { return Visit(E); }
98+
Expected<EntityPointerLevel> translate(const NamedDecl *D, bool IsRet) {
99+
if (IsRet && !isa<FunctionDecl>(D))
100+
return makeErrAtNode(
101+
Ctx, *D,
102+
"attempt to call getEntityNameForReturn on a NamedDecl of %s kind",
103+
D->getDeclKindName());
104+
105+
std::optional<EntityName> EN =
106+
IsRet ? getEntityNameForReturn(cast<FunctionDecl>(D))
107+
: getEntityName(D);
108+
if (EN)
109+
return createEntityPointerLevelFor(*EN);
110+
return makeEntityNameErr(Ctx, *D);
111+
}
112+
113+
static EntityPointerLevel incrementPointerLevel(const EntityPointerLevel &E) {
114+
return EntityPointerLevel({E.getEntity(), E.getPointerLevel() + 1});
115+
}
116+
117+
static EntityPointerLevel decrementPointerLevel(const EntityPointerLevel &E) {
118+
assert(E.getPointerLevel() > 0);
119+
return EntityPointerLevel({E.getEntity(), E.getPointerLevel() - 1});
120+
}
106121

107122
private:
108123
Expected<EntityPointerLevelSet> VisitStmt(const Stmt *E) {
@@ -117,7 +132,7 @@ class EntityPointerLevelTranslator
117132
Expected<EntityPointerLevelSet> VisitBinaryOperator(const BinaryOperator *E) {
118133
switch (E->getOpcode()) {
119134
case clang::BO_Add:
120-
if (hasPointerType(E->getLHS()))
135+
if (hasPtrOrArrType(E->getLHS()))
121136
return Visit(E->getLHS());
122137
return Visit(E->getRHS());
123138
case clang::BO_Sub:
@@ -162,7 +177,7 @@ class EntityPointerLevelTranslator
162177
// Translate((T*)base) -> Translate(p) if p has pointer type
163178
// -> {} otherwise
164179
Expected<EntityPointerLevelSet> VisitCastExpr(const CastExpr *E) {
165-
if (hasPointerType(E->getSubExpr()))
180+
if (hasPtrOrArrType(E->getSubExpr()))
166181
return Visit(E->getSubExpr());
167182
return EntityPointerLevelSet{};
168183
}
@@ -215,14 +230,14 @@ class EntityPointerLevelTranslator
215230
Expected<EntityPointerLevelSet> VisitDeclRefExpr(const DeclRefExpr *E) {
216231
if (auto EntityName = getEntityName(E->getDecl()))
217232
return EntityPointerLevelSet{createEntityPointerLevelFor(*EntityName)};
218-
return makeCreateEntityNameError(E->getDecl(), Ctx);
233+
return makeEntityNameErr(Ctx, *E->getDecl());
219234
}
220235

221236
// Translate({., ->}f) -> {(MemberDecl, 1)}
222237
Expected<EntityPointerLevelSet> VisitMemberExpr(const MemberExpr *E) {
223238
if (auto EntityName = getEntityName(E->getMemberDecl()))
224239
return EntityPointerLevelSet{createEntityPointerLevelFor(*EntityName)};
225-
return makeCreateEntityNameError(E->getMemberDecl(), Ctx);
240+
return makeEntityNameErr(Ctx, *E->getMemberDecl());
226241
}
227242

228243
Expected<EntityPointerLevelSet>
@@ -234,13 +249,68 @@ class EntityPointerLevelTranslator
234249

235250
Expected<EntityPointerLevelSet> clang::ssaf::translateEntityPointerLevel(
236251
const Expr *E, ASTContext &Ctx,
237-
std::function<EntityId(EntityName EN)> AddEntity) {
252+
llvm::function_ref<EntityId(EntityName EN)> AddEntity) {
238253
EntityPointerLevelTranslator Translator(AddEntity, Ctx);
239254

240255
return Translator.translate(E);
241256
}
242257

258+
/// Create an EntityPointerLevel from a ValueDecl of a pointer type.
259+
Expected<EntityPointerLevel> clang::ssaf::creatEntityPointerLevel(
260+
const NamedDecl *ND, llvm::function_ref<EntityId(EntityName EN)> AddEntity,
261+
bool IsFunRet) {
262+
EntityPointerLevelTranslator Translator(AddEntity, ND->getASTContext());
263+
264+
return Translator.translate(ND, IsFunRet);
265+
}
266+
267+
EntityPointerLevel
268+
clang::ssaf::incrementPointerLevel(const EntityPointerLevel &E) {
269+
return EntityPointerLevelTranslator::incrementPointerLevel(E);
270+
}
271+
243272
EntityPointerLevel clang::ssaf::buildEntityPointerLevel(EntityId Id,
244273
unsigned PtrLv) {
245274
return EntityPointerLevel({Id, PtrLv});
246275
}
276+
277+
// Writes an EntityPointerLevel as
278+
// Array [
279+
// Object { "@" : [entity-id]},
280+
// [pointer-level-integer]
281+
// ]
282+
llvm::json::Value clang::ssaf::entityPointerLevelToJSON(
283+
const EntityPointerLevel &EPL, JSONFormat::EntityIdToJSONFn EntityId2JSON) {
284+
return llvm::json::Array{EntityId2JSON(EPL.getEntity()),
285+
llvm::json::Value(EPL.getPointerLevel())};
286+
}
287+
288+
Expected<EntityPointerLevel> clang::ssaf::entityPointerLevelFromJSON(
289+
const llvm::json::Value &EPLData,
290+
JSONFormat::EntityIdFromJSONFn EntityIdFromJSON) {
291+
auto *AsArr = EPLData.getAsArray();
292+
293+
if (!AsArr || AsArr->size() != 2)
294+
return makeSawButExpectedError(
295+
EPLData, "an array with exactly two elements representing "
296+
"EntityId and PointerLevel, respectively");
297+
298+
auto *EntityIdObj = (*AsArr)[0].getAsObject();
299+
300+
if (!EntityIdObj)
301+
return makeSawButExpectedError((*AsArr)[0],
302+
"an object representing EntityId");
303+
304+
Expected<EntityId> Id = EntityIdFromJSON(*EntityIdObj);
305+
306+
if (!Id)
307+
return Id.takeError();
308+
309+
std::optional<uint64_t> PtrLv = (*AsArr)[1].getAsInteger();
310+
311+
if (!PtrLv)
312+
return makeSawButExpectedError((*AsArr)[1],
313+
"an integer representing PointerLevel");
314+
315+
return buildEntityPointerLevel(*Id, *PtrLv);
316+
}

clang/lib/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.cpp

Lines changed: 9 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@
77
//===----------------------------------------------------------------------===//
88

99
#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsage.h"
10+
#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevel.h"
11+
#include "clang/ScalableStaticAnalysisFramework/Analyses/EntityPointerLevel/EntityPointerLevelFormat.h"
1012
#include "clang/ScalableStaticAnalysisFramework/Analyses/UnsafeBufferUsage/UnsafeBufferUsageTest.h"
11-
#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h"
1213
#include "clang/ScalableStaticAnalysisFramework/Core/Serialization/JSONFormat.h"
1314
#include "llvm/Support/Error.h"
1415
#include "llvm/Support/JSON.h"
@@ -37,8 +38,7 @@ static Object serialize(const EntitySummary &S,
3738
Array UnsafeBuffersData;
3839

3940
for (const auto &EPL : getUnsafeBuffers(SS))
40-
UnsafeBuffersData.push_back(
41-
Array{Fn(EPL.getEntity()), EPL.getPointerLevel()});
41+
UnsafeBuffersData.push_back(entityPointerLevelToJSON(EPL, Fn));
4242
return Object{{SummarySerializationKey.data(), std::move(UnsafeBuffersData)}};
4343
}
4444

@@ -48,28 +48,18 @@ deserializeImpl(const Object &Data, JSONFormat::EntityIdFromJSONFn Fn) {
4848
Data.getArray(SummarySerializationKey.data());
4949

5050
if (!UnsafeBuffersData)
51-
return llvm::createStringError("expected a json::Object with a key %s",
51+
return makeSawButExpectedError(Object(Data), "an Object with a key %s",
5252
SummarySerializationKey.data());
5353

5454
EntityPointerLevelSet EPLs;
5555

5656
for (const auto &EltData : *UnsafeBuffersData) {
57-
const Array *EltDataAsArr = EltData.getAsArray();
57+
llvm::Expected<EntityPointerLevel> EPL =
58+
entityPointerLevelFromJSON(EltData, Fn);
5859

59-
if (!EltDataAsArr || EltDataAsArr->size() != 2)
60-
return llvm::createStringError("expected a json::Array of size 2");
61-
62-
const Object *IdData = (*EltDataAsArr)[0].getAsObject();
63-
std::optional<uint64_t> PtrLvData = (*EltDataAsArr)[1].getAsInteger();
64-
65-
if (!IdData || !PtrLvData)
66-
return llvm::createStringError("expected a json::Value of integer type");
67-
68-
llvm::Expected<EntityId> Id = Fn(*IdData);
69-
70-
if (!Id)
71-
return Id.takeError();
72-
EPLs.insert(buildEntityPointerLevel(Id.get(), *PtrLvData));
60+
if (!EPL)
61+
return EPL.takeError();
62+
EPLs.insert(*EPL);
7363
}
7464
return std::make_unique<UnsafeBufferUsageEntitySummary>(
7565
buildUnsafeBufferUsageEntitySummary(std::move(EPLs)));

0 commit comments

Comments
 (0)