254 lines · cpp
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=false7 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=true14 15#include "Inputs/system-header-simulator-for-malloc.h"16// For the tests in namespace protobuf_leak:17#include "Inputs/system-header-simulator-for-protobuf.h"18 19//===----------------------------------------------------------------------===//20// Report for which we expect NoOwnershipChangeVisitor to add a new note.21//===----------------------------------------------------------------------===//22 23bool coin();24 25// TODO: AST analysis of sink would reveal that it doesn't intent to free the26// allocated memory, but in this instance, its also the only function with27// the ability to do so, we should see a note here.28namespace memory_allocated_in_fn_call {29 30void sink(int *P) {31}32 33void foo() {34 sink(new int(5)); // expected-note {{Memory is allocated}}35} // expected-warning {{Potential memory leak [cplusplus.NewDeleteLeaks]}}36// expected-note@-1 {{Potential memory leak}}37 38} // namespace memory_allocated_in_fn_call39 40// Realize that sink() intends to deallocate memory, assume that it should've41// taken care of the leaked object as well.42namespace memory_passed_to_fn_call_delete {43 44void sink(int *P) {45 if (coin()) // ownership-note {{Assuming the condition is false}}46 // ownership-note@-1 {{Taking false branch}}47 delete P;48} // ownership-note {{Returning without deallocating memory or storing the pointer for later deallocation}}49 50void foo() {51 int *ptr = new int(5); // expected-note {{Memory is allocated}}52 sink(ptr); // ownership-note {{Calling 'sink'}}53 // ownership-note@-1 {{Returning from 'sink'}}54} // expected-warning {{Potential leak of memory pointed to by 'ptr' [cplusplus.NewDeleteLeaks]}}55// expected-note@-1 {{Potential leak}}56 57} // namespace memory_passed_to_fn_call_delete58 59namespace memory_passed_to_fn_call_free {60 61void sink(int *P) {62 if (coin()) // ownership-note {{Assuming the condition is false}}63 // ownership-note@-1 {{Taking false branch}}64 free(P);65} // ownership-note {{Returning without deallocating memory or storing the pointer for later deallocation}}66 67void foo() {68 int *ptr = (int *)malloc(sizeof(int)); // expected-note {{Memory is allocated}}69 sink(ptr); // ownership-note {{Calling 'sink'}}70 // ownership-note@-1 {{Returning from 'sink'}}71} // expected-warning {{Potential leak of memory pointed to by 'ptr' [unix.Malloc]}}72// expected-note@-1 {{Potential leak}}73 74} // namespace memory_passed_to_fn_call_free75 76// Function pointers cannot be resolved syntactically.77namespace memory_passed_to_fn_call_free_through_fn_ptr {78void (*freeFn)(void *) = free;79 80void sink(int *P) {81 if (coin())82 freeFn(P);83}84 85void foo() {86 int *ptr = (int *)malloc(sizeof(int)); // expected-note {{Memory is allocated}}87 sink(ptr);88} // expected-warning {{Potential leak of memory pointed to by 'ptr' [unix.Malloc]}}89// expected-note@-1 {{Potential leak}}90 91} // namespace memory_passed_to_fn_call_free_through_fn_ptr92 93namespace memory_shared_with_ptr_of_shorter_lifetime {94 95void sink(int *P) {96 int *Q = P;97 if (coin()) // ownership-note {{Assuming the condition is false}}98 // ownership-note@-1 {{Taking false branch}}99 delete P;100 (void)Q;101} // ownership-note {{Returning without deallocating memory or storing the pointer for later deallocation}}102 103void foo() {104 int *ptr = new int(5); // expected-note {{Memory is allocated}}105 sink(ptr); // ownership-note {{Calling 'sink'}}106 // ownership-note@-1 {{Returning from 'sink'}}107} // expected-warning {{Potential leak of memory pointed to by 'ptr' [cplusplus.NewDeleteLeaks]}}108// expected-note@-1 {{Potential leak}}109 110} // namespace memory_shared_with_ptr_of_shorter_lifetime111 112//===----------------------------------------------------------------------===//113// Report for which we *do not* expect NoOwnershipChangeVisitor add a new note,114// nor do we want it to.115//===----------------------------------------------------------------------===//116 117namespace memory_not_passed_to_fn_call {118 119void sink(int *P) {120 if (coin())121 delete P;122}123 124void foo() {125 int *ptr = new int(5); // expected-note {{Memory is allocated}}126 int *q = nullptr;127 sink(q);128 (void)ptr;129} // expected-warning {{Potential leak of memory pointed to by 'ptr' [cplusplus.NewDeleteLeaks]}}130// expected-note@-1 {{Potential leak}}131 132} // namespace memory_not_passed_to_fn_call133 134namespace memory_shared_with_ptr_of_same_lifetime {135 136void sink(int *P, int **Q) {137 // NOTE: Not a job of NoOwnershipChangeVisitor, but maybe this could be138 // highlighted still?139 *Q = P;140}141 142void foo() {143 int *ptr = new int(5); // expected-note {{Memory is allocated}}144 int *q = nullptr;145 sink(ptr, &q);146} // expected-warning {{Potential leak of memory pointed to by 'q' [cplusplus.NewDeleteLeaks]}}147// expected-note@-1 {{Potential leak}}148 149} // namespace memory_shared_with_ptr_of_same_lifetime150 151namespace memory_passed_into_fn_that_doesnt_intend_to_free {152 153void sink(int *P) {154}155 156void foo() {157 int *ptr = new int(5); // expected-note {{Memory is allocated}}158 sink(ptr);159} // expected-warning {{Potential leak of memory pointed to by 'ptr' [cplusplus.NewDeleteLeaks]}}160// expected-note@-1 {{Potential leak}}161 162} // namespace memory_passed_into_fn_that_doesnt_intend_to_free163 164namespace memory_passed_into_fn_that_doesnt_intend_to_free2 {165 166void bar();167 168void sink(int *P) {169 // Correctly realize that calling bar() doesn't mean that this function would170 // like to deallocate anything.171 bar();172}173 174void foo() {175 int *ptr = new int(5); // expected-note {{Memory is allocated}}176 sink(ptr);177} // expected-warning {{Potential leak of memory pointed to by 'ptr' [cplusplus.NewDeleteLeaks]}}178// expected-note@-1 {{Potential leak}}179 180} // namespace memory_passed_into_fn_that_doesnt_intend_to_free2181 182namespace refkind_from_unoallocated_to_allocated {183 184// RefKind of the symbol changed from nothing to Allocated. We don't want to185// emit notes when the RefKind changes in the stack frame.186static char *malloc_wrapper_ret() {187 return (char *)malloc(12); // expected-note {{Memory is allocated}}188}189void use_ret() {190 char *v;191 v = malloc_wrapper_ret(); // expected-note {{Calling 'malloc_wrapper_ret'}}192 // expected-note@-1 {{Returned allocated memory}}193} // expected-warning {{Potential leak of memory pointed to by 'v' [unix.Malloc]}}194// expected-note@-1 {{Potential leak of memory pointed to by 'v'}}195 196} // namespace refkind_from_unoallocated_to_allocated197 198// Check that memory leak is reported against a symbol if the last place it's199// mentioned is a base region of a lazy compound value, as the program cannot200// possibly free that memory.201namespace symbol_reaper_lifetime {202struct Nested {203 int buf[2];204};205struct Wrapping {206 Nested data;207};208 209Nested allocateWrappingAndReturnNested() {210 // expected-note@+1 {{Memory is allocated}}211 Wrapping const* p = new Wrapping();212 // expected-warning@+2 {{Potential leak of memory pointed to by 'p'}}213 // expected-note@+1 {{Potential leak of memory pointed to by 'p'}}214 return p->data;215}216 217void caller() {218 // expected-note@+1 {{Calling 'allocateWrappingAndReturnNested'}}219 Nested n = allocateWrappingAndReturnNested();220 (void)n;221} // no-warning: No potential memory leak here, because that's been already reported.222} // namespace symbol_reaper_lifetime223 224// Check that we do not report false positives in automatically generated225// protobuf code that passes dynamically allocated memory to a certain function226// named GetOwnedMessageInternal.227namespace protobuf_leak {228Arena *some_arena, *some_submessage_arena;229 230MessageLite *protobuf_leak() {231 MessageLite *p = new MessageLite(); // Real protobuf code instantiates a232 // subclass of MessageLite, but that's233 // not relevant for the bug.234 MessageLite *q = GetOwnedMessageInternal(some_arena, p, some_submessage_arena);235 return q;236 // No leak at end of function -- the pointer escapes in GetOwnedMessageInternal.237}238 239void validate_system_header() {240 // The case protobuf_leak would also pass if GetOwnedMessageInternal wasn't241 // declared in a system header. This test verifies that another function242 // declared in the same header behaves differently (doesn't escape memory) to243 // demonstrate that GetOwnedMessageInternal is indeed explicitly recognized244 // by the analyzer.245 246 // expected-note@+1 {{Memory is allocated}}247 MessageLite *p = new MessageLite();248 SomeOtherFunction(p);249 // expected-warning@+2 {{Potential leak of memory pointed to by 'p'}}250 // expected-note@+1 {{Potential leak of memory pointed to by 'p'}}251}252 253} // namespace protobuf_leak254