reftable: handle allocation errors
PATCH v4 24/25 reftable: introduce `REFTABLE_FREE_AND_NULL()`
In reply to Patrick SteinhardtWe have several calls to FREE_AND_NULL() in the reftable library, which of course uses free(3P). As the reftable allocators are pluggable we should rather call the reftable specific function, which is reftable_free().
Introduce a new macro REFTABLE_FREE_AND_NULL() and adapt the callsites accordingly.
--- reftable/basics.h | 1 + reftable/block.c | 6 +++--- reftable/iter.c | 2 +- reftable/pq.c | 2 +- reftable/reader.c | 2 +- reftable/record.c | 10 +++++----- reftable/stack.c | 8 ++++---- reftable/writer.c | 4 ++-- t/unit-tests/t-reftable-stack.c | 2 +- 9 files changed, 19 insertions(+), 18 deletions(-)
reftable/basics.h +1 -0
@@ -71,6 +71,7 @@ char *reftable_strdup(const char *str);REFTABLE_REALLOC_ARRAY (x, alloc); \ } \ } while (0) +#define REFTABLE_FREE_AND_NULL (p) do { reftable_free(p); (p) = NULL; } while (0)/* Find the longest shared prefix size of `a` and `b` */ struct strbuf ;
reftable/block.c +3 -3
@@ -551,9 +551,9 @@ int block_iter_seek_key(struct block_iter *it, const struct block_reader *br,void block_writer_release (struct block_writer *bw) {deflateEnd (bw->zstream); -FREE_AND_NULL (bw->zstream); -FREE_AND_NULL (bw->restarts); -FREE_AND_NULL (bw->compressed); +REFTABLE_FREE_AND_NULL (bw->zstream); +REFTABLE_FREE_AND_NULL (bw->restarts); +REFTABLE_FREE_AND_NULL (bw->compressed);strbuf_release (&bw->last_key);/* the block is not owned. */ }
reftable/iter.c +1 -1
@@ -237,7 +237,7 @@ void reftable_iterator_destroy(struct reftable_iterator *it)return ; it->ops->close (it->iter_arg); it->ops =NULL ; -FREE_AND_NULL (it->iter_arg); +REFTABLE_FREE_AND_NULL (it->iter_arg); } int reftable_iterator_seek_ref(struct reftable_iterator *it,
reftable/pq.c +1 -1
@@ -68,6 +68,6 @@ int merged_iter_pqueue_add(struct merged_iter_pqueue *pq, const struct pq_entryvoid merged_iter_pqueue_release (struct merged_iter_pqueue *pq) { -FREE_AND_NULL (pq->heap); +REFTABLE_FREE_AND_NULL (pq->heap);memset (pq,0 ,sizeof (*pq)); }
reftable/reader.c +1 -1
@@ -678,7 +678,7 @@ void reftable_reader_decref(struct reftable_reader *r)if (--r->refcount)return ;block_source_close (&r->source); -FREE_AND_NULL (r->name); +REFTABLE_FREE_AND_NULL (r->name);reftable_free (r); }
reftable/record.c +5 -5
@@ -476,8 +476,8 @@ static void reftable_obj_record_key(const void *r, struct strbuf *dest)static void reftable_obj_record_release (void *rec) {struct reftable_obj_record *obj = rec; -FREE_AND_NULL (obj->hash_prefix); -FREE_AND_NULL (obj->offsets); +REFTABLE_FREE_AND_NULL (obj->hash_prefix); +REFTABLE_FREE_AND_NULL (obj->offsets);memset (obj,0 ,sizeof (struct reftable_obj_record )); }
@@ -834,10 +834,10 @@ static int reftable_log_record_decode(void *rec, struct strbuf key,if (val_type != r->value_type) {switch (r->value_type) {case REFTABLE_LOG_UPDATE : -FREE_AND_NULL (r->value.update.message); +REFTABLE_FREE_AND_NULL (r->value.update.message); r->value.update.message_cap =0 ; -FREE_AND_NULL (r->value.update.email); -FREE_AND_NULL (r->value.update.name); +REFTABLE_FREE_AND_NULL (r->value.update.email); +REFTABLE_FREE_AND_NULL (r->value.update.name);break ;case REFTABLE_LOG_DELETION :break ;
reftable/stack.c +4 -4
@@ -203,7 +203,7 @@ void reftable_stack_destroy(struct reftable_stack *st) err =read_lines (st->list_file, &names);if (err <0 ) { -FREE_AND_NULL (names); +REFTABLE_FREE_AND_NULL (names); }if (st->readers) {
@@ -224,7 +224,7 @@ void reftable_stack_destroy(struct reftable_stack *st) }strbuf_release (&filename); st->readers_len =0 ; -FREE_AND_NULL (st->readers); +REFTABLE_FREE_AND_NULL (st->readers); }if (st->list_fd >=0 ) {
@@ -232,8 +232,8 @@ void reftable_stack_destroy(struct reftable_stack *st) st->list_fd =-1 ; } -FREE_AND_NULL (st->list_file); -FREE_AND_NULL (st->reftable_dir); +REFTABLE_FREE_AND_NULL (st->list_file); +REFTABLE_FREE_AND_NULL (st->reftable_dir);reftable_free (st);free_names (names); }
reftable/writer.c +2 -2
@@ -593,7 +593,7 @@ static void object_record_free(void *void_arg UNUSED, void *key) {struct obj_index_tree_node *entry = key; -FREE_AND_NULL (entry->offsets); +REFTABLE_FREE_AND_NULL (entry->offsets);strbuf_release (&entry->hash);reftable_free (entry); }
@@ -709,7 +709,7 @@ static void writer_clear_index(struct reftable_writer *w) {for (size_t i =0 ; w->index && i < w->index_len; i++)strbuf_release (&w->index[i].last_key); -FREE_AND_NULL (w->index); +REFTABLE_FREE_AND_NULL (w->index); w->index_len =0 ; w->index_cap =0 ; }
t/unit-tests/t-reftable-stack.c +1 -1
@@ -1149,7 +1149,7 @@ static void unclean_stack_close(struct reftable_stack *st)for (size_t i =0 ; i < st->readers_len; i++)reftable_reader_decref (st->readers[i]); st->readers_len =0 ; -FREE_AND_NULL (st->readers); +REFTABLE_FREE_AND_NULL (st->readers); }static void t_reftable_stack_compaction_concurrent_clean (void )
Signature · 1 line
2.47.0.rc0.dirty
PATCH v4 25/25 reftable/basics: ban standard allocator functions
In reply to Patrick SteinhardtThe reftable library uses pluggable allocators, which means that we shouldn't ever use the standard allocator functions. But it is an easy mistake to make to accidentally use e.g. free(3P) instead of the reftable-specific reftable_free() function, and we do not have any mechanism to detect this misuse right now.
Introduce a couple of macros that ban the standard allocators, similar to how we do it in "banned.h".
--- reftable/basics.c | 1 + reftable/basics.h | 9 +++++++++ 2 files changed, 10 insertions(+)
reftable/basics.c +1 -0
@@ -6,6 +6,7 @@ license that can be found in the LICENSE file or athttps://developers.google.com/open-source/licenses/bsd */ +#define REFTABLE_ALLOW_BANNED_ALLOCATORS #include "basics.h" #include "reftable-basics.h"
reftable/basics.h +9 -0
@@ -73,6 +73,15 @@ char *reftable_strdup(const char *str); } while (0)#define REFTABLE_FREE_AND_NULL (p) do { reftable_free(p); (p) = NULL; } while (0) +#ifndef REFTABLE_ALLOW_BANNED_ALLOCATORS +# define REFTABLE_BANNED (func) use_reftable_##func##_instead +# define malloc (sz) REFTABLE_BANNED(malloc) +# define realloc (ptr, sz) REFTABLE_BANNED(realloc) +# define free (ptr) REFTABLE_BANNED(free) +# define calloc (nelem, elsize) REFTABLE_BANNED(calloc) +# define strdup (str) REFTABLE_BANNED(strdup) +#endif +/* Find the longest shared prefix size of `a` and `b` */ struct strbuf ;int common_prefix_size (struct strbuf *a,struct strbuf *b);
Signature · 1 line
2.47.0.rc0.dirty
Re: [PATCH v4 25/25] reftable/basics: ban standard allocator functions
reftable/basics.h char *reftable_strdup(const char *str); · line 77
Quoted 2 lines · Patrick Steinhardt
reftable/basics.h +2
-0+#ifndef REFTABLE_ALLOW_BANNED_ALLOCATORS +# define REFTABLE_BANNED (func) use_reftable_##func##_instead
We'd need to mimic banned.h a bit better, by adding
#undef malloc
#undef realloc
#undef free
#undef calloc
#undef strdup
before (potentially re-)defining them.
Quoted 6 lines
reftable/basics.h +6
-0+# define malloc (sz) REFTABLE_BANNED(malloc) +# define realloc (ptr, sz) REFTABLE_BANNED(realloc) +# define free (ptr) REFTABLE_BANNED(free) +# define calloc (nelem, elsize) REFTABLE_BANNED(calloc) +# define strdup (str) REFTABLE_BANNED(strdup) +#endif
Re: [PATCH v4 25/25] reftable/basics: ban standard allocator functions
Quoted 13 lines · Junio C Hamano
reftable/basics.h char *reftable_strdup(const char *str); · line 77
Quoted 2 lines · Patrick Steinhardt
reftable/basics.h +2
-0+#ifndef REFTABLE_ALLOW_BANNED_ALLOCATORS +# define REFTABLE_BANNED (func) use_reftable_##func##_insteadWe'd need to mimic
banned.ha bit better, by adding#undef malloc
#undef realloc
#undef free
#undef calloc
#undef strdup
Quoted 8 lines
before (potentially re-)defining them.
reftable/basics.h char *reftable_strdup(const char *str); · line 83
Quoted 6 lines
reftable/basics.h +6
-0+# define malloc (sz) REFTABLE_BANNED(malloc) +# define realloc (ptr, sz) REFTABLE_BANNED(realloc) +# define free (ptr) REFTABLE_BANNED(free) +# define calloc (nelem, elsize) REFTABLE_BANNED(calloc) +# define strdup (str) REFTABLE_BANNED(strdup) +#endif
Oh, indeed, that's something I missed. Will do for a hopefully last reroll.
Patrick
Quoted 7 lines · Patrick Steinhardt
this is the fourth version of this patch series that converts the reftable library to start handling allocation errors. This is done such that the reftable library can truly behave like a library and let its callers handle such conditions.
Changes compared to v3:
Quoted 8 lines
- Fix some additional sites where we use strdup(3P)/free(3P) by accident.
- Convert preexisting callsites of free(3P) to use
reftable_free().- Introduce a
REFTABLE_FREE_AND_NULL()macro and use it.- Ban standard allocators in reftable code "banned.h"-style.
With this patch, there is only one hit from
$ git grep '[^_]free(' reftable/and no hits from
$ git grep '[^_]FREE_AND_NULL(' reftable/which is just as expected. Nicely done.
Shouldn't we add FREE_AND_NULL() to the banned list as well in the last step?
Thanks.
Am 01.10.24 um 19:52 schrieb Junio C Hamano:
Quoted 2 lines
Shouldn't we add
FREE_AND_NULL()to the banned list as well in the last step?
And perhaps the wrapper.h functions like xmalloc()? At least as long as git-compat-util.h is included by reftable/system.h. Can be done later, of course, no need to reroll just for that.
René
Quoted 7 lines · René Scharfe
Am 01.10.24 um 19:52 schrieb Junio C Hamano:
Quoted 2 lines
Shouldn't we add
FREE_AND_NULL()to the banned list as well in the last step?And perhaps the
wrapper.hfunctions likexmalloc()? At least as long asgit-compat-util.his included byreftable/system.h. Can be done later, of course, no need to reroll just for that.
Yeah, and I agree FREE_AND_NULL() falls into the same bucket as xmalloc() and friends, so no need to reroll just for that, either.
Thanks.
Quoted 6 lines · Junio C Hamano
Quoted 4 lines · René Scharfe
Am 01.10.24 um 19:52 schrieb Junio C Hamano:
Quoted 2 lines
Shouldn't we add
FREE_AND_NULL()to the banned list as well in the last step?
Quoted 6 lines
Quoted 3 lines
And perhaps the
wrapper.hfunctions likexmalloc()? At least as long asgit-compat-util.his included byreftable/system.h. Can be done later, of course, no need to reroll just for that.Yeah, and I agree
FREE_AND_NULL()falls into the same bucket asxmalloc()and friends, so no need to reroll just for that, either.
FREE_AND_NULL() would already be detected because it expands to code that contains free(). Same for REALLOC_ARRAY() and the likes.
I'm overall a bit more hesitant to also start banning all the Git specific wrappers, mostly because there are so many of them. My goal is to rather make them inaccessible in the "reftable/" code in the first place so that we don't have to play whack-a-mole. My plan here is to split out the POSIX-providing bits from "git-compat-util.h" into a new header "compat/posix.h" that we can include in "reftable/system.h".
But it'll take a while to get there :)
Thanks for your inputs!
Patrick
Quoted 2 lines · Patrick Steinhardt
FREE_AND_NULL()would already be detected because it expands to code that containsfree(). Same forREALLOC_ARRAY()and the likes.
Ah, that is very true.
v5 cover reftable: handle allocation errors
In reply to Patrick SteinhardtHi,
this is the fifth version of this patch series that converts the reftable library to start handling allocation errors. This is done such that the reftable library can truly behave like a library and let its callers handle such conditions.
Changes compared to v4:
- #undef standard allocators before banning them.
- Explain why we don't ban FREE_AND_NULL() and related as well as xmalloc() and related.
Thanks!
Patrick
Patrick Steinhardt (25):
reftable/error: introduce out-of-memory error code reftable/basics: merge "publicbasics" into "basics"
reftable: introduce reftable_strdup()
reftable/basics: handle allocation failures in reftable_calloc() reftable/basics: handle allocation failures in parse_names() reftable/record: handle allocation failures on copy reftable/record: handle allocation failures when decoding records reftable/writer: handle allocation failures in writer_index_hash() reftable/writer: handle allocation failures in reftable_new_writer() reftable/merged: handle allocation failures in
`merged_table_init_iter()`
reftable/reader: handle allocation failures for unindexed reader reftable/reader: handle allocation failures in reader_init_iter() reftable/stack: handle allocation failures on reload reftable/stack: handle allocation failures in reftable_new_stack() reftable/stack: handle allocation failures in stack_compact_range() reftable/stack: handle allocation failures in auto compaction reftable/iter: handle allocation failures when creating indexed table
iter
reftable/blocksource: handle allocation failures reftable/block: handle allocation failures
reftable/pq: handle allocation failures when adding entries reftable/tree: handle allocation failures
reftable: handle trivial allocation failures
reftable: fix calls to free(3P)
reftable: introduce REFTABLE_FREE_AND_NULL() reftable/basics: ban standard allocator functions
Makefile | 1 - refs/reftable-backend.c | 39 ++++-- reftable/basics.c | 93 +++++++++++++- reftable/basics.h | 28 ++++- reftable/block.c | 29 ++++- reftable/block.h | 4 +- reftable/blocksource.c | 25 +++- reftable/error.c | 2 + reftable/iter.c | 22 +++- reftable/iter.h | 2 +- reftable/merged.c | 84 ++++++++----- reftable/merged.h | 6 +- reftable/pq.c | 9 +- reftable/pq.h | 2 +- reftable/publicbasics.c | 66 ---------- reftable/reader.c | 70 ++++++++--- reftable/reader.h | 6 +- reftable/record.c | 174 +++++++++++++++++++------- reftable/record.h | 6 +- reftable/reftable-basics.h | 18 +++ reftable/reftable-error.h | 3 + reftable/reftable-malloc.h | 18 --- reftable/reftable-merged.h | 8 +- reftable/reftable-reader.h | 8 +- reftable/reftable-stack.h | 8 +- reftable/reftable-writer.h | 12 +- reftable/stack.c | 187 +++++++++++++++++++++------- reftable/tree.c | 42 +++++-- reftable/tree.h | 21 +++- reftable/writer.c | 154 +++++++++++++++-------- t/helper/test-reftable.c | 10 +- t/unit-tests/lib-reftable.c | 8 +- t/unit-tests/t-reftable-basics.c | 11 +- t/unit-tests/t-reftable-block.c | 24 ++-- t/unit-tests/t-reftable-merged.c | 16 ++- t/unit-tests/t-reftable-readwrite.c | 65 ++++++---- t/unit-tests/t-reftable-stack.c | 6 +- t/unit-tests/t-reftable-tree.c | 10 +- 38 files changed, 890 insertions(+), 407 deletions(-) delete mode 100644 reftable/publicbasics.c create mode 100644 reftable/reftable-basics.h delete mode 100644 reftable/reftable-malloc.h
Range-diff against v4:
1: 94eaef3ae5 = 1: 94eaef3ae5 reftable/error: introduce out-of-memory error code 2: fe55051cb7 = 2: fe55051cb7 reftable/basics: merge "publicbasics" into "basics" 3: b1a10d41d3 = 3: b1a10d41d3 reftable: introduce `reftable_strdup()` 4: fd9141e985 = 4: fd9141e985 reftable/basics: handle allocation failures in `reftable_calloc()` 5: bdfddbebce = 5: bdfddbebce reftable/basics: handle allocation failures in `parse_names()` 6: 756a32c285 = 6: 756a32c285 reftable/record: handle allocation failures on copy 7: ca64971e7b = 7: ca64971e7b reftable/record: handle allocation failures when decoding records 8: fc2f113cba = 8: fc2f113cba reftable/writer: handle allocation failures in `writer_index_hash()` 9: 0ed99e0bdf = 9: 0ed99e0bdf reftable/writer: handle allocation failures in `reftable_new_writer()` 10: 8dfbfd9286 = 10: 8dfbfd9286 reftable/merged: handle allocation failures in `merged_table_init_iter()` 11: 7b592a6c6b = 11: 7b592a6c6b reftable/reader: handle allocation failures for unindexed reader 12: ceed838265 = 12: ceed838265 reftable/reader: handle allocation failures in `reader_init_iter()` 13: 1e997a5766 = 13: 1e997a5766 reftable/stack: handle allocation failures on reload 14: 40d4d81378 = 14: 40d4d81378 reftable/stack: handle allocation failures in `reftable_new_stack()` 15: 6aaae4baa6 = 15: 6aaae4baa6 reftable/stack: handle allocation failures in `stack_compact_range()` 16: 103a59ef0a = 16: 103a59ef0a reftable/stack: handle allocation failures in auto compaction 17: 6799d299fe = 17: 6799d299fe reftable/iter: handle allocation failures when creating indexed table iter 18: c7e54d71d7 = 18: c7e54d71d7 reftable/blocksource: handle allocation failures 19: 92d39b9021 = 19: 92d39b9021 reftable/block: handle allocation failures 20: 3416004e0d = 20: 3416004e0d reftable/pq: handle allocation failures when adding entries 21: a363930488 = 21: a363930488 reftable/tree: handle allocation failures 22: 28661500ff = 22: 28661500ff reftable: handle trivial allocation failures 23: 228cc81263 = 23: 228cc81263 reftable: fix calls to free(3P) 24: 1c66f6ef8d = 24: 1c66f6ef8d reftable: introduce `REFTABLE_FREE_AND_NULL()` 25: 764961e6f0 ! 25: bc80f1e726 reftable/basics: ban standard allocator functions
@@ Commit message
Introduce a couple of macros that ban the standard allocators, similar to how we do it in "banned.h".
+ Note that we do not ban the following two classes of functions:
+
+ - Macros like FREE_AND_NULL() or REALLOC_ARRAY(). As those expand
+ to code that contains already-banned functions we'd get a compiler
+ error even without banning those macros explicitly.
+
+ - Git-specific allocators like xmalloc() and friends. The primary
+ reason is that there are simply too many of them, so we're rather
+ aiming for best effort here. Furthermore, the eventual goal is to
+ make them unavailable in the reftable library place by not pulling
+ them in via "git-compat-utils.h" anymore.
+
## reftable/basics.c ##
@@ reftable/basics.h: char *reftable_strdup(const char *str);
+#ifndef REFTABLE_ALLOW_BANNED_ALLOCATORS
+# define REFTABLE_BANNED(func) use_reftable_##func##_instead
++# undef malloc
+# define malloc(sz) REFTABLE_BANNED(malloc)
++# undef realloc
+# define realloc(ptr, sz) REFTABLE_BANNED(realloc)
++# undef free
+# define free(ptr) REFTABLE_BANNED(free)
++# undef calloc
+# define calloc(nelem, elsize) REFTABLE_BANNED(calloc)
++# undef strdup
+# define strdup(str) REFTABLE_BANNED(strdup) +#endif
+
Signature · 1 line
2.47.0.rc0.dirty
PATCH v5 1/25 reftable/error: introduce out-of-memory error code
The reftable library does not use the same memory allocation functions as the rest of the Git codebase. Instead, as the reftable library is supposed to be usable as a standalone library without Git, it provides a set of pluggable memory allocators.
Compared to xmalloc() and friends these allocators are _not_ expected to die when an allocation fails. This design choice is concious, as a library should leave it to its caller to handle any kind of error. While it is very likely that the caller cannot really do much in the case of an out-of-memory situation anyway, we are not the ones to make that decision.
Curiously though, we never handle allocation errors even though memory allocation functions are allowed to fail. And as we do not plug in Git's memory allocator via reftable_set_alloc() either the consequence is that we'd instead segfault as soon as we run out of memory.
While the easy fix would be to wire up xmalloc() and friends, it would only fix the usage of the reftable library in Git itself. Other users like libgit2, which is about to revive its efforts to land a backend for reftables, wouldn't be able to benefit from this solution.
Instead, we are about to do it the hard way: adapt all allocation sites to perform error checking. Introduce a new error code for out-of-memory errors that we will wire up in subsequent steps.
This commit also serves as the motivator for all the remaining steps in this series such that we do not have to repeat the same arguments in every single subsequent commit.
--- reftable/error.c | 2 ++ reftable/reftable-error.h | 3 +++ 2 files changed, 5 insertions(+)
reftable/error.c +2 -0
@@ -35,6 +35,8 @@ const char *reftable_error_str(int err)return "entry too large" ;case REFTABLE_OUTDATED_ERROR :return "data concurrently modified" ; +case REFTABLE_OUT_OF_MEMORY_ERROR : +return "out of memory" ;case -1 :return "general error" ;default :
reftable/reftable-error.h +3 -0
@@ -57,6 +57,9 @@ enum reftable_error {/* Trying to write out-of-date data. */ REFTABLE_OUTDATED_ERROR =-12 , + +/* An allocation has failed due to an out-of-memory situation. */ + REFTABLE_OUT_OF_MEMORY_ERROR = -13, };/* convert the numeric error code to a string. The string should not be
Signature · 1 line
2.47.0.rc0.dirty
PATCH v5 2/25 reftable/basics: merge "publicbasics" into "basics"
In reply to Patrick SteinhardtThe split between "basics" and "publicbasics" is somewhat arbitrary and not in line with how we typically structure code in the reftable library. While we do indeed split up headers into a public and internal part, we don't do that for the compilation unit itself. Furthermore, the declarations for "publicbasics.c" are in "reftable-malloc.h", which isn't in line with our naming schema, either.
Fix these inconsistencies by:
- Merging "publicbasics.c" into "basics.c".
- Renaming "reftable-malloc.h" to "reftable-basics.h" as the public header.
--- Makefile | 1 - reftable/basics.c | 55 +++++++++++++++++++++++++++++++ reftable/basics.h | 3 ++ reftable/publicbasics.c | 66 -------------------------------------- reftable/reftable-basics.h | 18 +++++++++++ reftable/reftable-malloc.h | 18 ----------- 6 files changed, 76 insertions(+), 85 deletions(-) delete mode 100644 reftable/publicbasics.c create mode 100644 reftable/reftable-basics.h delete mode 100644 reftable/reftable-malloc.h
Makefile +0 -1
@@ -2683,7 +2683,6 @@ REFTABLE_OBJS += reftable/error.o REFTABLE_OBJS += reftable/block.o REFTABLE_OBJS += reftable/blocksource.o REFTABLE_OBJS += reftable/iter.o -REFTABLE_OBJS += reftable/publicbasics.o REFTABLE_OBJS += reftable/merged.o REFTABLE_OBJS += reftable/pq.o REFTABLE_OBJS += reftable/reader.o
reftable/basics.c +55 -0
64 lines not on this page · raw message
reftable/basics.h +3 -0
@@ -14,6 +14,7 @@ license that can be found in the LICENSE file or at*/ #include "system.h" +#include "reftable-basics.h" /* Bigendian en/decoding of integers */
@@ -71,4 +72,6 @@ void *reftable_calloc(size_t nelem, size_t elsize);struct strbuf ;int common_prefix_size (struct strbuf *a,struct strbuf *b); +int hash_size (uint32_t id); +#endif
reftable/publicbasics.c deleted +0 -66
66 lines not on this page · raw message
reftable/reftable-basics.h new file +18 -0
@@ -0,0 +1,18 @@ +/* +* Copyright 2020 Google LLC +* +* Use of this source code is governed by a BSD-style +* license that can be found in the LICENSE file or at +* https://developers.google.com/open-source/licenses/bsd +*/ + +#ifndef REFTABLE_BASICS_H +#define REFTABLE_BASICS_H + +#include <stddef.h> + +/* Overrides the functions to use for memory management. */ +void reftable_set_alloc (void *(*malloc)(size_t ), +void *(*realloc)(void *,size_t ),void (*free)(void *)); + +#endif
reftable/reftable-malloc.h deleted +0 -18
@@ -1,18 +0,0 @@ -/* -Copyright 2020 Google LLC - -Use of this source code is governed by a BSD-style -license that can be found in the LICENSE file or at -https://developers.google.com/open-source/licenses/bsd -*/ - -#ifndef REFTABLE_H -#define REFTABLE_H - -#include <stddef.h> - -/* Overrides the functions to use for memory management. */ -void reftable_set_alloc (void *(*malloc)(size_t ), -void *(*realloc)(void *,size_t ),void (*free)(void *)); - -#endif
Signature · 1 line
2.47.0.rc0.dirty
PATCH v5 3/25 reftable: introduce `reftable_strdup()`
In reply to Patrick SteinhardtThe reftable library provides the ability to swap out allocators. There is a gap here though, because we continue to use xstrdup() even in the case where all the other allocators have been swapped out.
Introduce reftable_strdup() that uses reftable_malloc() to do the allocation.
--- reftable/basics.c | 10 ++++++++++ reftable/basics.h | 1 + 2 files changed, 11 insertions(+)
reftable/basics.c +10 -0
@@ -43,6 +43,16 @@ void *reftable_calloc(size_t nelem, size_t elsize)return p; } +char *reftable_strdup (const char *str) +{ +size_t len =strlen (str); +char *result =reftable_malloc (len +1 ); +if (!result) +return NULL ; +memcpy (result, str, len +1 ); +return result; +} +void reftable_set_alloc (void *(*malloc)(size_t ),void *(*realloc)(void *,size_t ),void (*free)(void *)) {
reftable/basics.h +1 -0
@@ -54,6 +54,7 @@ void *reftable_malloc(size_t sz);void *reftable_realloc (void *p,size_t sz);void reftable_free (void *p);void *reftable_calloc (size_t nelem,size_t elsize); +char *reftable_strdup (const char *str);#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)))
Signature · 1 line
2.47.0.rc0.dirty
PATCH v5 4/25 reftable/basics: handle allocation failures in `reftable_calloc()`
In reply to Patrick SteinhardtHandle allocation failures in reftable_calloc().
While at it, remove our use of st_mult() that would cause us to die on an overflow. From the caller's point of view there is not much of a difference between arguments that are too large to be multiplied and a request that is too big to handle by the allocator: in both cases the allocation cannot be fulfilled. And in neither of these cases do we want the reftable library to die.
While we could use unsigned_mult_overflows() to handle the overflow gracefully, we instead open-code it to further our goal of converting the reftable codebase to become a standalone library that can be reused by external projects.
--- reftable/basics.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-)
reftable/basics.c +10 -3
@@ -37,9 +37,16 @@ void reftable_free(void *p)void *reftable_calloc (size_t nelem,size_t elsize) { -size_t sz =st_mult (nelem, elsize); -void *p =reftable_malloc (sz); -memset (p,0 , sz); +void *p; + +if (nelem && elsize >SIZE_MAX / nelem) +return NULL ; + + p =reftable_malloc (nelem * elsize); +if (!p) +return NULL ; + +memset (p,0 , nelem * elsize);return p; }
Signature · 1 line
2.47.0.rc0.dirty
PATCH v5 5/25 reftable/basics: handle allocation failures in `parse_names()`
In reply to Patrick SteinhardtHandle allocation failures in parse_names() by returning NULL in case any allocation fails. While at it, refactor the function to return the array directly instead of assigning it to an out-pointer.
--- reftable/basics.c | 20 ++++++++++++++++---- reftable/basics.h | 9 ++++++--- reftable/stack.c | 6 +++++- t/unit-tests/t-reftable-basics.c | 11 ++++++----- 4 files changed, 33 insertions(+), 13 deletions(-)
reftable/basics.c +16 -4
@@ -135,14 +135,14 @@ size_t names_length(const char **names)return p - names; } -void parse_names (char *buf,int size,char ***namesp) +char **parse_names (char *buf,int size) {char **names =NULL ;size_t names_cap =0 ;size_t names_len =0 ; -char *p = buf;char *end = buf + size; +while (p < end) {char *next =strchr (p,'\n' );if (next && next < end) {
@@ -152,14 +152,26 @@ void parse_names(char *buf, int size, char ***namesp) }if (p < next) {REFTABLE_ALLOC_GROW (names, names_len +1 , names_cap); - names[names_len++] =xstrdup (p); +if (!names) + goto err; + + names[names_len] =reftable_strdup (p); +if (!names[names_len++]) + goto err; } p = next +1 ; }REFTABLE_REALLOC_ARRAY (names, names_len +1 ); names[names_len] =NULL ; - *namesp = names; + +return names; + +err: +for (size_t i =0 ; i < names_len; i++) +reftable_free (names[i]); +reftable_free (names); +return NULL ; }int names_equal (const char **a,const char **b)
reftable/basics.h +6 -3
@@ -38,9 +38,12 @@ size_t binsearch(size_t sz, int (*f)(size_t k, void *args), void *args);*/ void free_names (char **a); -/* parse a newline separated list of names. `size` is the length of the buffer, -* without terminating '\0'. Empty names are discarded. */ -void parse_names (char *buf,int size,char ***namesp); +/* +* Parse a newline separated list of names. `size` is the length of the buffer, +* without terminating '\0'. Empty names are discarded. Returns a `NULL` +* pointer when allocations fail. +*/ +char **parse_names (char *buf,int size);/* compares two NULL-terminated arrays of strings. */ int names_equal (const char **a,const char **b);
reftable/stack.c +5 -1
@@ -108,7 +108,11 @@ static int fd_read_lines(int fd, char ***namesp) } buf[size] =0 ; -parse_names (buf, size, namesp); + *namesp =parse_names (buf, size); +if (!*namesp) { + err =REFTABLE_OUT_OF_MEMORY_ERROR ; + goto done; + } done:reftable_free (buf);
t/unit-tests/t-reftable-basics.c +6 -5
@@ -72,13 +72,14 @@ int cmd_main(int argc UNUSED, const char *argv[] UNUSED) if_test ("parse_names works for basic input") {char in1[] ="line\n" ;char in2[] ="a\nb\nc" ; -char **out =NULL ; -parse_names (in1,strlen (in1), &out); +char **out =parse_names (in1,strlen (in1)); +check (out !=NULL );check_str (out[0 ],"line" );check (!out[1 ]);free_names (out); -parse_names (in2,strlen (in2), &out); + out =parse_names (in2,strlen (in2)); +check (out !=NULL );check_str (out[0 ],"a" );check_str (out[1 ],"b" );check_str (out[2 ],"c" );
@@ -88,8 +89,8 @@ int cmd_main(int argc UNUSED, const char *argv[] UNUSED) if_test ("parse_names drops empty string") {char in[] ="a\n\nb\n" ; -char **out =NULL ; -parse_names (in,strlen (in), &out); +char **out =parse_names (in,strlen (in)); +check (out !=NULL );check_str (out[0 ],"a" );/* simply '\n' should be dropped as empty string */ check_str (out[1 ],"b" );
Signature · 1 line
2.47.0.rc0.dirty
Re: [PATCH v5 05/25] reftable/basics: handle allocation failures in `parse_names()`
reftable/basics.c void parse_names(char *buf, int size, char ***namesp) · line 154
Quoted 12 lines · Patrick Steinhardt
Handle allocation failures in
parse_names()by returningNULLin case any allocation fails. While at it, refactor the function to return the array directly instead of assigning it to an out-pointer.Signed-off-by Patrick Steinhardt <[email protected]>---
reftable/basics.c +2
-1@@ -152,14 +152,26 @@ void parse_names(char *buf, int size, char ***namesp)REFTABLE_ALLOC_GROW (names, names_len +1 , names_cap); - names[names_len++] =xstrdup (p); +if (!names) + goto err;
Am I reading this correctly? Presumably, names_len can be non-zero here, right? And we now check for names==NULL to detect an allocation failure...
Quoted 6 lines
reftable/basics.c +3
-0+ names[names_len] =reftable_strdup (p); +if (!names[names_len++]) + goto err; } p = next +1 ; }
reftable/basics.c void parse_names(char *buf, int size, char ***namesp) · line 172
Quoted 10 lines
reftable/basics.c +6
-1REFTABLE_REALLOC_ARRAY (names, names_len +1 ); names[names_len] =NULL ; - *namesp = names; + +return names; + +err: +for (size_t i =0 ; i < names_len; i++) +reftable_free (names[i]);
... and then we potentially index into names[] because names_len is non-zero, thus crash because names is NULL.
Quoted 3 lines
reftable/basics.c +2
-0+reftable_free (names); +return NULL ; }
Re: [PATCH v5 05/25] reftable/basics: handle allocation failures in `parse_names()`
Quoted 25 lines · Eric Sunshine
reftable/basics.c void parse_names(char *buf, int size, char ***namesp) · line 154
Quoted 12 lines · Patrick Steinhardt
Handle allocation failures in
parse_names()by returningNULLin case any allocation fails. While at it, refactor the function to return the array directly instead of assigning it to an out-pointer.Signed-off-by Patrick Steinhardt <[email protected]>---
reftable/basics.c +2
-1@@ -152,14 +152,26 @@ void parse_names(char *buf, int size, char ***namesp)REFTABLE_ALLOC_GROW (names, names_len +1 , names_cap); - names[names_len++] =xstrdup (p); +if (!names) + goto err;Am I reading this correctly? Presumably,
names_lencan be non-zero here, right? And we now check for names==NULL to detect an allocation failure...Quoted 7 lines
reftable/basics.c +3
-0+ names[names_len] =reftable_strdup (p); +if (!names[names_len++]) + goto err; } p = next +1 ; }
Quoted 16 lines
reftable/basics.c void parse_names(char *buf, int size, char ***namesp) · line 169
Quoted 9 lines
reftable/basics.c +6
-1REFTABLE_REALLOC_ARRAY (names, names_len +1 ); names[names_len] =NULL ; - *namesp = names; + +return names; + +err: +for (size_t i =0 ; i < names_len; i++) +reftable_free (names[i]);... and then we potentially index into names[] because
names_lenis non-zero, thus crash becausenamesis NULL.Quoted 3 lines
reftable/basics.c +2
-0+reftable_free (names); +return NULL ; }
Good catch! I think we should queue something like the below on top of what we already have in next now.
Patrick
-- >8 --
Subject: [PATCH] reftable/basics: fix segfault when growing names array fails
When growing the names array fails we would end up with a NULL pointer. This causes two problems:
- We would run into a segfault because we try to free names that we have assigned to the array already.
- We lose track of the old array and cannot free its contents.
Fix this issue by using a temporary variable. Like this we do not clobber the old array that we tried to reallocate, which will remain valid when a call to realloc(3P) fails.
--- reftable/basics.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-)
reftable/basics.c +5 -3
@@ -152,9 +152,11 @@ char **parse_names(char *buf, int size) next = end; }if (p < next) { -REFTABLE_ALLOC_GROW (names, names_len +1 , names_cap); -if (!names) - goto err; +char **names_grown = names; +REFTABLE_ALLOC_GROW (names_grown, names_len +1 , names_cap); +if (!names_grown) + goto err; + names = names_grown; names[names_len] =reftable_strdup (p);if (!names[names_len++])
Re: [PATCH v5 05/25] reftable/basics: handle allocation failures in `parse_names()`
Quoted 33 lines · Patrick Steinhardt
Quoted 5 lines · Eric Sunshine
Am I reading this correctly? Presumably,
names_lencan be non-zero here, right? And we now check for names==NULL to detect an allocation failure...
... and then we potentially index into names[] becausenames_lenis non-zero, thus crash becausenamesis NULL.Good catch! I think we should queue something like the below on top of what we already have in
nextnow.-- >8 --
Subject: [PATCH] reftable/basics: fix segfault when growing
namesarray failsWhen growing the
namesarray fails we would end up with aNULLpointer. This causes two problems:- We would run into a segfault because we try to free names that we have assigned to the array already.
- We lose track of the old array and cannot free its contents.
Fix this issue by using a temporary variable. Like this we do not clobber the old array that we tried to reallocate, which will remain valid when a call to realloc(3P) fails.
Signed-off-by Patrick Steinhardt <[email protected]>--- reftable/basics.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-)
reftable/basics.c char **parse_names(char *buf, int size) · line 156
Quoted 8 lines
reftable/basics.c +5
-3@@ -152,9 +152,11 @@ char **parse_names(char *buf, int size) -REFTABLE_ALLOC_GROW (names, names_len +1 , names_cap); -if (!names) - goto err; +char **names_grown = names; +REFTABLE_ALLOC_GROW (names_grown, names_len +1 , names_cap); +if (!names_grown) + goto err; + names = names_grown;
Seems sensible; it covers both problems described by the commit message.
PATCH v5 6/25 reftable/record: handle allocation failures on copy
In reply to Patrick SteinhardtHandle allocation failures when copying records. While at it, convert from xstrdup() to reftable_strdup(). Adapt callsites to check for error codes.
--- reftable/record.c | 84 +++++++++++++++++++++++++++++++++-------------- reftable/record.h | 6 ++-- 2 files changed, 63 insertions(+), 27 deletions(-)
reftable/record.c +60 -24
@@ -215,13 +215,14 @@ static void reftable_ref_record_key(const void *r, struct strbuf *dest)strbuf_addstr (dest, rec->refname); } -static void reftable_ref_record_copy_from (void *rec,const void *src_rec, -int hash_size) +static int reftable_ref_record_copy_from (void *rec,const void *src_rec, +int hash_size) {struct reftable_ref_record *ref = rec;const struct reftable_ref_record *src = src_rec;char *refname =NULL ;size_t refname_cap =0 ; +int err;assert (hash_size >0 );
@@ -236,6 +237,11 @@ static void reftable_ref_record_copy_from(void *rec, const void *src_rec,REFTABLE_ALLOC_GROW (ref->refname, refname_len +1 , ref->refname_cap); +if (!ref->refname) { + err =REFTABLE_OUT_OF_MEMORY_ERROR ; + goto out; + } +memcpy (ref->refname, src->refname, refname_len); ref->refname[refname_len] =0 ; }
@@ -254,9 +260,17 @@ static void reftable_ref_record_copy_from(void *rec, const void *src_rec, src->value.val2.target_value, hash_size);break ;case REFTABLE_REF_SYMREF : - ref->value.symref =xstrdup (src->value.symref); + ref->value.symref =reftable_strdup (src->value.symref); +if (!ref->value.symref) { + err =REFTABLE_OUT_OF_MEMORY_ERROR ; + goto out; + }break ; } + + err =0 ; +out: +return err; }static void reftable_ref_record_release_void (void *rec)
@@ -457,23 +471,28 @@ static void reftable_obj_record_release(void *rec)memset (obj,0 ,sizeof (struct reftable_obj_record )); } -static void reftable_obj_record_copy_from (void *rec,const void *src_rec, - int hash_size UNUSED) +static int reftable_obj_record_copy_from (void *rec,const void *src_rec, + int hash_size UNUSED) {struct reftable_obj_record *obj = rec; -const struct reftable_obj_record *src = - (const struct reftable_obj_record *)src_rec; +const struct reftable_obj_record *src = src_rec;reftable_obj_record_release (obj);REFTABLE_ALLOC_ARRAY (obj->hash_prefix, src->hash_prefix_len); +if (!obj->hash_prefix) +return REFTABLE_OUT_OF_MEMORY_ERROR ; obj->hash_prefix_len = src->hash_prefix_len;if (src->hash_prefix_len)memcpy (obj->hash_prefix, src->hash_prefix, obj->hash_prefix_len);REFTABLE_ALLOC_ARRAY (obj->offsets, src->offset_len); +if (!obj->offsets) +return REFTABLE_OUT_OF_MEMORY_ERROR ; obj->offset_len = src->offset_len;COPY_ARRAY (obj->offsets, src->offsets, src->offset_len); + +return 0 ; }static uint8_t reftable_obj_record_val_type (const void *rec)
@@ -646,33 +665,44 @@ static void reftable_log_record_key(const void *r, struct strbuf *dest)strbuf_add (dest, i64,sizeof (i64)); } -static void reftable_log_record_copy_from (void *rec,const void *src_rec, -int hash_size) +static int reftable_log_record_copy_from (void *rec,const void *src_rec, +int hash_size) {struct reftable_log_record *dst = rec;const struct reftable_log_record *src = (const struct reftable_log_record *)src_rec; +int ret;reftable_log_record_release (dst); *dst = *src; +if (dst->refname) { - dst->refname =xstrdup (dst->refname); + dst->refname =reftable_strdup (dst->refname); +if (!dst->refname) { + ret =REFTABLE_OUT_OF_MEMORY_ERROR ; + goto out; + } } +switch (dst->value_type) {case REFTABLE_LOG_DELETION :break ;case REFTABLE_LOG_UPDATE : -if (dst->value.update.email) { +if (dst->value.update.email) dst->value.update.email = -xstrdup (dst->value.update.email); - } -if (dst->value.update.name) { +reftable_strdup (dst->value.update.email); +if (dst->value.update.name) dst->value.update.name = -xstrdup (dst->value.update.name); - } -if (dst->value.update.message) { +reftable_strdup (dst->value.update.name); +if (dst->value.update.message) dst->value.update.message = -xstrdup (dst->value.update.message); +reftable_strdup (dst->value.update.message); + +if (!dst->value.update.email || + !dst->value.update.name || + !dst->value.update.message) { + ret =REFTABLE_OUT_OF_MEMORY_ERROR ; + goto out; } memcpy(dst->value.update.new_hash,
@@ -681,6 +711,10 @@ static void reftable_log_record_copy_from(void *rec, const void *src_rec, src->value.update.old_hash, hash_size);break ; } + + ret =0 ; +out: +return ret; }static void reftable_log_record_release_void (void *rec)
@@ -954,8 +988,8 @@ static void reftable_index_record_key(const void *r, struct strbuf *dest)strbuf_addbuf (dest, &rec->last_key); } -static void reftable_index_record_copy_from (void *rec,const void *src_rec, - int hash_size UNUSED) +static int reftable_index_record_copy_from (void *rec,const void *src_rec, + int hash_size UNUSED) {struct reftable_index_record *dst = rec;const struct reftable_index_record *src = src_rec;
@@ -963,6 +997,8 @@ static void reftable_index_record_copy_from(void *rec, const void *src_rec,strbuf_reset (&dst->last_key);strbuf_addbuf (&dst->last_key, &src->last_key); dst->offset = src->offset; + +return 0 ; }static void reftable_index_record_release (void *rec)
@@ -1054,14 +1090,14 @@ int reftable_record_encode(struct reftable_record *rec, struct string_view dest, dest, hash_size); } -void reftable_record_copy_from (struct reftable_record *rec, +int reftable_record_copy_from (struct reftable_record *rec,struct reftable_record *src,int hash_size) {assert (src->type == rec->type); -reftable_record_vtable (rec)->copy_from (reftable_record_data (rec), -reftable_record_data (src), - hash_size); +return reftable_record_vtable (rec)->copy_from (reftable_record_data (rec), +reftable_record_data (src), + hash_size); }uint8_t reftable_record_val_type (struct reftable_record *rec)
reftable/record.h +3 -3
@@ -44,7 +44,7 @@ struct reftable_record_vtable {/* The record type of ('r' for ref). */ uint8_t type; -void (*copy_from)(void *dest,const void *src,int hash_size); +int (*copy_from)(void *dest,const void *src,int hash_size);/* a value of [0..7], indicating record subvariants (eg. ref vs. symref * vs ref deletion) */
@@ -137,8 +137,8 @@ void reftable_record_init(struct reftable_record *rec, uint8_t typ);int reftable_record_cmp (struct reftable_record *a,struct reftable_record *b);int reftable_record_equal (struct reftable_record *a,struct reftable_record *b,int hash_size);void reftable_record_key (struct reftable_record *rec,struct strbuf *dest); -void reftable_record_copy_from (struct reftable_record *rec, -struct reftable_record *src,int hash_size); +int reftable_record_copy_from (struct reftable_record *rec, +struct reftable_record *src,int hash_size);uint8_t reftable_record_val_type (struct reftable_record *rec);int reftable_record_encode (struct reftable_record *rec,struct string_view dest,int hash_size);
Signature · 1 line
2.47.0.rc0.dirty
PATCH v5 7/25 reftable/record: handle allocation failures when decoding records
In reply to Patrick SteinhardtHandle allocation failures when decoding records. While at it, fix some error codes to be REFTABLE_FORMAT_ERROR.
--- reftable/record.c | 80 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 63 insertions(+), 17 deletions(-)
reftable/record.c +63 -17
@@ -359,7 +359,7 @@ static int reftable_ref_record_decode(void *rec, struct strbuf key,uint64_t update_index =0 ;const char *refname =NULL ;size_t refname_cap =0 ; -int n; +int n, err;assert (hash_size >0 );
@@ -375,6 +375,10 @@ static int reftable_ref_record_decode(void *rec, struct strbuf key,SWAP (r->refname_cap, refname_cap);REFTABLE_ALLOC_GROW (r->refname, key.len +1 , r->refname_cap); +if (!r->refname) { + err =REFTABLE_OUT_OF_MEMORY_ERROR ; + goto done; + }memcpy (r->refname, key.buf, key.len); r->refname[key.len] =0 ;
@@ -383,7 +387,8 @@ static int reftable_ref_record_decode(void *rec, struct strbuf key,switch (val_type) {case REFTABLE_REF_VAL1 :if (in.len < hash_size) { -return -1 ; + err =REFTABLE_FORMAT_ERROR ; + goto done; }memcpy (r->value.val1, in.buf, hash_size);
@@ -392,7 +397,8 @@ static int reftable_ref_record_decode(void *rec, struct strbuf key,case REFTABLE_REF_VAL2 :if (in.len <2 * hash_size) { -return -1 ; + err =REFTABLE_FORMAT_ERROR ; + goto done; }memcpy (r->value.val2.value, in.buf, hash_size);
@@ -405,7 +411,8 @@ static int reftable_ref_record_decode(void *rec, struct strbuf key,case REFTABLE_REF_SYMREF : {int n =decode_string (scratch, in);if (n <0 ) { -return -1 ; + err =REFTABLE_FORMAT_ERROR ; + goto done; }string_view_consume (&in, n); r->value.symref =strbuf_detach (scratch,NULL );
@@ -419,6 +426,9 @@ static int reftable_ref_record_decode(void *rec, struct strbuf key, }return start.len - in.len; + +done: +return err; }static int reftable_ref_record_is_deletion_void (const void *p)
@@ -552,6 +562,8 @@ static int reftable_obj_record_decode(void *rec, struct strbuf key,reftable_obj_record_release (r);REFTABLE_ALLOC_ARRAY (r->hash_prefix, key.len); +if (!r->hash_prefix) +return REFTABLE_OUT_OF_MEMORY_ERROR ;memcpy (r->hash_prefix, key.buf, key.len); r->hash_prefix_len = key.len;
@@ -570,6 +582,8 @@ static int reftable_obj_record_decode(void *rec, struct strbuf key,return start.len - in.len;REFTABLE_ALLOC_ARRAY (r->offsets, count); +if (!r->offsets) +return REFTABLE_OUT_OF_MEMORY_ERROR ; r->offset_len = count; n =get_var_int (&r->offsets[0 ], &in);
@@ -801,12 +815,17 @@ static int reftable_log_record_decode(void *rec, struct strbuf key,struct reftable_log_record *r = rec;uint64_t max =0 ;uint64_t ts =0 ; -int n; +int err, n;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); +if (!r->refname) { + err =REFTABLE_OUT_OF_MEMORY_ERROR ; + goto done; + } +memcpy (r->refname, key.buf, key.len -8 ); ts =get_be64 (key.buf + key.len -8 );
@@ -829,8 +848,10 @@ static int reftable_log_record_decode(void *rec, struct strbuf key,if (val_type ==REFTABLE_LOG_DELETION )return 0 ; -if (in.len <2 * hash_size) -return REFTABLE_FORMAT_ERROR ; +if (in.len <2 * hash_size) { + err =REFTABLE_FORMAT_ERROR ; + goto done; + }memcpy (r->value.update.old_hash, in.buf, hash_size);memcpy (r->value.update.new_hash, in.buf + hash_size, hash_size);
@@ -838,8 +859,10 @@ static int reftable_log_record_decode(void *rec, struct strbuf key,string_view_consume (&in,2 * hash_size); n =decode_string (scratch, in); -if (n <0 ) +if (n <0 ) { + err =REFTABLE_FORMAT_ERROR ; goto done; + }string_view_consume (&in, n);/*
@@ -850,52 +873,75 @@ static int reftable_log_record_decode(void *rec, struct strbuf key,*/ if (!r->value.update.name ||strcmp (r->value.update.name, scratch->buf)) { - r->value.update.name = -reftable_realloc (r->value.update.name, scratch->len +1 ); +char *name =reftable_realloc (r->value.update.name, scratch->len +1 ); +if (!name) { + err =REFTABLE_OUT_OF_MEMORY_ERROR ; + goto done; + } + + r->value.update.name = name;memcpy (r->value.update.name, scratch->buf, scratch->len); r->value.update.name[scratch->len] =0 ; } n =decode_string (scratch, in); -if (n <0 ) +if (n <0 ) { + err =REFTABLE_FORMAT_ERROR ; goto done; + }string_view_consume (&in, n);/* Same as above, but for the reflog email. */ if (!r->value.update.email ||strcmp (r->value.update.email, scratch->buf)) { - r->value.update.email = -reftable_realloc (r->value.update.email, scratch->len +1 ); +char *email =reftable_realloc (r->value.update.email, scratch->len +1 ); +if (!email) { + err =REFTABLE_OUT_OF_MEMORY_ERROR ; + goto done; + } + + r->value.update.email = email;memcpy (r->value.update.email, scratch->buf, scratch->len); r->value.update.email[scratch->len] =0 ; } ts =0 ; n =get_var_int (&ts, &in); -if (n <0 ) +if (n <0 ) { + err =REFTABLE_FORMAT_ERROR ; goto done; + }string_view_consume (&in, n); r->value.update.time = ts; -if (in.len <2 ) +if (in.len <2 ) { + err =REFTABLE_FORMAT_ERROR ; goto done; + } r->value.update.tz_offset =get_be16 (in.buf);string_view_consume (&in,2 ); n =decode_string (scratch, in); -if (n <0 ) +if (n <0 ) { + err =REFTABLE_FORMAT_ERROR ; goto done; + }string_view_consume (&in, n);REFTABLE_ALLOC_GROW (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; + } +memcpy (r->value.update.message, scratch->buf, scratch->len); r->value.update.message[scratch->len] =0 ;return start.len - in.len; done: -return REFTABLE_FORMAT_ERROR ; +return err; }static int null_streq (const char *a,const char *b)
Signature · 1 line
2.47.0.rc0.dirty
PATCH v5 8/25 reftable/writer: handle allocation failures in `writer_index_hash()`
In reply to Patrick SteinhardtHandle allocation errors in writer_index_hash(). Adjust its only caller in reftable_writer_add_ref() accordingly.
--- reftable/writer.c | 61 +++++++++++++++++++++++++++++------------------ 1 file changed, 38 insertions(+), 23 deletions(-)
reftable/writer.c +38 -23
@@ -186,18 +186,22 @@ static int obj_index_tree_node_compare(const void *a, const void *b) &((const struct obj_index_tree_node *)b)->hash); } -static void writer_index_hash (struct reftable_writer *w,struct strbuf *hash) +static int writer_index_hash (struct reftable_writer *w,struct strbuf *hash) {uint64_t off = w->next; -struct obj_index_tree_node want = { .hash = *hash }; +struct obj_index_tree_node *key; +struct tree_node *node; -struct tree_node *node =tree_search (&want, &w->obj_index_tree, - &obj_index_tree_node_compare,0 ); -struct obj_index_tree_node *key =NULL ; + node =tree_search (&want, &w->obj_index_tree, + &obj_index_tree_node_compare,0 );if (!node) {struct obj_index_tree_node empty =OBJ_INDEX_TREE_NODE_INIT ; - key =reftable_malloc (sizeof (struct obj_index_tree_node )); + + key =reftable_malloc (sizeof (*key)); +if (!key) +return REFTABLE_OUT_OF_MEMORY_ERROR ; + *key = empty;strbuf_reset (&key->hash);
@@ -208,12 +212,15 @@ static void writer_index_hash(struct reftable_writer *w, struct strbuf *hash) key = node->key; } -if (key->offset_len >0 && key->offsets[key->offset_len -1 ] == off) { -return ; - } +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); +if (!key->offsets) +return REFTABLE_OUT_OF_MEMORY_ERROR ; key->offsets[key->offset_len++] = off; + +return 0 ; } static int writer_add_record(struct reftable_writer *w,
@@ -284,11 +291,11 @@ int reftable_writer_add_ref(struct reftable_writer *w, .ref = *ref }, }; -int err =0 ; +struct strbuf buf =STRBUF_INIT ; +int err; -if (!ref->refname) -return REFTABLE_API_ERROR ; -if (ref->update_index < w->min_update_index || +if (!ref->refname || + ref->update_index < w->min_update_index || ref->update_index > w->max_update_index)return REFTABLE_API_ERROR ;
@@ -296,24 +303,32 @@ int reftable_writer_add_ref(struct reftable_writer *w, err =writer_add_record (w, &rec);if (err <0 ) -return err; + goto out;if (!w->opts.skip_index_objects &&reftable_ref_record_val1 (ref)) { -struct strbuf h =STRBUF_INIT ; -strbuf_add (&h, (char *)reftable_ref_record_val1 (ref), +strbuf_add (&buf, (char *)reftable_ref_record_val1 (ref),hash_size (w->opts.hash_id)); -writer_index_hash (w, &h); -strbuf_release (&h); + + err =writer_index_hash (w, &buf); +if (err <0 ) + goto out; }if (!w->opts.skip_index_objects &&reftable_ref_record_val2 (ref)) { -struct strbuf h =STRBUF_INIT ; -strbuf_add (&h,reftable_ref_record_val2 (ref), +strbuf_reset (&buf); +strbuf_add (&buf,reftable_ref_record_val2 (ref),hash_size (w->opts.hash_id)); -writer_index_hash (w, &h); -strbuf_release (&h); + + err =writer_index_hash (w, &buf); +if (err <0 ) + goto out; } -return 0 ; + + err =0 ; + +out: +strbuf_release (&buf); +return err; } int reftable_writer_add_refs(struct reftable_writer *w,
Signature · 1 line
2.47.0.rc0.dirty
PATCH v5 9/25 reftable/writer: handle allocation failures in `reftable_new_writer()`
In reply to Patrick SteinhardtHandle allocation failures in reftable_new_writer(). Adapt the function to return an error code to return such failures. While at it, rename it to match our code style as we have to touch up every callsite anyway.
--- reftable/reftable-writer.h | 12 +++++++----- reftable/stack.c | 14 ++++++++++---- reftable/writer.c | 22 ++++++++++++++++------ t/unit-tests/lib-reftable.c | 8 +++++--- 4 files changed, 38 insertions(+), 18 deletions(-)
reftable/reftable-writer.h +7 -5
@@ -90,11 +90,13 @@ struct reftable_stats {int object_id_len; }; -/* reftable_new_writer creates a new writer */ -struct reftable_writer * -reftable_new_writer (ssize_t (*writer_func)(void *,const void *,size_t ), -int (*flush_func)(void *), -void *writer_arg,const struct reftable_write_options *opts); +struct reftable_writer ; + +/* Create a new writer. */ +int reftable_writer_new (struct reftable_writer **out, +ssize_t (*writer_func)(void *,const void *,size_t ), +int (*flush_func)(void *), +void *writer_arg,const struct reftable_write_options *opts);/* Set the range of update indices for the records we will add. When writing a table into a stack, the min should be at least
reftable/stack.c +10 -4
@@ -808,8 +808,11 @@ int reftable_addition_add(struct reftable_addition *add, } tab_fd =get_tempfile_fd (tab_file); - wr =reftable_new_writer (reftable_fd_write, reftable_fd_flush, &tab_fd, - &add->stack->opts); + err =reftable_writer_new (&wr, reftable_fd_write, reftable_fd_flush, + &tab_fd, &add->stack->opts); +if (err <0 ) + goto done; + err =write_table (wr, arg);if (err <0 ) goto done;
@@ -898,8 +901,11 @@ static int stack_compact_locked(struct reftable_stack *st, goto done; } - wr =reftable_new_writer (reftable_fd_write, reftable_fd_flush, - &tab_fd, &st->opts); + err =reftable_writer_new (&wr, reftable_fd_write, reftable_fd_flush, + &tab_fd, &st->opts); +if (err <0 ) + goto done; + err =stack_write_compact (st, wr, first, last, config);if (err <0 ) goto done;
reftable/writer.c +16 -6
@@ -117,13 +117,17 @@ static void writer_reinit_block_writer(struct reftable_writer *w, uint8_t typ) w->block_writer->restart_interval = w->opts.restart_interval; } -struct reftable_writer * -reftable_new_writer (ssize_t (*writer_func)(void *,const void *,size_t ), -int (*flush_func)(void *), -void *writer_arg,const struct reftable_write_options *_opts) +int reftable_writer_new (struct reftable_writer **out, +ssize_t (*writer_func)(void *,const void *,size_t ), +int (*flush_func)(void *), +void *writer_arg,const struct reftable_write_options *_opts) { -struct reftable_writer *wp =reftable_calloc (1 ,sizeof (*wp));struct reftable_write_options opts = {0 }; +struct reftable_writer *wp; + + wp =reftable_calloc (1 ,sizeof (*wp)); +if (!wp) +return REFTABLE_OUT_OF_MEMORY_ERROR ;if (_opts) opts = *_opts;
@@ -134,13 +138,19 @@ reftable_new_writer(ssize_t (*writer_func)(void *, const void *, size_t),strbuf_init (&wp->block_writer_data.last_key,0 );strbuf_init (&wp->last_key,0 );REFTABLE_CALLOC_ARRAY (wp->block, opts.block_size); +if (!wp->block) { +reftable_free (wp); +return REFTABLE_OUT_OF_MEMORY_ERROR ; + } wp->write = writer_func; wp->write_arg = writer_arg; wp->opts = opts; wp->flush = flush_func;writer_reinit_block_writer (wp,BLOCK_TYPE_REF ); -return wp; + *out = wp; + +return 0 ; } void reftable_writer_set_limits(struct reftable_writer *w, uint64_t min,
t/unit-tests/lib-reftable.c +5 -3
@@ -22,9 +22,11 @@ static int strbuf_writer_flush(void *arg UNUSED)struct reftable_writer *t_reftable_strbuf_writer (struct strbuf *buf,struct reftable_write_options *opts) { -return reftable_new_writer (&strbuf_writer_write, - &strbuf_writer_flush, - buf, opts); +struct reftable_writer *writer; +int ret =reftable_writer_new (&writer, &strbuf_writer_write, &strbuf_writer_flush, + buf, opts); +check (!ret); +return writer; } void t_reftable_write_to_buf(struct strbuf *buf,
Signature · 1 line
2.47.0.rc0.dirty
PATCH v5 10/25 reftable/merged: handle allocation failures in `merged_table_init_iter()`
In reply to Patrick SteinhardtHandle allocation failures in merged_table_init_iter(). While at it, merge merged_iter_init() into the function. It only has a single caller and merging them makes it easier to handle allocation failures consistently.
This change also requires us to adapt reftable_stack_init_*_iterator() to bubble up the new error codes of merged_table_iter_init(). Adapt callsites accordingly.
--- refs/reftable-backend.c | 39 +++++++++++++---- reftable/merged.c | 74 ++++++++++++++++++++------------ reftable/merged.h | 6 +-- reftable/reftable-merged.h | 8 ++-- reftable/reftable-stack.h | 8 ++-- reftable/stack.c | 34 ++++++++++----- t/helper/test-reftable.c | 10 ++++- t/unit-tests/t-reftable-merged.c | 12 ++++-- t/unit-tests/t-reftable-stack.c | 4 +- 9 files changed, 131 insertions(+), 64 deletions(-)
refs/reftable-backend.c +31 -8
@@ -1299,7 +1299,9 @@ static int write_transaction_table(struct reftable_writer *writer, void *cb_datastruct reftable_log_record log = {0 };struct reftable_iterator it = {0 }; -reftable_stack_init_log_iterator (arg->stack, &it); + ret =reftable_stack_init_log_iterator (arg->stack, &it); +if (ret <0 ) + goto done;/* * When deleting refs we also delete all reflog entries
@@ -1669,7 +1671,10 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)* copy over all log entries from the old reflog. Last but not least, * when renaming we also have to delete all the old reflog entries. */ -reftable_stack_init_log_iterator (arg->stack, &it); + ret =reftable_stack_init_log_iterator (arg->stack, &it); +if (ret <0 ) + goto done; + ret =reftable_iterator_seek_log (&it, arg->oldname);if (ret <0 ) goto done;
@@ -1890,7 +1895,10 @@ static struct reftable_reflog_iterator *reflog_iterator_for_stack(struct reftablif (ret <0 ) goto done; -reftable_stack_init_log_iterator (stack, &iter->iter); + ret =reftable_stack_init_log_iterator (stack, &iter->iter); +if (ret <0 ) + goto done; + ret =reftable_iterator_seek_log (&iter->iter,"" );if (ret <0 ) goto done;
@@ -1957,7 +1965,10 @@ static int reftable_be_for_each_reflog_ent_reverse(struct ref_store *ref_store,if (refs->err <0 )return refs->err; -reftable_stack_init_log_iterator (stack, &it); + ret =reftable_stack_init_log_iterator (stack, &it); +if (ret <0 ) + goto done; + ret =reftable_iterator_seek_log (&it, refname);while (!ret) { ret =reftable_iterator_next_log (&it, &log);
@@ -1973,6 +1984,7 @@ static int reftable_be_for_each_reflog_ent_reverse(struct ref_store *ref_store,break ; } +done:reftable_log_record_release (&log);reftable_iterator_destroy (&it);return ret;
@@ -1994,7 +2006,10 @@ static int reftable_be_for_each_reflog_ent(struct ref_store *ref_store,if (refs->err <0 )return refs->err; -reftable_stack_init_log_iterator (stack, &it); + ret =reftable_stack_init_log_iterator (stack, &it); +if (ret <0 ) + goto done; + ret =reftable_iterator_seek_log (&it, refname);while (!ret) {struct reftable_log_record log = {0 };
@@ -2044,7 +2059,10 @@ static int reftable_be_reflog_exists(struct ref_store *ref_store,if (ret <0 ) goto done; -reftable_stack_init_log_iterator (stack, &it); + ret =reftable_stack_init_log_iterator (stack, &it); +if (ret <0 ) + goto done; + ret =reftable_iterator_seek_log (&it, refname);if (ret <0 ) goto done;
@@ -2150,7 +2168,9 @@ static int write_reflog_delete_table(struct reftable_writer *writer, void *cb_dareftable_writer_set_limits (writer, ts, ts); -reftable_stack_init_log_iterator (arg->stack, &it); + ret =reftable_stack_init_log_iterator (arg->stack, &it); +if (ret <0 ) + goto out;/* * In order to delete a table we need to delete all reflog entries one
@@ -2174,6 +2194,7 @@ static int write_reflog_delete_table(struct reftable_writer *writer, void *cb_da ret =reftable_writer_add_log (writer, &tombstone); } +out:reftable_log_record_release (&log);reftable_iterator_destroy (&it);return ret;
@@ -2312,7 +2333,9 @@ static int reftable_be_reflog_expire(struct ref_store *ref_store,if (ret <0 ) goto done; -reftable_stack_init_log_iterator (stack, &it); + ret =reftable_stack_init_log_iterator (stack, &it); +if (ret <0 ) + goto done; ret =reftable_iterator_seek_log (&it, refname);if (ret <0 )
reftable/merged.c +47 -27
94 lines not on this page · raw message
reftable/merged.h +3 -3
@@ -26,8 +26,8 @@ struct reftable_merged_table {struct reftable_iterator ; -void merged_table_init_iter (struct reftable_merged_table *mt, -struct reftable_iterator *it, -uint8_t typ); +int merged_table_init_iter (struct reftable_merged_table *mt, +struct reftable_iterator *it, +uint8_t typ);#endif
reftable/reftable-merged.h +4 -4
@@ -37,12 +37,12 @@ int reftable_merged_table_new(struct reftable_merged_table **dest, uint32_t hash_id);/* Initialize a merged table iterator for reading refs. */ -void reftable_merged_table_init_ref_iterator (struct reftable_merged_table *mt, -struct reftable_iterator *it); +int reftable_merged_table_init_ref_iterator (struct reftable_merged_table *mt, +struct reftable_iterator *it);/* Initialize a merged table iterator for reading logs. */ -void reftable_merged_table_init_log_iterator (struct reftable_merged_table *mt, -struct reftable_iterator *it); +int reftable_merged_table_init_log_iterator (struct reftable_merged_table *mt, +struct reftable_iterator *it);/* returns the max update_index covered by this merged table. */ uint64_t
reftable/reftable-stack.h +4 -4
@@ -73,16 +73,16 @@ struct reftable_iterator;* be used to iterate through refs. The iterator is valid until the next reload * or write. */ -void reftable_stack_init_ref_iterator (struct reftable_stack *st, -struct reftable_iterator *it); +int reftable_stack_init_ref_iterator (struct reftable_stack *st, +struct reftable_iterator *it);/* * Initialize an iterator for the merged tables contained in the stack that can * be used to iterate through logs. The iterator is valid until the next reload * or write. */ -void reftable_stack_init_log_iterator (struct reftable_stack *st, -struct reftable_iterator *it); +int reftable_stack_init_log_iterator (struct reftable_stack *st, +struct reftable_iterator *it);/* returns the merged_table for seeking. This table is valid until the * next write or reload, and should not be closed or deleted.
reftable/stack.c +23 -11
@@ -136,18 +136,18 @@ int read_lines(const char *filename, char ***namesp)return err; } -void reftable_stack_init_ref_iterator (struct reftable_stack *st, +int reftable_stack_init_ref_iterator (struct reftable_stack *st,struct reftable_iterator *it) { -merged_table_init_iter (reftable_stack_merged_table (st), - it,BLOCK_TYPE_REF ); +return merged_table_init_iter (reftable_stack_merged_table (st), + it,BLOCK_TYPE_REF ); } -void reftable_stack_init_log_iterator (struct reftable_stack *st, -struct reftable_iterator *it) +int reftable_stack_init_log_iterator (struct reftable_stack *st, +struct reftable_iterator *it) { -merged_table_init_iter (reftable_stack_merged_table (st), - it,BLOCK_TYPE_LOG ); +return merged_table_init_iter (reftable_stack_merged_table (st), + it,BLOCK_TYPE_LOG ); } struct reftable_merged_table *
@@ -952,7 +952,10 @@ static int stack_write_compact(struct reftable_stack *st,if (err <0 ) goto done; -merged_table_init_iter (mt, &it,BLOCK_TYPE_REF ); + err =merged_table_init_iter (mt, &it,BLOCK_TYPE_REF ); +if (err <0 ) + goto done; + err =reftable_iterator_seek_ref (&it,"" );if (err <0 ) goto done;
@@ -977,7 +980,10 @@ static int stack_write_compact(struct reftable_stack *st, }reftable_iterator_destroy (&it); -merged_table_init_iter (mt, &it,BLOCK_TYPE_LOG ); + err =merged_table_init_iter (mt, &it,BLOCK_TYPE_LOG ); +if (err <0 ) + goto done; + err =reftable_iterator_seek_log (&it,"" );if (err <0 ) goto done;
@@ -1496,7 +1502,10 @@ int reftable_stack_read_ref(struct reftable_stack *st, const char *refname,struct reftable_iterator it = {0 };int ret; -reftable_merged_table_init_ref_iterator (st->merged, &it); + ret =reftable_merged_table_init_ref_iterator (st->merged, &it); +if (ret) + goto out; + ret =reftable_iterator_seek_ref (&it, refname);if (ret) goto out;
@@ -1523,7 +1532,10 @@ int reftable_stack_read_log(struct reftable_stack *st, const char *refname,struct reftable_iterator it = {0 };int err; -reftable_stack_init_log_iterator (st, &it); + err =reftable_stack_init_log_iterator (st, &it); +if (err) + goto done; + err =reftable_iterator_seek_log (&it, refname);if (err) goto done;
t/helper/test-reftable.c +8 -2
@@ -28,7 +28,10 @@ static int dump_table(struct reftable_merged_table *mt)const struct git_hash_algo *algop;int err; -reftable_merged_table_init_ref_iterator (mt, &it); + err =reftable_merged_table_init_ref_iterator (mt, &it); +if (err <0 ) +return err; + err =reftable_iterator_seek_ref (&it,"" );if (err <0 )return err;
@@ -63,7 +66,10 @@ static int dump_table(struct reftable_merged_table *mt)reftable_iterator_destroy (&it);reftable_ref_record_release (&ref); -reftable_merged_table_init_log_iterator (mt, &it); + err =reftable_merged_table_init_log_iterator (mt, &it); +if (err <0 ) +return err; + err =reftable_iterator_seek_log (&it,"" );if (err <0 )return err;
t/unit-tests/t-reftable-merged.c +8 -4
@@ -82,7 +82,8 @@ static void t_merged_single_record(void)struct reftable_iterator it = {0 };int err; -merged_table_init_iter (mt, &it,BLOCK_TYPE_REF ); + err =merged_table_init_iter (mt, &it,BLOCK_TYPE_REF ); +check (!err); err =reftable_iterator_seek_ref (&it,"a" );check (!err);
@@ -161,7 +162,8 @@ static void t_merged_refs(void)size_t cap =0 ;size_t i; -merged_table_init_iter (mt, &it,BLOCK_TYPE_REF ); + err =merged_table_init_iter (mt, &it,BLOCK_TYPE_REF ); +check (!err); err =reftable_iterator_seek_ref (&it,"a" );check (!err); check_int(reftable_merged_table_hash_id(mt), ==, GIT_SHA1_FORMAT_ID);
@@ -367,7 +369,8 @@ static void t_merged_logs(void)size_t cap =0 ;size_t i; -merged_table_init_iter (mt, &it,BLOCK_TYPE_LOG ); + err =merged_table_init_iter (mt, &it,BLOCK_TYPE_LOG ); +check (!err); err =reftable_iterator_seek_log (&it,"a" );check (!err); check_int(reftable_merged_table_hash_id(mt), ==, GIT_SHA1_FORMAT_ID);
@@ -390,7 +393,8 @@ static void t_merged_logs(void)check (reftable_log_record_equal (want[i], &out[i],GIT_SHA1_RAWSZ )); -merged_table_init_iter (mt, &it,BLOCK_TYPE_LOG ); + err =merged_table_init_iter (mt, &it,BLOCK_TYPE_LOG ); +check (!err); err =reftable_iterator_seek_log_at (&it,"a" ,2 );check (!err);reftable_log_record_release (&out[0 ]);
t/unit-tests/t-reftable-stack.c +3 -1
@@ -599,7 +599,9 @@ static void t_reftable_stack_iterator(void)reftable_iterator_destroy (&it); -reftable_stack_init_log_iterator (st, &it); + err =reftable_stack_init_log_iterator (st, &it); +check (!err); +reftable_iterator_seek_log (&it, logs[0 ].refname);for (i =0 ; ; i++) {struct reftable_log_record log = {0 };
Signature · 1 line
2.47.0.rc0.dirty
PATCH v5 11/25 reftable/reader: handle allocation failures for unindexed reader
In reply to Patrick SteinhardtHandle allocation failures when creating unindexed readers.
--- reftable/reader.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-)
reftable/reader.c +17 -6
@@ -734,19 +734,30 @@ static int reftable_reader_refs_for_unindexed(struct reftable_reader *r,REFTABLE_ALLOC_ARRAY (ti,1 );table_iter_init (ti, r); err =table_iter_seek_start (ti,BLOCK_TYPE_REF ,0 ); -if (err <0 ) { -reftable_free (ti); -return err; - } +if (err <0 ) + goto out; - filter =reftable_malloc (sizeof (struct filtering_ref_iterator )); + filter =reftable_malloc (sizeof (*filter)); +if (!filter) { + err =REFTABLE_OUT_OF_MEMORY_ERROR ; + goto out; + } *filter = empty;strbuf_add (&filter->oid, oid, oid_len);iterator_from_table_iter (&filter->it, ti);iterator_from_filtering_ref_iterator (it, filter); -return 0 ; + + err =0 ; + +out: +if (err <0 ) { +if (ti) +table_iter_close (ti); +reftable_free (ti); + } +return err; } int reftable_reader_refs_for(struct reftable_reader *r,
Signature · 1 line
2.47.0.rc0.dirty
PATCH v5 12/25 reftable/reader: handle allocation failures in `reader_init_iter()`
In reply to Patrick SteinhardtHandle allocation failures in reader_init_iter(). This requires us to also adapt reftable_reader_init_*_iterator() to bubble up the new error codes. Adapt callers accordingly.
--- reftable/merged.c | 4 +++- reftable/reader.c | 28 +++++++++++++++--------- reftable/reader.h | 6 ++--- reftable/reftable-reader.h | 8 +++---- t/unit-tests/t-reftable-readwrite.c | 34 +++++++++++++++++++---------- 5 files changed, 50 insertions(+), 30 deletions(-)
reftable/merged.c +3 -1
@@ -244,7 +244,9 @@ int merged_table_init_iter(struct reftable_merged_table *mt,for (size_t i =0 ; i < mt->readers_len; i++) {reftable_record_init (&subiters[i].rec, typ); -reader_init_iter (mt->readers[i], &subiters[i].iter, typ); + ret =reader_init_iter (mt->readers[i], &subiters[i].iter, typ); +if (ret <0 ) + goto out; }REFTABLE_CALLOC_ARRAY (mi,1 );
reftable/reader.c +18 -10
@@ -554,32 +554,37 @@ static void iterator_from_table_iter(struct reftable_iterator *it, it->ops = &table_iter_vtable; } -void reader_init_iter (struct reftable_reader *r, -struct reftable_iterator *it, -uint8_t typ) +int reader_init_iter (struct reftable_reader *r, +struct reftable_iterator *it, +uint8_t typ) {struct reftable_reader_offsets *offs =reader_offsets_for (r, typ);if (offs->is_present) {struct table_iter *ti;REFTABLE_ALLOC_ARRAY (ti,1 ); +if (!ti) +return REFTABLE_OUT_OF_MEMORY_ERROR ; +table_iter_init (ti, r);iterator_from_table_iter (it, ti); }else {iterator_set_empty (it); } + +return 0 ; } -void reftable_reader_init_ref_iterator (struct reftable_reader *r, -struct reftable_iterator *it) +int reftable_reader_init_ref_iterator (struct reftable_reader *r, +struct reftable_iterator *it) { -reader_init_iter (r, it,BLOCK_TYPE_REF ); +return reader_init_iter (r, it,BLOCK_TYPE_REF ); } -void reftable_reader_init_log_iterator (struct reftable_reader *r, -struct reftable_iterator *it) +int reftable_reader_init_log_iterator (struct reftable_reader *r, +struct reftable_iterator *it) { -reader_init_iter (r, it,BLOCK_TYPE_LOG ); +return reader_init_iter (r, it,BLOCK_TYPE_LOG ); } int reftable_reader_new(struct reftable_reader **out,
@@ -689,7 +694,10 @@ static int reftable_reader_refs_for_indexed(struct reftable_reader *r,struct indexed_table_ref_iter *itr =NULL ;/* Look through the reverse index. */ -reader_init_iter (r, &oit,BLOCK_TYPE_OBJ ); + err =reader_init_iter (r, &oit,BLOCK_TYPE_OBJ ); +if (err <0 ) + goto done; + err =iterator_seek (&oit, &want);if (err !=0 ) goto done;
reftable/reader.h +3 -3
@@ -56,9 +56,9 @@ struct reftable_reader {const char *reader_name (struct reftable_reader *r); -void reader_init_iter (struct reftable_reader *r, -struct reftable_iterator *it, -uint8_t typ); +int reader_init_iter (struct reftable_reader *r, +struct reftable_iterator *it, +uint8_t typ);/* initialize a block reader to read from `r` */ int reader_init_block_reader(struct reftable_reader *r, struct block_reader *br,
reftable/reftable-reader.h +4 -4
@@ -46,12 +46,12 @@ void reftable_reader_incref(struct reftable_reader *reader);void reftable_reader_decref (struct reftable_reader *reader);/* Initialize a reftable iterator for reading refs. */ -void reftable_reader_init_ref_iterator (struct reftable_reader *r, -struct reftable_iterator *it); +int reftable_reader_init_ref_iterator (struct reftable_reader *r, +struct reftable_iterator *it);/* Initialize a reftable iterator for reading logs. */ -void reftable_reader_init_log_iterator (struct reftable_reader *r, -struct reftable_iterator *it); +int reftable_reader_init_log_iterator (struct reftable_reader *r, +struct reftable_iterator *it);/* returns the hash ID used in this table. */ uint32_t reftable_reader_hash_id (struct reftable_reader *r);
t/unit-tests/t-reftable-readwrite.c +22 -12
@@ -206,7 +206,8 @@ static void t_log_write_read(void) err =reftable_reader_new (&reader, &source,"file.log" );check (!err); -reftable_reader_init_ref_iterator (reader, &it); + err =reftable_reader_init_ref_iterator (reader, &it); +check (!err); err =reftable_iterator_seek_ref (&it, names[N -1 ]);check (!err);
@@ -221,8 +222,8 @@ static void t_log_write_read(void)reftable_iterator_destroy (&it);reftable_ref_record_release (&ref); -reftable_reader_init_log_iterator (reader, &it); - + err =reftable_reader_init_log_iterator (reader, &it); +check (!err); err =reftable_iterator_seek_log (&it,"" );check (!err);
@@ -296,7 +297,8 @@ static void t_log_zlib_corruption(void) err =reftable_reader_new (&reader, &source,"file.log" );check (!err); -reftable_reader_init_log_iterator (reader, &it); + err =reftable_reader_init_log_iterator (reader, &it); +check (!err); err =reftable_iterator_seek_log (&it,"refname" ); check_int(err, ==, REFTABLE_ZLIB_ERROR);
@@ -325,7 +327,8 @@ static void t_table_read_write_sequential(void) err =reftable_reader_new (&reader, &source,"file.ref" );check (!err); -reftable_reader_init_ref_iterator (reader, &it); + err =reftable_reader_init_ref_iterator (reader, &it); +check (!err); err =reftable_iterator_seek_ref (&it,"" );check (!err);
@@ -376,7 +379,8 @@ static void t_table_read_api(void) err =reftable_reader_new (&reader, &source,"file.ref" );check (!err); -reftable_reader_init_ref_iterator (reader, &it); + err =reftable_reader_init_ref_iterator (reader, &it); +check (!err); err =reftable_iterator_seek_ref (&it, names[0 ]);check (!err);
@@ -419,7 +423,8 @@ static void t_table_read_write_seek(int index, int hash_id) }for (i =1 ; i <N ; i++) { -reftable_reader_init_ref_iterator (reader, &it); + err =reftable_reader_init_ref_iterator (reader, &it); +check (!err); err =reftable_iterator_seek_ref (&it, names[i]);check (!err); err =reftable_iterator_next_ref (&it, &ref);
@@ -435,7 +440,8 @@ static void t_table_read_write_seek(int index, int hash_id)strbuf_addstr (&pastLast, names[N -1 ]);strbuf_addstr (&pastLast,"/" ); -reftable_reader_init_ref_iterator (reader, &it); + err =reftable_reader_init_ref_iterator (reader, &it); +check (!err); err =reftable_iterator_seek_ref (&it, pastLast.buf);if (err ==0 ) {struct reftable_ref_record ref = {0 };
@@ -534,7 +540,8 @@ static void t_table_refs_for(int indexed)if (!indexed) reader->obj_offsets.is_present =0 ; -reftable_reader_init_ref_iterator (reader, &it); + err =reftable_reader_init_ref_iterator (reader, &it); +check (!err); err =reftable_iterator_seek_ref (&it,"" );check (!err);reftable_iterator_destroy (&it);
@@ -593,7 +600,8 @@ static void t_write_empty_table(void) err =reftable_reader_new (&rd, &source,"filename" );check (!err); -reftable_reader_init_ref_iterator (rd, &it); + err =reftable_reader_init_ref_iterator (rd, &it); +check (!err); err =reftable_iterator_seek_ref (&it,"" );check (!err);
@@ -802,7 +810,8 @@ static void t_write_multiple_indices(void)* Seeking the log uses the log index now. In case there is any * confusion regarding indices we would notice here. */ -reftable_reader_init_log_iterator (reader, &it); + err =reftable_reader_init_log_iterator (reader, &it); +check (!err); err =reftable_iterator_seek_log (&it,"" );check (!err);
@@ -858,7 +867,8 @@ static void t_write_multi_level_index(void)/* * Seeking the last ref should work as expected. */ -reftable_reader_init_ref_iterator (reader, &it); + err =reftable_reader_init_ref_iterator (reader, &it); +check (!err); err =reftable_iterator_seek_ref (&it,"refs/heads/199" );check (!err);
Signature · 1 line
2.47.0.rc0.dirty
PATCH v5 13/25 reftable/stack: handle allocation failures on reload
In reply to Patrick SteinhardtHandle allocation failures in reftable_stack_reload_once().
--- reftable/stack.c | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-)
reftable/stack.c +25 -9
@@ -214,13 +214,13 @@ void reftable_stack_destroy(struct reftable_stack *st) }static struct reftable_reader **stack_copy_readers (struct reftable_stack *st, -int cur_len) +size_t cur_len) {struct reftable_reader **cur =reftable_calloc (cur_len,sizeof (*cur)); -int i =0 ; -for (i =0 ; i < cur_len; i++) { +if (!cur) +return NULL ; +for (size_t i =0 ; i < cur_len; i++) cur[i] = st->readers[i]; - }return cur; }
@@ -229,18 +229,30 @@ static int reftable_stack_reload_once(struct reftable_stack *st, int reuse_open) {size_t cur_len = !st->merged ?0 : st->merged->readers_len; -struct reftable_reader **cur =stack_copy_readers (st, cur_len); +struct reftable_reader **cur;struct reftable_reader **reused =NULL ; -size_t reused_len =0 , reused_alloc =0 ; -size_t names_len =names_length (names); -struct reftable_reader **new_readers = -reftable_calloc (names_len,sizeof (*new_readers)); +struct reftable_reader **new_readers; +size_t reused_len =0 , reused_alloc =0 , names_len;size_t new_readers_len =0 ;struct reftable_merged_table *new_merged =NULL ;struct strbuf table_path =STRBUF_INIT ;int err =0 ;size_t i; + cur =stack_copy_readers (st, cur_len); +if (!cur) { + err =REFTABLE_OUT_OF_MEMORY_ERROR ; + goto done; + } + + names_len =names_length (names); + + new_readers =reftable_calloc (names_len,sizeof (*new_readers)); +if (!new_readers) { + err =REFTABLE_OUT_OF_MEMORY_ERROR ; + goto done; + } +while (*names) {struct reftable_reader *rd =NULL ;const char *name = *names++;
@@ -261,6 +273,10 @@ static int reftable_stack_reload_once(struct reftable_stack *st,* do by bumping their refcount. */ REFTABLE_ALLOC_GROW (reused, reused_len +1 , reused_alloc); +if (!reused) { + err =REFTABLE_OUT_OF_MEMORY_ERROR ; + goto done; + } reused[reused_len++] = rd;reftable_reader_incref (rd);break ;
Signature · 1 line
2.47.0.rc0.dirty
PATCH v5 14/25 reftable/stack: handle allocation failures in `reftable_new_stack()`
In reply to Patrick SteinhardtHandle allocation failures in reftable_new_stack().
--- reftable/stack.c | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-)
reftable/stack.c +26 -8
@@ -56,10 +56,16 @@ static int reftable_fd_flush(void *arg)int reftable_new_stack (struct reftable_stack **dest,const char *dir,const struct reftable_write_options *_opts) { -struct reftable_stack *p =reftable_calloc (1 ,sizeof (*p));struct strbuf list_file_name =STRBUF_INIT ; -struct reftable_write_options opts = {0 }; -int err =0 ; +struct reftable_write_options opts = {0 }; +struct reftable_stack *p; +int err; + + p =reftable_calloc (1 ,sizeof (*p)); +if (!p) { + err =REFTABLE_OUT_OF_MEMORY_ERROR ; + goto out; + }if (_opts) opts = *_opts;
@@ -74,15 +80,23 @@ int reftable_new_stack(struct reftable_stack **dest, const char *dir, p->list_file =strbuf_detach (&list_file_name,NULL ); p->list_fd =-1 ; - p->reftable_dir =xstrdup (dir); p->opts = opts; + p->reftable_dir =reftable_strdup (dir); +if (!p->reftable_dir) { + err =REFTABLE_OUT_OF_MEMORY_ERROR ; + goto out; + } err =reftable_stack_reload_maybe_reuse (p,1 ); -if (err <0 ) { +if (err <0 ) + goto out; + + *dest = p; + err =0 ; + +out: +if (err <0 )reftable_stack_destroy (p); - }else { - *dest = p; - }return err; }
@@ -171,6 +185,10 @@ void reftable_stack_destroy(struct reftable_stack *st) {char **names =NULL ;int err =0 ; + +if (!st) +return ; +if (st->merged) {reftable_merged_table_free (st->merged); st->merged =NULL ;
Signature · 1 line
2.47.0.rc0.dirty
PATCH v5 15/25 reftable/stack: handle allocation failures in `stack_compact_range()`
In reply to Patrick SteinhardtHandle allocation failures in stack_compact_range().
--- reftable/stack.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-)
reftable/stack.c +17 -2
@@ -1131,6 +1131,11 @@ static int stack_compact_range(struct reftable_stack *st,* from the point of view of the newer process. */ REFTABLE_CALLOC_ARRAY (table_locks, last - first +1 ); +if (!table_locks) { + err =REFTABLE_OUT_OF_MEMORY_ERROR ; + goto done; + } +for (i = last +1 ; i > first; i--) {stack_filename (&table_name, st,reader_name (st->readers[i -1 ]));
@@ -1312,8 +1317,18 @@ static int stack_compact_range(struct reftable_stack *st,* thus have to allocate `readers_len + 1` many entries. */ REFTABLE_CALLOC_ARRAY (names, st->merged->readers_len +1 ); -for (size_t i =0 ; i < st->merged->readers_len; i++) - names[i] =xstrdup (st->readers[i]->name); +if (!names) { + err =REFTABLE_OUT_OF_MEMORY_ERROR ; + goto done; + } + +for (size_t i =0 ; i < st->merged->readers_len; i++) { + names[i] =reftable_strdup (st->readers[i]->name); +if (!names[i]) { + err =REFTABLE_OUT_OF_MEMORY_ERROR ; + goto done; + } + } first_to_replace = first; last_to_replace = last; }
Signature · 1 line
2.47.0.rc0.dirty
PATCH v5 16/25 reftable/stack: handle allocation failures in auto compaction
In reply to Patrick SteinhardtHandle allocation failures in reftable_stack_auto_compact().
--- reftable/stack.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-)
reftable/stack.c +12 -4
@@ -1518,6 +1518,8 @@ static uint64_t *stack_table_sizes_for_compaction(struct reftable_stack *st)uint64_t *sizes;REFTABLE_CALLOC_ARRAY (sizes, st->merged->readers_len); +if (!sizes) +return NULL ;for (size_t i =0 ; i < st->merged->readers_len; i++) sizes[i] = st->readers[i]->size - overhead;
@@ -1527,11 +1529,17 @@ static uint64_t *stack_table_sizes_for_compaction(struct reftable_stack *st)int reftable_stack_auto_compact (struct reftable_stack *st) { -uint64_t *sizes =stack_table_sizes_for_compaction (st); -struct segment seg = -suggest_compaction_segment (sizes, st->merged->readers_len, - st->opts.auto_compaction_factor); +struct segment seg; +uint64_t *sizes; + + sizes =stack_table_sizes_for_compaction (st); +if (!sizes) +return REFTABLE_OUT_OF_MEMORY_ERROR ; + + seg =suggest_compaction_segment (sizes, st->merged->readers_len, + st->opts.auto_compaction_factor);reftable_free (sizes); +if (segment_size (&seg) >0 )return stack_compact_range (st, seg.start, seg.end -1 ,NULL ,STACK_COMPACT_RANGE_BEST_EFFORT );
Signature · 1 line
2.47.0.rc0.dirty
PATCH v5 17/25 reftable/iter: handle allocation failures when creating indexed table iter
In reply to Patrick SteinhardtHandle allocation failures in new_indexed_table_ref_iter(). While at it, rename the function to match our coding style.
--- reftable/iter.c | 20 ++++++++++++++++---- reftable/iter.h | 2 +- reftable/reader.c | 7 ++++++- 3 files changed, 23 insertions(+), 6 deletions(-)
reftable/iter.c +16 -4
@@ -181,14 +181,20 @@ static int indexed_table_ref_iter_next(void *p, struct reftable_record *rec) } } -int new_indexed_table_ref_iter (struct indexed_table_ref_iter **dest, +int indexed_table_ref_iter_new (struct indexed_table_ref_iter **dest,struct reftable_reader *r,uint8_t *oid,int oid_len,uint64_t *offsets,int offset_len) {struct indexed_table_ref_iter empty =INDEXED_TABLE_REF_ITER_INIT ; -struct indexed_table_ref_iter *itr =reftable_calloc (1 ,sizeof (*itr)); +struct indexed_table_ref_iter *itr;int err =0 ; + itr =reftable_calloc (1 ,sizeof (*itr)); +if (!itr) { + err =REFTABLE_OUT_OF_MEMORY_ERROR ; + goto out; + } + *itr = empty; itr->r = r;strbuf_add (&itr->oid, oid, oid_len);
@@ -197,10 +203,16 @@ int new_indexed_table_ref_iter(struct indexed_table_ref_iter **dest, itr->offset_len = offset_len; err =indexed_table_ref_iter_next_block (itr); +if (err <0 ) + goto out; + + *dest = itr; + err =0 ; + +out:if (err <0 ) { + *dest =NULL ;reftable_free (itr); - }else { - *dest = itr; }return err; }
reftable/iter.h +1 -1
@@ -82,7 +82,7 @@ void iterator_from_indexed_table_ref_iter(struct reftable_iterator *it, struct indexed_table_ref_iter *itr);/* Takes ownership of `offsets` */ -int new_indexed_table_ref_iter (struct indexed_table_ref_iter **dest, +int indexed_table_ref_iter_new (struct indexed_table_ref_iter **dest,struct reftable_reader *r,uint8_t *oid,int oid_len,uint64_t *offsets,int offset_len);
reftable/reader.c +6 -1
@@ -715,7 +715,7 @@ static int reftable_reader_refs_for_indexed(struct reftable_reader *r, goto done; } - err =new_indexed_table_ref_iter (&itr, r, oid,hash_size (r->hash_id), + err =indexed_table_ref_iter_new (&itr, r, oid,hash_size (r->hash_id), got.u.obj.offsets, got.u.obj.offset_len);if (err <0 )
@@ -740,6 +740,11 @@ static int reftable_reader_refs_for_unindexed(struct reftable_reader *r,int err;REFTABLE_ALLOC_ARRAY (ti,1 ); +if (!ti) { + err =REFTABLE_OUT_OF_MEMORY_ERROR ; + goto out; + } +table_iter_init (ti, r); err =table_iter_seek_start (ti,BLOCK_TYPE_REF ,0 );if (err <0 )
Signature · 1 line
2.47.0.rc0.dirty
PATCH v5 18/25 reftable/blocksource: handle allocation failures
In reply to Patrick SteinhardtHandle allocation failures in the blocksource code.
--- reftable/blocksource.c | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-)
reftable/blocksource.c +20 -5
@@ -30,6 +30,8 @@ static int strbuf_read_block(void *v, struct reftable_block *dest, uint64_t off,struct strbuf *b = v;assert (off + size <= b->len);REFTABLE_CALLOC_ARRAY (dest->data, size); +if (!dest->data) +return -1 ;memcpy (dest->data, b->buf + off, size); dest->len = size;return size;
@@ -98,27 +100,40 @@ int reftable_block_source_from_file(struct reftable_block_source *bs, {struct file_block_source *p;struct stat st; -int fd; +int fd, err; fd =open (name,O_RDONLY );if (fd <0 ) {if (errno ==ENOENT )return REFTABLE_NOT_EXIST_ERROR ; -return -1 ; + err =-1 ; + goto out; }if (fstat (fd, &st) <0 ) { -close (fd); -return REFTABLE_IO_ERROR ; + err =REFTABLE_IO_ERROR ; + goto out; }REFTABLE_CALLOC_ARRAY (p,1 ); +if (!p) { + err =REFTABLE_OUT_OF_MEMORY_ERROR ; + goto out; + } + p->size = st.st_size; p->data =xmmap (NULL , st.st_size,PROT_READ ,MAP_PRIVATE , fd,0 ); -close (fd);assert (!bs->ops); bs->ops = &file_vtable; bs->arg = p; + + err =0 ; + +out: +if (fd >=0 ) +close (fd); +if (err <0 ) +reftable_free (p);return 0 ; }
Signature · 1 line
2.47.0.rc0.dirty
PATCH v5 19/25 reftable/block: handle allocation failures
In reply to Patrick SteinhardtHandle allocation failures in block_writer_init() and block_reader_init(). This requires us to bubble up error codes into writer_reinit_block_writer(). Adapt call sites accordingly.
--- reftable/block.c | 23 +++++++++++++-- reftable/block.h | 4 +-- reftable/writer.c | 51 ++++++++++++++++++++++----------- t/unit-tests/t-reftable-block.c | 20 +++++++------ 4 files changed, 69 insertions(+), 29 deletions(-)
reftable/block.c +21 -2
@@ -52,6 +52,8 @@ static int block_writer_register_restart(struct block_writer *w, int n,return -1 ;if (is_restart) {REFTABLE_ALLOC_GROW (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; }
@@ -63,8 +65,8 @@ static int block_writer_register_restart(struct block_writer *w, int n,return 0 ; } -void block_writer_init (struct block_writer *bw,uint8_t typ,uint8_t *buf, -uint32_t block_size,uint32_t header_off,int hash_size) +int block_writer_init (struct block_writer *bw,uint8_t typ,uint8_t *buf, +uint32_t block_size,uint32_t header_off,int hash_size) { bw->buf = buf; bw->hash_size = hash_size;
@@ -78,8 +80,12 @@ void block_writer_init(struct block_writer *bw, uint8_t typ, uint8_t *buf, bw->last_key.len =0 ;if (!bw->zstream) {REFTABLE_CALLOC_ARRAY (bw->zstream,1 ); +if (!bw->zstream) +return REFTABLE_OUT_OF_MEMORY_ERROR ;deflateInit (bw->zstream,9 ); } + +return 0 ; }uint8_t block_writer_type (struct block_writer *bw)
@@ -163,6 +169,10 @@ int block_writer_finish(struct block_writer *w)*/ compressed_len =deflateBound (w->zstream, src_len);REFTABLE_ALLOC_GROW (w->compressed, compressed_len, w->compressed_cap); +if (!w->compressed) { + ret =REFTABLE_OUT_OF_MEMORY_ERROR ; +return ret; + } w->zstream->next_out = w->compressed; w->zstream->avail_out = compressed_len;
@@ -219,12 +229,21 @@ int block_reader_init(struct block_reader *br, struct reftable_block *block,/* Log blocks specify the *uncompressed* size in their header. */ REFTABLE_ALLOC_GROW (br->uncompressed_data, sz, br->uncompressed_cap); +if (!br->uncompressed_data) { + err =REFTABLE_OUT_OF_MEMORY_ERROR ; + goto done; + }/* Copy over the block header verbatim. It's not compressed. */ memcpy (br->uncompressed_data, block->data, block_header_skip);if (!br->zstream) {REFTABLE_CALLOC_ARRAY (br->zstream,1 ); +if (!br->zstream) { + err =REFTABLE_OUT_OF_MEMORY_ERROR ; + goto done; + } + err =inflateInit (br->zstream); }else { err =inflateReset (br->zstream);
reftable/block.h +2 -2
@@ -45,8 +45,8 @@ struct block_writer {/* * initializes the blockwriter to write `typ` entries, using `buf` as temporary * storage. `buf` is not owned by the block_writer. */ -void block_writer_init (struct block_writer *bw,uint8_t typ,uint8_t *buf, -uint32_t block_size,uint32_t header_off,int hash_size); +int block_writer_init (struct block_writer *bw,uint8_t typ,uint8_t *buf, +uint32_t block_size,uint32_t header_off,int hash_size);/* returns the block type (eg. 'r' for ref records. */ uint8_t block_writer_type (struct block_writer *bw);
reftable/writer.c +34 -17
98 lines not on this page · raw message
t/unit-tests/t-reftable-block.c +12 -8
@@ -34,8 +34,9 @@ static void t_ref_block_read_write(void)REFTABLE_CALLOC_ARRAY (block.data, block_size); block.len = block_size;block_source_from_strbuf (&block.source ,&buf); -block_writer_init (&bw,BLOCK_TYPE_REF , block.data, block_size, - header_off,hash_size (GIT_SHA1_FORMAT_ID )); + ret =block_writer_init (&bw,BLOCK_TYPE_REF , block.data, block_size, + header_off,hash_size (GIT_SHA1_FORMAT_ID )); +check (!ret); rec.u.ref.refname = (char *)"" ; rec.u.ref.value_type =REFTABLE_REF_DELETION ;
@@ -126,8 +127,9 @@ static void t_log_block_read_write(void)REFTABLE_CALLOC_ARRAY (block.data, block_size); block.len = block_size;block_source_from_strbuf (&block.source ,&buf); -block_writer_init (&bw,BLOCK_TYPE_LOG , block.data, block_size, - header_off,hash_size (GIT_SHA1_FORMAT_ID )); + ret =block_writer_init (&bw,BLOCK_TYPE_LOG , block.data, block_size, + header_off,hash_size (GIT_SHA1_FORMAT_ID )); +check (!ret);for (i =0 ; i <N ; i++) { rec.u.log.refname =xstrfmt ("branch%02" PRIuMAX , (uintmax_t )i);
@@ -214,8 +216,9 @@ static void t_obj_block_read_write(void)REFTABLE_CALLOC_ARRAY (block.data, block_size); block.len = block_size;block_source_from_strbuf (&block.source, &buf); -block_writer_init (&bw,BLOCK_TYPE_OBJ , block.data, block_size, - header_off,hash_size (GIT_SHA1_FORMAT_ID )); + ret =block_writer_init (&bw,BLOCK_TYPE_OBJ , block.data, block_size, + header_off,hash_size (GIT_SHA1_FORMAT_ID )); +check (!ret);for (i =0 ; i <N ; i++) {uint8_t bytes[] = { i, i +1 , i +2 , i +3 , i +5 }, *allocated;
@@ -296,8 +299,9 @@ static void t_index_block_read_write(void)REFTABLE_CALLOC_ARRAY (block.data, block_size); block.len = block_size;block_source_from_strbuf (&block.source, &buf); -block_writer_init (&bw,BLOCK_TYPE_INDEX , block.data, block_size, - header_off,hash_size (GIT_SHA1_FORMAT_ID )); + ret =block_writer_init (&bw,BLOCK_TYPE_INDEX , block.data, block_size, + header_off,hash_size (GIT_SHA1_FORMAT_ID )); +check (!ret);for (i =0 ; i <N ; i++) {strbuf_init (&recs[i].u.idx.last_key,9 );
Signature · 1 line
2.47.0.rc0.dirty
PATCH v5 20/25 reftable/pq: handle allocation failures when adding entries
In reply to Patrick SteinhardtHandle allocation failures when adding entries to the pqueue. Adapt its only caller accordingly.
--- reftable/merged.c | 5 ++++- reftable/pq.c | 7 ++++++- reftable/pq.h | 2 +- 3 files changed, 11 insertions(+), 3 deletions(-)
reftable/merged.c +4 -1
@@ -54,7 +54,10 @@ static int merged_iter_advance_subiter(struct merged_iter *mi, size_t idx)if (err)return err; -merged_iter_pqueue_add (&mi->pq, &e); + err =merged_iter_pqueue_add (&mi->pq, &e); +if (err) +return err; +return 0 ; }
reftable/pq.c +6 -1
@@ -8,6 +8,7 @@ license that can be found in the LICENSE file or at#include "pq.h" +#include "reftable-error.h" #include "reftable-record.h" #include "system.h" #include "basics.h"
@@ -44,11 +45,13 @@ struct pq_entry merged_iter_pqueue_remove(struct merged_iter_pqueue *pq)return e; } -void merged_iter_pqueue_add (struct merged_iter_pqueue *pq,const struct pq_entry *e) +int merged_iter_pqueue_add (struct merged_iter_pqueue *pq,const struct pq_entry *e) {size_t i =0 ;REFTABLE_ALLOC_GROW (pq->heap, pq->len +1 , pq->cap); +if (!pq->heap) +return REFTABLE_OUT_OF_MEMORY_ERROR ; pq->heap[pq->len++] = *e; i = pq->len -1 ;
@@ -59,6 +62,8 @@ void merged_iter_pqueue_add(struct merged_iter_pqueue *pq, const struct pq_entrySWAP (pq->heap[j], pq->heap[i]); i = j; } + +return 0 ; }void merged_iter_pqueue_release (struct merged_iter_pqueue *pq)
reftable/pq.h +1 -1
@@ -23,7 +23,7 @@ struct merged_iter_pqueue { };struct pq_entry merged_iter_pqueue_remove (struct merged_iter_pqueue *pq); -void merged_iter_pqueue_add (struct merged_iter_pqueue *pq,const struct pq_entry *e); +int merged_iter_pqueue_add (struct merged_iter_pqueue *pq,const struct pq_entry *e);void merged_iter_pqueue_release (struct merged_iter_pqueue *pq);int pq_less (struct pq_entry *a,struct pq_entry *b);
Signature · 1 line
2.47.0.rc0.dirty
PATCH v5 21/25 reftable/tree: handle allocation failures
In reply to Patrick SteinhardtThe tree interfaces of the reftable library handle both insertion and searching of tree nodes with a single function, where the behaviour is altered between the two via an insert bit. This makes it quit awkward to handle allocation failures because on inserting we'd have to check for NULL pointers and return an error, whereas on searching entries we don't have to handle it as an allocation error.
Split up concerns of this function into two separate functions, one for inserting entries and one for searching entries. This makes it easy for us to check for allocation errors as tree_insert() should never return a NULL pointer now. Adapt callers accordingly.
--- reftable/tree.c | 42 +++++++++++++++++++++++----------- reftable/tree.h | 21 +++++++++++++---- reftable/writer.c | 7 +++--- t/unit-tests/t-reftable-tree.c | 10 ++++---- 4 files changed, 54 insertions(+), 26 deletions(-)
reftable/tree.c +29 -13
@@ -11,28 +11,44 @@ license that can be found in the LICENSE file or at#include "basics.h" -struct tree_node *tree_search (void *key,struct tree_node **rootp, -int (*compare)(const void *,const void *), -int insert) +struct tree_node *tree_search (struct tree_node *tree, +void *key, +int (*compare)(const void *,const void *)) {int res; +if (!tree) +return NULL ; + res =compare (key, tree->key); +if (res <0 ) +return tree_search (tree->left, key, compare); +else if (res >0 ) +return tree_search (tree->right, key, compare); +return tree; +} + +struct tree_node *tree_insert (struct tree_node **rootp, +void *key, +int (*compare)(const void *,const void *)) +{ +int res; +if (!*rootp) { -if (!insert) { +struct tree_node *n; + +REFTABLE_CALLOC_ARRAY (n,1 ); +if (!n)return NULL ; - }else { -struct tree_node *n; -REFTABLE_CALLOC_ARRAY (n,1 ); - n->key = key; - *rootp = n; -return *rootp; - } + + n->key = key; + *rootp = n; +return *rootp; } res =compare (key, (*rootp)->key);if (res <0 ) -return tree_search (key, &(*rootp)->left, compare, insert); +return tree_insert (&(*rootp)->left, key, compare);else if (res >0 ) -return tree_search (key, &(*rootp)->right, compare, insert); +return tree_insert (&(*rootp)->right, key, compare);return *rootp; }
reftable/tree.h +16 -5
@@ -15,12 +15,23 @@ struct tree_node {struct tree_node *left, *right; }; -/* looks for `key` in `rootp` using `compare` as comparison function. If insert -* is set, insert the key if it's not found. Else, return NULL. +/* +* Search the tree for the node matching the given key using `compare` as +* comparison function. Returns the node whose key matches or `NULL` in case +* the key does not exist in the tree. +*/ +struct tree_node *tree_search (struct tree_node *tree, +void *key, +int (*compare)(const void *,const void *)); + +/* +* Insert a node into the tree. Returns the newly inserted node if the key does +* not yet exist. Otherwise it returns the preexisting node. Returns `NULL` +* when allocating the new node fails. */ -struct tree_node *tree_search (void *key,struct tree_node **rootp, -int (*compare)(const void *,const void *), -int insert); +struct tree_node *tree_insert (struct tree_node **rootp, +void *key, +int (*compare)(const void *,const void *));/* performs an infix walk of the tree. */ void infix_walk(struct tree_node *t, void (*action)(void *arg, void *key),
reftable/writer.c +3 -4
@@ -208,8 +208,7 @@ static int writer_index_hash(struct reftable_writer *w, struct strbuf *hash)struct obj_index_tree_node *key;struct tree_node *node; - node =tree_search (&want, &w->obj_index_tree, - &obj_index_tree_node_compare,0 ); + node =tree_search (w->obj_index_tree, &want, &obj_index_tree_node_compare);if (!node) {struct obj_index_tree_node empty =OBJ_INDEX_TREE_NODE_INIT ;
@@ -221,8 +220,8 @@ static int writer_index_hash(struct reftable_writer *w, struct strbuf *hash)strbuf_reset (&key->hash);strbuf_addbuf (&key->hash, hash); -tree_search ((void *)key, &w->obj_index_tree, - &obj_index_tree_node_compare,1 ); +tree_insert (&w->obj_index_tree, key, + &obj_index_tree_node_compare); }else { key = node->key; }
t/unit-tests/t-reftable-tree.c +6 -4
@@ -37,16 +37,17 @@ static void t_tree_search(void)* values[1] and values[10] (inclusive) in the tree. */ do { - nodes[i] =tree_search (&values[i], &root, &t_compare,1 ); + nodes[i] =tree_insert (&root, &values[i], &t_compare); +check (nodes[i] !=NULL ); i = (i *7 ) %11 ; }while (i !=1 );for (i =1 ; i <ARRAY_SIZE (nodes); i++) {check_pointer_eq (&values[i], nodes[i]->key); -check_pointer_eq (nodes[i],tree_search (&values[i], &root, &t_compare,0 )); +check_pointer_eq (nodes[i],tree_search (root, &values[i], &t_compare)); } -check (!tree_search (values, &root, t_compare,0 )); +check (!tree_search (root, values, t_compare));tree_free (root); }
@@ -62,7 +63,8 @@ static void t_infix_walk(void)size_t count =0 ;do { -tree_search (&values[i], &root, t_compare,1 ); +struct tree_node *node =tree_insert (&root, &values[i], t_compare); +check (node !=NULL ); i = (i *7 ) %11 ; count++; }while (i !=1 );
Signature · 1 line
2.47.0.rc0.dirty
PATCH v5 22/25 reftable: handle trivial allocation failures
In reply to Patrick SteinhardtHandle trivial allocation failures in the reftable library and its unit tests.
--- reftable/merged.c | 3 +++ reftable/reader.c | 10 +++++++++- reftable/stack.c | 20 ++++++++++++++++++++ reftable/writer.c | 13 +++++++++++-- t/unit-tests/t-reftable-block.c | 4 ++++ t/unit-tests/t-reftable-merged.c | 4 ++++ t/unit-tests/t-reftable-readwrite.c | 27 ++++++++++++++++----------- 7 files changed, 67 insertions(+), 14 deletions(-)
reftable/merged.c +3 -0
@@ -203,6 +203,9 @@ int reftable_merged_table_new(struct reftable_merged_table **dest, }REFTABLE_CALLOC_ARRAY (m,1 ); +if (!m) +return REFTABLE_OUT_OF_MEMORY_ERROR ; + m->readers = readers; m->readers_len = n; m->min = first_min;
reftable/reader.c +9 -1
@@ -598,6 +598,10 @@ int reftable_reader_new(struct reftable_reader **out,int err;REFTABLE_CALLOC_ARRAY (r,1 ); +if (!r) { + err =REFTABLE_OUT_OF_MEMORY_ERROR ; + goto done; + }/* * We need one extra byte to read the type of first block. We also
@@ -627,7 +631,11 @@ int reftable_reader_new(struct reftable_reader **out, r->size = file_size -footer_size (r->version); r->source = *source; - r->name =xstrdup (name); + r->name =reftable_strdup (name); +if (!r->name) { + err =REFTABLE_OUT_OF_MEMORY_ERROR ; + goto done; + } r->hash_id =0 ; r->refcount =1 ;
reftable/stack.c +20 -0
@@ -116,6 +116,11 @@ static int fd_read_lines(int fd, char ***namesp) }REFTABLE_ALLOC_ARRAY (buf, size +1 ); +if (!buf) { + err =REFTABLE_OUT_OF_MEMORY_ERROR ; + goto done; + } +if (read_in_full (fd, buf, size) != size) { err =REFTABLE_IO_ERROR ; goto done;
@@ -140,6 +145,8 @@ int read_lines(const char *filename, char ***namesp)if (fd <0 ) {if (errno ==ENOENT ) {REFTABLE_CALLOC_ARRAY (*namesp,1 ); +if (!*namesp) +return REFTABLE_OUT_OF_MEMORY_ERROR ;return 0 ; }
@@ -420,6 +427,10 @@ static int reftable_stack_reload_maybe_reuse(struct reftable_stack *st, }REFTABLE_CALLOC_ARRAY (names,1 ); +if (!names) { + err =REFTABLE_OUT_OF_MEMORY_ERROR ; + goto out; + } }else { err =fd_read_lines (fd, &names);if (err <0 )
@@ -779,7 +790,11 @@ int reftable_stack_new_addition(struct reftable_addition **dest, {int err =0 ;struct reftable_addition empty =REFTABLE_ADDITION_INIT ; +REFTABLE_CALLOC_ARRAY (*dest,1 ); +if (!*dest) +return REFTABLE_OUT_OF_MEMORY_ERROR ; + **dest = empty; err =reftable_stack_init_addition (*dest, st);if (err) {
@@ -886,7 +901,12 @@ int reftable_addition_add(struct reftable_addition *add,REFTABLE_ALLOC_GROW (add->new_tables, add->new_tables_len +1 , add->new_tables_cap); +if (!add->new_tables) { + err =REFTABLE_OUT_OF_MEMORY_ERROR ; + goto done; + } add->new_tables[add->new_tables_len++] =strbuf_detach (&next_name,NULL ); + done:delete_tempfile (&tab_file);strbuf_release (&temp_tab_file_name);
reftable/writer.c +11 -2
@@ -49,8 +49,14 @@ static int padded_write(struct reftable_writer *w, uint8_t *data, size_t len, {int n =0 ;if (w->pending_padding >0 ) { -uint8_t *zeroed =reftable_calloc (w->pending_padding,sizeof (*zeroed)); -int n = w->write (w->write_arg, zeroed, w->pending_padding); +uint8_t *zeroed; +int n; + + zeroed =reftable_calloc (w->pending_padding,sizeof (*zeroed)); +if (!zeroed) +return -1 ; + + n = w->write (w->write_arg, zeroed, w->pending_padding);if (n <0 )return n;
@@ -767,6 +773,9 @@ static int writer_flush_nonempty_block(struct reftable_writer *w)* case we will end up with a multi-level index. */ REFTABLE_ALLOC_GROW (w->index, w->index_len +1 , w->index_cap); +if (!w->index) +return REFTABLE_OUT_OF_MEMORY_ERROR ; + index_record.offset = w->next;strbuf_reset (&index_record.last_key);strbuf_addbuf (&index_record.last_key, &w->block_writer->last_key);
t/unit-tests/t-reftable-block.c +4 -0
@@ -32,6 +32,7 @@ static void t_ref_block_read_write(void)struct strbuf want =STRBUF_INIT , buf =STRBUF_INIT ;REFTABLE_CALLOC_ARRAY (block.data, block_size); +check (block.data !=NULL ); block.len = block_size;block_source_from_strbuf (&block.source ,&buf); ret = block_writer_init(&bw, BLOCK_TYPE_REF, block.data, block_size,
@@ -125,6 +126,7 @@ static void t_log_block_read_write(void)struct strbuf want =STRBUF_INIT , buf =STRBUF_INIT ;REFTABLE_CALLOC_ARRAY (block.data, block_size); +check (block.data !=NULL ); block.len = block_size;block_source_from_strbuf (&block.source ,&buf); ret = block_writer_init(&bw, BLOCK_TYPE_LOG, block.data, block_size,
@@ -214,6 +216,7 @@ static void t_obj_block_read_write(void)struct strbuf want =STRBUF_INIT , buf =STRBUF_INIT ;REFTABLE_CALLOC_ARRAY (block.data, block_size); +check (block.data !=NULL ); block.len = block_size;block_source_from_strbuf (&block.source, &buf); ret = block_writer_init(&bw, BLOCK_TYPE_OBJ, block.data, block_size,
@@ -297,6 +300,7 @@ static void t_index_block_read_write(void)struct strbuf want =STRBUF_INIT , buf =STRBUF_INIT ;REFTABLE_CALLOC_ARRAY (block.data, block_size); +check (block.data !=NULL ); block.len = block_size;block_source_from_strbuf (&block.source, &buf); ret = block_writer_init(&bw, BLOCK_TYPE_INDEX, block.data, block_size,
t/unit-tests/t-reftable-merged.c +4 -0
@@ -29,7 +29,9 @@ merged_table_from_records(struct reftable_ref_record **refs,int err;REFTABLE_CALLOC_ARRAY (*readers, n); +check (*readers !=NULL );REFTABLE_CALLOC_ARRAY (*source, n); +check (*source !=NULL );for (size_t i =0 ; i < n; i++) {t_reftable_write_to_buf (&buf[i], refs[i], sizes[i],NULL ,0 , &opts);
@@ -285,7 +287,9 @@ merged_table_from_log_records(struct reftable_log_record **logs,int err;REFTABLE_CALLOC_ARRAY (*readers, n); +check (*readers !=NULL );REFTABLE_CALLOC_ARRAY (*source, n); +check (*source !=NULL );for (size_t i =0 ; i < n; i++) {t_reftable_write_to_buf (&buf[i],NULL ,0 , logs[i], sizes[i], &opts);
t/unit-tests/t-reftable-readwrite.c +16 -11
@@ -52,8 +52,11 @@ static void write_table(char ***names, struct strbuf *buf, int N,int i;REFTABLE_CALLOC_ARRAY (*names,N +1 ); +check (*names !=NULL );REFTABLE_CALLOC_ARRAY (refs,N ); +check (refs !=NULL );REFTABLE_CALLOC_ARRAY (logs,N ); +check (logs !=NULL );for (i =0 ; i <N ; i++) { refs[i].refname = (*names)[i] =xstrfmt ("refs/heads/branch%02d" , i);
@@ -150,23 +153,25 @@ static void t_log_overflow(void)static void t_log_write_read (void ) { -int N =2 ; -char **names =reftable_calloc (N +1 ,sizeof (*names)); -int err;struct reftable_write_options opts = { .block_size =256 , };struct reftable_ref_record ref = {0 }; -int i =0 ;struct reftable_log_record log = {0 }; -int n;struct reftable_iterator it = {0 };struct reftable_reader *reader;struct reftable_block_source source = {0 };struct strbuf buf =STRBUF_INIT ;struct reftable_writer *w =t_reftable_strbuf_writer (&buf, &opts);const struct reftable_stats *stats =NULL ; +int N =2 , err, i, n; +char **names; + + names =reftable_calloc (N +1 ,sizeof (*names)); +check (names !=NULL ); +reftable_writer_set_limits (w,0 ,N ); +for (i =0 ; i <N ; i++) {char name[256 ];struct reftable_ref_record ref = {0 };
@@ -178,6 +183,7 @@ static void t_log_write_read(void) err =reftable_writer_add_ref (w, &ref);check (!err); } +for (i =0 ; i <N ; i++) {struct reftable_log_record log = {0 };
@@ -476,8 +482,7 @@ static void t_table_read_write_seek_index(void)static void t_table_refs_for (int indexed) { -int N =50 ; -char **want_names =reftable_calloc (N +1 ,sizeof (*want_names)); +char **want_names;int want_names_len =0 ;uint8_t want_hash[GIT_SHA1_RAWSZ ];
@@ -485,15 +490,15 @@ static void t_table_refs_for(int indexed) .block_size = 256, };struct reftable_ref_record ref = {0 }; -int i =0 ; -int n; -int err;struct reftable_reader *reader;struct reftable_block_source source = {0 };struct strbuf buf =STRBUF_INIT ;struct reftable_writer *w =t_reftable_strbuf_writer (&buf, &opts);struct reftable_iterator it = {0 }; -int j; +int N =50 , n, j, err, i; + + want_names =reftable_calloc (N +1 ,sizeof (*want_names)); +check (want_names !=NULL );t_reftable_set_hash (want_hash,4 ,GIT_SHA1_FORMAT_ID );
Signature · 1 line
2.47.0.rc0.dirty
PATCH v5 23/25 reftable: fix calls to free(3P)
In reply to Patrick SteinhardtThere are a small set of calls to free(3P) in the reftable library. As the reftable allocators are pluggable we should rather call the reftable specific function, which is reftable_free().
Convert the code accordingly.
--- reftable/stack.c | 2 +- t/unit-tests/t-reftable-readwrite.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-)
reftable/stack.c +1 -1
@@ -1421,7 +1421,7 @@ static int stack_compact_range(struct reftable_stack *st,struct lock_file *table_lock = &table_locks[i];char *table_path =get_locked_file_path (table_lock);unlink (table_path); -free (table_path); +reftable_free (table_path); } done:
t/unit-tests/t-reftable-readwrite.c +2 -2
@@ -76,8 +76,8 @@ static void write_table(char ***names, struct strbuf *buf, int N,t_reftable_write_to_buf (buf, refs,N , logs,N , &opts); -free (refs); -free (logs); +reftable_free (refs); +reftable_free (logs); }static void t_log_buffer_size (void )
Signature · 1 line
2.47.0.rc0.dirty
PATCH v5 24/25 reftable: introduce `REFTABLE_FREE_AND_NULL()`
In reply to Patrick SteinhardtWe have several calls to FREE_AND_NULL() in the reftable library, which of course uses free(3P). As the reftable allocators are pluggable we should rather call the reftable specific function, which is reftable_free().
Introduce a new macro REFTABLE_FREE_AND_NULL() and adapt the callsites accordingly.
--- reftable/basics.h | 1 + reftable/block.c | 6 +++--- reftable/iter.c | 2 +- reftable/pq.c | 2 +- reftable/reader.c | 2 +- reftable/record.c | 10 +++++----- reftable/stack.c | 8 ++++---- reftable/writer.c | 4 ++-- t/unit-tests/t-reftable-stack.c | 2 +- 9 files changed, 19 insertions(+), 18 deletions(-)
reftable/basics.h +1 -0
@@ -71,6 +71,7 @@ char *reftable_strdup(const char *str);REFTABLE_REALLOC_ARRAY (x, alloc); \ } \ } while (0) +#define REFTABLE_FREE_AND_NULL (p) do { reftable_free(p); (p) = NULL; } while (0)/* Find the longest shared prefix size of `a` and `b` */ struct strbuf ;
reftable/block.c +3 -3
@@ -551,9 +551,9 @@ int block_iter_seek_key(struct block_iter *it, const struct block_reader *br,void block_writer_release (struct block_writer *bw) {deflateEnd (bw->zstream); -FREE_AND_NULL (bw->zstream); -FREE_AND_NULL (bw->restarts); -FREE_AND_NULL (bw->compressed); +REFTABLE_FREE_AND_NULL (bw->zstream); +REFTABLE_FREE_AND_NULL (bw->restarts); +REFTABLE_FREE_AND_NULL (bw->compressed);strbuf_release (&bw->last_key);/* the block is not owned. */ }
reftable/iter.c +1 -1
@@ -237,7 +237,7 @@ void reftable_iterator_destroy(struct reftable_iterator *it)return ; it->ops->close (it->iter_arg); it->ops =NULL ; -FREE_AND_NULL (it->iter_arg); +REFTABLE_FREE_AND_NULL (it->iter_arg); } int reftable_iterator_seek_ref(struct reftable_iterator *it,
reftable/pq.c +1 -1
@@ -68,6 +68,6 @@ int merged_iter_pqueue_add(struct merged_iter_pqueue *pq, const struct pq_entryvoid merged_iter_pqueue_release (struct merged_iter_pqueue *pq) { -FREE_AND_NULL (pq->heap); +REFTABLE_FREE_AND_NULL (pq->heap);memset (pq,0 ,sizeof (*pq)); }
reftable/reader.c +1 -1
@@ -678,7 +678,7 @@ void reftable_reader_decref(struct reftable_reader *r)if (--r->refcount)return ;block_source_close (&r->source); -FREE_AND_NULL (r->name); +REFTABLE_FREE_AND_NULL (r->name);reftable_free (r); }
reftable/record.c +5 -5
@@ -476,8 +476,8 @@ static void reftable_obj_record_key(const void *r, struct strbuf *dest)static void reftable_obj_record_release (void *rec) {struct reftable_obj_record *obj = rec; -FREE_AND_NULL (obj->hash_prefix); -FREE_AND_NULL (obj->offsets); +REFTABLE_FREE_AND_NULL (obj->hash_prefix); +REFTABLE_FREE_AND_NULL (obj->offsets);memset (obj,0 ,sizeof (struct reftable_obj_record )); }
@@ -834,10 +834,10 @@ static int reftable_log_record_decode(void *rec, struct strbuf key,if (val_type != r->value_type) {switch (r->value_type) {case REFTABLE_LOG_UPDATE : -FREE_AND_NULL (r->value.update.message); +REFTABLE_FREE_AND_NULL (r->value.update.message); r->value.update.message_cap =0 ; -FREE_AND_NULL (r->value.update.email); -FREE_AND_NULL (r->value.update.name); +REFTABLE_FREE_AND_NULL (r->value.update.email); +REFTABLE_FREE_AND_NULL (r->value.update.name);break ;case REFTABLE_LOG_DELETION :break ;
reftable/stack.c +4 -4
@@ -203,7 +203,7 @@ void reftable_stack_destroy(struct reftable_stack *st) err =read_lines (st->list_file, &names);if (err <0 ) { -FREE_AND_NULL (names); +REFTABLE_FREE_AND_NULL (names); }if (st->readers) {
@@ -224,7 +224,7 @@ void reftable_stack_destroy(struct reftable_stack *st) }strbuf_release (&filename); st->readers_len =0 ; -FREE_AND_NULL (st->readers); +REFTABLE_FREE_AND_NULL (st->readers); }if (st->list_fd >=0 ) {
@@ -232,8 +232,8 @@ void reftable_stack_destroy(struct reftable_stack *st) st->list_fd =-1 ; } -FREE_AND_NULL (st->list_file); -FREE_AND_NULL (st->reftable_dir); +REFTABLE_FREE_AND_NULL (st->list_file); +REFTABLE_FREE_AND_NULL (st->reftable_dir);reftable_free (st);free_names (names); }
reftable/writer.c +2 -2
@@ -593,7 +593,7 @@ static void object_record_free(void *void_arg UNUSED, void *key) {struct obj_index_tree_node *entry = key; -FREE_AND_NULL (entry->offsets); +REFTABLE_FREE_AND_NULL (entry->offsets);strbuf_release (&entry->hash);reftable_free (entry); }
@@ -709,7 +709,7 @@ static void writer_clear_index(struct reftable_writer *w) {for (size_t i =0 ; w->index && i < w->index_len; i++)strbuf_release (&w->index[i].last_key); -FREE_AND_NULL (w->index); +REFTABLE_FREE_AND_NULL (w->index); w->index_len =0 ; w->index_cap =0 ; }
t/unit-tests/t-reftable-stack.c +1 -1
@@ -1149,7 +1149,7 @@ static void unclean_stack_close(struct reftable_stack *st)for (size_t i =0 ; i < st->readers_len; i++)reftable_reader_decref (st->readers[i]); st->readers_len =0 ; -FREE_AND_NULL (st->readers); +REFTABLE_FREE_AND_NULL (st->readers); }static void t_reftable_stack_compaction_concurrent_clean (void )
Signature · 1 line
2.47.0.rc0.dirty
PATCH v5 25/25 reftable/basics: ban standard allocator functions
In reply to Patrick SteinhardtThe reftable library uses pluggable allocators, which means that we shouldn't ever use the standard allocator functions. But it is an easy mistake to make to accidentally use e.g. free(3P) instead of the reftable-specific reftable_free() function, and we do not have any mechanism to detect this misuse right now.
Introduce a couple of macros that ban the standard allocators, similar to how we do it in "banned.h".
Note that we do not ban the following two classes of functions:
- Macros like FREE_AND_NULL() or REALLOC_ARRAY(). As those expand to code that contains already-banned functions we'd get a compiler error even without banning those macros explicitly.
- Git-specific allocators like xmalloc() and friends. The primary reason is that there are simply too many of them, so we're rather aiming for best effort here. Furthermore, the eventual goal is to make them unavailable in the reftable library place by not pulling them in via "git-compat-utils.h" anymore.
--- reftable/basics.c | 1 + reftable/basics.h | 14 ++++++++++++++ 2 files changed, 15 insertions(+)
reftable/basics.c +1 -0
@@ -6,6 +6,7 @@ license that can be found in the LICENSE file or athttps://developers.google.com/open-source/licenses/bsd */ +#define REFTABLE_ALLOW_BANNED_ALLOCATORS #include "basics.h" #include "reftable-basics.h"
reftable/basics.h +14 -0
@@ -73,6 +73,20 @@ char *reftable_strdup(const char *str); } while (0)#define REFTABLE_FREE_AND_NULL (p) do { reftable_free(p); (p) = NULL; } while (0) +#ifndef REFTABLE_ALLOW_BANNED_ALLOCATORS +# define REFTABLE_BANNED (func) use_reftable_##func##_instead +# undef malloc +# define malloc (sz) REFTABLE_BANNED(malloc) +# undef realloc +# define realloc (ptr, sz) REFTABLE_BANNED(realloc) +# undef free +# define free (ptr) REFTABLE_BANNED(free) +# undef calloc +# define calloc (nelem, elsize) REFTABLE_BANNED(calloc) +# undef strdup +# define strdup (str) REFTABLE_BANNED(strdup) +#endif +/* Find the longest shared prefix size of `a` and `b` */ struct strbuf ;int common_prefix_size (struct strbuf *a,struct strbuf *b);
Signature · 1 line
2.47.0.rc0.dirty
Quoted 6 lines · Patrick Steinhardt
Changes compared to v4:
-
#undefstandard allocators before banning them.- Explain why we don't ban
FREE_AND_NULL()and related as well asxmalloc()and related.
Looking good.
Let me mark the topic for 'next' so that it can graduate in the first batch after the upcoming release.
Thanks.