diff options
| author | 2026-01-17 15:18:06 +0100 | |
|---|---|---|
| committer | 2026-01-17 16:04:00 +0100 | |
| commit | e45099fb21b679aa0cdb0db394587bb5ba675b0c (patch) | |
| tree | b1a2c1dd34877d83a04d6d6fab804fb2e5a65f2d /src/oplist.c | |
| parent | 26dd27c435a0d110c924e1fef34ad0f6ae60e251 (diff) | |
| download | libplist-e45099fb21b679aa0cdb0db394587bb5ba675b0c.tar.gz libplist-e45099fb21b679aa0cdb0db394587bb5ba675b0c.tar.bz2 | |
Prevent deep nesting of plist structures in all input/output formats
Thanks to @unbengable12 for reporting. Addresses #288, #289, #290, #291, and #292.
Diffstat (limited to 'src/oplist.c')
| -rw-r--r-- | src/oplist.c | 69 |
1 files changed, 37 insertions, 32 deletions
diff --git a/src/oplist.c b/src/oplist.c index 680873c..0eea27a 100644 --- a/src/oplist.c +++ b/src/oplist.c | |||
| @@ -367,6 +367,11 @@ static plist_err_t _node_estimate_size(node_t node, uint64_t *size, uint32_t dep | |||
| 367 | return PLIST_ERR_INVALID_ARG; | 367 | return PLIST_ERR_INVALID_ARG; |
| 368 | } | 368 | } |
| 369 | 369 | ||
| 370 | if (depth > PLIST_MAX_NESTING_DEPTH) { | ||
| 371 | PLIST_OSTEP_WRITE_ERR("node tree is nested too deeply\n"); | ||
| 372 | return PLIST_ERR_MAX_NESTING; | ||
| 373 | } | ||
| 374 | |||
| 370 | if (hash_table_lookup(visited, node)) { | 375 | if (hash_table_lookup(visited, node)) { |
| 371 | PLIST_OSTEP_WRITE_ERR("circular reference detected\n"); | 376 | PLIST_OSTEP_WRITE_ERR("circular reference detected\n"); |
| 372 | return PLIST_ERR_CIRCULAR_REF; | 377 | return PLIST_ERR_CIRCULAR_REF; |
| @@ -511,7 +516,7 @@ struct _parse_ctx { | |||
| 511 | const char *start; | 516 | const char *start; |
| 512 | const char *pos; | 517 | const char *pos; |
| 513 | const char *end; | 518 | const char *end; |
| 514 | int err; | 519 | plist_err_t err; |
| 515 | uint32_t depth; | 520 | uint32_t depth; |
| 516 | }; | 521 | }; |
| 517 | typedef struct _parse_ctx* parse_ctx; | 522 | typedef struct _parse_ctx* parse_ctx; |
| @@ -568,50 +573,50 @@ static void parse_dict_data(parse_ctx ctx, plist_t dict) | |||
| 568 | } | 573 | } |
| 569 | key = NULL; | 574 | key = NULL; |
| 570 | ctx->err = node_from_openstep(ctx, &key); | 575 | ctx->err = node_from_openstep(ctx, &key); |
| 571 | if (ctx->err != 0) { | 576 | if (ctx->err != PLIST_ERR_SUCCESS) { |
| 572 | break; | 577 | break; |
| 573 | } | 578 | } |
| 574 | if (!PLIST_IS_STRING(key)) { | 579 | if (!PLIST_IS_STRING(key)) { |
| 575 | PLIST_OSTEP_ERR("Invalid type for dictionary key at offset %ld\n", (long int)(ctx->pos - ctx->start)); | 580 | PLIST_OSTEP_ERR("Invalid type for dictionary key at offset %ld\n", (long int)(ctx->pos - ctx->start)); |
| 576 | ctx->err++; | 581 | ctx->err = PLIST_ERR_PARSE; |
| 577 | break; | 582 | break; |
| 578 | } | 583 | } |
| 579 | parse_skip_ws(ctx); | 584 | parse_skip_ws(ctx); |
| 580 | if (ctx->pos >= ctx->end) { | 585 | if (ctx->pos >= ctx->end) { |
| 581 | PLIST_OSTEP_ERR("EOF while parsing dictionary '=' delimiter at offset %ld\n", (long int)(ctx->pos - ctx->start)); | 586 | PLIST_OSTEP_ERR("EOF while parsing dictionary '=' delimiter at offset %ld\n", (long int)(ctx->pos - ctx->start)); |
| 582 | ctx->err++; | 587 | ctx->err = PLIST_ERR_PARSE; |
| 583 | break; | 588 | break; |
| 584 | } | 589 | } |
| 585 | if (*ctx->pos != '=') { | 590 | if (*ctx->pos != '=') { |
| 586 | PLIST_OSTEP_ERR("Missing '=' while parsing dictionary item at offset %ld\n", (long int)(ctx->pos - ctx->start)); | 591 | PLIST_OSTEP_ERR("Missing '=' while parsing dictionary item at offset %ld\n", (long int)(ctx->pos - ctx->start)); |
| 587 | ctx->err++; | 592 | ctx->err = PLIST_ERR_PARSE; |
| 588 | break; | 593 | break; |
| 589 | } | 594 | } |
| 590 | ctx->pos++; | 595 | ctx->pos++; |
| 591 | if (ctx->pos >= ctx->end) { | 596 | if (ctx->pos >= ctx->end) { |
| 592 | PLIST_OSTEP_ERR("EOF while parsing dictionary item at offset %ld\n", (long int)(ctx->pos - ctx->start)); | 597 | PLIST_OSTEP_ERR("EOF while parsing dictionary item at offset %ld\n", (long int)(ctx->pos - ctx->start)); |
| 593 | ctx->err++; | 598 | ctx->err = PLIST_ERR_PARSE; |
| 594 | break; | 599 | break; |
| 595 | } | 600 | } |
| 596 | val = NULL; | 601 | val = NULL; |
| 597 | ctx->err = node_from_openstep(ctx, &val); | 602 | ctx->err = node_from_openstep(ctx, &val); |
| 598 | if (ctx->err != 0) { | 603 | if (ctx->err != PLIST_ERR_SUCCESS) { |
| 599 | break; | 604 | break; |
| 600 | } | 605 | } |
| 601 | if (!val) { | 606 | if (!val) { |
| 602 | PLIST_OSTEP_ERR("Missing value for dictionary item at offset %ld\n", (long int)(ctx->pos - ctx->start)); | 607 | PLIST_OSTEP_ERR("Missing value for dictionary item at offset %ld\n", (long int)(ctx->pos - ctx->start)); |
| 603 | ctx->err++; | 608 | ctx->err = PLIST_ERR_PARSE; |
| 604 | break; | 609 | break; |
| 605 | } | 610 | } |
| 606 | parse_skip_ws(ctx); | 611 | parse_skip_ws(ctx); |
| 607 | if (ctx->pos >= ctx->end) { | 612 | if (ctx->pos >= ctx->end) { |
| 608 | PLIST_OSTEP_ERR("EOF while parsing dictionary item terminator ';' at offset %ld\n", (long int)(ctx->pos - ctx->start)); | 613 | PLIST_OSTEP_ERR("EOF while parsing dictionary item terminator ';' at offset %ld\n", (long int)(ctx->pos - ctx->start)); |
| 609 | ctx->err++; | 614 | ctx->err = PLIST_ERR_PARSE; |
| 610 | break; | 615 | break; |
| 611 | } | 616 | } |
| 612 | if (*ctx->pos != ';') { | 617 | if (*ctx->pos != ';') { |
| 613 | PLIST_OSTEP_ERR("Missing terminating ';' while parsing dictionary item at offset %ld\n", (long int)(ctx->pos - ctx->start)); | 618 | PLIST_OSTEP_ERR("Missing terminating ';' while parsing dictionary item at offset %ld\n", (long int)(ctx->pos - ctx->start)); |
| 614 | ctx->err++; | 619 | ctx->err = PLIST_ERR_PARSE; |
| 615 | break; | 620 | break; |
| 616 | } | 621 | } |
| 617 | 622 | ||
| @@ -631,10 +636,10 @@ static plist_err_t node_from_openstep(parse_ctx ctx, plist_t *plist) | |||
| 631 | plist_t subnode = NULL; | 636 | plist_t subnode = NULL; |
| 632 | const char *p = NULL; | 637 | const char *p = NULL; |
| 633 | ctx->depth++; | 638 | ctx->depth++; |
| 634 | if (ctx->depth > 1000) { | 639 | if (ctx->depth > PLIST_MAX_NESTING_DEPTH) { |
| 635 | PLIST_OSTEP_ERR("Too many levels of recursion (%u) at offset %ld\n", ctx->depth, (long int)(ctx->pos - ctx->start)); | 640 | PLIST_OSTEP_ERR("Too many levels of recursion (%u) at offset %ld\n", ctx->depth, (long int)(ctx->pos - ctx->start)); |
| 636 | ctx->err++; | 641 | ctx->err = PLIST_ERR_MAX_NESTING; |
| 637 | return PLIST_ERR_PARSE; | 642 | return ctx->err; |
| 638 | } | 643 | } |
| 639 | while (ctx->pos < ctx->end && !ctx->err) { | 644 | while (ctx->pos < ctx->end && !ctx->err) { |
| 640 | parse_skip_ws(ctx); | 645 | parse_skip_ws(ctx); |
| @@ -652,12 +657,12 @@ static plist_err_t node_from_openstep(parse_ctx ctx, plist_t *plist) | |||
| 652 | } | 657 | } |
| 653 | if (ctx->pos >= ctx->end) { | 658 | if (ctx->pos >= ctx->end) { |
| 654 | PLIST_OSTEP_ERR("EOF while parsing dictionary terminator '}' at offset %ld\n", (long int)(ctx->pos - ctx->start)); | 659 | PLIST_OSTEP_ERR("EOF while parsing dictionary terminator '}' at offset %ld\n", (long int)(ctx->pos - ctx->start)); |
| 655 | ctx->err++; | 660 | ctx->err = PLIST_ERR_PARSE; |
| 656 | break; | 661 | break; |
| 657 | } | 662 | } |
| 658 | if (*ctx->pos != '}') { | 663 | if (*ctx->pos != '}') { |
| 659 | PLIST_OSTEP_ERR("Missing terminating '}' at offset %ld\n", (long int)(ctx->pos - ctx->start)); | 664 | PLIST_OSTEP_ERR("Missing terminating '}' at offset %ld\n", (long int)(ctx->pos - ctx->start)); |
| 660 | ctx->err++; | 665 | ctx->err = PLIST_ERR_PARSE; |
| 661 | goto err_out; | 666 | goto err_out; |
| 662 | } | 667 | } |
| 663 | ctx->pos++; | 668 | ctx->pos++; |
| @@ -675,11 +680,11 @@ static plist_err_t node_from_openstep(parse_ctx ctx, plist_t *plist) | |||
| 675 | break; | 680 | break; |
| 676 | } | 681 | } |
| 677 | ctx->err = node_from_openstep(ctx, &tmp); | 682 | ctx->err = node_from_openstep(ctx, &tmp); |
| 678 | if (ctx->err != 0) { | 683 | if (ctx->err != PLIST_ERR_SUCCESS) { |
| 679 | break; | 684 | break; |
| 680 | } | 685 | } |
| 681 | if (!tmp) { | 686 | if (!tmp) { |
| 682 | ctx->err++; | 687 | ctx->err = PLIST_ERR_PARSE; |
| 683 | break; | 688 | break; |
| 684 | } | 689 | } |
| 685 | plist_array_append_item(subnode, tmp); | 690 | plist_array_append_item(subnode, tmp); |
| @@ -687,7 +692,7 @@ static plist_err_t node_from_openstep(parse_ctx ctx, plist_t *plist) | |||
| 687 | parse_skip_ws(ctx); | 692 | parse_skip_ws(ctx); |
| 688 | if (ctx->pos >= ctx->end) { | 693 | if (ctx->pos >= ctx->end) { |
| 689 | PLIST_OSTEP_ERR("EOF while parsing array item delimiter ',' at offset %ld\n", (long int)(ctx->pos - ctx->start)); | 694 | PLIST_OSTEP_ERR("EOF while parsing array item delimiter ',' at offset %ld\n", (long int)(ctx->pos - ctx->start)); |
| 690 | ctx->err++; | 695 | ctx->err = PLIST_ERR_PARSE; |
| 691 | break; | 696 | break; |
| 692 | } | 697 | } |
| 693 | if (*ctx->pos != ',') { | 698 | if (*ctx->pos != ',') { |
| @@ -697,17 +702,17 @@ static plist_err_t node_from_openstep(parse_ctx ctx, plist_t *plist) | |||
| 697 | } | 702 | } |
| 698 | plist_free(tmp); | 703 | plist_free(tmp); |
| 699 | tmp = NULL; | 704 | tmp = NULL; |
| 700 | if (ctx->err) { | 705 | if (ctx->err != PLIST_ERR_SUCCESS) { |
| 701 | goto err_out; | 706 | goto err_out; |
| 702 | } | 707 | } |
| 703 | if (ctx->pos >= ctx->end) { | 708 | if (ctx->pos >= ctx->end) { |
| 704 | PLIST_OSTEP_ERR("EOF while parsing array terminator ')' at offset %ld\n", (long int)(ctx->pos - ctx->start)); | 709 | PLIST_OSTEP_ERR("EOF while parsing array terminator ')' at offset %ld\n", (long int)(ctx->pos - ctx->start)); |
| 705 | ctx->err++; | 710 | ctx->err = PLIST_ERR_PARSE; |
| 706 | break; | 711 | break; |
| 707 | } | 712 | } |
| 708 | if (*ctx->pos != ')') { | 713 | if (*ctx->pos != ')') { |
| 709 | PLIST_OSTEP_ERR("Missing terminating ')' at offset %ld\n", (long int)(ctx->pos - ctx->start)); | 714 | PLIST_OSTEP_ERR("Missing terminating ')' at offset %ld\n", (long int)(ctx->pos - ctx->start)); |
| 710 | ctx->err++; | 715 | ctx->err = PLIST_ERR_PARSE; |
| 711 | goto err_out; | 716 | goto err_out; |
| 712 | } | 717 | } |
| 713 | ctx->pos++; | 718 | ctx->pos++; |
| @@ -722,7 +727,7 @@ static plist_err_t node_from_openstep(parse_ctx ctx, plist_t *plist) | |||
| 722 | parse_skip_ws(ctx); | 727 | parse_skip_ws(ctx); |
| 723 | if (ctx->pos >= ctx->end) { | 728 | if (ctx->pos >= ctx->end) { |
| 724 | PLIST_OSTEP_ERR("EOF while parsing data terminator '>' at offset %ld\n", (long int)(ctx->pos - ctx->start)); | 729 | PLIST_OSTEP_ERR("EOF while parsing data terminator '>' at offset %ld\n", (long int)(ctx->pos - ctx->start)); |
| 725 | ctx->err++; | 730 | ctx->err = PLIST_ERR_PARSE; |
| 726 | break; | 731 | break; |
| 727 | } | 732 | } |
| 728 | if (*ctx->pos == '>') { | 733 | if (*ctx->pos == '>') { |
| @@ -730,19 +735,19 @@ static plist_err_t node_from_openstep(parse_ctx ctx, plist_t *plist) | |||
| 730 | } | 735 | } |
| 731 | if (!isxdigit(*ctx->pos)) { | 736 | if (!isxdigit(*ctx->pos)) { |
| 732 | PLIST_OSTEP_ERR("Invalid byte group in data at offset %ld\n", (long int)(ctx->pos - ctx->start)); | 737 | PLIST_OSTEP_ERR("Invalid byte group in data at offset %ld\n", (long int)(ctx->pos - ctx->start)); |
| 733 | ctx->err++; | 738 | ctx->err = PLIST_ERR_PARSE; |
| 734 | break; | 739 | break; |
| 735 | } | 740 | } |
| 736 | uint8_t b = HEX_DIGIT(*ctx->pos); | 741 | uint8_t b = HEX_DIGIT(*ctx->pos); |
| 737 | ctx->pos++; | 742 | ctx->pos++; |
| 738 | if (ctx->pos >= ctx->end) { | 743 | if (ctx->pos >= ctx->end) { |
| 739 | PLIST_OSTEP_ERR("Unexpected end of data at offset %ld\n", (long int)(ctx->pos - ctx->start)); | 744 | PLIST_OSTEP_ERR("Unexpected end of data at offset %ld\n", (long int)(ctx->pos - ctx->start)); |
| 740 | ctx->err++; | 745 | ctx->err = PLIST_ERR_PARSE; |
| 741 | break; | 746 | break; |
| 742 | } | 747 | } |
| 743 | if (!isxdigit(*ctx->pos)) { | 748 | if (!isxdigit(*ctx->pos)) { |
| 744 | PLIST_OSTEP_ERR("Invalid byte group in data at offset %ld\n", (long int)(ctx->pos - ctx->start)); | 749 | PLIST_OSTEP_ERR("Invalid byte group in data at offset %ld\n", (long int)(ctx->pos - ctx->start)); |
| 745 | ctx->err++; | 750 | ctx->err = PLIST_ERR_PARSE; |
| 746 | break; | 751 | break; |
| 747 | } | 752 | } |
| 748 | b = (b << 4) + HEX_DIGIT(*ctx->pos); | 753 | b = (b << 4) + HEX_DIGIT(*ctx->pos); |
| @@ -758,14 +763,14 @@ static plist_err_t node_from_openstep(parse_ctx ctx, plist_t *plist) | |||
| 758 | byte_array_free(bytes); | 763 | byte_array_free(bytes); |
| 759 | plist_free_data(data); | 764 | plist_free_data(data); |
| 760 | PLIST_OSTEP_ERR("EOF while parsing data terminator '>' at offset %ld\n", (long int)(ctx->pos - ctx->start)); | 765 | PLIST_OSTEP_ERR("EOF while parsing data terminator '>' at offset %ld\n", (long int)(ctx->pos - ctx->start)); |
| 761 | ctx->err++; | 766 | ctx->err = PLIST_ERR_PARSE; |
| 762 | goto err_out; | 767 | goto err_out; |
| 763 | } | 768 | } |
| 764 | if (*ctx->pos != '>') { | 769 | if (*ctx->pos != '>') { |
| 765 | byte_array_free(bytes); | 770 | byte_array_free(bytes); |
| 766 | plist_free_data(data); | 771 | plist_free_data(data); |
| 767 | PLIST_OSTEP_ERR("Missing terminating '>' at offset %ld\n", (long int)(ctx->pos - ctx->start)); | 772 | PLIST_OSTEP_ERR("Missing terminating '>' at offset %ld\n", (long int)(ctx->pos - ctx->start)); |
| 768 | ctx->err++; | 773 | ctx->err = PLIST_ERR_PARSE; |
| 769 | goto err_out; | 774 | goto err_out; |
| 770 | } | 775 | } |
| 771 | ctx->pos++; | 776 | ctx->pos++; |
| @@ -793,13 +798,13 @@ static plist_err_t node_from_openstep(parse_ctx ctx, plist_t *plist) | |||
| 793 | if (ctx->pos >= ctx->end) { | 798 | if (ctx->pos >= ctx->end) { |
| 794 | plist_free_data(data); | 799 | plist_free_data(data); |
| 795 | PLIST_OSTEP_ERR("EOF while parsing quoted string at offset %ld\n", (long int)(ctx->pos - ctx->start)); | 800 | PLIST_OSTEP_ERR("EOF while parsing quoted string at offset %ld\n", (long int)(ctx->pos - ctx->start)); |
| 796 | ctx->err++; | 801 | ctx->err = PLIST_ERR_PARSE; |
| 797 | goto err_out; | 802 | goto err_out; |
| 798 | } | 803 | } |
| 799 | if (*ctx->pos != c) { | 804 | if (*ctx->pos != c) { |
| 800 | plist_free_data(data); | 805 | plist_free_data(data); |
| 801 | PLIST_OSTEP_ERR("Missing closing quote (%c) at offset %ld\n", c, (long int)(ctx->pos - ctx->start)); | 806 | PLIST_OSTEP_ERR("Missing closing quote (%c) at offset %ld\n", c, (long int)(ctx->pos - ctx->start)); |
| 802 | ctx->err++; | 807 | ctx->err = PLIST_ERR_PARSE; |
| 803 | goto err_out; | 808 | goto err_out; |
| 804 | } | 809 | } |
| 805 | size_t slen = ctx->pos - p; | 810 | size_t slen = ctx->pos - p; |
| @@ -900,7 +905,7 @@ static plist_err_t node_from_openstep(parse_ctx ctx, plist_t *plist) | |||
| 900 | } else { | 905 | } else { |
| 901 | plist_free_data(data); | 906 | plist_free_data(data); |
| 902 | PLIST_OSTEP_ERR("Unexpected character when parsing unquoted string at offset %ld\n", (long int)(ctx->pos - ctx->start)); | 907 | PLIST_OSTEP_ERR("Unexpected character when parsing unquoted string at offset %ld\n", (long int)(ctx->pos - ctx->start)); |
| 903 | ctx->err++; | 908 | ctx->err = PLIST_ERR_PARSE; |
| 904 | break; | 909 | break; |
| 905 | } | 910 | } |
| 906 | } | 911 | } |
| @@ -909,11 +914,11 @@ static plist_err_t node_from_openstep(parse_ctx ctx, plist_t *plist) | |||
| 909 | ctx->depth--; | 914 | ctx->depth--; |
| 910 | 915 | ||
| 911 | err_out: | 916 | err_out: |
| 912 | if (ctx->err) { | 917 | if (ctx->err != PLIST_ERR_SUCCESS) { |
| 913 | plist_free(subnode); | 918 | plist_free(subnode); |
| 914 | plist_free(*plist); | 919 | plist_free(*plist); |
| 915 | *plist = NULL; | 920 | *plist = NULL; |
| 916 | return PLIST_ERR_PARSE; | 921 | return ctx->err; |
| 917 | } | 922 | } |
| 918 | return PLIST_ERR_SUCCESS; | 923 | return PLIST_ERR_SUCCESS; |
| 919 | } | 924 | } |
