| // RUN: %clang_analyze_cc1 \ |
| // RUN: -analyzer-checker=core,debug.ExprInspection \ |
| // RUN: -verify %s \ |
| // RUN: -Wno-undefined-bool-conversion |
| // RUN: %clang_analyze_cc1 \ |
| // RUN: -analyzer-checker=core,debug.ExprInspection,unix.Malloc \ |
| // RUN: -verify %s \ |
| // RUN: -Wno-undefined-bool-conversion |
| // unix.Malloc is necessary to model __builtin_alloca, |
| // which could trigger an "unexpected region" bug in StackAddrEscapeChecker. |
| |
| typedef __INTPTR_TYPE__ intptr_t; |
| |
| template <typename T> |
| void clang_analyzer_dump(T x); |
| |
| using size_t = decltype(sizeof(int)); |
| void * malloc(size_t size); |
| void free(void*); |
| |
| const int& g() { |
| int s; |
| return s; // expected-warning{{Address of stack memory associated with local variable 's' returned}} expected-warning{{reference to stack memory associated with local variable 's' returned}} |
| } |
| |
| const int& g2() { |
| int s1; |
| int &s2 = s1; // expected-note {{binding reference variable 's2' here}} |
| return s2; // expected-warning{{Address of stack memory associated with local variable 's1' returned}} expected-warning {{reference to stack memory associated with local variable 's1' returned}} |
| } |
| |
| const int& g3() { |
| int s1; |
| int &s2 = s1; // expected-note {{binding reference variable 's2' here}} |
| int &s3 = s2; // expected-note {{binding reference variable 's3' here}} |
| return s3; // expected-warning{{Address of stack memory associated with local variable 's1' returned}} expected-warning {{reference to stack memory associated with local variable 's1' returned}} |
| } |
| |
| void g4() { |
| static const int &x = 3; // no warning |
| } |
| |
| int get_value(); |
| |
| const int &get_reference1() { return get_value(); } // expected-warning{{Address of stack memory associated with temporary object of type 'int' returned}} expected-warning {{returning reference to local temporary}} |
| |
| const int &get_reference2() { |
| const int &x = get_value(); // expected-note {{binding reference variable 'x' here}} |
| return x; // expected-warning{{Address of stack memory associated with temporary object of type 'int' lifetime extended by local variable 'x' returned to caller}} expected-warning {{returning reference to local temporary}} |
| } |
| |
| const int &get_reference3() { |
| const int &x1 = get_value(); // expected-note {{binding reference variable 'x1' here}} |
| const int &x2 = x1; // expected-note {{binding reference variable 'x2' here}} |
| return x2; // expected-warning{{Address of stack memory associated with temporary object of type 'int' lifetime extended by local variable 'x1' returned to caller}} expected-warning {{returning reference to local temporary}} |
| } |
| |
| int global_var; |
| int *f1() { |
| int &y = global_var; |
| return &y; |
| } |
| |
| int *f2() { |
| int x1; |
| int &x2 = x1; // expected-note {{binding reference variable 'x2' here}} |
| return &x2; // expected-warning{{Address of stack memory associated with local variable 'x1' returned}} expected-warning {{address of stack memory associated with local variable 'x1' returned}} |
| } |
| |
| int *f3() { |
| int x1; |
| int *const &x2 = &x1; // expected-note {{binding reference variable 'x2' here}} |
| return x2; // expected-warning {{address of stack memory associated with local variable 'x1' returned}} expected-warning {{Address of stack memory associated with local variable 'x1' returned to caller}} |
| } |
| |
| const int *f4() { |
| const int &x1 = get_value(); // expected-note {{binding reference variable 'x1' here}} |
| const int &x2 = x1; // expected-note {{binding reference variable 'x2' here}} |
| return &x2; // expected-warning{{Address of stack memory associated with temporary object of type 'int' lifetime extended by local variable 'x1' returned to caller}} expected-warning {{returning address of local temporary}} |
| } |
| |
| struct S { |
| int x; |
| }; |
| |
| int *mf() { |
| S s1; |
| S &s2 = s1; // expected-note {{binding reference variable 's2' here}} |
| int &x = s2.x; // expected-note {{binding reference variable 'x' here}} |
| return &x; // expected-warning{{Address of stack memory associated with local variable 's1' returned}} expected-warning {{address of stack memory associated with local variable 's1' returned}} |
| } |
| |
| void *lf() { |
| label: |
| void *const &x = &&label; // expected-note {{binding reference variable 'x' here}} |
| return x; // expected-warning {{returning address of label, which is local}} |
| } |
| |
| template <typename T> |
| struct TS { |
| int *get(); |
| int *m() { |
| int *&x = get(); |
| return x; |
| } |
| }; |
| |
| int* f5() { |
| int& i = i; // expected-warning {{Assigned value is garbage or undefined}} expected-warning{{reference 'i' is not yet bound to a value when used within its own initialization}} |
| return &i; |
| } |
| |
| void *radar13226577() { |
| void *p = &p; |
| return p; // expected-warning {{stack memory associated with local variable 'p' returned to caller}} |
| } |
| |
| namespace rdar13296133 { |
| class ConvertsToBool { |
| public: |
| operator bool() const { return this; } |
| }; |
| |
| class ConvertsToIntptr { |
| public: |
| operator intptr_t() const { return reinterpret_cast<intptr_t>(this); } |
| }; |
| |
| class ConvertsToPointer { |
| public: |
| operator const void *() const { return this; } |
| }; |
| |
| intptr_t returnAsNonLoc() { |
| ConvertsToIntptr obj; |
| return obj; // expected-warning{{Address of stack memory associated with local variable 'obj' returned to caller}} |
| } |
| |
| bool returnAsBool() { |
| ConvertsToBool obj; |
| return obj; // no-warning |
| } |
| |
| intptr_t returnAsNonLocViaPointer() { |
| ConvertsToPointer obj; |
| return reinterpret_cast<intptr_t>(static_cast<const void *>(obj)); // expected-warning{{Address of stack memory associated with local variable 'obj' returned to caller}} |
| } |
| |
| bool returnAsBoolViaPointer() { |
| ConvertsToPointer obj; |
| return obj; // no-warning |
| } |
| } // namespace rdar13296133 |
| |
| void write_stack_address_to(char **q) { |
| char local; |
| *q = &local; |
| // expected-warning@-1 {{Address of stack memory associated with local \ |
| variable 'local' is still referred to by the caller variable 'p' upon \ |
| returning to the caller}} |
| } |
| |
| void test_stack() { |
| char *p; |
| write_stack_address_to(&p); |
| } |
| |
| struct C { |
| ~C() {} // non-trivial class |
| }; |
| |
| C make1() { |
| C c; |
| return c; // no-warning |
| } |
| |
| 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 caller 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 caller 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 caller 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; // 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; // expected-warning{{local variable 'local' is still referred to by the global variable 'global_ppp'}} |
| } |
| |
| 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; // 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 caller 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; // 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 caller variable 'pp'}} |
| } |
| |
| 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; // expected-warning{{local variable 'local' is still referred to by the caller variable 'pp'}} |
| }; |
| 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 caller 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; // expected-warning{{local variable 'local' is still referred to by the global variable 'global_rtp'}} |
| } |
| |
| 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; // 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 caller 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; // 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 caller 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; // 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 caller 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 caller 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; // expected-warning{{'local' is still referred to by the global variable 'global_s'}} |
| } |
| |
| 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; // 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 caller 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 caller 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; // 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; |
| 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; // 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 caller 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; // 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 caller 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; // 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; // 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' is still referred to by the caller variable 'natCaller'}} |
| } |
| |
| 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@-1 {{binding reference member 'ref' to stack allocated parameter 'x'}} |
| }; |
| |
| class CPtr { |
| int* ptr; |
| void memFun(int x) { |
| ptr = &x; |
| } |
| }; |
| } // namespace leaking_as_member |
| |
| namespace origin_region_limitation { |
| void leaker(int ***leakerArg) { |
| int local; |
| clang_analyzer_dump(*leakerArg); // expected-warning{{&SymRegion{reg_$0<int ** arg>}}} |
| // Incorrect message: 'arg', after it is reinitialized with value returned by 'tweak' |
| // is no longer relevant. |
| // The message must refer to 'original_arg' instead, but there is no easy way to |
| // connect the SymRegion stored in 'original_arg' and 'original_arg' as variable. |
| **leakerArg = &local; // expected-warning{{ 'local' is still referred to by the caller variable 'arg'}} |
| } |
| |
| int **tweak(); |
| |
| void foo(int **arg) { |
| int **original_arg = arg; |
| arg = tweak(); |
| leaker(&original_arg); |
| } |
| } // namespace origin_region_limitation |
| |
| 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 |
| |
| namespace not_leaking_via_simple_ptr { |
| void simple_ptr(const char *p) { |
| char tmp; |
| p = &tmp; // no-warning |
| } |
| |
| void ref_ptr(const char *&p) { |
| char tmp; |
| p = &tmp; // expected-warning{{variable 'tmp' is still referred to by the caller variable 'p'}} |
| } |
| |
| struct S { |
| const char *p; |
| }; |
| |
| void struct_ptr(S s) { |
| char tmp; |
| s.p = &tmp; // no-warning |
| } |
| |
| void array(const char arr[2]) { |
| char tmp; |
| arr = &tmp; // no-warning |
| } |
| |
| extern void copy(char *output, const char *input, unsigned size); |
| extern bool foo(const char *input); |
| extern void bar(char *output, unsigned count); |
| extern bool baz(char *output, const char *input); |
| |
| void repo(const char *input, char *output) { |
| char temp[64]; |
| copy(temp, input, sizeof(temp)); |
| |
| char result[64]; |
| input = temp; |
| if (foo(temp)) { |
| bar(result, sizeof(result)); |
| input = result; |
| } |
| if (!baz(output, input)) { |
| copy(output, input, sizeof(result)); |
| } |
| } |
| } // namespace not_leaking_via_simple_ptr |
| |
| namespace early_reclaim_dead_limitation { |
| void foo(); |
| void top(char **p) { |
| char local; |
| *p = &local; |
| foo(); // no-warning FIXME: p binding is reclaimed before the function end |
| } |
| } // namespace early_reclaim_dead_limitation |
| |
| namespace alloca_region_pointer { |
| void callee(char **pptr) { |
| char local; |
| *pptr = &local; |
| } // no crash |
| |
| void top_alloca_no_crash_fn() { |
| char **pptr = (char**)__builtin_alloca(sizeof(char*)); |
| callee(pptr); |
| } |
| |
| void top_malloc_no_crash_fn() { |
| char **pptr = (char**)malloc(sizeof(char*)); |
| callee(pptr); |
| free(pptr); |
| } |
| } // namespace alloca_region_pointer |