Skip to content

Commit

Permalink
reftable: avoid leaks on realloc error
Browse files Browse the repository at this point in the history
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() and reftable_buf_add() avoid leaking by restoring the
original pointer value on failure, but all other callers seem to be OK
with losing the old allocation.  Add a new variant of the macro,
REFTABLE_ALLOC_GROW_OR_NULL, which plugs the leak and zeros the
allocation counter.  Use it for those callers.

Signed-off-by: René Scharfe <[email protected]>
Signed-off-by: Junio C Hamano <[email protected]>
  • Loading branch information
rscharfe authored and gitster committed Dec 28, 2024
1 parent 76cf4f6 commit 8db127d
Show file tree
Hide file tree
Showing 7 changed files with 61 additions and 16 deletions.
10 changes: 10 additions & 0 deletions reftable/basics.h
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,16 @@ char *reftable_strdup(const char *str);
REFTABLE_REALLOC_ARRAY(x, alloc); \
} \
} while (0)

#define REFTABLE_ALLOC_GROW_OR_NULL(x, nr, alloc) do { \
void *reftable_alloc_grow_or_null_orig_ptr = (x); \
REFTABLE_ALLOC_GROW((x), (nr), (alloc)); \
if (!(x)) { \
reftable_free(reftable_alloc_grow_or_null_orig_ptr); \
alloc = 0; \
} \
} while (0)

#define REFTABLE_FREE_AND_NULL(p) do { reftable_free(p); (p) = NULL; } while (0)

