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 * 62 _umem_debug_init(void) 63 { 64 return ("default,verbose"); 65 } 66 67 const char * 68 _umem_logging_init(void) 69 { 70 return ("transaction,contents,fail"); 71 } 72 73 int 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 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 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 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 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 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 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 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 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 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 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 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 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