Skip to content

Commit 9f6b7b4

Browse files
authored
[analyzer] StackAddrEscapeChecker: also check return for child stack frames (#126986)
Fixes #123459. This changes checking of the returned expr to also look for memory regions whose stack frame context was a child of the current stack frame context, e.g., for cases like this given in #123459: ``` struct S { int *p; }; S f() { S s; { int a = 1; s.p = &a; } return s; } ```
1 parent b55f751 commit 9f6b7b4

File tree

3 files changed

+74
-1
lines changed

3 files changed

+74
-1
lines changed

clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,13 @@ class FindStackRegionsSymbolVisitor final : public SymbolVisitor {
274274
void SaveIfEscapes(const MemRegion *MR) {
275275
const StackSpaceRegion *SSR =
276276
MR->getMemorySpace()->getAs<StackSpaceRegion>();
277-
if (SSR && SSR->getStackFrame() == PoppedStackFrame)
277+
278+
if (!SSR)
279+
return;
280+
281+
const StackFrameContext *CapturedSFC = SSR->getStackFrame();
282+
if (CapturedSFC == PoppedStackFrame ||
283+
PoppedStackFrame->isParentOf(CapturedSFC))
278284
EscapingStackRegions.push_back(MR);
279285
}
280286

clang/test/Analysis/stack-addr-ps.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -982,6 +982,51 @@ int& ret_local_field_ref() {
982982
}
983983
} //namespace return_address_of_true_positives
984984

985+
namespace return_from_child_block_scope {
986+
struct S {
987+
int *p;
988+
};
989+
990+
S return_child_stack_context() {
991+
S s;
992+
{
993+
int a = 1;
994+
s = (S){ &a };
995+
}
996+
return s; // expected-warning {{Address of stack memory associated with local variable 'a' returned to caller}}
997+
}
998+
999+
S return_child_stack_context_field() {
1000+
S s;
1001+
{
1002+
int a = 1;
1003+
s.p = &a;
1004+
}
1005+
return s; // expected-warning {{Address of stack memory associated with local variable 'a' returned to caller}}
1006+
}
1007+
1008+
// The below are reproducers from Issue #123459
1009+
template <typename V>
1010+
struct T {
1011+
V* q{};
1012+
T() = default;
1013+
T(T&& rhs) { q = rhs.q; rhs.q = nullptr;}
1014+
T& operator=(T&& rhs) { q = rhs.q; rhs.q = nullptr;}
1015+
void push_back(const V& v) { if (q == nullptr) q = new V(v); }
1016+
~T() { delete q; }
1017+
};
1018+
1019+
T<S> f() {
1020+
T<S> t;
1021+
{
1022+
int a = 1;
1023+
t.push_back({ &a });
1024+
}
1025+
return t; // expected-warning {{Address of stack memory associated with local variable 'a' returned to caller}}
1026+
}
1027+
1028+
} // namespace return_from_child_block_scope
1029+
9851030
namespace true_negatives_return_expressions {
9861031
struct Container { int *x; };
9871032

clang/test/Analysis/stackaddrleak.c

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,25 @@ int *g_no_lifetime_bound() {
6868
int i = 0;
6969
return f_no_lifetime_bound(&i); // no-warning
7070
}
71+
72+
struct child_stack_context_s {
73+
int *p;
74+
};
75+
76+
struct child_stack_context_s return_child_stack_context() {
77+
struct child_stack_context_s s;
78+
{
79+
int a = 1;
80+
s = (struct child_stack_context_s){ &a };
81+
}
82+
return s; // expected-warning {{Address of stack memory associated with local variable 'a' returned to caller}}
83+
}
84+
85+
struct child_stack_context_s return_child_stack_context_field() {
86+
struct child_stack_context_s s;
87+
{
88+
int a = 1;
89+
s.p = &a;
90+
}
91+
return s; // expected-warning {{Address of stack memory associated with local variable 'a' returned to caller}}
92+
}

0 commit comments

Comments
 (0)