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 extern size_t unsigned_value (void)
23 extern volatile size_t unsigned_value_source
;
24 return unsigned_value_source
;
27 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 copying 1 byte from a string of length 1" } */
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 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). */
245 size_t n
= strlen (s
) - strlen (s
);
246 CPY (d
, s
, n
); /* { dg-warning ".strncpy\[^\n\r\]* destination unchanged after copying no bytes" } */
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).
255 size_t n = i < strlen (s) ? i : strlen (s);
262 /* Verify strncpy warnings for arrays of known bounds. */
264 void test_strncpy_array (Dest
*pd
, int i
, const char* s
)
267 #define CPY(d, s, len) (strncpy ((d), (s), (len)), sink (d))
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" } */
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" } */
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);
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));
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" } */
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. */
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" } */
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" } */
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 *-*-* } } */
306 /* Verify that a copy that nul-terminates is not diagnosed. */
307 CPY (pd
->a5
, "1234", sizeof pd
->a5
);
309 /* Same above, diagnosed by -Wstringop-overflow. */
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
);
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. */
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 "truncated" "bug 81704" { xfail *-*-* } } */
335 dst7
[sizeof dst7
- 1] = '\0';
340 const char a
[] = "0123456789";
341 strncpy (dst7
, a
, sizeof dst7
);
342 dst7
[sizeof dst7
- 1] = '\0';
347 strncpy (dst7
, s
, sizeof dst7
);
348 dst7
[sizeof dst7
- 1] = '\0';
353 strncpy (pd
->a5
, "01234", sizeof pd
->a5
); /* { dg-bogus "truncated" "bug 81704" { xfail *-*-* } } */
354 pd
->a5
[sizeof pd
->a5
- 1] = '\0';
359 strncpy (pd
->a5
, s
, sizeof pd
->a5
);
360 pd
->a5
[sizeof pd
->a5
- 1] = '\0';
366 char *p
= (char*)__builtin_malloc (n
);
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" } */
378 pd
->a5
[sizeof pd
->a5
] = '\0';
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
396 void test_strncpy_flexarray (Flex
*pf
, const char* s
)
399 #define CPY(d, s, len) (strncpy ((d), (s), (len)), sink (d))
401 CPY (array
, "12345", 7);
402 CPY (array
, "12345", 123);
408 CPY (pf
->a0
, s
, 1234);
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);
415 CPY (pf
->ax
, s
, 12345);
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);
422 /* Verify warnings for dynamically allocated objects. */
424 void test_strncpy_alloc (const char* s
)
427 char *d
= (char *)__builtin_malloc (n
);
429 CPY (d
, s
, n
); /* { dg-warning "specified bound 7 equals destination size" "bug 79016" { xfail *-*-* } } */
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" } */
436 /* Verify warnings for VLAs. */
438 void test_strncpy_vla (unsigned n
, const char* s
)
441 CPY (vla
, s
, 0); /* { dg-warning ".strncpy\[^\n\r\]* destination unchanged after copying no bytes" } */
447 CPY (vla
, "", 0); /* { dg-warning ".strncpy\[^\n\r\]* destination unchanged after copying no bytes" } */
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" } */