1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright(c) 2010-2014 Intel Corporation 3 */ 4 5 #include <stdio.h> 6 #include <string.h> 7 #include <inttypes.h> 8 9 #include <rte_string_fns.h> 10 11 #include <cmdline_parse.h> 12 #include <cmdline_parse_num.h> 13 14 #include "test_cmdline.h" 15 16 struct num_unsigned_str { 17 const char * str; 18 uint64_t result; 19 }; 20 21 struct num_signed_str { 22 const char * str; 23 int64_t result; 24 }; 25 26 const struct num_unsigned_str num_valid_positive_strs[] = { 27 /* decimal positive */ 28 {"0", 0 }, 29 {"127", INT8_MAX }, 30 {"128", INT8_MAX + 1 }, 31 {"255", UINT8_MAX }, 32 {"256", UINT8_MAX + 1 }, 33 {"32767", INT16_MAX }, 34 {"32768", INT16_MAX + 1 }, 35 {"65535", UINT16_MAX }, 36 {"65536", UINT16_MAX + 1 }, 37 {"2147483647", INT32_MAX }, 38 {"2147483648", INT32_MAX + 1U }, 39 {"4294967295", UINT32_MAX }, 40 {"4294967296", UINT32_MAX + 1ULL }, 41 {"9223372036854775807", INT64_MAX }, 42 {"9223372036854775808", INT64_MAX + 1ULL}, 43 {"18446744073709551615", UINT64_MAX }, 44 /* hexadecimal (no leading zeroes) */ 45 {"0x0", 0 }, 46 {"0x7F", INT8_MAX }, 47 {"0x80", INT8_MAX + 1 }, 48 {"0xFF", UINT8_MAX }, 49 {"0x100", UINT8_MAX + 1 }, 50 {"0x7FFF", INT16_MAX }, 51 {"0x8000", INT16_MAX + 1 }, 52 {"0xFFFF", UINT16_MAX }, 53 {"0x10000", UINT16_MAX + 1 }, 54 {"0x7FFFFFFF", INT32_MAX }, 55 {"0x80000000", INT32_MAX + 1U }, 56 {"0xFFFFFFFF", UINT32_MAX }, 57 {"0x100000000", UINT32_MAX + 1ULL }, 58 {"0x7FFFFFFFFFFFFFFF", INT64_MAX }, 59 {"0x8000000000000000", INT64_MAX + 1ULL}, 60 {"0xFFFFFFFFFFFFFFFF", UINT64_MAX }, 61 /* hexadecimal (with leading zeroes) */ 62 {"0x00", 0 }, 63 {"0x7F", INT8_MAX }, 64 {"0x80", INT8_MAX + 1 }, 65 {"0xFF", UINT8_MAX }, 66 {"0x0100", UINT8_MAX + 1 }, 67 {"0x7FFF", INT16_MAX }, 68 {"0x8000", INT16_MAX + 1 }, 69 {"0xFFFF", UINT16_MAX }, 70 {"0x00010000", UINT16_MAX + 1 }, 71 {"0x7FFFFFFF", INT32_MAX }, 72 {"0x80000000", INT32_MAX + 1U }, 73 {"0xFFFFFFFF", UINT32_MAX }, 74 {"0x0000000100000000", UINT32_MAX + 1ULL }, 75 {"0x7FFFFFFFFFFFFFFF", INT64_MAX }, 76 {"0x8000000000000000", INT64_MAX + 1ULL}, 77 {"0xFFFFFFFFFFFFFFFF", UINT64_MAX }, 78 /* check all characters */ 79 {"0x1234567890ABCDEF", 0x1234567890ABCDEFULL }, 80 {"0x1234567890abcdef", 0x1234567890ABCDEFULL }, 81 /* binary (no leading zeroes) */ 82 {"0b0", 0 }, 83 {"0b1111111", INT8_MAX }, 84 {"0b10000000", INT8_MAX + 1 }, 85 {"0b11111111", UINT8_MAX }, 86 {"0b100000000", UINT8_MAX + 1 }, 87 {"0b111111111111111", INT16_MAX }, 88 {"0b1000000000000000", INT16_MAX + 1 }, 89 {"0b1111111111111111", UINT16_MAX }, 90 {"0b10000000000000000", UINT16_MAX + 1 }, 91 {"0b1111111111111111111111111111111", INT32_MAX }, 92 {"0b10000000000000000000000000000000", INT32_MAX + 1U }, 93 {"0b11111111111111111111111111111111", UINT32_MAX }, 94 {"0b100000000000000000000000000000000", UINT32_MAX + 1ULL }, 95 {"0b111111111111111111111111111111111111111111111111111111111111111", 96 INT64_MAX }, 97 {"0b1000000000000000000000000000000000000000000000000000000000000000", 98 INT64_MAX + 1ULL}, 99 {"0b1111111111111111111111111111111111111111111111111111111111111111", 100 UINT64_MAX }, 101 /* binary (with leading zeroes) */ 102 {"0b01111111", INT8_MAX }, 103 {"0b0000000100000000", UINT8_MAX + 1 }, 104 {"0b0111111111111111", INT16_MAX }, 105 {"0b00000000000000010000000000000000", UINT16_MAX + 1 }, 106 {"0b01111111111111111111111111111111", INT32_MAX }, 107 {"0b0000000000000000000000000000000100000000000000000000000000000000", 108 UINT32_MAX + 1ULL }, 109 {"0b0111111111111111111111111111111111111111111111111111111111111111", 110 INT64_MAX }, 111 /* octal */ 112 {"00", 0 }, 113 {"0177", INT8_MAX }, 114 {"0200", INT8_MAX + 1 }, 115 {"0377", UINT8_MAX }, 116 {"0400", UINT8_MAX + 1 }, 117 {"077777", INT16_MAX }, 118 {"0100000", INT16_MAX + 1 }, 119 {"0177777", UINT16_MAX }, 120 {"0200000", UINT16_MAX + 1 }, 121 {"017777777777", INT32_MAX }, 122 {"020000000000", INT32_MAX + 1U }, 123 {"037777777777", UINT32_MAX }, 124 {"040000000000", UINT32_MAX + 1ULL }, 125 {"0777777777777777777777", INT64_MAX }, 126 {"01000000000000000000000", INT64_MAX + 1ULL}, 127 {"01777777777777777777777", UINT64_MAX }, 128 /* check all numbers */ 129 {"012345670", 012345670 }, 130 {"076543210", 076543210 }, 131 }; 132 133 const struct num_signed_str num_valid_negative_strs[] = { 134 /* deciman negative */ 135 {"-128", INT8_MIN }, 136 {"-129", INT8_MIN - 1 }, 137 {"-32768", INT16_MIN }, 138 {"-32769", INT16_MIN - 1 }, 139 {"-2147483648", INT32_MIN }, 140 {"-2147483649", INT32_MIN - 1LL }, 141 {"-9223372036854775808", INT64_MIN }, 142 }; 143 144 const struct num_unsigned_str num_garbage_positive_strs[] = { 145 /* valid strings with garbage on the end, should still be valid */ 146 /* decimal */ 147 {"9223372036854775807\0garbage", INT64_MAX }, 148 {"9223372036854775807\tgarbage", INT64_MAX }, 149 {"9223372036854775807\rgarbage", INT64_MAX }, 150 {"9223372036854775807\ngarbage", INT64_MAX }, 151 {"9223372036854775807#garbage", INT64_MAX }, 152 {"9223372036854775807 garbage", INT64_MAX }, 153 /* hex */ 154 {"0x7FFFFFFFFFFFFFFF\0garbage", INT64_MAX }, 155 {"0x7FFFFFFFFFFFFFFF\tgarbage", INT64_MAX }, 156 {"0x7FFFFFFFFFFFFFFF\rgarbage", INT64_MAX }, 157 {"0x7FFFFFFFFFFFFFFF\ngarbage", INT64_MAX }, 158 {"0x7FFFFFFFFFFFFFFF#garbage", INT64_MAX }, 159 {"0x7FFFFFFFFFFFFFFF garbage", INT64_MAX }, 160 /* binary */ 161 {"0b1111111111111111111111111111111\0garbage", INT32_MAX }, 162 {"0b1111111111111111111111111111111\rgarbage", INT32_MAX }, 163 {"0b1111111111111111111111111111111\tgarbage", INT32_MAX }, 164 {"0b1111111111111111111111111111111\ngarbage", INT32_MAX }, 165 {"0b1111111111111111111111111111111#garbage", INT32_MAX }, 166 {"0b1111111111111111111111111111111 garbage", INT32_MAX }, 167 /* octal */ 168 {"01777777777777777777777\0garbage", UINT64_MAX }, 169 {"01777777777777777777777\rgarbage", UINT64_MAX }, 170 {"01777777777777777777777\tgarbage", UINT64_MAX }, 171 {"01777777777777777777777\ngarbage", UINT64_MAX }, 172 {"01777777777777777777777#garbage", UINT64_MAX }, 173 {"01777777777777777777777 garbage", UINT64_MAX }, 174 }; 175 176 const struct num_signed_str num_garbage_negative_strs[] = { 177 /* valid strings with garbage on the end, should still be valid */ 178 {"-9223372036854775808\0garbage", INT64_MIN }, 179 {"-9223372036854775808\rgarbage", INT64_MIN }, 180 {"-9223372036854775808\tgarbage", INT64_MIN }, 181 {"-9223372036854775808\ngarbage", INT64_MIN }, 182 {"-9223372036854775808#garbage", INT64_MIN }, 183 {"-9223372036854775808 garbage", INT64_MIN }, 184 }; 185 186 const char * num_invalid_strs[] = { 187 "18446744073709551616", /* out of range unsigned */ 188 "-9223372036854775809", /* out of range negative signed */ 189 "0x10000000000000000", /* out of range hex */ 190 /* out of range binary */ 191 "0b10000000000000000000000000000000000000000000000000000000000000000", 192 "020000000000000000000000", /* out of range octal */ 193 /* wrong chars */ 194 "0123456239", 195 "0x1234580AGE", 196 "0b0111010101g001", 197 "0b01110101017001", 198 /* false negative numbers */ 199 "-12345F623", 200 "-0x1234580A", 201 "-0b0111010101", 202 /* too long (128+ chars) */ 203 "0b1111000011110000111100001111000011110000111100001111000011110000" 204 "1111000011110000111100001111000011110000111100001111000011110000", 205 "1E3", 206 "0A", 207 "-B", 208 "+4", 209 "1.23G", 210 "", 211 " ", 212 "#", 213 "\r", 214 "\t", 215 "\n", 216 "\0", 217 }; 218 219 #define NUM_POSITIVE_STRS_SIZE \ 220 (sizeof(num_valid_positive_strs) / sizeof(num_valid_positive_strs[0])) 221 #define NUM_NEGATIVE_STRS_SIZE \ 222 (sizeof(num_valid_negative_strs) / sizeof(num_valid_negative_strs[0])) 223 #define NUM_POSITIVE_GARBAGE_STRS_SIZE \ 224 (sizeof(num_garbage_positive_strs) / sizeof(num_garbage_positive_strs[0])) 225 #define NUM_NEGATIVE_GARBAGE_STRS_SIZE \ 226 (sizeof(num_garbage_negative_strs) / sizeof(num_garbage_negative_strs[0])) 227 #define NUM_INVALID_STRS_SIZE \ 228 (sizeof(num_invalid_strs) / sizeof(num_invalid_strs[0])) 229 230 231 232 static int 233 can_parse_unsigned(uint64_t expected_result, enum cmdline_numtype type) 234 { 235 switch (type) { 236 case UINT8: 237 if (expected_result > UINT8_MAX) 238 return 0; 239 break; 240 case UINT16: 241 if (expected_result > UINT16_MAX) 242 return 0; 243 break; 244 case UINT32: 245 if (expected_result > UINT32_MAX) 246 return 0; 247 break; 248 case INT8: 249 if (expected_result > INT8_MAX) 250 return 0; 251 break; 252 case INT16: 253 if (expected_result > INT16_MAX) 254 return 0; 255 break; 256 case INT32: 257 if (expected_result > INT32_MAX) 258 return 0; 259 break; 260 case INT64: 261 if (expected_result > INT64_MAX) 262 return 0; 263 break; 264 default: 265 return 1; 266 } 267 return 1; 268 } 269 270 static int 271 can_parse_signed(int64_t expected_result, enum cmdline_numtype type) 272 { 273 switch (type) { 274 case UINT8: 275 if (expected_result > UINT8_MAX || expected_result < 0) 276 return 0; 277 break; 278 case UINT16: 279 if (expected_result > UINT16_MAX || expected_result < 0) 280 return 0; 281 break; 282 case UINT32: 283 if (expected_result > UINT32_MAX || expected_result < 0) 284 return 0; 285 break; 286 case UINT64: 287 if (expected_result < 0) 288 return 0; 289 break; 290 case INT8: 291 if (expected_result > INT8_MAX || expected_result < INT8_MIN) 292 return 0; 293 break; 294 case INT16: 295 if (expected_result > INT16_MAX || expected_result < INT16_MIN) 296 return 0; 297 break; 298 case INT32: 299 if (expected_result > INT32_MAX || expected_result < INT32_MIN) 300 return 0; 301 break; 302 default: 303 return 1; 304 } 305 return 1; 306 } 307 308 /* test invalid parameters */ 309 int 310 test_parse_num_invalid_param(void) 311 { 312 char buf[CMDLINE_TEST_BUFSIZE]; 313 uint32_t result; 314 cmdline_parse_token_num_t token; 315 int ret = 0; 316 317 /* set up a token */ 318 token.num_data.type = UINT32; 319 320 /* copy string to buffer */ 321 strlcpy(buf, num_valid_positive_strs[0].str, sizeof(buf)); 322 323 /* try all null */ 324 ret = cmdline_parse_num(NULL, NULL, NULL, 0); 325 if (ret != -1) { 326 printf("Error: parser accepted null parameters!\n"); 327 return -1; 328 } 329 330 /* try null token */ 331 ret = cmdline_parse_num(NULL, buf, (void*)&result, sizeof(result)); 332 if (ret != -1) { 333 printf("Error: parser accepted null token!\n"); 334 return -1; 335 } 336 337 /* try null buf */ 338 ret = cmdline_parse_num((cmdline_parse_token_hdr_t*)&token, NULL, 339 (void*)&result, sizeof(result)); 340 if (ret != -1) { 341 printf("Error: parser accepted null string!\n"); 342 return -1; 343 } 344 345 /* try null result */ 346 ret = cmdline_parse_num((cmdline_parse_token_hdr_t*)&token, buf, 347 NULL, 0); 348 if (ret == -1) { 349 printf("Error: parser rejected null result!\n"); 350 return -1; 351 } 352 353 /* test help function */ 354 memset(&buf, 0, sizeof(buf)); 355 356 /* try all null */ 357 ret = cmdline_get_help_num(NULL, NULL, 0); 358 if (ret != -1) { 359 printf("Error: help function accepted null parameters!\n"); 360 return -1; 361 } 362 363 /* try null token */ 364 ret = cmdline_get_help_num(NULL, buf, sizeof(buf)); 365 if (ret != -1) { 366 printf("Error: help function accepted null token!\n"); 367 return -1; 368 } 369 370 /* coverage! */ 371 ret = cmdline_get_help_num((cmdline_parse_token_hdr_t*)&token, buf, sizeof(buf)); 372 if (ret < 0) { 373 printf("Error: help function failed with valid parameters!\n"); 374 return -1; 375 } 376 377 return 0; 378 } 379 /* test valid parameters but invalid data */ 380 int 381 test_parse_num_invalid_data(void) 382 { 383 enum cmdline_numtype type; 384 int ret = 0; 385 unsigned i; 386 char buf[CMDLINE_TEST_BUFSIZE]; 387 uint64_t result; /* pick largest buffer */ 388 cmdline_parse_token_num_t token; 389 390 /* cycle through all possible parsed types */ 391 for (type = UINT8; type <= INT64; type++) { 392 token.num_data.type = type; 393 394 /* test full strings */ 395 for (i = 0; i < NUM_INVALID_STRS_SIZE; i++) { 396 397 memset(&result, 0, sizeof(uint64_t)); 398 memset(&buf, 0, sizeof(buf)); 399 400 ret = cmdline_parse_num((cmdline_parse_token_hdr_t*)&token, 401 num_invalid_strs[i], (void*)&result, sizeof(result)); 402 if (ret != -1) { 403 /* get some info about what we are trying to parse */ 404 cmdline_get_help_num((cmdline_parse_token_hdr_t*)&token, 405 buf, sizeof(buf)); 406 407 printf("Error: parsing %s as %s succeeded!\n", 408 num_invalid_strs[i], buf); 409 return -1; 410 } 411 } 412 } 413 return 0; 414 } 415 416 /* test valid parameters and data */ 417 int 418 test_parse_num_valid(void) 419 { 420 int ret = 0; 421 enum cmdline_numtype type; 422 unsigned i; 423 char buf[CMDLINE_TEST_BUFSIZE]; 424 uint64_t result; 425 cmdline_parse_token_num_t token; 426 427 /** valid strings **/ 428 429 /* cycle through all possible parsed types */ 430 for (type = UINT8; type <= INT64; type++) { 431 token.num_data.type = type; 432 433 /* test positive strings */ 434 for (i = 0; i < NUM_POSITIVE_STRS_SIZE; i++) { 435 result = 0; 436 memset(&buf, 0, sizeof(buf)); 437 438 cmdline_get_help_num((cmdline_parse_token_hdr_t*)&token, 439 buf, sizeof(buf)); 440 441 ret = cmdline_parse_num((cmdline_parse_token_hdr_t*) &token, 442 num_valid_positive_strs[i].str, 443 (void*)&result, sizeof(result)); 444 445 /* if it should have passed but didn't, or if it should have failed but didn't */ 446 if ((ret < 0) == (can_parse_unsigned(num_valid_positive_strs[i].result, type) > 0)) { 447 printf("Error: parser behaves unexpectedly when parsing %s as %s!\n", 448 num_valid_positive_strs[i].str, buf); 449 return -1; 450 } 451 /* check if result matches what it should have matched 452 * since unsigned numbers don't care about number of bits, we can just convert 453 * everything to uint64_t without any worries. */ 454 if (ret > 0 && num_valid_positive_strs[i].result != result) { 455 printf("Error: parsing %s as %s failed: result mismatch!\n", 456 num_valid_positive_strs[i].str, buf); 457 return -1; 458 } 459 } 460 461 /* test negative strings */ 462 for (i = 0; i < NUM_NEGATIVE_STRS_SIZE; i++) { 463 result = 0; 464 memset(&buf, 0, sizeof(buf)); 465 466 cmdline_get_help_num((cmdline_parse_token_hdr_t*)&token, 467 buf, sizeof(buf)); 468 469 ret = cmdline_parse_num((cmdline_parse_token_hdr_t*) &token, 470 num_valid_negative_strs[i].str, 471 (void*)&result, sizeof(result)); 472 473 /* if it should have passed but didn't, or if it should have failed but didn't */ 474 if ((ret < 0) == (can_parse_signed(num_valid_negative_strs[i].result, type) > 0)) { 475 printf("Error: parser behaves unexpectedly when parsing %s as %s!\n", 476 num_valid_negative_strs[i].str, buf); 477 return -1; 478 } 479 /* check if result matches what it should have matched 480 * the result is signed in this case, so we have to account for that */ 481 if (ret > 0) { 482 /* detect negative */ 483 switch (type) { 484 case INT8: 485 result = (int8_t) result; 486 break; 487 case INT16: 488 result = (int16_t) result; 489 break; 490 case INT32: 491 result = (int32_t) result; 492 break; 493 default: 494 break; 495 } 496 if (num_valid_negative_strs[i].result == (int64_t) result) 497 continue; 498 printf("Error: parsing %s as %s failed: result mismatch!\n", 499 num_valid_negative_strs[i].str, buf); 500 return -1; 501 } 502 } 503 } 504 505 /** garbage strings **/ 506 507 /* cycle through all possible parsed types */ 508 for (type = UINT8; type <= INT64; type++) { 509 token.num_data.type = type; 510 511 /* test positive garbage strings */ 512 for (i = 0; i < NUM_POSITIVE_GARBAGE_STRS_SIZE; i++) { 513 result = 0; 514 memset(&buf, 0, sizeof(buf)); 515 516 cmdline_get_help_num((cmdline_parse_token_hdr_t*)&token, 517 buf, sizeof(buf)); 518 519 ret = cmdline_parse_num((cmdline_parse_token_hdr_t*) &token, 520 num_garbage_positive_strs[i].str, 521 (void*)&result, sizeof(result)); 522 523 /* if it should have passed but didn't, or if it should have failed but didn't */ 524 if ((ret < 0) == (can_parse_unsigned(num_garbage_positive_strs[i].result, type) > 0)) { 525 printf("Error: parser behaves unexpectedly when parsing %s as %s!\n", 526 num_garbage_positive_strs[i].str, buf); 527 return -1; 528 } 529 /* check if result matches what it should have matched 530 * since unsigned numbers don't care about number of bits, we can just convert 531 * everything to uint64_t without any worries. */ 532 if (ret > 0 && num_garbage_positive_strs[i].result != result) { 533 printf("Error: parsing %s as %s failed: result mismatch!\n", 534 num_garbage_positive_strs[i].str, buf); 535 return -1; 536 } 537 } 538 539 /* test negative strings */ 540 for (i = 0; i < NUM_NEGATIVE_GARBAGE_STRS_SIZE; i++) { 541 result = 0; 542 memset(&buf, 0, sizeof(buf)); 543 544 cmdline_get_help_num((cmdline_parse_token_hdr_t*)&token, 545 buf, sizeof(buf)); 546 547 ret = cmdline_parse_num((cmdline_parse_token_hdr_t*) &token, 548 num_garbage_negative_strs[i].str, 549 (void*)&result, sizeof(result)); 550 551 /* if it should have passed but didn't, or if it should have failed but didn't */ 552 if ((ret < 0) == (can_parse_signed(num_garbage_negative_strs[i].result, type) > 0)) { 553 printf("Error: parser behaves unexpectedly when parsing %s as %s!\n", 554 num_garbage_negative_strs[i].str, buf); 555 return -1; 556 } 557 /* check if result matches what it should have matched 558 * the result is signed in this case, so we have to account for that */ 559 if (ret > 0) { 560 /* detect negative */ 561 switch (type) { 562 case INT8: 563 if (result & (INT8_MAX + 1)) 564 result |= 0xFFFFFFFFFFFFFF00ULL; 565 break; 566 case INT16: 567 if (result & (INT16_MAX + 1)) 568 result |= 0xFFFFFFFFFFFF0000ULL; 569 break; 570 case INT32: 571 if (result & (INT32_MAX + 1ULL)) 572 result |= 0xFFFFFFFF00000000ULL; 573 break; 574 default: 575 break; 576 } 577 if (num_garbage_negative_strs[i].result == (int64_t) result) 578 continue; 579 printf("Error: parsing %s as %s failed: result mismatch!\n", 580 num_garbage_negative_strs[i].str, buf); 581 return -1; 582 } 583 } 584 } 585 586 memset(&buf, 0, sizeof(buf)); 587 588 /* coverage! */ 589 cmdline_get_help_num((cmdline_parse_token_hdr_t*)&token, 590 buf, sizeof(buf)); 591 592 return 0; 593 } 594