From 2b3cc554270d34ca1e1fae3eb2cbbc740a65bbf3 Mon Sep 17 00:00:00 2001 From: Nikias Bassen Date: Thu, 5 Nov 2026 10:07:22 +0100 Subject: bplist: limit total parsed nodes to prevent memory exhaustion Binary plist objects may be referenced multiple times, and each reference is parsed into a separate node. A tiny crafted input where arrays reference the same objects repeatedly (without forming a cycle, so the recursion check does not trigger) expands into millions of nodes and exhausts memory. Track the number of nodes parsed and fail with PLIST_ERR_PARSE once it exceeds num_objects * 16 + 65536. Legitimate files create roughly as many nodes as they have objects, so they stay well below this limit. Credit to OSSFuzz --- src/bplist.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'src') diff --git a/src/bplist.c b/src/bplist.c index 8168572..e5ace3a 100644 --- a/src/bplist.c +++ b/src/bplist.c @@ -237,6 +237,8 @@ struct bplist_data { uint8_t offset_size; const char* offset_table; uint32_t level; + uint64_t nodes_parsed; + uint64_t max_nodes; ptrarray_t* used_indexes; plist_err_t err; }; @@ -866,6 +868,14 @@ static plist_t parse_bin_node_at_index(struct bplist_data *bplist, uint32_t node return NULL; } + /* Objects can be referenced multiple times, so limit the total number of + nodes created to avoid excessive memory use from reference expansion. */ + if (++bplist->nodes_parsed > bplist->max_nodes) { + PLIST_BIN_ERR("too many nodes created while parsing (reference expansion?)\n"); + 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); @@ -1003,6 +1013,8 @@ plist_err_t plist_from_bin(const char *plist_bin, uint32_t length, plist_t * pli bplist.offset_size = offset_size; bplist.offset_table = offset_table; bplist.level = 0; + bplist.nodes_parsed = 0; + bplist.max_nodes = num_objects * 16 + 65536; bplist.used_indexes = ptr_array_new(16); bplist.err = PLIST_ERR_SUCCESS; -- cgit v1.1-32-gdbae