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 
nonOwningCall(int * Resource,size_t Size)44 void nonOwningCall(int *Resource, size_t Size) {}
nonOwningCall(FILE * Resource)45 void nonOwningCall(FILE *Resource) {}
46 
consumesResource(gsl::owner<int * > Resource,size_t Size)47 void consumesResource(gsl::owner<int *> Resource, size_t Size) {}
consumesResource(gsl::owner<FILE * > Resource)48 void consumesResource(gsl::owner<FILE *> Resource) {}
49 
testNonCasted(void * Resource)50 void testNonCasted(void *Resource) {}
51 
testNonCastedOwner(gsl::owner<void * > Resource)52 void testNonCastedOwner(gsl::owner<void *> Resource) {}
53 
fileFactory1()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<>'
fileFactory2()56 gsl::owner<FILE *> fileFactory2() { return std::fopen("new_file.txt", "w"); } // Ok
57 
arrayFactory1()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<>'
arrayFactory2()60 gsl::owner<int *> arrayFactory2() { return (int *)std::malloc(100); } // Ok
dataFactory1()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<>'
dataFactory2()63 gsl::owner<void *> dataFactory2() { return std::malloc(100); } // Ok
64 
test_resource_creators()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 
test_legacy_consumers()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