145
+ − 1 /* Test exercising -Wstringop-overflow warnings. */
111
+ − 2 /* { dg-do compile } */
+ − 3 /* { dg-options "-O2 -Wstringop-overflow=1" } */
+ − 4
+ − 5 #define offsetof(type, mem) __builtin_offsetof (type, mem)
+ − 6
+ − 7 /* Return the number of bytes from member MEM of TYPE to the end
+ − 8 of object OBJ. */
+ − 9 #define offsetfrom(type, obj, mem) (sizeof (obj) - offsetof (type, mem))
+ − 10
+ − 11
+ − 12 typedef __SIZE_TYPE__ size_t;
+ − 13 extern void* memcpy (void*, const void*, size_t);
+ − 14 extern void* memset (void*, int, __SIZE_TYPE__);
+ − 15
+ − 16
+ − 17 struct A { char a, b; };
+ − 18 struct B { struct A a; char c, d; };
+ − 19
+ − 20 /* Function to call to "escape" pointers from tests below to prevent
+ − 21 GCC from assuming the values of the objects they point to stay
+ − 22 the unchanged. */
+ − 23 void escape (void*, ...);
+ − 24
+ − 25 /* Function to "generate" a random number each time it's called. Declared
+ − 26 (but not defined) and used to prevent GCC from making assumptions about
+ − 27 their values based on the variables uses in the tested expressions. */
+ − 28 size_t random_unsigned_value (void);
+ − 29
+ − 30 /* Return a random unsigned value between MIN and MAX. */
+ − 31
+ − 32 static inline size_t
+ − 33 range (size_t min, size_t max)
+ − 34 {
+ − 35 const size_t val = random_unsigned_value ();
+ − 36 return val < min || max < val ? min : val;
+ − 37 }
+ − 38
+ − 39 /* Verify that writing past the end of a local array is diagnosed. */
+ − 40
+ − 41 void test_memop_warn_local (const void *src)
+ − 42 {
+ − 43 size_t n;
+ − 44
+ − 45 n = range (8, 32);
+ − 46
+ − 47 struct A a[2];
+ − 48
+ − 49 memcpy (a, src, n); /* { dg-warning "writing between 8 and 32 bytes into a region of size 4 overflows the destination" } */
+ − 50 escape (a, src);
+ − 51
145
+ − 52 /* At -Wstringop-overflow=1 the destination is considered to be
111
+ − 53 the whole array and its size is therefore sizeof a. */
+ − 54 memcpy (&a[0], src, n); /* { dg-warning "writing between 8 and 32 bytes into a region of size 4 overflows the destination" } */
+ − 55 escape (a, src);
+ − 56
+ − 57 /* Verify the same as above but by writing into the first mmeber
+ − 58 of the first element of the array. */
+ − 59 memcpy (&a[0].a, src, n); /* { dg-warning "writing between 8 and 32 bytes into a region of size 4 overflows the destination" } */
+ − 60 escape (a, src);
+ − 61
+ − 62 n = range (12, 32);
+ − 63
+ − 64 struct B b[2];
+ − 65
+ − 66 memcpy (&b[0], src, n); /* { dg-warning "writing between 12 and 32 bytes into a region of size 8 overflows the destination" } */
+ − 67 escape (b);
+ − 68
+ − 69 /* The following idiom of clearing multiple members of a struct is
+ − 70 used in a few places in the Linux kernel. Verify that a warning
+ − 71 is issued for it when it writes past the end of the array object. */
+ − 72 memset (&b[0].a.b, 0, offsetfrom (struct B, b, a.b) + 1); /* { dg-warning "writing 8 bytes into a region of size 7" } */
+ − 73 escape (b);
+ − 74
+ − 75 memset (&b->a.b, 0, offsetfrom (struct B, b, a.b) + 1); /* { dg-warning "writing 8 bytes into a region of size 7" } */
+ − 76 escape (b);
+ − 77
+ − 78 memset (&b[0].c, 0, offsetfrom (struct B, b, c) + 1); /* { dg-warning "writing 7 bytes into a region of size 6" } */
+ − 79 escape (b);
+ − 80
+ − 81 memset (&b->c, 0, offsetfrom (struct B, b, c) + 1); /* { dg-warning "writing 7 bytes into a region of size 6" } */
+ − 82 escape (b);
+ − 83
+ − 84 memset (&b[0].d, 0, offsetfrom (struct B, b, d) + 1); /* { dg-warning "writing 6 bytes into a region of size 5" } */
+ − 85 escape (b);
+ − 86
+ − 87 memset (&b->d, 0, offsetfrom (struct B, b, d) + 1); /* { dg-warning "writing 6 bytes into a region of size 5" } */
+ − 88 escape (b);
+ − 89
+ − 90 /* Same as above but clearing just elements of the second element
+ − 91 of the array. */
+ − 92 memset (&b[1].a.b, 0, offsetfrom (struct B, b[1], a.b) + 1); /* { dg-warning "writing 4 bytes into a region of size 3" } */
+ − 93 escape (b);
+ − 94
+ − 95 memset (&b[1].c, 0, offsetfrom (struct B, b[1], c) + 1); /* { dg-warning "writing 3 bytes into a region of size 2" } */
+ − 96 escape (b);
+ − 97
+ − 98 memset (&b[1].d, 0, offsetfrom (struct B, b[1], d) + 1); /* { dg-warning "writing 2 bytes into a region of size 1" } */
+ − 99 escape (b);
+ − 100 }
+ − 101
+ − 102 /* Verify that writing past the end of a dynamically allocated array
+ − 103 of known size is diagnosed. */
+ − 104
+ − 105 void test_memop_warn_alloc (const void *src)
+ − 106 {
+ − 107 size_t n;
+ − 108
+ − 109 n = range (8, 32);
+ − 110
+ − 111 struct A *a = __builtin_malloc (sizeof *a * 2);
+ − 112
145
+ − 113 memcpy (a, src, n); /* { dg-warning "writing between 8 and 32 bytes into a region of size 4 " "memcpy into allocated" } */
111
+ − 114 escape (a, src);
+ − 115
145
+ − 116 /* At -Wstringop-overflow=1 the destination is considered to be
111
+ − 117 the whole array and its size is therefore sizeof a. */
145
+ − 118 memcpy (&a[0], src, n); /* { dg-warning "writing between 8 and 32 bytes into a region of size 4 overflows the destination" "memcpy into allocated" } */
111
+ − 119 escape (a, src);
+ − 120
+ − 121 /* Verify the same as above but by writing into the first mmeber
+ − 122 of the first element of the array. */
+ − 123 memcpy (&a[0].a, src, n); /* { dg-warning "writing between 8 and 32 bytes into a region of size 4 overflows the destination" "memcpy into allocated" { xfail *-*-*} } */
+ − 124 escape (a, src);
+ − 125
+ − 126 n = range (12, 32);
+ − 127
+ − 128 struct B *b = __builtin_malloc (sizeof *b * 2);
+ − 129
145
+ − 130 memcpy (&b[0], src, n); /* { dg-warning "writing between 12 and 32 bytes into a region of size 8 " "memcpy into allocated" } */
111
+ − 131 escape (b);
+ − 132
+ − 133 /* The following idiom of clearing multiple members of a struct is
+ − 134 used in a few places in the Linux kernel. Verify that a warning
+ − 135 is issued for it when it writes past the end of the array object. */
+ − 136 memset (&b[0].a.b, 0, offsetfrom (struct B, b, a.b) + 1); /* { dg-warning "writing 8 bytes into a region of size 7" "memcpy into allocated" { xfail *-*-*} } */
+ − 137 escape (b);
+ − 138
+ − 139 memset (&b->a.b, 0, offsetfrom (struct B, b, a.b) + 1); /* { dg-warning "writing 8 bytes into a region of size 7" "memcpy into allocated" { xfail *-*-*} } */
+ − 140 escape (b);
+ − 141
+ − 142 memset (&b[0].c, 0, offsetfrom (struct B, b, c) + 1); /* { dg-warning "writing 7 bytes into a region of size 6" "memcpy into allocated" { xfail *-*-*} } */
+ − 143 escape (b);
+ − 144
+ − 145 memset (&b->c, 0, offsetfrom (struct B, b, c) + 1); /* { dg-warning "writing 7 bytes into a region of size 6" "memcpy into allocated" { xfail *-*-*} } */
+ − 146 escape (b);
+ − 147
+ − 148 memset (&b[0].d, 0, offsetfrom (struct B, b, d) + 1); /* { dg-warning "writing 6 bytes into a region of size 5" "memcpy into allocated" { xfail *-*-*} } */
+ − 149 escape (b);
+ − 150
+ − 151 memset (&b->d, 0, offsetfrom (struct B, b, d) + 1); /* { dg-warning "writing 6 bytes into a region of size 5" "memcpy into allocated" { xfail *-*-*} } */
+ − 152 escape (b);
+ − 153
+ − 154 /* Same as above but clearing just elements of the second element
+ − 155 of the array. */
+ − 156 memset (&b[1].a.b, 0, offsetfrom (struct B, b[1], a.b) + 1); /* { dg-warning "writing 4 bytes into a region of size 3" "memcpy into allocated" { xfail *-*-*} } */
+ − 157 escape (b);
+ − 158
+ − 159 memset (&b[1].c, 0, offsetfrom (struct B, b[1], c) + 1); /* { dg-warning "writing 3 bytes into a region of size 2" "memcpy into allocated" { xfail *-*-*} } */
+ − 160 escape (b);
+ − 161
+ − 162 memset (&b[1].d, 0, offsetfrom (struct B, b[1], d) + 1); /* { dg-warning "writing 2 bytes into a region of size 1" "memcpy into allocated" { xfail *-*-*} } */
+ − 163 escape (b);
+ − 164 }
+ − 165
+ − 166
+ − 167 void test_memop_nowarn (const void *src)
+ − 168 {
+ − 169 struct B b[2];
+ − 170
+ − 171 size_t n = range (sizeof b, 32);
+ − 172
+ − 173 /* Verify that clearing the whole array is not diagnosed regardless
+ − 174 of whether the expression pointing to its beginning is obtained
+ − 175 from the array itself or its first member(s). */
+ − 176 memcpy (b, src, n);
+ − 177 escape (b);
+ − 178
+ − 179 memcpy (&b[0], src, n);
+ − 180 escape (b);
+ − 181
+ − 182 memcpy (&b[0].a, src, n);
+ − 183 escape (b, src);
+ − 184
+ − 185 memcpy (&b[0].a.a, src, n);
+ − 186 escape (b, src);
+ − 187
+ − 188 /* Clearing multiple elements of an array of structs. */
+ − 189 memset (&b[0].a.b, 0, sizeof b - offsetof (struct B, a.b));
+ − 190 escape (b);
+ − 191
+ − 192 memset (&b->a.b, 0, sizeof b - offsetof (struct B, a.b));
+ − 193 escape (b);
+ − 194
+ − 195 memset (&b[0].c, 0, sizeof b - offsetof (struct B, c));
+ − 196 escape (b);
+ − 197
+ − 198 memset (&b->c, 0, sizeof b - offsetof (struct B, c));
+ − 199 escape (b);
+ − 200
+ − 201 memset (&b[0].d, 0, sizeof b - offsetof (struct B, d));
+ − 202 escape (b);
+ − 203
+ − 204 memset (&b->d, 0, sizeof b - offsetof (struct B, d));
+ − 205 escape (b);
+ − 206
+ − 207 /* Same as above but clearing just elements of the second element
+ − 208 of the array. */
+ − 209 memset (&b[1].a.b, 0, sizeof b[1] - offsetof (struct B, a.b));
+ − 210 escape (b);
+ − 211
+ − 212 memset (&b[1].c, 0, sizeof b[1] - offsetof (struct B, c));
+ − 213 escape (b);
+ − 214
+ − 215 memset (&b[1].d, 0, sizeof b[1] - offsetof (struct B, d));
+ − 216 escape (b);
+ − 217 }
+ − 218
+ − 219
+ − 220 /* The foollowing function could specify in its API that it takes
+ − 221 an array of exactly two elements, as shown below. Verify that
+ − 222 writing into both elements is not diagnosed. */
+ − 223 void test_memop_nowarn_arg (struct A[2], const void*);
+ − 224
+ − 225 void test_memop_nowarn_arg (struct A *a, const void *src)
+ − 226 {
+ − 227 memcpy (a, src, 2 * sizeof *a);
+ − 228 escape (a, src);
+ − 229
+ − 230 memcpy (a, src, range (2 * sizeof *a, 123));
+ − 231 escape (a, src);
+ − 232 }
+ − 233
+ − 234
+ − 235 struct C { char a[3], b; };
+ − 236 struct D { struct C c; char d, e; };
+ − 237
+ − 238 extern char* strncpy (char*, const char*, __SIZE_TYPE__);
+ − 239
+ − 240 void test_stringop_warn (void)
+ − 241 {
+ − 242 size_t n = range (2 * sizeof (struct D) + 1, 33);
+ − 243
+ − 244 struct C c[2];
+ − 245
+ − 246 /* Similarly, at -Wstringop-overflow=1 the destination is considered
+ − 247 to be the whole array and its size is therefore sizeof c. */
+ − 248 strncpy (c[0].a, "123", n); /* { dg-warning "writing between 13 and 33 bytes into a region of size 8 overflows the destination" } */
+ − 249
+ − 250 escape (c);
+ − 251 }
+ − 252
+ − 253
+ − 254 void test_stringop_nowarn (void)
+ − 255 {
+ − 256 struct D d[2];
+ − 257
+ − 258 strncpy (d[0].c.a, "123", range (sizeof d, 32));
+ − 259 escape (d);
+ − 260 }