diff options
| author | 2026-11-05 12:01:59 +0100 | |
|---|---|---|
| committer | 2026-11-05 12:01:59 +0100 | |
| commit | 4dc93bc274867b42aa82fc91ee4d5878d23a3f36 (patch) | |
| tree | 11c566b80561bb44018bceea028ea03d47e53458 | |
| parent | f252f243f9774b2b830172b49aa340d6994d8081 (diff) | |
| download | libplist-4dc93bc274867b42aa82fc91ee4d5878d23a3f36.tar.gz libplist-4dc93bc274867b42aa82fc91ee4d5878d23a3f36.tar.bz2 | |
Harden allocation failure handling and size arithmetic
- check malloc/calloc/realloc results in constructors, the JSON and
OpenStep writers, the XML text-part helpers and base64 decoding, and
return PLIST_ERR_NO_MEM instead of asserting or dereferencing NULL
- guard size calculations against overflow in the UTF-8/UTF-16BE
conversions, ptrarray, and bytearray
- make hash_table_insert() report failure instead of silently dropping
the entry; writers propagate PLIST_ERR_NO_MEM, and dict lookup caches
are discarded so lookups fall back to a linear search
- don't overwrite an earlier parse error code in the XML parser, and
report PLIST_ERR_NO_MEM when reading text content fails for lack of memory
- OpenStep parser: check node/data/string allocations and dict/array
insertion results
Based on #323 by bg0d-droid.
Co-authored-by: bg0d-droid <81825610+bg0d-droid@users.noreply.github.com>
| -rw-r--r-- | src/bplist.c | 19 | ||||
| -rw-r--r-- | src/bytearray.c | 38 | ||||
| -rw-r--r-- | src/hashtable.c | 13 | ||||
| -rw-r--r-- | src/hashtable.h | 3 | ||||
| -rw-r--r-- | src/jplist.c | 13 | ||||
| -rw-r--r-- | src/oplist.c | 82 | ||||
| -rw-r--r-- | src/out-default.c | 4 | ||||
| -rw-r--r-- | src/out-limd.c | 4 | ||||
| -rw-r--r-- | src/out-plutil.c | 4 | ||||
| -rw-r--r-- | src/plist.c | 36 | ||||
| -rw-r--r-- | src/ptrarray.c | 24 | ||||
| -rw-r--r-- | src/xplist.c | 97 |
12 files changed, 291 insertions, 46 deletions
diff --git a/src/bplist.c b/src/bplist.c index e5ace3a..45caae4 100644 --- a/src/bplist.c +++ b/src/bplist.c @@ -397,7 +397,11 @@ static char *plist_utf16be_to_utf8(uint16_t *unistr, size_t len, size_t *items_r uint32_t w; int read_lead_surrogate = 0; - /* allocate with enough space */ + /* allocate with enough space, guarding against integer overflow */ + if (len > (SIZE_MAX / 4) - 1) { + PLIST_BIN_ERR("%s: Input length %" PRIu64 " is too large\n", __func__, (uint64_t)len); + return NULL; + } outbuf = (char*)malloc(4*(len+1)); if (!outbuf) { PLIST_BIN_ERR("%s: Could not allocate %" PRIu64 " bytes\n", __func__, (uint64_t)(4*(len+1))); @@ -1112,13 +1116,18 @@ static plist_err_t serialize_plist(node_t node, void* data, uint32_t depth) } // mark as active - hash_table_insert(ser->in_stack, node, (void*)1); + if (hash_table_insert(ser->in_stack, node, (void*)1) < 0) { + return PLIST_ERR_NO_MEM; + } // insert new ref index_val = (uint64_t *) malloc(sizeof(uint64_t)); if (!index_val) return PLIST_ERR_NO_MEM; *index_val = ser->objects->len; - hash_table_insert(ser->ref_table, node, index_val); + if (hash_table_insert(ser->ref_table, node, index_val) < 0) { + free(index_val); + return PLIST_ERR_NO_MEM; + } // now append current node to object array if (ptr_array_add(ser->objects, node) < 0) { @@ -1228,6 +1237,10 @@ static uint16_t *plist_utf8_to_utf16be(const unsigned char *unistr, size_t size, unsigned char c2; unsigned char c3; + if (size > (SIZE_MAX / sizeof(uint16_t) - 1) / 2) { + PLIST_BIN_ERR("%s: Input size %" PRIu64 " is too large\n", __func__, (uint64_t)size); + return NULL; + } outbuf = (uint16_t*)malloc(((size*2)+1)*sizeof(uint16_t)); if (!outbuf) { PLIST_BIN_ERR("%s: Could not allocate %" PRIu64 " bytes\n", __func__, (uint64_t)((size*2)+1)*sizeof(uint16_t)); diff --git a/src/bytearray.c b/src/bytearray.c index 89c5ec7..19303e0 100644 --- a/src/bytearray.c +++ b/src/bytearray.c @@ -19,6 +19,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include <string.h> +#include <stdint.h> #include "bytearray.h" #define PAGE_SIZE 4096 @@ -26,8 +27,24 @@ bytearray_t *byte_array_new(size_t initial) { bytearray_t *a = (bytearray_t*)malloc(sizeof(bytearray_t)); - a->capacity = (initial > PAGE_SIZE) ? (initial+(PAGE_SIZE-1)) & (~(PAGE_SIZE-1)) : PAGE_SIZE; + if (!a) { + return NULL; + } + if (initial > PAGE_SIZE) { + if (initial > SIZE_MAX - (PAGE_SIZE-1)) { + /* rounding up to the page size would overflow */ + free(a); + return NULL; + } + a->capacity = (initial+(PAGE_SIZE-1)) & (~(PAGE_SIZE-1)); + } else { + a->capacity = PAGE_SIZE; + } a->data = malloc(a->capacity); + if (!a->data) { + free(a); + return NULL; + } a->len = 0; a->stream = NULL; return a; @@ -36,6 +53,9 @@ bytearray_t *byte_array_new(size_t initial) bytearray_t *byte_array_new_for_stream(FILE *stream) { bytearray_t *a = (bytearray_t*)malloc(sizeof(bytearray_t)); + if (!a) { + return NULL; + } a->capacity = (size_t)-1; a->data = NULL; a->len = 0; @@ -57,9 +77,15 @@ void byte_array_grow(bytearray_t *ba, size_t amount) if (ba->stream || !ba->data) { return; } - size_t increase = (amount > PAGE_SIZE) ? (amount+(PAGE_SIZE-1)) & (~(PAGE_SIZE-1)) : PAGE_SIZE; - if (increase < amount || ba->capacity + increase < ba->capacity) { - /* size overflow */ + size_t increase; + if (amount > PAGE_SIZE) { + /* round up to the page size, guarding against integer overflow */ + increase = (amount <= SIZE_MAX - (PAGE_SIZE-1)) ? ((amount+(PAGE_SIZE-1)) & (~(PAGE_SIZE-1))) : 0; + } else { + increase = PAGE_SIZE; + } + if (increase == 0 || increase > SIZE_MAX - ba->capacity) { + /* integer overflow */ free(ba->data); ba->data = NULL; ba->len = 0; @@ -101,6 +127,10 @@ void byte_array_append(bytearray_t *ba, void *buf, size_t len) #endif } } else { + if (len > SIZE_MAX - ba->len) { + /* integer overflow when computing the new total length */ + return; + } size_t remaining = ba->capacity-ba->len; if (len > remaining) { size_t needed = len - remaining; diff --git a/src/hashtable.c b/src/hashtable.c index dd6dbfc..e3f9b98 100644 --- a/src/hashtable.c +++ b/src/hashtable.c @@ -24,6 +24,9 @@ hashtable_t* hash_table_new(hash_func_t hash_func, compare_func_t compare_func, { hashtable_t* ht = (hashtable_t*)malloc(sizeof(hashtable_t)); int i; + if (!ht) { + return NULL; + } for (i = 0; i < 4096; i++) { ht->entries[i] = NULL; } @@ -55,9 +58,9 @@ void hash_table_destroy(hashtable_t *ht) free(ht); } -void hash_table_insert(hashtable_t* ht, void *key, void *value) +int hash_table_insert(hashtable_t* ht, void *key, void *value) { - if (!ht || !key) return; + if (!ht || !key) return -1; unsigned int hash = ht->hash_func(key); @@ -69,7 +72,7 @@ void hash_table_insert(hashtable_t* ht, void *key, void *value) if (ht->compare_func(e->key, key)) { // element already present. replace value. e->value = value; - return; + return 0; } e = e->next; } @@ -78,6 +81,9 @@ void hash_table_insert(hashtable_t* ht, void *key, void *value) // make a new entry. hashentry_t* entry = (hashentry_t*)malloc(sizeof(hashentry_t)); + if (!entry) { + return -1; + } entry->key = key; entry->value = value; if (!ht->entries[idx0]) { @@ -89,6 +95,7 @@ void hash_table_insert(hashtable_t* ht, void *key, void *value) } ht->entries[idx0] = entry; ht->count++; + return 0; } void* hash_table_lookup(hashtable_t* ht, void *key) diff --git a/src/hashtable.h b/src/hashtable.h index 514cfec..1b6976d 100644 --- a/src/hashtable.h +++ b/src/hashtable.h @@ -43,7 +43,8 @@ typedef struct hashtable_t { hashtable_t* hash_table_new(hash_func_t hash_func, compare_func_t compare_func, free_func_t free_func); void hash_table_destroy(hashtable_t *ht); -void hash_table_insert(hashtable_t* ht, void *key, void *value); +/* returns 0 on success, -1 on error (invalid argument or out of memory) */ +int hash_table_insert(hashtable_t* ht, void *key, void *value); void* hash_table_lookup(hashtable_t* ht, void *key); void hash_table_remove(hashtable_t* ht, void *key); diff --git a/src/jplist.c b/src/jplist.c index d065f4d..fe0b5cb 100644 --- a/src/jplist.c +++ b/src/jplist.c @@ -126,6 +126,9 @@ static plist_err_t node_to_json(node_t node, bytearray_t **outbuf, uint32_t dept case PLIST_INT: val = (char*)malloc(64); + if (!val) { + return PLIST_ERR_NO_MEM; + } if (node_data->length == 16) { val_len = snprintf(val, 64, "%" PRIu64, node_data->intval); } else { @@ -137,6 +140,9 @@ static plist_err_t node_to_json(node_t node, bytearray_t **outbuf, uint32_t dept case PLIST_REAL: val = (char*)malloc(64); + if (!val) { + return PLIST_ERR_NO_MEM; + } val_len = dtostr(val, 64, node_data->realval); str_buf_append(*outbuf, val, val_len); free(val); @@ -285,6 +291,9 @@ static plist_err_t node_to_json(node_t node, bytearray_t **outbuf, uint32_t dept case PLIST_UID: if (coerce) { val = (char*)malloc(64); + if (!val) { + return PLIST_ERR_NO_MEM; + } if (node_data->length == 16) { val_len = snprintf(val, 64, "%" PRIu64, node_data->intval); } else { @@ -322,7 +331,9 @@ static plist_err_t _node_estimate_size(node_t node, uint64_t *size, uint32_t dep } // mark as visited - hash_table_insert(visited, node, (void*)1); + if (hash_table_insert(visited, node, (void*)1) < 0) { + return PLIST_ERR_NO_MEM; + } data = plist_get_data(node); if (node->children) { diff --git a/src/oplist.c b/src/oplist.c index a635302..51af4df 100644 --- a/src/oplist.c +++ b/src/oplist.c @@ -140,6 +140,9 @@ static plist_err_t node_to_openstep(node_t node, bytearray_t **outbuf, uint32_t { case PLIST_INT: val = (char*)malloc(64); + if (!val) { + return PLIST_ERR_NO_MEM; + } if (node_data->length == 16) { val_len = snprintf(val, 64, "%" PRIu64, node_data->intval); } else { @@ -151,6 +154,9 @@ static plist_err_t node_to_openstep(node_t node, bytearray_t **outbuf, uint32_t case PLIST_REAL: val = (char*)malloc(64); + if (!val) { + return PLIST_ERR_NO_MEM; + } val_len = dtostr(val, 64, node_data->realval); str_buf_append(*outbuf, val, val_len); free(val); @@ -330,6 +336,9 @@ static plist_err_t node_to_openstep(node_t node, bytearray_t **outbuf, uint32_t case PLIST_UID: if (coerce) { val = (char*)malloc(64); + if (!val) { + return PLIST_ERR_NO_MEM; + } if (node_data->length == 16) { val_len = snprintf(val, 64, "%" PRIu64, node_data->intval); } else { @@ -368,7 +377,9 @@ static plist_err_t _node_estimate_size(node_t node, uint64_t *size, uint32_t dep } // mark as visited - hash_table_insert(visited, node, (void*)1); + if (hash_table_insert(visited, node, (void*)1) < 0) { + return PLIST_ERR_NO_MEM; + } data = plist_get_data(node); if (node->children) { @@ -655,7 +666,10 @@ static void parse_dict_data(parse_ctx ctx, plist_t dict) break; } - plist_dict_set_item(dict, plist_get_string_ptr(key, NULL), val); + if (plist_dict_set_item(dict, plist_get_string_ptr(key, NULL), val) != PLIST_ERR_SUCCESS) { + ctx->err = PLIST_ERR_NO_MEM; + break; + } plist_free(key); key = NULL; val = NULL; @@ -682,9 +696,20 @@ static plist_err_t node_from_openstep(parse_ctx ctx, plist_t *plist) break; } plist_data_t data = plist_new_plist_data(); + if (!data) { + PLIST_OSTEP_ERR("Out of memory\n"); + ctx->err = PLIST_ERR_NO_MEM; + goto err_out; + } if (*ctx->pos == '{') { data->type = PLIST_DICT; subnode = plist_new_node(data); + if (!subnode) { + plist_free_data(data); + PLIST_OSTEP_ERR("Out of memory\n"); + ctx->err = PLIST_ERR_NO_MEM; + goto err_out; + } ctx->pos++; parse_dict_data(ctx, subnode); if (ctx->err) { @@ -707,6 +732,12 @@ static plist_err_t node_from_openstep(parse_ctx ctx, plist_t *plist) } else if (*ctx->pos == '(') { data->type = PLIST_ARRAY; subnode = plist_new_node(data); + if (!subnode) { + plist_free_data(data); + PLIST_OSTEP_ERR("Out of memory\n"); + ctx->err = PLIST_ERR_NO_MEM; + goto err_out; + } ctx->pos++; plist_t tmp = NULL; while (ctx->pos < ctx->end && !ctx->err) { @@ -722,7 +753,10 @@ static plist_err_t node_from_openstep(parse_ctx ctx, plist_t *plist) ctx->err = PLIST_ERR_PARSE; break; } - plist_array_append_item(subnode, tmp); + if (plist_array_append_item(subnode, tmp) != PLIST_ERR_SUCCESS) { + ctx->err = PLIST_ERR_NO_MEM; + break; + } tmp = NULL; parse_skip_ws(ctx); if (ctx->pos >= ctx->end) { @@ -758,6 +792,12 @@ static plist_err_t node_from_openstep(parse_ctx ctx, plist_t *plist) data->type = PLIST_DATA; ctx->pos++; bytearray_t *bytes = byte_array_new(256); + if (!bytes) { + plist_free_data(data); + PLIST_OSTEP_ERR("Out of memory\n"); + ctx->err = PLIST_ERR_NO_MEM; + goto err_out; + } while (ctx->pos < ctx->end && !ctx->err) { parse_skip_ws(ctx); if (ctx->pos >= ctx->end) { @@ -819,6 +859,12 @@ static plist_err_t node_from_openstep(parse_ctx ctx, plist_t *plist) bytes->data = NULL; byte_array_free(bytes); *plist = plist_new_node(data); + if (!*plist) { + plist_free_data(data); + PLIST_OSTEP_ERR("Out of memory\n"); + ctx->err = PLIST_ERR_NO_MEM; + goto err_out; + } parse_skip_ws(ctx); break; } else if (*ctx->pos == '"' || *ctx->pos == '\'') { @@ -850,6 +896,12 @@ static plist_err_t node_from_openstep(parse_ctx ctx, plist_t *plist) size_t slen = ctx->pos - p; ctx->pos++; // skip the closing quote char* strbuf = (char*)malloc(slen+1); + if (!strbuf) { + plist_free_data(data); + PLIST_OSTEP_ERR("Out of memory\n"); + ctx->err = PLIST_ERR_NO_MEM; + goto err_out; + } if (num_escapes > 0) { size_t i = 0; size_t o = 0; @@ -921,6 +973,12 @@ static plist_err_t node_from_openstep(parse_ctx ctx, plist_t *plist) data->strval = strbuf; data->length = slen; *plist = plist_new_node(data); + if (!*plist) { + plist_free_data(data); + PLIST_OSTEP_ERR("Out of memory\n"); + ctx->err = PLIST_ERR_NO_MEM; + goto err_out; + } parse_skip_ws(ctx); break; } else { @@ -938,8 +996,20 @@ static plist_err_t node_from_openstep(parse_ctx ctx, plist_t *plist) if (slen > 0) { data->type = PLIST_STRING; data->strval = strndup(p, slen); + if (!data->strval) { + plist_free_data(data); + PLIST_OSTEP_ERR("Out of memory\n"); + ctx->err = PLIST_ERR_NO_MEM; + goto err_out; + } data->length = slen; *plist = plist_new_node(data); + if (!*plist) { + plist_free_data(data); + PLIST_OSTEP_ERR("Out of memory\n"); + ctx->err = PLIST_ERR_NO_MEM; + goto err_out; + } parse_skip_ws(ctx); break; } else { @@ -980,11 +1050,17 @@ plist_err_t plist_from_openstep(const char *plist_ostep, uint32_t length, plist_ if (!*plist) { /* whitespace only file is considered an empty dictionary */ *plist = plist_new_dict(); + if (!*plist) { + err = PLIST_ERR_NO_MEM; + } } else if (ctx.pos < ctx.end && *ctx.pos == '=') { /* attempt to parse this as 'strings' data */ plist_free(*plist); *plist = NULL; plist_t pl = plist_new_dict(); + if (!pl) { + return PLIST_ERR_NO_MEM; + } ctx.pos = plist_ostep; parse_dict_data(&ctx, pl); if (ctx.err > 0) { diff --git a/src/out-default.c b/src/out-default.c index 845be3c..9fe8334 100644 --- a/src/out-default.c +++ b/src/out-default.c @@ -302,7 +302,9 @@ static plist_err_t _node_estimate_size(node_t node, uint64_t *size, uint32_t dep } // mark as visited - hash_table_insert(visited, node, (void*)1); + if (hash_table_insert(visited, node, (void*)1) < 0) { + return PLIST_ERR_NO_MEM; + } data = plist_get_data(node); if (node->children) { diff --git a/src/out-limd.c b/src/out-limd.c index 259d036..dd4c252 100644 --- a/src/out-limd.c +++ b/src/out-limd.c @@ -293,7 +293,9 @@ static plist_err_t _node_estimate_size(node_t node, uint64_t *size, uint32_t dep } // mark as visited - hash_table_insert(visited, node, (void*)1); + if (hash_table_insert(visited, node, (void*)1) < 0) { + return PLIST_ERR_NO_MEM; + } data = plist_get_data(node); if (node->children) { diff --git a/src/out-plutil.c b/src/out-plutil.c index ef17b7c..52edaaf 100644 --- a/src/out-plutil.c +++ b/src/out-plutil.c @@ -311,7 +311,9 @@ static plist_err_t _node_estimate_size(node_t node, uint64_t *size, uint32_t dep } // mark as visited - hash_table_insert(visited, node, (void*)1); + if (hash_table_insert(visited, node, (void*)1) < 0) { + return PLIST_ERR_NO_MEM; + } data = plist_get_data(node); if (node->children) { diff --git a/src/plist.c b/src/plist.c index 66fe1ab..8d3c8ef 100644 --- a/src/plist.c +++ b/src/plist.c @@ -909,7 +909,11 @@ static plist_t plist_copy_node(node_t root) if (f->copydata->hashtable && (f->node_index % 2 != 0)) { node_t new_key = node_prev_sibling((node_t)newch); if (new_key) { - hash_table_insert((hashtable_t*)f->copydata->hashtable, new_key->data, newch); + if (hash_table_insert((hashtable_t*)f->copydata->hashtable, new_key->data, newch) < 0) { + /* drop the lookup cache; lookups fall back to a linear search */ + hash_table_destroy((hashtable_t*)f->copydata->hashtable); + f->copydata->hashtable = NULL; + } } } break; @@ -1329,6 +1333,16 @@ plist_t plist_dict_get_item(plist_t node, const char* key) return ret; } +/* Drop the key lookup cache of a dict; lookups then fall back to a linear search. */ +static void plist_dict_drop_cache(plist_t node) +{ + plist_data_t data = plist_get_data(node); + if (data && data->hashtable) { + hash_table_destroy((hashtable_t*)data->hashtable); + data->hashtable = NULL; + } +} + plist_err_t plist_dict_set_item(plist_t node, const char* key, plist_t item) { if (!PLIST_IS_DICT(node) || !key || !item) { @@ -1373,7 +1387,10 @@ plist_err_t plist_dict_set_item(plist_t node, const char* key, plist_t item) // rollback: reinsert old value int rb = node_insert((node_t)node, (unsigned)idx, old_val); if (rb == NODE_ERR_SUCCESS && ht) { - hash_table_insert(ht, ((node_t)old_key)->data, old_item); + if (hash_table_insert(ht, ((node_t)old_key)->data, old_item) < 0) { + plist_dict_drop_cache(node); + ht = NULL; + } } PLIST_ERR("%s: failed to replace dict value (err=%d)\n", __func__, r); return PLIST_ERR_UNKNOWN; @@ -1382,7 +1399,10 @@ plist_err_t plist_dict_set_item(plist_t node, const char* key, plist_t item) // update hash table if (ht) { - hash_table_insert(ht, (plist_data_t)((node_t)key_node)->data, item); + if (hash_table_insert(ht, (plist_data_t)((node_t)key_node)->data, item) < 0) { + plist_dict_drop_cache(node); + ht = NULL; + } } // now it’s safe to free old value @@ -1409,7 +1429,10 @@ plist_err_t plist_dict_set_item(plist_t node, const char* key, plist_t item) if (ht) { // store pointer to item in hash table - hash_table_insert(ht, (plist_data_t)((node_t)key_node)->data, item); + if (hash_table_insert(ht, (plist_data_t)((node_t)key_node)->data, item) < 0) { + plist_dict_drop_cache(node); + ht = NULL; + } } else if (((node_t)node)->count > 500) { // make new hash table ht = hash_table_new(dict_key_hash, dict_key_compare, NULL); @@ -1419,7 +1442,10 @@ plist_err_t plist_dict_set_item(plist_t node, const char* key, plist_t item) ht && current; current = (plist_t)node_next_sibling(node_next_sibling((node_t)current))) { - hash_table_insert(ht, ((node_t)current)->data, node_next_sibling((node_t)current)); + if (hash_table_insert(ht, ((node_t)current)->data, node_next_sibling((node_t)current)) < 0) { + hash_table_destroy(ht); + ht = NULL; + } } ((plist_data_t)((node_t)node)->data)->hashtable = ht; } diff --git a/src/ptrarray.c b/src/ptrarray.c index bdcd428..eb9ff0a 100644 --- a/src/ptrarray.c +++ b/src/ptrarray.c @@ -20,11 +20,24 @@ */ #include "ptrarray.h" #include <string.h> +#include <stdint.h> +#include <limits.h> ptrarray_t *ptr_array_new(int capacity) { - ptrarray_t *pa = (ptrarray_t*)malloc(sizeof(ptrarray_t)); - pa->pdata = (void**)malloc(sizeof(void*) * capacity); + ptrarray_t *pa; + if (capacity < 0 || (size_t)capacity > SIZE_MAX / sizeof(void*)) { + return NULL; + } + pa = (ptrarray_t*)malloc(sizeof(ptrarray_t)); + if (!pa) { + return NULL; + } + pa->pdata = (void**)malloc(sizeof(void*) * (size_t)capacity); + if (!pa->pdata) { + free(pa); + return NULL; + } pa->capacity = capacity; pa->capacity_step = (capacity > 4096) ? 4096 : capacity; pa->len = 0; @@ -45,7 +58,12 @@ int ptr_array_insert(ptrarray_t *pa, void *data, long array_index) if (!pa || !pa->pdata) return -1; long remaining = pa->capacity-pa->len; if (remaining == 0) { - void **newdata = (void**)realloc(pa->pdata, sizeof(void*) * (pa->capacity + pa->capacity_step)); + void **newdata; + if (pa->capacity > INT_MAX - pa->capacity_step || + (size_t)(pa->capacity + pa->capacity_step) > SIZE_MAX / sizeof(void*)) { + return -1; + } + newdata = (void**)realloc(pa->pdata, sizeof(void*) * (size_t)(pa->capacity + pa->capacity_step)); if (!newdata) { return -1; } diff --git a/src/xplist.c b/src/xplist.c index 0fe0877..19d98ed 100644 --- a/src/xplist.c +++ b/src/xplist.c @@ -276,6 +276,7 @@ static plist_err_t node_to_xml(node_t node, bytearray_t **outbuf, uint32_t depth uint32_t indent = (depth > 8) ? 8 : depth; uint32_t maxread = MAX_DATA_BYTES_PER_LINE(indent); size_t count = 0; + size_t encoded = 0; size_t amount = (node_data->length / 3 * 4) + 4 + (((node_data->length / maxread) + 1) * (indent+1)); if ((*outbuf)->len + amount > (*outbuf)->capacity) { str_buf_grow(*outbuf, amount); @@ -288,7 +289,14 @@ static plist_err_t node_to_xml(node_t node, bytearray_t **outbuf, uint32_t depth str_buf_append(*outbuf, "\t", 1); } count = (node_data->length-j < maxread) ? node_data->length-j : maxread; - assert((*outbuf)->len + count < (*outbuf)->capacity); + /* base64encode writes 4*ceil(count/3) bytes plus a terminating NUL */ + encoded = ((count + 2) / 3) * 4 + 1; + if ((*outbuf)->len + encoded > (*outbuf)->capacity) { + str_buf_grow(*outbuf, (*outbuf)->len + encoded - (*outbuf)->capacity); + if (!(*outbuf)->data) { + return PLIST_ERR_NO_MEM; + } + } (*outbuf)->len += base64encode((char*)(*outbuf)->data + (*outbuf)->len, node_data->buff + j, count); str_buf_append(*outbuf, "\n", 1); j+=count; @@ -422,7 +430,9 @@ static plist_err_t _node_estimate_size(node_t node, uint64_t *size, uint32_t dep } // mark as visited - hash_table_insert(visited, node, (void*)1); + if (hash_table_insert(visited, node, (void*)1) < 0) { + return PLIST_ERR_NO_MEM; + } data = plist_get_data(node); if (node->children) { @@ -677,7 +687,9 @@ static void text_parts_free(text_part_t *tp) static text_part_t* text_part_append(text_part_t* parts, const char *begin, size_t length, int is_cdata) { text_part_t* newpart = (text_part_t*)malloc(sizeof(text_part_t)); - assert(newpart); + if (!newpart) { + return NULL; + } parts->next = text_part_init(newpart, begin, length, is_cdata); return newpart; } @@ -716,6 +728,10 @@ static text_part_t* get_text_parts(parse_ctx ctx, const char* tag, size_t tag_le if (*ctx->pos == '-' && *(ctx->pos+1) == '-') { if (last) { last = text_part_append(last, p, q-p, 0); + if (!last) { + ctx->err = PLIST_ERR_NO_MEM; + return NULL; + } } else if (parts) { last = text_part_init(parts, p, q-p, 0); } @@ -738,6 +754,10 @@ static text_part_t* get_text_parts(parse_ctx ctx, const char* tag, size_t tag_le if (q-p > 0) { if (last) { last = text_part_append(last, p, q-p, 0); + if (!last) { + ctx->err = PLIST_ERR_NO_MEM; + return NULL; + } } else if (parts) { last = text_part_init(parts, p, q-p, 0); } @@ -753,6 +773,10 @@ static text_part_t* get_text_parts(parse_ctx ctx, const char* tag, size_t tag_le q = ctx->pos; if (last) { last = text_part_append(last, p, q-p, 1); + if (!last) { + ctx->err = PLIST_ERR_NO_MEM; + return NULL; + } } else if (parts) { last = text_part_init(parts, p, q-p, 1); } @@ -802,6 +826,10 @@ static text_part_t* get_text_parts(parse_ctx ctx, const char* tag, size_t tag_le if (q > p) { if (last) { last = text_part_append(last, p, q-p, 0); + if (!last) { + ctx->err = PLIST_ERR_NO_MEM; + return NULL; + } } else if (parts) { last = text_part_init(parts, p, q-p, 0); } @@ -920,11 +948,12 @@ static int unescape_entities(char *str, size_t *length) return 0; } -static char* text_parts_get_content(text_part_t *tp, int unesc_entities, int trim_ws, size_t *length, int *requires_free) +static char* text_parts_get_content(text_part_t *tp, int unesc_entities, int trim_ws, size_t *length, int *requires_free, plist_err_t *err) { char *str = NULL; size_t total_length = 0; + if (err) *err = PLIST_ERR_PARSE; if (!tp) { return NULL; } @@ -938,11 +967,18 @@ static char* text_parts_get_content(text_part_t *tp, int unesc_entities, int tri } text_part_t *tmp = tp; while (tp && tp->begin) { + if (tp->length > SIZE_MAX - 1 - total_length) { + if (err) *err = PLIST_ERR_NO_MEM; + return NULL; + } total_length += tp->length; tp = (text_part_t*)tp->next; } str = (char*)malloc(total_length + 1); - assert(str); + if (!str) { + if (err) *err = PLIST_ERR_NO_MEM; + return NULL; + } p = str; tp = tmp; while (tp && tp->begin) { @@ -985,6 +1021,7 @@ static char* text_parts_get_content(text_part_t *tp, int unesc_entities, int tri static plist_err_t node_from_xml(parse_ctx ctx, plist_t *plist) { + plist_err_t content_err = PLIST_ERR_PARSE; char tag[16] = { 0 }; char *keyname = NULL; plist_t subnode = NULL; @@ -1191,15 +1228,19 @@ static plist_err_t node_from_xml(parse_ctx ctx, plist_t *plist) if (!tp) { PLIST_XML_ERR("Could not parse text content for '%s' node\n", tag); text_parts_free((text_part_t*)first_part.next); - ctx->err = PLIST_ERR_PARSE; + if (PLIST_ERR_SUCCESS == ctx->err) { + ctx->err = PLIST_ERR_PARSE; + } goto err_out; } if (tp->begin) { - char *str_content = text_parts_get_content(tp, 0, 1, NULL, NULL); + char *str_content = text_parts_get_content(tp, 0, 1, NULL, NULL, &content_err); if (!str_content) { PLIST_XML_ERR("Could not get text content for '%s' node\n", tag); text_parts_free((text_part_t*)first_part.next); - ctx->err = PLIST_ERR_PARSE; + if (PLIST_ERR_SUCCESS == ctx->err) { + ctx->err = content_err; + } goto err_out; } char *str = str_content; @@ -1263,15 +1304,19 @@ static plist_err_t node_from_xml(parse_ctx ctx, plist_t *plist) if (!tp) { PLIST_XML_ERR("Could not parse text content for '%s' node\n", tag); text_parts_free((text_part_t*)first_part.next); - ctx->err = PLIST_ERR_PARSE; + if (PLIST_ERR_SUCCESS == ctx->err) { + ctx->err = PLIST_ERR_PARSE; + } goto err_out; } if (tp->begin) { - char *str_content = text_parts_get_content(tp, 0, 1, NULL, NULL); + char *str_content = text_parts_get_content(tp, 0, 1, NULL, NULL, &content_err); if (!str_content) { PLIST_XML_ERR("Could not get text content for '%s' node\n", tag); text_parts_free((text_part_t*)first_part.next); - ctx->err = PLIST_ERR_PARSE; + if (PLIST_ERR_SUCCESS == ctx->err) { + ctx->err = content_err; + } goto err_out; } errno = 0; @@ -1335,14 +1380,18 @@ static plist_err_t node_from_xml(parse_ctx ctx, plist_t *plist) if (!tp) { PLIST_XML_ERR("Could not parse text content for '%s' node\n", tag); text_parts_free((text_part_t*)first_part.next); - ctx->err = PLIST_ERR_PARSE; + if (PLIST_ERR_SUCCESS == ctx->err) { + ctx->err = PLIST_ERR_PARSE; + } goto err_out; } - str = text_parts_get_content(tp, 1, 0, &length, NULL); + str = text_parts_get_content(tp, 1, 0, &length, NULL, &content_err); text_parts_free((text_part_t*)first_part.next); if (!str) { PLIST_XML_ERR("Could not get text content for '%s' node\n", tag); - ctx->err = PLIST_ERR_PARSE; + if (PLIST_ERR_SUCCESS == ctx->err) { + ctx->err = content_err; + } goto err_out; } if (!strcmp(tag, "key") && !keyname && parent && (plist_get_node_type(parent) == PLIST_DICT)) { @@ -1366,16 +1415,20 @@ static plist_err_t node_from_xml(parse_ctx ctx, plist_t *plist) if (!tp) { PLIST_XML_ERR("Could not parse text content for '%s' node\n", tag); text_parts_free((text_part_t*)first_part.next); - ctx->err = PLIST_ERR_PARSE; + if (PLIST_ERR_SUCCESS == ctx->err) { + ctx->err = PLIST_ERR_PARSE; + } goto err_out; } if (tp->begin) { int requires_free = 0; - char *str_content = text_parts_get_content(tp, 0, 0, NULL, &requires_free); + char *str_content = text_parts_get_content(tp, 0, 0, NULL, &requires_free, &content_err); if (!str_content) { PLIST_XML_ERR("Could not get text content for '%s' node\n", tag); text_parts_free((text_part_t*)first_part.next); - ctx->err = PLIST_ERR_PARSE; + if (PLIST_ERR_SUCCESS == ctx->err) { + ctx->err = content_err; + } goto err_out; } size_t size = tp->length; @@ -1404,16 +1457,20 @@ static plist_err_t node_from_xml(parse_ctx ctx, plist_t *plist) if (!tp) { PLIST_XML_ERR("Could not parse text content for '%s' node\n", tag); text_parts_free((text_part_t*)first_part.next); - ctx->err = PLIST_ERR_PARSE; + if (PLIST_ERR_SUCCESS == ctx->err) { + ctx->err = PLIST_ERR_PARSE; + } goto err_out; } Time64_T timev = 0; if (tp->begin) { - char *str_content = text_parts_get_content(tp, 0, 1, NULL, NULL); + char *str_content = text_parts_get_content(tp, 0, 1, NULL, NULL, &content_err); if (!str_content) { PLIST_XML_ERR("Could not get text content for '%s' node\n", tag); text_parts_free((text_part_t*)first_part.next); - ctx->err = PLIST_ERR_PARSE; + if (PLIST_ERR_SUCCESS == ctx->err) { + ctx->err = content_err; + } goto err_out; } struct TM btime; |
