| /* Test to exercise attribute "nonstring". |
| { dg-do compile } |
| { dg-options "-O2 -Wattributes -Wstringop-truncation -ftrack-macro-expansion=0" } */ |
| |
| #define ATTR(list) __attribute__ (list) |
| #define NONSTR ATTR ((nonstring)) |
| #define strncpy(d, s, n) (__builtin_strncpy ((d), (s), (n)), sink (d)) |
| |
| void sink (void*); |
| |
| /* Global string with an unknown bound. */ |
| extern char gsx[]; |
| |
| /* Global string with an known bound. */ |
| extern char gs3[3]; |
| |
| /* Global non-strings with an unknown bound. */ |
| extern NONSTR char gax_1[]; |
| extern char NONSTR gax_2[]; |
| extern char gax_3[] NONSTR; |
| |
| /* Global non-strings with a known bound. */ |
| NONSTR char gns3[3]; |
| char NONSTR gns4[4]; |
| char gns5[5] NONSTR; |
| |
| /* Global string pointer. */ |
| extern char *ps_1; |
| |
| /* Global non-string pointers. */ |
| extern NONSTR char *pns_1; |
| extern char* NONSTR pns_2; |
| extern char *pns_3 NONSTR; |
| |
| struct MemArrays |
| { |
| NONSTR char ma3[3]; |
| char NONSTR ma4[4]; |
| char ma5[5] NONSTR; |
| char max[] NONSTR; |
| }; |
| |
| |
| void test_array (const char *s, unsigned n) |
| { |
| const char s7[] = "1234567"; |
| |
| strncpy (gs3, "", 0); /* { dg-warning "destination unchanged after copying no bytes" } */ |
| strncpy (gs3, "a", 1); /* { dg-warning "output truncated before terminating nul copying 1 byte from a string of the same length" } */ |
| strncpy (gs3, "a", 2); |
| strncpy (gs3, "a", 3); |
| strncpy (gs3, "ab", 3); |
| strncpy (gs3, "abc", 3); /* { dg-warning "output truncated before terminating nul copying 3 bytes from a string of the same length" } */ |
| |
| /* It might perhaps be helpful to diagnose certain truncation even |
| for non-strings. Then again, since the destination has been |
| explicitly annotated as non-string, it might be viewed as a false |
| positive. A valid use case seen in Glibc goes something like this: |
| |
| #if FOO |
| # define S "1234" |
| #else |
| # define S "12345678" |
| #endif |
| |
| strncpy (d, S, 8); |
| */ |
| strncpy (gax_3, s7, 3); |
| |
| strncpy (gax_1, "a", 1); |
| strncpy (gax_2, "ab", 2); |
| strncpy (gax_3, "abc", 3); |
| strncpy (gax_3, s7, 3); |
| |
| strncpy (gax_1, s, 1); |
| strncpy (gax_2, s, 1); |
| strncpy (gax_3, s, 1); |
| |
| strncpy (gax_1, s, n); |
| strncpy (gax_2, s, n); |
| strncpy (gax_3, s, n); |
| } |
| |
| |
| void test_pointer (const char *s, unsigned n) |
| { |
| const char s7[] = "1234567"; |
| |
| strncpy (pns_1, "a", 1); |
| strncpy (pns_2, "ab", 2); |
| strncpy (pns_3, "abc", 3); |
| strncpy (pns_3, s7, 3); |
| |
| strncpy (pns_1, s, 1); |
| strncpy (pns_2, s, 1); |
| strncpy (pns_3, s, 1); |
| |
| strncpy (pns_1, s, n); |
| strncpy (pns_2, s, n); |
| strncpy (pns_3, s, n); |
| } |
| |
| |
| void test_member_array (struct MemArrays *p, const char *s, unsigned n) |
| { |
| const char s7[] = "1234567"; |
| |
| strncpy (p->ma3, "", 0); |
| strncpy (p->ma3, "a", 1); |
| strncpy (p->ma4, "ab", 2); |
| strncpy (p->ma5, "abc", 3); |
| strncpy (p->max, "abcd", 4); |
| strncpy (p->max, s7, 5); |
| |
| strncpy (p->ma3, s, 1); |
| strncpy (p->ma4, s, 1); |
| strncpy (p->ma5, s, 1); |
| strncpy (p->max, s, 1); |
| |
| strncpy (p->ma3, s7, n); |
| strncpy (p->ma4, s7, n); |
| strncpy (p->ma5, s7, n); |
| strncpy (p->max, s7, n); |
| } |