131
|
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 }
|