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 28 void sink(int *P) { 29 } 30 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 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 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 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 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 78 void sink(int *P) { 79 if (coin()) 80 freeFn(P); 81 } 82 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 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 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 117 void sink(int *P) { 118 if (coin()) 119 delete P; 120 } 121 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 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 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 151 void sink(int *P) { 152 } 153 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 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 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. 184 static char *malloc_wrapper_ret() { 185 return (char *)malloc(12); // expected-note {{Memory is allocated}} 186 } 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