Skip to content

Commit aa4f81e

Browse files
authored
[analyzer] Fix false positive for stack-addr leak on simple param ptr (#107003)
Assigning to a pointer parameter does not leak the stack address because it stays within the function and is not shared with the caller. Previous implementation reported any association of a pointer parameter with a local address, which is too broad. This fix enforces that the pointer to a stack variable is related by at least one level of indirection. CPP-5642 Fixes #106834
1 parent 0748f42 commit aa4f81e

File tree

2 files changed

+58
-1
lines changed

2 files changed

+58
-1
lines changed

clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,8 @@ void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS,
420420
return true;
421421
}
422422
if (isa<StackArgumentsSpaceRegion>(ReferrerMemSpace) &&
423+
// Not a simple ptr (int*) but something deeper, e.g. int**
424+
isa<SymbolicRegion>(Referrer->getBaseRegion()) &&
423425
ReferrerStackSpace->getStackFrame() == PoppedFrame && TopFrame) {
424426
// Output parameter of a top-level function
425427
V.emplace_back(Referrer, Referred);

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

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ namespace rdar13296133 {
137137
ConvertsToPointer obj;
138138
return obj; // no-warning
139139
}
140-
}
140+
} // namespace rdar13296133
141141

142142
void write_stack_address_to(char **q) {
143143
char local;
@@ -791,3 +791,58 @@ void global_ptr_to_ptr() {
791791
*global_pp = nullptr;
792792
}
793793
} // namespace leaking_via_indirect_global_invalidated
794+
795+
namespace not_leaking_via_simple_ptr {
796+
void simple_ptr(const char *p) {
797+
char tmp;
798+
p = &tmp; // no-warning
799+
}
800+
801+
void ref_ptr(const char *&p) {
802+
char tmp;
803+
p = &tmp; // expected-warning{{variable 'tmp' is still referred to by the caller variable 'p'}}
804+
}
805+
806+
struct S {
807+
const char *p;
808+
};
809+
810+
void struct_ptr(S s) {
811+
char tmp;
812+
s.p = &tmp; // no-warning
813+
}
814+
815+
void array(const char arr[2]) {
816+
char tmp;
817+
arr = &tmp; // no-warning
818+
}
819+
820+
extern void copy(char *output, const char *input, unsigned size);
821+
extern bool foo(const char *input);
822+
extern void bar(char *output, unsigned count);
823+
extern bool baz(char *output, const char *input);
824+
825+
void repo(const char *input, char *output) {
826+
char temp[64];
827+
copy(temp, input, sizeof(temp));
828+
829+
char result[64];
830+
input = temp;
831+
if (foo(temp)) {
832+
bar(result, sizeof(result));
833+
input = result;
834+
}
835+
if (!baz(output, input)) {
836+
copy(output, input, sizeof(result));
837+
}
838+
}
839+
} // namespace not_leaking_via_simple_ptr
840+
841+
namespace early_reclaim_dead_limitation {
842+
void foo();
843+
void top(char **p) {
844+
char local;
845+
*p = &local;
846+
foo(); // no-warning FIXME: p binding is reclaimed before the function end
847+
}
848+
} // namespace early_reclaim_dead_limitation

0 commit comments

Comments
 (0)