summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Nikias Bassen2015-02-05 14:42:00 +0100
committerGravatar Nikias Bassen2015-02-05 14:42:00 +0100
commit40cf910a621445bf5c2638f39d245fc7c3c8f72a (patch)
tree66e3dad973a0677c29783d79d8e0978c0a8e3ae3
parent6a781e6f69cb820347bae02a1f947e6b01ccaa47 (diff)
downloadlibplist-40cf910a621445bf5c2638f39d245fc7c3c8f72a.tar.gz
libplist-40cf910a621445bf5c2638f39d245fc7c3c8f72a.tar.bz2
bplist: Refactor binary plist parsing in a recursive way
-rw-r--r--src/bplist.c376
1 files changed, 167 insertions, 209 deletions
diff --git a/src/bplist.c b/src/bplist.c
index 5ddca26..3ba46a2 100644
--- a/src/bplist.c
+++ b/src/bplist.c
@@ -1,8 +1,9 @@
/*
- * plist.c
+ * bplist.c
* Binary plist implementation
*
- * Copyright (c) 2008 Jonathan Beck All Rights Reserved.
+ * Copyright (c) 2011-2015 Nikias Bassen, All Rights Reserved.
+ * Copyright (c) 2008-2010 Jonathan Beck, All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -95,7 +96,7 @@ static void float_byte_convert(uint8_t * address, size_t size)
union plist_uint_ptr
{
- void *src;
+ const void *src;
uint8_t *u8ptr;
uint16_t *u16ptr;
uint32_t *u32ptr;
@@ -202,7 +203,20 @@ static uint32_t uint24_from_be(union plist_uint_ptr buf)
#define NODE_IS_ROOT(x) (((node_t*)x)->isRoot)
-static plist_t parse_uint_node(char *bnode, uint8_t size, char **next_object)
+struct bplist_data {
+ const char* data;
+ uint64_t size;
+ uint64_t num_objects;
+ uint8_t dict_size;
+ uint8_t offset_size;
+ const char* offset_table;
+ uint32_t level;
+ uint32_t *used_indexes;
+};
+
+static plist_t parse_bin_node_at_index(struct bplist_data *bplist, uint32_t node_index);
+
+static plist_t parse_uint_node(const char **bnode, uint8_t size)
{
plist_data_t data = plist_new_plist_data();
@@ -213,12 +227,12 @@ static plist_t parse_uint_node(char *bnode, uint8_t size, char **next_object)
case sizeof(uint16_t):
case sizeof(uint32_t):
case sizeof(uint64_t):
- memcpy(&data->intval, bnode, size);
+ memcpy(&data->intval, *bnode, size);
data->intval = UINT_TO_HOST(&data->intval, size);
data->length = sizeof(uint64_t);
break;
case 16:
- memcpy(&data->intval, bnode+8, sizeof(uint64_t));
+ memcpy(&data->intval, (*bnode)+8, sizeof(uint64_t));
data->intval = UINT_TO_HOST(&data->intval, sizeof(uint64_t));
data->length = size;
break;
@@ -227,13 +241,13 @@ static plist_t parse_uint_node(char *bnode, uint8_t size, char **next_object)
return NULL;
};
- *next_object = bnode + size;
+ (*bnode) += size;
data->type = PLIST_UINT;
return node_create(NULL, data);
}
-static plist_t parse_real_node(char *bnode, uint8_t size)
+static plist_t parse_real_node(const char **bnode, uint8_t size)
{
plist_data_t data = plist_new_plist_data();
float floatval = 0.0;
@@ -241,7 +255,7 @@ static plist_t parse_real_node(char *bnode, uint8_t size)
size = 1 << size; // make length less misleading
buf = malloc (size);
- memcpy (buf, bnode, size);
+ memcpy (buf, *bnode, size);
switch (size)
{
case sizeof(float):
@@ -265,7 +279,7 @@ static plist_t parse_real_node(char *bnode, uint8_t size)
return node_create(NULL, data);
}
-static plist_t parse_date_node(char *bnode, uint8_t size)
+static plist_t parse_date_node(const char **bnode, uint8_t size)
{
plist_t node = parse_real_node(bnode, size);
plist_data_t data = plist_get_data(node);
@@ -279,13 +293,13 @@ static plist_t parse_date_node(char *bnode, uint8_t size)
return node;
}
-static plist_t parse_string_node(char *bnode, uint64_t size)
+static plist_t parse_string_node(const char **bnode, uint64_t size)
{
plist_data_t data = plist_new_plist_data();
data->type = PLIST_STRING;
data->strval = (char *) malloc(sizeof(char) * (size + 1));
- memcpy(data->strval, bnode, size);
+ memcpy(data->strval, *bnode, size);
data->strval[size] = '\0';
data->length = strlen(data->strval);
@@ -348,7 +362,7 @@ static char *plist_utf16_to_utf8(uint16_t *unistr, long len, long *items_read, l
return outbuf;
}
-static plist_t parse_unicode_node(char *bnode, uint64_t size)
+static plist_t parse_unicode_node(const char **bnode, uint64_t size)
{
plist_data_t data = plist_new_plist_data();
uint64_t i = 0;
@@ -359,7 +373,7 @@ static plist_t parse_unicode_node(char *bnode, uint64_t size)
data->type = PLIST_STRING;
unicodestr = (uint16_t*) malloc(sizeof(uint16_t) * size);
- memcpy(unicodestr, bnode, sizeof(uint16_t) * size);
+ memcpy(unicodestr, *bnode, sizeof(uint16_t) * size);
for (i = 0; i < size; i++)
byte_convert((uint8_t *) (unicodestr + i), sizeof(uint16_t));
@@ -375,43 +389,112 @@ static plist_t parse_unicode_node(char *bnode, uint64_t size)
return node_create(NULL, data);
}
-static plist_t parse_data_node(char *bnode, uint64_t size)
+static plist_t parse_data_node(const char **bnode, uint64_t size)
{
plist_data_t data = plist_new_plist_data();
data->type = PLIST_DATA;
data->length = size;
data->buff = (uint8_t *) malloc(sizeof(uint8_t) * size);
- memcpy(data->buff, bnode, sizeof(uint8_t) * size);
+ memcpy(data->buff, *bnode, sizeof(uint8_t) * size);
return node_create(NULL, data);
}
-static plist_t parse_dict_node(char *bnode, uint64_t size, uint32_t ref_size)
+static plist_t parse_dict_node(struct bplist_data *bplist, const char** bnode, uint64_t size)
{
+ uint64_t j;
+ uint64_t str_i = 0, str_j = 0;
+ uint64_t index1, index2;
plist_data_t data = plist_new_plist_data();
data->type = PLIST_DICT;
data->length = size;
- data->buff = (uint8_t *) malloc(sizeof(uint8_t) * size * ref_size * 2);
- memcpy(data->buff, bnode, sizeof(uint8_t) * size * ref_size * 2);
- return node_create(NULL, data);
+ plist_t node = node_create(NULL, data);
+
+ for (j = 0; j < data->length; j++) {
+ str_i = j * bplist->dict_size;
+ str_j = (j + size) * bplist->dict_size;
+
+ index1 = UINT_TO_HOST((*bnode) + str_i, bplist->dict_size);
+ index2 = UINT_TO_HOST((*bnode) + str_j, bplist->dict_size);
+
+ if (index1 >= bplist->num_objects) {
+ plist_free(node);
+ return NULL;
+ }
+ if (index2 >= bplist->num_objects) {
+ plist_free(node);
+ return NULL;
+ }
+
+ /* process key node */
+ plist_t key = parse_bin_node_at_index(bplist, index1);
+ if (!key) {
+ plist_free(node);
+ return NULL;
+ }
+ /* enforce key type */
+ plist_get_data(key)->type = PLIST_KEY;
+ if (!plist_get_data(key)->strval) {
+ fprintf(stderr, "ERROR: Malformed binary plist dict, invalid key node encountered!\n");
+ plist_free(key);
+ plist_free(node);
+ return NULL;
+ }
+
+ /* process value node */
+ plist_t val = parse_bin_node_at_index(bplist, index2);
+ if (!val) {
+ plist_free(key);
+ plist_free(node);
+ return NULL;
+ }
+
+ node_attach(node, key);
+ node_attach(node, val);
+ }
+
+ return node;
}
-static plist_t parse_array_node(char *bnode, uint64_t size, uint32_t ref_size)
+static plist_t parse_array_node(struct bplist_data *bplist, const char** bnode, uint64_t size)
{
+ uint64_t j;
+ uint32_t str_j = 0;
+ uint32_t index1;
+
plist_data_t data = plist_new_plist_data();
data->type = PLIST_ARRAY;
data->length = size;
- data->buff = (uint8_t *) malloc(sizeof(uint8_t) * size * ref_size);
- memcpy(data->buff, bnode, sizeof(uint8_t) * size * ref_size);
- return node_create(NULL, data);
+ plist_t node = node_create(NULL, data);
+
+ for (j = 0; j < data->length; j++) {
+ str_j = j * bplist->dict_size;
+ index1 = UINT_TO_HOST((*bnode) + str_j, bplist->dict_size);
+
+ if (index1 >= bplist->num_objects) {
+ plist_free(node);
+ return NULL;
+ }
+
+ /* process value node */
+ plist_t val = parse_bin_node_at_index(bplist, index1);
+ if (!val) {
+ plist_free(node);
+ return NULL;
+ }
+
+ node_attach(node, val);
+ }
+
+ return node;
}
-static plist_t parse_uid_node(char *bnode, uint8_t size, char **next_object)
+static plist_t parse_uid_node(const char **bnode, uint8_t size)
{
plist_data_t data = plist_new_plist_data();
@@ -422,7 +505,7 @@ static plist_t parse_uid_node(char *bnode, uint8_t size, char **next_object)
case sizeof(uint16_t):
case sizeof(uint32_t):
case sizeof(uint64_t):
- memcpy(&data->intval, bnode, size);
+ memcpy(&data->intval, *bnode, size);
data->intval = UINT_TO_HOST(&data->intval, size);
break;
default:
@@ -430,15 +513,13 @@ static plist_t parse_uid_node(char *bnode, uint8_t size, char **next_object)
return NULL;
};
- *next_object = bnode + size;
data->type = PLIST_UID;
data->length = sizeof(uint64_t);
return node_create(NULL, data);
}
-
-static plist_t parse_bin_node(char *object, uint8_t dict_size, char **next_object)
+static plist_t parse_bin_node(struct bplist_data *bplist, const char** object)
{
uint16_t type = 0;
uint64_t size = 0;
@@ -446,9 +527,9 @@ static plist_t parse_bin_node(char *object, uint8_t dict_size, char **next_objec
if (!object)
return NULL;
- type = (*object) & 0xF0;
- size = (*object) & 0x0F;
- object++;
+ type = (**object) & BPLIST_MASK;
+ size = (**object) & BPLIST_FILL;
+ (*object)++;
switch (type)
{
@@ -481,7 +562,7 @@ static plist_t parse_bin_node(char *object, uint8_t dict_size, char **next_objec
}
case BPLIST_UINT:
- return parse_uint_node(object, size, next_object);
+ return parse_uint_node(object, size);
case BPLIST_REAL:
return parse_real_node(object, size);
@@ -493,9 +574,8 @@ static plist_t parse_bin_node(char *object, uint8_t dict_size, char **next_objec
return parse_date_node(object, size);
case BPLIST_DATA:
- if (0x0F == size)
- {
- plist_t size_node = parse_bin_node(object, dict_size, &object);
+ if (BPLIST_FILL == size) {
+ plist_t size_node = parse_bin_node(bplist, object);
if (plist_get_node_type(size_node) != PLIST_UINT)
return NULL;
plist_get_uint_val(size_node, &size);
@@ -504,9 +584,8 @@ static plist_t parse_bin_node(char *object, uint8_t dict_size, char **next_objec
return parse_data_node(object, size);
case BPLIST_STRING:
- if (0x0F == size)
- {
- plist_t size_node = parse_bin_node(object, dict_size, &object);
+ if (BPLIST_FILL == size) {
+ plist_t size_node = parse_bin_node(bplist, object);
if (plist_get_node_type(size_node) != PLIST_UINT)
return NULL;
plist_get_uint_val(size_node, &size);
@@ -515,9 +594,8 @@ static plist_t parse_bin_node(char *object, uint8_t dict_size, char **next_objec
return parse_string_node(object, size);
case BPLIST_UNICODE:
- if (0x0F == size)
- {
- plist_t size_node = parse_bin_node(object, dict_size, &object);
+ if (BPLIST_FILL == size) {
+ plist_t size_node = parse_bin_node(bplist, object);
if (plist_get_node_type(size_node) != PLIST_UINT)
return NULL;
plist_get_uint_val(size_node, &size);
@@ -525,76 +603,64 @@ static plist_t parse_bin_node(char *object, uint8_t dict_size, char **next_objec
}
return parse_unicode_node(object, size);
- case BPLIST_UNK_0x70:
+ case BPLIST_SET:
case BPLIST_ARRAY:
- if (0x0F == size)
- {
- plist_t size_node = parse_bin_node(object, dict_size, &object);
+ if (BPLIST_FILL == size) {
+ plist_t size_node = parse_bin_node(bplist, object);
if (plist_get_node_type(size_node) != PLIST_UINT)
return NULL;
plist_get_uint_val(size_node, &size);
plist_free(size_node);
}
- return parse_array_node(object, size, dict_size);
+ return parse_array_node(bplist, object, size);
case BPLIST_UID:
- return parse_uid_node(object, size, next_object);
+ return parse_uid_node(object, size);
- case BPLIST_SET:
case BPLIST_DICT:
- if (0x0F == size)
- {
- plist_t size_node = parse_bin_node(object, dict_size, &object);
+ if (BPLIST_FILL == size) {
+ plist_t size_node = parse_bin_node(bplist, object);
if (plist_get_node_type(size_node) != PLIST_UINT)
return NULL;
plist_get_uint_val(size_node, &size);
plist_free(size_node);
}
- return parse_dict_node(object, size, dict_size);
+ return parse_dict_node(bplist, object, size);
default:
return NULL;
}
return NULL;
}
-static void* copy_plist_data(const void* src)
+static plist_t parse_bin_node_at_index(struct bplist_data *bplist, uint32_t node_index)
{
- plist_data_t srcdata = (plist_data_t) src;
- plist_data_t dstdata = plist_new_plist_data();
+ int i;
+ const char* ptr;
+ plist_t plist;
- dstdata->type = srcdata->type;
- dstdata->length = srcdata->length;
- switch (dstdata->type)
- {
- case PLIST_BOOLEAN:
- dstdata->boolval = srcdata->boolval;
- break;
- case PLIST_UINT:
- dstdata->intval = srcdata->intval;
- break;
- case PLIST_DATE:
- dstdata->timeval.tv_sec = srcdata->timeval.tv_sec;
- dstdata->timeval.tv_usec = srcdata->timeval.tv_usec;
- break;
- case PLIST_REAL:
- dstdata->realval = srcdata->realval;
- break;
- case PLIST_KEY:
- case PLIST_STRING:
- dstdata->strval = strdup(srcdata->strval);
- break;
- case PLIST_DATA:
- dstdata->buff = (uint8_t*) malloc(sizeof(uint8_t) * srcdata->length);
- memcpy(dstdata->buff, srcdata->buff, sizeof(uint8_t) * srcdata->length);
- break;
- case PLIST_UID:
- dstdata->intval = srcdata->intval;
- break;
- default:
- break;
+ ptr = bplist->data + UINT_TO_HOST(bplist->offset_table + node_index * bplist->offset_size, bplist->offset_size);
+ /* make sure the node offset is in a sane range */
+ if ((ptr < bplist->data) || (ptr >= bplist->offset_table)) {
+ return NULL;
}
- return dstdata;
+ /* store node_index for current recursion level */
+ bplist->used_indexes[bplist->level] = node_index;
+ /* recursion check */
+ if (bplist->level > 0) {
+ for (i = bplist->level-1; i >= 0; i--) {
+ if (bplist->used_indexes[i] == bplist->used_indexes[bplist->level]) {
+ fprintf(stderr, "Recursion detected in binary plist. Aborting.\n");
+ return NULL;
+ }
+ }
+ }
+
+ /* finally parse node */
+ bplist->level++;
+ plist = parse_bin_node(bplist, &ptr);
+ bplist->level--;
+ return plist;
}
PLIST_API void plist_from_bin(const char *plist_bin, uint32_t length, plist_t * plist)
@@ -602,19 +668,11 @@ PLIST_API void plist_from_bin(const char *plist_bin, uint32_t length, plist_t *
char *trailer = NULL;
uint8_t offset_size = 0;
- uint8_t dict_param_size = 0;
+ uint8_t dict_size = 0;
uint64_t num_objects = 0;
uint64_t root_object = 0;
uint64_t offset_table_index = 0;
- plist_t *nodeslist = NULL;
- uint64_t i = 0;
- uint64_t current_offset = 0;
- char *offset_table = NULL;
- uint32_t j = 0, str_i = 0, str_j = 0;
- uint32_t index1 = 0, index2 = 0;
-
-
//first check we have enough data
if (!(length >= BPLIST_MAGIC_SIZE + BPLIST_VERSION_SIZE + BPLIST_TRL_SIZE))
return;
@@ -629,7 +687,7 @@ PLIST_API void plist_from_bin(const char *plist_bin, uint32_t length, plist_t *
trailer = (char *) (plist_bin + (length - BPLIST_TRL_SIZE));
offset_size = trailer[BPLIST_TRL_OFFSIZE_IDX];
- dict_param_size = trailer[BPLIST_TRL_PARMSIZE_IDX];
+ dict_size = trailer[BPLIST_TRL_PARMSIZE_IDX];
num_objects = be64dec(trailer + BPLIST_TRL_NUMOBJ_IDX);
root_object = be64dec(trailer + BPLIST_TRL_ROOTOBJ_IDX);
offset_table_index = be64dec(trailer + BPLIST_TRL_OFFTAB_IDX);
@@ -637,122 +695,22 @@ PLIST_API void plist_from_bin(const char *plist_bin, uint32_t length, plist_t *
if (num_objects == 0)
return;
- //allocate serialized array of nodes
- nodeslist = (plist_t *) malloc(sizeof(plist_t) * num_objects);
-
- if (!nodeslist)
+ if (root_object >= num_objects)
return;
- //parse serialized nodes
- offset_table = (char *) (plist_bin + offset_table_index);
- for (i = 0; i < num_objects; i++)
- {
- char *obj = NULL;
- current_offset = UINT_TO_HOST(offset_table + i * offset_size, offset_size);
-
- obj = (char *) (plist_bin + current_offset);
- nodeslist[i] = parse_bin_node(obj, dict_param_size, &obj);
- }
-
- //setup children for structured types
- for (i = 0; i < num_objects; i++)
- {
-
- plist_data_t data = plist_get_data(nodeslist[i]);
- if (!data) {
- break;
- }
+ struct bplist_data bplist;
+ bplist.data = plist_bin;
+ bplist.size = length;
+ bplist.num_objects = num_objects;
+ bplist.dict_size = dict_size;
+ bplist.offset_size = offset_size;
+ bplist.offset_table = (char *) (plist_bin + offset_table_index);
+ bplist.level = 0;
+ bplist.used_indexes = (uint32_t*)malloc(sizeof(uint32_t) * num_objects);
- switch (data->type)
- {
- case PLIST_DICT:
- for (j = 0; j < data->length; j++)
- {
- node_t* n = NULL;
- str_i = j * dict_param_size;
- str_j = (j + data->length) * dict_param_size;
-
- index1 = UINT_TO_HOST(data->buff + str_i, dict_param_size);
- index2 = UINT_TO_HOST(data->buff + str_j, dict_param_size);
-
- // process key node
- if (index1 < num_objects)
- {
- // is node already attached?
- if (NODE_IS_ROOT(nodeslist[index1])) {
- // use original
- n = nodeslist[index1];
- } else {
- // use a copy, because this node is already attached elsewhere
- n = node_copy_deep(nodeslist[index1], copy_plist_data);
- }
-
- // enforce key type
- plist_get_data(n)->type = PLIST_KEY;
-
- // attach key node
- node_attach(nodeslist[i], n);
- }
-
- // process value node
- if (index2 < num_objects)
- {
- // is node already attached?
- if (NODE_IS_ROOT(nodeslist[index2])) {
- // use original
- n = nodeslist[index2];
- } else {
- // use a copy, because this node is already attached elsewhere
- n = node_copy_deep(nodeslist[index2], copy_plist_data);
-
- // ensure key type is never used for values, especially if we copy a key node
- if (plist_get_data(n)->type == PLIST_KEY) {
- plist_get_data(n)->type = PLIST_STRING;
- }
- }
-
- // attach value node
- node_attach(nodeslist[i], n);
- }
- }
- break;
+ *plist = parse_bin_node_at_index(&bplist, root_object);
- case PLIST_ARRAY:
- for (j = 0; j < data->length; j++)
- {
- str_j = j * dict_param_size;
- index1 = UINT_TO_HOST(data->buff + str_j, dict_param_size);
-
- if (index1 < num_objects)
- {
- if (NODE_IS_ROOT(nodeslist[index1]))
- node_attach(nodeslist[i], nodeslist[index1]);
- else
- node_attach(nodeslist[i], node_copy_deep(nodeslist[index1], copy_plist_data));
- }
- }
- break;
- default:
- break;
- }
- }
-
- *plist = nodeslist[root_object];
-
- // free unreferenced nodes that would otherwise leak memory
- for (i = 0; i < num_objects; i++) {
- plist_data_t data = plist_get_data(nodeslist[i]);
- if ((data->type == PLIST_DICT) || (data->type == PLIST_ARRAY)) {
- free(data->buff);
- data->buff = NULL;
- }
- if (i == root_object) continue;
- node_t* node = (node_t*)nodeslist[i];
- if (node && NODE_IS_ROOT(node)) {
- plist_free(node);
- }
- }
- free(nodeslist);
+ free(bplist.used_indexes);
}
static unsigned int plist_data_hash(const void* key)