diff options
Diffstat (limited to 'src/bplist.c')
-rw-r--r-- | src/bplist.c | 216 |
1 files changed, 134 insertions, 82 deletions
diff --git a/src/bplist.c b/src/bplist.c index 14db755..93f0bc6 100644 --- a/src/bplist.c +++ b/src/bplist.c @@ -32,11 +32,11 @@ #include <ctype.h> #include <inttypes.h> -#include <plist/plist.h> #include "plist.h" #include "hashtable.h" #include "bytearray.h" #include "ptrarray.h" +#include "plist/plist.h" #include <node.h> @@ -47,7 +47,8 @@ #define BPLIST_VERSION ((uint8_t*)"00") #define BPLIST_VERSION_SIZE 2 -typedef struct __attribute__((packed)) { +#pragma pack(push,1) +typedef struct { uint8_t unused[6]; uint8_t offset_size; uint8_t ref_size; @@ -55,6 +56,7 @@ typedef struct __attribute__((packed)) { uint64_t root_object_index; uint64_t offset_table_offset; } bplist_trailer_t; +#pragma pack(pop) enum { @@ -62,7 +64,7 @@ enum BPLIST_FALSE = 0x08, BPLIST_TRUE = 0x09, BPLIST_FILL = 0x0F, /* will be used for length grabbing */ - BPLIST_UINT = 0x10, + BPLIST_INT = 0x10, BPLIST_REAL = 0x20, BPLIST_DATE = 0x30, BPLIST_DATA = 0x40, @@ -143,28 +145,28 @@ union plist_uint_ptr #ifdef __BIG_ENDIAN__ #define beNtoh(x,n) (x >> ((8-n) << 3)) #else -#define beNtoh(x,n) be64toh(x << ((8-n) << 3)) +#define beNtoh(x,n) be64toh((x) << ((8-(n)) << 3)) #endif #define UINT_TO_HOST(x, n) \ ({ \ union plist_uint_ptr __up; \ - __up.src = (n > 8) ? x + (n - 8) : x; \ - (n >= 8 ? be64toh( get_unaligned(__up.u64ptr) ) : \ - (n == 4 ? be32toh( get_unaligned(__up.u32ptr) ) : \ - (n == 2 ? be16toh( get_unaligned(__up.u16ptr) ) : \ - (n == 1 ? *__up.u8ptr : \ + __up.src = ((n) > 8) ? (x) + ((n) - 8) : (x); \ + ((n) >= 8 ? be64toh( get_unaligned(__up.u64ptr) ) : \ + ((n) == 4 ? be32toh( get_unaligned(__up.u32ptr) ) : \ + ((n) == 2 ? be16toh( get_unaligned(__up.u16ptr) ) : \ + ((n) == 1 ? *__up.u8ptr : \ beNtoh( get_unaligned(__up.u64ptr), n) \ )))); \ }) #define get_needed_bytes(x) \ - ( ((uint64_t)x) < (1ULL << 8) ? 1 : \ - ( ((uint64_t)x) < (1ULL << 16) ? 2 : \ - ( ((uint64_t)x) < (1ULL << 24) ? 3 : \ - ( ((uint64_t)x) < (1ULL << 32) ? 4 : 8)))) + ( ((uint64_t)(x)) < (1ULL << 8) ? 1 : \ + ( ((uint64_t)(x)) < (1ULL << 16) ? 2 : \ + ( ((uint64_t)(x)) < (1ULL << 24) ? 3 : \ + ( ((uint64_t)(x)) < (1ULL << 32) ? 4 : 8)))) -#define get_real_bytes(x) (x == (float) x ? sizeof(float) : sizeof(double)) +#define get_real_bytes(x) ((x) == (float) (x) ? sizeof(float) : sizeof(double)) #if (defined(__LITTLE_ENDIAN__) \ && !defined(__FLOAT_WORD_ORDER__)) \ @@ -182,7 +184,7 @@ union plist_uint_ptr #endif #if __has_builtin(__builtin_umulll_overflow) || __GNUC__ >= 5 -#define uint64_mul_overflow(a, b, r) __builtin_umulll_overflow(a, b, (unsigned long long*)r) +#define uint64_mul_overflow(a, b, r) __builtin_umulll_overflow(a, b, (unsigned long long*)(r)) #else static int uint64_mul_overflow(uint64_t a, uint64_t b, uint64_t *res) { @@ -191,7 +193,7 @@ static int uint64_mul_overflow(uint64_t a, uint64_t b, uint64_t *res) } #endif -#define NODE_IS_ROOT(x) (((node_t*)x)->isRoot) +#define NODE_IS_ROOT(x) (((node_t)(x))->isRoot) struct bplist_data { const char* data; @@ -227,9 +229,16 @@ void plist_bin_deinit(void) /* deinit binary plist stuff */ } +void plist_bin_set_debug(int debug) +{ +#if DEBUG + plist_bin_debug = debug; +#endif +} + 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) +static plist_t parse_int_node(const char **bnode, uint8_t size) { plist_data_t data = plist_new_plist_data(); @@ -254,7 +263,7 @@ static plist_t parse_uint_node(const char **bnode, uint8_t size) data->intval = UINT_TO_HOST(*bnode, size); (*bnode) += size; - data->type = PLIST_UINT; + data->type = PLIST_INT; return node_create(NULL, data); } @@ -317,7 +326,8 @@ static plist_t parse_string_node(const char **bnode, uint64_t size) static char *plist_utf16be_to_utf8(uint16_t *unistr, long len, long *items_read, long *items_written) { if (!unistr || (len <= 0)) return NULL; - char *outbuf; + char* outbuf; + char* outbuf_new; int p = 0; long i = 0; @@ -325,6 +335,7 @@ static char *plist_utf16be_to_utf8(uint16_t *unistr, long len, long *items_read, uint32_t w; int read_lead_surrogate = 0; + /* allocate with enough space */ outbuf = (char*)malloc(4*(len+1)); if (!outbuf) { PLIST_BIN_ERR("%s: Could not allocate %" PRIu64 " bytes\n", __func__, (uint64_t)(4*(len+1))); @@ -339,7 +350,7 @@ static char *plist_utf16be_to_utf8(uint16_t *unistr, long len, long *items_read, read_lead_surrogate = 1; w = 0x010000 + ((wc & 0x3FF) << 10); } else { - // This is invalid, the next 16 bit char should be a trail surrogate. + // This is invalid, the next 16 bit char should be a trail surrogate. // Handling error by skipping. read_lead_surrogate = 0; } @@ -374,30 +385,29 @@ static char *plist_utf16be_to_utf8(uint16_t *unistr, long len, long *items_read, } outbuf[p] = 0; + /* reduce the size to the actual size */ + outbuf_new = (char*)realloc(outbuf, p+1); + if (outbuf_new) { + outbuf = outbuf_new; + } + return outbuf; } static plist_t parse_unicode_node(const char **bnode, uint64_t size) { plist_data_t data = plist_new_plist_data(); - char *tmpstr = NULL; long items_read = 0; long items_written = 0; data->type = PLIST_STRING; - - tmpstr = plist_utf16be_to_utf8((uint16_t*)(*bnode), size, &items_read, &items_written); - if (!tmpstr) { + data->strval = plist_utf16be_to_utf8((uint16_t*)(*bnode), size, &items_read, &items_written); + if (!data->strval) { plist_free_data(data); return NULL; } - tmpstr[items_written] = '\0'; - - data->type = PLIST_STRING; - data->strval = realloc(tmpstr, items_written+1); - if (!data->strval) - data->strval = tmpstr; data->length = items_written; + return node_create(NULL, data); } @@ -490,8 +500,8 @@ static plist_t parse_dict_node(struct bplist_data *bplist, const char** bnode, u return NULL; } - node_attach(node, key); - node_attach(node, val); + node_attach((node_t)node, (node_t)key); + node_attach((node_t)node, (node_t)val); } return node; @@ -535,7 +545,7 @@ static plist_t parse_array_node(struct bplist_data *bplist, const char** bnode, return NULL; } - node_attach(node, val); + node_attach((node_t)node, (node_t)val); } return node; @@ -583,8 +593,8 @@ static plist_t parse_bin_node(struct bplist_data *bplist, const char** object) case BPLIST_DICT: { uint16_t next_size = **object & BPLIST_FILL; - if ((**object & BPLIST_MASK) != BPLIST_UINT) { - PLIST_BIN_ERR("%s: invalid size node type for node type 0x%02x: found 0x%02x, expected 0x%02x\n", __func__, type, **object & BPLIST_MASK, BPLIST_UINT); + if ((**object & BPLIST_MASK) != BPLIST_INT) { + PLIST_BIN_ERR("%s: invalid size node type for node type 0x%02x: found 0x%02x, expected 0x%02x\n", __func__, type, **object & BPLIST_MASK, BPLIST_INT); return NULL; } (*object)++; @@ -630,16 +640,23 @@ static plist_t parse_bin_node(struct bplist_data *bplist, const char** object) } case BPLIST_NULL: + { + plist_data_t data = plist_new_plist_data(); + data->type = PLIST_NULL; + data->length = 0; + return node_create(NULL, data); + } + default: return NULL; } - case BPLIST_UINT: + case BPLIST_INT: if (pobject + (uint64_t)(1 << size) > poffset_table) { - PLIST_BIN_ERR("%s: BPLIST_UINT data bytes point outside of valid range\n", __func__); + PLIST_BIN_ERR("%s: BPLIST_INT data bytes point outside of valid range\n", __func__); return NULL; } - return parse_uint_node(object, size); + return parse_int_node(object, size); case BPLIST_REAL: if (pobject + (uint64_t)(1 << size) > poffset_table) { @@ -734,7 +751,7 @@ static plist_t parse_bin_node_at_index(struct bplist_data *bplist, uint32_t node ptr = bplist->data + UINT_TO_HOST(idx_ptr, bplist->offset_size); /* make sure the node offset is in a sane range */ - if ((ptr < bplist->data) || (ptr >= bplist->offset_table)) { + if ((ptr < bplist->data+BPLIST_MAGIC_SIZE+BPLIST_VERSION_SIZE) || (ptr >= bplist->offset_table)) { PLIST_BIN_ERR("offset for node index %u points outside of valid range\n", node_index); return NULL; } @@ -751,8 +768,8 @@ static plist_t parse_bin_node_at_index(struct bplist_data *bplist, uint32_t node /* recursion check */ if (bplist->level > 0) { for (i = bplist->level-1; i >= 0; i--) { - uint32_t node_i = (uint32_t)(uintptr_t)ptr_array_index(bplist->used_indexes, i); - uint32_t node_level = (uint32_t)(uintptr_t)ptr_array_index(bplist->used_indexes, bplist->level); + void *node_i = ptr_array_index(bplist->used_indexes, i); + void *node_level = ptr_array_index(bplist->used_indexes, bplist->level); if (node_i == node_level) { PLIST_BIN_ERR("recursion detected in binary plist\n"); return NULL; @@ -767,7 +784,7 @@ static plist_t parse_bin_node_at_index(struct bplist_data *bplist, uint32_t node return plist; } -PLIST_API void plist_from_bin(const char *plist_bin, uint32_t length, plist_t * plist) +plist_err_t plist_from_bin(const char *plist_bin, uint32_t length, plist_t * plist) { bplist_trailer_t *trailer = NULL; uint8_t offset_size = 0; @@ -779,20 +796,28 @@ PLIST_API void plist_from_bin(const char *plist_bin, uint32_t length, plist_t * const char *start_data = NULL; const char *end_data = NULL; + if (!plist) { + return PLIST_ERR_INVALID_ARG; + } + *plist = NULL; + if (!plist_bin || length == 0) { + return PLIST_ERR_INVALID_ARG; + } + //first check we have enough data if (!(length >= BPLIST_MAGIC_SIZE + BPLIST_VERSION_SIZE + sizeof(bplist_trailer_t))) { PLIST_BIN_ERR("plist data is to small to hold a binary plist\n"); - return; + return PLIST_ERR_PARSE; } //check that plist_bin in actually a plist if (memcmp(plist_bin, BPLIST_MAGIC, BPLIST_MAGIC_SIZE) != 0) { PLIST_BIN_ERR("bplist magic mismatch\n"); - return; + return PLIST_ERR_PARSE; } //check for known version if (memcmp(plist_bin + BPLIST_MAGIC_SIZE, BPLIST_VERSION, BPLIST_VERSION_SIZE) != 0) { PLIST_BIN_ERR("unsupported binary plist version '%.2s\n", plist_bin+BPLIST_MAGIC_SIZE); - return; + return PLIST_ERR_PARSE; } start_data = plist_bin + BPLIST_MAGIC_SIZE + BPLIST_VERSION_SIZE; @@ -809,37 +834,37 @@ PLIST_API void plist_from_bin(const char *plist_bin, uint32_t length, plist_t * if (num_objects == 0) { PLIST_BIN_ERR("number of objects must be larger than 0\n"); - return; + return PLIST_ERR_PARSE; } if (offset_size == 0) { PLIST_BIN_ERR("offset size in trailer must be larger than 0\n"); - return; + return PLIST_ERR_PARSE; } if (ref_size == 0) { PLIST_BIN_ERR("object reference size in trailer must be larger than 0\n"); - return; + return PLIST_ERR_PARSE; } if (root_object >= num_objects) { PLIST_BIN_ERR("root object index (%" PRIu64 ") must be smaller than number of objects (%" PRIu64 ")\n", root_object, num_objects); - return; + return PLIST_ERR_PARSE; } if (offset_table < start_data || offset_table >= end_data) { PLIST_BIN_ERR("offset table offset points outside of valid range\n"); - return; + return PLIST_ERR_PARSE; } if (uint64_mul_overflow(num_objects, offset_size, &offset_table_size)) { PLIST_BIN_ERR("integer overflow when calculating offset table size\n"); - return; + return PLIST_ERR_PARSE; } - if ((offset_table + offset_table_size < offset_table) || (offset_table + offset_table_size > end_data)) { + if (offset_table_size > (uint64_t)(end_data - offset_table)) { PLIST_BIN_ERR("offset table points outside of valid range\n"); - return; + return PLIST_ERR_PARSE; } struct bplist_data bplist; @@ -854,12 +879,18 @@ PLIST_API void plist_from_bin(const char *plist_bin, uint32_t length, plist_t * if (!bplist.used_indexes) { PLIST_BIN_ERR("failed to create array to hold used node indexes. Out of memory?\n"); - return; + return PLIST_ERR_NO_MEM; } *plist = parse_bin_node_at_index(&bplist, root_object); ptr_array_free(bplist.used_indexes); + + if (!*plist) { + return PLIST_ERR_PARSE; + } + + return PLIST_ERR_SUCCESS; } static unsigned int plist_data_hash(const void* key) @@ -875,7 +906,8 @@ static unsigned int plist_data_hash(const void* key) switch (data->type) { case PLIST_BOOLEAN: - case PLIST_UINT: + case PLIST_NULL: + case PLIST_INT: case PLIST_REAL: case PLIST_DATE: case PLIST_UID: @@ -914,7 +946,7 @@ struct serialize_s hashtable_t* ref_table; }; -static void serialize_plist(node_t* node, void* data) +static void serialize_plist(node_t node, void* data) { uint64_t *index_val = NULL; struct serialize_s *ser = (struct serialize_s *) data; @@ -937,15 +969,13 @@ static void serialize_plist(node_t* node, void* data) ptr_array_add(ser->objects, node); //now recurse on children - node_t *ch; + node_t ch; for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) { serialize_plist(ch, data); } - - return; } -#define Log2(x) (x == 8 ? 3 : (x == 4 ? 2 : (x == 2 ? 1 : 0))) +#define Log2(x) ((x) == 8 ? 3 : ((x) == 4 ? 2 : ((x) == 2 ? 1 : 0))) static void write_int(bytearray_t * bplist, uint64_t val) { @@ -954,7 +984,7 @@ static void write_int(bytearray_t * bplist, uint64_t val) //do not write 3bytes int node if (size == 3) size++; - sz = BPLIST_UINT | Log2(size); + sz = BPLIST_INT | Log2(size); val = be64toh(val); byte_array_append(bplist, &sz, 1); @@ -963,7 +993,7 @@ static void write_int(bytearray_t * bplist, uint64_t val) static void write_uint(bytearray_t * bplist, uint64_t val) { - uint8_t sz = BPLIST_UINT | 4; + uint8_t sz = BPLIST_INT | 4; uint64_t zero = 0; val = be64toh(val); @@ -979,18 +1009,24 @@ static void write_real(bytearray_t * bplist, double val) buff[7] = BPLIST_REAL | Log2(size); if (size == sizeof(float)) { float floatval = (float)val; - *(uint32_t*)(buff+8) = float_bswap32(*(uint32_t*)&floatval); + uint32_t intval; + memcpy(&intval, &floatval, sizeof(float)); + *(uint32_t*)(buff+8) = float_bswap32(intval); } else { - *(uint64_t*)(buff+8) = float_bswap64(*(uint64_t*)&val); + uint64_t intval; + memcpy(&intval, &val, sizeof(double)); + *(uint64_t*)(buff+8) = float_bswap64(intval); } byte_array_append(bplist, buff+7, size+1); } static void write_date(bytearray_t * bplist, double val) { + uint64_t intval; + memcpy(&intval, &val, sizeof(double)); uint8_t buff[16]; buff[7] = BPLIST_DATE | 3; - *(uint64_t*)(buff+8) = float_bswap64(*(uint64_t*)&val); + *(uint64_t*)(buff+8) = float_bswap64(intval); byte_array_append(bplist, buff+7, 9); } @@ -1085,9 +1121,9 @@ static void write_unicode(bytearray_t * bplist, char *val, uint64_t size) free(unicodestr); } -static void write_array(bytearray_t * bplist, node_t* node, hashtable_t* ref_table, uint8_t ref_size) +static void write_array(bytearray_t * bplist, node_t node, hashtable_t* ref_table, uint8_t ref_size) { - node_t* cur = NULL; + node_t cur = NULL; uint64_t i = 0; uint64_t size = node_n_children(node); @@ -1104,9 +1140,9 @@ static void write_array(bytearray_t * bplist, node_t* node, hashtable_t* ref_tab } } -static void write_dict(bytearray_t * bplist, node_t* node, hashtable_t* ref_table, uint8_t ref_size) +static void write_dict(bytearray_t * bplist, node_t node, hashtable_t* ref_table, uint8_t ref_size) { - node_t* cur = NULL; + node_t cur = NULL; uint64_t i = 0; uint64_t size = node_n_children(node) / 2; @@ -1158,7 +1194,7 @@ static int is_ascii_string(char* s, int len) return ret; } -PLIST_API void plist_to_bin(plist_t plist, char **plist_bin, uint32_t * length) +plist_err_t plist_to_bin(plist_t plist, char **plist_bin, uint32_t * length) { ptrarray_t* objects = NULL; hashtable_t* ref_table = NULL; @@ -1176,18 +1212,26 @@ PLIST_API void plist_to_bin(plist_t plist, char **plist_bin, uint32_t * length) uint64_t objects_len = 0; //check for valid input - if (!plist || !plist_bin || *plist_bin || !length) - return; + if (!plist || !plist_bin || !length) { + return PLIST_ERR_INVALID_ARG; + } //list of objects objects = ptr_array_new(4096); + if (!objects) { + return PLIST_ERR_NO_MEM; + } //hashtable to write only once same nodes ref_table = hash_table_new(plist_data_hash, plist_data_compare, free); + if (!ref_table) { + ptr_array_free(objects); + return PLIST_ERR_NO_MEM; + } //serialize plist ser_s.objects = objects; ser_s.ref_table = ref_table; - serialize_plist(plist, &ser_s); + serialize_plist((node_t)plist, &ser_s); //now stream to output buffer offset_size = 0; //unknown yet @@ -1201,12 +1245,13 @@ PLIST_API void plist_to_bin(plist_t plist, char **plist_bin, uint32_t * length) uint64_t req = 0; for (i = 0; i < num_objects; i++) { - node_t* node = ptr_array_index(objects, i); + node_t node = (node_t)ptr_array_index(objects, i); plist_data_t data = plist_get_data(node); uint64_t size; uint8_t bsize; switch (data->type) { + case PLIST_NULL: case PLIST_BOOLEAN: req += 1; break; @@ -1281,6 +1326,11 @@ PLIST_API void plist_to_bin(plist_t plist, char **plist_bin, uint32_t * length) //setup a dynamic bytes array to store bplist in bplist_buff = byte_array_new(req); + if (!bplist_buff) { + ptr_array_free(objects); + hash_table_destroy(ref_table); + return PLIST_ERR_NO_MEM; + } //set magic number and version byte_array_append(bplist_buff, BPLIST_MAGIC, BPLIST_MAGIC_SIZE); @@ -1297,12 +1347,17 @@ PLIST_API void plist_to_bin(plist_t plist, char **plist_bin, uint32_t * length) switch (data->type) { + case PLIST_NULL: { + uint8_t b = 0; + byte_array_append(bplist_buff, &b, 1); + break; + } case PLIST_BOOLEAN: { uint8_t b = data->boolval ? BPLIST_TRUE : BPLIST_FALSE; byte_array_append(bplist_buff, &b, 1); break; } - case PLIST_UINT: + case PLIST_INT: if (data->length == 16) { write_uint(bplist_buff, data->intval); } else { @@ -1329,10 +1384,10 @@ PLIST_API void plist_to_bin(plist_t plist, char **plist_bin, uint32_t * length) write_data(bplist_buff, data->buff, data->length); break; case PLIST_ARRAY: - write_array(bplist_buff, ptr_array_index(objects, i), ref_table, ref_size); + write_array(bplist_buff, (node_t)ptr_array_index(objects, i), ref_table, ref_size); break; case PLIST_DICT: - write_dict(bplist_buff, ptr_array_index(objects, i), ref_table, ref_size); + write_dict(bplist_buff, (node_t)ptr_array_index(objects, i), ref_table, ref_size); break; case PLIST_DATE: write_date(bplist_buff, data->realval); @@ -1370,14 +1425,11 @@ PLIST_API void plist_to_bin(plist_t plist, char **plist_bin, uint32_t * length) byte_array_append(bplist_buff, &trailer, sizeof(bplist_trailer_t)); //set output buffer and size - *plist_bin = bplist_buff->data; + *plist_bin = (char*)bplist_buff->data; *length = bplist_buff->len; bplist_buff->data = NULL; // make sure we don't free the output buffer byte_array_free(bplist_buff); -} -PLIST_API void plist_to_bin_free(char *plist_bin) -{ - free(plist_bin); + return PLIST_ERR_SUCCESS; } |