Skip to content

Commit 73e35b1

Browse files
committed
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
2 parents bc2c657 + 1e78120 commit 73e35b1

File tree

9 files changed

+116
-36
lines changed

9 files changed

+116
-36
lines changed

reftable/basics.c

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -124,11 +124,8 @@ int reftable_buf_add(struct reftable_buf *buf, const void *data, size_t len)
124124
size_t newlen = buf->len + len;
125125

126126
if (newlen + 1 > buf->alloc) {
127-
char *reallocated = buf->buf;
128-
REFTABLE_ALLOC_GROW(reallocated, newlen + 1, buf->alloc);
129-
if (!reallocated)
127+
if (REFTABLE_ALLOC_GROW(buf->buf, newlen + 1, buf->alloc))
130128
return REFTABLE_OUT_OF_MEMORY_ERROR;
131-
buf->buf = reallocated;
132129
}
133130

134131
memcpy(buf->buf + buf->len, data, len);
@@ -233,11 +230,9 @@ char **parse_names(char *buf, int size)
233230
next = end;
234231
}
235232
if (p < next) {
236-
char **names_grown = names;
237-
REFTABLE_ALLOC_GROW(names_grown, names_len + 1, names_cap);
238-
if (!names_grown)
233+
if (REFTABLE_ALLOC_GROW(names, names_len + 1,
234+
names_cap))
239235
goto err;
240-
names = names_grown;
241236

242237
names[names_len] = reftable_strdup(p);
243238
if (!names[names_len++])
@@ -246,7 +241,8 @@ char **parse_names(char *buf, int size)
246241
p = next + 1;
247242
}
248243

249-
REFTABLE_REALLOC_ARRAY(names, names_len + 1);
244+
if (REFTABLE_ALLOC_GROW(names, names_len + 1, names_cap))
245+
goto err;
250246
names[names_len] = NULL;
251247

252248
return names;

reftable/basics.h

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -120,15 +120,38 @@ char *reftable_strdup(const char *str);
120120
#define REFTABLE_ALLOC_ARRAY(x, alloc) (x) = reftable_malloc(st_mult(sizeof(*(x)), (alloc)))
121121
#define REFTABLE_CALLOC_ARRAY(x, alloc) (x) = reftable_calloc((alloc), sizeof(*(x)))
122122
#define REFTABLE_REALLOC_ARRAY(x, alloc) (x) = reftable_realloc((x), st_mult(sizeof(*(x)), (alloc)))
123-
#define REFTABLE_ALLOC_GROW(x, nr, alloc) \
124-
do { \
125-
if ((nr) > alloc) { \
126-
alloc = 2 * (alloc) + 1; \
127-
if (alloc < (nr)) \
128-
alloc = (nr); \
129-
REFTABLE_REALLOC_ARRAY(x, alloc); \
130-
} \
131-
} while (0)
123+
124+
static inline void *reftable_alloc_grow(void *p, size_t nelem, size_t elsize,
125+
size_t *allocp)
126+
{
127+
void *new_p;
128+
size_t alloc = *allocp * 2 + 1;
129+
if (alloc < nelem)
130+
alloc = nelem;
131+
new_p = reftable_realloc(p, st_mult(elsize, alloc));
132+
if (!new_p)
133+
return p;
134+
*allocp = alloc;
135+
return new_p;
136+
}
137+
138+
#define REFTABLE_ALLOC_GROW(x, nr, alloc) ( \
139+
(nr) > (alloc) && ( \
140+
(x) = reftable_alloc_grow((x), (nr), sizeof(*(x)), &(alloc)), \
141+
(nr) > (alloc) \
142+
) \
143+
)
144+
145+
#define REFTABLE_ALLOC_GROW_OR_NULL(x, nr, alloc) do { \
146+
size_t reftable_alloc_grow_or_null_alloc = alloc; \
147+
if (REFTABLE_ALLOC_GROW((x), (nr), reftable_alloc_grow_or_null_alloc)) { \
148+
REFTABLE_FREE_AND_NULL(x); \
149+
alloc = 0; \
150+
} else { \
151+
alloc = reftable_alloc_grow_or_null_alloc; \
152+
} \
153+
} while (0)
154+
132155
#define REFTABLE_FREE_AND_NULL(p) do { reftable_free(p); (p) = NULL; } while (0)
133156

134157
#ifndef REFTABLE_ALLOW_BANNED_ALLOCATORS

