// RUN: %clang_analyze_cc1 -verify -analyzer-output=text %s \ // RUN: -analyzer-checker=core \ // RUN: -analyzer-checker=cplusplus \ // RUN: -analyzer-checker=unix \ // RUN: -analyzer-config \ // RUN: unix.DynamicMemoryModeling:AddNoOwnershipChangeNotes=false // RUN: %clang_analyze_cc1 -verify=expected,ownership -analyzer-output=text %s \ // RUN: -analyzer-checker=core \ // RUN: -analyzer-checker=cplusplus \ // RUN: -analyzer-checker=unix \ // RUN: -analyzer-config \ // RUN: unix.DynamicMemoryModeling:AddNoOwnershipChangeNotes=true #include "Inputs/system-header-simulator-for-malloc.h" //===----------------------------------------------------------------------===// // Report for which we expect NoOwnershipChangeVisitor to add a new note. //===----------------------------------------------------------------------===// bool coin(); // TODO: AST analysis of sink would reveal that it doesn't intent to free the // allocated memory, but in this instance, its also the only function with // the ability to do so, we should see a note here. namespace memory_allocated_in_fn_call { void sink(int *P) { } void foo() { sink(new int(5)); // expected-note {{Memory is allocated}} } // expected-warning {{Potential memory leak [cplusplus.NewDeleteLeaks]}} // expected-note@-1 {{Potential memory leak}} } // namespace memory_allocated_in_fn_call // Realize that sink() intends to deallocate memory, assume that it should've // taken care of the leaked object as well. namespace memory_passed_to_fn_call_delete { void sink(int *P) { if (coin()) // ownership-note {{Assuming the condition is false}} // ownership-note@-1 {{Taking false branch}} delete P; } // ownership-note {{Returning without deallocating memory or storing the pointer for later deallocation}} void foo() { int *ptr = new int(5); // expected-note {{Memory is allocated}} sink(ptr); // ownership-note {{Calling 'sink'}} // ownership-note@-1 {{Returning from 'sink'}} } // expected-warning {{Potential leak of memory pointed to by 'ptr' [cplusplus.NewDeleteLeaks]}} // expected-note@-1 {{Potential leak}} } // namespace memory_passed_to_fn_call_delete namespace memory_passed_to_fn_call_free { void sink(int *P) { if (coin()) // ownership-note {{Assuming the condition is false}} // ownership-note@-1 {{Taking false branch}} free(P); } // ownership-note {{Returning without deallocating memory or storing the pointer for later deallocation}} void foo() { int *ptr = (int *)malloc(sizeof(int)); // expected-note {{Memory is allocated}} sink(ptr); // ownership-note {{Calling 'sink'}} // ownership-note@-1 {{Returning from 'sink'}} } // expected-warning {{Potential leak of memory pointed to by 'ptr' [unix.Malloc]}} // expected-note@-1 {{Potential leak}} } // namespace memory_passed_to_fn_call_free // Function pointers cannot be resolved syntactically. namespace memory_passed_to_fn_call_free_through_fn_ptr { void (*freeFn)(void *) = free; void sink(int *P) { if (coin()) freeFn(P); } void foo() { int *ptr = (int *)malloc(sizeof(int)); // expected-note {{Memory is allocated}} sink(ptr); } // expected-warning {{Potential leak of memory pointed to by 'ptr' [unix.Malloc]}} // expected-note@-1 {{Potential leak}} } // namespace memory_passed_to_fn_call_free_through_fn_ptr namespace memory_shared_with_ptr_of_shorter_lifetime { void sink(int *P) { int *Q = P; if (coin()) // ownership-note {{Assuming the condition is false}} // ownership-note@-1 {{Taking false branch}} delete P; (void)Q; } // ownership-note {{Returning without deallocating memory or storing the pointer for later deallocation}} void foo() { int *ptr = new int(5); // expected-note {{Memory is allocated}} sink(ptr); // ownership-note {{Calling 'sink'}} // ownership-note@-1 {{Returning from 'sink'}} } // expected-warning {{Potential leak of memory pointed to by 'ptr' [cplusplus.NewDeleteLeaks]}} // expected-note@-1 {{Potential leak}} } // namespace memory_shared_with_ptr_of_shorter_lifetime //===----------------------------------------------------------------------===// // Report for which we *do not* expect NoOwnershipChangeVisitor add a new note, // nor do we want it to. //===----------------------------------------------------------------------===// namespace memory_not_passed_to_fn_call { void sink(int *P) { if (coin()) delete P; } void foo() { int *ptr = new int(5); // expected-note {{Memory is allocated}} int *q = nullptr; sink(q); (void)ptr; } // expected-warning {{Potential leak of memory pointed to by 'ptr' [cplusplus.NewDeleteLeaks]}} // expected-note@-1 {{Potential leak}} } // namespace memory_not_passed_to_fn_call namespace memory_shared_with_ptr_of_same_lifetime { void sink(int *P, int **Q) { // NOTE: Not a job of NoOwnershipChangeVisitor, but maybe this could be // highlighted still? *Q = P; } void foo() { int *ptr = new int(5); // expected-note {{Memory is allocated}} int *q = nullptr; sink(ptr, &q); } // expected-warning {{Potential leak of memory pointed to by 'q' [cplusplus.NewDeleteLeaks]}} // expected-note@-1 {{Potential leak}} } // namespace memory_shared_with_ptr_of_same_lifetime namespace memory_passed_into_fn_that_doesnt_intend_to_free { void sink(int *P) { } void foo() { int *ptr = new int(5); // expected-note {{Memory is allocated}} sink(ptr); } // expected-warning {{Potential leak of memory pointed to by 'ptr' [cplusplus.NewDeleteLeaks]}} // expected-note@-1 {{Potential leak}} } // namespace memory_passed_into_fn_that_doesnt_intend_to_free namespace memory_passed_into_fn_that_doesnt_intend_to_free2 { void bar(); void sink(int *P) { // Correctly realize that calling bar() doesn't mean that this function would // like to deallocate anything. bar(); } void foo() { int *ptr = new int(5); // expected-note {{Memory is allocated}} sink(ptr); } // expected-warning {{Potential leak of memory pointed to by 'ptr' [cplusplus.NewDeleteLeaks]}} // expected-note@-1 {{Potential leak}} } // namespace memory_passed_into_fn_that_doesnt_intend_to_free2 namespace refkind_from_unoallocated_to_allocated { // RefKind of the symbol changed from nothing to Allocated. We don't want to // emit notes when the RefKind changes in the stack frame. static char *malloc_wrapper_ret() { return (char *)malloc(12); // expected-note {{Memory is allocated}} } void use_ret() { char *v; v = malloc_wrapper_ret(); // expected-note {{Calling 'malloc_wrapper_ret'}} // expected-note@-1 {{Returned allocated memory}} } // expected-warning {{Potential leak of memory pointed to by 'v' [unix.Malloc]}} // expected-note@-1 {{Potential leak of memory pointed to by 'v'}} } // namespace refkind_from_unoallocated_to_allocated // Check that memory leak is reported against a symbol if the last place it's // mentioned is a base region of a lazy compound value, as the program cannot // possibly free that memory. namespace symbol_reaper_lifetime { struct Nested { int buf[2]; }; struct Wrapping { Nested data; }; Nested allocateWrappingAndReturnNested() { // expected-note@+1 {{Memory is allocated}} Wrapping const* p = new Wrapping(); // expected-warning@+2 {{Potential leak of memory pointed to by 'p'}} // expected-note@+1 {{Potential leak of memory pointed to by 'p'}} return p->data; } void caller() { // expected-note@+1 {{Calling 'allocateWrappingAndReturnNested'}} Nested n = allocateWrappingAndReturnNested(); (void)n; } // no-warning: No potential memory leak here, because that's been already reported. } // namespace symbol_reaper_lifetime