[clang] [analyzer] Detect leaks of stack addresses via output params, indirect globals (PR #105648)
Arseniy Zaostrovnykh via cfe-commits
cfe-commits at lists.llvm.org
Thu Aug 22 05:46:21 PDT 2024
https://github.com/necto updated https://github.com/llvm/llvm-project/pull/105648
>From db68dcfba96bfbf9367ba4159b6bf179c8c56f4f Mon Sep 17 00:00:00 2001
From: Arseniy Zaostrovnykh <necto.ne at gmail.com>
Date: Tue, 20 Aug 2024 10:26:38 +0200
Subject: [PATCH 1/3] [analyzer] [NFC] Add tests for and refactor
StackAddrEscapeChecker
These tests and refactoring are preparatory for the upcoming changes:
detection of the indirect leak via global variables and output parameters.
CPP-4734
---
.../Checkers/StackAddrEscapeChecker.cpp | 71 ++-
clang/test/Analysis/stack-addr-ps.c | 31 +
clang/test/Analysis/stack-addr-ps.cpp | 596 ++++++++++++++++++
3 files changed, 665 insertions(+), 33 deletions(-)
diff --git a/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
index ea09c43cc5ce90..2bd4ca4528de8b 100644
--- a/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
@@ -288,12 +288,37 @@ void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS,
EmitStackError(C, R, RetE);
}
+std::optional<std::string> printReferrer(const MemRegion *Referrer) {
+ assert(Referrer);
+ const StringRef ReferrerMemorySpace = [](const MemSpaceRegion *Space) {
+ if (isa<StaticGlobalSpaceRegion>(Space))
+ return "static";
+ if (isa<GlobalsSpaceRegion>(Space))
+ return "global";
+ assert(isa<StackSpaceRegion>(Space));
+ return "stack";
+ }(Referrer->getMemorySpace());
+
+ // We should really only have VarRegions here.
+ // Anything else is really surprising, and we should get notified if such
+ // ever happens.
+ const auto *ReferrerVar = dyn_cast<VarRegion>(Referrer);
+ if (!ReferrerVar) {
+ assert(false && "We should have a VarRegion here");
+ return std::nullopt; // Defensively skip this one.
+ }
+ const std::string ReferrerVarName =
+ ReferrerVar->getDecl()->getDeclName().getAsString();
+
+ return (ReferrerMemorySpace + " variable '" + ReferrerVarName + "'").str();
+}
+
void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS,
CheckerContext &Ctx) const {
if (!ChecksEnabled[CK_StackAddrEscapeChecker])
return;
- ProgramStateRef State = Ctx.getState();
+ ExplodedNode *Node = Ctx.getPredecessor();
// Iterate over all bindings to global variables and see if it contains
// a memory region in the stack space.
@@ -315,15 +340,10 @@ void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS,
if (!ReferrerMemSpace || !ReferredMemSpace)
return false;
- const auto *ReferrerFrame = ReferrerMemSpace->getStackFrame();
- const auto *ReferredFrame = ReferredMemSpace->getStackFrame();
-
- if (ReferrerMemSpace && ReferredMemSpace) {
- if (ReferredFrame == PoppedFrame &&
- ReferrerFrame->isParentOf(PoppedFrame)) {
- V.emplace_back(Referrer, Referred);
- return true;
- }
+ if (ReferredMemSpace->getStackFrame() == PoppedFrame &&
+ ReferrerMemSpace->getStackFrame()->isParentOf(PoppedFrame)) {
+ V.emplace_back(Referrer, Referred);
+ return true;
}
return false;
}
@@ -352,6 +372,7 @@ void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS,
};
CallBack Cb(Ctx);
+ ProgramStateRef State = Node->getState();
State->getStateManager().getStoreManager().iterBindings(State->getStore(),
Cb);
@@ -359,7 +380,7 @@ void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS,
return;
// Generate an error node.
- ExplodedNode *N = Ctx.generateNonFatalErrorNode(State);
+ ExplodedNode *N = Ctx.generateNonFatalErrorNode(State, Node);
if (!N)
return;
@@ -374,13 +395,13 @@ void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS,
// Generate a report for this bug.
const StringRef CommonSuffix =
- "upon returning to the caller. This will be a dangling reference";
+ " upon returning to the caller. This will be a dangling reference";
SmallString<128> Buf;
llvm::raw_svector_ostream Out(Buf);
const SourceRange Range = genName(Out, Referred, Ctx.getASTContext());
if (isa<CXXTempObjectRegion, CXXLifetimeExtendedObjectRegion>(Referrer)) {
- Out << " is still referred to by a temporary object on the stack "
+ Out << " is still referred to by a temporary object on the stack"
<< CommonSuffix;
auto Report =
std::make_unique<PathSensitiveBugReport>(*BT_stackleak, Out.str(), N);
@@ -390,28 +411,12 @@ void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS,
return;
}
- const StringRef ReferrerMemorySpace = [](const MemSpaceRegion *Space) {
- if (isa<StaticGlobalSpaceRegion>(Space))
- return "static";
- if (isa<GlobalsSpaceRegion>(Space))
- return "global";
- assert(isa<StackSpaceRegion>(Space));
- return "stack";
- }(Referrer->getMemorySpace());
-
- // We should really only have VarRegions here.
- // Anything else is really surprising, and we should get notified if such
- // ever happens.
- const auto *ReferrerVar = dyn_cast<VarRegion>(Referrer);
- if (!ReferrerVar) {
- assert(false && "We should have a VarRegion here");
- continue; // Defensively skip this one.
+ auto ReferrerVariable = printReferrer(Referrer);
+ if (!ReferrerVariable) {
+ continue;
}
- const std::string ReferrerVarName =
- ReferrerVar->getDecl()->getDeclName().getAsString();
- Out << " is still referred to by the " << ReferrerMemorySpace
- << " variable '" << ReferrerVarName << "' " << CommonSuffix;
+ Out << " is still referred to by the " << *ReferrerVariable << CommonSuffix;
auto Report =
std::make_unique<PathSensitiveBugReport>(*BT_stackleak, Out.str(), N);
if (Range.isValid())
diff --git a/clang/test/Analysis/stack-addr-ps.c b/clang/test/Analysis/stack-addr-ps.c
index e69ab4189b524f..2e14b7820be136 100644
--- a/clang/test/Analysis/stack-addr-ps.c
+++ b/clang/test/Analysis/stack-addr-ps.c
@@ -95,3 +95,34 @@ void callTestRegister(void) {
char buf[20];
testRegister(buf); // no-warning
}
+
+void top_level_leaking(int **out) {
+ int local = 42;
+ *out = &local; // no-warning FIXME
+}
+
+void callee_leaking_via_param(int **out) {
+ int local = 1;
+ *out = &local;
+ // expected-warning at -1{{Address of stack memory associated with local variable 'local' is still referred to by the stack variable 'ptr'}}
+}
+
+void caller_for_leaking_callee() {
+ int *ptr = 0;
+ callee_leaking_via_param(&ptr);
+}
+
+void callee_nested_leaking(int **out) {
+ int local = 1;
+ *out = &local;
+ // expected-warning at -1{{Address of stack memory associated with local variable 'local' is still referred to by the stack variable 'ptr'}}
+}
+
+void caller_mid_for_nested_leaking(int **mid) {
+ callee_nested_leaking(mid);
+}
+
+void caller_for_nested_leaking() {
+ int *ptr = 0;
+ caller_mid_for_nested_leaking(&ptr);
+}
diff --git a/clang/test/Analysis/stack-addr-ps.cpp b/clang/test/Analysis/stack-addr-ps.cpp
index bd856be2b8d690..68ccc322bf2ef2 100644
--- a/clang/test/Analysis/stack-addr-ps.cpp
+++ b/clang/test/Analysis/stack-addr-ps.cpp
@@ -161,3 +161,599 @@ C make1() {
void test_copy_elision() {
C c1 = make1();
}
+
+namespace leaking_via_direct_pointer {
+void* returned_direct_pointer_top() {
+ int local = 42;
+ int* p = &local;
+ return p; // expected-warning{{associated with local variable 'local' returned}}
+}
+
+int* returned_direct_pointer_callee() {
+ int local = 42;
+ int* p = &local;
+ return p; // expected-warning{{associated with local variable 'local' returned}}
+}
+
+void returned_direct_pointer_caller() {
+ int* loc_ptr = nullptr;
+ loc_ptr = returned_direct_pointer_callee();
+ (void)loc_ptr;
+}
+
+void* global_ptr;
+
+void global_direct_pointer() {
+ int local = 42;
+ global_ptr = &local;
+} // expected-warning{{local variable 'local' is still referred to by the global variable 'global_ptr'}}
+
+void static_direct_pointer_top() {
+ int local = 42;
+ static int* p = &local;
+ (void)p;
+} // expected-warning{{local variable 'local' is still referred to by the static variable 'p'}}
+
+void static_direct_pointer_callee() {
+ int local = 42;
+ static int* p = &local;
+ (void)p; // expected-warning{{local variable 'local' is still referred to by the static variable 'p'}}
+}
+
+void static_direct_pointer_caller() {
+ static_direct_pointer_callee();
+}
+
+void lambda_to_global_direct_pointer() {
+ auto lambda = [&] {
+ int local = 42;
+ global_ptr = &local; // expected-warning{{local variable 'local' is still referred to by the global variable 'global_ptr'}}
+ };
+ lambda();
+}
+
+void lambda_to_context_direct_pointer() {
+ int *p = nullptr;
+ auto lambda = [&] {
+ int local = 42;
+ p = &local; // expected-warning{{local variable 'local' is still referred to by the stack variable 'p'}}
+ };
+ lambda();
+ (void)p;
+}
+
+template<typename Callable>
+class MyFunction {
+ Callable* fptr;
+ public:
+ MyFunction(Callable* callable) :fptr(callable) {}
+};
+
+void* lambda_to_context_direct_pointer_uncalled() {
+ int *p = nullptr;
+ auto lambda = [&] {
+ int local = 42;
+ p = &local; // no-warning: analyzed only as top-level, ignored explicitly by the checker
+ };
+ return new MyFunction(&lambda);
+}
+
+void lambda_to_context_direct_pointer_lifetime_extended() {
+ int *p = nullptr;
+ auto lambda = [&] {
+ int&& local = 42;
+ p = &local; // expected-warning{{'int' lifetime extended by local variable 'local' is still referred to by the stack variable 'p'}}
+ };
+ lambda();
+ (void)p;
+}
+
+template<typename Callback>
+void lambda_param_capture_direct_pointer_callee(Callback& callee) {
+ int local = 42;
+ callee(local); // expected-warning{{'local' is still referred to by the stack variable 'p'}}
+}
+
+void lambda_param_capture_direct_pointer_caller() {
+ int* p = nullptr;
+ auto capt = [&p](int& param) {
+ p = ¶m;
+ };
+ lambda_param_capture_direct_pointer_callee(capt);
+}
+} // namespace leaking_via_direct_pointer
+
+namespace leaking_via_ptr_to_ptr {
+void** returned_ptr_to_ptr_top() {
+ int local = 42;
+ int* p = &local;
+ void** pp = (void**)&p;
+ return pp; // expected-warning{{associated with local variable 'p' returned}}
+}
+
+void** global_pp;
+
+void global_ptr_local_to_ptr() {
+ int local = 42;
+ int* p = &local;
+ global_pp = (void**)&p;
+} // expected-warning{{local variable 'p' is still referred to by the global variable 'global_pp'}}
+
+void global_ptr_to_ptr() {
+ int local = 42;
+ *global_pp = &local; // no-warning FIXME
+}
+
+void *** global_ppp;
+
+void global_ptr_to_ptr_to_ptr() {
+ int local = 42;
+ **global_ppp = &local; // no-warning FIXME
+}
+
+void** get_some_pp();
+
+void static_ptr_to_ptr() {
+ int local = 42;
+ static void** pp = get_some_pp();
+ *pp = &local;
+} // no-warning False Negative, requires relating multiple bindings to cross the invented pointer.
+
+void param_ptr_to_ptr_top(void** pp) {
+ int local = 42;
+ *pp = &local; // no-warning FIXME
+}
+
+void param_ptr_to_ptr_callee(void** pp) {
+ int local = 42;
+ *pp = &local; // expected-warning{{local variable 'local' is still referred to by the stack variable 'p'}}
+}
+
+void param_ptr_to_ptr_caller() {
+ void* p = nullptr;
+ param_ptr_to_ptr_callee((void**)&p);
+}
+
+void param_ptr_to_ptr_to_ptr_top(void*** ppp) {
+ int local = 42;
+ **ppp = &local; // no-warning FIXME
+}
+
+void param_ptr_to_ptr_to_ptr_callee(void*** ppp) {
+ int local = 42;
+ **ppp = &local; // no-warning FIXME
+}
+
+void param_ptr_to_ptr_to_ptr_caller(void** pp) {
+ param_ptr_to_ptr_to_ptr_callee(&pp);
+}
+
+void lambda_to_context_ptr_to_ptr(int **pp) {
+ auto lambda = [&] {
+ int local = 42;
+ *pp = &local; // no-warning FIXME
+ };
+ lambda();
+ (void)*pp;
+}
+
+void param_ptr_to_ptr_fptr(int **pp) {
+ int local = 42;
+ *pp = &local; // expected-warning{{local variable 'local' is still referred to by the stack variable 'p'}}
+}
+
+void param_ptr_to_ptr_fptr_caller(void (*fptr)(int**)) {
+ int* p = nullptr;
+ fptr(&p);
+}
+
+void param_ptr_to_ptr_caller_caller() {
+ void (*fptr)(int**) = param_ptr_to_ptr_fptr;
+ param_ptr_to_ptr_fptr_caller(fptr);
+}
+} // namespace leaking_via_ptr_to_ptr
+
+namespace leaking_via_ref_to_ptr {
+void** make_ptr_to_ptr();
+void*& global_rtp = *make_ptr_to_ptr();
+
+void global_ref_to_ptr() {
+ int local = 42;
+ int* p = &local;
+ global_rtp = p; // no-warning FIXME
+}
+
+void static_ref_to_ptr() {
+ int local = 42;
+ static void*& p = *make_ptr_to_ptr();
+ p = &local;
+ (void)p;
+} // no-warning False Negative, requires relating multiple bindings to cross the invented pointer.
+
+void param_ref_to_ptr_top(void*& rp) {
+ int local = 42;
+ int* p = &local;
+ rp = p; // no-warning FIXME
+}
+
+void param_ref_to_ptr_callee(void*& rp) {
+ int local = 42;
+ int* p = &local;
+ rp = p; // expected-warning{{local variable 'local' is still referred to by the stack variable 'p'}}
+}
+
+void param_ref_to_ptr_caller() {
+ void* p = nullptr;
+ param_ref_to_ptr_callee(p);
+}
+} // namespace leaking_via_ref_to_ptr
+
+namespace leaking_via_arr_of_ptr_static_idx {
+void** returned_arr_of_ptr_top() {
+ int local = 42;
+ int* p = &local;
+ void** arr = new void*[2];
+ arr[1] = p;
+ return arr;
+} // no-warning False Negative
+
+void** returned_arr_of_ptr_callee() {
+ int local = 42;
+ int* p = &local;
+ void** arr = new void*[2];
+ arr[1] = p;
+ return arr;
+} // no-warning False Negative
+
+void returned_arr_of_ptr_caller() {
+ void** arr = returned_arr_of_ptr_callee();
+ (void)arr[1];
+}
+
+void* global_aop[2];
+
+void global_arr_of_ptr() {
+ int local = 42;
+ int* p = &local;
+ global_aop[1] = p;
+} // expected-warning{{local variable 'local' is still referred to by the global variable 'global_aop'}}
+
+void static_arr_of_ptr() {
+ int local = 42;
+ static void* arr[2];
+ arr[1] = &local;
+ (void)arr[1];
+} // expected-warning{{local variable 'local' is still referred to by the static variable 'arr'}}
+
+void param_arr_of_ptr_top(void* arr[2]) {
+ int local = 42;
+ int* p = &local;
+ arr[1] = p; // no-warning FIXME
+}
+
+void param_arr_of_ptr_callee(void* arr[2]) {
+ int local = 42;
+ int* p = &local;
+ arr[1] = p; // expected-warning{{local variable 'local' is still referred to by the stack variable 'arrStack'}}
+}
+
+void param_arr_of_ptr_caller() {
+ void* arrStack[2];
+ param_arr_of_ptr_callee(arrStack);
+ (void)arrStack[1];
+}
+} // namespace leaking_via_arr_of_ptr_static_idx
+
+namespace leaking_via_arr_of_ptr_dynamic_idx {
+void** returned_arr_of_ptr_top(int idx) {
+ int local = 42;
+ int* p = &local;
+ void** arr = new void*[2];
+ arr[idx] = p;
+ return arr;
+} // no-warning False Negative
+
+void** returned_arr_of_ptr_callee(int idx) {
+ int local = 42;
+ int* p = &local;
+ void** arr = new void*[2];
+ arr[idx] = p;
+ return arr;
+} // no-warning False Negative
+
+void returned_arr_of_ptr_caller(int idx) {
+ void** arr = returned_arr_of_ptr_callee(idx);
+ (void)arr[idx];
+}
+
+void* global_aop[2];
+
+void global_arr_of_ptr(int idx) {
+ int local = 42;
+ int* p = &local;
+ global_aop[idx] = p;
+} // expected-warning{{local variable 'local' is still referred to by the global variable 'global_aop'}}
+
+void static_arr_of_ptr(int idx) {
+ int local = 42;
+ static void* arr[2];
+ arr[idx] = &local;
+ (void)arr[idx];
+} // expected-warning{{local variable 'local' is still referred to by the static variable 'arr'}}
+
+void param_arr_of_ptr_top(void* arr[2], int idx) {
+ int local = 42;
+ int* p = &local;
+ arr[idx] = p; // no-warning FIXME
+}
+
+void param_arr_of_ptr_callee(void* arr[2], int idx) {
+ int local = 42;
+ int* p = &local;
+ arr[idx] = p; // expected-warning{{local variable 'local' is still referred to by the stack variable 'arrStack'}}
+}
+
+void param_arr_of_ptr_caller(int idx) {
+ void* arrStack[2];
+ param_arr_of_ptr_callee(arrStack, idx);
+ (void)arrStack[idx];
+}
+} // namespace leaking_via_arr_of_ptr_dynamic_idx
+
+namespace leaking_via_struct_with_ptr {
+struct S {
+ int* p;
+};
+
+S returned_struct_with_ptr_top() {
+ int local = 42;
+ S s;
+ s.p = &local;
+ return s;
+} // no-warning False Negative, requires traversing returned LazyCompoundVals
+
+S returned_struct_with_ptr_callee() {
+ int local = 42;
+ S s;
+ s.p = &local;
+ return s; // expected-warning{{'local' is still referred to by the stack variable 's'}}
+}
+
+void returned_struct_with_ptr_caller() {
+ S s = returned_struct_with_ptr_callee();
+ (void)s.p;
+}
+
+S global_s;
+
+void global_struct_with_ptr() {
+ int local = 42;
+ global_s.p = &local;
+} // expected-warning{{'local' is still referred to by the global variable 'global_s'}}
+
+void static_struct_with_ptr() {
+ int local = 42;
+ static S s;
+ s.p = &local;
+ (void)s.p;
+} // expected-warning{{'local' is still referred to by the static variable 's'}}
+} // namespace leaking_via_struct_with_ptr
+
+namespace leaking_via_ref_to_struct_with_ptr {
+struct S {
+ int* p;
+};
+
+S &global_s = *(new S);
+
+void global_ref_to_struct_with_ptr() {
+ int local = 42;
+ global_s.p = &local; // no-warning FIXME
+}
+
+void static_ref_to_struct_with_ptr() {
+ int local = 42;
+ static S &s = *(new S);
+ s.p = &local;
+ (void)s.p;
+} // no-warning False Negative, requires relating multiple bindings to cross a heap region.
+
+void param_ref_to_struct_with_ptr_top(S &s) {
+ int local = 42;
+ s.p = &local; // no-warning FIXME
+}
+
+void param_ref_to_struct_with_ptr_callee(S &s) {
+ int local = 42;
+ s.p = &local; // expected-warning{{'local' is still referred to by the stack variable 'sStack'}}
+}
+
+void param_ref_to_struct_with_ptr_caller() {
+ S sStack;
+ param_ref_to_struct_with_ptr_callee(sStack);
+}
+
+template<typename Callable>
+void lambda_param_capture_callee(Callable& callee) {
+ int local = 42;
+ callee(local); // expected-warning{{'local' is still referred to by the stack variable 'p'}}
+}
+
+void lambda_param_capture_caller() {
+ int* p = nullptr;
+ auto capt = [&p](int& param) {
+ p = ¶m;
+ };
+ lambda_param_capture_callee(capt);
+}
+} // namespace leaking_via_ref_to_struct_with_ptr
+
+namespace leaking_via_ptr_to_struct_with_ptr {
+struct S {
+ int* p;
+};
+
+S* returned_ptr_to_struct_with_ptr_top() {
+ int local = 42;
+ S* s = new S;
+ s->p = &local;
+ return s;
+} // no-warning False Negative
+
+S* returned_ptr_to_struct_with_ptr_callee() {
+ int local = 42;
+ S* s = new S;
+ s->p = &local;
+ return s;
+} // no-warning False Negative
+
+void returned_ptr_to_struct_with_ptr_caller() {
+ S* s = returned_ptr_to_struct_with_ptr_callee();
+ (void)s->p;
+}
+
+S* global_s;
+
+void global_ptr_to_struct_with_ptr() {
+ int local = 42;
+ global_s->p = &local; // no-warning FIXME
+}
+
+void static_ptr_to_struct_with_ptr_new() {
+ int local = 42;
+ static S* s = new S;
+ s->p = &local;
+ (void)s->p;
+} // no-warning False Negative, requires relating multiple bindings to cross a heap region.
+
+S* get_some_s();
+
+void static_ptr_to_struct_with_ptr_generated() {
+ int local = 42;
+ static S* s = get_some_s();
+ s->p = &local;
+} // no-warning False Negative, requires relating multiple bindings to cross the invented pointer.
+
+void param_ptr_to_struct_with_ptr_top(S* s) {
+ int local = 42;
+ s->p = &local; // no-warning FIXME
+}
+
+void param_ptr_to_struct_with_ptr_callee(S* s) {
+ int local = 42;
+ s->p = &local; // expected-warning{{'local' is still referred to by the stack variable 's'}}
+}
+
+void param_ptr_to_struct_with_ptr_caller() {
+ S s;
+ param_ptr_to_struct_with_ptr_callee(&s);
+ (void)s.p;
+}
+} // namespace leaking_via_ptr_to_struct_with_ptr
+
+namespace leaking_via_arr_of_struct_with_ptr {
+struct S {
+ int* p;
+};
+
+S* returned_ptr_to_struct_with_ptr_top() {
+ int local = 42;
+ S* s = new S[2];
+ s[1].p = &local;
+ return s;
+} // no-warning False Negative
+
+S* returned_ptr_to_struct_with_ptr_callee() {
+ int local = 42;
+ S* s = new S[2];
+ s[1].p = &local;
+ return s;
+} // no-warning False Negative
+
+void returned_ptr_to_struct_with_ptr_caller() {
+ S* s = returned_ptr_to_struct_with_ptr_callee();
+ (void)s[1].p;
+}
+
+S global_s[2];
+
+void global_ptr_to_struct_with_ptr() {
+ int local = 42;
+ global_s[1].p = &local;
+} // expected-warning{{'local' is still referred to by the global variable 'global_s'}}
+
+void static_ptr_to_struct_with_ptr_new() {
+ int local = 42;
+ static S* s = new S[2];
+ s[1].p = &local;
+ (void)s[1].p;
+}
+
+S* get_some_s();
+
+void static_ptr_to_struct_with_ptr_generated() {
+ int local = 42;
+ static S* s = get_some_s();
+ s[1].p = &local;
+} // no-warning False Negative, requires relating multiple bindings to cross the invented pointer.
+
+void param_ptr_to_struct_with_ptr_top(S s[2]) {
+ int local = 42;
+ s[1].p = &local; // no-warning FIXME
+}
+
+void param_ptr_to_struct_with_ptr_callee(S s[2]) {
+ int local = 42;
+ s[1].p = &local; // expected-warning{{'local' is still referred to by the stack variable 's'}}
+}
+
+void param_ptr_to_struct_with_ptr_caller() {
+ S s[2];
+ param_ptr_to_struct_with_ptr_callee(s);
+ (void)s[1].p;
+}
+} // namespace leaking_via_arr_of_struct_with_ptr
+
+namespace leaking_via_nested_and_indirect {
+struct NestedAndTransitive {
+ int** p;
+ NestedAndTransitive* next[3];
+};
+
+NestedAndTransitive global_nat;
+
+void global_nested_and_transitive() {
+ int local = 42;
+ *global_nat.next[2]->next[1]->p = &local; // no-warning FIXME
+}
+
+void param_nested_and_transitive_top(NestedAndTransitive* nat) {
+ int local = 42;
+ *nat->next[2]->next[1]->p = &local; // no-warning FIXME
+}
+
+void param_nested_and_transitive_callee(NestedAndTransitive* nat) {
+ int local = 42;
+ *nat->next[2]->next[1]->p = &local; // no-warning FIXME
+}
+
+void param_nested_and_transitive_caller(NestedAndTransitive natCaller) {
+ param_nested_and_transitive_callee(&natCaller);
+}
+
+} // namespace leaking_via_nested_and_indirect
+
+namespace leaking_as_member {
+class CRef {
+ int& ref; // expected-note{{reference member declared here}}
+ CRef(int x) : ref(x) {}
+ // expected-warning at -1 {{binding reference member 'ref' to stack allocated parameter 'x'}}
+};
+
+class CPtr {
+ int* ptr;
+ void memFun(int x) {
+ ptr = &x;
+ }
+};
+} // namespace leaking_as_member
>From d6f5423ff8fb9d344d0b73451690ce0f43a6a8e6 Mon Sep 17 00:00:00 2001
From: Arseniy Zaostrovnykh <necto.ne at gmail.com>
Date: Tue, 20 Aug 2024 10:53:25 +0200
Subject: [PATCH 2/3] [analyzer] Detect leak of a stack address through output
arguments
At this point only functions called from other functions (i.e., not
top-level) are covered. Top-level functions have a different exit
sequence, and will be handled by a subsequent change.
CPP-4734
---
.../Checkers/StackAddrEscapeChecker.cpp | 62 ++++++++++++++-----
clang/test/Analysis/stack-addr-ps.cpp | 6 +-
2 files changed, 49 insertions(+), 19 deletions(-)
diff --git a/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
index 2bd4ca4528de8b..a704c4ff2eeb02 100644
--- a/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
@@ -288,6 +288,23 @@ void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS,
EmitStackError(C, R, RetE);
}
+static const MemSpaceRegion *getStackOrGlobalSpaceRegion(const MemRegion *R) {
+ assert(R);
+ if (const auto *MemSpace = R->getMemorySpace()) {
+ if (const auto *SSR = MemSpace->getAs<StackSpaceRegion>())
+ return SSR;
+ if (const auto *GSR = MemSpace->getAs<GlobalsSpaceRegion>())
+ return GSR;
+ }
+ // If R describes a lambda capture, it will be a symbolic region
+ // referring to a field region of another symbolic region.
+ if (const auto *SymReg = R->getBaseRegion()->getAs<SymbolicRegion>()) {
+ if (const auto *OriginReg = SymReg->getSymbol()->getOriginRegion())
+ return getStackOrGlobalSpaceRegion(OriginReg);
+ }
+ return nullptr;
+}
+
std::optional<std::string> printReferrer(const MemRegion *Referrer) {
assert(Referrer);
const StringRef ReferrerMemorySpace = [](const MemSpaceRegion *Space) {
@@ -297,20 +314,29 @@ std::optional<std::string> printReferrer(const MemRegion *Referrer) {
return "global";
assert(isa<StackSpaceRegion>(Space));
return "stack";
- }(Referrer->getMemorySpace());
-
- // We should really only have VarRegions here.
- // Anything else is really surprising, and we should get notified if such
- // ever happens.
- const auto *ReferrerVar = dyn_cast<VarRegion>(Referrer);
- if (!ReferrerVar) {
- assert(false && "We should have a VarRegion here");
- return std::nullopt; // Defensively skip this one.
+ }(getStackOrGlobalSpaceRegion(Referrer));
+
+ while (!Referrer->canPrintPretty()) {
+ if (const auto *SymReg = dyn_cast<SymbolicRegion>(Referrer)) {
+ Referrer = SymReg->getSymbol()->getOriginRegion()->getBaseRegion();
+ } else if (isa<CXXThisRegion>(Referrer)) {
+ // Skip members of a class, it is handled by
+ // warn_bind_ref_member_to_parameter_addr
+ return std::nullopt;
+ } else {
+ Referrer->dump();
+ assert(false && "Unexpected referrer region type.");
+ return std::nullopt;
+ }
}
- const std::string ReferrerVarName =
- ReferrerVar->getDecl()->getDeclName().getAsString();
+ assert(Referrer);
+ assert(Referrer->canPrintPretty());
- return (ReferrerMemorySpace + " variable '" + ReferrerVarName + "'").str();
+ std::string buf;
+ llvm::raw_string_ostream os(buf);
+ os << ReferrerMemorySpace << " variable ";
+ Referrer->printPretty(os);
+ return buf;
}
void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS,
@@ -332,16 +358,20 @@ void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS,
/// referred by an other stack variable from different stack frame.
bool checkForDanglingStackVariable(const MemRegion *Referrer,
const MemRegion *Referred) {
- const auto *ReferrerMemSpace =
- Referrer->getMemorySpace()->getAs<StackSpaceRegion>();
+ const auto *ReferrerMemSpace = getStackOrGlobalSpaceRegion(Referrer);
const auto *ReferredMemSpace =
Referred->getMemorySpace()->getAs<StackSpaceRegion>();
if (!ReferrerMemSpace || !ReferredMemSpace)
return false;
+ const auto *ReferrerStackSpace =
+ ReferrerMemSpace->getAs<StackSpaceRegion>();
+ if (!ReferrerStackSpace)
+ return false;
+
if (ReferredMemSpace->getStackFrame() == PoppedFrame &&
- ReferrerMemSpace->getStackFrame()->isParentOf(PoppedFrame)) {
+ ReferrerStackSpace->getStackFrame()->isParentOf(PoppedFrame)) {
V.emplace_back(Referrer, Referred);
return true;
}
@@ -387,7 +417,7 @@ void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS,
if (!BT_stackleak)
BT_stackleak =
std::make_unique<BugType>(CheckNames[CK_StackAddrEscapeChecker],
- "Stack address stored into global variable");
+ "Stack address leaks outside of stack frame");
for (const auto &P : Cb.V) {
const MemRegion *Referrer = P.first->getBaseRegion();
diff --git a/clang/test/Analysis/stack-addr-ps.cpp b/clang/test/Analysis/stack-addr-ps.cpp
index 68ccc322bf2ef2..b8bd70ff5d98f5 100644
--- a/clang/test/Analysis/stack-addr-ps.cpp
+++ b/clang/test/Analysis/stack-addr-ps.cpp
@@ -321,7 +321,7 @@ void param_ptr_to_ptr_to_ptr_top(void*** ppp) {
void param_ptr_to_ptr_to_ptr_callee(void*** ppp) {
int local = 42;
- **ppp = &local; // no-warning FIXME
+ **ppp = &local; // expected-warning{{local variable 'local' is still referred to by the stack variable 'pp'}}
}
void param_ptr_to_ptr_to_ptr_caller(void** pp) {
@@ -331,7 +331,7 @@ void param_ptr_to_ptr_to_ptr_caller(void** pp) {
void lambda_to_context_ptr_to_ptr(int **pp) {
auto lambda = [&] {
int local = 42;
- *pp = &local; // no-warning FIXME
+ *pp = &local; // expected-warning{{local variable 'local' is still referred to by the stack variable 'pp'}}
};
lambda();
(void)*pp;
@@ -734,7 +734,7 @@ void param_nested_and_transitive_top(NestedAndTransitive* nat) {
void param_nested_and_transitive_callee(NestedAndTransitive* nat) {
int local = 42;
- *nat->next[2]->next[1]->p = &local; // no-warning FIXME
+ *nat->next[2]->next[1]->p = &local; // expected-warning{{local variable 'local' is still referred to by the stack variable 'natCaller'}}
}
void param_nested_and_transitive_caller(NestedAndTransitive natCaller) {
>From daec8e894b174fa5b01b5fde2da4eb230b7cade7 Mon Sep 17 00:00:00 2001
From: Arseniy Zaostrovnykh <necto.ne at gmail.com>
Date: Tue, 20 Aug 2024 11:16:10 +0200
Subject: [PATCH 3/3] [analyzer] Detect leaks on top-level via output params,
indirect globals
Fix some false negatives of StackAddrEscapeChecker:
- Output parameters
```
void top(int **out) {
int local = 42;
*out = &local; // Noncompliant
}
```
- Indirect global pointers
```
int **global;
void top() {
int local = 42;
*global = &local; // Noncompliant
}
```
Note that now StackAddrEscapeChecker produces a diagnostic if a function
with an output parameter is analyzed as top-level or as a callee. I took
special care to make sure the reports point to the same primary location
and, in many cases, feature the same primary message. That is the
motivation to modify Core/BugReporter.cpp and Core/ExplodedGraph.cpp
To avoid false positive reports when a global indirect pointer is
assigned a local address, invalidated, and then reset, I rely on the
fact that the invalidation symbol will be a DerivedSymbol of a
ConjuredSymbol that refers to the same memory region.
The checker still has a false negative for non-trivial escaping via a
returned value. It requires a more sophisticated traversal akin to
scanReachableSymbols, which out of the scope of this change.
CPP-4734
---
.../Checkers/StackAddrEscapeChecker.cpp | 69 +++++++++--
clang/lib/StaticAnalyzer/Core/BugReporter.cpp | 13 +-
.../lib/StaticAnalyzer/Core/ExplodedGraph.cpp | 2 +-
clang/test/Analysis/copy-elision.cpp | 20 +--
.../test/Analysis/incorrect-checker-names.cpp | 4 +-
clang/test/Analysis/loop-block-counts.c | 2 +-
clang/test/Analysis/stack-addr-ps.c | 6 +-
clang/test/Analysis/stack-addr-ps.cpp | 115 ++++++++++--------
.../Analysis/stack-capture-leak-no-arc.mm | 4 +-
clang/test/Analysis/stackaddrleak.c | 8 +-
10 files changed, 160 insertions(+), 83 deletions(-)
diff --git a/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
index a704c4ff2eeb02..ef477a808fac17 100644
--- a/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
@@ -305,6 +305,14 @@ static const MemSpaceRegion *getStackOrGlobalSpaceRegion(const MemRegion *R) {
return nullptr;
}
+const MemRegion *getOriginBaseRegion(const MemRegion *Referrer) {
+ Referrer = Referrer->getBaseRegion();
+ while (const auto *SymReg = dyn_cast<SymbolicRegion>(Referrer)) {
+ Referrer = SymReg->getSymbol()->getOriginRegion()->getBaseRegion();
+ }
+ return Referrer;
+}
+
std::optional<std::string> printReferrer(const MemRegion *Referrer) {
assert(Referrer);
const StringRef ReferrerMemorySpace = [](const MemSpaceRegion *Space) {
@@ -312,8 +320,12 @@ std::optional<std::string> printReferrer(const MemRegion *Referrer) {
return "static";
if (isa<GlobalsSpaceRegion>(Space))
return "global";
- assert(isa<StackSpaceRegion>(Space));
- return "stack";
+ // UnknownSpaceRegion usually refers to pointers-to-pointers,
+ // might be in top frame or not with pointers-to-pointers-to-pointers
+ assert((isa<UnknownSpaceRegion, StackSpaceRegion>(Space)));
+ // These two cases are deliberately combined to keep the message identical
+ // between the top-level and inlined analysis of the same function
+ return "caller";
}(getStackOrGlobalSpaceRegion(Referrer));
while (!Referrer->canPrintPretty()) {
@@ -346,12 +358,27 @@ void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS,
ExplodedNode *Node = Ctx.getPredecessor();
+ bool ExitingTopFrame =
+ Ctx.getPredecessor()->getLocationContext()->inTopFrame();
+
+ if (ExitingTopFrame && Node->getLocation().getTag() &&
+ Node->getLocation().getTag()->getTagDescription() ==
+ "ExprEngine : Clean Node" &&
+ Node->getFirstPred()) {
+ // When finishing analysis of a top-level function, engine proactively
+ // removes dead symbols thus preventing this checker from looking through
+ // the output parameters. Take 1 step back, to the node where these symbols
+ // and their bindings are still present
+ Node = Node->getFirstPred();
+ }
+
// Iterate over all bindings to global variables and see if it contains
// a memory region in the stack space.
class CallBack : public StoreManager::BindingsHandler {
private:
CheckerContext &Ctx;
const StackFrameContext *PoppedFrame;
+ const bool TopFrame;
/// Look for stack variables referring to popped stack variables.
/// Returns true only if it found some dangling stack variables
@@ -367,24 +394,48 @@ void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS,
const auto *ReferrerStackSpace =
ReferrerMemSpace->getAs<StackSpaceRegion>();
+
if (!ReferrerStackSpace)
return false;
- if (ReferredMemSpace->getStackFrame() == PoppedFrame &&
- ReferrerStackSpace->getStackFrame()->isParentOf(PoppedFrame)) {
+ if (const auto *ReferredFrame = ReferredMemSpace->getStackFrame();
+ ReferredFrame != PoppedFrame) {
+ return false;
+ }
+
+ if (ReferrerStackSpace->getStackFrame()->isParentOf(PoppedFrame)) {
+ V.emplace_back(Referrer, Referred);
+ return true;
+ }
+ if (isa<StackArgumentsSpaceRegion>(ReferrerMemSpace) &&
+ ReferrerStackSpace->getStackFrame() == PoppedFrame && TopFrame) {
+ // Output parameter of a top-level function
V.emplace_back(Referrer, Referred);
return true;
}
return false;
}
+ void updateInvalidatedRegions(const MemRegion *Region) {
+ if (const auto *SymReg = Region->getAs<SymbolicRegion>()) {
+ SymbolRef Symbol = SymReg->getSymbol();
+ if (const auto *DerS = dyn_cast<SymbolDerived>(Symbol);
+ DerS && isa_and_nonnull<SymbolConjured>(DerS->getParentSymbol())) {
+ InvalidatedRegions.insert(Symbol->getOriginRegion()->getBaseRegion());
+ }
+ }
+ }
+
public:
SmallVector<std::pair<const MemRegion *, const MemRegion *>, 10> V;
+ llvm::SmallPtrSet<const MemRegion *, 4> InvalidatedRegions;
- CallBack(CheckerContext &CC) : Ctx(CC), PoppedFrame(CC.getStackFrame()) {}
+ CallBack(CheckerContext &CC, bool TopFrame)
+ : Ctx(CC), PoppedFrame(CC.getStackFrame()), TopFrame(TopFrame) {}
bool HandleBinding(StoreManager &SMgr, Store S, const MemRegion *Region,
SVal Val) override {
+ updateInvalidatedRegions(Region);
const MemRegion *VR = Val.getAsRegion();
if (!VR)
return true;
@@ -393,7 +444,8 @@ void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS,
return true;
// Check the globals for the same.
- if (!isa<GlobalsSpaceRegion>(Region->getMemorySpace()))
+ if (!isa_and_nonnull<GlobalsSpaceRegion>(
+ getStackOrGlobalSpaceRegion(Region)))
return true;
if (VR && VR->hasStackStorage() && !isNotInCurrentFrame(VR, Ctx))
V.emplace_back(Region, VR);
@@ -401,7 +453,7 @@ void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS,
}
};
- CallBack Cb(Ctx);
+ CallBack Cb(Ctx, ExitingTopFrame);
ProgramStateRef State = Node->getState();
State->getStateManager().getStoreManager().iterBindings(State->getStore(),
Cb);
@@ -422,6 +474,9 @@ void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS,
for (const auto &P : Cb.V) {
const MemRegion *Referrer = P.first->getBaseRegion();
const MemRegion *Referred = P.second;
+ if (Cb.InvalidatedRegions.contains(getOriginBaseRegion(Referrer))) {
+ continue;
+ }
// Generate a report for this bug.
const StringRef CommonSuffix =
diff --git a/clang/lib/StaticAnalyzer/Core/BugReporter.cpp b/clang/lib/StaticAnalyzer/Core/BugReporter.cpp
index d73dc40cf03fbb..ea916c3585cadc 100644
--- a/clang/lib/StaticAnalyzer/Core/BugReporter.cpp
+++ b/clang/lib/StaticAnalyzer/Core/BugReporter.cpp
@@ -2436,8 +2436,19 @@ PathSensitiveBugReport::getLocation() const {
if (auto FE = P.getAs<FunctionExitPoint>()) {
if (const ReturnStmt *RS = FE->getStmt())
return PathDiagnosticLocation::createBegin(RS, SM, LC);
+
+ // If we are exiting a destructor call, it is more useful to point to the
+ // next stmt which is usually the temporary declaration.
+ // For non-destructor and non-top-level calls, the next stmt will still
+ // refer to the last executed stmt of the body.
+ S = ErrorNode->getNextStmtForDiagnostics();
+ // If next stmt is not found, it is likely the end of a top-level function
+ // analysis. find the last execution statement then.
+ if (!S)
+ S = ErrorNode->getPreviousStmtForDiagnostics();
}
- S = ErrorNode->getNextStmtForDiagnostics();
+ if (!S)
+ S = ErrorNode->getNextStmtForDiagnostics();
}
if (S) {
diff --git a/clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp b/clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp
index f84da769d182f8..11cef00ada330b 100644
--- a/clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp
@@ -376,7 +376,7 @@ const Stmt *ExplodedNode::getNextStmtForDiagnostics() const {
const Stmt *ExplodedNode::getPreviousStmtForDiagnostics() const {
for (const ExplodedNode *N = getFirstPred(); N; N = N->getFirstPred())
- if (const Stmt *S = N->getStmtForDiagnostics())
+ if (const Stmt *S = N->getStmtForDiagnostics(); S && !isa<CompoundStmt>(S))
return S;
return nullptr;
diff --git a/clang/test/Analysis/copy-elision.cpp b/clang/test/Analysis/copy-elision.cpp
index 991f325c05853d..423c4519f5bfc6 100644
--- a/clang/test/Analysis/copy-elision.cpp
+++ b/clang/test/Analysis/copy-elision.cpp
@@ -158,19 +158,19 @@ ClassWithoutDestructor make1(AddressVector<ClassWithoutDestructor> &v) {
return ClassWithoutDestructor(v);
// no-elide-warning at -1 {{Address of stack memory associated with temporary \
object of type 'ClassWithoutDestructor' is still \
-referred to by the stack variable 'v' upon returning to the caller}}
+referred to by the caller variable 'v' upon returning to the caller}}
}
ClassWithoutDestructor make2(AddressVector<ClassWithoutDestructor> &v) {
return make1(v);
// no-elide-warning at -1 {{Address of stack memory associated with temporary \
object of type 'ClassWithoutDestructor' is still \
-referred to by the stack variable 'v' upon returning to the caller}}
+referred to by the caller variable 'v' upon returning to the caller}}
}
ClassWithoutDestructor make3(AddressVector<ClassWithoutDestructor> &v) {
return make2(v);
// no-elide-warning at -1 {{Address of stack memory associated with temporary \
object of type 'ClassWithoutDestructor' is still \
-referred to by the stack variable 'v' upon returning to the caller}}
+referred to by the caller variable 'v' upon returning to the caller}}
}
void testMultipleReturns() {
@@ -193,7 +193,7 @@ void testMultipleReturns() {
void consume(ClassWithoutDestructor c) {
c.push();
// expected-warning at -1 {{Address of stack memory associated with local \
-variable 'c' is still referred to by the stack variable 'v' upon returning \
+variable 'c' is still referred to by the caller variable 'v' upon returning \
to the caller}}
}
@@ -267,7 +267,7 @@ struct TestCtorInitializer {
: c(ClassWithDestructor(v)) {}
// no-elide-warning at -1 {{Address of stack memory associated with temporary \
object of type 'ClassWithDestructor' is still referred \
-to by the stack variable 'v' upon returning to the caller}}
+to by the caller variable 'v' upon returning to the caller}}
};
void testCtorInitializer() {
@@ -303,19 +303,19 @@ ClassWithDestructor make1(AddressVector<ClassWithDestructor> &v) {
return ClassWithDestructor(v);
// no-elide-warning at -1 {{Address of stack memory associated with temporary \
object of type 'ClassWithDestructor' is still referred \
-to by the stack variable 'v' upon returning to the caller}}
+to by the caller variable 'v' upon returning to the caller}}
}
ClassWithDestructor make2(AddressVector<ClassWithDestructor> &v) {
return make1(v);
// no-elide-warning at -1 {{Address of stack memory associated with temporary \
object of type 'ClassWithDestructor' is still referred \
-to by the stack variable 'v' upon returning to the caller}}
+to by the caller variable 'v' upon returning to the caller}}
}
ClassWithDestructor make3(AddressVector<ClassWithDestructor> &v) {
return make2(v);
// no-elide-warning at -1 {{Address of stack memory associated with temporary \
object of type 'ClassWithDestructor' is still referred \
-to by the stack variable 'v' upon returning to the caller}}
+to by the caller variable 'v' upon returning to the caller}}
}
void testMultipleReturnsWithDestructors() {
@@ -360,7 +360,7 @@ void testMultipleReturnsWithDestructors() {
void consume(ClassWithDestructor c) {
c.push();
// expected-warning at -1 {{Address of stack memory associated with local \
-variable 'c' is still referred to by the stack variable 'v' upon returning \
+variable 'c' is still referred to by the caller variable 'v' upon returning \
to the caller}}
}
@@ -407,7 +407,7 @@ struct Foo {
Foo make1(Foo **r) {
return Foo(r);
// no-elide-warning at -1 {{Address of stack memory associated with temporary \
-object of type 'Foo' is still referred to by the stack \
+object of type 'Foo' is still referred to by the caller \
variable 'z' upon returning to the caller}}
}
diff --git a/clang/test/Analysis/incorrect-checker-names.cpp b/clang/test/Analysis/incorrect-checker-names.cpp
index 9854a503fc4f62..8dee0b6f468581 100644
--- a/clang/test/Analysis/incorrect-checker-names.cpp
+++ b/clang/test/Analysis/incorrect-checker-names.cpp
@@ -16,5 +16,5 @@ char const *p;
void f0() {
char const str[] = "This will change";
p = str;
-} // expected-warning{{Address of stack memory associated with local variable 'str' is still referred to by the global variable 'p' upon returning to the caller. This will be a dangling reference [core.StackAddressEscape]}}
-// expected-note at -1{{Address of stack memory associated with local variable 'str' is still referred to by the global variable 'p' upon returning to the caller. This will be a dangling reference}}
+} // expected-warning at -1{{Address of stack memory associated with local variable 'str' is still referred to by the global variable 'p' upon returning to the caller. This will be a dangling reference [core.StackAddressEscape]}}
+// expected-note at -2{{Address of stack memory associated with local variable 'str' is still referred to by the global variable 'p' upon returning to the caller. This will be a dangling reference}}
diff --git a/clang/test/Analysis/loop-block-counts.c b/clang/test/Analysis/loop-block-counts.c
index 9af67b1b632f51..ae3705f8bd7ccc 100644
--- a/clang/test/Analysis/loop-block-counts.c
+++ b/clang/test/Analysis/loop-block-counts.c
@@ -6,7 +6,7 @@ void callee(void **p) {
int x;
*p = &x;
// expected-warning at -1 {{Address of stack memory associated with local \
-variable 'x' is still referred to by the stack variable 'arr' upon \
+variable 'x' is still referred to by the caller variable 'arr' upon \
returning to the caller}}
}
diff --git a/clang/test/Analysis/stack-addr-ps.c b/clang/test/Analysis/stack-addr-ps.c
index 2e14b7820be136..138b8c16b02bde 100644
--- a/clang/test/Analysis/stack-addr-ps.c
+++ b/clang/test/Analysis/stack-addr-ps.c
@@ -98,13 +98,13 @@ void callTestRegister(void) {
void top_level_leaking(int **out) {
int local = 42;
- *out = &local; // no-warning FIXME
+ *out = &local; // expected-warning{{Address of stack memory associated with local variable 'local' is still referred to by the caller variable 'out'}}
}
void callee_leaking_via_param(int **out) {
int local = 1;
*out = &local;
- // expected-warning at -1{{Address of stack memory associated with local variable 'local' is still referred to by the stack variable 'ptr'}}
+ // expected-warning at -1{{Address of stack memory associated with local variable 'local' is still referred to by the caller variable 'ptr'}}
}
void caller_for_leaking_callee() {
@@ -115,7 +115,7 @@ void caller_for_leaking_callee() {
void callee_nested_leaking(int **out) {
int local = 1;
*out = &local;
- // expected-warning at -1{{Address of stack memory associated with local variable 'local' is still referred to by the stack variable 'ptr'}}
+ // expected-warning at -1{{Address of stack memory associated with local variable 'local' is still referred to by the caller variable 'ptr'}}
}
void caller_mid_for_nested_leaking(int **mid) {
diff --git a/clang/test/Analysis/stack-addr-ps.cpp b/clang/test/Analysis/stack-addr-ps.cpp
index b8bd70ff5d98f5..b98fc3beade761 100644
--- a/clang/test/Analysis/stack-addr-ps.cpp
+++ b/clang/test/Analysis/stack-addr-ps.cpp
@@ -140,7 +140,7 @@ void write_stack_address_to(char **q) {
char local;
*q = &local;
// expected-warning at -1 {{Address of stack memory associated with local \
-variable 'local' is still referred to by the stack variable 'p' upon \
+variable 'local' is still referred to by the caller variable 'p' upon \
returning to the caller}}
}
@@ -185,14 +185,14 @@ void* global_ptr;
void global_direct_pointer() {
int local = 42;
- global_ptr = &local;
-} // expected-warning{{local variable 'local' is still referred to by the global variable 'global_ptr'}}
+ global_ptr = &local; // expected-warning{{local variable 'local' is still referred to by the global variable 'global_ptr'}}
+}
void static_direct_pointer_top() {
int local = 42;
static int* p = &local;
- (void)p;
-} // expected-warning{{local variable 'local' is still referred to by the static variable 'p'}}
+ (void)p; // expected-warning{{local variable 'local' is still referred to by the static variable 'p'}}
+}
void static_direct_pointer_callee() {
int local = 42;
@@ -216,7 +216,7 @@ void lambda_to_context_direct_pointer() {
int *p = nullptr;
auto lambda = [&] {
int local = 42;
- p = &local; // expected-warning{{local variable 'local' is still referred to by the stack variable 'p'}}
+ p = &local; // expected-warning{{local variable 'local' is still referred to by the caller variable 'p'}}
};
lambda();
(void)p;
@@ -242,7 +242,7 @@ void lambda_to_context_direct_pointer_lifetime_extended() {
int *p = nullptr;
auto lambda = [&] {
int&& local = 42;
- p = &local; // expected-warning{{'int' lifetime extended by local variable 'local' is still referred to by the stack variable 'p'}}
+ p = &local; // expected-warning{{'int' lifetime extended by local variable 'local' is still referred to by the caller variable 'p'}}
};
lambda();
(void)p;
@@ -251,7 +251,7 @@ void lambda_to_context_direct_pointer_lifetime_extended() {
template<typename Callback>
void lambda_param_capture_direct_pointer_callee(Callback& callee) {
int local = 42;
- callee(local); // expected-warning{{'local' is still referred to by the stack variable 'p'}}
+ callee(local); // expected-warning{{'local' is still referred to by the caller variable 'p'}}
}
void lambda_param_capture_direct_pointer_caller() {
@@ -276,19 +276,19 @@ void** global_pp;
void global_ptr_local_to_ptr() {
int local = 42;
int* p = &local;
- global_pp = (void**)&p;
-} // expected-warning{{local variable 'p' is still referred to by the global variable 'global_pp'}}
+ global_pp = (void**)&p; // expected-warning{{local variable 'p' is still referred to by the global variable 'global_pp'}}
+}
void global_ptr_to_ptr() {
int local = 42;
- *global_pp = &local; // no-warning FIXME
+ *global_pp = &local; // expected-warning{{local variable 'local' is still referred to by the global variable 'global_pp'}}
}
void *** global_ppp;
void global_ptr_to_ptr_to_ptr() {
int local = 42;
- **global_ppp = &local; // no-warning FIXME
+ **global_ppp = &local; // expected-warning{{local variable 'local' is still referred to by the global variable 'global_ppp'}}
}
void** get_some_pp();
@@ -301,12 +301,12 @@ void static_ptr_to_ptr() {
void param_ptr_to_ptr_top(void** pp) {
int local = 42;
- *pp = &local; // no-warning FIXME
+ *pp = &local; // expected-warning{{local variable 'local' is still referred to by the caller variable 'pp'}}
}
void param_ptr_to_ptr_callee(void** pp) {
int local = 42;
- *pp = &local; // expected-warning{{local variable 'local' is still referred to by the stack variable 'p'}}
+ *pp = &local; // expected-warning{{local variable 'local' is still referred to by the caller variable 'p'}}
}
void param_ptr_to_ptr_caller() {
@@ -316,12 +316,12 @@ void param_ptr_to_ptr_caller() {
void param_ptr_to_ptr_to_ptr_top(void*** ppp) {
int local = 42;
- **ppp = &local; // no-warning FIXME
+ **ppp = &local; // expected-warning {{local variable 'local' is still referred to by the caller variable 'ppp'}}
}
void param_ptr_to_ptr_to_ptr_callee(void*** ppp) {
int local = 42;
- **ppp = &local; // expected-warning{{local variable 'local' is still referred to by the stack variable 'pp'}}
+ **ppp = &local; // expected-warning{{local variable 'local' is still referred to by the caller variable 'pp'}}
}
void param_ptr_to_ptr_to_ptr_caller(void** pp) {
@@ -331,7 +331,7 @@ void param_ptr_to_ptr_to_ptr_caller(void** pp) {
void lambda_to_context_ptr_to_ptr(int **pp) {
auto lambda = [&] {
int local = 42;
- *pp = &local; // expected-warning{{local variable 'local' is still referred to by the stack variable 'pp'}}
+ *pp = &local; // expected-warning{{local variable 'local' is still referred to by the caller variable 'pp'}}
};
lambda();
(void)*pp;
@@ -339,7 +339,7 @@ void lambda_to_context_ptr_to_ptr(int **pp) {
void param_ptr_to_ptr_fptr(int **pp) {
int local = 42;
- *pp = &local; // expected-warning{{local variable 'local' is still referred to by the stack variable 'p'}}
+ *pp = &local; // expected-warning{{local variable 'local' is still referred to by the caller variable 'p'}}
}
void param_ptr_to_ptr_fptr_caller(void (*fptr)(int**)) {
@@ -360,7 +360,7 @@ void*& global_rtp = *make_ptr_to_ptr();
void global_ref_to_ptr() {
int local = 42;
int* p = &local;
- global_rtp = p; // no-warning FIXME
+ global_rtp = p; // expected-warning{{local variable 'local' is still referred to by the global variable 'global_rtp'}}
}
void static_ref_to_ptr() {
@@ -373,13 +373,13 @@ void static_ref_to_ptr() {
void param_ref_to_ptr_top(void*& rp) {
int local = 42;
int* p = &local;
- rp = p; // no-warning FIXME
+ rp = p; // expected-warning{{local variable 'local' is still referred to by the caller variable 'rp'}}
}
void param_ref_to_ptr_callee(void*& rp) {
int local = 42;
int* p = &local;
- rp = p; // expected-warning{{local variable 'local' is still referred to by the stack variable 'p'}}
+ rp = p; // expected-warning{{local variable 'local' is still referred to by the caller variable 'p'}}
}
void param_ref_to_ptr_caller() {
@@ -415,26 +415,26 @@ void* global_aop[2];
void global_arr_of_ptr() {
int local = 42;
int* p = &local;
- global_aop[1] = p;
-} // expected-warning{{local variable 'local' is still referred to by the global variable 'global_aop'}}
+ global_aop[1] = p; // expected-warning{{local variable 'local' is still referred to by the global variable 'global_aop'}}
+}
void static_arr_of_ptr() {
int local = 42;
static void* arr[2];
arr[1] = &local;
- (void)arr[1];
-} // expected-warning{{local variable 'local' is still referred to by the static variable 'arr'}}
+ (void)arr[1]; // expected-warning{{local variable 'local' is still referred to by the static variable 'arr'}}
+}
void param_arr_of_ptr_top(void* arr[2]) {
int local = 42;
int* p = &local;
- arr[1] = p; // no-warning FIXME
+ arr[1] = p; // expected-warning{{local variable 'local' is still referred to by the caller variable 'arr'}}
}
void param_arr_of_ptr_callee(void* arr[2]) {
int local = 42;
int* p = &local;
- arr[1] = p; // expected-warning{{local variable 'local' is still referred to by the stack variable 'arrStack'}}
+ arr[1] = p; // expected-warning{{local variable 'local' is still referred to by the caller variable 'arrStack'}}
}
void param_arr_of_ptr_caller() {
@@ -471,26 +471,26 @@ void* global_aop[2];
void global_arr_of_ptr(int idx) {
int local = 42;
int* p = &local;
- global_aop[idx] = p;
-} // expected-warning{{local variable 'local' is still referred to by the global variable 'global_aop'}}
+ global_aop[idx] = p; // expected-warning{{local variable 'local' is still referred to by the global variable 'global_aop'}}
+}
void static_arr_of_ptr(int idx) {
int local = 42;
static void* arr[2];
arr[idx] = &local;
- (void)arr[idx];
-} // expected-warning{{local variable 'local' is still referred to by the static variable 'arr'}}
+ (void)arr[idx]; // expected-warning{{local variable 'local' is still referred to by the static variable 'arr'}}
+}
void param_arr_of_ptr_top(void* arr[2], int idx) {
int local = 42;
int* p = &local;
- arr[idx] = p; // no-warning FIXME
+ arr[idx] = p; // expected-warning{{local variable 'local' is still referred to by the caller variable 'arr'}}
}
void param_arr_of_ptr_callee(void* arr[2], int idx) {
int local = 42;
int* p = &local;
- arr[idx] = p; // expected-warning{{local variable 'local' is still referred to by the stack variable 'arrStack'}}
+ arr[idx] = p; // expected-warning{{local variable 'local' is still referred to by the caller variable 'arrStack'}}
}
void param_arr_of_ptr_caller(int idx) {
@@ -516,7 +516,7 @@ S returned_struct_with_ptr_callee() {
int local = 42;
S s;
s.p = &local;
- return s; // expected-warning{{'local' is still referred to by the stack variable 's'}}
+ return s; // expected-warning{{'local' is still referred to by the caller variable 's'}}
}
void returned_struct_with_ptr_caller() {
@@ -528,15 +528,15 @@ S global_s;
void global_struct_with_ptr() {
int local = 42;
- global_s.p = &local;
-} // expected-warning{{'local' is still referred to by the global variable 'global_s'}}
+ global_s.p = &local; // expected-warning{{'local' is still referred to by the global variable 'global_s'}}
+}
void static_struct_with_ptr() {
int local = 42;
static S s;
s.p = &local;
- (void)s.p;
-} // expected-warning{{'local' is still referred to by the static variable 's'}}
+ (void)s.p; // expected-warning{{'local' is still referred to by the static variable 's'}}
+}
} // namespace leaking_via_struct_with_ptr
namespace leaking_via_ref_to_struct_with_ptr {
@@ -548,7 +548,7 @@ S &global_s = *(new S);
void global_ref_to_struct_with_ptr() {
int local = 42;
- global_s.p = &local; // no-warning FIXME
+ global_s.p = &local; // expected-warning{{'local' is still referred to by the global variable 'global_s'}}
}
void static_ref_to_struct_with_ptr() {
@@ -560,12 +560,12 @@ void static_ref_to_struct_with_ptr() {
void param_ref_to_struct_with_ptr_top(S &s) {
int local = 42;
- s.p = &local; // no-warning FIXME
+ s.p = &local; // expected-warning{{'local' is still referred to by the caller variable 's'}}
}
void param_ref_to_struct_with_ptr_callee(S &s) {
int local = 42;
- s.p = &local; // expected-warning{{'local' is still referred to by the stack variable 'sStack'}}
+ s.p = &local; // expected-warning{{'local' is still referred to by the caller variable 'sStack'}}
}
void param_ref_to_struct_with_ptr_caller() {
@@ -576,7 +576,7 @@ void param_ref_to_struct_with_ptr_caller() {
template<typename Callable>
void lambda_param_capture_callee(Callable& callee) {
int local = 42;
- callee(local); // expected-warning{{'local' is still referred to by the stack variable 'p'}}
+ callee(local); // expected-warning{{'local' is still referred to by the caller variable 'p'}}
}
void lambda_param_capture_caller() {
@@ -616,7 +616,7 @@ S* global_s;
void global_ptr_to_struct_with_ptr() {
int local = 42;
- global_s->p = &local; // no-warning FIXME
+ global_s->p = &local; // expected-warning{{'local' is still referred to by the global variable 'global_s'}}
}
void static_ptr_to_struct_with_ptr_new() {
@@ -636,12 +636,12 @@ void static_ptr_to_struct_with_ptr_generated() {
void param_ptr_to_struct_with_ptr_top(S* s) {
int local = 42;
- s->p = &local; // no-warning FIXME
+ s->p = &local; // expected-warning{{'local' is still referred to by the caller variable 's'}}
}
void param_ptr_to_struct_with_ptr_callee(S* s) {
int local = 42;
- s->p = &local; // expected-warning{{'local' is still referred to by the stack variable 's'}}
+ s->p = &local; // expected-warning{{'local' is still referred to by the caller variable 's'}}
}
void param_ptr_to_struct_with_ptr_caller() {
@@ -679,8 +679,8 @@ S global_s[2];
void global_ptr_to_struct_with_ptr() {
int local = 42;
- global_s[1].p = &local;
-} // expected-warning{{'local' is still referred to by the global variable 'global_s'}}
+ global_s[1].p = &local; // expected-warning{{'local' is still referred to by the global variable 'global_s'}}
+}
void static_ptr_to_struct_with_ptr_new() {
int local = 42;
@@ -699,12 +699,12 @@ void static_ptr_to_struct_with_ptr_generated() {
void param_ptr_to_struct_with_ptr_top(S s[2]) {
int local = 42;
- s[1].p = &local; // no-warning FIXME
+ s[1].p = &local; // expected-warning{{'local' is still referred to by the caller variable 's'}}
}
void param_ptr_to_struct_with_ptr_callee(S s[2]) {
int local = 42;
- s[1].p = &local; // expected-warning{{'local' is still referred to by the stack variable 's'}}
+ s[1].p = &local; // expected-warning{{'local' is still referred to by the caller variable 's'}}
}
void param_ptr_to_struct_with_ptr_caller() {
@@ -724,17 +724,17 @@ NestedAndTransitive global_nat;
void global_nested_and_transitive() {
int local = 42;
- *global_nat.next[2]->next[1]->p = &local; // no-warning FIXME
+ *global_nat.next[2]->next[1]->p = &local; // expected-warning{{'local' is still referred to by the global variable 'global_nat'}}
}
void param_nested_and_transitive_top(NestedAndTransitive* nat) {
int local = 42;
- *nat->next[2]->next[1]->p = &local; // no-warning FIXME
+ *nat->next[2]->next[1]->p = &local; // expected-warning{{'local' is still referred to by the caller variable 'nat'}}
}
void param_nested_and_transitive_callee(NestedAndTransitive* nat) {
int local = 42;
- *nat->next[2]->next[1]->p = &local; // expected-warning{{local variable 'local' is still referred to by the stack variable 'natCaller'}}
+ *nat->next[2]->next[1]->p = &local; // expected-warning{{'local' is still referred to by the caller variable 'natCaller'}}
}
void param_nested_and_transitive_caller(NestedAndTransitive natCaller) {
@@ -757,3 +757,14 @@ class CPtr {
}
};
} // namespace leaking_as_member
+
+namespace leaking_via_indirect_global_invalidated {
+void** global_pp;
+void opaque();
+void global_ptr_to_ptr() {
+ int local = 42;
+ *global_pp = &local;
+ opaque();
+ *global_pp = nullptr;
+}
+} // namespace leaking_via_indirect_global_invalidated
diff --git a/clang/test/Analysis/stack-capture-leak-no-arc.mm b/clang/test/Analysis/stack-capture-leak-no-arc.mm
index 84bbc5fdc5253a..1831e239a42d25 100644
--- a/clang/test/Analysis/stack-capture-leak-no-arc.mm
+++ b/clang/test/Analysis/stack-capture-leak-no-arc.mm
@@ -40,7 +40,7 @@ dispatch_block_t test_block_inside_block_async_leak() {
// called.
void output_block(dispatch_block_t * blk) {
int x = 0;
- *blk = ^{ f(x); }; // expected-warning {{Address of stack-allocated block declared on line 43 is still referred to by the stack variable 'blk' upon returning to the caller. This will be a dangling reference [core.StackAddressEscape]}}
+ *blk = ^{ f(x); }; // expected-warning {{Address of stack-allocated block declared on line 43 is still referred to by the caller variable 'blk' upon returning to the caller. This will be a dangling reference [core.StackAddressEscape]}}
}
// The block literal captures nothing thus is treated as a constant.
@@ -54,7 +54,7 @@ void test_block_leak() {
__block dispatch_block_t blk;
int x = 0;
dispatch_block_t p = ^{
- blk = ^{ // expected-warning {{Address of stack-allocated block declared on line 57 is still referred to by the stack variable 'blk' upon returning to the caller. This will be a dangling reference [core.StackAddressEscape]}}
+ blk = ^{ // expected-warning {{Address of stack-allocated block declared on line 57 is still referred to by the caller variable 'blk' upon returning to the caller. This will be a dangling reference [core.StackAddressEscape]}}
f(x);
};
};
diff --git a/clang/test/Analysis/stackaddrleak.c b/clang/test/Analysis/stackaddrleak.c
index 39c29f2a2635b5..5f508275ba9c8d 100644
--- a/clang/test/Analysis/stackaddrleak.c
+++ b/clang/test/Analysis/stackaddrleak.c
@@ -7,7 +7,7 @@ char const *p;
void f0(void) {
char const str[] = "This will change";
p = str;
-} // expected-warning{{Address of stack memory associated with local variable 'str' is still referred to by the global variable 'p' upon returning to the caller. This will be a dangling reference}}
+} // expected-warning at -1{{Address of stack memory associated with local variable 'str' is still referred to by the global variable 'p' upon returning to the caller. This will be a dangling reference}}
void f1(void) {
char const str[] = "This will change";
@@ -17,7 +17,7 @@ void f1(void) {
void f2(void) {
p = (const char *) __builtin_alloca(12);
-} // expected-warning{{Address of stack memory allocated by call to alloca() on line 19 is still referred to by the global variable 'p' upon returning to the caller. This will be a dangling reference}}
+} // expected-warning at -1{{Address of stack memory allocated by call to alloca() on line 19 is still referred to by the global variable 'p' upon returning to the caller. This will be a dangling reference}}
// PR 7383 - previously the stack address checker would crash on this example
// because it would attempt to do a direct load from 'pr7383_list'.
@@ -33,7 +33,7 @@ void test_multi_return(void) {
int x;
a = &x;
b = &x;
-} // expected-warning{{Address of stack memory associated with local variable 'x' is still referred to by the static variable 'a' upon returning}} expected-warning{{Address of stack memory associated with local variable 'x' is still referred to by the static variable 'b' upon returning}}
+} // expected-warning at -1{{Address of stack memory associated with local variable 'x' is still referred to by the static variable 'a' upon returning}} expected-warning at -1{{Address of stack memory associated with local variable 'x' is still referred to by the static variable 'b' upon returning}}
intptr_t returnAsNonLoc(void) {
int x;
@@ -49,7 +49,7 @@ void assignAsNonLoc(void) {
extern intptr_t ip;
int x;
ip = (intptr_t)&x;
-} // expected-warning{{Address of stack memory associated with local variable 'x' is still referred to by the global variable 'ip' upon returning}}
+} // expected-warning at -1{{Address of stack memory associated with local variable 'x' is still referred to by the global variable 'ip' upon returning}}
void assignAsBool(void) {
extern bool b;
More information about the cfe-commits
mailing list