Mercurial > hg > CbC > CbC_gcc
comparison gcc/testsuite/c-c++-common/Wstringop-truncation.c @ 131:84e7813d76e9
gcc-8.2
author | mir3636 |
---|---|
date | Thu, 25 Oct 2018 07:37:49 +0900 |
parents | |
children | 1830386684a0 |
comparison
equal
deleted
inserted
replaced
111:04ced10e8804 | 131:84e7813d76e9 |
---|---|
1 /* PR middle-end/81117 - Improve buffer overflow checking in strncpy | |
2 { dg-do compile } | |
3 { dg-options "-O2 -Wstringop-truncation -Wno-stringop-overflow -ftrack-macro-expansion=0" } | |
4 { dg-require-effective-target alloca } */ | |
5 | |
6 | |
7 typedef __SIZE_TYPE__ size_t; | |
8 | |
9 #if __cplusplus | |
10 extern "C" { | |
11 #endif | |
12 | |
13 size_t strlen (const char*); | |
14 char* strncat (char*, const char*, size_t); | |
15 char* strncpy (char*, const char*, size_t); | |
16 | |
17 #if __cplusplus | |
18 } | |
19 #endif | |
20 | |
21 static size_t unsigned_value (void) | |
22 { | |
23 extern volatile size_t unsigned_value_source; | |
24 return unsigned_value_source; | |
25 } | |
26 | |
27 static size_t unsigned_range (size_t min, size_t max) | |
28 { | |
29 size_t val = unsigned_value (); | |
30 return val < min || max < val ? min : val; | |
31 } | |
32 | |
33 #define UR(min, max) unsigned_range (min, max) | |
34 | |
35 void sink (void*); | |
36 | |
37 #define S4 "123" | |
38 const char a4[] = "123"; | |
39 | |
40 #define CHOOSE(a, b) (unsigned_value () & 1 ? a : b) | |
41 | |
42 | |
43 typedef struct Dest | |
44 { | |
45 char a5[5]; | |
46 char b7[7]; | |
47 char c3ns[3] __attribute__ ((nonstring)); | |
48 } Dest; | |
49 | |
50 char dst7[7]; | |
51 char dst2_5[2][5]; | |
52 | |
53 /* Verify strncat warnings for arrays of known bounds. */ | |
54 | |
55 void test_strncat_array (Dest *pd) | |
56 { | |
57 #define CAT(d, s, len) (strncat ((d), (s), (len)), sink (d)) | |
58 | |
59 CAT (dst7, S4, 2); /* { dg-warning "output truncated copying 2 bytes from a string of length 3" } */ | |
60 | |
61 CAT (dst7, a4, 1); /* { dg-warning "output truncated copying 1 byte from a string of length 3" } */ | |
62 | |
63 /* There is no truncation here but possible overflow so these | |
64 are diagnosed by -Wstringop-overflow: | |
65 CAT (dst7, S4, 3); | |
66 CAT (dst7, a4, 3); | |
67 */ | |
68 | |
69 CAT (pd->a5, S4, 2); /* { dg-warning "output truncated copying 2 bytes from a string of length 3" } */ | |
70 CAT (pd->a5, S4, 1); /* { dg-warning "output truncated copying 1 byte from a string of length 3" } */ | |
71 } | |
72 | |
73 /* Verify strncat warnings for arrays of known bounds and a non-const | |
74 character count in some range. */ | |
75 | |
76 void test_strncat_array_range (Dest *pd) | |
77 { | |
78 CAT (dst7, S4, UR (0, 1)); /* { dg-warning "output truncated copying between 0 and 1 bytes from a string of length 3" } */ | |
79 CAT (dst7, S4, UR (0, 2)); /* { dg-warning "output truncated copying between 0 and 2 bytes from a string of length 3" } */ | |
80 CAT (dst7, S4, UR (1, 3)); /* { dg-warning "output truncated copying between 1 and 3 bytes from a string of length 3" } */ | |
81 CAT (dst7, S4, UR (2, 4)); /* { dg-warning "output may be truncated copying between 2 and 4 bytes from a string of length 3" } */ | |
82 | |
83 CAT (dst7, S4, UR (0, 7)); | |
84 CAT (dst7, S4, UR (1, 7)); | |
85 CAT (dst7, S4, UR (6, 7)); | |
86 | |
87 CAT (dst7, S4, UR (0, 99)); | |
88 | |
89 CAT (dst7, S4, UR (0, 99)); | |
90 } | |
91 | |
92 /* Verify strncat warnings for arrays of unknown bounds. */ | |
93 | |
94 void test_strncat_vla (char *d, unsigned n) | |
95 { | |
96 CAT (d, S4, 2); /* { dg-warning "output truncated copying 2 bytes from a string of length 3" } */ | |
97 CAT (d, S4, 4); | |
98 | |
99 CAT (d, a4, 2); /* { dg-warning "output truncated copying 2 bytes from a string of length 3" } */ | |
100 | |
101 /* There is no truncation here but possible overflow so these | |
102 are diagnosed by -Wstringop-overflow: | |
103 CAT (d, S4, 3); | |
104 CAT (d, a4, 3); | |
105 */ | |
106 CAT (d, a4, 4); | |
107 | |
108 char vla[n]; | |
109 | |
110 CAT (vla, S4, 2); /* { dg-warning "output truncated copying 2 bytes from a string of length 3" } */ | |
111 | |
112 CAT (vla, S4, 4); | |
113 CAT (vla, S4, n); | |
114 | |
115 CAT (vla, a4, 2); /* { dg-warning "output truncated copying 2 bytes from a string of length 3" } */ | |
116 | |
117 CAT (vla, a4, 4); | |
118 CAT (vla, a4, n); | |
119 | |
120 CAT (d, vla, 1); | |
121 CAT (d, vla, 3); | |
122 CAT (d, vla, 4); | |
123 CAT (d, vla, n); | |
124 | |
125 /* There is no truncation here but possible overflow so these | |
126 are diagnosed by -Wstringop-overflow: | |
127 CAT (vla, S4, 3); | |
128 CAT (vla, a4, 3); | |
129 */ | |
130 } | |
131 | |
132 /* Verify strncpy warnings with at least one pointer to an object | |
133 or string of unknown size (destination) or length (source). */ | |
134 | |
135 void test_strncpy_ptr (char *d, const char* s, const char *t, int i) | |
136 { | |
137 #define CPY(d, s, len) (strncpy ((d), (s), (len)), sink (d)) | |
138 | |
139 /* Strncpy doesn't nul-terminate so the following is diagnosed. */ | |
140 CPY (d, "", 0); /* { dg-warning ".strncpy\[^\n\r\]* destination unchanged after copying no bytes" } */ | |
141 CPY (d, s, 0); /* { dg-warning ".strncpy\[^\n\r\]* destination unchanged after copying no bytes" } */ | |
142 | |
143 /* This is safe. */ | |
144 CPY (d, "", 1); | |
145 CPY (d, "", 2); | |
146 | |
147 /* This could be safe. */ | |
148 CPY (d, s, 1); | |
149 CPY (d, s, 2); | |
150 | |
151 /* Truncation. */ | |
152 CPY (d, "123", 1); /* { dg-warning ".strncpy\[^\n\r\]* output truncated copying 1 byte from a string of length 3" } */ | |
153 CPY (d, "123", 2); /* { dg-warning ".strncpy\[^\n\r\]* output truncated copying 2 bytes from a string of length 3" } */ | |
154 CPY (d, "123", 3); /* { dg-warning ".strncpy\[^\n\r\]* output truncated before terminating nul copying 3 bytes from a string of the same length" } */ | |
155 CPY (d, "123", 4); | |
156 CPY (d, "123", 9); | |
157 | |
158 CPY (d, S4, sizeof S4); /* Covered by -Wsizeof-pointer-memaccess. */ | |
159 CPY (d, S4, sizeof S4 - 1); /* { dg-warning ".strncpy\[^\n\r\]* output truncated before terminating nul copying 3 bytes from a string of the same length" } */ | |
160 | |
161 CPY (d, a4, sizeof a4); /* Covered by -Wsizeof-pointer-memaccess. */ | |
162 CPY (d, a4, sizeof a4 - 1); /* { dg-warning ".strncpy\[^\n\r\]* output truncated before terminating nul copying 3 bytes from a string of the same length" } */ | |
163 CPY (d, a4, sizeof a4 - 3); /* { dg-warning ".strncpy\[^\n\r\]* output truncated copying 1 byte from a string of length 3" } */ | |
164 CPY (d, a4, sizeof a4 - 4); /* { dg-warning ".strncpy\[^\n\r\]* destination unchanged after copying no bytes from a string of length 3" } */ | |
165 | |
166 CPY (d, S4, strlen (S4)); /* { dg-warning ".strncpy\[^\n\r\]* output truncated before terminating nul copying 3 bytes from a string of the same length" } */ | |
167 /* Likely buggy but no truncation. Diagnosed by -Wstringop-overflow. */ | |
168 CPY (d, a4, strlen (a4) + 1); | |
169 CPY (d, S4, strlen (S4) + i); | |
170 | |
171 CPY (d, a4, strlen (a4)); /* { dg-warning ".strncpy\[^\n\r\]* output truncated before terminating nul copying 3 bytes from a string of the same length" } */ | |
172 /* As above, buggy but no evidence of truncation. */ | |
173 CPY (d, S4, strlen (S4) + 1); | |
174 | |
175 CPY (d, CHOOSE ("", "1"), 0); /* { dg-warning ".strncpy\[^\n\r\]* destination unchanged after copying no bytes" } */ | |
176 CPY (d, CHOOSE ("1", "12"), 0); /* { dg-warning ".strncpy\[^\n\r\]* destination unchanged after copying no bytes" } */ | |
177 | |
178 CPY (d, CHOOSE ("", "1"), 1); /* { dg-warning ".strncpy\[^\n\r\]* output may be truncated copying 1 byte from a string of length 1" } */ | |
179 CPY (d, CHOOSE ("1", ""), 1); /* { dg-warning ".strncpy\[^\n\r\]* output may be truncated copying 1 byte from a string of length 1" } */ | |
180 CPY (d, CHOOSE (s, "1"), 1); /* { dg-warning ".strncpy\[^\n\r\]* output may be truncated copying 1 byte from a string of length 1" } */ | |
181 CPY (d, CHOOSE (s, t), 1); | |
182 | |
183 CPY (d, CHOOSE ("", "1"), 2); | |
184 CPY (d, CHOOSE ("1", ""), 2); | |
185 CPY (d, CHOOSE ("1", "2"), 2); | |
186 CPY (d, CHOOSE ("1", s), 2); | |
187 CPY (d, CHOOSE (s, "1"), 2); | |
188 CPY (d, CHOOSE (s, t), 2); | |
189 | |
190 CPY (d, CHOOSE ("", "123"), 1); /* { dg-warning ".strncpy\[^\n\r\]* output may be truncated copying 1 byte from a string of length 3" } */ | |
191 CPY (d, CHOOSE ("1", "123"), 1); /* { dg-warning ".strncpy\[^\n\r\]* output truncated before terminating nul copying 1 byte from a string of the same length" } */ | |
192 CPY (d, CHOOSE ("12", "123"), 1); /* { dg-warning ".strncpy\[^\n\r\]* output truncated copying 1 byte from a string of length 2" } */ | |
193 CPY (d, CHOOSE ("123", "12"), 1); /* { dg-warning ".strncpy\[^\n\r\]* output truncated copying 1 byte from a string of length 2" } */ | |
194 | |
195 { | |
196 signed char n = strlen (s); /* { dg-message "length computed here" } */ | |
197 CPY (d, s, n); /* { dg-warning ".strncpy\[^\n\r\]* output truncated before terminating nul copying as many bytes from a string as its length" } */ | |
198 } | |
199 | |
200 { | |
201 short n = strlen (s); /* { dg-message "length computed here" } */ | |
202 CPY (d, s, n); /* { dg-warning ".strncpy\[^\n\r\]* output truncated before terminating nul copying as many bytes from a string as its length" } */ | |
203 } | |
204 | |
205 { | |
206 int n = strlen (s); /* { dg-message "length computed here" } */ | |
207 CPY (d, s, n); /* { dg-warning ".strncpy\[^\n\r\]* output truncated before terminating nul copying as many bytes from a string as its length" } */ | |
208 } | |
209 | |
210 { | |
211 unsigned n = strlen (s); /* { dg-message "length computed here" } */ | |
212 CPY (d, s, n); /* { dg-warning ".strncpy\[^\n\r\]* output truncated before terminating nul copying as many bytes from a string as its length" } */ | |
213 } | |
214 | |
215 { | |
216 size_t n; | |
217 n = strlen (s); /* { dg-message "length computed here" } */ | |
218 CPY (d, s, n); /* { dg-warning ".strncpy\[^\n\r\]* output truncated before terminating nul copying as many bytes from a string as its length" } */ | |
219 } | |
220 | |
221 { | |
222 size_t n; | |
223 char *dp2 = d + 1; | |
224 n = strlen (s); /* { dg-message "length computed here" } */ | |
225 CPY (dp2, s, n); /* { dg-warning ".strncpy\[^\n\r\]* output truncated before terminating nul copying as many bytes from a string as its length" } */ | |
226 } | |
227 | |
228 { | |
229 /* The following is likely buggy but there's no apparent truncation | |
230 so it's not diagnosed by -Wstringop-truncation. Instead, it is | |
231 diagnosed by -Wstringop-overflow (tested elsewhere). */ | |
232 int n; | |
233 n = strlen (s) - 1; | |
234 CPY (d, s, n); | |
235 } | |
236 | |
237 { | |
238 /* Same as above. */ | |
239 size_t n; | |
240 n = strlen (s) - 1; | |
241 CPY (d, s, n); | |
242 } | |
243 | |
244 { | |
245 size_t n = strlen (s) - strlen (s); | |
246 CPY (d, s, n); /* { dg-warning ".strncpy\[^\n\r\]* destination unchanged after copying no bytes" } */ | |
247 } | |
248 | |
249 { | |
250 /* This use of strncpy is dubious but it's probably not worth | |
251 worrying about (truncation may not actually take place when | |
252 i is the result). It is diagnosed with -Wstringop-overflow | |
253 (although more by accident than by design). | |
254 | |
255 size_t n = i < strlen (s) ? i : strlen (s); | |
256 CPY (d, s, n); | |
257 */ | |
258 } | |
259 } | |
260 | |
261 | |
262 /* Verify strncpy warnings for arrays of known bounds. */ | |
263 | |
264 void test_strncpy_array (Dest *pd, int i, const char* s) | |
265 { | |
266 #undef CPY | |
267 #define CPY(d, s, len) (strncpy ((d), (s), (len)), sink (d)) | |
268 | |
269 CPY (dst7, s, 7); /* { dg-warning "specified bound 7 equals destination size" } */ | |
270 CPY (dst7, s, sizeof dst7); /* { dg-warning "specified bound 7 equals destination size" } */ | |
271 | |
272 CPY (dst2_5[0], s, sizeof dst2_5[0]); /* { dg-warning "specified bound 5 equals destination size" "bug 77293" { xfail *-*-* } } */ | |
273 CPY (dst2_5[1], s, sizeof dst2_5[1]); /* { dg-warning "specified bound 5 equals destination size" } */ | |
274 | |
275 /* Verify that copies that nul-terminate are not diagnosed. */ | |
276 CPY (dst7, "", sizeof dst7); | |
277 CPY (dst7 + 6, "", sizeof dst7 - 6); | |
278 CPY (dst7, "1", sizeof dst7); | |
279 CPY (dst7 + 1, "1", sizeof dst7 - 1); | |
280 CPY (dst7, "123456", sizeof dst7); | |
281 CPY (dst7 + 1, "12345", sizeof dst7 - 1); | |
282 | |
283 CPY (dst7 + i, s, 6); | |
284 CPY (dst7 + i, s, 7); /* { dg-warning "specified bound 7 equals destination size" } */ | |
285 /* The following two calls are diagnosed by -Wstringop-overflow. */ | |
286 CPY (dst7 + i, s, 8); | |
287 CPY (dst7 + i, s, UR (8, 9)); | |
288 | |
289 /* No nul-termination here. */ | |
290 CPY (dst7 + 2, "12345", sizeof dst7 - 2); /* { dg-warning "output truncated before terminating nul copying 5 bytes from a string of the same length" } */ | |
291 | |
292 /* Because strnlen appends as many NULs as necessary to write the specified | |
293 number of byts the following doesn't (necessarily) truncate but rather | |
294 overflow, and so is diagnosed by -Wstringop-overflow. */ | |
295 CPY (dst7, s, 8); | |
296 | |
297 CPY (dst7 + 1, s, 6); /* { dg-warning "specified bound 6 equals destination size" } */ | |
298 CPY (dst7 + 6, s, 1); /* { dg-warning "specified bound 1 equals destination size" } */ | |
299 | |
300 CPY (pd->a5, s, 5); /* { dg-warning "specified bound 5 equals destination size" } */ | |
301 CPY (pd->a5, s, sizeof pd->a5); /* { dg-warning "specified bound 5 equals destination size" } */ | |
302 | |
303 /* The following is not yet handled. */ | |
304 CPY (pd->a5 + i, s, sizeof pd->a5); /* { dg-warning "specified bound 5 equals destination size" "member array" { xfail *-*-* } } */ | |
305 | |
306 /* Verify that a copy that nul-terminates is not diagnosed. */ | |
307 CPY (pd->a5, "1234", sizeof pd->a5); | |
308 | |
309 /* Same above, diagnosed by -Wstringop-overflow. */ | |
310 CPY (pd->a5, s, 6); | |
311 | |
312 /* Exercise destination with attribute "nonstring". */ | |
313 CPY (pd->c3ns, "", 3); | |
314 CPY (pd->c3ns, "", 1); | |
315 /* It could be argued that truncation in the literal case should be | |
316 diagnosed even for non-strings. Using strncpy in this case is | |
317 pointless and should be replaced with memcpy. But it would likely | |
318 be viewed as a false positive. */ | |
319 CPY (pd->c3ns, "12", 1); | |
320 CPY (pd->c3ns, "12", 2); | |
321 CPY (pd->c3ns, "12", 3); | |
322 CPY (pd->c3ns, "123", 3); | |
323 CPY (pd->c3ns, s, 3); | |
324 CPY (pd->c3ns, s, sizeof pd->c3ns); | |
325 | |
326 /* Verify that the idiom of calling strncpy with a bound equal to | |
327 the size of the destination (and thus potentially without NUL- | |
328 terminating it) immediately followed by setting the last element | |
329 of the array to NUL is not diagnosed. */ | |
330 { | |
331 /* This might be better written using memcpy() but it's safe so | |
332 it probably shouldn't be diagnosed. It currently triggers | |
333 a warning because of bug 81704. */ | |
334 strncpy (dst7, "0123456", sizeof dst7); /* { dg-bogus "\\\[-Wstringop-truncation]" "bug 81704" { xfail *-*-* } } */ | |
335 dst7[sizeof dst7 - 1] = '\0'; | |
336 sink (dst7); | |
337 } | |
338 | |
339 { | |
340 const char a[] = "0123456789"; | |
341 strncpy (dst7, a, sizeof dst7); | |
342 dst7[sizeof dst7 - 1] = '\0'; | |
343 sink (dst7); | |
344 } | |
345 | |
346 { | |
347 strncpy (dst7, s, sizeof dst7); | |
348 dst7[sizeof dst7 - 1] = '\0'; | |
349 sink (dst7); | |
350 } | |
351 | |
352 { | |
353 strncpy (pd->a5, "01234", sizeof pd->a5); /* { dg-bogus "\\\[-Wstringop-truncation]" "bug 81704" { xfail *-*-* } } */ | |
354 pd->a5[sizeof pd->a5 - 1] = '\0'; | |
355 sink (pd); | |
356 } | |
357 | |
358 { | |
359 strncpy (pd->a5, s, sizeof pd->a5); | |
360 pd->a5[sizeof pd->a5 - 1] = '\0'; | |
361 sink (pd); | |
362 } | |
363 | |
364 { | |
365 unsigned n = 7; | |
366 char *p = (char*)__builtin_malloc (n); | |
367 strncpy (p, s, n); | |
368 p[n - 1] = '\0'; | |
369 sink (p); | |
370 } | |
371 | |
372 { | |
373 /* This should be diagnosed because the NUL-termination doesn't | |
374 immediately follow the strncpy call (sink may expect pd->a5 | |
375 to be NUL-terminated). */ | |
376 strncpy (pd->a5, s, sizeof pd->a5); /* { dg-warning "specified bound 5 equals destination size" } */ | |
377 sink (pd); | |
378 pd->a5[sizeof pd->a5] = '\0'; | |
379 sink (pd); | |
380 } | |
381 } | |
382 | |
383 typedef struct Flex | |
384 { | |
385 size_t n; | |
386 char a0[0]; | |
387 char ax[]; | |
388 } Flex; | |
389 | |
390 extern char array[]; | |
391 | |
392 /* Verify that no warning is issued for array of unknown bound, flexible | |
393 array members, or zero-length arrays, except when the source is definitely | |
394 truncated. */ | |
395 | |
396 void test_strncpy_flexarray (Flex *pf, const char* s) | |
397 { | |
398 #undef CPY | |
399 #define CPY(d, s, len) (strncpy ((d), (s), (len)), sink (d)) | |
400 | |
401 CPY (array, "12345", 7); | |
402 CPY (array, "12345", 123); | |
403 | |
404 CPY (array, s, 7); | |
405 CPY (array, s, 123); | |
406 | |
407 CPY (pf->a0, s, 1); | |
408 CPY (pf->a0, s, 1234); | |
409 | |
410 CPY (pf->a0, "", 1); | |
411 CPY (pf->a0, "12345", 5); /* { dg-warning "output truncated before terminating nul copying 5 bytes from a string of the same length" } */ | |
412 CPY (pf->a0, "12345", 1234); | |
413 | |
414 CPY (pf->ax, s, 5); | |
415 CPY (pf->ax, s, 12345); | |
416 | |
417 CPY (pf->ax, "1234", 5); | |
418 CPY (pf->ax, "12345", 5); /* { dg-warning "output truncated before terminating nul copying 5 bytes from a string of the same length" } */ | |
419 CPY (pf->ax, "12345", 12345); | |
420 } | |
421 | |
422 /* Verify warnings for dynamically allocated objects. */ | |
423 | |
424 void test_strncpy_alloc (const char* s) | |
425 { | |
426 size_t n = 7; | |
427 char *d = (char *)__builtin_malloc (n); | |
428 | |
429 CPY (d, s, n); /* { dg-warning "specified bound 7 equals destination size" "bug 79016" { xfail *-*-* } } */ | |
430 | |
431 Dest *pd = (Dest *)__builtin_malloc (sizeof *pd * n); | |
432 CPY (pd->a5, s, 5); /* { dg-warning "specified bound 5 equals destination size" } */ | |
433 CPY (pd->a5, s, sizeof pd->a5); /* { dg-warning "specified bound 5 equals destination size" } */ | |
434 } | |
435 | |
436 /* Verify warnings for VLAs. */ | |
437 | |
438 void test_strncpy_vla (unsigned n, const char* s) | |
439 { | |
440 char vla[n]; | |
441 CPY (vla, s, 0); /* { dg-warning ".strncpy\[^\n\r\]* destination unchanged after copying no bytes" } */ | |
442 | |
443 CPY (vla, s, 1); | |
444 CPY (vla, s, 2); | |
445 CPY (vla, s, n); | |
446 | |
447 CPY (vla, "", 0); /* { dg-warning ".strncpy\[^\n\r\]* destination unchanged after copying no bytes" } */ | |
448 CPY (vla, "", 1); | |
449 CPY (vla, S4, 3); /* { dg-warning ".strncpy\[^\n\r\]* output truncated before terminating nul copying 3 bytes from a string of the same length" } */ | |
450 CPY (vla, S4, n); | |
451 } |