1 /* Test exercising -Wstringop-overflow warnings. */
2 /* { dg-do compile } */
3 /* { dg-options "-O2 -Wstringop-overflow=1 -Wno-array-bounds" } */
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
52 /* At -Wstringop-overflow=1 the destination is considered to be
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 members 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
113 memcpy (a, src, n); /* { dg-warning "writing between 8 and 32 bytes into a region of size 4 " "memcpy into allocated" } */
114 escape (a, src);
115
116 /* At -Wstringop-overflow=1 the destination is considered to be
117 the whole array and its size is therefore sizeof a. */
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" } */
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 " "memcpy into allocated" } */
124 escape (a, src);
125
126 n = range (12, 32);
127
128 struct B *b = __builtin_malloc (sizeof (struct B[2]));
129
130 memcpy (&b[0], src, n); /* { dg-warning "writing between 12 and 32 bytes into a region of size 8 " "memcpy into allocated" } */
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, struct B[2], a.b) + 1); /* { dg-warning "writing 8 bytes into a region of size " "memcpy into allocated" } */
137 escape (b);
138
139 memset (&b->a.b, 0, offsetfrom (struct B, struct B[2], a.b) + 1); /* { dg-warning "writing 8 bytes into a region of size " "memcpy into allocated" } */
140 escape (b);
141
142 memset (&b[0].c, 0, offsetfrom (struct B, struct B[2], c) + 1); /* { dg-warning "writing 7 bytes into a region of size " "memcpy into allocated" } */
143 escape (b);
144
145 memset (&b->c, 0, offsetfrom (struct B, struct B[2], c) + 1); /* { dg-warning "writing 7 bytes into a region of size " "memcpy into allocated" } */
146 escape (b);
147
148 memset (&b[0].d, 0, offsetfrom (struct B, struct B[2], d) + 1); /* { dg-warning "writing 6 bytes into a region of size " "memcpy into allocated" } */
149 escape (b);
150
151 memset (&b->d, 0, offsetfrom (struct B, struct B[2], d) + 1); /* { dg-warning "writing 6 bytes into a region of size " "memcpy into allocated" } */
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 " "memcpy into allocated" } */
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 " "memcpy into allocated" } */
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" } */
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 }