| // 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" | |
| // For the tests in namespace protobuf_leak: | |
| #include "Inputs/system-header-simulator-for-protobuf.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 | |
| // Check that we do not report false positives in automatically generated | |
| // protobuf code that passes dynamically allocated memory to a certain function | |
| // named GetOwnedMessageInternal. | |
| namespace protobuf_leak { | |
| Arena *some_arena, *some_submessage_arena; | |
| MessageLite *protobuf_leak() { | |
| MessageLite *p = new MessageLite(); // Real protobuf code instantiates a | |
| // subclass of MessageLite, but that's | |
| // not relevant for the bug. | |
| MessageLite *q = GetOwnedMessageInternal(some_arena, p, some_submessage_arena); | |
| return q; | |
| // No leak at end of function -- the pointer escapes in GetOwnedMessageInternal. | |
| } | |
| void validate_system_header() { | |
| // The case protobuf_leak would also pass if GetOwnedMessageInternal wasn't | |
| // declared in a system header. This test verifies that another function | |
| // declared in the same header behaves differently (doesn't escape memory) to | |
| // demonstrate that GetOwnedMessageInternal is indeed explicitly recognized | |
| // by the analyzer. | |
| // expected-note@+1 {{Memory is allocated}} | |
| MessageLite *p = new MessageLite(); | |
| SomeOtherFunction(p); | |
| // expected-warning@+2 {{Potential leak of memory pointed to by 'p'}} | |
| // expected-note@+1 {{Potential leak of memory pointed to by 'p'}} | |
| } | |
| } // namespace protobuf_leak |