reftable/block.c

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@ static int block_writer_register_restart(struct block_writer *w, int n,
5353
if (2 + 3 * rlen + n > w->block_size - w->next)
5454
return -1;
5555
if (is_restart) {
56-
REFTABLE_ALLOC_GROW(w->restarts, w->restart_len + 1, w->restart_cap);
56+
REFTABLE_ALLOC_GROW_OR_NULL(w->restarts, w->restart_len + 1,
57+
w->restart_cap);
5758
if (!w->restarts)
5859
return REFTABLE_OUT_OF_MEMORY_ERROR;
5960
w->restarts[w->restart_len++] = w->next;
@@ -176,7 +177,8 @@ int block_writer_finish(struct block_writer *w)
176177
* is guaranteed to return `Z_STREAM_END`.
177178
*/
178179
compressed_len = deflateBound(w->zstream, src_len);
179-
REFTABLE_ALLOC_GROW(w->compressed, compressed_len, w->compressed_cap);
180+
REFTABLE_ALLOC_GROW_OR_NULL(w->compressed, compressed_len,
181+
w->compressed_cap);
180182
if (!w->compressed) {
181183
ret = REFTABLE_OUT_OF_MEMORY_ERROR;
182184
return ret;
@@ -235,8 +237,8 @@ int block_reader_init(struct block_reader *br, struct reftable_block *block,
235237
uLong src_len = block->len - block_header_skip;
236238

237239
/* Log blocks specify the *uncompressed* size in their header. */
238-
REFTABLE_ALLOC_GROW(br->uncompressed_data, sz,
239-
br->uncompressed_cap);
240+
REFTABLE_ALLOC_GROW_OR_NULL(br->uncompressed_data, sz,
241+
br->uncompressed_cap);
240242
if (!br->uncompressed_data) {
241243
err = REFTABLE_OUT_OF_MEMORY_ERROR;
242244
goto done;

reftable/pq.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ int merged_iter_pqueue_add(struct merged_iter_pqueue *pq, const struct pq_entry
4949
{
5050
size_t i = 0;
5151

52-
REFTABLE_ALLOC_GROW(pq->heap, pq->len + 1, pq->cap);
52+
REFTABLE_ALLOC_GROW_OR_NULL(pq->heap, pq->len + 1, pq->cap);
5353
if (!pq->heap)
5454
return REFTABLE_OUT_OF_MEMORY_ERROR;
5555
pq->heap[pq->len++] = *e;

reftable/record.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -246,8 +246,8 @@ static int reftable_ref_record_copy_from(void *rec, const void *src_rec,
246246
if (src->refname) {
247247
size_t refname_len = strlen(src->refname);
248248

249-
REFTABLE_ALLOC_GROW(ref->refname, refname_len + 1,
250-
ref->refname_cap);
249+
REFTABLE_ALLOC_GROW_OR_NULL(ref->refname, refname_len + 1,
250+
ref->refname_cap);
251251
if (!ref->refname) {
252252
err = REFTABLE_OUT_OF_MEMORY_ERROR;
253253
goto out;
@@ -385,7 +385,7 @@ static int reftable_ref_record_decode(void *rec, struct reftable_buf key,
385385
SWAP(r->refname, refname);
386386
SWAP(r->refname_cap, refname_cap);
387387

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

842-
REFTABLE_ALLOC_GROW(r->refname, key.len - 8, r->refname_cap);
842+
REFTABLE_ALLOC_GROW_OR_NULL(r->refname, key.len - 8, r->refname_cap);
843843
if (!r->refname) {
844844
err = REFTABLE_OUT_OF_MEMORY_ERROR;
845845
goto done;
@@ -947,8 +947,8 @@ static int reftable_log_record_decode(void *rec, struct reftable_buf key,
947947
}
948948
string_view_consume(&in, n);
949949

950-
REFTABLE_ALLOC_GROW(r->value.update.message, scratch->len + 1,
951-
r->value.update.message_cap);
950+
REFTABLE_ALLOC_GROW_OR_NULL(r->value.update.message, scratch->len + 1,
951+
r->value.update.message_cap);
952952
if (!r->value.update.message) {
953953
err = REFTABLE_OUT_OF_MEMORY_ERROR;
954954
goto done;

reftable/stack.c

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,9 @@ static int reftable_stack_reload_once(struct reftable_stack *st,
317317
* thus need to keep them alive here, which we
318318
* do by bumping their refcount.
319319
*/
320-
REFTABLE_ALLOC_GROW(reused, reused_len + 1, reused_alloc);
320+
REFTABLE_ALLOC_GROW_OR_NULL(reused,
321+
reused_len + 1,
322+
reused_alloc);
321323
if (!reused) {
322324
err = REFTABLE_OUT_OF_MEMORY_ERROR;
323325
goto done;
@@ -949,8 +951,8 @@ int reftable_addition_add(struct reftable_addition *add,
949951
if (err < 0)
950952
goto done;
951953

952-
REFTABLE_ALLOC_GROW(add->new_tables, add->new_tables_len + 1,
953-
add->new_tables_cap);
954+
REFTABLE_ALLOC_GROW_OR_NULL(add->new_tables, add->new_tables_len + 1,
955+
add->new_tables_cap);
954956
if (!add->new_tables) {
955957
err = REFTABLE_OUT_OF_MEMORY_ERROR;
956958
goto done;

reftable/writer.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,8 @@ static int writer_index_hash(struct reftable_writer *w, struct reftable_buf *has
254254
if (key->offset_len > 0 && key->offsets[key->offset_len - 1] == off)
255255
return 0;
256256

257-
REFTABLE_ALLOC_GROW(key->offsets, key->offset_len + 1, key->offset_cap);
257+
REFTABLE_ALLOC_GROW_OR_NULL(key->offsets, key->offset_len + 1,
258+
key->offset_cap);
258259
if (!key->offsets)
259260
return REFTABLE_OUT_OF_MEMORY_ERROR;
260261
key->offsets[key->offset_len++] = off;
@@ -820,7 +821,7 @@ static int writer_flush_nonempty_block(struct reftable_writer *w)
820821
* Note that this also applies when flushing index blocks, in which
821822
* case we will end up with a multi-level index.
822823
*/
823-
REFTABLE_ALLOC_GROW(w->index, w->index_len + 1, w->index_cap);
824+
REFTABLE_ALLOC_GROW_OR_NULL(w->index, w->index_len + 1, w->index_cap);
824825
if (!w->index)
825826
return REFTABLE_OUT_OF_MEMORY_ERROR;
826827

t/unit-tests/t-reftable-basics.c

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@ static int integer_needle_lesseq(size_t i, void *_args)
2020
return args->needle <= args->haystack[i];
2121
}
2222

23+
static void *realloc_stub(void *p UNUSED, size_t size UNUSED)
24+
{
25+
return NULL;
26+
}
27+
2328
int cmd_main(int argc UNUSED, const char *argv[] UNUSED)
2429
{
2530
if_test ("binary search with binsearch works") {
@@ -141,5 +146,56 @@ int cmd_main(int argc UNUSED, const char *argv[] UNUSED)
141146
check_int(in, ==, out);
142147
}
143148

149+
if_test ("REFTABLE_ALLOC_GROW works") {
150+
int *arr = NULL, *old_arr;
151+
size_t alloc = 0, old_alloc;
152+
153+
check(!REFTABLE_ALLOC_GROW(arr, 1, alloc));
154+
check(arr != NULL);
155+
check_uint(alloc, >=, 1);
156+
arr[0] = 42;
157+
158+
old_alloc = alloc;
159+
old_arr = arr;
160+
reftable_set_alloc(malloc, realloc_stub, free);
161+
check(REFTABLE_ALLOC_GROW(arr, old_alloc + 1, alloc));
162+
check(arr == old_arr);
163+
check_uint(alloc, ==, old_alloc);
164+
165+
old_alloc = alloc;
166+
reftable_set_alloc(malloc, realloc, free);
167+
check(!REFTABLE_ALLOC_GROW(arr, old_alloc + 1, alloc));
168+
check(arr != NULL);
169+
check_uint(alloc, >, old_alloc);
170+
arr[alloc - 1] = 42;
171+
172+
reftable_free(arr);
173+
}
174+
175+
if_test ("REFTABLE_ALLOC_GROW_OR_NULL works") {
176+
int *arr = NULL;
177+
size_t alloc = 0, old_alloc;
178+
179+
REFTABLE_ALLOC_GROW_OR_NULL(arr, 1, alloc);
180+
check(arr != NULL);
181+
check_uint(alloc, >=, 1);
182+
arr[0] = 42;
183+
184+
old_alloc = alloc;
185+
REFTABLE_ALLOC_GROW_OR_NULL(arr, old_alloc + 1, alloc);
186+
check(arr != NULL);
187+
check_uint(alloc, >, old_alloc);
188+
arr[alloc - 1] = 42;
189+
190+
old_alloc = alloc;
191+
reftable_set_alloc(malloc, realloc_stub, free);
192+
REFTABLE_ALLOC_GROW_OR_NULL(arr, old_alloc + 1, alloc);
193+
check(arr == NULL);
194+
check_uint(alloc, ==, 0);
195+
reftable_set_alloc(malloc, realloc, free);
196+
197+
reftable_free(arr);
198+
}
199+
144200
return test_done();
145201
}

t/unit-tests/t-reftable-merged.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ static void t_merged_refs(void)
178178
if (err > 0)
179179
break;
180180

181-
REFTABLE_ALLOC_GROW(out, len + 1, cap);
181+
check(!REFTABLE_ALLOC_GROW(out, len + 1, cap));
182182
out[len++] = ref;
183183
}
184184
reftable_iterator_destroy(&it);
@@ -459,7 +459,7 @@ static void t_merged_logs(void)
459459
if (err > 0)
460460
break;
461461

462-
REFTABLE_ALLOC_GROW(out, len + 1, cap);
462+
check(!REFTABLE_ALLOC_GROW(out, len + 1, cap));
463463
out[len++] = log;
464464
}
465465
reftable_iterator_destroy(&it);

0 commit comments

Comments
 (0)