1 // RUN: %check_clang_tidy %s cppcoreguidelines-owning-memory %t \ 2 // RUN: -config='{CheckOptions: \ 3 // RUN: [{key: cppcoreguidelines-owning-memory.LegacyResourceProducers, value: "::malloc;::aligned_alloc;::realloc;::calloc;::fopen;::freopen;::tmpfile"}, \ 4 // RUN: {key: cppcoreguidelines-owning-memory.LegacyResourceConsumers, value: "::free;::realloc;::freopen;::fclose"}]}' \ 5 // RUN: -- -nostdlib -nostdinc++ 6 7 namespace gsl { 8 template <class T> 9 using owner = T; 10 } // namespace gsl 11 12 extern "C" { 13 using size_t = decltype(sizeof(void*)); 14 using FILE = int; 15 16 void *malloc(size_t ByteCount); 17 void *aligned_alloc(size_t Alignment, size_t Size); 18 void *calloc(size_t Count, size_t SizeSingle); 19 void *realloc(void *Resource, size_t NewByteCount); 20 void free(void *Resource); 21 22 FILE *tmpfile(void); 23 FILE *fopen(const char *filename, const char *mode); 24 FILE *freopen(const char *filename, const char *mode, FILE *stream); 25 void fclose(FILE *Resource); 26 } 27 28 namespace std { 29 using ::FILE; 30 using ::size_t; 31 32 using ::fclose; 33 using ::fopen; 34 using ::freopen; 35 using ::tmpfile; 36 37 using ::aligned_alloc; 38 using ::calloc; 39 using ::free; 40 using ::malloc; 41 using ::realloc; 42 } // namespace std 43 44 void nonOwningCall(int *Resource, size_t Size) {} 45 void nonOwningCall(FILE *Resource) {} 46 47 void consumesResource(gsl::owner<int *> Resource, size_t Size) {} 48 void consumesResource(gsl::owner<FILE *> Resource) {} 49 50 void testNonCasted(void *Resource) {} 51 52 void testNonCastedOwner(gsl::owner<void *> Resource) {} 53 54 FILE *fileFactory1() { return ::fopen("new_file.txt", "w"); } 55 // CHECK-MESSAGES: [[@LINE-1]]:24: warning: returning a newly created resource of type 'FILE *' (aka 'int *') or 'gsl::owner<>' from a function whose return type is not 'gsl::owner<>' 56 gsl::owner<FILE *> fileFactory2() { return std::fopen("new_file.txt", "w"); } // Ok 57 58 int *arrayFactory1() { return (int *)std::malloc(100); } 59 // CHECK-MESSAGES: [[@LINE-1]]:24: warning: returning a newly created resource of type 'int *' or 'gsl::owner<>' from a function whose return type is not 'gsl::owner<>' 60 gsl::owner<int *> arrayFactory2() { return (int *)std::malloc(100); } // Ok 61 void *dataFactory1() { return std::malloc(100); } 62 // CHECK-MESSAGES: [[@LINE-1]]:24: warning: returning a newly created resource of type 'void *' or 'gsl::owner<>' from a function whose return type is not 'gsl::owner<>' 63 gsl::owner<void *> dataFactory2() { return std::malloc(100); } // Ok 64 65 void test_resource_creators() { 66 const unsigned int ByteCount = 25 * sizeof(int); 67 int Bad = 42; 68 69 int *IntArray1 = (int *)std::malloc(ByteCount); 70 // CHECK-MESSAGES: [[@LINE-1]]:3: warning: initializing non-owner 'int *' with a newly created 'gsl::owner<>' 71 int *IntArray2 = static_cast<int *>(std::malloc(ByteCount)); // Bad 72 // CHECK-MESSAGES: [[@LINE-1]]:3: warning: initializing non-owner 'int *' with a newly created 'gsl::owner<>' 73 void *IntArray3 = std::malloc(ByteCount); 74 // CHECK-MESSAGES: [[@LINE-1]]:3: warning: initializing non-owner 'void *' with a newly created 'gsl::owner<>' 75 76 int *IntArray4 = (int *)::malloc(ByteCount); 77 // CHECK-MESSAGES: [[@LINE-1]]:3: warning: initializing non-owner 'int *' with a newly created 'gsl::owner<>' 78 int *IntArray5 = static_cast<int *>(::malloc(ByteCount)); // Bad 79 // CHECK-MESSAGES: [[@LINE-1]]:3: warning: initializing non-owner 'int *' with a newly created 'gsl::owner<>' 80 void *IntArray6 = ::malloc(ByteCount); 81 // CHECK-MESSAGES: [[@LINE-1]]:3: warning: initializing non-owner 'void *' with a newly created 'gsl::owner<>' 82 83 gsl::owner<int *> IntArray7 = (int *)malloc(ByteCount); // Ok 84 gsl::owner<void *> IntArray8 = malloc(ByteCount); // Ok 85 86 gsl::owner<int *> IntArray9 = &Bad; 87 // CHECK-MESSAGES: [[@LINE-1]]:3: warning: expected initialization with value of type 'gsl::owner<>'; got 'int *' 88 89 nonOwningCall((int *)malloc(ByteCount), 25); 90 // CHECK-MESSAGES: [[@LINE-1]]:24: warning: initializing non-owner argument of type 'int *' with a newly created 'gsl::owner<>' 91 nonOwningCall((int *)::malloc(ByteCount), 25); 92 // CHECK-MESSAGES: [[@LINE-1]]:24: warning: initializing non-owner argument of type 'int *' with a newly created 'gsl::owner<>' 93 94 consumesResource((int *)malloc(ByteCount), 25); // Ok 95 consumesResource((int *)::malloc(ByteCount), 25); // Ok 96 97 testNonCasted(malloc(ByteCount)); 98 // CHECK-MESSAGES: [[@LINE-1]]:17: warning: initializing non-owner argument of type 'void *' with a newly created 'gsl::owner<>' 99 testNonCastedOwner(gsl::owner<void *>(malloc(ByteCount))); // Ok 100 testNonCastedOwner(malloc(ByteCount)); // Ok 101 102 FILE *File1 = std::fopen("test_name.txt", "w+"); 103 // CHECK-MESSAGES: [[@LINE-1]]:3: warning: initializing non-owner 'FILE *' (aka 'int *') with a newly created 'gsl::owner<>' 104 FILE *File2 = ::fopen("test_name.txt", "w+"); 105 // CHECK-MESSAGES: [[@LINE-1]]:3: warning: initializing non-owner 'FILE *' (aka 'int *') with a newly created 'gsl::owner<>' 106 107 gsl::owner<FILE *> File3 = ::fopen("test_name.txt", "w+"); // Ok 108 109 FILE *File4; 110 File4 = ::fopen("test_name.txt", "w+"); 111 // CHECK-MESSAGES: [[@LINE-1]]:3: warning: assigning newly created 'gsl::owner<>' to non-owner 'FILE *' (aka 'int *') 112 113 gsl::owner<FILE *> File5; 114 File5 = ::fopen("test_name.txt", "w+"); // Ok 115 File5 = File1; 116 // CHECK-MESSAGES: [[@LINE-1]]:3: warning: expected assignment source to be of type 'gsl::owner<>'; got 'FILE *' (aka 'int *') 117 118 gsl::owner<FILE *> File6 = File1; 119 // CHECK-MESSAGES: [[@LINE-1]]:3: warning: expected initialization with value of type 'gsl::owner<>'; got 'FILE *' (aka 'int *') 120 121 FILE *File7 = tmpfile(); 122 // CHECK-MESSAGES: [[@LINE-1]]:3: warning: initializing non-owner 'FILE *' (aka 'int *') with a newly created 'gsl::owner<>' 123 gsl::owner<FILE *> File8 = tmpfile(); // Ok 124 125 nonOwningCall(::fopen("test_name.txt", "r")); 126 // CHECK-MESSAGES: [[@LINE-1]]:17: warning: initializing non-owner argument of type 'FILE *' (aka 'int *') with a newly created 'gsl::owner<>' 127 nonOwningCall(std::fopen("test_name.txt", "r")); 128 // CHECK-MESSAGES: [[@LINE-1]]:17: warning: initializing non-owner argument of type 'FILE *' (aka 'int *') with a newly created 'gsl::owner<>' 129 130 consumesResource(::fopen("test_name.txt", "r")); // Ok 131 132 int *HeapPointer3 = (int *)aligned_alloc(16ul, 4ul * 32ul); 133 // CHECK-MESSAGES: [[@LINE-1]]:3: warning: initializing non-owner 'int *' with a newly created 'gsl::owner<>' 134 gsl::owner<int *> HeapPointer4 = static_cast<int *>(aligned_alloc(16ul, 4ul * 32ul)); // Ok 135 136 void *HeapPointer5 = calloc(10ul, 4ul); 137 // CHECK-MESSAGES: [[@LINE-1]]:3: warning: initializing non-owner 'void *' with a newly created 'gsl::owner<>' 138 gsl::owner<void *> HeapPointer6 = calloc(10ul, 4ul); // Ok 139 } 140 141 void test_legacy_consumers() { 142 int StackInteger = 42; 143 144 int *StackPointer = &StackInteger; 145 int *HeapPointer1 = (int *)malloc(100); 146 // CHECK-MESSAGES: [[@LINE-1]]:3: warning: initializing non-owner 'int *' with a newly created 'gsl::owner<>' 147 gsl::owner<int *> HeapPointer2 = (int *)malloc(100); 148 149 std::free(StackPointer); 150 // CHECK-MESSAGES: [[@LINE-1]]:3: warning: calling legacy resource function without passing a 'gsl::owner<>' 151 std::free(HeapPointer1); 152 // CHECK-MESSAGES: [[@LINE-1]]:3: warning: calling legacy resource function without passing a 'gsl::owner<>' 153 std::free(HeapPointer2); // Ok 154 // CHECK MESSAGES: [[@LINE-1]]:3: warning: calling legacy resource function without passing a 'gsl::owner<>' 155 156 // FIXME: the check complains about initialization of 'void *' with new created owner. 157 // This happens, because the argument of `free` is not marked as 'owner<>' (and cannot be), 158 // and the check will not figure out could be meant as owner. 159 // This property will probably never be fixed, because it is probably a rather rare 160 // use-case and 'owner<>' should be wrapped in RAII classes anyway! 161 std::free(std::malloc(100)); // Ok but silly :) 162 // CHECK-MESSAGES: [[@LINE-1]]:13: warning: initializing non-owner argument of type 'void *' with a newly created 'gsl::owner<>' 163 164 // Demonstrate, that multi-argument functions are diagnosed as well. 165 std::realloc(StackPointer, 200); 166 // CHECK-MESSAGES: [[@LINE-1]]:3: warning: calling legacy resource function without passing a 'gsl::owner<>' 167 std::realloc(HeapPointer1, 200); 168 // CHECK-MESSAGES: [[@LINE-1]]:3: warning: calling legacy resource function without passing a 'gsl::owner<>' 169 std::realloc(HeapPointer2, 200); // Ok 170 std::realloc(std::malloc(100), 200); // Ok but silly 171 // CHECK-MESSAGES: [[@LINE-1]]:16: warning: initializing non-owner argument of type 'void *' with a newly created 'gsl::owner<>' 172 173 fclose(fileFactory1()); 174 // CHECK-MESSAGES: [[@LINE-1]]:3: warning: calling legacy resource function without passing a 'gsl::owner<>' 175 fclose(fileFactory2()); // Ok, same as FIXME with `free(malloc(100))` applies here 176 // CHECK-MESSAGES: [[@LINE-1]]:10: warning: initializing non-owner argument of type 'FILE *' (aka 'int *') with a newly created 'gsl::owner<>' 177 178 gsl::owner<FILE *> File1 = fopen("testfile.txt", "r"); // Ok 179 FILE *File2 = freopen("testfile.txt", "w", File1); 180 // CHECK-MESSAGES: [[@LINE-1]]:3: warning: initializing non-owner 'FILE *' (aka 'int *') with a newly created 'gsl::owner<>' 181 // CHECK-MESSAGES: [[@LINE-2]]:17: warning: calling legacy resource function without passing a 'gsl::owner<>' 182 // FIXME: The warning for not passing and owner<> is a false positive since both the filename and the 183 // mode are not supposed to be owners but still pointers. The check is to coarse for 184 // this function. Maybe `freopen` gets special treatment. 185 186 gsl::owner<FILE *> File3 = freopen("testfile.txt", "w", File2); // Bad, File2 no owner 187 // CHECK-MESSAGES: [[@LINE-1]]:30: warning: calling legacy resource function without passing a 'gsl::owner<>' 188 189 FILE *TmpFile = tmpfile(); 190 // CHECK-MESSAGES: [[@LINE-1]]:3: warning: initializing non-owner 'FILE *' (aka 'int *') with a newly created 'gsl::owner<>' 191 FILE *File6 = freopen("testfile.txt", "w", TmpFile); // Bad, both return and argument 192 // CHECK-MESSAGES: [[@LINE-1]]:3: warning: initializing non-owner 'FILE *' (aka 'int *') with a newly created 'gsl::owner<>' 193 // CHECK-MESSAGES: [[@LINE-2]]:17: warning: calling legacy resource function without passing a 'gsl::owner<>' 194 } 195