Merge branch 'rs/reftable-realloc-errors'
The custom allocator code in the reftable library did not handle failing realloc() very well, which has been addressed. * rs/reftable-realloc-errors: t-reftable-merged: handle realloc errors reftable: handle realloc error in parse_names() reftable: fix allocation count on realloc error reftable: avoid leaks on realloc error
This commit is contained in:
@ -124,11 +124,8 @@ int reftable_buf_add(struct reftable_buf *buf, const void *data, size_t len)
|
|||||||
size_t newlen = buf->len + len;
|
size_t newlen = buf->len + len;
|
||||||
|
|
||||||
if (newlen + 1 > buf->alloc) {
|
if (newlen + 1 > buf->alloc) {
|
||||||
char *reallocated = buf->buf;
|
if (REFTABLE_ALLOC_GROW(buf->buf, newlen + 1, buf->alloc))
|
||||||
REFTABLE_ALLOC_GROW(reallocated, newlen + 1, buf->alloc);
|
|
||||||
if (!reallocated)
|
|
||||||
return REFTABLE_OUT_OF_MEMORY_ERROR;
|
return REFTABLE_OUT_OF_MEMORY_ERROR;
|
||||||
buf->buf = reallocated;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(buf->buf + buf->len, data, len);
|
memcpy(buf->buf + buf->len, data, len);
|
||||||
@ -233,11 +230,9 @@ char **parse_names(char *buf, int size)
|
|||||||
next = end;
|
next = end;
|
||||||
}
|
}
|
||||||
if (p < next) {
|
if (p < next) {
|
||||||
char **names_grown = names;
|
if (REFTABLE_ALLOC_GROW(names, names_len + 1,
|
||||||
REFTABLE_ALLOC_GROW(names_grown, names_len + 1, names_cap);
|
names_cap))
|
||||||
if (!names_grown)
|
|
||||||
goto err;
|
goto err;
|
||||||
names = names_grown;
|
|
||||||
|
|
||||||
names[names_len] = reftable_strdup(p);
|
names[names_len] = reftable_strdup(p);
|
||||||
if (!names[names_len++])
|
if (!names[names_len++])
|
||||||
@ -246,7 +241,8 @@ char **parse_names(char *buf, int size)
|
|||||||
p = next + 1;
|
p = next + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
REFTABLE_REALLOC_ARRAY(names, names_len + 1);
|
if (REFTABLE_ALLOC_GROW(names, names_len + 1, names_cap))
|
||||||
|
goto err;
|
||||||
names[names_len] = NULL;
|
names[names_len] = NULL;
|
||||||
|
|
||||||
return names;
|
return names;
|
||||||
|
@ -120,15 +120,38 @@ char *reftable_strdup(const char *str);
|
|||||||
#define REFTABLE_ALLOC_ARRAY(x, alloc) (x) = reftable_malloc(st_mult(sizeof(*(x)), (alloc)))
|
#define REFTABLE_ALLOC_ARRAY(x, alloc) (x) = reftable_malloc(st_mult(sizeof(*(x)), (alloc)))
|
||||||
#define REFTABLE_CALLOC_ARRAY(x, alloc) (x) = reftable_calloc((alloc), sizeof(*(x)))
|
#define REFTABLE_CALLOC_ARRAY(x, alloc) (x) = reftable_calloc((alloc), sizeof(*(x)))
|
||||||
#define REFTABLE_REALLOC_ARRAY(x, alloc) (x) = reftable_realloc((x), st_mult(sizeof(*(x)), (alloc)))
|
#define REFTABLE_REALLOC_ARRAY(x, alloc) (x) = reftable_realloc((x), st_mult(sizeof(*(x)), (alloc)))
|
||||||
#define REFTABLE_ALLOC_GROW(x, nr, alloc) \
|
|
||||||
do { \
|
static inline void *reftable_alloc_grow(void *p, size_t nelem, size_t elsize,
|
||||||
if ((nr) > alloc) { \
|
size_t *allocp)
|
||||||
alloc = 2 * (alloc) + 1; \
|
{
|
||||||
if (alloc < (nr)) \
|
void *new_p;
|
||||||
alloc = (nr); \
|
size_t alloc = *allocp * 2 + 1;
|
||||||
REFTABLE_REALLOC_ARRAY(x, alloc); \
|
if (alloc < nelem)
|
||||||
} \
|
alloc = nelem;
|
||||||
} while (0)
|
new_p = reftable_realloc(p, st_mult(elsize, alloc));
|
||||||
|
if (!new_p)
|
||||||
|
return p;
|
||||||
|
*allocp = alloc;
|
||||||
|
return new_p;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define REFTABLE_ALLOC_GROW(x, nr, alloc) ( \
|
||||||
|
(nr) > (alloc) && ( \
|
||||||
|
(x) = reftable_alloc_grow((x), (nr), sizeof(*(x)), &(alloc)), \
|
||||||
|
(nr) > (alloc) \
|
||||||
|
) \
|
||||||
|
)
|
||||||
|
|
||||||
|
#define REFTABLE_ALLOC_GROW_OR_NULL(x, nr, alloc) do { \
|
||||||
|
size_t reftable_alloc_grow_or_null_alloc = alloc; \
|
||||||
|
if (REFTABLE_ALLOC_GROW((x), (nr), reftable_alloc_grow_or_null_alloc)) { \
|
||||||
|
REFTABLE_FREE_AND_NULL(x); \
|
||||||
|
alloc = 0; \
|
||||||
|
} else { \
|
||||||
|
alloc = reftable_alloc_grow_or_null_alloc; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
#define REFTABLE_FREE_AND_NULL(p) do { reftable_free(p); (p) = NULL; } while (0)
|
#define REFTABLE_FREE_AND_NULL(p) do { reftable_free(p); (p) = NULL; } while (0)
|
||||||
|
|
||||||
#ifndef REFTABLE_ALLOW_BANNED_ALLOCATORS
|
#ifndef REFTABLE_ALLOW_BANNED_ALLOCATORS
|
||||||
|
@ -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)
|
if (2 + 3 * rlen + n > w->block_size - w->next)
|
||||||
return -1;
|
return -1;
|
||||||
if (is_restart) {
|
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)
|
if (!w->restarts)
|
||||||
return REFTABLE_OUT_OF_MEMORY_ERROR;
|
return REFTABLE_OUT_OF_MEMORY_ERROR;
|
||||||
w->restarts[w->restart_len++] = w->next;
|
w->restarts[w->restart_len++] = w->next;
|
||||||
@ -176,7 +177,8 @@ int block_writer_finish(struct block_writer *w)
|
|||||||
* is guaranteed to return `Z_STREAM_END`.
|
* is guaranteed to return `Z_STREAM_END`.
|
||||||
*/
|
*/
|
||||||
compressed_len = deflateBound(w->zstream, src_len);
|
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) {
|
if (!w->compressed) {
|
||||||
ret = REFTABLE_OUT_OF_MEMORY_ERROR;
|
ret = REFTABLE_OUT_OF_MEMORY_ERROR;
|
||||||
return ret;
|
return ret;
|
||||||
@ -235,8 +237,8 @@ int block_reader_init(struct block_reader *br, struct reftable_block *block,
|
|||||||
uLong src_len = block->len - block_header_skip;
|
uLong src_len = block->len - block_header_skip;
|
||||||
|
|
||||||
/* Log blocks specify the *uncompressed* size in their header. */
|
/* Log blocks specify the *uncompressed* size in their header. */
|
||||||
REFTABLE_ALLOC_GROW(br->uncompressed_data, sz,
|
REFTABLE_ALLOC_GROW_OR_NULL(br->uncompressed_data, sz,
|
||||||
br->uncompressed_cap);
|
br->uncompressed_cap);
|
||||||
if (!br->uncompressed_data) {
|
if (!br->uncompressed_data) {
|
||||||
err = REFTABLE_OUT_OF_MEMORY_ERROR;
|
err = REFTABLE_OUT_OF_MEMORY_ERROR;
|
||||||
goto done;
|
goto done;
|
||||||
|
@ -49,7 +49,7 @@ int merged_iter_pqueue_add(struct merged_iter_pqueue *pq, const struct pq_entry
|
|||||||
{
|
{
|
||||||
size_t i = 0;
|
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)
|
if (!pq->heap)
|
||||||
return REFTABLE_OUT_OF_MEMORY_ERROR;
|
return REFTABLE_OUT_OF_MEMORY_ERROR;
|
||||||
pq->heap[pq->len++] = *e;
|
pq->heap[pq->len++] = *e;
|
||||||
|
@ -246,8 +246,8 @@ static int reftable_ref_record_copy_from(void *rec, const void *src_rec,
|
|||||||
if (src->refname) {
|
if (src->refname) {
|
||||||
size_t refname_len = strlen(src->refname);
|
size_t refname_len = strlen(src->refname);
|
||||||
|
|
||||||
REFTABLE_ALLOC_GROW(ref->refname, refname_len + 1,
|
REFTABLE_ALLOC_GROW_OR_NULL(ref->refname, refname_len + 1,
|
||||||
ref->refname_cap);
|
ref->refname_cap);
|
||||||
if (!ref->refname) {
|
if (!ref->refname) {
|
||||||
err = REFTABLE_OUT_OF_MEMORY_ERROR;
|
err = REFTABLE_OUT_OF_MEMORY_ERROR;
|
||||||
goto out;
|
goto out;
|
||||||
@ -385,7 +385,7 @@ static int reftable_ref_record_decode(void *rec, struct reftable_buf key,
|
|||||||
SWAP(r->refname, refname);
|
SWAP(r->refname, refname);
|
||||||
SWAP(r->refname_cap, refname_cap);
|
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) {
|
if (!r->refname) {
|
||||||
err = REFTABLE_OUT_OF_MEMORY_ERROR;
|
err = REFTABLE_OUT_OF_MEMORY_ERROR;
|
||||||
goto done;
|
goto done;
|
||||||
@ -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)
|
if (key.len <= 9 || key.buf[key.len - 9] != 0)
|
||||||
return REFTABLE_FORMAT_ERROR;
|
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) {
|
if (!r->refname) {
|
||||||
err = REFTABLE_OUT_OF_MEMORY_ERROR;
|
err = REFTABLE_OUT_OF_MEMORY_ERROR;
|
||||||
goto done;
|
goto done;
|
||||||
@ -947,8 +947,8 @@ static int reftable_log_record_decode(void *rec, struct reftable_buf key,
|
|||||||
}
|
}
|
||||||
string_view_consume(&in, n);
|
string_view_consume(&in, n);
|
||||||
|
|
||||||
REFTABLE_ALLOC_GROW(r->value.update.message, scratch->len + 1,
|
REFTABLE_ALLOC_GROW_OR_NULL(r->value.update.message, scratch->len + 1,
|
||||||
r->value.update.message_cap);
|
r->value.update.message_cap);
|
||||||
if (!r->value.update.message) {
|
if (!r->value.update.message) {
|
||||||
err = REFTABLE_OUT_OF_MEMORY_ERROR;
|
err = REFTABLE_OUT_OF_MEMORY_ERROR;
|
||||||
goto done;
|
goto done;
|
||||||
|
@ -317,7 +317,9 @@ static int reftable_stack_reload_once(struct reftable_stack *st,
|
|||||||
* thus need to keep them alive here, which we
|
* thus need to keep them alive here, which we
|
||||||
* do by bumping their refcount.
|
* 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) {
|
if (!reused) {
|
||||||
err = REFTABLE_OUT_OF_MEMORY_ERROR;
|
err = REFTABLE_OUT_OF_MEMORY_ERROR;
|
||||||
goto done;
|
goto done;
|
||||||
@ -949,8 +951,8 @@ int reftable_addition_add(struct reftable_addition *add,
|
|||||||
if (err < 0)
|
if (err < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
REFTABLE_ALLOC_GROW(add->new_tables, add->new_tables_len + 1,
|
REFTABLE_ALLOC_GROW_OR_NULL(add->new_tables, add->new_tables_len + 1,
|
||||||
add->new_tables_cap);
|
add->new_tables_cap);
|
||||||
if (!add->new_tables) {
|
if (!add->new_tables) {
|
||||||
err = REFTABLE_OUT_OF_MEMORY_ERROR;
|
err = REFTABLE_OUT_OF_MEMORY_ERROR;
|
||||||
goto done;
|
goto done;
|
||||||
|
@ -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)
|
if (key->offset_len > 0 && key->offsets[key->offset_len - 1] == off)
|
||||||
return 0;
|
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)
|
if (!key->offsets)
|
||||||
return REFTABLE_OUT_OF_MEMORY_ERROR;
|
return REFTABLE_OUT_OF_MEMORY_ERROR;
|
||||||
key->offsets[key->offset_len++] = off;
|
key->offsets[key->offset_len++] = off;
|
||||||
@ -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
|
* Note that this also applies when flushing index blocks, in which
|
||||||
* case we will end up with a multi-level index.
|
* 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)
|
if (!w->index)
|
||||||
return REFTABLE_OUT_OF_MEMORY_ERROR;
|
return REFTABLE_OUT_OF_MEMORY_ERROR;
|
||||||
|
|
||||||
|
@ -20,6 +20,11 @@ static int integer_needle_lesseq(size_t i, void *_args)
|
|||||||
return args->needle <= args->haystack[i];
|
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)
|
int cmd_main(int argc UNUSED, const char *argv[] UNUSED)
|
||||||
{
|
{
|
||||||
if_test ("binary search with binsearch works") {
|
if_test ("binary search with binsearch works") {
|
||||||
@ -141,5 +146,56 @@ int cmd_main(int argc UNUSED, const char *argv[] UNUSED)
|
|||||||
check_int(in, ==, out);
|
check_int(in, ==, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if_test ("REFTABLE_ALLOC_GROW works") {
|
||||||
|
int *arr = NULL, *old_arr;
|
||||||
|
size_t alloc = 0, old_alloc;
|
||||||
|
|
||||||
|
check(!REFTABLE_ALLOC_GROW(arr, 1, alloc));
|
||||||
|
check(arr != NULL);
|
||||||
|
check_uint(alloc, >=, 1);
|
||||||
|
arr[0] = 42;
|
||||||
|
|
||||||
|
old_alloc = alloc;
|
||||||
|
old_arr = arr;
|
||||||
|
reftable_set_alloc(malloc, realloc_stub, free);
|
||||||
|
check(REFTABLE_ALLOC_GROW(arr, old_alloc + 1, alloc));
|
||||||
|
check(arr == old_arr);
|
||||||
|
check_uint(alloc, ==, old_alloc);
|
||||||
|
|
||||||
|
old_alloc = alloc;
|
||||||
|
reftable_set_alloc(malloc, realloc, free);
|
||||||
|
check(!REFTABLE_ALLOC_GROW(arr, old_alloc + 1, alloc));
|
||||||
|
check(arr != NULL);
|
||||||
|
check_uint(alloc, >, old_alloc);
|
||||||
|
arr[alloc - 1] = 42;
|
||||||
|
|
||||||
|
reftable_free(arr);
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
return test_done();
|
||||||
}
|
}
|
||||||
|
@ -178,7 +178,7 @@ static void t_merged_refs(void)
|
|||||||
if (err > 0)
|
if (err > 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
REFTABLE_ALLOC_GROW(out, len + 1, cap);
|
check(!REFTABLE_ALLOC_GROW(out, len + 1, cap));
|
||||||
out[len++] = ref;
|
out[len++] = ref;
|
||||||
}
|
}
|
||||||
reftable_iterator_destroy(&it);
|
reftable_iterator_destroy(&it);
|
||||||
@ -459,7 +459,7 @@ static void t_merged_logs(void)
|
|||||||
if (err > 0)
|
if (err > 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
REFTABLE_ALLOC_GROW(out, len + 1, cap);
|
check(!REFTABLE_ALLOC_GROW(out, len + 1, cap));
|
||||||
out[len++] = log;
|
out[len++] = log;
|
||||||
}
|
}
|
||||||
reftable_iterator_destroy(&it);
|
reftable_iterator_destroy(&it);
|
||||||
|
Reference in New Issue
Block a user