Skip to content

Commit 34480e0

Browse files
committed
Identify DeclRefExpr as a use of an origin
1 parent 577d963 commit 34480e0

File tree

2 files changed

+89
-26
lines changed

2 files changed

+89
-26
lines changed

clang/lib/Analysis/LifetimeSafety.cpp

Lines changed: 64 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ class OriginManager {
119119
return AllOrigins.back();
120120
}
121121

122+
// TODO: Mark this method as const once we remove the call to getOrCreate.
122123
OriginID get(const Expr &E) {
123124
// Origin of DeclRefExpr is that of the declaration it refers to.
124125
if (const auto *DRE = dyn_cast<DeclRefExpr>(&E))
@@ -315,22 +316,28 @@ class ReturnOfOriginFact : public Fact {
315316
};
316317

317318
class UseFact : public Fact {
318-
OriginID UsedOrigin;
319319
const Expr *UseExpr;
320+
// True if this use is a write operation (e.g., left-hand side of assignment).
321+
// Write operations are exempted from use-after-free checks.
322+
bool IsWritten = false;
320323

321324
public:
322325
static bool classof(const Fact *F) { return F->getKind() == Kind::Use; }
323326

324-
UseFact(OriginID UsedOrigin, const Expr *UseExpr)
325-
: Fact(Kind::Use), UsedOrigin(UsedOrigin), UseExpr(UseExpr) {}
327+
UseFact(const Expr *UseExpr) : Fact(Kind::Use), UseExpr(UseExpr) {}
326328

327-
OriginID getUsedOrigin() const { return UsedOrigin; }
329+
OriginID getUsedOrigin(const OriginManager &OM) const {
330+
// TODO: Remove const cast and make OriginManager::get as const.
331+
return const_cast<OriginManager &>(OM).get(*UseExpr);
332+
}
328333
const Expr *getUseExpr() const { return UseExpr; }
334+
void markAsWritten() { IsWritten = true; }
335+
bool isWritten() const { return IsWritten; }
329336

330337
void dump(llvm::raw_ostream &OS, const OriginManager &OM) const override {
331338
OS << "Use (";
332-
OM.dump(getUsedOrigin(), OS);
333-
OS << ")\n";
339+
OM.dump(getUsedOrigin(OM), OS);
340+
OS << " " << (isWritten() ? "Write" : "Read") << ")\n";
334341
}
335342
};
336343

@@ -425,6 +432,8 @@ class FactGeneratorVisitor : public ConstStmtVisitor<FactGeneratorVisitor> {
425432
addAssignOriginFact(*VD, *InitExpr);
426433
}
427434

435+
void VisitDeclRefExpr(const DeclRefExpr *DRE) { handleUse(DRE); }
436+
428437
void VisitCXXNullPtrLiteralExpr(const CXXNullPtrLiteralExpr *N) {
429438
/// TODO: Handle nullptr expr as a special 'null' loan. Uninitialized
430439
/// pointers can use the same type of loan.
@@ -458,10 +467,6 @@ class FactGeneratorVisitor : public ConstStmtVisitor<FactGeneratorVisitor> {
458467
}
459468
}
460469
}
461-
} else if (UO->getOpcode() == UO_Deref) {
462-
// This is a pointer use, like '*p'.
463-
OriginID OID = FactMgr.getOriginMgr().get(*UO->getSubExpr());
464-
CurrentBlockFacts.push_back(FactMgr.createFact<UseFact>(OID, UO));
465470
}
466471
}
467472

@@ -476,20 +481,13 @@ class FactGeneratorVisitor : public ConstStmtVisitor<FactGeneratorVisitor> {
476481
}
477482

478483
void VisitBinaryOperator(const BinaryOperator *BO) {
479-
if (BO->isAssignmentOp()) {
480-
const Expr *LHSExpr = BO->getLHS();
481-
const Expr *RHSExpr = BO->getRHS();
482-
483-
// We are interested in assignments like `ptr1 = ptr2` or `ptr = &var`
484-
// LHS must be a pointer/reference type that can be an origin.
485-
// RHS must also represent an origin (either another pointer/ref or an
486-
// address-of).
487-
if (const auto *DRE_LHS = dyn_cast<DeclRefExpr>(LHSExpr))
488-
if (const auto *VD_LHS =
489-
dyn_cast<ValueDecl>(DRE_LHS->getDecl()->getCanonicalDecl());
490-
VD_LHS && hasOrigin(VD_LHS->getType()))
491-
addAssignOriginFact(*VD_LHS, *RHSExpr);
492-
}
484+
if (BO->isAssignmentOp())
485+
handleAssignment(BO->getLHS(), BO->getRHS());
486+
}
487+
488+
void VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *OCE) {
489+
if (OCE->isAssignmentOp() && OCE->getNumArgs() == 2)
490+
handleAssignment(OCE->getArg(0), OCE->getArg(1));
493491
}
494492

