https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/154316
>From 0be0b1335e8855e4e128edcd4661888c299d1287 Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena <u...@google.com> Date: Tue, 19 Aug 2025 12:00:53 +0000 Subject: [PATCH] Identify DeclRefExpr as a use of an origin --- clang/lib/Analysis/LifetimeSafety.cpp | 90 +++++++++++++------ .../Sema/warn-lifetime-safety-dataflow.cpp | 26 ++++++ 2 files changed, 90 insertions(+), 26 deletions(-) diff --git a/clang/lib/Analysis/LifetimeSafety.cpp b/clang/lib/Analysis/LifetimeSafety.cpp index 9397c530a9af2..770840870a585 100644 --- a/clang/lib/Analysis/LifetimeSafety.cpp +++ b/clang/lib/Analysis/LifetimeSafety.cpp @@ -119,6 +119,7 @@ class OriginManager { return AllOrigins.back(); } + // TODO: Mark this method as const once we remove the call to getOrCreate. OriginID get(const Expr &E) { // Origin of DeclRefExpr is that of the declaration it refers to. if (const auto *DRE = dyn_cast<DeclRefExpr>(&E)) @@ -315,22 +316,28 @@ class ReturnOfOriginFact : public Fact { }; class UseFact : public Fact { - OriginID UsedOrigin; const Expr *UseExpr; + // True if this use is a write operation (e.g., left-hand side of assignment). + // Write operations are exempted from use-after-free checks. + bool IsWritten = false; public: static bool classof(const Fact *F) { return F->getKind() == Kind::Use; } - UseFact(OriginID UsedOrigin, const Expr *UseExpr) - : Fact(Kind::Use), UsedOrigin(UsedOrigin), UseExpr(UseExpr) {} + UseFact(const Expr *UseExpr) : Fact(Kind::Use), UseExpr(UseExpr) {} - OriginID getUsedOrigin() const { return UsedOrigin; } + OriginID getUsedOrigin(const OriginManager &OM) const { + // TODO: Remove const cast and make OriginManager::get as const. + return const_cast<OriginManager &>(OM).get(*UseExpr); + } const Expr *getUseExpr() const { return UseExpr; } + void markAsWritten() { IsWritten = true; } + bool isWritten() const { return IsWritten; } void dump(llvm::raw_ostream &OS, const OriginManager &OM) const override { OS << "Use ("; - OM.dump(getUsedOrigin(), OS); - OS << ")\n"; + OM.dump(getUsedOrigin(OM), OS); + OS << " " << (isWritten() ? "Write" : "Read") << ")\n"; } }; @@ -428,6 +435,8 @@ class FactGeneratorVisitor : public ConstStmtVisitor<FactGeneratorVisitor> { addAssignOriginFact(*VD, *InitExpr); } + void VisitDeclRefExpr(const DeclRefExpr *DRE) { handleUse(DRE); } + void VisitCXXNullPtrLiteralExpr(const CXXNullPtrLiteralExpr *N) { /// TODO: Handle nullptr expr as a special 'null' loan. Uninitialized /// pointers can use the same type of loan. @@ -461,10 +470,6 @@ class FactGeneratorVisitor : public ConstStmtVisitor<FactGeneratorVisitor> { } } } - } else if (UO->getOpcode() == UO_Deref) { - // This is a pointer use, like '*p'. - OriginID OID = FactMgr.getOriginMgr().get(*UO->getSubExpr()); - CurrentBlockFacts.push_back(FactMgr.createFact<UseFact>(OID, UO)); } } @@ -479,20 +484,13 @@ class FactGeneratorVisitor : public ConstStmtVisitor<FactGeneratorVisitor> { } void VisitBinaryOperator(const BinaryOperator *BO) { - if (BO->isAssignmentOp()) { - const Expr *LHSExpr = BO->getLHS(); - const Expr *RHSExpr = BO->getRHS(); - - // We are interested in assignments like `ptr1 = ptr2` or `ptr = &var` - // LHS must be a pointer/reference type that can be an origin. - // RHS must also represent an origin (either another pointer/ref or an - // address-of). - if (const auto *DRE_LHS = dyn_cast<DeclRefExpr>(LHSExpr)) - if (const auto *VD_LHS = - dyn_cast<ValueDecl>(DRE_LHS->getDecl()->getCanonicalDecl()); - VD_LHS && hasOrigin(VD_LHS->getType())) - addAssignOriginFact(*VD_LHS, *RHSExpr); - } + if (BO->isAssignmentOp()) + handleAssignment(BO->getLHS(), BO->getRHS()); + } + + void VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *OCE) { + if (OCE->isAssignmentOp() && OCE->getNumArgs() == 2) + handleAssignment(OCE->getArg(0), OCE->getArg(1)); } void VisitCXXFunctionalCastExpr(const CXXFunctionalCastExpr *FCE) { @@ -559,9 +557,48 @@ class FactGeneratorVisitor : public ConstStmtVisitor<FactGeneratorVisitor> { return false; } + void handleAssignment(const Expr *LHSExpr, const Expr *RHSExpr) { + // Find the underlying variable declaration for the left-hand side. + if (const auto *DRE_LHS = + dyn_cast<DeclRefExpr>(LHSExpr->IgnoreParenImpCasts())) { + markUseAsWrite(DRE_LHS); + if (const auto *VD_LHS = dyn_cast<ValueDecl>(DRE_LHS->getDecl())) + if (hasOrigin(VD_LHS->getType())) + // We are interested in assignments like `ptr1 = ptr2` or `ptr = &var` + // LHS must be a pointer/reference type that can be an origin. + // RHS must also represent an origin (either another pointer/ref or an + // address-of). + addAssignOriginFact(*VD_LHS, *RHSExpr); + } + } + + // A DeclRefExpr is a use of the referenced decl. It is checked for + // use-after-free unless it is being written to (e.g. on the left-hand side + // of an assignment). + void handleUse(const DeclRefExpr *DRE) { + const auto *VD = dyn_cast<ValueDecl>(DRE->getDecl()); + if (VD && hasOrigin(VD->getType())) { + UseFact *UF = FactMgr.createFact<UseFact>(DRE); + CurrentBlockFacts.push_back(UF); + assert(!UseFacts.contains(DRE)); + UseFacts[DRE] = UF; + } + } + + void markUseAsWrite(const DeclRefExpr *DRE) { + assert(UseFacts.contains(DRE)); + UseFacts[DRE]->markAsWritten(); + } + FactManager &FactMgr; const CFGBlock *CurrentBlock = nullptr; llvm::SmallVector<Fact *> CurrentBlockFacts; + // To distinguish between reads and writes for use-after-free checks, this map + // stores the `UseFact` for each `DeclRefExpr`. We initially identify all + // `DeclRefExpr`s as "read" uses. When an assignment is processed, the use + // corresponding to the left-hand side is updated to be a "write", thereby + // exempting it from the check. + llvm::DenseMap<const DeclRefExpr *, UseFact *> UseFacts; }; class FactGenerator : public RecursiveASTVisitor<FactGenerator> { @@ -1076,8 +1113,9 @@ class LifetimeChecker { /// graph. It determines if the loans held by the used origin have expired /// at the point of use. void checkUse(const UseFact *UF) { - - OriginID O = UF->getUsedOrigin(); + if (UF->isWritten()) + return; + OriginID O = UF->getUsedOrigin(FactMgr.getOriginMgr()); // Get the set of loans that the origin might hold at this program point. LoanSet HeldLoans = LoanPropagation.getLoans(O, UF); diff --git a/clang/test/Sema/warn-lifetime-safety-dataflow.cpp b/clang/test/Sema/warn-lifetime-safety-dataflow.cpp index bcde9adf25ca5..5660a8ace4a09 100644 --- a/clang/test/Sema/warn-lifetime-safety-dataflow.cpp +++ b/clang/test/Sema/warn-lifetime-safety-dataflow.cpp @@ -293,3 +293,29 @@ void pointer_indirection() { int *q = *pp; // CHECK: AssignOrigin (Dest: {{[0-9]+}} (Decl: q), Src: {{[0-9]+}} (Expr: ImplicitCastExpr)) } + +// CHECK-LABEL: Function: test_use_facts +void usePointer(MyObj*); +void test_use_facts() { + // CHECK: Block B{{[0-9]+}}: + MyObj x; + MyObj *p; + p = &x; + // CHECK: Use ([[O_P:[0-9]+]] (Decl: p) Write) + (void)*p; + // CHECK: Use ([[O_P]] (Decl: p) Read) + usePointer(p); + // CHECK: Use ([[O_P]] (Decl: p) Read) + p->id = 1; + // CHECK: Use ([[O_P]] (Decl: p) Read) + + + MyObj* q; + q = p; + // CHECK: Use ([[O_P]] (Decl: p) Read) + // CHECK: Use ([[O_Q:[0-9]+]] (Decl: q) Write) + usePointer(q); + // CHECK: Use ([[O_Q]] (Decl: q) Read) + q->id = 2; + // CHECK: Use ([[O_Q]] (Decl: q) Read) +} _______________________________________________ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits