1 /* PR middle-end/81117 - Improve buffer overflow checking in strncpy
3 { dg-options "-O2 -Wstringop-truncation -Wno-stringop-overflow -ftrack-macro-expansion=0" }
4 { dg-require-effective-target alloca } */
7 typedef __SIZE_TYPE__
size_t;
13 size_t strlen (const char*);
14 char* strncat (char*, const char*, size_t);
15 char* strncpy (char*, const char*, size_t);
21 static size_t unsigned_value (void)
23 extern volatile size_t unsigned_value_source
;
24 return unsigned_value_source
;
27 static size_t unsigned_range (size_t min
, size_t max
)
29 size_t val
= unsigned_value ();
30 return val
< min
|| max
< val
? min
: val
;
33 #define UR(min, max) unsigned_range (min, max)
38 const char a4
[] = "123";
40 #define CHOOSE(a, b) (unsigned_value () & 1 ? a : b)
47 char c3ns
[3] __attribute__ ((nonstring
));
53 /* Verify strncat warnings for arrays of known bounds. */
55 void test_strncat_array (Dest
*pd
)
57 #define CAT(d, s, len) (strncat ((d), (s), (len)), sink (d))
59 CAT (dst7
, S4
, 2); /* { dg-warning "output truncated copying 2 bytes from a string of length 3" } */
61 CAT (dst7
, a4
, 1); /* { dg-warning "output truncated copying 1 byte from a string of length 3" } */
63 /* There is no truncation here but possible overflow so these
64 are diagnosed by -Wstringop-overflow:
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" } */
73 /* Verify strncat warnings for arrays of known bounds and a non-const
74 character count in some range. */
76 void test_strncat_array_range (Dest
*pd
)
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" } */
83 CAT (dst7
, S4
, UR (0, 7));
84 CAT (dst7
, S4
, UR (1, 7));
85 CAT (dst7
, S4
, UR (6, 7));
87 CAT (dst7
, S4
, UR (0, 99));
89 CAT (dst7
, S4
, UR (0, 99));
92 /* Verify strncat warnings for arrays of unknown bounds. */
94 void test_strncat_vla (char *d
, unsigned n
)
96 CAT (d
, S4
, 2); /* { dg-warning "output truncated copying 2 bytes from a string of length 3" } */
99 CAT (d
, a4
, 2); /* { dg-warning "output truncated copying 2 bytes from a string of length 3" } */
101 /* There is no truncation here but possible overflow so these
102 are diagnosed by -Wstringop-overflow:
110 CAT (vla
, S4
, 2); /* { dg-warning "output truncated copying 2 bytes from a string of length 3" } */
115 CAT (vla
, a4
, 2); /* { dg-warning "output truncated copying 2 bytes from a string of length 3" } */
125 /* There is no truncation here but possible overflow so these
126 are diagnosed by -Wstringop-overflow:
132 /* Verify strncpy warnings with at least one pointer to an object
133 or string of unknown size (destination) or length (source). */
135 void test_strncpy_ptr (char *d
, const char* s
, const char *t
, int i
)
137 #define CPY(d, s, len) (strncpy ((d), (s), (len)), sink (d))
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" } */
147 /* This could be safe. */
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" } */
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" } */
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" } */
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
);
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);
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" } */
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);
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);
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" } */
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" } */
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" } */
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" } */
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" } */
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" } */
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" } */
229 /* The following truncates the terminating nul. The warning should
230 say that but doesn't. */
233 CPY (d
, s
, n
); /* { dg-warning "\\\[-Wstringop-truncation" } */
240 CPY (d
, s
, n
); /* { dg-warning "\\\[-Wstringop-truncation" } */
244 size_t n
= strlen (s
) - strlen (s
);
245 CPY (d
, s
, n
); /* { dg-warning ".strncpy\[^\n\r\]* destination unchanged after copying no bytes" } */
249 /* This use of strncpy is dubious but it's probably not worth
250 worrying about (truncation may not actually take place when
251 i is the result). It is diagnosed with -Wstringop-overflow
252 (although more by accident than by design).
254 size_t n = i < strlen (s) ? i : strlen (s);
261 /* Verify strncpy warnings for arrays of known bounds. */
263 void test_strncpy_array (Dest
*pd
, int i
, const char* s
)
266 #define CPY(d, s, len) (strncpy ((d), (s), (len)), sink (d))
268 CPY (dst7
, s
, 7); /* { dg-warning "specified bound 7 equals destination size" } */
269 CPY (dst7
, s
, sizeof dst7
); /* { dg-warning "specified bound 7 equals destination size" } */
271 CPY (dst2_5
[0], s
, sizeof dst2_5
[0]); /* { dg-warning "specified bound 5 equals destination size" "bug 77293" } */
272 CPY (dst2_5
[1], s
, sizeof dst2_5
[1]); /* { dg-warning "specified bound 5 equals destination size" } */
274 /* Verify that copies that nul-terminate are not diagnosed. */
275 CPY (dst7
, "", sizeof dst7
);
276 CPY (dst7
+ 6, "", sizeof dst7
- 6);
277 CPY (dst7
, "1", sizeof dst7
);
278 CPY (dst7
+ 1, "1", sizeof dst7
- 1);
279 CPY (dst7
, "123456", sizeof dst7
);
280 CPY (dst7
+ 1, "12345", sizeof dst7
- 1);
282 CPY (dst7
+ i
, s
, 6);
283 CPY (dst7
+ i
, s
, 7); /* { dg-warning "specified bound 7 equals destination size" } */
284 /* The following two calls are diagnosed by -Wstringop-overflow. */
285 CPY (dst7
+ i
, s
, 8);
286 CPY (dst7
+ i
, s
, UR (8, 9));
288 /* No nul-termination here. */
289 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 /* Because strnlen appends as many NULs as necessary to write the specified
292 number of byts the following doesn't (necessarily) truncate but rather
293 overflow, and so is diagnosed by -Wstringop-overflow. */
296 CPY (dst7
+ 1, s
, 6); /* { dg-warning "specified bound 6 equals destination size" } */
297 CPY (dst7
+ 6, s
, 1); /* { dg-warning "specified bound 1 equals destination size" } */
299 CPY (pd
->a5
, s
, 5); /* { dg-warning "specified bound 5 equals destination size" } */
300 CPY (pd
->a5
, s
, sizeof pd
->a5
); /* { dg-warning "specified bound 5 equals destination size" } */
302 CPY (pd
->a5
+ i
, s
, sizeof pd
->a5
); /* { dg-warning "specified bound 5 equals destination size" "member array" } */
304 /* Verify that a copy that nul-terminates is not diagnosed. */
305 CPY (pd
->a5
, "1234", sizeof pd
->a5
);
307 /* Same above, diagnosed by -Wstringop-overflow. */
310 /* Exercise destination with attribute "nonstring". */
311 CPY (pd
->c3ns
, "", 3);
312 CPY (pd
->c3ns
, "", 1);
313 /* It could be argued that truncation in the literal case should be
314 diagnosed even for non-strings. Using strncpy in this case is
315 pointless and should be replaced with memcpy. But it would likely
316 be viewed as a false positive. */
317 CPY (pd
->c3ns
, "12", 1);
318 CPY (pd
->c3ns
, "12", 2);
319 CPY (pd
->c3ns
, "12", 3);
320 CPY (pd
->c3ns
, "123", 3);
321 CPY (pd
->c3ns
, s
, 3);
322 CPY (pd
->c3ns
, s
, sizeof pd
->c3ns
);
324 /* Verify that the idiom of calling strncpy with a bound equal to
325 the size of the destination (and thus potentially without NUL-
326 terminating it) immediately followed by setting the last element
327 of the array to NUL is not diagnosed. */
329 /* This might be better written using memcpy() but it's safe so
330 it shouldn't be diagnosed. */
331 strncpy (dst7
, "0123456", sizeof dst7
); /* { dg-bogus "\\\[-Wstringop-truncation]" } */
332 dst7
[sizeof dst7
- 1] = '\0';
337 const char a
[] = "0123456789";
338 strncpy (dst7
, a
, sizeof dst7
);
339 dst7
[sizeof dst7
- 1] = '\0';
344 strncpy (dst7
, s
, sizeof dst7
);
345 dst7
[sizeof dst7
- 1] = '\0';
350 strncpy (pd
->a5
, "01234", sizeof pd
->a5
); /* { dg-bogus "\\\[-Wstringop-truncation]" } */
351 pd
->a5
[sizeof pd
->a5
- 1] = '\0';
356 strncpy (pd
->a5
, s
, sizeof pd
->a5
);
357 pd
->a5
[sizeof pd
->a5
- 1] = '\0';
363 char *p
= (char*)__builtin_malloc (n
);
370 /* This should be diagnosed because the NUL-termination doesn't
371 immediately follow the strncpy call (sink may expect pd->a5
372 to be NUL-terminated). */
373 strncpy (pd
->a5
, s
, sizeof pd
->a5
); /* { dg-warning "specified bound 5 equals destination size" } */
375 pd
->a5
[sizeof pd
->a5
] = '\0';
389 /* Verify that no warning is issued for array of unknown bound, flexible
390 array members, or zero-length arrays, except when the source is definitely
393 void test_strncpy_flexarray (Flex
*pf
, const char* s
)
396 #define CPY(d, s, len) (strncpy ((d), (s), (len)), sink (d))
398 CPY (array
, "12345", 7);
399 CPY (array
, "12345", 123);
405 CPY (pf
->a0
, s
, 1234);
408 CPY (pf
->a0
, "12345", 5); /* { dg-warning "output truncated before terminating nul copying 5 bytes from a string of the same length" } */
409 CPY (pf
->a0
, "12345", 1234);
412 CPY (pf
->ax
, s
, 12345);
414 CPY (pf
->ax
, "1234", 5);
415 CPY (pf
->ax
, "12345", 5); /* { dg-warning "output truncated before terminating nul copying 5 bytes from a string of the same length" } */
416 CPY (pf
->ax
, "12345", 12345);
419 /* Verify warnings for dynamically allocated objects. */
421 void test_strncpy_alloc (const char* s
)
424 char *d
= (char *)__builtin_malloc (n
);
426 CPY (d
, s
, n
); /* { dg-warning "specified bound 7 equals destination size" } */
428 Dest
*pd
= (Dest
*)__builtin_malloc (sizeof *pd
* n
);
429 CPY (pd
->a5
, s
, 5); /* { dg-warning "specified bound 5 equals destination size" } */
430 CPY (pd
->a5
, s
, sizeof pd
->a5
); /* { dg-warning "specified bound 5 equals destination size" } */
433 /* Verify warnings for VLAs. */
435 void test_strncpy_vla (unsigned n
, const char* s
)
438 CPY (vla
, s
, 0); /* { dg-warning ".strncpy\[^\n\r\]* destination unchanged after copying no bytes" } */
444 CPY (vla
, "", 0); /* { dg-warning ".strncpy\[^\n\r\]* destination unchanged after copying no bytes" } */
446 CPY (vla
, S4
, 3); /* { dg-warning ".strncpy\[^\n\r\]* output truncated before terminating nul copying 3 bytes from a string of the same length" } */