[llvm-branch-commits] [clang] [analyzer] Only bind aggregate lifetime sources in LifetimeModeling for annotated functions (PR #214824)
Benedek Kaibas via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Sun Aug 9 15:33:40 PDT 2026
https://github.com/benedekaibas updated https://github.com/llvm/llvm-project/pull/214824
>From b7cc8da6f3afbd43bf372aca7b898f9e287b3651 Mon Sep 17 00:00:00 2001
From: benedekaibas <kaibas01 at allegheny.edu>
Date: Fri, 7 Aug 2026 15:22:52 +0200
Subject: [PATCH 1/4] [analyzer] Only bind aggregate lifetime sources in
LifetimeModeling for annotated functions
---
.../Checkers/LifetimeModeling.cpp | 21 +++++++++---
clang/test/Analysis/lifetime-bound.cpp | 32 +++++++++++++++++++
2 files changed, 49 insertions(+), 4 deletions(-)
diff --git a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp
index c2995deb3a6e1..46c8df0d610f3 100644
--- a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp
@@ -168,6 +168,17 @@ lifetime_modeling::getRegionsFromAggrVal(SVal Val, CheckerContext &C) {
return {};
}
+static bool isAnnotated(const FunctionDecl *FD) {
+ for (const ParmVarDecl *PVD : FD->parameters()) {
+ if (PVD->hasAttr<LifetimeBoundAttr>())
+ return true;
+ }
+
+ if (lifetimes::implicitObjectParamIsLifetimeBound(FD))
+ return true;
+ return false;
+}
+
void LifetimeModeling::checkPostCall(const CallEvent &Call,
CheckerContext &C) const {
ProgramStateRef State = C.getState();
@@ -181,11 +192,13 @@ void LifetimeModeling::checkPostCall(const CallEvent &Call,
return;
SVal RetVal = Call.getReturnValue();
- SmallVector<const MemRegion *, 4> AggrRegs =
- lifetime_modeling::getRegionsFromAggrVal(RetVal, C);
- for (const MemRegion *I : AggrRegs) {
- State = bindSource(State, RetVal, I);
+ if (isAnnotated(FD)) {
+ SmallVector<const MemRegion *, 4> AggrRegs =
+ lifetime_modeling::getRegionsFromAggrVal(RetVal, C);
+ for (const MemRegion *I : AggrRegs) {
+ State = bindSource(State, RetVal, I);
+ }
}
for (const ParmVarDecl *PVD : FD->parameters()) {
diff --git a/clang/test/Analysis/lifetime-bound.cpp b/clang/test/Analysis/lifetime-bound.cpp
index 749f264fe53eb..c421ba17325d4 100644
--- a/clang/test/Analysis/lifetime-bound.cpp
+++ b/clang/test/Analysis/lifetime-bound.cpp
@@ -471,3 +471,35 @@ IntPtrArr return_array_field_not_yet_detected() {
// expected-note at -2 {{Address of stack memory associated with local variable 'z' returned to caller}}
// expected-warning at -3 {{address of stack memory associated with local variable 'z' returned}}
}
+
+struct Hold {
+ int *ptr;
+};
+
+Hold retPtr(int &x) {
+ return Hold{&x};
+}
+// Even though there is a lifetime error in the function
+// UseAfterLifetimeEnd should not emit a warning for this
+// case since there is no annotation present in the code.
+// The warning present in the test comes from core.StackAddressEscape
+// checker.
+Hold return_by_val_no_ann() {
+ int num = 4;
+ return retPtr(num);
+ // expected-warning at -1 {{Address of stack memory associated with local variable 'num' returned to caller}}
+ // expected-note at -2 {{Address of stack memory associated with local variable 'num' returned to caller}}
+}
+
+int *unwrap(Hold i [[clang::lifetimebound]]) { return i.ptr; }
+
+// FIXME: If an annotated argument is a by-value struct then
+// Arg.getAsRegion() returns null for CompoundVal/LazyCompoundVal
+// and the dangling pointer will not be detected.
+int *arg_aggregate_lifetimebound() {
+ int local_num = 5;
+ Hold h{&local_num};
+ return unwrap(h);
+ // expected-warning at -1 {{Address of stack memory associated with local variable 'local_num' returned to caller}}
+ // expected-note at -2 {{Address of stack memory associated with local variable 'local_num' returned to caller}}
+}
>From 57cb6096e6d56dc63bf6637589c2af0dd11495e0 Mon Sep 17 00:00:00 2001
From: benedekaibas <kaibas01 at allegheny.edu>
Date: Sun, 9 Aug 2026 21:45:58 +0200
Subject: [PATCH 2/4] Refactor hasAnyParamLifetimeAnnotated.
---
clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp | 8 +++-----
1 file changed, 3 insertions(+), 5 deletions(-)
diff --git a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp
index 46c8df0d610f3..4474fa0b31fa6 100644
--- a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp
@@ -168,15 +168,13 @@ lifetime_modeling::getRegionsFromAggrVal(SVal Val, CheckerContext &C) {
return {};
}
-static bool isAnnotated(const FunctionDecl *FD) {
+static bool hasAnyParamLifetimeAnnotated(const FunctionDecl *FD) {
for (const ParmVarDecl *PVD : FD->parameters()) {
if (PVD->hasAttr<LifetimeBoundAttr>())
return true;
}
- if (lifetimes::implicitObjectParamIsLifetimeBound(FD))
- return true;
- return false;
+ return lifetimes::implicitObjectParamIsLifetimeBound(FD);
}
void LifetimeModeling::checkPostCall(const CallEvent &Call,
@@ -193,7 +191,7 @@ void LifetimeModeling::checkPostCall(const CallEvent &Call,
SVal RetVal = Call.getReturnValue();
- if (isAnnotated(FD)) {
+ if (hasAnyParamLifetimeAnnotated(FD)) {
SmallVector<const MemRegion *, 4> AggrRegs =
lifetime_modeling::getRegionsFromAggrVal(RetVal, C);
for (const MemRegion *I : AggrRegs) {
>From 39785aaf49c98cca4732b86a5214a7e90fde3fb0 Mon Sep 17 00:00:00 2001
From: benedekaibas <kaibas01 at allegheny.edu>
Date: Sun, 9 Aug 2026 21:57:44 +0200
Subject: [PATCH 3/4] Use auto for AggrRegs in checkPostCall.
---
clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp
index 4474fa0b31fa6..0cb8b7c1c7c75 100644
--- a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp
@@ -192,8 +192,7 @@ void LifetimeModeling::checkPostCall(const CallEvent &Call,
SVal RetVal = Call.getReturnValue();
if (hasAnyParamLifetimeAnnotated(FD)) {
- SmallVector<const MemRegion *, 4> AggrRegs =
- lifetime_modeling::getRegionsFromAggrVal(RetVal, C);
+ auto AggrRegs = lifetime_modeling::getRegionsFromAggrVal(RetVal, C);
for (const MemRegion *I : AggrRegs) {
State = bindSource(State, RetVal, I);
}
>From fbda2f34948493a657988e71fcc6b53ae7a8194c Mon Sep 17 00:00:00 2001
From: benedekaibas <kaibas01 at allegheny.edu>
Date: Mon, 10 Aug 2026 00:32:28 +0200
Subject: [PATCH 4/4] Handle by-value struct cases and remove test case
comment.
---
.../Checkers/LifetimeModeling.cpp | 12 +++++++---
clang/test/Analysis/lifetime-bound.cpp | 24 +++++++------------
2 files changed, 18 insertions(+), 18 deletions(-)
diff --git a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp
index 0cb8b7c1c7c75..dcdc70e74f237 100644
--- a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp
@@ -193,8 +193,8 @@ void LifetimeModeling::checkPostCall(const CallEvent &Call,
if (hasAnyParamLifetimeAnnotated(FD)) {
auto AggrRegs = lifetime_modeling::getRegionsFromAggrVal(RetVal, C);
- for (const MemRegion *I : AggrRegs) {
- State = bindSource(State, RetVal, I);
+ for (const MemRegion *R : AggrRegs) {
+ State = bindSource(State, RetVal, R);
}
}
@@ -202,8 +202,14 @@ void LifetimeModeling::checkPostCall(const CallEvent &Call,
if (PVD->hasAttr<LifetimeBoundAttr>()) {
unsigned Idx = PVD->getFunctionScopeIndex();
SVal Arg = Call.getArgSVal(Idx);
- if (const MemRegion *ArgValRegion = Arg.getAsRegion())
+ if (const MemRegion *ArgValRegion = Arg.getAsRegion()) {
State = bindSource(State, RetVal, ArgValRegion);
+ } else {
+ auto AggrRegs = lifetime_modeling::getRegionsFromAggrVal(Arg, C);
+ for (const MemRegion *R : AggrRegs) {
+ State = bindSource(State, RetVal, R);
+ }
+ }
}
}
diff --git a/clang/test/Analysis/lifetime-bound.cpp b/clang/test/Analysis/lifetime-bound.cpp
index c421ba17325d4..a9fd1a8fd92cc 100644
--- a/clang/test/Analysis/lifetime-bound.cpp
+++ b/clang/test/Analysis/lifetime-bound.cpp
@@ -476,30 +476,24 @@ struct Hold {
int *ptr;
};
-Hold retPtr(int &x) {
- return Hold{&x};
-}
-// Even though there is a lifetime error in the function
+Hold takePtr(int &x);
+// Even though there might be a lifetime error in the function
// UseAfterLifetimeEnd should not emit a warning for this
// case since there is no annotation present in the code.
-// The warning present in the test comes from core.StackAddressEscape
-// checker.
Hold return_by_val_no_ann() {
int num = 4;
- return retPtr(num);
- // expected-warning at -1 {{Address of stack memory associated with local variable 'num' returned to caller}}
- // expected-note at -2 {{Address of stack memory associated with local variable 'num' returned to caller}}
+ return takePtr(num);
}
int *unwrap(Hold i [[clang::lifetimebound]]) { return i.ptr; }
-// FIXME: If an annotated argument is a by-value struct then
-// Arg.getAsRegion() returns null for CompoundVal/LazyCompoundVal
-// and the dangling pointer will not be detected.
int *arg_aggregate_lifetimebound() {
- int local_num = 5;
+ int local_num = 5; // expected-note {{'local_num' initialized here}}
Hold h{&local_num};
return unwrap(h);
- // expected-warning at -1 {{Address of stack memory associated with local variable 'local_num' returned to caller}}
- // expected-note at -2 {{Address of stack memory associated with local variable 'local_num' returned to caller}}
+ // expected-warning at -1 {{Returning value bound to 'local_num' that will go out of scope}}
+ // expected-note at -2 {{Lifetime of 'local_num' ended here}}
+ // expected-note at -3 {{Value's lifetime bound to the lifetime of 'local_num' here}}
+ // expected-warning at -4 {{Address of stack memory associated with local variable 'local_num' returned to caller}}
+ // expected-note at -5 {{Address of stack memory associated with local variable 'local_num' returned to caller}}
}
More information about the llvm-branch-commits
mailing list