| /* Test exercising -Wstringop-overread warnings for reading past the end. */ |
| /* { dg-do compile } */ |
| /* { dg-options "-O2 -Wstringop-overread -ftrack-macro-expansion=0" } */ |
| |
| #define PTRDIFF_MAX __PTRDIFF_MAX__ |
| #define SIZE_MAX __SIZE_MAX__ |
| |
| #define offsetof(type, mem) __builtin_offsetof (type, mem) |
| |
| /* Return the number of bytes from member MEM of TYPE to the end |
| of object OBJ. */ |
| #define offsetfrom(type, obj, mem) (sizeof (obj) - offsetof (type, mem)) |
| |
| |
| typedef __SIZE_TYPE__ size_t; |
| extern void* memchr (const void*, int, size_t); |
| extern int memcmp (const void*, const void*, size_t); |
| extern void* memcpy (void*, const void*, size_t); |
| extern void* memmove (void*, const void*, size_t); |
| extern void* mempcpy (void*, const void*, size_t); |
| |
| #define memchr(d, s, n) sink (memchr (d, s, n)) |
| #define memcmp(d, s, n) sink (d, memcmp (d, s, n)) |
| #define memcpy(d, s, n) sink (memcpy (d, s, n)) |
| #define memmove(d, s, n) sink (memmove (d, s, n)) |
| #define mempcpy(d, s, n) sink (mempcpy (d, s, n)) |
| |
| struct A { char a, b; }; |
| struct B { struct A a; char c, d; }; |
| |
| /* Function to call to "escape" pointers from tests below to prevent |
| GCC from assuming the values of the objects they point to stay |
| the unchanged. */ |
| void sink (void*, ...); |
| |
| /* Function to "generate" a random number each time it's called. Declared |
| (but not defined) and used to prevent GCC from making assumptions about |
| their values based on the variables uses in the tested expressions. */ |
| size_t random_unsigned_value (void); |
| |
| /* Return a random unsigned value between MIN and MAX. */ |
| |
| static inline size_t |
| range (size_t min, size_t max) |
| { |
| const size_t val = random_unsigned_value (); |
| return val < min || max < val ? min : val; |
| } |
| |
| #define R(min, max) range (min, max) |
| |
| /* Verify that reading beyond the end of a local array is diagnosed. */ |
| |
| void test_memop_warn_local (void *p, const void *q) |
| { |
| memcpy (p, "1234", R (6, 7)); /* { dg-warning "reading between 6 and 7 bytes from a region of size 5" } */ |
| |
| struct A a[2]; |
| |
| memcpy (p, a, R (7, 8)); /* { dg-warning "reading between 7 and 8 bytes from a region of size 4" } */ |
| |
| /* At -Wstringop-overflow=1 the destination is considered to be |
| the whole array and its size is therefore sizeof a. */ |
| memcpy (p, &a[0], R (8, 9)); /* { dg-warning "reading between 8 and 9 bytes from a region of size 4" } */ |
| |
| /* Verify the same as above but by reading from the first mmeber |
| of the first element of the array. */ |
| memcpy (p, &a[0].a, R (8, 9)); /* { dg-warning "reading between 8 and 9 bytes from a region of size 4" } */ |
| |
| struct B b[2]; |
| |
| memcpy (p, &b[0], R (12, 32)); /* { dg-warning "reading between 12 and 32 bytes from a region of size 8" } */ |
| |
| /* Verify memchr/memcmp. */ |
| int i = R (0, 255); |
| memchr ("", i, 2); /* { dg-warning "specified bound 2 exceeds source size 1" "memchr" } */ |
| memchr ("", i, 2); /* { dg-warning "specified bound 2 exceeds source size 1" "memchr" } */ |
| memchr ("123", i, 5); /* { dg-warning "specified bound 5 exceeds source size 4" "memchr" } */ |
| memchr (a, i, sizeof a + 1); /* { dg-warning "specified bound 5 exceeds source size 4" "memchr" } */ |
| |
| memcmp (p, "", 2); /* { dg-warning "specified bound 2 exceeds source size 1" "memcmp" } */ |
| memcmp (p, "123", 5); /* { dg-warning "specified bound 5 exceeds source size 4" "memcmp" } */ |
| memcmp (p, a, sizeof a + 1); /* { dg-warning "specified bound 5 exceeds source size 4" "memcmp" } */ |
| |
| size_t n = PTRDIFF_MAX + (size_t)1; |
| memchr (p, 1, n); /* { dg-warning "exceeds maximum object size" "memchr" } */ |
| memcmp (p, q, n); /* { dg-warning "exceeds maximum object size" "memcmp" } */ |
| |
| n = SIZE_MAX; |
| memchr (p, 1, n); /* { dg-warning "exceeds maximum object size" "memchr" } */ |
| memcmp (p, q, n); /* { dg-warning "exceeds maximum object size" "memcmp" } */ |
| } |
| |
| /* Verify that reading beyond the end of a dynamically allocated array |
| of known size is diagnosed. */ |
| |
| void test_memop_warn_alloc (void *p) |
| { |
| size_t n; |
| |
| n = range (8, 32); |
| |
| struct A *a = __builtin_malloc (sizeof *a * 2); |
| |
| memcpy (p, a, n); /* { dg-warning "reading between 8 and 32 bytes from a region of size 4" "memcpy from allocated" } */ |
| |
| memcpy (p, &a[0], n); /* { dg-warning "reading between 8 and 32 bytes from a region of size 4" "memcpy from allocated" } */ |
| |
| memcpy (p, &a[0].a, n); /* { dg-warning "reading between 8 and 32 bytes from a region of size " "memcpy from allocated" } */ |
| |
| n = range (12, 32); |
| |
| struct B *b = __builtin_malloc (sizeof *b * 2); |
| |
| memcpy (p, &b[0], n); /* { dg-warning "reading between 12 and 32 bytes from a region of size 8" "memcpy from allocated" } */ |
| |
| /* Verify memchr/memcmp. */ |
| n = sizeof *b * 2 + 1; |
| |
| memchr (b, 1, n); /* { dg-warning "specified bound 9 exceeds source size 8" "memchr from allocated" } */ |
| memcmp (p, b, n); /* { dg-warning "specified bound 9 exceeds source size 8" "memcmp from allocated" } */ |
| } |
| |
| |
| void test_memop_nowarn (void *p) |
| { |
| struct B b[2]; |
| |
| size_t n = range (sizeof b, 32); |
| |
| /* Verify that copying the whole array is not diagnosed regardless |
| of whether the expression pointing to its beginning is obtained |
| from the array itself or its first member(s). */ |
| memcpy (p, b, n); |
| |
| memcpy (p, &b[0], n); |
| |
| memcpy (p, &b[0].a, n); |
| |
| memcpy (p, &b[0].a.a, n); |
| |
| /* Verify that memchr/memcmp doesn't cause a warning. */ |
| memchr (p, 1, n); |
| memchr (b, 2, n); |
| memchr (&b[0], 3, n); |
| memchr (&b[0].a, 4, n); |
| memchr (&b[0].a.a, 5, n); |
| memchr ("01234567", R (0, 255), n); |
| |
| memcmp (p, p, n); |
| memcmp (p, b, n); |
| memcmp (p, &b[0], n); |
| memcmp (p, &b[0].a, n); |
| memcmp (p, &b[0].a.a, n); |
| memcmp (p, "01234567", n); |
| } |
| |
| |
| /* The following function could specify in its API that it takes |
| an array of exactly two elements, as shown below (or simply be |
| called with such an array). Verify that reading from both |
| elements is not diagnosed. */ |
| void test_memop_nowarn_arg (void*, const struct A[2]); |
| |
| void test_memop_nowarn_arg (void *p, const struct A *a) |
| { |
| memcpy (p, a, 2 * sizeof *a); |
| |
| memcpy (p, a, range (2 * sizeof *a, 123)); |
| |
| memchr (p, 1, 1234); |
| memcmp (p, a, 1234); |
| } |