When realloc(3) fails, it returns NULL and keeps the original allocation
intact. REFTABLE_ALLOC_GROW overwrites both the original pointer and
the allocation count variable in that case, simultaneously leaking the
original allocation and misrepresenting the number of storable items.
parse_names() avoids the leak by keeping the original pointer if
reallocation fails, but still increase the allocation count in such a
case as if it succeeded. That's OK, because the error handling code
just frees everything and doesn't look at names_cap anymore.
reftable_buf_add() does the same, but here it is a problem as it leaves
the reftable_buf in a broken state, with ->alloc being roughly twice as
big as the actually allocated memory, allowing out-of-bounds writes in
subsequent calls.
Reimplement REFTABLE_ALLOC_GROW to avoid leaks, keep allocation counts
in sync and still signal failures to callers while avoiding code
duplication in callers. Make it an expression that evaluates to 0 if no
reallocation is needed or it succeeded and 1 on failure while keeping
the original pointer and allocation counter values.
Adjust REFTABLE_ALLOC_GROW_OR_NULL to the new calling convention for
REFTABLE_ALLOC_GROW, but keep its support for non-size_t alloc variables
for now.
Signed-off-by: René Scharfe <l.s.r@web.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
René Scharfe committedDec 28, 2024 at 10:48 UTC2cca185e85171c462166839cfd6ee57c09573160
3 files changed+55-21
reftable/basics.c
+3-8
index 70b1091d14..cd6b39dbe9 100644--- a/reftable/basics.c+++ b/reftable/basics.c@@ -124,11 +124,8 @@ int reftable_buf_add(struct reftable_buf *buf, const void *data, size_t len) size_t newlen = buf->len + len; if (newlen + 1 > buf->alloc) {- char *reallocated = buf->buf;- REFTABLE_ALLOC_GROW(reallocated, newlen + 1, buf->alloc);- if (!reallocated)+ if (REFTABLE_ALLOC_GROW(buf->buf, newlen + 1, buf->alloc)) return REFTABLE_OUT_OF_MEMORY_ERROR;- buf->buf = reallocated; } memcpy(buf->buf + buf->len, data, len);@@ -233,11 +230,9 @@ char **parse_names(char *buf, int size) next = end; } if (p < next) {- char **names_grown = names;- REFTABLE_ALLOC_GROW(names_grown, names_len + 1, names_cap);- if (!names_grown)+ if (REFTABLE_ALLOC_GROW(names, names_len + 1,+ names_cap)) goto err;- names = names_grown; names[names_len] = reftable_strdup(p); if (!names[names_len++])