#ifndef REFTABLE_ALLOW_BANNED_ALLOCATORS
Expand Down
10 changes: 6 additions & 4 deletions reftable/block.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ static int block_writer_register_restart(struct block_writer *w, int n,
if (2 + 3 * rlen + n > w->block_size - w->next)
return -1;
if (is_restart) {
REFTABLE_ALLOC_GROW(w->restarts, w->restart_len + 1, w->restart_cap);
REFTABLE_ALLOC_GROW_OR_NULL(w->restarts, w->restart_len + 1,
w->restart_cap);
if (!w->restarts)
return REFTABLE_OUT_OF_MEMORY_ERROR;
w->restarts[w->restart_len++] = w->next;
Expand Down Expand Up @@ -176,7 +177,8 @@ int block_writer_finish(struct block_writer *w)
* is guaranteed to return `Z_STREAM_END`.
*/
compressed_len = deflateBound(w->zstream, src_len);
REFTABLE_ALLOC_GROW(w->compressed, compressed_len, w->compressed_cap);
REFTABLE_ALLOC_GROW_OR_NULL(w->compressed, compressed_len,
w->compressed_cap);
if (!w->compressed) {
ret = REFTABLE_OUT_OF_MEMORY_ERROR;
return ret;
Expand Down Expand Up @@ -235,8 +237,8 @@ int block_reader_init(struct block_reader *br, struct reftable_block *block,
uLong src_len = block->len - block_header_skip;

/* Log blocks specify the *uncompressed* size in their header. */
REFTABLE_ALLOC_GROW(br->uncompressed_data, sz,
br->uncompressed_cap);
REFTABLE_ALLOC_GROW_OR_NULL(br->uncompressed_data, sz,
br->uncompressed_cap);
if (!br->uncompressed_data) {
err = REFTABLE_OUT_OF_MEMORY_ERROR;
goto done;
Expand Down
2 changes: 1 addition & 1 deletion reftable/pq.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ int merged_iter_pqueue_add(struct merged_iter_pqueue *pq, const struct pq_entry
{
size_t i = 0;

REFTABLE_ALLOC_GROW(pq->heap, pq->len + 1, pq->cap);
REFTABLE_ALLOC_GROW_OR_NULL(pq->heap, pq->len + 1, pq->cap);
if (!pq->heap)
return REFTABLE_OUT_OF_MEMORY_ERROR;
pq->heap[pq->len++] = *e;
Expand Down
12 changes: 6 additions & 6 deletions reftable/record.c
Original file line number Diff line number Diff line change
Expand Up @@ -246,8 +246,8 @@ static int reftable_ref_record_copy_from(void *rec, const void *src_rec,
if (src->refname) {
size_t refname_len = strlen(src->refname);

REFTABLE_ALLOC_GROW(ref->refname, refname_len + 1,
ref->refname_cap);
REFTABLE_ALLOC_GROW_OR_NULL(ref->refname, refname_len + 1,
ref->refname_cap);
if (!ref->refname) {
err = REFTABLE_OUT_OF_MEMORY_ERROR;
goto out;
Expand Down Expand Up @@ -385,7 +385,7 @@ static int reftable_ref_record_decode(void *rec, struct reftable_buf key,
SWAP(r->refname, refname);
SWAP(r->refname_cap, refname_cap);

REFTABLE_ALLOC_GROW(r->refname, key.len + 1, r->refname_cap);
REFTABLE_ALLOC_GROW_OR_NULL(r->refname, key.len + 1, r->refname_cap);
if (!r->refname) {
err = REFTABLE_OUT_OF_MEMORY_ERROR;
goto done;
Expand Down Expand Up @@ -839,7 +839,7 @@ static int reftable_log_record_decode(void *rec, struct reftable_buf key,
if (key.len <= 9 || key.buf[key.len - 9] != 0)
return REFTABLE_FORMAT_ERROR;

REFTABLE_ALLOC_GROW(r->refname, key.len - 8, r->refname_cap);
REFTABLE_ALLOC_GROW_OR_NULL(r->refname, key.len - 8, r->refname_cap);
if (!r->refname) {
err = REFTABLE_OUT_OF_MEMORY_ERROR;
goto done;
Expand Down Expand Up @@ -947,8 +947,8 @@ static int reftable_log_record_decode(void *rec, struct reftable_buf key,
}
string_view_consume(&in, n);

REFTABLE_ALLOC_GROW(r->value.update.message, scratch->len + 1,
r->value.update.message_cap);
REFTABLE_ALLOC_GROW_OR_NULL(r->value.update.message, scratch->len + 1,
r->value.update.message_cap);
if (!r->value.update.message) {
err = REFTABLE_OUT_OF_MEMORY_ERROR;
goto done;
Expand Down
8 changes: 5 additions & 3 deletions reftable/stack.c
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,9 @@ static int reftable_stack_reload_once(struct reftable_stack *st,
* thus need to keep them alive here, which we
* do by bumping their refcount.
*/
REFTABLE_ALLOC_GROW(reused, reused_len + 1, reused_alloc);
REFTABLE_ALLOC_GROW_OR_NULL(reused,
reused_len + 1,
reused_alloc);
if (!reused) {
err = REFTABLE_OUT_OF_MEMORY_ERROR;
goto done;
Expand Down Expand Up @@ -949,8 +951,8 @@ int reftable_addition_add(struct reftable_addition *add,
if (err < 0)
goto done;

REFTABLE_ALLOC_GROW(add->new_tables, add->new_tables_len + 1,
add->new_tables_cap);
REFTABLE_ALLOC_GROW_OR_NULL(add->new_tables, add->new_tables_len + 1,
add->new_tables_cap);
if (!add->new_tables) {
err = REFTABLE_OUT_OF_MEMORY_ERROR;
goto done;
Expand Down
5 changes: 3 additions & 2 deletions reftable/writer.c
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,8 @@ static int writer_index_hash(struct reftable_writer *w, struct reftable_buf *has
if (key->offset_len > 0 && key->offsets[key->offset_len - 1] == off)
return 0;

REFTABLE_ALLOC_GROW(key->offsets, key->offset_len + 1, key->offset_cap);
REFTABLE_ALLOC_GROW_OR_NULL(key->offsets, key->offset_len + 1,
key->offset_cap);
if (!key->offsets)
return REFTABLE_OUT_OF_MEMORY_ERROR;
key->offsets[key->offset_len++] = off;
Expand Down Expand Up @@ -820,7 +821,7 @@ static int writer_flush_nonempty_block(struct reftable_writer *w)
* Note that this also applies when flushing index blocks, in which
* case we will end up with a multi-level index.
*/
REFTABLE_ALLOC_GROW(w->index, w->index_len + 1, w->index_cap);
REFTABLE_ALLOC_GROW_OR_NULL(w->index, w->index_len + 1, w->index_cap);
if (!w->index)
return REFTABLE_OUT_OF_MEMORY_ERROR;

Expand Down
30 changes: 30 additions & 0 deletions t/unit-tests/t-reftable-basics.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ static int integer_needle_lesseq(size_t i, void *_args)
return args->needle <= args->haystack[i];
}

static void *realloc_stub(void *p UNUSED, size_t size UNUSED)
{
return NULL;
}

int cmd_main(int argc UNUSED, const char *argv[] UNUSED)
{
if_test ("binary search with binsearch works") {
Expand Down Expand Up @@ -141,5 +146,30 @@ int cmd_main(int argc UNUSED, const char *argv[] UNUSED)
check_int(in, ==, out);
}

if_test ("REFTABLE_ALLOC_GROW_OR_NULL works") {
int *arr = NULL;
size_t alloc = 0, old_alloc;

REFTABLE_ALLOC_GROW_OR_NULL(arr, 1, alloc);
check(arr != NULL);
check_uint(alloc, >=, 1);
arr[0] = 42;

old_alloc = alloc;
REFTABLE_ALLOC_GROW_OR_NULL(arr, old_alloc + 1, alloc);
check(arr != NULL);
check_uint(alloc, >, old_alloc);
arr[alloc - 1] = 42;

old_alloc = alloc;
reftable_set_alloc(malloc, realloc_stub, free);
REFTABLE_ALLOC_GROW_OR_NULL(arr, old_alloc + 1, alloc);
check(arr == NULL);
check_uint(alloc, ==, 0);
reftable_set_alloc(malloc, realloc, free);

reftable_free(arr);
}

return test_done();
}

0 comments on commit 8db127d

Please sign in to comment.