diff options
| author | 2026-01-21 12:24:52 +0100 | |
|---|---|---|
| committer | 2026-01-21 12:26:13 +0100 | |
| commit | c0f9df912d2a4001e56321fb53615e6474b32232 (patch) | |
| tree | ce3d46fa9ac9e173d2f86451037d1456205c067f /src/jplist.c | |
| parent | c18d6b323e8121c041e8b88d2ea6b6e85ca41274 (diff) | |
| download | libplist-c0f9df912d2a4001e56321fb53615e6474b32232.tar.gz libplist-c0f9df912d2a4001e56321fb53615e6474b32232.tar.bz2 | |
jsmn: use size_t for token offsets and harden against overflow
Use size_t for token start/end offsets instead of int, replace the -1
sentinel with SIZE_MAX, and add a defensive guard against offset
wraparound. This prevents overflow when parsing very large JSON inputs.
This addresses issue #282.
Credit to @ylwango613 for repporting.
Diffstat (limited to 'src/jplist.c')
| -rw-r--r-- | src/jplist.c | 23 |
1 files changed, 17 insertions, 6 deletions
diff --git a/src/jplist.c b/src/jplist.c index 9a40844..2c88756 100644 --- a/src/jplist.c +++ b/src/jplist.c | |||
| @@ -719,8 +719,8 @@ static plist_t parse_array(const char* js, jsmntok_info_t* ti, int* index, uint3 | |||
| 719 | return NULL; | 719 | return NULL; |
| 720 | } | 720 | } |
| 721 | plist_t arr = plist_new_array(); | 721 | plist_t arr = plist_new_array(); |
| 722 | int num_tokens = ti->tokens[*index].size; | 722 | size_t num_tokens = ti->tokens[*index].size; |
| 723 | int num; | 723 | size_t num; |
| 724 | int j = (*index)+1; | 724 | int j = (*index)+1; |
| 725 | for (num = 0; num < num_tokens; num++) { | 725 | for (num = 0; num < num_tokens; num++) { |
| 726 | if (j >= ti->count) { | 726 | if (j >= ti->count) { |
| @@ -770,8 +770,8 @@ static plist_t parse_object(const char* js, jsmntok_info_t* ti, int* index, uint | |||
| 770 | ti->err = PLIST_ERR_MAX_NESTING; | 770 | ti->err = PLIST_ERR_MAX_NESTING; |
| 771 | return NULL; | 771 | return NULL; |
| 772 | } | 772 | } |
| 773 | int num_tokens = ti->tokens[*index].size; | 773 | size_t num_tokens = ti->tokens[*index].size; |
| 774 | int num; | 774 | size_t num; |
| 775 | int j = (*index)+1; | 775 | int j = (*index)+1; |
| 776 | if (num_tokens % 2 != 0) { | 776 | if (num_tokens % 2 != 0) { |
| 777 | PLIST_JSON_ERR("%s: number of children must be even\n", __func__); | 777 | PLIST_JSON_ERR("%s: number of children must be even\n", __func__); |
| @@ -844,14 +844,15 @@ plist_err_t plist_from_json(const char *json, uint32_t length, plist_t * plist) | |||
| 844 | 844 | ||
| 845 | jsmn_parser parser; | 845 | jsmn_parser parser; |
| 846 | jsmn_init(&parser); | 846 | jsmn_init(&parser); |
| 847 | int maxtoks = 256; | 847 | unsigned int maxtoks = 256; |
| 848 | int curtoks = 0; | 848 | unsigned int curtoks = 0; |
| 849 | int r = 0; | 849 | int r = 0; |
| 850 | jsmntok_t *tokens = NULL; | 850 | jsmntok_t *tokens = NULL; |
| 851 | 851 | ||
| 852 | do { | 852 | do { |
| 853 | jsmntok_t* newtokens = (jsmntok_t*)realloc(tokens, sizeof(jsmntok_t)*maxtoks); | 853 | jsmntok_t* newtokens = (jsmntok_t*)realloc(tokens, sizeof(jsmntok_t)*maxtoks); |
| 854 | if (!newtokens) { | 854 | if (!newtokens) { |
| 855 | free(tokens); | ||
| 855 | PLIST_JSON_ERR("%s: Out of memory\n", __func__); | 856 | PLIST_JSON_ERR("%s: Out of memory\n", __func__); |
| 856 | return PLIST_ERR_NO_MEM; | 857 | return PLIST_ERR_NO_MEM; |
| 857 | } | 858 | } |
| @@ -861,8 +862,14 @@ plist_err_t plist_from_json(const char *json, uint32_t length, plist_t * plist) | |||
| 861 | 862 | ||
| 862 | r = jsmn_parse(&parser, json, length, tokens, maxtoks); | 863 | r = jsmn_parse(&parser, json, length, tokens, maxtoks); |
| 863 | if (r == JSMN_ERROR_NOMEM) { | 864 | if (r == JSMN_ERROR_NOMEM) { |
| 865 | if (maxtoks > (unsigned int)INT_MAX - 16) { | ||
| 866 | free(tokens); | ||
| 867 | return PLIST_ERR_NO_MEM; | ||
| 868 | } | ||
| 864 | maxtoks+=16; | 869 | maxtoks+=16; |
| 865 | continue; | 870 | continue; |
| 871 | } else if (r < 0) { | ||
| 872 | break; | ||
| 866 | } | 873 | } |
| 867 | } while (r == JSMN_ERROR_NOMEM); | 874 | } while (r == JSMN_ERROR_NOMEM); |
| 868 | 875 | ||
| @@ -879,6 +886,10 @@ plist_err_t plist_from_json(const char *json, uint32_t length, plist_t * plist) | |||
| 879 | PLIST_JSON_ERR("%s: Incomplete JSON, more bytes expected\n", __func__); | 886 | PLIST_JSON_ERR("%s: Incomplete JSON, more bytes expected\n", __func__); |
| 880 | free(tokens); | 887 | free(tokens); |
| 881 | return PLIST_ERR_PARSE; | 888 | return PLIST_ERR_PARSE; |
| 889 | case JSMN_ERROR_LIMIT: | ||
| 890 | PLIST_JSON_ERR("%s: Input data too large\n", __func__); | ||
| 891 | free(tokens); | ||
| 892 | return PLIST_ERR_PARSE; | ||
| 882 | default: | 893 | default: |
| 883 | break; | 894 | break; |
| 884 | } | 895 | } |
