[clang] [LifetimeSafety] Add parameter lifetime tracking in CFG (PR #169320)
Utkarsh Saxena via cfe-commits
cfe-commits at lists.llvm.org
Mon Nov 24 04:18:02 PST 2025
https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/169320
>From c9e98f4c545ae4cdec77b39d157cb4a6ac18dc1d Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena <usx at google.com>
Date: Mon, 24 Nov 2025 11:54:49 +0000
Subject: [PATCH] fix-missing-lifetimeends-for-params
---
clang/lib/Analysis/CFG.cpp | 10 ++++++++++
clang/test/Analysis/scopes-cfg-output.cpp | 2 ++
clang/test/Sema/warn-lifetime-safety.cpp | 20 ++++++++++++++------
3 files changed, 26 insertions(+), 6 deletions(-)
diff --git a/clang/lib/Analysis/CFG.cpp b/clang/lib/Analysis/CFG.cpp
index cdde849b0e026..c052af5e96b21 100644
--- a/clang/lib/Analysis/CFG.cpp
+++ b/clang/lib/Analysis/CFG.cpp
@@ -1666,6 +1666,13 @@ std::unique_ptr<CFG> CFGBuilder::buildCFG(const Decl *D, Stmt *Statement) {
assert(Succ == &cfg->getExit());
Block = nullptr; // the EXIT block is empty. Create all other blocks lazily.
+ // Add parameters to the initial scope so to handle their dtos and lifetime
+ // ends.
+ LocalScope *paramScope = nullptr;
+ if (const auto *FD = dyn_cast_or_null<FunctionDecl>(D))
+ for (ParmVarDecl *PD : FD->parameters())
+ paramScope = addLocalScopeForVarDecl(PD, paramScope);
+
if (BuildOpts.AddImplicitDtors)
if (const CXXDestructorDecl *DD = dyn_cast_or_null<CXXDestructorDecl>(D))
addImplicitDtorsForDestructor(DD);
@@ -2246,6 +2253,9 @@ LocalScope* CFGBuilder::addLocalScopeForVarDecl(VarDecl *VD,
if (!VD->hasLocalStorage())
return Scope;
+ if (isa<ParmVarDecl>(VD) && VD->getType()->isReferenceType())
+ return Scope;
+
if (!BuildOpts.AddLifetime && !BuildOpts.AddScopes &&
!needsAutomaticDestruction(VD)) {
assert(BuildOpts.AddImplicitDtors);
diff --git a/clang/test/Analysis/scopes-cfg-output.cpp b/clang/test/Analysis/scopes-cfg-output.cpp
index 6ed6f3638f75b..9c75492c33a42 100644
--- a/clang/test/Analysis/scopes-cfg-output.cpp
+++ b/clang/test/Analysis/scopes-cfg-output.cpp
@@ -1437,12 +1437,14 @@ void test_cleanup_functions() {
// CHECK-NEXT: 4: return;
// CHECK-NEXT: 5: CleanupFunction (cleanup_int)
// CHECK-NEXT: 6: CFGScopeEnd(i)
+// CHECK-NEXT: 7: CFGScopeEnd(m)
// CHECK-NEXT: Preds (1): B3
// CHECK-NEXT: Succs (1): B0
// CHECK: [B2]
// CHECK-NEXT: 1: return;
// CHECK-NEXT: 2: CleanupFunction (cleanup_int)
// CHECK-NEXT: 3: CFGScopeEnd(i)
+// CHECK-NEXT: 4: CFGScopeEnd(m)
// CHECK-NEXT: Preds (1): B3
// CHECK-NEXT: Succs (1): B0
// CHECK: [B3]
diff --git a/clang/test/Sema/warn-lifetime-safety.cpp b/clang/test/Sema/warn-lifetime-safety.cpp
index e80a05860389c..c08543c27778f 100644
--- a/clang/test/Sema/warn-lifetime-safety.cpp
+++ b/clang/test/Sema/warn-lifetime-safety.cpp
@@ -529,14 +529,14 @@ TriviallyDestructedClass* trivial_class_uar () {
return ptr; // expected-note {{returned here}}
}
-// FIXME: No lifetime warning for this as no expire facts are generated for parameters
const int& return_parameter(int a) {
- return a;
+ return a; // expected-warning {{address of stack memory is returned later}}
+ // expected-note at -1 {{returned here}}
}
-// FIXME: No lifetime warning for this as no expire facts are generated for parameters
int* return_pointer_to_parameter(int a) {
- return &a;
+ return &a; // expected-warning {{address of stack memory is returned later}}
+ // expected-note at -1 {{returned here}}
}
const int& return_reference_to_parameter(int a)
@@ -788,9 +788,17 @@ const MyObj& lifetimebound_return_ref_to_local() {
// expected-note at -1 {{returned here}}
}
-// FIXME: Fails to diagnose UAR when a reference to a by-value param escapes via the return value.
View lifetimebound_return_of_by_value_param(MyObj stack_param) {
- return Identity(stack_param);
+ return Identity(stack_param); // expected-warning {{address of stack memory is returned later}}
+ // expected-note at -1 {{returned here}}
+}
+
+void LambdaUARParam() {
+ auto lambda = [](MyObj stack_param) {
+ return Identity(stack_param); // expected-warning {{address of stack memory is returned later}}
+ // expected-note at -1 {{returned here}}
+ };
+ lambda(MyObj{});
}
// FIXME: Fails to diagnose UAF when a reference to a by-value param escapes via an out-param.
More information about the cfe-commits
mailing list