xref: /llvm-project/clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp (revision 9a1e6373ab3edc38486af504154db2d804e72d3d)
1 // RUN: %clang_cc1 -std=c++20 -Wno-all -Wunsafe-buffer-usage \
2 // RUN:            -fsafe-buffer-usage-suggestions \
3 // RUN:            -fblocks -include %s -verify %s
4 
5 // RUN: %clang -x c++ -fsyntax-only -fblocks -include %s %s 2>&1 | FileCheck --allow-empty %s
6 // RUN: %clang_cc1 -std=c++11 -fblocks -include %s %s 2>&1 | FileCheck --allow-empty %s
7 // RUN: %clang_cc1 -std=c++20 -fblocks -include %s %s 2>&1 | FileCheck --allow-empty %s
8 // CHECK-NOT: [-Wunsafe-buffer-usage]
9 
10 #ifndef INCLUDED
11 #define INCLUDED
12 #pragma clang system_header
13 
14 // no spanification warnings for system headers
15 void foo(...);  // let arguments of `foo` to hold testing expressions
16 void testAsSystemHeader(char *p) {
17   ++p;
18 
19   auto ap1 = p;
20   auto ap2 = &p;
21 
22   foo(p[1],
23       ap1[1],
24       ap2[2][3]);
25 }
26 
27 #else
28 
29 void testIncrement(char *p) { // expected-warning{{'p' is an unsafe pointer used for buffer access}}
30   ++p; // expected-note{{used in pointer arithmetic here}}
31   p++; // expected-note{{used in pointer arithmetic here}}
32   --p; // expected-note{{used in pointer arithmetic here}}
33   p--; // expected-note{{used in pointer arithmetic here}}
34 }
35 
36 void * voidPtrCall(void);
37 char * charPtrCall(void);
38 
39 void testArraySubscripts(int idx, int *p, int **pp) {
40 // expected-warning@-1{{'p' is an unsafe pointer used for buffer access}}
41 // expected-warning@-2{{'pp' is an unsafe pointer used for buffer access}}
42   foo(p[1],             // expected-note{{used in buffer access here}}
43       pp[1][1],         // expected-note{{used in buffer access here}}
44                         // expected-warning@-1{{unsafe buffer access}}
45       1[1[pp]],         // expected-note{{used in buffer access here}}
46                         // expected-warning@-1{{unsafe buffer access}}
47       1[pp][1]          // expected-note{{used in buffer access here}}
48                         // expected-warning@-1{{unsafe buffer access}}
49       );
50 
51   if (p[3]) {           // expected-note{{used in buffer access here}}
52     void * q = p;
53 
54     foo(((int*)q)[10]); // expected-warning{{unsafe buffer access}}
55   }
56 
57   foo(((int*)voidPtrCall())[3], // expected-warning{{unsafe buffer access}}
58       3[(int*)voidPtrCall()],   // expected-warning{{unsafe buffer access}}
59       charPtrCall()[3],         // expected-warning{{unsafe buffer access}}
60       3[charPtrCall()]          // expected-warning{{unsafe buffer access}}
61       );
62 
63     int a[10];          // expected-warning{{'a' is an unsafe buffer that does not perform bounds checks}}
64                         // expected-note@-1{{change type of 'a' to 'std::array' to label it for hardening}}
65     int b[10][10];      // expected-warning{{'b' is an unsafe buffer that does not perform bounds checks}}
66 
67   foo(a[idx], idx[a],   // expected-note2{{used in buffer access here}}
68       b[idx][idx + 1],  // expected-warning{{unsafe buffer access}}
69                         // expected-note@-1{{used in buffer access here}}
70       (idx + 1)[b][idx],// expected-warning{{unsafe buffer access}}
71                         // expected-note@-1{{used in buffer access here}}
72       (idx + 1)[idx[b]]);
73                         // expected-warning@-1{{unsafe buffer access}}
74                         // expected-note@-2{{used in buffer access here}}
75 
76   // Not to warn when index is zero
77   foo(p[0], pp[0][0], 0[0[pp]], 0[pp][0],
78       ((int*)voidPtrCall())[0],
79       0[(int*)voidPtrCall()],
80       charPtrCall()[0],
81       0[charPtrCall()]
82       );
83 }
84 
85 void testArraySubscriptsWithAuto() {
86   int a[10];
87   // We do not fix a declaration if the type is `auto`. Because the actual type may change later.
88   auto ap1 = a;   // expected-warning{{'ap1' is an unsafe pointer used for buffer access}}
89   foo(ap1[1]);    // expected-note{{used in buffer access here}}
90 
91   // In case the type is `auto *`, we know it must be a pointer. We can fix it.
92   auto * ap2 = a; // expected-warning{{'ap2' is an unsafe pointer used for buffer access}} \
93                      expected-note{{change type of 'ap2' to 'std::span' to preserve bounds information}}
94   foo(ap2[1]);    // expected-note{{used in buffer access here}}
95 }
96 
97 void testUnevaluatedContext(int * p) {// no-warning
98   foo(sizeof(p[1]),             // no-warning
99       sizeof(decltype(p[1])));  // no-warning
100 }
101 
102 void testQualifiedParameters(const int * p, const int * const q, const int a[10], const int b[10][10]) {
103   // expected-warning@-1{{'p' is an unsafe pointer used for buffer access}}
104   // expected-warning@-2{{'q' is an unsafe pointer used for buffer access}}
105   // expected-warning@-3{{'a' is an unsafe pointer used for buffer access}}
106   // expected-warning@-4{{'b' is an unsafe pointer used for buffer access}}
107 
108   foo(p[1], 1[p], p[-1],    // expected-note3{{used in buffer access here}}
109       q[1], 1[q], q[-1],    // expected-note3{{used in buffer access here}}
110       a[1],                 // expected-note{{used in buffer access here}}     `a` is of pointer type
111       b[1][2]               // expected-note{{used in buffer access here}}     `b[1]` is of array type
112                             // expected-warning@-1{{unsafe buffer access}}
113       );
114 }
115 
116 struct T {
117   int a[10];
118   int * b;
119   struct {
120     int a[10];
121     int * b;
122   } c;
123 };
124 
125 typedef struct T T_t;
126 
127 T_t funRetT();
128 T_t * funRetTStar();
129 
130 void testStructMembers(struct T * sp, struct T s, T_t * sp2, T_t s2) {
131   foo(sp->a[1],     // expected-warning{{unsafe buffer access}}
132       sp->b[1],     // expected-warning{{unsafe buffer access}}
133       sp->c.a[1],   // expected-warning{{unsafe buffer access}}
134       sp->c.b[1],   // expected-warning{{unsafe buffer access}}
135       s.a[1],       // expected-warning{{unsafe buffer access}}
136       s.b[1],       // expected-warning{{unsafe buffer access}}
137       s.c.a[1],     // expected-warning{{unsafe buffer access}}
138       s.c.b[1],     // expected-warning{{unsafe buffer access}}
139       sp2->a[1],    // expected-warning{{unsafe buffer access}}
140       sp2->b[1],    // expected-warning{{unsafe buffer access}}
141       sp2->c.a[1],  // expected-warning{{unsafe buffer access}}
142       sp2->c.b[1],  // expected-warning{{unsafe buffer access}}
143       s2.a[1],      // expected-warning{{unsafe buffer access}}
144       s2.b[1],      // expected-warning{{unsafe buffer access}}
145       s2.c.a[1],           // expected-warning{{unsafe buffer access}}
146       s2.c.b[1],           // expected-warning{{unsafe buffer access}}
147       funRetT().a[1],      // expected-warning{{unsafe buffer access}}
148       funRetT().b[1],      // expected-warning{{unsafe buffer access}}
149       funRetTStar()->a[1], // expected-warning{{unsafe buffer access}}
150       funRetTStar()->b[1]  // expected-warning{{unsafe buffer access}}
151       );
152 }
153 
154 int garray[10];     // expected-warning{{'garray' is an unsafe buffer that does not perform bounds checks}}
155 int * gp = garray;  // expected-warning{{'gp' is an unsafe pointer used for buffer access}}
156 int gvar = gp[1];   // FIXME: file scope unsafe buffer access is not warned
157 
158 void testLambdaCaptureAndGlobal(int * p) {
159   // expected-warning@-1{{'p' is an unsafe pointer used for buffer access}}
160   int a[10];              // expected-warning{{'a' is an unsafe buffer that does not perform bounds checks}}
161 
162   auto Lam = [p, a](int idx) {
163     return p[1]           // expected-note{{used in buffer access here}}
164       + a[idx] + garray[idx]// expected-note2{{used in buffer access here}}
165       + gp[1];            // expected-note{{used in buffer access here}}
166 
167   };
168 }
169 
170 auto file_scope_lambda = [](int *ptr) {
171   // expected-warning@-1{{'ptr' is an unsafe pointer used for buffer access}}
172 
173   ptr[5] = 10;  // expected-note{{used in buffer access here}}
174 };
175 
176 void testLambdaCapture() {
177   int a[10];              // expected-warning{{'a' is an unsafe buffer that does not perform bounds checks}}
178   int b[10];              // expected-warning{{'b' is an unsafe buffer that does not perform bounds checks}}
179                           // expected-note@-1{{change type of 'b' to 'std::array' to label it for hardening}}
180   int c[10];
181 
182   auto Lam1 = [a](unsigned idx) {
183     return a[idx];           // expected-note{{used in buffer access here}}
184   };
185 
186   auto Lam2 = [x = b[c[5]]]() { // expected-note{{used in buffer access here}}
187     return x;
188   };
189 
190   auto Lam = [x = c](unsigned idx) { // expected-warning{{'x' is an unsafe pointer used for buffer access}}
191     return x[idx]; // expected-note{{used in buffer access here}}
192   };
193 }
194 
195 void testLambdaImplicitCapture(long idx) {
196   int a[10];              // expected-warning{{'a' is an unsafe buffer that does not perform bounds checks}}
197                           // expected-note@-1{{change type of 'a' to 'std::array' to label it for hardening}}
198   int b[10];              // expected-warning{{'b' is an unsafe buffer that does not perform bounds checks}}
199                           // expected-note@-1{{change type of 'b' to 'std::array' to label it for hardening}}
200 
201   auto Lam1 = [=]() {
202     return a[idx];           // expected-note{{used in buffer access here}}
203   };
204 
205   auto Lam2 = [&]() {
206     return b[idx];           // expected-note{{used in buffer access here}}
207   };
208 }
209 
210 typedef T_t * T_ptr_t;
211 
212 void testTypedefs(T_ptr_t p) {
213   // expected-warning@-1{{'p' is an unsafe pointer used for buffer access}}
214   foo(p[1],       // expected-note{{used in buffer access here}}
215       p[1].a[1],  // expected-note{{used in buffer access here}}
216                   // expected-warning@-1{{unsafe buffer access}}
217       p[1].b[1]   // expected-note{{used in buffer access here}}
218                   // expected-warning@-1{{unsafe buffer access}}
219       );
220 }
221 
222 template<typename T, int N> T f(T t, T * pt, T a[N], T (&b)[N]) {
223   // expected-warning@-1{{'t' is an unsafe pointer used for buffer access}}
224   // expected-warning@-2{{'pt' is an unsafe pointer used for buffer access}}
225   // expected-warning@-3{{'a' is an unsafe pointer used for buffer access}}
226   // expected-warning@-4{{'b' is an unsafe buffer that does not perform bounds checks}}
227   foo(pt[1],    // expected-note{{used in buffer access here}}
228       a[1],     // expected-note{{used in buffer access here}}
229       b[1]);    // expected-note{{used in buffer access here}}
230   return &t[1]; // expected-note{{used in buffer access here}}
231 }
232 
233 // Testing pointer arithmetic for pointer-to-int, qualified multi-level
234 // pointer, pointer to a template type, and auto type
235 T_ptr_t getPtr();
236 
237 template<typename T>
238 void testPointerArithmetic(int * p, const int **q, T * x) {
239 // expected-warning@-1{{'p' is an unsafe pointer used for buffer access}}
240 // expected-warning@-2{{'x' is an unsafe pointer used for buffer access}}
241   int a[10];
242   auto y = &a[0]; // expected-warning{{'y' is an unsafe pointer used for buffer access}}
243 
244   foo(p + 1, 1 + p, p - 1,      // expected-note3{{used in pointer arithmetic here}}
245       *q + 1, 1 + *q, *q - 1,   // expected-warning3{{unsafe pointer arithmetic}}
246       x + 1, 1 + x, x - 1,      // expected-note3{{used in pointer arithmetic here}}
247       y + 1, 1 + y, y - 1,      // expected-note3{{used in pointer arithmetic here}}
248       getPtr() + 1, 1 + getPtr(), getPtr() - 1 // expected-warning3{{unsafe pointer arithmetic}}
249       );
250 
251   p += 1;  p -= 1;  // expected-note2{{used in pointer arithmetic here}}
252   *q += 1; *q -= 1; // expected-warning2{{unsafe pointer arithmetic}}
253   y += 1; y -= 1;   // expected-note2{{used in pointer arithmetic here}}
254   x += 1; x -= 1;   // expected-note2{{used in pointer arithmetic here}}
255 }
256 
257 void testTemplate(int * p) {
258   int *a[10];
259   foo(f(p, &p, a, a)[1]); // expected-warning{{unsafe buffer access}}
260                           // FIXME: expected note@-1{{in instantiation of function template specialization 'f<int *, 10>' requested here}}
261 
262   const int **q = const_cast<const int **>(&p);
263 
264   testPointerArithmetic(p, q, p); //FIXME: expected note{{in instantiation of}}
265 }
266 
267 void testPointerToMember() {
268   struct S_t {
269     int x;
270     int * y;
271   } S;
272 
273   int S_t::* p = &S_t::x;
274   int * S_t::* q = &S_t::y;
275 
276   foo(S.*p,
277       (S.*q)[1]);  // expected-warning{{unsafe buffer access}}
278 }
279 
280 // test that nested callable definitions are scanned only once
281 void testNestedCallableDefinition(int * p) {
282   class A {
283     void inner(int * p) {
284       // expected-warning@-1{{'p' is an unsafe pointer used for buffer access}}
285       p++; // expected-note{{used in pointer arithmetic here}}
286     }
287 
288     static void innerStatic(int * p) {
289       // expected-warning@-1{{'p' is an unsafe pointer used for buffer access}}
290       p++; // expected-note{{used in pointer arithmetic here}}
291     }
292 
293     void innerInner(int * p) {
294       auto Lam = [p]() {
295         int * q = p;    // expected-warning{{'q' is an unsafe pointer used for buffer access}}
296         q++;            // expected-note{{used in pointer arithmetic here}}
297         return *q;
298       };
299     }
300   };
301 
302   auto Lam = [p]() {
303     int * q = p;  // expected-warning{{'q' is an unsafe pointer used for buffer access}}
304     q++;          // expected-note{{used in pointer arithmetic here}}
305     return *q;
306   };
307 
308   auto LamLam = [p]() {
309     auto Lam = [p]() {
310       int * q = p;  // expected-warning{{'q' is an unsafe pointer used for buffer access}}
311       q++;          // expected-note{{used in pointer arithmetic here}}
312       return *q;
313     };
314   };
315 
316   void (^Blk)(int*) = ^(int *p) {
317     // expected-warning@-1{{'p' is an unsafe pointer used for buffer access}}
318     p++;   // expected-note{{used in pointer arithmetic here}}
319   };
320 
321   void (^BlkBlk)(int*) = ^(int *p) {
322     void (^Blk)(int*) = ^(int *p) {
323       // expected-warning@-1{{'p' is an unsafe pointer used for buffer access}}
324       p++;   // expected-note{{used in pointer arithmetic here}}
325     };
326     Blk(p);
327   };
328 
329   // lambda and block as call arguments...
330   foo( [p]() { int * q = p; // expected-warning{{'q' is an unsafe pointer used for buffer access}}
331               q++;          // expected-note{{used in pointer arithmetic here}}
332               return *q;
333        },
334        ^(int *p) {  // expected-warning{{'p' is an unsafe pointer used for buffer access}}
335         p++;        // expected-note{{used in pointer arithmetic here}}
336        }
337      );
338 }
339 
340 int testVariableDecls(int * p) {
341   // expected-warning@-1{{'p' is an unsafe pointer used for buffer access}}
342   int * q = p++;      // expected-note{{used in pointer arithmetic here}}
343   int a[p[1]];        // expected-note{{used in buffer access here}}
344   int b = p[1];       // expected-note{{used in buffer access here}}
345   return p[1];        // expected-note{{used in buffer access here}}
346 }
347 
348 template<typename T> void fArr(T t[], long long idx) {
349   // expected-warning@-1{{'t' is an unsafe pointer used for buffer access}}
350   foo(t[1]);    // expected-note{{used in buffer access here}}
351   T ar[8];      // expected-warning{{'ar' is an unsafe buffer that does not perform bounds checks}}
352                 // expected-note@-1{{change type of 'ar' to 'std::array' to label it for hardening}}
353   foo(ar[idx]);   // expected-note{{used in buffer access here}}
354 }
355 
356 template void fArr<int>(int t[], long long); // FIXME: expected note {{in instantiation of}}
357 
358 int testReturn(int t[]) {// expected-note{{change type of 't' to 'std::span' to preserve bounds information}}
359   // expected-warning@-1{{'t' is an unsafe pointer used for buffer access}}
360   return t[1]; // expected-note{{used in buffer access here}}
361 }
362 
363 int testArrayAccesses(int n, int idx) {
364     // auto deduced array type
365     int cArr[2][3] = {{1, 2, 3}, {4, 5, 6}};
366     // expected-warning@-1{{'cArr' is an unsafe buffer that does not perform bounds checks}}
367     int d = cArr[0][0];
368     foo(cArr[0][0]);
369     foo(cArr[idx][idx + 1]);        // expected-note{{used in buffer access here}}
370                                     // expected-warning@-1{{unsafe buffer access}}
371     auto cPtr = cArr[idx][idx * 2]; // expected-note{{used in buffer access here}}
372                                     // expected-warning@-1{{unsafe buffer access}}
373     foo(cPtr);
374 
375     // Typdefs
376     typedef int A[3];
377     const A tArr = {4, 5, 6};
378     foo(tArr[0], tArr[1]);
379     return cArr[0][1];      // expected-warning{{unsafe buffer access}}
380 }
381 
382 void testArrayPtrArithmetic(int x[]) { // expected-warning{{'x' is an unsafe pointer used for buffer access}}
383   foo (x + 3); // expected-note{{used in pointer arithmetic here}}
384 
385   int y[3] = {0, 1, 2}; // expected-warning{{'y' is an unsafe buffer that does not perform bounds checks}}
386   foo(y + 4); // expected-note{{used in pointer arithmetic here}}
387 }
388 
389 void testMultiLineDeclStmt(int * p) {
390   int
391 
392   *
393 
394   ap1 = p;      // expected-warning{{'ap1' is an unsafe pointer used for buffer access}} \
395          	   expected-note{{change type of 'ap1' to 'std::span' to preserve bounds information}}
396 
397   foo(ap1[1]);  // expected-note{{used in buffer access here}}
398 }
399 
400 #endif
401