From 40666fa02457c84c3b8561b2bb36928bd23e9f04 Mon Sep 17 00:00:00 2001 From: Nikias Bassen Date: Thu, 5 Nov 2026 10:40:10 +0100 Subject: bytearray: grow buffer geometrically to avoid quadratic appends byte_array_grow() increased the capacity by a single page at a time, so building large outputs (e.g. the plutil writer on a plist with heavily shared references) caused a realloc and memcpy per page, i.e. quadratic behavior that ran into fuzzer timeouts. Double the capacity when growing. If the larger allocation fails, fall back to the minimum size required before putting the array into its failed state, so low-memory systems are not penalized by the extra headroom. Also guard against size_t overflow in the capacity arithmetic. --- src/bytearray.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/bytearray.c b/src/bytearray.c index 05ea0bb..89c5ec7 100644 --- a/src/bytearray.c +++ b/src/bytearray.c @@ -58,7 +58,26 @@ void byte_array_grow(bytearray_t *ba, size_t amount) return; } size_t increase = (amount > PAGE_SIZE) ? (amount+(PAGE_SIZE-1)) & (~(PAGE_SIZE-1)) : PAGE_SIZE; - void *newdata = realloc(ba->data, ba->capacity + increase); + if (increase < amount || ba->capacity + increase < ba->capacity) { + /* size overflow */ + free(ba->data); + ba->data = NULL; + ba->len = 0; + ba->capacity = 0; + return; + } + /* grow geometrically (doubling) to keep repeated appends linear; if that + * fails, fall back to the minimum required size before giving up */ + void *newdata = NULL; + if (ba->capacity <= ((size_t)-1) / 2 && ba->capacity > increase) { + newdata = realloc(ba->data, ba->capacity * 2); + if (newdata) { + increase = ba->capacity; + } + } + if (!newdata) { + newdata = realloc(ba->data, ba->capacity + increase); + } if (!newdata) { /* out of memory: put the array into a failed state (data == NULL), * further appends are ignored and callers must check ba->data */ -- cgit v1.1-32-gdbae