llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang-analysis
Author: Utkarsh Saxena (usx95)
<details>
<summary>Changes</summary>
# Improve lifetime safety analysis for GSL pointer types
This PR enhances lifetime safety analysis for GSL pointer types by:
1. Tracking more methods that return pointers or references to objects owned by
the implicit object argument
2. Ensuring that methods like `data()`, dereference operators, and begin/end
iterators are properly tracked
The changes allow the analyzer to detect more cases where addresses of stack
memory are returned, particularly when working with GSL pointer types that
provide access to their underlying objects through methods like `data()` or
operators like `*` and `->`.
One particular idiom that is now detected is iterators involved in range-based
for-loops.
```cpp
View ContainerMyObjReturnView(Container<MyObj> c) {
for (const MyObj& x : c) { // expected-warning {{address of stack memory
is returned later}}
return x; // expected-note {{returned here}}
}
for (View x : c) { // expected-warning {{address of stack memory is returned
later}}
return x; // expected-note {{returned here}}
}
return Global;
}
```
---
Full diff: https://github.com/llvm/llvm-project/pull/177660.diff
2 Files Affected:
- (modified) clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp (+19-17)
- (modified) clang/test/Sema/warn-lifetime-safety.cpp (+75)
``````````diff
diff --git a/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
index dd925d2b8fe6e..4215a805dbf91 100644
--- a/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp
@@ -13,6 +13,7 @@
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/Type.h"
#include "clang/AST/TypeLoc.h"
+#include "llvm/ADT/StringSet.h"
namespace clang::lifetimes {
@@ -115,35 +116,36 @@ bool shouldTrackImplicitObjectArg(const CXXMethodDecl
*Callee,
if (isGslPointerType(Conv->getConversionType()) &&
Callee->getParent()->hasAttr<OwnerAttr>())
return true;
- if (!isInStlNamespace(Callee->getParent()))
- return false;
if (!isGslPointerType(Callee->getFunctionObjectParameterType()) &&
!isGslOwnerType(Callee->getFunctionObjectParameterType()))
return false;
+ static const llvm::StringSet<> TransparentFns = {
+ // Begin and end iterators.
+ "begin", "end", "rbegin", "rend", "cbegin", "cend", "crbegin", "crend",
+ // Inner pointer getters.
+ "c_str", "data", "get",
+ // Map and set types.
+ "find", "equal_range", "lower_bound", "upper_bound"};
// Track dereference operator for GSL pointers in STL. Only do so for
lifetime
// safety analysis and not for Sema's statement-local analysis as it starts
// to have false-positives.
if (RunningUnderLifetimeSafety &&
- isGslPointerType(Callee->getFunctionObjectParameterType()) &&
- (Callee->getOverloadedOperator() == OverloadedOperatorKind::OO_Star ||
- Callee->getOverloadedOperator() == OverloadedOperatorKind::OO_Arrow))
- return true;
+ isGslPointerType(Callee->getFunctionObjectParameterType())) {
+ if (Callee->getOverloadedOperator() == OverloadedOperatorKind::OO_Star ||
+ Callee->getOverloadedOperator() == OverloadedOperatorKind::OO_Arrow)
+ return true;
+ if (Callee->getIdentifier() && TransparentFns.contains(Callee->getName()))
+ return true;
+ }
+
+ if (!isInStlNamespace(Callee->getParent()))
+ return false;
if (isPointerLikeType(Callee->getReturnType())) {
if (!Callee->getIdentifier())
return false;
- return llvm::StringSwitch<bool>(Callee->getName())
- .Cases(
- {// Begin and end iterators.
- "begin", "end", "rbegin", "rend", "cbegin", "cend", "crbegin",
- "crend",
- // Inner pointer getters.
- "c_str", "data", "get",
- // Map and set types.
- "find", "equal_range", "lower_bound", "upper_bound"},
- true)
- .Default(false);
+ return TransparentFns.contains(Callee->getName());
}
if (Callee->getReturnType()->isReferenceType()) {
if (!Callee->getIdentifier()) {
diff --git a/clang/test/Sema/warn-lifetime-safety.cpp
b/clang/test/Sema/warn-lifetime-safety.cpp
index d6064dea9e545..c80556715fedf 100644
--- a/clang/test/Sema/warn-lifetime-safety.cpp
+++ b/clang/test/Sema/warn-lifetime-safety.cpp
@@ -14,6 +14,7 @@ struct [[gsl::Owner]] MyObj {
MyObj operator+(MyObj);
View getView() const [[clang::lifetimebound]];
+ const int* getData() const [[clang::lifetimebound]];
};
struct [[gsl::Owner]] MyTrivialObj {
@@ -25,6 +26,10 @@ struct [[gsl::Pointer()]] View {
View(const MyTrivialObj &); // Borrows from MyTrivialObj
View();
void use() const;
+
+ const MyObj* data() const;
+ const MyObj& operator*() const;
+ const MyObj* operator->() const;
};
class TriviallyDestructedClass {
@@ -1455,6 +1460,76 @@ void bar() {
}
}
+namespace DereferenceViews {
+const MyObj& testDeref(MyObj obj) {
+ View v = obj; // expected-warning {{address of stack memory is returned
later}}
+ return *v; // expected-note {{returned here}}
+}
+const MyObj* testDerefAddr(MyObj obj) {
+ View v = obj; // expected-warning {{address of stack memory is returned
later}}
+ return &*v; // expected-note {{returned here}}
+}
+const MyObj* testData(MyObj obj) {
+ View v = obj; // expected-warning {{address of stack memory is returned
later}}
+ return v.data(); // expected-note {{returned here}}
+}
+const int* testLifetimeboundAccessorOfMyObj(MyObj obj) {
+ View v = obj; // expected-warning {{address of stack memory is
returned later}}
+ const MyObj* ptr = v.data();
+ return ptr->getData(); // expected-note {{returned here}}
+}
+const int* testLifetimeboundAccessorOfMyObjThroughDeref(MyObj obj) {
+ View v = obj; // expected-warning {{address of stack memory is
returned later}}
+ return v->getData(); // expected-note {{returned here}}
+}
+} // namespace DereferenceViews
+
+namespace ViewsBeginEndIterators {
+template <typename T>
+struct [[gsl::Pointer]] Iterator {
+ Iterator operator++();
+ T& operator*() const;
+ T* operator->() const;
+ bool operator!=(const Iterator& other) const;
+};
+
+template <typename T>
+struct [[gsl::Owner]] Container {
+using It = Iterator<T>;
+It begin() const [[clang::lifetimebound]];
+It end() const [[clang::lifetimebound]];
+};
+
+MyObj Global;
+
+const MyObj& ContainerMyObjReturnRef(Container<MyObj> c) {
+ for (const MyObj& x : c) { // expected-warning {{address of stack memory is
returned later}}
+ return x; // expected-note {{returned here}}
+ }
+ return Global;
+}
+
+View ContainerMyObjReturnView(Container<MyObj> c) {
+ for (const MyObj& x : c) { // expected-warning {{address of stack memory is
returned later}}
+ return x; // expected-note {{returned here}}
+ }
+ for (View x : c) { // expected-warning {{address of stack memory is
returned later}}
+ return x; // expected-note {{returned here}}
+ }
+ return Global;
+}
+
+View ContainerViewsOk(Container<View> c) {
+ for (View x : c) {
+ return x;
+ }
+ for (const View& x : c) {
+ return x;
+ }
+ return Global;
+}
+} // namespace ViewsBeginEndIterators
+
namespace reference_type_decl_ref_expr {
struct S {
S();
``````````
</details>
https://github.com/llvm/llvm-project/pull/177660
_______________________________________________
llvm-branch-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits