| // RUN: %clang_cc1 -std=c++20 -Wno-all -Wunsafe-buffer-usage \ |
| // RUN: -fsafe-buffer-usage-suggestions \ |
| // RUN: -fblocks -include %s -verify %s |
| |
| // RUN: %clang -x c++ -fsyntax-only -fblocks -include %s %s 2>&1 | FileCheck --allow-empty %s |
| // RUN: %clang_cc1 -std=c++11 -fblocks -include %s %s 2>&1 | FileCheck --allow-empty %s |
| // RUN: %clang_cc1 -std=c++20 -fblocks -include %s %s 2>&1 | FileCheck --allow-empty %s |
| // CHECK-NOT: [-Wunsafe-buffer-usage] |
| |
| #ifndef INCLUDED |
| #define INCLUDED |
| #pragma clang system_header |
| |
| // no spanification warnings for system headers |
| void foo(...); // let arguments of `foo` to hold testing expressions |
| void testAsSystemHeader(char *p) { |
| ++p; |
| |
| auto ap1 = p; |
| auto ap2 = &p; |
| |
| foo(p[1], |
| ap1[1], |
| ap2[2][3]); |
| } |
| |
| #else |
| |
| void testIncrement(char *p) { // expected-warning{{'p' is an unsafe pointer used for buffer access}} |
| ++p; // expected-note{{used in pointer arithmetic here}} |
| p++; // expected-note{{used in pointer arithmetic here}} |
| --p; // expected-note{{used in pointer arithmetic here}} |
| p--; // expected-note{{used in pointer arithmetic here}} |
| } |
| |
| void * voidPtrCall(void); |
| char * charPtrCall(void); |
| |
| void testArraySubscripts(int idx, int *p, int **pp) { |
| // expected-warning@-1{{'p' is an unsafe pointer used for buffer access}} |
| // expected-warning@-2{{'pp' is an unsafe pointer used for buffer access}} |
| foo(p[1], // expected-note{{used in buffer access here}} |
| pp[1][1], // expected-note{{used in buffer access here}} |
| // expected-warning@-1{{unsafe buffer access}} |
| 1[1[pp]], // expected-note{{used in buffer access here}} |
| // expected-warning@-1{{unsafe buffer access}} |
| 1[pp][1] // expected-note{{used in buffer access here}} |
| // expected-warning@-1{{unsafe buffer access}} |
| ); |
| |
| if (p[3]) { // expected-note{{used in buffer access here}} |
| void * q = p; |
| |
| foo(((int*)q)[10]); // expected-warning{{unsafe buffer access}} |
| } |
| |
| foo(((int*)voidPtrCall())[3], // expected-warning{{unsafe buffer access}} |
| 3[(int*)voidPtrCall()], // expected-warning{{unsafe buffer access}} |
| charPtrCall()[3], // expected-warning{{unsafe buffer access}} |
| 3[charPtrCall()] // expected-warning{{unsafe buffer access}} |
| ); |
| |
| int a[10]; // expected-warning{{'a' is an unsafe buffer that does not perform bounds checks}} |
| // expected-note@-1{{change type of 'a' to 'std::array' to label it for hardening}} |
| int b[10][10]; // expected-warning{{'b' is an unsafe buffer that does not perform bounds checks}} |
| |
| foo(a[idx], idx[a], // expected-note2{{used in buffer access here}} |
| b[idx][idx + 1], // expected-warning{{unsafe buffer access}} |
| // expected-note@-1{{used in buffer access here}} |
| (idx + 1)[b][idx],// expected-warning{{unsafe buffer access}} |
| // expected-note@-1{{used in buffer access here}} |
| (idx + 1)[idx[b]]); |
| // expected-warning@-1{{unsafe buffer access}} |
| // expected-note@-2{{used in buffer access here}} |
| |
| // Not to warn when index is zero |
| foo(p[0], pp[0][0], 0[0[pp]], 0[pp][0], |
| ((int*)voidPtrCall())[0], |
| 0[(int*)voidPtrCall()], |
| charPtrCall()[0], |
| 0[charPtrCall()] |
| ); |
| } |
| |
| void testArraySubscriptsWithAuto() { |
| int a[10]; |
| // We do not fix a declaration if the type is `auto`. Because the actual type may change later. |
| auto ap1 = a; // expected-warning{{'ap1' is an unsafe pointer used for buffer access}} |
| foo(ap1[1]); // expected-note{{used in buffer access here}} |
| |
| // In case the type is `auto *`, we know it must be a pointer. We can fix it. |
| auto * ap2 = a; // expected-warning{{'ap2' is an unsafe pointer used for buffer access}} \ |
| expected-note{{change type of 'ap2' to 'std::span' to preserve bounds information}} |
| foo(ap2[1]); // expected-note{{used in buffer access here}} |
| } |
| |
| void testUnevaluatedContext(int * p) {// no-warning |
| foo(sizeof(p[1]), // no-warning |
| sizeof(decltype(p[1]))); // no-warning |
| } |
| |
| void testQualifiedParameters(const int * p, const int * const q, const int a[10], const int b[10][10]) { |
| // expected-warning@-1{{'p' is an unsafe pointer used for buffer access}} |
| // expected-warning@-2{{'q' is an unsafe pointer used for buffer access}} |
| // expected-warning@-3{{'a' is an unsafe pointer used for buffer access}} |
| // expected-warning@-4{{'b' is an unsafe pointer used for buffer access}} |
| |
| foo(p[1], 1[p], p[-1], // expected-note3{{used in buffer access here}} |
| q[1], 1[q], q[-1], // expected-note3{{used in buffer access here}} |
| a[1], // expected-note{{used in buffer access here}} `a` is of pointer type |
| b[1][2] // expected-note{{used in buffer access here}} `b[1]` is of array type |
| // expected-warning@-1{{unsafe buffer access}} |
| ); |
| } |
| |
| struct T { |
| int a[10]; |
| int * b; |
| struct { |
| int a[10]; |
| int * b; |
| } c; |
| }; |
| |
| typedef struct T T_t; |
| |
| T_t funRetT(); |
| T_t * funRetTStar(); |
| |
| void testStructMembers(struct T * sp, struct T s, T_t * sp2, T_t s2) { |
| foo(sp->a[1], // expected-warning{{unsafe buffer access}} |
| sp->b[1], // expected-warning{{unsafe buffer access}} |
| sp->c.a[1], // expected-warning{{unsafe buffer access}} |
| sp->c.b[1], // expected-warning{{unsafe buffer access}} |
| s.a[1], // expected-warning{{unsafe buffer access}} |
| s.b[1], // expected-warning{{unsafe buffer access}} |
| s.c.a[1], // expected-warning{{unsafe buffer access}} |
| s.c.b[1], // expected-warning{{unsafe buffer access}} |
| sp2->a[1], // expected-warning{{unsafe buffer access}} |
| sp2->b[1], // expected-warning{{unsafe buffer access}} |
| sp2->c.a[1], // expected-warning{{unsafe buffer access}} |
| sp2->c.b[1], // expected-warning{{unsafe buffer access}} |
| s2.a[1], // expected-warning{{unsafe buffer access}} |
| s2.b[1], // expected-warning{{unsafe buffer access}} |
| s2.c.a[1], // expected-warning{{unsafe buffer access}} |
| s2.c.b[1], // expected-warning{{unsafe buffer access}} |
| funRetT().a[1], // expected-warning{{unsafe buffer access}} |
| funRetT().b[1], // expected-warning{{unsafe buffer access}} |
| funRetTStar()->a[1], // expected-warning{{unsafe buffer access}} |
| funRetTStar()->b[1] // expected-warning{{unsafe buffer access}} |
| ); |
| } |
| |
| int garray[10]; // expected-warning{{'garray' is an unsafe buffer that does not perform bounds checks}} |
| int * gp = garray; // expected-warning{{'gp' is an unsafe pointer used for buffer access}} |
| int gvar = gp[1]; // FIXME: file scope unsafe buffer access is not warned |
| |
| void testLambdaCaptureAndGlobal(int * p) { |
| // expected-warning@-1{{'p' is an unsafe pointer used for buffer access}} |
| int a[10]; // expected-warning{{'a' is an unsafe buffer that does not perform bounds checks}} |
| |
| auto Lam = [p, a](int idx) { |
| return p[1] // expected-note{{used in buffer access here}} |
| + a[idx] + garray[idx]// expected-note2{{used in buffer access here}} |
| + gp[1]; // expected-note{{used in buffer access here}} |
| |
| }; |
| } |
| |
| auto file_scope_lambda = [](int *ptr) { |
| // expected-warning@-1{{'ptr' is an unsafe pointer used for buffer access}} |
| |
| ptr[5] = 10; // expected-note{{used in buffer access here}} |
| }; |
| |
| void testLambdaCapture() { |
| int a[10]; // expected-warning{{'a' is an unsafe buffer that does not perform bounds checks}} |
| int b[10]; // expected-warning{{'b' is an unsafe buffer that does not perform bounds checks}} |
| // expected-note@-1{{change type of 'b' to 'std::array' to label it for hardening}} |
| int c[10]; |
| |
| auto Lam1 = [a](unsigned idx) { |
| return a[idx]; // expected-note{{used in buffer access here}} |
| }; |
| |
| auto Lam2 = [x = b[c[5]]]() { // expected-note{{used in buffer access here}} |
| return x; |
| }; |
| |
| auto Lam = [x = c](unsigned idx) { // expected-warning{{'x' is an unsafe pointer used for buffer access}} |
| return x[idx]; // expected-note{{used in buffer access here}} |
| }; |
| } |
| |
| void testLambdaImplicitCapture(long idx) { |
| int a[10]; // expected-warning{{'a' is an unsafe buffer that does not perform bounds checks}} |
| // expected-note@-1{{change type of 'a' to 'std::array' to label it for hardening}} |
| int b[10]; // expected-warning{{'b' is an unsafe buffer that does not perform bounds checks}} |
| // expected-note@-1{{change type of 'b' to 'std::array' to label it for hardening}} |
| |
| auto Lam1 = [=]() { |
| return a[idx]; // expected-note{{used in buffer access here}} |
| }; |
| |
| auto Lam2 = [&]() { |
| return b[idx]; // expected-note{{used in buffer access here}} |
| }; |
| } |
| |
| typedef T_t * T_ptr_t; |
| |
| void testTypedefs(T_ptr_t p) { |
| // expected-warning@-1{{'p' is an unsafe pointer used for buffer access}} |
| foo(p[1], // expected-note{{used in buffer access here}} |
| p[1].a[1], // expected-note{{used in buffer access here}} |
| // expected-warning@-1{{unsafe buffer access}} |
| p[1].b[1] // expected-note{{used in buffer access here}} |
| // expected-warning@-1{{unsafe buffer access}} |
| ); |
| } |
| |
| template<typename T, int N> T f(T t, T * pt, T a[N], T (&b)[N]) { |
| // expected-warning@-1{{'t' is an unsafe pointer used for buffer access}} |
| // expected-warning@-2{{'pt' is an unsafe pointer used for buffer access}} |
| // expected-warning@-3{{'a' is an unsafe pointer used for buffer access}} |
| // expected-warning@-4{{'b' is an unsafe buffer that does not perform bounds checks}} |
| foo(pt[1], // expected-note{{used in buffer access here}} |
| a[1], // expected-note{{used in buffer access here}} |
| b[1]); // expected-note{{used in buffer access here}} |
| return &t[1]; // expected-note{{used in buffer access here}} |
| } |
| |
| // Testing pointer arithmetic for pointer-to-int, qualified multi-level |
| // pointer, pointer to a template type, and auto type |
| T_ptr_t getPtr(); |
| |
| template<typename T> |
| void testPointerArithmetic(int * p, const int **q, T * x) { |
| // expected-warning@-1{{'p' is an unsafe pointer used for buffer access}} |
| // expected-warning@-2{{'x' is an unsafe pointer used for buffer access}} |
| int a[10]; |
| auto y = &a[0]; // expected-warning{{'y' is an unsafe pointer used for buffer access}} |
| |
| foo(p + 1, 1 + p, p - 1, // expected-note3{{used in pointer arithmetic here}} |
| *q + 1, 1 + *q, *q - 1, // expected-warning3{{unsafe pointer arithmetic}} |
| x + 1, 1 + x, x - 1, // expected-note3{{used in pointer arithmetic here}} |
| y + 1, 1 + y, y - 1, // expected-note3{{used in pointer arithmetic here}} |
| getPtr() + 1, 1 + getPtr(), getPtr() - 1 // expected-warning3{{unsafe pointer arithmetic}} |
| ); |
| |
| p += 1; p -= 1; // expected-note2{{used in pointer arithmetic here}} |
| *q += 1; *q -= 1; // expected-warning2{{unsafe pointer arithmetic}} |
| y += 1; y -= 1; // expected-note2{{used in pointer arithmetic here}} |
| x += 1; x -= 1; // expected-note2{{used in pointer arithmetic here}} |
| } |
| |
| void testTemplate(int * p) { |
| int *a[10]; |
| foo(f(p, &p, a, a)[1]); // expected-warning{{unsafe buffer access}} |
| // FIXME: expected note@-1{{in instantiation of function template specialization 'f<int *, 10>' requested here}} |
| |
| const int **q = const_cast<const int **>(&p); |
| |
| testPointerArithmetic(p, q, p); //FIXME: expected note{{in instantiation of}} |
| } |
| |
| void testPointerToMember() { |
| struct S_t { |
| int x; |
| int * y; |
| } S; |
| |
| int S_t::* p = &S_t::x; |
| int * S_t::* q = &S_t::y; |
| |
| foo(S.*p, |
| (S.*q)[1]); // expected-warning{{unsafe buffer access}} |
| } |
| |
| // test that nested callable definitions are scanned only once |
| void testNestedCallableDefinition(int * p) { |
| class A { |
| void inner(int * p) { |
| // expected-warning@-1{{'p' is an unsafe pointer used for buffer access}} |
| p++; // expected-note{{used in pointer arithmetic here}} |
| } |
| |
| static void innerStatic(int * p) { |
| // expected-warning@-1{{'p' is an unsafe pointer used for buffer access}} |
| p++; // expected-note{{used in pointer arithmetic here}} |
| } |
| |
| void innerInner(int * p) { |
| auto Lam = [p]() { |
| int * q = p; // expected-warning{{'q' is an unsafe pointer used for buffer access}} |
| q++; // expected-note{{used in pointer arithmetic here}} |
| return *q; |
| }; |
| } |
| }; |
| |
| auto Lam = [p]() { |
| int * q = p; // expected-warning{{'q' is an unsafe pointer used for buffer access}} |
| q++; // expected-note{{used in pointer arithmetic here}} |
| return *q; |
| }; |
| |
| auto LamLam = [p]() { |
| auto Lam = [p]() { |
| int * q = p; // expected-warning{{'q' is an unsafe pointer used for buffer access}} |
| q++; // expected-note{{used in pointer arithmetic here}} |
| return *q; |
| }; |
| }; |
| |
| void (^Blk)(int*) = ^(int *p) { |
| // expected-warning@-1{{'p' is an unsafe pointer used for buffer access}} |
| p++; // expected-note{{used in pointer arithmetic here}} |
| }; |
| |
| void (^BlkBlk)(int*) = ^(int *p) { |
| void (^Blk)(int*) = ^(int *p) { |
| // expected-warning@-1{{'p' is an unsafe pointer used for buffer access}} |
| p++; // expected-note{{used in pointer arithmetic here}} |
| }; |
| Blk(p); |
| }; |
| |
| // lambda and block as call arguments... |
| foo( [p]() { int * q = p; // expected-warning{{'q' is an unsafe pointer used for buffer access}} |
| q++; // expected-note{{used in pointer arithmetic here}} |
| return *q; |
| }, |
| ^(int *p) { // expected-warning{{'p' is an unsafe pointer used for buffer access}} |
| p++; // expected-note{{used in pointer arithmetic here}} |
| } |
| ); |
| } |
| |
| int testVariableDecls(int * p) { |
| // expected-warning@-1{{'p' is an unsafe pointer used for buffer access}} |
| int * q = p++; // expected-note{{used in pointer arithmetic here}} |
| int a[p[1]]; // expected-note{{used in buffer access here}} |
| int b = p[1]; // expected-note{{used in buffer access here}} |
| return p[1]; // expected-note{{used in buffer access here}} |
| } |
| |
| template<typename T> void fArr(T t[], long long idx) { |
| // expected-warning@-1{{'t' is an unsafe pointer used for buffer access}} |
| foo(t[1]); // expected-note{{used in buffer access here}} |
| T ar[8]; // expected-warning{{'ar' is an unsafe buffer that does not perform bounds checks}} |
| // expected-note@-1{{change type of 'ar' to 'std::array' to label it for hardening}} |
| foo(ar[idx]); // expected-note{{used in buffer access here}} |
| } |
| |
| template void fArr<int>(int t[], long long); // FIXME: expected note {{in instantiation of}} |
| |
| int testReturn(int t[]) {// expected-note{{change type of 't' to 'std::span' to preserve bounds information}} |
| // expected-warning@-1{{'t' is an unsafe pointer used for buffer access}} |
| return t[1]; // expected-note{{used in buffer access here}} |
| } |
| |
| int testArrayAccesses(int n, int idx) { |
| // auto deduced array type |
| int cArr[2][3] = {{1, 2, 3}, {4, 5, 6}}; |
| // expected-warning@-1{{'cArr' is an unsafe buffer that does not perform bounds checks}} |
| int d = cArr[0][0]; |
| foo(cArr[0][0]); |
| foo(cArr[idx][idx + 1]); // expected-note{{used in buffer access here}} |
| // expected-warning@-1{{unsafe buffer access}} |
| auto cPtr = cArr[idx][idx * 2]; // expected-note{{used in buffer access here}} |
| // expected-warning@-1{{unsafe buffer access}} |
| foo(cPtr); |
| |
| // Typdefs |
| typedef int A[3]; |
| const A tArr = {4, 5, 6}; |
| foo(tArr[0], tArr[1]); |
| return cArr[0][1]; // expected-warning{{unsafe buffer access}} |
| } |
| |
| void testArrayPtrArithmetic(int x[]) { // expected-warning{{'x' is an unsafe pointer used for buffer access}} |
| foo (x + 3); // expected-note{{used in pointer arithmetic here}} |
| |
| int y[3] = {0, 1, 2}; // expected-warning{{'y' is an unsafe buffer that does not perform bounds checks}} |
| foo(y + 4); // expected-note{{used in pointer arithmetic here}} |
| } |
| |
| void testMultiLineDeclStmt(int * p) { |
| int |
| |
| * |
| |
| ap1 = p; // expected-warning{{'ap1' is an unsafe pointer used for buffer access}} \ |
| expected-note{{change type of 'ap1' to 'std::span' to preserve bounds information}} |
| |
| foo(ap1[1]); // expected-note{{used in buffer access here}} |
| } |
| |
| #endif |