summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Nikias Bassen2026-01-17 15:18:06 +0100
committerGravatar Nikias Bassen2026-01-17 16:04:00 +0100
commite45099fb21b679aa0cdb0db394587bb5ba675b0c (patch)
treeb1a2c1dd34877d83a04d6d6fab804fb2e5a65f2d
parent26dd27c435a0d110c924e1fef34ad0f6ae60e251 (diff)
downloadlibplist-e45099fb21b679aa0cdb0db394587bb5ba675b0c.tar.gz
libplist-e45099fb21b679aa0cdb0db394587bb5ba675b0c.tar.bz2
Prevent deep nesting of plist structures in all input/output formats
Thanks to @unbengable12 for reporting. Addresses #288, #289, #290, #291, and #292.
-rw-r--r--include/plist/plist.h1
-rw-r--r--src/bplist.c26
-rw-r--r--src/jplist.c48
-rw-r--r--src/oplist.c69
-rw-r--r--src/out-default.c7
-rw-r--r--src/out-limd.c7
-rw-r--r--src/out-plutil.c7
-rw-r--r--src/plist.h4
-rw-r--r--src/xplist.c111
9 files changed, 185 insertions, 95 deletions
diff --git a/include/plist/plist.h b/include/plist/plist.h
index 5ea30b8..8ed9063 100644
--- a/include/plist/plist.h
+++ b/include/plist/plist.h
@@ -145,6 +145,7 @@ extern "C"
PLIST_ERR_NO_MEM = -4, /**< not enough memory to handle the operation */
PLIST_ERR_IO = -5, /**< I/O error */
PLIST_ERR_CIRCULAR_REF = -6, /**< circular reference detected */
+ PLIST_ERR_MAX_NESTING = -7, /**< maximum nesting depth exceeded */
PLIST_ERR_UNKNOWN = -255 /**< an unspecified error occurred */
} plist_err_t;
diff --git a/src/bplist.c b/src/bplist.c
index a2dc957..254c0ff 100644
--- a/src/bplist.c
+++ b/src/bplist.c
@@ -239,6 +239,7 @@ struct bplist_data {
const char* offset_table;
uint32_t level;
ptrarray_t* used_indexes;
+ plist_err_t err;
};
#ifdef DEBUG
@@ -787,6 +788,7 @@ static plist_t parse_bin_node_at_index(struct bplist_data *bplist, uint32_t node
if (node_index >= bplist->num_objects) {
PLIST_BIN_ERR("node index (%u) must be smaller than the number of objects (%" PRIu64 ")\n", node_index, bplist->num_objects);
+ bplist->err = PLIST_ERR_PARSE;
return NULL;
}
@@ -794,6 +796,7 @@ static plist_t parse_bin_node_at_index(struct bplist_data *bplist, uint32_t node
if (idx_ptr < bplist->offset_table ||
idx_ptr >= bplist->offset_table + bplist->num_objects * bplist->offset_size) {
PLIST_BIN_ERR("node index %u points outside of valid range\n", node_index);
+ bplist->err = PLIST_ERR_PARSE;
return NULL;
}
@@ -801,6 +804,14 @@ static plist_t parse_bin_node_at_index(struct bplist_data *bplist, uint32_t node
/* make sure the node offset is in a sane range */
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);
+ bplist->err = PLIST_ERR_PARSE;
+ return NULL;
+ }
+
+ /* check nesting depth */
+ if (bplist->level > PLIST_MAX_NESTING_DEPTH) {
+ PLIST_BIN_ERR("maximum nesting depth (%u) exceeded\n",(unsigned)PLIST_MAX_NESTING_DEPTH);
+ bplist->err = PLIST_ERR_MAX_NESTING;
return NULL;
}
@@ -820,6 +831,7 @@ static plist_t parse_bin_node_at_index(struct bplist_data *bplist, uint32_t node
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");
+ bplist->err = PLIST_ERR_PARSE;
return NULL;
}
}
@@ -931,6 +943,7 @@ plist_err_t plist_from_bin(const char *plist_bin, uint32_t length, plist_t * pli
bplist.offset_table = offset_table;
bplist.level = 0;
bplist.used_indexes = ptr_array_new(16);
+ bplist.err = PLIST_ERR_SUCCESS;
if (!bplist.used_indexes) {
PLIST_BIN_ERR("failed to create array to hold used node indexes. Out of memory?\n");
@@ -942,7 +955,7 @@ plist_err_t plist_from_bin(const char *plist_bin, uint32_t length, plist_t * pli
ptr_array_free(bplist.used_indexes);
if (!*plist) {
- return PLIST_ERR_PARSE;
+ return (bplist.err != PLIST_ERR_SUCCESS) ? bplist.err : PLIST_ERR_PARSE;
}
return PLIST_ERR_SUCCESS;
@@ -1002,11 +1015,16 @@ struct serialize_s
hashtable_t* in_stack;
};
-static plist_err_t serialize_plist(node_t node, void* data)
+static plist_err_t serialize_plist(node_t node, void* data, uint32_t depth)
{
uint64_t *index_val = NULL;
struct serialize_s *ser = (struct serialize_s *) data;
+ if (depth > PLIST_MAX_NESTING_DEPTH) {
+ PLIST_BIN_WRITE_ERR("maximum nesting depth (%u) exceeded\n", (unsigned)PLIST_MAX_NESTING_DEPTH);
+ return PLIST_ERR_MAX_NESTING;
+ }
+
// circular reference check: is node on current recursion stack?
if (hash_table_lookup(ser->in_stack, node)) {
PLIST_BIN_WRITE_ERR("circular reference detected\n");
@@ -1036,7 +1054,7 @@ static plist_err_t serialize_plist(node_t node, void* data)
node_t ch;
plist_err_t err = PLIST_ERR_SUCCESS;
for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) {
- err = serialize_plist(ch, data);
+ err = serialize_plist(ch, data, depth+1);
if (err != PLIST_ERR_SUCCESS) {
break;
}
@@ -1313,7 +1331,7 @@ plist_err_t plist_to_bin(plist_t plist, char **plist_bin, uint32_t * length)
ser_s.objects = objects;
ser_s.ref_table = ref_table;
ser_s.in_stack = in_stack;
- plist_err_t err = serialize_plist((node_t)plist, &ser_s);
+ plist_err_t err = serialize_plist((node_t)plist, &ser_s, 0);
if (err != PLIST_ERR_SUCCESS) {
ptr_array_free(objects);
hash_table_destroy(ref_table);
diff --git a/src/jplist.c b/src/jplist.c
index 2e53400..9a40844 100644
--- a/src/jplist.c
+++ b/src/jplist.c
@@ -323,6 +323,11 @@ static plist_err_t _node_estimate_size(node_t node, uint64_t *size, uint32_t dep
return PLIST_ERR_INVALID_ARG;
}
+ if (depth > PLIST_MAX_NESTING_DEPTH) {
+ PLIST_JSON_WRITE_ERR("maximum nesting depth (%u) exceeded\n", (unsigned)PLIST_MAX_NESTING_DEPTH);
+ return PLIST_ERR_MAX_NESTING;
+ }
+
if (hash_table_lookup(visited, node)) {
PLIST_JSON_WRITE_ERR("circular reference detected\n");
return PLIST_ERR_CIRCULAR_REF;
@@ -471,6 +476,7 @@ plist_err_t plist_to_json(plist_t plist, char **plist_json, uint32_t* length, in
typedef struct {
jsmntok_t* tokens;
int count;
+ plist_err_t err;
} jsmntok_info_t;
static int64_t parse_decimal(const char* str, const char* str_end, char** endp)
@@ -698,12 +704,18 @@ static plist_t parse_string(const char* js, jsmntok_info_t* ti, int* index)
return node;
}
-static plist_t parse_object(const char* js, jsmntok_info_t* ti, int* index);
+static plist_t parse_object(const char* js, jsmntok_info_t* ti, int* index, uint32_t depth);
-static plist_t parse_array(const char* js, jsmntok_info_t* ti, int* index)
+static plist_t parse_array(const char* js, jsmntok_info_t* ti, int* index, uint32_t depth)
{
if (ti->tokens[*index].type != JSMN_ARRAY) {
PLIST_JSON_ERR("%s: token type != JSMN_ARRAY\n", __func__);
+ ti->err = PLIST_ERR_PARSE;
+ return NULL;
+ }
+ if (depth > PLIST_MAX_NESTING_DEPTH) {
+ PLIST_JSON_ERR("%s: maximum nesting depth (%u) exceeded\n", __func__, (unsigned)PLIST_MAX_NESTING_DEPTH);
+ ti->err = PLIST_ERR_MAX_NESTING;
return NULL;
}
plist_t arr = plist_new_array();
@@ -714,15 +726,16 @@ static plist_t parse_array(const char* js, jsmntok_info_t* ti, int* index)
if (j >= ti->count) {
PLIST_JSON_ERR("%s: token index out of valid range\n", __func__);
plist_free(arr);
+ ti->err = PLIST_ERR_PARSE;
return NULL;
}
plist_t val = NULL;
switch (ti->tokens[j].type) {
case JSMN_OBJECT:
- val = parse_object(js, ti, &j);
+ val = parse_object(js, ti, &j, depth+1);
break;
case JSMN_ARRAY:
- val = parse_array(js, ti, &j);
+ val = parse_array(js, ti, &j, depth+1);
break;
case JSMN_STRING:
val = parse_string(js, ti, &j);
@@ -737,6 +750,7 @@ static plist_t parse_array(const char* js, jsmntok_info_t* ti, int* index)
plist_array_append_item(arr, val);
} else {
plist_free(arr);
+ ti->err = PLIST_ERR_PARSE;
return NULL;
}
}
@@ -744,10 +758,16 @@ static plist_t parse_array(const char* js, jsmntok_info_t* ti, int* index)
return arr;
}
-static plist_t parse_object(const char* js, jsmntok_info_t* ti, int* index)
+static plist_t parse_object(const char* js, jsmntok_info_t* ti, int* index, uint32_t depth)
{
if (ti->tokens[*index].type != JSMN_OBJECT) {
PLIST_JSON_ERR("%s: token type != JSMN_OBJECT\n", __func__);
+ ti->err = PLIST_ERR_PARSE;
+ return NULL;
+ }
+ if (depth > PLIST_MAX_NESTING_DEPTH) {
+ PLIST_JSON_ERR("%s: maximum nesting depth (%u) exceeded\n", __func__, (unsigned)PLIST_MAX_NESTING_DEPTH);
+ ti->err = PLIST_ERR_MAX_NESTING;
return NULL;
}
int num_tokens = ti->tokens[*index].size;
@@ -755,6 +775,7 @@ static plist_t parse_object(const char* js, jsmntok_info_t* ti, int* index)
int j = (*index)+1;
if (num_tokens % 2 != 0) {
PLIST_JSON_ERR("%s: number of children must be even\n", __func__);
+ ti->err = PLIST_ERR_PARSE;
return NULL;
}
plist_t obj = plist_new_dict();
@@ -762,12 +783,14 @@ static plist_t parse_object(const char* js, jsmntok_info_t* ti, int* index)
if (j+1 >= ti->count) {
PLIST_JSON_ERR("%s: token index out of valid range\n", __func__);
plist_free(obj);
+ ti->err = PLIST_ERR_PARSE;
return NULL;
}
if (ti->tokens[j].type == JSMN_STRING) {
char* key = unescape_string(js + ti->tokens[j].start, ti->tokens[j].end - ti->tokens[j].start, NULL);
if (!key) {
plist_free(obj);
+ ti->err = PLIST_ERR_PARSE;
return NULL;
}
plist_t val = NULL;
@@ -775,10 +798,10 @@ static plist_t parse_object(const char* js, jsmntok_info_t* ti, int* index)
num++;
switch (ti->tokens[j].type) {
case JSMN_OBJECT:
- val = parse_object(js, ti, &j);
+ val = parse_object(js, ti, &j, depth+1);
break;
case JSMN_ARRAY:
- val = parse_array(js, ti, &j);
+ val = parse_array(js, ti, &j, depth+1);
break;
case JSMN_STRING:
val = parse_string(js, ti, &j);
@@ -794,12 +817,14 @@ static plist_t parse_object(const char* js, jsmntok_info_t* ti, int* index)
} else {
free(key);
plist_free(obj);
+ ti->err = PLIST_ERR_PARSE;
return NULL;
}
free(key);
} else {
PLIST_JSON_ERR("%s: keys must be of type STRING\n", __func__);
plist_free(obj);
+ ti->err = PLIST_ERR_PARSE;
return NULL;
}
}
@@ -859,7 +884,7 @@ plist_err_t plist_from_json(const char *json, uint32_t length, plist_t * plist)
}
int startindex = 0;
- jsmntok_info_t ti = { tokens, parser.toknext };
+ jsmntok_info_t ti = { tokens, parser.toknext, PLIST_ERR_SUCCESS };
switch (tokens[startindex].type) {
case JSMN_PRIMITIVE:
*plist = parse_primitive(json, &ti, &startindex);
@@ -868,14 +893,17 @@ plist_err_t plist_from_json(const char *json, uint32_t length, plist_t * plist)
*plist = parse_string(json, &ti, &startindex);
break;
case JSMN_ARRAY:
- *plist = parse_array(json, &ti, &startindex);
+ *plist = parse_array(json, &ti, &startindex, 0);
break;
case JSMN_OBJECT:
- *plist = parse_object(json, &ti, &startindex);
+ *plist = parse_object(json, &ti, &startindex, 0);
break;
default:
break;
}
free(tokens);
+ if (!*plist) {
+ return (ti.err != PLIST_ERR_SUCCESS) ? ti.err : PLIST_ERR_PARSE;
+ }
return PLIST_ERR_SUCCESS;
}
diff --git a/src/oplist.c b/src/oplist.c
index 680873c..0eea27a 100644
--- a/src/oplist.c
+++ b/src/oplist.c
@@ -367,6 +367,11 @@ static plist_err_t _node_estimate_size(node_t node, uint64_t *size, uint32_t dep
return PLIST_ERR_INVALID_ARG;
}
+ if (depth > PLIST_MAX_NESTING_DEPTH) {
+ PLIST_OSTEP_WRITE_ERR("node tree is nested too deeply\n");
+ return PLIST_ERR_MAX_NESTING;
+ }
+
if (hash_table_lookup(visited, node)) {
PLIST_OSTEP_WRITE_ERR("circular reference detected\n");
return PLIST_ERR_CIRCULAR_REF;
@@ -511,7 +516,7 @@ struct _parse_ctx {
const char *start;
const char *pos;
const char *end;
- int err;
+ plist_err_t err;
uint32_t depth;
};
typedef struct _parse_ctx* parse_ctx;
@@ -568,50 +573,50 @@ static void parse_dict_data(parse_ctx ctx, plist_t dict)
}
key = NULL;
ctx->err = node_from_openstep(ctx, &key);
- if (ctx->err != 0) {
+ if (ctx->err != PLIST_ERR_SUCCESS) {
break;
}
if (!PLIST_IS_STRING(key)) {
PLIST_OSTEP_ERR("Invalid type for dictionary key at offset %ld\n", (long int)(ctx->pos - ctx->start));
- ctx->err++;
+ ctx->err = PLIST_ERR_PARSE;
break;
}
parse_skip_ws(ctx);
if (ctx->pos >= ctx->end) {
PLIST_OSTEP_ERR("EOF while parsing dictionary '=' delimiter at offset %ld\n", (long int)(ctx->pos - ctx->start));
- ctx->err++;
+ ctx->err = PLIST_ERR_PARSE;
break;
}
if (*ctx->pos != '=') {
PLIST_OSTEP_ERR("Missing '=' while parsing dictionary item at offset %ld\n", (long int)(ctx->pos - ctx->start));
- ctx->err++;
+ ctx->err = PLIST_ERR_PARSE;
break;
}
ctx->pos++;
if (ctx->pos >= ctx->end) {
PLIST_OSTEP_ERR("EOF while parsing dictionary item at offset %ld\n", (long int)(ctx->pos - ctx->start));
- ctx->err++;
+ ctx->err = PLIST_ERR_PARSE;
break;
}
val = NULL;
ctx->err = node_from_openstep(ctx, &val);
- if (ctx->err != 0) {
+ if (ctx->err != PLIST_ERR_SUCCESS) {
break;
}
if (!val) {
PLIST_OSTEP_ERR("Missing value for dictionary item at offset %ld\n", (long int)(ctx->pos - ctx->start));
- ctx->err++;
+ ctx->err = PLIST_ERR_PARSE;
break;
}
parse_skip_ws(ctx);
if (ctx->pos >= ctx->end) {
PLIST_OSTEP_ERR("EOF while parsing dictionary item terminator ';' at offset %ld\n", (long int)(ctx->pos - ctx->start));
- ctx->err++;
+ ctx->err = PLIST_ERR_PARSE;
break;
}
if (*ctx->pos != ';') {
PLIST_OSTEP_ERR("Missing terminating ';' while parsing dictionary item at offset %ld\n", (long int)(ctx->pos - ctx->start));
- ctx->err++;
+ ctx->err = PLIST_ERR_PARSE;
break;
}
@@ -631,10 +636,10 @@ static plist_err_t node_from_openstep(parse_ctx ctx, plist_t *plist)
plist_t subnode = NULL;
const char *p = NULL;
ctx->depth++;
- if (ctx->depth > 1000) {
+ if (ctx->depth > PLIST_MAX_NESTING_DEPTH) {
PLIST_OSTEP_ERR("Too many levels of recursion (%u) at offset %ld\n", ctx->depth, (long int)(ctx->pos - ctx->start));
- ctx->err++;
- return PLIST_ERR_PARSE;
+ ctx->err = PLIST_ERR_MAX_NESTING;
+ return ctx->err;
}
while (ctx->pos < ctx->end && !ctx->err) {
parse_skip_ws(ctx);
@@ -652,12 +657,12 @@ static plist_err_t node_from_openstep(parse_ctx ctx, plist_t *plist)
}
if (ctx->pos >= ctx->end) {
PLIST_OSTEP_ERR("EOF while parsing dictionary terminator '}' at offset %ld\n", (long int)(ctx->pos - ctx->start));
- ctx->err++;
+ ctx->err = PLIST_ERR_PARSE;
break;
}
if (*ctx->pos != '}') {
PLIST_OSTEP_ERR("Missing terminating '}' at offset %ld\n", (long int)(ctx->pos - ctx->start));
- ctx->err++;
+ ctx->err = PLIST_ERR_PARSE;
goto err_out;
}
ctx->pos++;
@@ -675,11 +680,11 @@ static plist_err_t node_from_openstep(parse_ctx ctx, plist_t *plist)
break;
}
ctx->err = node_from_openstep(ctx, &tmp);
- if (ctx->err != 0) {
+ if (ctx->err != PLIST_ERR_SUCCESS) {
break;
}
if (!tmp) {
- ctx->err++;
+ ctx->err = PLIST_ERR_PARSE;
break;
}
plist_array_append_item(subnode, tmp);
@@ -687,7 +692,7 @@ static plist_err_t node_from_openstep(parse_ctx ctx, plist_t *plist)
parse_skip_ws(ctx);
if (ctx->pos >= ctx->end) {
PLIST_OSTEP_ERR("EOF while parsing array item delimiter ',' at offset %ld\n", (long int)(ctx->pos - ctx->start));
- ctx->err++;
+ ctx->err = PLIST_ERR_PARSE;
break;
}
if (*ctx->pos != ',') {
@@ -697,17 +702,17 @@ static plist_err_t node_from_openstep(parse_ctx ctx, plist_t *plist)
}
plist_free(tmp);
tmp = NULL;
- if (ctx->err) {
+ if (ctx->err != PLIST_ERR_SUCCESS) {
goto err_out;
}
if (ctx->pos >= ctx->end) {
PLIST_OSTEP_ERR("EOF while parsing array terminator ')' at offset %ld\n", (long int)(ctx->pos - ctx->start));
- ctx->err++;
+ ctx->err = PLIST_ERR_PARSE;
break;
}
if (*ctx->pos != ')') {
PLIST_OSTEP_ERR("Missing terminating ')' at offset %ld\n", (long int)(ctx->pos - ctx->start));
- ctx->err++;
+ ctx->err = PLIST_ERR_PARSE;
goto err_out;
}
ctx->pos++;
@@ -722,7 +727,7 @@ static plist_err_t node_from_openstep(parse_ctx ctx, plist_t *plist)
parse_skip_ws(ctx);
if (ctx->pos >= ctx->end) {
PLIST_OSTEP_ERR("EOF while parsing data terminator '>' at offset %ld\n", (long int)(ctx->pos - ctx->start));
- ctx->err++;
+ ctx->err = PLIST_ERR_PARSE;
break;
}
if (*ctx->pos == '>') {
@@ -730,19 +735,19 @@ static plist_err_t node_from_openstep(parse_ctx ctx, plist_t *plist)
}
if (!isxdigit(*ctx->pos)) {
PLIST_OSTEP_ERR("Invalid byte group in data at offset %ld\n", (long int)(ctx->pos - ctx->start));
- ctx->err++;
+ ctx->err = PLIST_ERR_PARSE;
break;
}
uint8_t b = HEX_DIGIT(*ctx->pos);
ctx->pos++;
if (ctx->pos >= ctx->end) {
PLIST_OSTEP_ERR("Unexpected end of data at offset %ld\n", (long int)(ctx->pos - ctx->start));
- ctx->err++;
+ ctx->err = PLIST_ERR_PARSE;
break;
}
if (!isxdigit(*ctx->pos)) {
PLIST_OSTEP_ERR("Invalid byte group in data at offset %ld\n", (long int)(ctx->pos - ctx->start));
- ctx->err++;
+ ctx->err = PLIST_ERR_PARSE;
break;
}
b = (b << 4) + HEX_DIGIT(*ctx->pos);
@@ -758,14 +763,14 @@ static plist_err_t node_from_openstep(parse_ctx ctx, plist_t *plist)
byte_array_free(bytes);
plist_free_data(data);
PLIST_OSTEP_ERR("EOF while parsing data terminator '>' at offset %ld\n", (long int)(ctx->pos - ctx->start));
- ctx->err++;
+ ctx->err = PLIST_ERR_PARSE;
goto err_out;
}
if (*ctx->pos != '>') {
byte_array_free(bytes);
plist_free_data(data);
PLIST_OSTEP_ERR("Missing terminating '>' at offset %ld\n", (long int)(ctx->pos - ctx->start));
- ctx->err++;
+ ctx->err = PLIST_ERR_PARSE;
goto err_out;
}
ctx->pos++;
@@ -793,13 +798,13 @@ static plist_err_t node_from_openstep(parse_ctx ctx, plist_t *plist)
if (ctx->pos >= ctx->end) {
plist_free_data(data);
PLIST_OSTEP_ERR("EOF while parsing quoted string at offset %ld\n", (long int)(ctx->pos - ctx->start));
- ctx->err++;
+ ctx->err = PLIST_ERR_PARSE;
goto err_out;
}
if (*ctx->pos != c) {
plist_free_data(data);
PLIST_OSTEP_ERR("Missing closing quote (%c) at offset %ld\n", c, (long int)(ctx->pos - ctx->start));
- ctx->err++;
+ ctx->err = PLIST_ERR_PARSE;
goto err_out;
}
size_t slen = ctx->pos - p;
@@ -900,7 +905,7 @@ static plist_err_t node_from_openstep(parse_ctx ctx, plist_t *plist)
} else {
plist_free_data(data);
PLIST_OSTEP_ERR("Unexpected character when parsing unquoted string at offset %ld\n", (long int)(ctx->pos - ctx->start));
- ctx->err++;
+ ctx->err = PLIST_ERR_PARSE;
break;
}
}
@@ -909,11 +914,11 @@ static plist_err_t node_from_openstep(parse_ctx ctx, plist_t *plist)
ctx->depth--;
err_out:
- if (ctx->err) {
+ if (ctx->err != PLIST_ERR_SUCCESS) {
plist_free(subnode);
plist_free(*plist);
*plist = NULL;
- return PLIST_ERR_PARSE;
+ return ctx->err;
}
return PLIST_ERR_SUCCESS;
}
diff --git a/src/out-default.c b/src/out-default.c
index 09e64c3..fb57bcf 100644
--- a/src/out-default.c
+++ b/src/out-default.c
@@ -318,6 +318,13 @@ static plist_err_t _node_estimate_size(node_t node, uint64_t *size, uint32_t dep
return PLIST_ERR_INVALID_ARG;
}
+ if (depth > PLIST_MAX_NESTING_DEPTH) {
+#if DEBUG
+ fprintf(stderr, "libplist: ERROR: maximum nesting depth (%u) exceeded\n", (unsigned)PLIST_MAX_NESTING_DEPTH);
+#endif
+ return PLIST_ERR_MAX_NESTING;
+ }
+
if (hash_table_lookup(visited, node)) {
#if DEBUG
fprintf(stderr, "libplist: ERROR: circular reference detected\n");
diff --git a/src/out-limd.c b/src/out-limd.c
index e281644..35247fb 100644
--- a/src/out-limd.c
+++ b/src/out-limd.c
@@ -286,6 +286,13 @@ static plist_err_t _node_estimate_size(node_t node, uint64_t *size, uint32_t dep
return PLIST_ERR_INVALID_ARG;
}
+ if (depth > PLIST_MAX_NESTING_DEPTH) {
+#if DEBUG
+ fprintf(stderr, "libplist: ERROR: maximum nesting depth (%u) exceeded\n", (unsigned)PLIST_MAX_NESTING_DEPTH);
+#endif
+ return PLIST_ERR_MAX_NESTING;
+ }
+
if (hash_table_lookup(visited, node)) {
#if DEBUG
fprintf(stderr, "libplist: ERROR: circular reference detected\n");
diff --git a/src/out-plutil.c b/src/out-plutil.c
index 5354aa3..bf9e72e 100644
--- a/src/out-plutil.c
+++ b/src/out-plutil.c
@@ -312,6 +312,13 @@ static plist_err_t _node_estimate_size(node_t node, uint64_t *size, uint32_t dep
return PLIST_ERR_INVALID_ARG;
}
+ if (depth > PLIST_MAX_NESTING_DEPTH) {
+#if DEBUG
+ fprintf(stderr, "libplist: ERROR: maximum nesting depth (%u) exceeded\n", (unsigned)PLIST_MAX_NESTING_DEPTH);
+#endif
+ return PLIST_ERR_MAX_NESTING;
+ }
+
if (hash_table_lookup(visited, node)) {
#if DEBUG
fprintf(stderr, "libplist: ERROR: circular reference detected\n");
diff --git a/src/plist.h b/src/plist.h
index 3043fb7..e310bcc 100644
--- a/src/plist.h
+++ b/src/plist.h
@@ -49,6 +49,10 @@
#endif
#endif
+#ifndef PLIST_MAX_NESTING_DEPTH
+#define PLIST_MAX_NESTING_DEPTH 512
+#endif
+
#include "plist/plist.h"
struct plist_data_s
diff --git a/src/xplist.c b/src/xplist.c
index c25c6b9..9870104 100644
--- a/src/xplist.c
+++ b/src/xplist.c
@@ -452,6 +452,11 @@ static plist_err_t _node_estimate_size(node_t node, uint64_t *size, uint32_t dep
return PLIST_ERR_INVALID_ARG;
}
+ if (depth > PLIST_MAX_NESTING_DEPTH) {
+ PLIST_XML_WRITE_ERR("maximum nesting depth (%u) exceeded\n", (unsigned)PLIST_MAX_NESTING_DEPTH);
+ return PLIST_ERR_MAX_NESTING;
+ }
+
if (hash_table_lookup(visited, node)) {
PLIST_XML_WRITE_ERR("circular reference detected\n");
return PLIST_ERR_CIRCULAR_REF;
@@ -596,7 +601,7 @@ plist_err_t plist_to_xml(plist_t plist, char **plist_xml, uint32_t * length)
struct _parse_ctx {
const char *pos;
const char *end;
- int err;
+ plist_err_t err;
};
typedef struct _parse_ctx* parse_ctx;
@@ -720,21 +725,21 @@ static text_part_t* get_text_parts(parse_ctx ctx, const char* tag, size_t tag_le
find_char(ctx, '<', 0);
if (ctx->pos >= ctx->end || *ctx->pos != '<') {
PLIST_XML_ERR("EOF while looking for closing tag\n");
- ctx->err++;
+ ctx->err = PLIST_ERR_PARSE;
return NULL;
}
q = ctx->pos;
ctx->pos++;
if (ctx->pos >= ctx->end) {
PLIST_XML_ERR("EOF while parsing '%s'\n", p);
- ctx->err++;
+ ctx->err = PLIST_ERR_PARSE;
return NULL;
}
if (*ctx->pos == '!') {
ctx->pos++;
if (ctx->pos >= ctx->end-1) {
PLIST_XML_ERR("EOF while parsing <! special tag\n");
- ctx->err++;
+ ctx->err = PLIST_ERR_PARSE;
return NULL;
}
if (*ctx->pos == '-' && *(ctx->pos+1) == '-') {
@@ -747,7 +752,7 @@ static text_part_t* get_text_parts(parse_ctx ctx, const char* tag, size_t tag_le
find_str(ctx, "-->", 3, 0);
if (ctx->pos > ctx->end-3 || strncmp(ctx->pos, "-->", 3) != 0) {
PLIST_XML_ERR("EOF while looking for end of comment\n");
- ctx->err++;
+ ctx->err = PLIST_ERR_PARSE;
return NULL;
}
ctx->pos += 3;
@@ -755,7 +760,7 @@ static text_part_t* get_text_parts(parse_ctx ctx, const char* tag, size_t tag_le
ctx->pos++;
if (ctx->pos >= ctx->end - 8) {
PLIST_XML_ERR("EOF while parsing <[ tag\n");
- ctx->err++;
+ ctx->err = PLIST_ERR_PARSE;
return NULL;
}
if (strncmp(ctx->pos, "CDATA[", 6) == 0) {
@@ -771,7 +776,7 @@ static text_part_t* get_text_parts(parse_ctx ctx, const char* tag, size_t tag_le
find_str(ctx, "]]>", 3, 0);
if (ctx->pos > ctx->end-3 || strncmp(ctx->pos, "]]>", 3) != 0) {
PLIST_XML_ERR("EOF while looking for end of CDATA block\n");
- ctx->err++;
+ ctx->err = PLIST_ERR_PARSE;
return NULL;
}
q = ctx->pos;
@@ -785,14 +790,14 @@ static text_part_t* get_text_parts(parse_ctx ctx, const char* tag, size_t tag_le
p = ctx->pos;
find_next(ctx, " \r\n\t>", 5, 1);
PLIST_XML_ERR("Invalid special tag <[%.*s> encountered inside <%s> tag\n", (int)(ctx->pos - p), p, tag);
- ctx->err++;
+ ctx->err = PLIST_ERR_PARSE;
return NULL;
}
} else {
p = ctx->pos;
find_next(ctx, " \r\n\t>", 5, 1);
PLIST_XML_ERR("Invalid special tag <!%.*s> encountered inside <%s> tag\n", (int)(ctx->pos - p), p, tag);
- ctx->err++;
+ ctx->err = PLIST_ERR_PARSE;
return NULL;
}
} else if (*ctx->pos == '/') {
@@ -801,25 +806,25 @@ static text_part_t* get_text_parts(parse_ctx ctx, const char* tag, size_t tag_le
p = ctx->pos;
find_next(ctx, " \r\n\t>", 5, 1);
PLIST_XML_ERR("Invalid tag <%.*s> encountered inside <%s> tag\n", (int)(ctx->pos - p), p, tag);
- ctx->err++;
+ ctx->err = PLIST_ERR_PARSE;
return NULL;
}
} while (1);
ctx->pos++;
if (ctx->pos >= ctx->end-tag_len || strncmp(ctx->pos, tag, tag_len) != 0) {
PLIST_XML_ERR("EOF or end tag mismatch\n");
- ctx->err++;
+ ctx->err = PLIST_ERR_PARSE;
return NULL;
}
ctx->pos+=tag_len;
parse_skip_ws(ctx);
if (ctx->pos >= ctx->end) {
PLIST_XML_ERR("EOF while parsing closing tag\n");
- ctx->err++;
+ ctx->err = PLIST_ERR_PARSE;
return NULL;
} else if (*ctx->pos != '>') {
PLIST_XML_ERR("Invalid closing tag; expected '>', found '%c'\n", *ctx->pos);
- ctx->err++;
+ ctx->err = PLIST_ERR_PARSE;
return NULL;
}
ctx->pos++;
@@ -995,6 +1000,7 @@ static plist_err_t node_from_xml(parse_ctx ctx, plist_t *plist)
void *prev;
};
struct node_path_item* node_path = NULL;
+ int depth = 0;
while (ctx->pos < ctx->end && !ctx->err) {
parse_skip_ws(ctx);
@@ -1005,13 +1011,13 @@ static plist_err_t node_from_xml(parse_ctx ctx, plist_t *plist)
p = ctx->pos;
find_next(ctx, " \t\r\n", 4, 0);
PLIST_XML_ERR("Expected: opening tag, found: %.*s\n", (int)(ctx->pos - p), p);
- ctx->err++;
+ ctx->err = PLIST_ERR_PARSE;
goto err_out;
}
ctx->pos++;
if (ctx->pos >= ctx->end) {
PLIST_XML_ERR("EOF while parsing tag\n");
- ctx->err++;
+ ctx->err = PLIST_ERR_PARSE;
goto err_out;
}
@@ -1019,12 +1025,12 @@ static plist_err_t node_from_xml(parse_ctx ctx, plist_t *plist)
find_str(ctx, "?>", 2, 1);
if (ctx->pos > ctx->end-2) {
PLIST_XML_ERR("EOF while looking for <? tag closing marker\n");
- ctx->err++;
+ ctx->err = PLIST_ERR_PARSE;
goto err_out;
}
if (strncmp(ctx->pos, "?>", 2) != 0) {
PLIST_XML_ERR("Couldn't find <? tag closing marker\n");
- ctx->err++;
+ ctx->err = PLIST_ERR_PARSE;
goto err_out;
}
ctx->pos += 2;
@@ -1036,7 +1042,7 @@ static plist_err_t node_from_xml(parse_ctx ctx, plist_t *plist)
find_str(ctx,"-->", 3, 0);
if (ctx->pos > ctx->end-3 || strncmp(ctx->pos, "-->", 3) != 0) {
PLIST_XML_ERR("Couldn't find end of comment\n");
- ctx->err++;
+ ctx->err = PLIST_ERR_PARSE;
goto err_out;
}
ctx->pos+=3;
@@ -1047,7 +1053,7 @@ static plist_err_t node_from_xml(parse_ctx ctx, plist_t *plist)
find_next(ctx, " \t\r\n[>", 6, 1);
if (ctx->pos >= ctx->end) {
PLIST_XML_ERR("EOF while parsing !DOCTYPE\n");
- ctx->err++;
+ ctx->err = PLIST_ERR_PARSE;
goto err_out;
}
if (*ctx->pos == '[') {
@@ -1065,7 +1071,7 @@ static plist_err_t node_from_xml(parse_ctx ctx, plist_t *plist)
find_str(ctx, "]>", 2, 1);
if (ctx->pos > ctx->end-2 || strncmp(ctx->pos, "]>", 2) != 0) {
PLIST_XML_ERR("Couldn't find end of DOCTYPE\n");
- ctx->err++;
+ ctx->err = PLIST_ERR_PARSE;
goto err_out;
}
ctx->pos += 2;
@@ -1074,7 +1080,7 @@ static plist_err_t node_from_xml(parse_ctx ctx, plist_t *plist)
p = ctx->pos;
find_next(ctx, " \r\n\t>", 5, 1);
PLIST_XML_ERR("Invalid or incomplete special tag <%.*s> encountered\n", (int)(ctx->pos - p), p);
- ctx->err++;
+ ctx->err = PLIST_ERR_PARSE;
goto err_out;
}
continue;
@@ -1085,7 +1091,7 @@ static plist_err_t node_from_xml(parse_ctx ctx, plist_t *plist)
find_next(ctx," \r\n\t<>", 6, 0);
if (ctx->pos >= ctx->end) {
PLIST_XML_ERR("Unexpected EOF while parsing XML\n");
- ctx->err++;
+ ctx->err = PLIST_ERR_PARSE;
goto err_out;
}
size_t taglen = ctx->pos - p;
@@ -1097,12 +1103,12 @@ static plist_err_t node_from_xml(parse_ctx ctx, plist_t *plist)
}
if (ctx->pos >= ctx->end) {
PLIST_XML_ERR("Unexpected EOF while parsing XML\n");
- ctx->err++;
+ ctx->err = PLIST_ERR_PARSE;
goto err_out;
}
if (*ctx->pos != '>') {
PLIST_XML_ERR("Missing '>' for tag <%s\n", tag);
- ctx->err++;
+ ctx->err = PLIST_ERR_PARSE;
goto err_out;
}
if (*(ctx->pos-1) == '/') {
@@ -1123,14 +1129,14 @@ static plist_err_t node_from_xml(parse_ctx ctx, plist_t *plist)
}
if (is_empty) {
PLIST_XML_ERR("Empty plist tag\n");
- ctx->err++;
+ ctx->err = PLIST_ERR_PARSE;
goto err_out;
}
struct node_path_item *path_item = (struct node_path_item*)malloc(sizeof(struct node_path_item));
if (!path_item) {
PLIST_XML_ERR("out of memory when allocating node path item\n");
- ctx->err++;
+ ctx->err = PLIST_ERR_PARSE;
goto err_out;
}
path_item->type = "plist";
@@ -1141,17 +1147,17 @@ static plist_err_t node_from_xml(parse_ctx ctx, plist_t *plist)
} else if (!strcmp(tag, "/plist")) {
if (!has_content) {
PLIST_XML_ERR("encountered empty plist tag\n");
- ctx->err++;
+ ctx->err = PLIST_ERR_PARSE;
goto err_out;
}
if (!node_path) {
PLIST_XML_ERR("node path is empty while trying to match closing tag with opening tag\n");
- ctx->err++;
+ ctx->err = PLIST_ERR_PARSE;
goto err_out;
}
if (strcmp(node_path->type, tag+1) != 0) {
PLIST_XML_ERR("mismatching closing tag <%s> found for opening tag <%s>\n", tag, node_path->type);
- ctx->err++;
+ ctx->err = PLIST_ERR_PARSE;
goto err_out;
}
struct node_path_item *path_item = node_path;
@@ -1179,7 +1185,7 @@ 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++;
+ ctx->err = PLIST_ERR_PARSE;
goto err_out;
}
if (tp->begin) {
@@ -1188,7 +1194,7 @@ static plist_err_t node_from_xml(parse_ctx ctx, plist_t *plist)
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++;
+ ctx->err = PLIST_ERR_PARSE;
goto err_out;
}
char *str = str_content;
@@ -1230,7 +1236,7 @@ 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++;
+ ctx->err = PLIST_ERR_PARSE;
goto err_out;
}
if (tp->begin) {
@@ -1239,7 +1245,7 @@ static plist_err_t node_from_xml(parse_ctx ctx, plist_t *plist)
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++;
+ ctx->err = PLIST_ERR_PARSE;
goto err_out;
}
data->realval = atof(str_content);
@@ -1274,14 +1280,14 @@ 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++;
+ ctx->err = PLIST_ERR_PARSE;
goto err_out;
}
str = text_parts_get_content(tp, 1, &length, NULL);
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++;
+ ctx->err = PLIST_ERR_PARSE;
goto err_out;
}
if (!strcmp(tag, "key") && !keyname && parent && (plist_get_node_type(parent) == PLIST_DICT)) {
@@ -1315,7 +1321,7 @@ 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++;
+ ctx->err = PLIST_ERR_PARSE;
goto err_out;
}
if (tp->begin) {
@@ -1324,7 +1330,7 @@ static plist_err_t node_from_xml(parse_ctx ctx, plist_t *plist)
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++;
+ ctx->err = PLIST_ERR_PARSE;
goto err_out;
}
size_t size = tp->length;
@@ -1347,7 +1353,7 @@ 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++;
+ ctx->err = PLIST_ERR_PARSE;
goto err_out;
}
Time64_T timev = 0;
@@ -1358,7 +1364,7 @@ static plist_err_t node_from_xml(parse_ctx ctx, plist_t *plist)
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++;
+ ctx->err = PLIST_ERR_PARSE;
goto err_out;
}
@@ -1387,7 +1393,7 @@ static plist_err_t node_from_xml(parse_ctx ctx, plist_t *plist)
} else {
PLIST_XML_ERR("Unexpected tag <%s%s> encountered\n", tag, (is_empty) ? "/" : "");
ctx->pos = ctx->end;
- ctx->err++;
+ ctx->err = PLIST_ERR_PARSE;
goto err_out;
}
if (subnode && !closing_tag) {
@@ -1405,7 +1411,7 @@ static plist_err_t node_from_xml(parse_ctx ctx, plist_t *plist)
case PLIST_DICT:
if (!keyname) {
PLIST_XML_ERR("missing key name while adding dict item\n");
- ctx->err++;
+ ctx->err = PLIST_ERR_PARSE;
goto err_out;
}
plist_dict_set_item(parent, keyname, subnode);
@@ -1416,35 +1422,42 @@ static plist_err_t node_from_xml(parse_ctx ctx, plist_t *plist)
default:
/* should not happen */
PLIST_XML_ERR("parent is not a structured node\n");
- ctx->err++;
+ ctx->err = PLIST_ERR_PARSE;
goto err_out;
}
}
if (!is_empty && (data->type == PLIST_DICT || data->type == PLIST_ARRAY)) {
+ if (depth >= PLIST_MAX_NESTING_DEPTH) {
+ PLIST_XML_ERR("maximum nesting depth (%u) exceeded\n", (unsigned)PLIST_MAX_NESTING_DEPTH);
+ ctx->err = PLIST_ERR_MAX_NESTING;
+ goto err_out;
+ }
struct node_path_item *path_item = (struct node_path_item*)malloc(sizeof(struct node_path_item));
if (!path_item) {
PLIST_XML_ERR("out of memory when allocating node path item\n");
- ctx->err++;
+ ctx->err = PLIST_ERR_PARSE;
goto err_out;
}
path_item->type = (data->type == PLIST_DICT) ? XPLIST_DICT : XPLIST_ARRAY;
path_item->prev = node_path;
node_path = path_item;
+ depth++;
parent = subnode;
}
subnode = NULL;
} else if (closing_tag) {
if (!node_path) {
PLIST_XML_ERR("node path is empty while trying to match closing tag with opening tag\n");
- ctx->err++;
+ ctx->err = PLIST_ERR_PARSE;
goto err_out;
}
if (strcmp(node_path->type, tag+1) != 0) {
PLIST_XML_ERR("unexpected %s found (for opening %s)\n", tag, node_path->type);
- ctx->err++;
+ ctx->err = PLIST_ERR_PARSE;
goto err_out;
}
+ if (depth > 0) depth--;
struct node_path_item *path_item = node_path;
node_path = (struct node_path_item*)node_path->prev;
free(path_item);
@@ -1466,7 +1479,7 @@ static plist_err_t node_from_xml(parse_ctx ctx, plist_t *plist)
if (node_path) {
PLIST_XML_ERR("EOF encountered while </%s> was expected\n", node_path->type);
- ctx->err++;
+ ctx->err = PLIST_ERR_PARSE;
}
err_out:
@@ -1481,10 +1494,10 @@ err_out:
free(path_item);
}
- if (ctx->err) {
+ if (ctx->err != PLIST_ERR_SUCCESS) {
plist_free(*plist);
*plist = NULL;
- return PLIST_ERR_PARSE;
+ return ctx->err;
}
/* check if we have a UID "dict" so we can replace it with a proper UID node */
@@ -1511,7 +1524,7 @@ plist_err_t plist_from_xml(const char *plist_xml, uint32_t length, plist_t * pli
return PLIST_ERR_INVALID_ARG;
}
- struct _parse_ctx ctx = { plist_xml, plist_xml + length, 0 };
+ struct _parse_ctx ctx = { plist_xml, plist_xml + length, PLIST_ERR_SUCCESS };
return node_from_xml(&ctx, plist);
}