summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Nikias Bassen2026-11-05 12:01:59 +0100
committerGravatar Nikias Bassen2026-11-05 12:01:59 +0100
commit4dc93bc274867b42aa82fc91ee4d5878d23a3f36 (patch)
tree11c566b80561bb44018bceea028ea03d47e53458
parentf252f243f9774b2b830172b49aa340d6994d8081 (diff)
downloadlibplist-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.c19
-rw-r--r--src/bytearray.c38
-rw-r--r--src/hashtable.c13
-rw-r--r--src/hashtable.h3
-rw-r--r--src/jplist.c13
-rw-r--r--src/oplist.c82
-rw-r--r--src/out-default.c4
-rw-r--r--src/out-limd.c4
-rw-r--r--src/out-plutil.c4
-rw-r--r--src/plist.c36
-rw-r--r--src/ptrarray.c24
-rw-r--r--src/xplist.c97
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;