1 /* PR middle-end/81117 - Improve buffer overflow checking in strncpy
2    { dg-do compile }
3    { dg-options "-O2 -Wstringop-overflow -Wno-stringop-truncation -ftrack-macro-expansion=0" } */
4 
5 typedef __SIZE_TYPE__ size_t;
6 
7 #if __cplusplus
8 extern "C" {
9 #endif
10 
11 size_t strlen (const char*);
12 char* strncat (char*, const char*, size_t);
13 char* strncpy (char*, const char*, size_t);
14 #if __cplusplus
15 }
16 #endif
17 
18 const char ar[] = "123";
19 
20 void test_strncat (char **d, const char* s, int i)
21 {
22   /* Use a fresh pointer for each test to prevent the optimizer from
23      eliminating redundant writes into the same destination.  Avoid
24      calling functions like sink() on the result that would have to
25      be assumed to change the source string by the alias oracle.  */
26 #define T(d, s, len) strncat (*d++, (s), (len))
27 
28   T (d, "",    0);
29   T (d, "",    1);
30   T (d, "",    2);
31   T (d, "",    3);
32   T (d, "123", 0);
33   /* The following two calls truncate the copy and are diagnosed
test_string_literal(char * dst)34      by -Wstringop-truncation but there is evidence of overflow so
35      they're not diagnosed by -Wstringop-overflow.  */
36   T (d, "123", 1);
37   T (d, "123", 2);
38 
39   T (d, "123", 3);                /* { dg-warning ".strncat\[^\n\r\]* specified bound 3 equals source length" } */
40   T (d, "123", 4);
41   T (d, "123", 9);
42 
43   T (d, s, strlen (s));           /* { dg-warning ".strncat\[^\n\r\]* specified bound depends on the length of the source argument" } */
44   T (d, s, strlen (s) + 1);       /* { dg-warning ".strncat\[^\n\r\]* specified bound depends on the length of the source argument" } */
45   /* The following could also be diagnosed by -Wstringop-truncation
46      (with some effort to distinguish the pattern from others like
47      the one above.  */
48   T (d, s, strlen (s) - 1);       /* { dg-warning ".strncat\[^\n\r\]* specified bound depends on the length of the source argument" } */
49   T (d, s, strlen (s) - i);       /* { dg-warning ".strncat\[^\n\r\]* specified bound depends on the length of the source argument" } */
50 
51   /* The following is dubious but not necessarily a smoking gun.  */
52   T (d, s, strlen (s) - strlen (s));
53 
54   {
55     signed char n = strlen (s);   /* { dg-message "length computed here" } */
56     T (d, s, n);                  /* { dg-warning ".strncat\[^\n\r\]* specified bound depends on the length of the source argument" } */
57   }
58 
59   {
60     short n = strlen (s);         /* { dg-message "length computed here" } */
61     T (d, s, n);                  /* { dg-warning ".strncat\[^\n\r\]* specified bound depends on the length of the source argument" } */
62   }
63 
64   {
65     int n = strlen (s);           /* { dg-message "length computed here" } */
test_char_array(char * dst)66     T (d, s, n);                  /* { dg-warning ".strncat\[^\n\r\]* specified bound depends on the length of the source argument" } */
67   }
68 
69   {
70     unsigned n = strlen (s);      /* { dg-message "length computed here" } */
71     T (d, s, n);                  /* { dg-warning ".strncat\[^\n\r\]* specified bound depends on the length of the source argument" } */
72   }
73 
74   {
75     size_t n;
76     n = strlen (s);               /* { dg-message "length computed here" } */
77     T (d, s, n);                  /* { dg-warning ".strncat\[^\n\r\]* specified bound depends on the length of the source argument" } */
78   }
79 
80   {
81     size_t n;
82     n = strlen (s) - 1;           /* { dg-message "length computed here" } */
83     T (d, s, n);                  /* { dg-message ".strncat\[^\n\r\]* specified bound depends on the length of the source argument" } */
84   }
85 
86   {
87     /* This doesn't overflow so iit should not be diagnosed.  */
88     size_t n = strlen (s) - strlen (s);
89     T (d, s, n);
90   }
test_char_array_chk(char * dst)91 
92   {
93     size_t n = i < strlen (s) ? i : strlen (s);   /* { dg-message "length computed here" } */
94     T (d, s, n);                  /* { dg-message ".strncat\[^\n\r\]* specified bound depends on the length of the source argument" } */
95   }
96 }
97 
98 
99 void test_strncpy (char **d, const char* s, int i)
100 {
101 #undef T
102 #define T(d, s, len) strncpy (*d++, (s), (len))
103 
104   T (d, "",    0);
105   T (d, "",    1);
106   T (d, "",    2);
107   T (d, "",    3);
108   T (d, "123", 0);
109   T (d, "123", 1);
110   T (d, "123", 2);
111   T (d, "123", 3);
112   T (d, "123", 4);
test_string_literal_chk(char * dst)113   T (d, "123", 9);
114 
115   T (d, "123", sizeof "123");
116   T (d, ar, sizeof ar);
117 
118   /* There is no overflow in the following calls but they are diagnosed
119      by -Wstringop-truncation.  Verify that they aren'y also diagnosed
120      by -Wstringop-overflow.  */
121   T (d, s, strlen (s));
122 
123   {
124     int n = strlen (s);
125     T (d, s, n);
126   }
127 
128   {
129     unsigned n = strlen (s);
130     T (d, s, n);
131   }
132 
133   {
134     size_t n;
135     n = strlen (s);
136     T (d, s, n);
137   }
138 
139   {
140     size_t n;
141     n = strlen (s) - 1;
142     T (d, s, n);
143   }
144 
145   {
146     /* This is diagnosed by -Wstringop-truncation.  Verify that it isn't
147        also diagnosed by -Wstringop-overflow.  */
148     size_t n = strlen (s) - strlen (s);
149     T (d, s, n);
150   }
151 
152   {
153     /* This use of strncpy is certainly dubious and it could well be
154        diagnosed by -Wstringop-truncation but it isn't.  */
155     size_t n = i < strlen (s) ? i : strlen (s);   /* { dg-message "length computed here" "note" { xfail *-*-* } } */
156     T (d, s, n);                  /* { dg-message ".strncpy\[^\n\r]* specified bound depends on the length of the source argument" "pr?????" { xfail *-*-* } } */
157   }
158 }
159