495493
void VisitCXXFunctionalCastExpr(const CXXFunctionalCastExpr *FCE) {
@@ -556,8 +554,47 @@ class FactGeneratorVisitor : public ConstStmtVisitor<FactGeneratorVisitor> {
556554
return false;
557555
}
558556

557+
void handleAssignment(const Expr *LHSExpr, const Expr *RHSExpr) {
558+
// Find the underlying variable declaration for the left-hand side.
559+
if (const auto *DRE_LHS =
560+
dyn_cast<DeclRefExpr>(LHSExpr->IgnoreParenImpCasts())) {
561+
markUseAsWrite(DRE_LHS);
562+
if (const auto *VD_LHS = dyn_cast<ValueDecl>(DRE_LHS->getDecl()))
563+
if (hasOrigin(VD_LHS->getType()))
564+
// We are interested in assignments like `ptr1 = ptr2` or `ptr = &var`
565+
// LHS must be a pointer/reference type that can be an origin.
566+
// RHS must also represent an origin (either another pointer/ref or an
567+
// address-of).
568+
addAssignOriginFact(*VD_LHS, *RHSExpr);
569+
}
570+
}
571+
572+
// A DeclRefExpr is a use of the referenced decl. It is checked for
573+
// use-after-free unless it is being written to (e.g. on the left-hand side
574+
// of an assignment).
575+
void handleUse(const DeclRefExpr *DRE) {
576+
const auto *VD = dyn_cast<ValueDecl>(DRE->getDecl());
577+
if (VD && hasOrigin(VD->getType())) {
578+
UseFact *UF = FactMgr.createFact<UseFact>(DRE);
579+
CurrentBlockFacts.push_back(UF);
580+
assert(!UseFacts.contains(DRE));
581+
UseFacts[DRE] = UF;
582+
}
583+
}
584+
585+
void markUseAsWrite(const DeclRefExpr *DRE) {
586+
assert(UseFacts.contains(DRE));
587+
UseFacts[DRE]->markAsWritten();
588+
}
589+
559590
FactManager &FactMgr;
560591
llvm::SmallVector<Fact *> CurrentBlockFacts;
592+
// To distinguish between reads and writes for use-after-free checks, this map
593+
// stores the `UseFact` for each `DeclRefExpr`. We initially identify all
594+
// `DeclRefExpr`s as "read" uses. When an assignment is processed, the use
595+
// corresponding to the left-hand side is updated to be a "write", thereby
596+
// exempting it from the check.
597+
llvm::DenseMap<const DeclRefExpr *, UseFact *> UseFacts;
561598
};
562599

563600
class FactGenerator : public RecursiveASTVisitor<FactGenerator> {
@@ -1073,8 +1110,9 @@ class LifetimeChecker {
10731110
/// graph. It determines if the loans held by the used origin have expired
10741111
/// at the point of use.
10751112
void checkUse(const UseFact *UF) {
1076-
1077-
OriginID O = UF->getUsedOrigin();
1113+
if (UF->isWritten())
1114+
return;
1115+
OriginID O = UF->getUsedOrigin(FactMgr.getOriginMgr());
10781116

10791117
// Get the set of loans that the origin might hold at this program point.
10801118
LoanSet HeldLoans = LoanPropagation.getLoans(O, UF);

clang/test/Sema/warn-lifetime-safety-dataflow.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,3 +313,28 @@ void ternary_operator() {
313313
// CHECK: End of Block
314314
}
315315

316+
// CHECK-LABEL: Function: test_use_facts
317+
void usePointer(MyObj*);
318+
void test_use_facts() {
319+
// CHECK: Block B{{[0-9]+}}:
320+
MyObj x;
321+
MyObj *p;
322+
p = &x;
323+
// CHECK: Use ([[O_P:[0-9]+]] (Decl: p) Write)
324+
(void)*p;
325+
// CHECK: Use ([[O_P]] (Decl: p) Read)
326+
usePointer(p);
327+
// CHECK: Use ([[O_P]] (Decl: p) Read)
328+
p->id = 1;
329+
// CHECK: Use ([[O_P]] (Decl: p) Read)
330+
331+
332+
MyObj* q;
333+
q = p;
334+
// CHECK: Use ([[O_P]] (Decl: p) Read)
335+
// CHECK: Use ([[O_Q:[0-9]+]] (Decl: q) Write)
336+
usePointer(q);
337+
// CHECK: Use ([[O_Q]] (Decl: q) Read)
338+
q->id = 2;
339+
// CHECK: Use ([[O_Q]] (Decl: q) Read)
340+
}

0 commit comments

Comments
 (0)