1 | /* Test strlcat functions. |
2 | Copyright (C) 2023-2024 Free Software Foundation, Inc. |
3 | |
4 | The GNU C Library is free software; you can redistribute it and/or |
5 | modify it under the terms of the GNU Lesser General Public |
6 | License as published by the Free Software Foundation; either |
7 | version 2.1 of the License, or (at your option) any later version. |
8 | |
9 | The GNU C Library is distributed in the hope that it will be useful, |
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | Lesser General Public License for more details. |
13 | |
14 | You should have received a copy of the GNU Lesser General Public |
15 | License along with the GNU C Library; if not, see |
16 | <https://www.gnu.org/licenses/>. */ |
17 | |
18 | #define TEST_MAIN |
19 | #ifndef WIDE |
20 | # define TEST_NAME "strlcat" |
21 | #else |
22 | # define TEST_NAME "wcslcat" |
23 | #endif /* WIDE */ |
24 | #include "test-string.h" |
25 | |
26 | #ifdef WIDE |
27 | # include <wchar.h> |
28 | # define BIG_CHAR WCHAR_MAX |
29 | # define CHAR wchar_t |
30 | # define MEMCMP wmemcmp |
31 | # define MEMCPY wmemcpy |
32 | # define MEMSET wmemset |
33 | # define SIMPLE_STRLCAT simple_wcslcat |
34 | # define SMALL_CHAR 1273 |
35 | # define STRLCAT wcslcat |
36 | # define STRLEN wcslen |
37 | # define STRNLEN wcsnlen |
38 | # define UCHAR wchar_t |
39 | #else |
40 | # define BIG_CHAR CHAR_MAX |
41 | # define CHAR char |
42 | # define MEMCMP memcmp |
43 | # define MEMCPY memcpy |
44 | # define MEMSET memset |
45 | # define SMALL_CHAR 127 |
46 | # define SIMPLE_STRLCAT simple_strlcat |
47 | # define STRLCAT strlcat |
48 | # define STRLEN strlen |
49 | # define STRNLEN strnlen |
50 | # define UCHAR unsigned char |
51 | #endif /* !WIDE */ |
52 | |
53 | /* Naive implementation to verify results. */ |
54 | size_t |
55 | SIMPLE_STRLCAT (CHAR *dst, const CHAR *src, size_t n) |
56 | { |
57 | size_t src_length = STRLEN (src); |
58 | |
59 | if (n == 0) |
60 | return src_length; |
61 | |
62 | size_t dst_length = STRNLEN (dst, n); |
63 | |
64 | if (dst_length != n) |
65 | { |
66 | size_t to_copy = n - dst_length - 1; |
67 | |
68 | if (to_copy > src_length) |
69 | to_copy = src_length; |
70 | |
71 | CHAR *target = dst + dst_length; |
72 | MEMCPY (target, src, to_copy); |
73 | target[to_copy] = '\0'; |
74 | } |
75 | return dst_length + src_length; |
76 | } |
77 | |
78 | IMPL (SIMPLE_STRLCAT, 0) |
79 | IMPL (STRLCAT, 1) |
80 | |
81 | typedef size_t (*proto_t) (CHAR *, const CHAR *, size_t); |
82 | |
83 | static void |
84 | do_one_test (impl_t *impl, CHAR *dst, const CHAR *src, |
85 | size_t n) |
86 | { |
87 | size_t exp_ret, func_ret; |
88 | size_t src_length = STRLEN (src); |
89 | size_t dst_length = STRNLEN (dst, n); |
90 | |
91 | if (n == 0) |
92 | exp_ret = src_length; |
93 | else |
94 | exp_ret = src_length + dst_length; |
95 | |
96 | func_ret = CALL (impl, dst, src, n); |
97 | if (exp_ret != func_ret) |
98 | { |
99 | error (status: 0, errnum: 0, format: "Wrong result in function %s %zd != %zd" , impl->name, |
100 | exp_ret, func_ret); |
101 | ret = 1; |
102 | return; |
103 | } |
104 | |
105 | if (dst_length != n) |
106 | { |
107 | size_t to_copy = n - dst_length - 1; |
108 | if (to_copy > src_length) |
109 | to_copy = src_length; |
110 | if (MEMCMP (dst + dst_length, src, to_copy) != 0) |
111 | { |
112 | error (status: 0, errnum: 0, format: "Incorrect concatenation in function %s" , |
113 | impl->name); |
114 | ret = 1; |
115 | return; |
116 | } |
117 | if (dst[dst_length + to_copy] != '\0') |
118 | { |
119 | error (status: 0, errnum: 0, format: "There is no zero in the end of output string in %s" , |
120 | impl->name); |
121 | ret = 1; |
122 | return; |
123 | } |
124 | } |
125 | return; |
126 | } |
127 | |
128 | static void |
129 | do_test (size_t align1, size_t align2, size_t len1, size_t len2, |
130 | size_t n, int max_char) |
131 | { |
132 | size_t i; |
133 | CHAR *s1, *s2; |
134 | |
135 | align1 &= 7; |
136 | if ((align1 + len1) * sizeof (CHAR) >= page_size) |
137 | return; |
138 | if ((align1 + n) * sizeof (CHAR) > page_size) |
139 | return; |
140 | align2 &= 7; |
141 | if ((align2 + len1 + len2) * sizeof (CHAR) >= page_size) |
142 | return; |
143 | if ((align2 + len1 + n) * sizeof (CHAR) > page_size) |
144 | return; |
145 | s1 = (CHAR *) (buf1) + align1; |
146 | s2 = (CHAR *) (buf2) + align2; |
147 | |
148 | for (i = 0; i < len1; ++i) |
149 | s1[i] = 32 + 23 * i % (max_char - 32); |
150 | s1[len1] = '\0'; |
151 | |
152 | FOR_EACH_IMPL (impl, 0) |
153 | { |
154 | |
155 | for (i = 0; i < len2; i++) |
156 | s2[i] = 32 + 23 * i % (max_char - 32); |
157 | s2[len2] = '\0'; |
158 | |
159 | do_one_test (impl, dst: s2, src: s1, n); |
160 | } |
161 | } |
162 | |
163 | static void |
164 | do_overflow_tests (void) |
165 | { |
166 | size_t i, j, len; |
167 | const size_t one = 1; |
168 | CHAR *s1, *s2; |
169 | uintptr_t s1_addr; |
170 | s1 = (CHAR *) buf1; |
171 | s2 = (CHAR *) buf2; |
172 | s1_addr = (uintptr_t)s1; |
173 | for (j = 0; j < 200; ++j) |
174 | s2[j] = 32 + 23 * j % (BIG_CHAR - 32); |
175 | s2[200] = 0; |
176 | for (i = 0; i < 750; ++i) { |
177 | for (j = 0; j < i; ++j) |
178 | s1[j] = 32 + 23 * j % (BIG_CHAR - 32); |
179 | s1[i] = '\0'; |
180 | |
181 | FOR_EACH_IMPL (impl, 0) |
182 | { |
183 | s2[200] = '\0'; |
184 | do_one_test (impl, dst: s2, src: s1, SIZE_MAX - i); |
185 | s2[200] = '\0'; |
186 | do_one_test (impl, dst: s2, src: s1, n: i - s1_addr); |
187 | s2[200] = '\0'; |
188 | do_one_test (impl, dst: s2, src: s1, n: -s1_addr - i); |
189 | s2[200] = '\0'; |
190 | do_one_test (impl, dst: s2, src: s1, SIZE_MAX - s1_addr - i); |
191 | s2[200] = '\0'; |
192 | do_one_test (impl, dst: s2, src: s1, SIZE_MAX - s1_addr + i); |
193 | } |
194 | |
195 | len = 0; |
196 | for (j = 8 * sizeof(size_t) - 1; j ; --j) |
197 | { |
198 | len |= one << j; |
199 | FOR_EACH_IMPL (impl, 0) |
200 | { |
201 | s2[200] = '\0'; |
202 | do_one_test (impl, dst: s2, src: s1, n: len - i); |
203 | s2[200] = '\0'; |
204 | do_one_test (impl, dst: s2, src: s1, n: len + i); |
205 | s2[200] = '\0'; |
206 | do_one_test (impl, dst: s2, src: s1, n: len - s1_addr - i); |
207 | s2[200] = '\0'; |
208 | do_one_test (impl, dst: s2, src: s1, n: len - s1_addr + i); |
209 | |
210 | s2[200] = '\0'; |
211 | do_one_test (impl, dst: s2, src: s1, n: ~len - i); |
212 | s2[200] = '\0'; |
213 | do_one_test (impl, dst: s2, src: s1, n: ~len + i); |
214 | s2[200] = '\0'; |
215 | do_one_test (impl, dst: s2, src: s1, n: ~len - s1_addr - i); |
216 | s2[200] = '\0'; |
217 | do_one_test (impl, dst: s2, src: s1, n: ~len - s1_addr + i); |
218 | } |
219 | } |
220 | } |
221 | } |
222 | |
223 | static void |
224 | do_random_tests (void) |
225 | { |
226 | size_t i, j, n, align1, align2, len1, len2, N; |
227 | UCHAR *p1 = (UCHAR *) (buf1 + page_size) - 1024; |
228 | UCHAR *p2 = (UCHAR *) (buf2 + page_size) - 1024; |
229 | UCHAR *p3 = (UCHAR *) buf1; |
230 | size_t res; |
231 | fprintf (stdout, "Number of iterations in random test = %zd\n" , |
232 | ITERATIONS); |
233 | for (n = 0; n < ITERATIONS; n++) |
234 | { |
235 | N = random () & 1023; |
236 | align1 = random () & 255; |
237 | align2 = random () & 255; |
238 | len1 = random () & 255; |
239 | len2 = random () & 255; |
240 | |
241 | for (i = 0; i < len1; i++) |
242 | { |
243 | p1[align1 + i] = random () & BIG_CHAR; |
244 | if (!p1[align1 + i]) |
245 | p1[align1 + i] = (random () & SMALL_CHAR) + 3; |
246 | } |
247 | p1[align1 + len1] = 0; |
248 | |
249 | for (i = 0; i < len2; i++) |
250 | { |
251 | p3[i] = random () & BIG_CHAR; |
252 | if (!p3[i]) |
253 | p3[i] = (random () & SMALL_CHAR) + 3; |
254 | } |
255 | p3[len2] = 0; |
256 | |
257 | size_t exp_ret; |
258 | size_t src_length = STRLEN ((CHAR *) (p1 + align1)); |
259 | size_t dst_length = STRNLEN ((CHAR *) (p3), N); |
260 | |
261 | if (N == 0) |
262 | exp_ret = src_length; |
263 | else |
264 | exp_ret = src_length + dst_length; |
265 | |
266 | FOR_EACH_IMPL (impl, 1) |
267 | { |
268 | MEMSET (p2 - 64, '\1', align2 + 64); |
269 | MEMSET (p2 + align2 + len2 + 1, '\1', 1024 - align2 - len2 - 1); |
270 | MEMCPY (p2 + align2, p3, len2 + 1); |
271 | res = CALL (impl, (CHAR *) (p2 + align2), |
272 | (CHAR *) (p1 + align1), N); |
273 | if (res != exp_ret) |
274 | { |
275 | error (status: 0, errnum: 0, format: "Iteration %zd - wrong result in function %s " |
276 | "(%zd, %zd, %zd, %zd, %zd) %zd != %zd" , |
277 | n, impl->name, align1, align2, len1, len2, N, |
278 | res, exp_ret); |
279 | ret = 1; |
280 | } |
281 | |
282 | for (j = 0; j < align2 + 64; ++j) |
283 | { |
284 | if (p2[j - 64] != '\1') |
285 | { |
286 | error (status: 0, errnum: 0, format: "Iteration %zd - garbage before dst, %s " |
287 | "%zd, %zd, %zd, %zd, %zd)" , |
288 | n, impl->name, align1, align2, len1, len2, N); |
289 | ret = 1; |
290 | break; |
291 | } |
292 | } |
293 | if (MEMCMP (p2 + align2, p3, len2)) |
294 | { |
295 | error (status: 0, errnum: 0, format: "Iteration %zd - garbage in string before, %s " |
296 | "(%zd, %zd, %zd, %zd, %zd)" , |
297 | n, impl->name, align1, align2, len1, len2, N); |
298 | ret = 1; |
299 | } |
300 | if (N > len2 + 1) |
301 | { |
302 | j = (N - len2 - 1 >= len1) ? len1 : N - len2 - 1; |
303 | if (MEMCMP (p2 + align2 + len2, p1 + align1, j)) |
304 | { |
305 | error (status: 0, errnum: 0, format: "Iteration %zd - different strings, %s " |
306 | "(%zd, %zd, %zd, %zd)" , n, impl->name, align1, |
307 | align2, len2, j); |
308 | ret = 1; |
309 | } |
310 | |
311 | if (p2[align2 + len2 + j] != '\0') |
312 | { |
313 | error (status: 0, errnum: 0, format: "Iteration %zd - there is no zero at the " |
314 | "end of output string, %s (%zd, %zd, %zd, %zd, %zd)" , |
315 | n, impl->name, align1, align2, len1, len2, N); |
316 | ret = 1; |
317 | } |
318 | |
319 | for (j = j + align2 + len2 + 1; j < 1024; ++j) |
320 | { |
321 | if (p2[j] != '\1') |
322 | { |
323 | error (status: 0, errnum: 0, format: "Iteration %zd - garbage after, %s " |
324 | "(%zd, %zd, %zd, %zd, %zd)" , |
325 | n, impl->name, align1, align2, len1, len2, N); |
326 | ret = 1; |
327 | break; |
328 | } |
329 | } |
330 | } |
331 | else |
332 | { |
333 | if (p2[align2 + len2] != '\0') |
334 | { |
335 | error (status: 0, errnum: 0, format: "Iteration %zd - destination modified, %s " |
336 | "(%zd, %zd, %zd, %zd, %zd)" , |
337 | n, impl->name, align1, align2, len1, len2, N); |
338 | ret = 1; |
339 | } |
340 | } |
341 | } |
342 | } |
343 | } |
344 | |
345 | int |
346 | test_main (void) |
347 | { |
348 | size_t i, n; |
349 | |
350 | test_init (); |
351 | |
352 | printf (format: "%28s" , "" ); |
353 | FOR_EACH_IMPL (impl, 0) |
354 | printf (format: "\t%s" , impl->name); |
355 | putchar (c: '\n'); |
356 | |
357 | for (n = 2; n <= 2048; n*=4) |
358 | { |
359 | do_test (align1: 0, align2: 2, len1: 2, len2: 2, n, SMALL_CHAR); |
360 | do_test (align1: 0, align2: 0, len1: 4, len2: 4, n, SMALL_CHAR); |
361 | do_test (align1: 4, align2: 0, len1: 4, len2: 4, n, BIG_CHAR); |
362 | do_test (align1: 0, align2: 0, len1: 8, len2: 8, n, SMALL_CHAR); |
363 | do_test (align1: 0, align2: 8, len1: 8, len2: 8, n, SMALL_CHAR); |
364 | |
365 | do_test (align1: 0, align2: 2, len1: 2, len2: 2, SIZE_MAX, SMALL_CHAR); |
366 | do_test (align1: 0, align2: 0, len1: 4, len2: 4, SIZE_MAX, SMALL_CHAR); |
367 | do_test (align1: 4, align2: 0, len1: 4, len2: 4, SIZE_MAX, BIG_CHAR); |
368 | do_test (align1: 0, align2: 0, len1: 8, len2: 8, SIZE_MAX, SMALL_CHAR); |
369 | do_test (align1: 0, align2: 8, len1: 8, len2: 8, SIZE_MAX, SMALL_CHAR); |
370 | |
371 | for (i = 1; i < 8; ++i) |
372 | { |
373 | do_test (align1: 0, align2: 0, len1: 8 << i, len2: 8 << i, n, SMALL_CHAR); |
374 | do_test (align1: 8 - i, align2: 2 * i, len1: 8 << i, len2: 8 << i, n, SMALL_CHAR); |
375 | do_test (align1: 0, align2: 0, len1: 8 << i, len2: 2 << i, n, SMALL_CHAR); |
376 | do_test (align1: 8 - i, align2: 2 * i, len1: 8 << i, len2: 2 << i, n, SMALL_CHAR); |
377 | |
378 | do_test (align1: 0, align2: 0, len1: 8 << i, len2: 8 << i, SIZE_MAX, SMALL_CHAR); |
379 | do_test (align1: 8 - i, align2: 2 * i, len1: 8 << i, len2: 8 << i, SIZE_MAX, SMALL_CHAR); |
380 | do_test (align1: 0, align2: 0, len1: 8 << i, len2: 2 << i, SIZE_MAX, SMALL_CHAR); |
381 | do_test (align1: 8 - i, align2: 2 * i, len1: 8 << i, len2: 2 << i, SIZE_MAX, SMALL_CHAR); |
382 | } |
383 | |
384 | for (i = 1; i < 8; ++i) |
385 | { |
386 | do_test (align1: i, align2: 2 * i, len1: 8 << i, len2: 1, n, SMALL_CHAR); |
387 | do_test (align1: 2 * i, align2: i, len1: 8 << i, len2: 1, n, BIG_CHAR); |
388 | do_test (align1: i, align2: i, len1: 8 << i, len2: 10, n, SMALL_CHAR); |
389 | |
390 | do_test (align1: i, align2: 2 * i, len1: 8 << i, len2: 1, SIZE_MAX, SMALL_CHAR); |
391 | do_test (align1: 2 * i, align2: i, len1: 8 << i, len2: 1, SIZE_MAX, BIG_CHAR); |
392 | do_test (align1: i, align2: i, len1: 8 << i, len2: 10, SIZE_MAX, SMALL_CHAR); |
393 | } |
394 | } |
395 | |
396 | do_random_tests (); |
397 | do_overflow_tests (); |
398 | return ret; |
399 | } |
400 | |
401 | #include <support/test-driver.c> |
402 | |