xref: /llvm-project/clang/test/Analysis/NewDeleteLeaks.cpp (revision 73716baa30ebc75783d2902e12accea35dec193a)
1 // RUN: %clang_analyze_cc1 -verify -analyzer-output=text %s \
2 // RUN:   -analyzer-checker=core \
3 // RUN:   -analyzer-checker=cplusplus \
4 // RUN:   -analyzer-checker=unix \
5 // RUN:   -analyzer-config \
6 // RUN:     unix.DynamicMemoryModeling:AddNoOwnershipChangeNotes=false
7 
8 // RUN: %clang_analyze_cc1 -verify=expected,ownership -analyzer-output=text %s \
9 // RUN:   -analyzer-checker=core \
10 // RUN:   -analyzer-checker=cplusplus \
11 // RUN:   -analyzer-checker=unix \
12 // RUN:   -analyzer-config \
13 // RUN:     unix.DynamicMemoryModeling:AddNoOwnershipChangeNotes=true
14 
15 #include "Inputs/system-header-simulator-for-malloc.h"
16 
17 //===----------------------------------------------------------------------===//
18 // Report for which we expect NoOwnershipChangeVisitor to add a new note.
19 //===----------------------------------------------------------------------===//
20 
21 bool coin();
22 
23 // TODO: AST analysis of sink would reveal that it doesn't intent to free the
24 // allocated memory, but in this instance, its also the only function with
25 // the ability to do so, we should see a note here.
26 namespace memory_allocated_in_fn_call {
27 
sink(int * P)28 void sink(int *P) {
29 }
30 
foo()31 void foo() {
32   sink(new int(5)); // expected-note {{Memory is allocated}}
33 } // expected-warning {{Potential memory leak [cplusplus.NewDeleteLeaks]}}
34 // expected-note@-1 {{Potential memory leak}}
35 
36 } // namespace memory_allocated_in_fn_call
37 
38 // Realize that sink() intends to deallocate memory, assume that it should've
39 // taken care of the leaked object as well.
40 namespace memory_passed_to_fn_call_delete {
41 
sink(int * P)42 void sink(int *P) {
43   if (coin()) // ownership-note {{Assuming the condition is false}}
44               // ownership-note@-1 {{Taking false branch}}
45     delete P;
46 } // ownership-note {{Returning without deallocating memory or storing the pointer for later deallocation}}
47 
foo()48 void foo() {
49   int *ptr = new int(5); // expected-note {{Memory is allocated}}
50   sink(ptr);             // ownership-note {{Calling 'sink'}}
51                          // ownership-note@-1 {{Returning from 'sink'}}
52 } // expected-warning {{Potential leak of memory pointed to by 'ptr' [cplusplus.NewDeleteLeaks]}}
53 // expected-note@-1 {{Potential leak}}
54 
55 } // namespace memory_passed_to_fn_call_delete
56 
57 namespace memory_passed_to_fn_call_free {
58 
sink(int * P)59 void sink(int *P) {
60   if (coin()) // ownership-note {{Assuming the condition is false}}
61               // ownership-note@-1 {{Taking false branch}}
62     free(P);
63 } // ownership-note {{Returning without deallocating memory or storing the pointer for later deallocation}}
64 
foo()65 void foo() {
66   int *ptr = (int *)malloc(sizeof(int)); // expected-note {{Memory is allocated}}
67   sink(ptr);                             // ownership-note {{Calling 'sink'}}
68                                          // ownership-note@-1 {{Returning from 'sink'}}
69 } // expected-warning {{Potential leak of memory pointed to by 'ptr' [unix.Malloc]}}
70 // expected-note@-1 {{Potential leak}}
71 
72 } // namespace memory_passed_to_fn_call_free
73 
74 // Function pointers cannot be resolved syntactically.
75 namespace memory_passed_to_fn_call_free_through_fn_ptr {
76 void (*freeFn)(void *) = free;
77 
sink(int * P)78 void sink(int *P) {
79   if (coin())
80     freeFn(P);
81 }
82 
foo()83 void foo() {
84   int *ptr = (int *)malloc(sizeof(int)); // expected-note {{Memory is allocated}}
85   sink(ptr);
86 } // expected-warning {{Potential leak of memory pointed to by 'ptr' [unix.Malloc]}}
87 // expected-note@-1 {{Potential leak}}
88 
89 } // namespace memory_passed_to_fn_call_free_through_fn_ptr
90 
91 namespace memory_shared_with_ptr_of_shorter_lifetime {
92 
sink(int * P)93 void sink(int *P) {
94   int *Q = P;
95   if (coin()) // ownership-note {{Assuming the condition is false}}
96               // ownership-note@-1 {{Taking false branch}}
97     delete P;
98   (void)Q;
99 } // ownership-note {{Returning without deallocating memory or storing the pointer for later deallocation}}
100 
foo()101 void foo() {
102   int *ptr = new int(5); // expected-note {{Memory is allocated}}
103   sink(ptr);             // ownership-note {{Calling 'sink'}}
104                          // ownership-note@-1 {{Returning from 'sink'}}
105 } // expected-warning {{Potential leak of memory pointed to by 'ptr' [cplusplus.NewDeleteLeaks]}}
106 // expected-note@-1 {{Potential leak}}
107 
108 } // namespace memory_shared_with_ptr_of_shorter_lifetime
109 
110 //===----------------------------------------------------------------------===//
111 // Report for which we *do not* expect NoOwnershipChangeVisitor add a new note,
112 // nor do we want it to.
113 //===----------------------------------------------------------------------===//
114 
115 namespace memory_not_passed_to_fn_call {
116 
sink(int * P)117 void sink(int *P) {
118   if (coin())
119     delete P;
120 }
121 
foo()122 void foo() {
123   int *ptr = new int(5); // expected-note {{Memory is allocated}}
124   int *q = nullptr;
125   sink(q);
126   (void)ptr;
127 } // expected-warning {{Potential leak of memory pointed to by 'ptr' [cplusplus.NewDeleteLeaks]}}
128 // expected-note@-1 {{Potential leak}}
129 
130 } // namespace memory_not_passed_to_fn_call
131 
132 namespace memory_shared_with_ptr_of_same_lifetime {
133 
sink(int * P,int ** Q)134 void sink(int *P, int **Q) {
135   // NOTE: Not a job of NoOwnershipChangeVisitor, but maybe this could be
136   // highlighted still?
137   *Q = P;
138 }
139 
foo()140 void foo() {
141   int *ptr = new int(5); // expected-note {{Memory is allocated}}
142   int *q = nullptr;
143   sink(ptr, &q);
144 } // expected-warning {{Potential leak of memory pointed to by 'q' [cplusplus.NewDeleteLeaks]}}
145 // expected-note@-1 {{Potential leak}}
146 
147 } // namespace memory_shared_with_ptr_of_same_lifetime
148 
149 namespace memory_passed_into_fn_that_doesnt_intend_to_free {
150 
sink(int * P)151 void sink(int *P) {
152 }
153 
foo()154 void foo() {
155   int *ptr = new int(5); // expected-note {{Memory is allocated}}
156   sink(ptr);
157 } // expected-warning {{Potential leak of memory pointed to by 'ptr' [cplusplus.NewDeleteLeaks]}}
158 // expected-note@-1 {{Potential leak}}
159 
160 } // namespace memory_passed_into_fn_that_doesnt_intend_to_free
161 
162 namespace memory_passed_into_fn_that_doesnt_intend_to_free2 {
163 
164 void bar();
165 
sink(int * P)166 void sink(int *P) {
167   // Correctly realize that calling bar() doesn't mean that this function would
168   // like to deallocate anything.
169   bar();
170 }
171 
foo()172 void foo() {
173   int *ptr = new int(5); // expected-note {{Memory is allocated}}
174   sink(ptr);
175 } // expected-warning {{Potential leak of memory pointed to by 'ptr' [cplusplus.NewDeleteLeaks]}}
176 // expected-note@-1 {{Potential leak}}
177 
178 } // namespace memory_passed_into_fn_that_doesnt_intend_to_free2
179 
180 namespace refkind_from_unoallocated_to_allocated {
181 
182 // RefKind of the symbol changed from nothing to Allocated. We don't want to
183 // emit notes when the RefKind changes in the stack frame.
malloc_wrapper_ret()184 static char *malloc_wrapper_ret() {
185   return (char *)malloc(12); // expected-note {{Memory is allocated}}
186 }
use_ret()187 void use_ret() {
188   char *v;
189   v = malloc_wrapper_ret(); // expected-note {{Calling 'malloc_wrapper_ret'}}
190                             // expected-note@-1 {{Returned allocated memory}}
191 } // expected-warning {{Potential leak of memory pointed to by 'v' [unix.Malloc]}}
192 // expected-note@-1 {{Potential leak of memory pointed to by 'v'}}
193 
194 } // namespace refkind_from_unoallocated_to_allocated
195 
196 // Check that memory leak is reported against a symbol if the last place it's
197 // mentioned is a base region of a lazy compound value, as the program cannot
198 // possibly free that memory.
199 namespace symbol_reaper_lifetime {
200 struct Nested {
201   int buf[2];
202 };
203 struct Wrapping {
204   Nested data;
205 };
206 
allocateWrappingAndReturnNested()207 Nested allocateWrappingAndReturnNested() {
208   // expected-note@+1 {{Memory is allocated}}
209   Wrapping const* p = new Wrapping();
210   // expected-warning@+2 {{Potential leak of memory pointed to by 'p'}}
211   // expected-note@+1    {{Potential leak of memory pointed to by 'p'}}
212   return p->data;
213 }
214 
caller()215 void caller() {
216   // expected-note@+1 {{Calling 'allocateWrappingAndReturnNested'}}
217   Nested n = allocateWrappingAndReturnNested();
218   (void)n;
219 } // no-warning: No potential memory leak here, because that's been already reported.
220 } // namespace symbol_reaper_lifetime
221