1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright 2025 Oxide Computer Company
14 */
15
16 /*
17 * Basic tests for the ilstr string handling routines.
18 */
19
20 #include <stdlib.h>
21 #include <stdio.h>
22 #include <string.h>
23 #include <strings.h>
24 #include <errno.h>
25 #include <upanic.h>
26 #include <umem.h>
27 #include <sys/ilstr.h>
28 #include <sys/sysmacros.h>
29 #include <sys/debug.h>
30
31 #define VERIFYSTRING(ils, expected) \
32 do { \
33 const char *check = ilstr_cstr(ils); \
34 VERIFY(check != NULL); \
35 if (strcmp(check, (expected)) != 0) { \
36 char buf[2048]; \
37 (void) snprintf(buf, sizeof (buf), \
38 "\"%s\" != expected \"%s\"", \
39 check, (expected)); \
40 assfail(buf, __FILE__, __LINE__); \
41 } \
42 } while (0)
43
44 typedef enum ilstr_test_types {
45 ITT_STD = 0x01,
46 ITT_PRE = 0x02,
47 } ilstr_test_types_t;
48
49 #define ITT_ALL (ITT_STD | ITT_PRE)
50
51 typedef struct ilstr_test {
52 char *ist_name;
53 int (*ist_func)(ilstr_t *ils);
54 uint_t ist_trials;
55 ilstr_test_types_t ist_types;
56 } ilstr_test_t;
57
58 #define PREALLOC_SZ 1024
59 static char ilsbuf[PREALLOC_SZ];
60
61 const char *
_umem_debug_init(void)62 _umem_debug_init(void)
63 {
64 return ("default,verbose");
65 }
66
67 const char *
_umem_logging_init(void)68 _umem_logging_init(void)
69 {
70 return ("transaction,contents,fail");
71 }
72
73 int
ist_empty(ilstr_t * ils)74 ist_empty(ilstr_t *ils)
75 {
76 VERIFY3U(ilstr_len(ils), ==, 0);
77 VERIFY(ilstr_is_empty(ils));
78 VERIFY(ilstr_cstr(ils) != NULL);
79 VERIFY3U(ilstr_cstr(ils)[0], ==, '\0');
80 VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_OK);
81
82 return (0);
83 }
84
85 int
ist_prealloc_toobig(ilstr_t * ils)86 ist_prealloc_toobig(ilstr_t *ils)
87 {
88 /*
89 * Fill the buffer all the way up with characters:
90 */
91 for (uint_t n = 0; n < PREALLOC_SZ - 1; n++) {
92 ilstr_append_str(ils, "A");
93 }
94 VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_OK);
95
96 /*
97 * Confirm that we cannot append strings:
98 */
99 ilstr_append_str(ils, "A");
100 VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_NOMEM);
101
102 ilstr_append_str(ils, "A");
103 VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_NOMEM);
104
105 /*
106 * Confirm that we cannot prepend a character:
107 */
108 ilstr_prepend_char(ils, 'A');
109 VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_NOMEM);
110
111 /*
112 * Clear out the string and make sure we can start again:
113 */
114 ilstr_reset(ils);
115
116 ilstr_append_str(ils, "B");
117 VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_OK);
118 VERIFYSTRING(ils, "B");
119
120 /*
121 * Fill the buffer all the way up with characters:
122 */
123 ilstr_reset(ils);
124 for (uint_t n = 0; n < PREALLOC_SZ - 1; n++) {
125 ilstr_append_str(ils, "C");
126 }
127 VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_OK);
128
129 /*
130 * Confirm that we cannot prepend strings:
131 */
132 ilstr_prepend_str(ils, "D");
133 VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_NOMEM);
134
135 ilstr_prepend_str(ils, "D");
136 VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_NOMEM);
137
138 /*
139 * Confirm that we cannot append a character:
140 */
141 ilstr_append_char(ils, 'D');
142 VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_NOMEM);
143
144 return (0);
145 }
146
147 int
ist_standard_toobig(ilstr_t * ils)148 ist_standard_toobig(ilstr_t *ils)
149 {
150 /*
151 * Pack the buffer with characters, so that we've been forced to
152 * allocate at least one extra chunk:
153 */
154 for (uint_t n = 0; n < PREALLOC_SZ - 1; n++) {
155 ilstr_append_str(ils, "A");
156 }
157 VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_OK);
158
159 /*
160 * Continue adding characters until we hit apparent memory exhaustion:
161 */
162 for (uint_t n = 0; n < PREALLOC_SZ - 1; n++) {
163 /*
164 * Use the umem fault injection facility to fail any
165 * allocations made while we append to the string:
166 */
167 umem_setmtbf(1);
168 ilstr_append_str(ils, "A");
169 umem_setmtbf(0);
170
171 if (ilstr_errno(ils) == ILSTR_ERROR_NOMEM) {
172 break;
173 }
174
175 VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_OK);
176 }
177
178 /*
179 * Confirm that we ran out of memory along the way, and didn't write
180 * too many characters in the process:
181 */
182 VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_NOMEM);
183 VERIFY3U(ilstr_len(ils), <, 2 * PREALLOC_SZ);
184
185 return (0);
186 }
187
188 int
ist_huge(ilstr_t * ils)189 ist_huge(ilstr_t *ils)
190 {
191 /*
192 * Build a 26MB string by repeating the alphabet over and over:
193 */
194 uint_t target = 26 * 1024 * 1024;
195
196 for (uint_t n = 0; n < target / 26; n++) {
197 ilstr_append_str(ils, "abcdefghijklmnopqrstuvwxyz");
198
199 if (ilstr_errno(ils) == ILSTR_ERROR_NOMEM) {
200 return (ENOMEM);
201 }
202 VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_OK);
203 }
204
205 VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_OK);
206 VERIFY3U(ilstr_len(ils), ==, target);
207 VERIFY(!ilstr_is_empty(ils));
208
209 return (0);
210 }
211
212 int
ist_printf_1(ilstr_t * ils)213 ist_printf_1(ilstr_t *ils)
214 {
215 const char *want = "a\nb\n1000\ntest string\n";
216
217 ilstr_aprintf(ils, "a\nb\n%u\n%s\n", 1000, "test string");
218 VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_OK);
219 VERIFYSTRING(ils, want);
220
221 return (0);
222 }
223
224 int
ist_printf_2(ilstr_t * ils)225 ist_printf_2(ilstr_t *ils)
226 {
227 int r = 0;
228
229 const char *lorem = "Lorem ipsum dolor sit amet, consectetur "
230 "adipiscing elit, sed do eiusmod tempor incididunt ut labore "
231 "et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud "
232 "exercitation ullamco laboris nisi ut aliquip ex ea commodo "
233 "consequat.";
234 char *want;
235
236 if (asprintf(&want, "%s\n\tnumber 1\n%s\n\n%s\n number 100000000\n",
237 lorem, lorem, lorem) < 0) {
238 return (errno);
239 }
240
241 ilstr_aprintf(ils, "%s\n\t", lorem);
242 ilstr_append_str(ils, "number");
243 ilstr_aprintf(ils, " %u\n%s\n\n", 1, lorem);
244 ilstr_append_str(ils, lorem);
245 ilstr_aprintf(ils, "\n number %lld\n", (long long)100000000);
246
247 VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_OK);
248 if (strcmp(ilstr_cstr(ils), want) != 0) {
249 printf("want: %s\n", want);
250 printf("got: %s\n", ilstr_cstr(ils));
251 r = ENOENT;
252 }
253
254 free(want);
255
256 return (r);
257 }
258
259 int
ist_resets(ilstr_t * ils)260 ist_resets(ilstr_t *ils)
261 {
262 VERIFYSTRING(ils, "");
263
264 ilstr_reset(ils);
265 VERIFYSTRING(ils, "");
266
267 ilstr_append_str(ils, "abc");
268 VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_OK);
269 VERIFYSTRING(ils, "abc");
270
271 ilstr_append_str(ils, "def");
272 VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_OK);
273 VERIFYSTRING(ils, "abcdef");
274
275 ilstr_reset(ils);
276 VERIFYSTRING(ils, "");
277
278 ilstr_append_str(ils, "xyz");
279 VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_OK);
280 VERIFYSTRING(ils, "xyz");
281
282 ilstr_reset(ils);
283 VERIFY(strcmp(ilstr_cstr(ils), "") == 0);
284 VERIFYSTRING(ils, "");
285
286 return (0);
287 }
288
289 int
ist_random(ilstr_t * ils)290 ist_random(ilstr_t *ils)
291 {
292 char *work;
293 uint_t target = 256 + arc4random_uniform(1024 - 256);
294
295 printf(" - target string length %u\n", target);
296 if ((work = calloc(1, 1024 + 1)) == NULL) {
297 return (errno);
298 }
299
300 VERIFY3U(ilstr_len(ils), ==, 0);
301 VERIFY(ilstr_is_empty(ils));
302 VERIFY3U(ilstr_cstr(ils)[0], ==, '\0');
303 VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_OK);
304
305 for (uint_t n = 0; n < target; n++) {
306 char c[2] = { arc4random_uniform('Z' - 'A') + 'A', '\0' };
307
308 work[n] = c[0];
309 ilstr_append_str(ils, c);
310
311 VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_OK);
312 VERIFY3U(ilstr_len(ils), ==, n + 1);
313 VERIFY(!ilstr_is_empty(ils));
314 VERIFYSTRING(ils, work);
315 }
316
317 VERIFY(!ilstr_is_empty(ils));
318 VERIFY3U(ilstr_len(ils), ==, target);
319 VERIFYSTRING(ils, work);
320 printf(" - final string: %s\n", work);
321
322 free(work);
323 return (0);
324 }
325
326 int
ist_prepend_char(ilstr_t * ils)327 ist_prepend_char(ilstr_t *ils)
328 {
329 ilstr_append_str(ils, "ackwards");
330 ilstr_prepend_char(ils, 'B');
331 ilstr_append_char(ils, '!');
332
333 VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_OK);
334 VERIFYSTRING(ils, "Backwards!");
335
336 return (0);
337 }
338
339 int
ist_prepend_str(ilstr_t * ils)340 ist_prepend_str(ilstr_t *ils)
341 {
342 ilstr_prepend_str(ils, "string was prepended");
343 VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_OK);
344 VERIFYSTRING(ils, "string was prepended");
345
346 ilstr_prepend_str(ils, "this ");
347 VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_OK);
348 VERIFYSTRING(ils, "this string was prepended");
349
350 ilstr_append_str(ils, ", successfully");
351 VERIFY3U(ilstr_errno(ils), ==, ILSTR_ERROR_OK);
352 VERIFYSTRING(ils, "this string was prepended, successfully");
353
354 return (0);
355 }
356
357 int
ist_building_list(ilstr_t * ils)358 ist_building_list(ilstr_t *ils)
359 {
360 const char *items[] = { "one", "two", "three" };
361 const char *expect[] = {
362 "empty list",
363 "populated list (one)",
364 "populated list (one, two)",
365 "populated list (one, two, three)",
366 };
367
368 for (uint_t n = 0; n <= 3; n++) {
369 ilstr_reset(ils);
370
371 for (uint_t i = 0; i < n; i++) {
372 if (!ilstr_is_empty(ils)) {
373 ilstr_append_str(ils, ", ");
374 }
375
376 ilstr_append_str(ils, items[i]);
377 }
378
379 if (ilstr_is_empty(ils)) {
380 ilstr_append_str(ils, "empty list");
381 } else {
382 ilstr_prepend_str(ils, "populated list (");
383 ilstr_append_str(ils, ")");
384 }
385
386 VERIFYSTRING(ils, expect[n]);
387 }
388
389 return (0);
390 }
391
392 uint_t
ist_drive_test(const ilstr_test_t * ist)393 ist_drive_test(const ilstr_test_t *ist)
394 {
395 uint_t nfails = 0;
396 int r;
397 ilstr_t ils;
398
399 for (uint_t n = 0; n < ist->ist_trials; n++) {
400 if (ist->ist_types & ITT_STD) {
401 ilstr_init(&ils, 0);
402 printf("STD[%s]... run %d\n", ist->ist_name, n);
403 if ((r = ist->ist_func(&ils)) != 0) {
404 (void) fprintf(stderr,
405 "TEST FAILED: STD[%s]: %s\n",
406 ist->ist_name, strerror(r));
407 nfails += 1;
408 } else {
409 printf("TEST PASSED: STD[%s]\n",
410 ist->ist_name);
411 }
412 ilstr_fini(&ils);
413 printf("\n");
414 }
415
416 if (ist->ist_types & ITT_PRE) {
417 ilstr_init_prealloc(&ils, ilsbuf, sizeof (ilsbuf));
418 printf("PRE[%s]... run %d\n", ist->ist_name, n);
419 if ((r = ist->ist_func(&ils)) != 0) {
420 (void) fprintf(stderr,
421 "TEST FAILED: PRE[%s]: %s\n",
422 ist->ist_name, strerror(r));
423 nfails += 1;
424 } else {
425 printf("TEST PASSED: PRE[%s]\n",
426 ist->ist_name);
427 }
428 ilstr_fini(&ils);
429 printf("\n");
430 }
431 }
432
433 return (nfails);
434 }
435
436 static const ilstr_test_t ilstr_tests[] = {
437 { "empty", ist_empty, 1, ITT_ALL },
438 { "resets", ist_resets, 1, ITT_ALL },
439 { "printf-1", ist_printf_1, 1, ITT_ALL },
440 { "printf-2", ist_printf_2, 1, ITT_ALL },
441 { "prealloc_toobig", ist_prealloc_toobig, 1, ITT_PRE },
442 { "standard_toobig", ist_standard_toobig, 1, ITT_STD },
443 { "prepend_char", ist_prepend_char, 1, ITT_ALL },
444 { "prepend_str", ist_prepend_str, 1, ITT_ALL },
445 { "building_list", ist_building_list, 1, ITT_ALL },
446 /*
447 * Run the random generation test many times, as an attempt at fuzzing:
448 */
449 { "random", ist_random, 1000, ITT_ALL },
450 /*
451 * Run the huge allocation test some number of times to try to make
452 * sure we exercise allocation and free of different buffer sizes, and
453 * to increase the likelihood of detecting any heap corruption:
454 */
455 { "huge", ist_huge, 100, ITT_STD },
456 };
457
458 int
main(void)459 main(void)
460 {
461 uint_t nfails = 0;
462
463 for (uint_t i = 0; i < ARRAY_SIZE(ilstr_tests); i++) {
464 nfails += ist_drive_test(&ilstr_tests[i]);
465 }
466
467 char *aoe;
468 if ((aoe = getenv("PANIC_ON_EXIT")) != NULL && strcmp(aoe, "1") == 0) {
469 const char *msg = "PANIC_ON_EXIT set; panicking for findleaks";
470 upanic(msg, strlen(msg));
471 }
472
473 return (nfails == 0 ? 0 : 1);
474 }
475