* [PATCH 00/22] reftable: handle allocation errors
@ 2024-09-16 12:28 Patrick Steinhardt
2024-09-16 12:28 ` [PATCH 01/22] reftable/error: introduce out-of-memory error code Patrick Steinhardt
` (25 more replies)
0 siblings, 26 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-16 12:28 UTC (permalink / raw)
To: git; +Cc: Edward Thomson
Hi,
the reftable library provides pluggable allocators that can be switched
out via `reftable_set_alloc()`. These pluggable allocators are not
expected to behave like `xmalloc()` and friends -- in other words, they
are allowed to fail. This is done such that the reftable library can
truly behave like a library and let its callers handle such conditions.
It has been true since the inception of the reftable library that
allocations may fail. But regardless of that we do not have any error
handling for this scenario at all. Consequently, we can easily cause a
segfault when running into an out-of-memory situation.
While the easy solution would be to plug in `xmalloc()` and friends such
that we abort on these cases, that would contradict the very notion of
what this library should be. Most importantly, I want to soonish revive
the effort to implement a reftable backend in libgit2, and thus I have
to make sure that the reftable library (of which we are the canonical
upstream now) can be used in such contexts.
This patch series here is the first one in a set of series that will
detangle the reftable library from the rest of the Git codebase again so
that it becomes a true standalone library that other projects can use.
The series is based on top of ed155187b4 (Sync with Git 2.46.1,
2024-09-13) with the following two depedencies merged into it to avoid
conflicts:
- cp/unit-test-reftable-stack at 2b14ced370 (t-reftable-stack: add
test for stack iterators, 2024-09-08).
- ps/reftable-exclude at adf7a0ceef (refs/reftable: wire up support
for exclude patterns, 2024-09-09).
Thanks!
Patrick
Patrick Steinhardt (22):
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
Makefile | 1 -
refs/reftable-backend.c | 39 ++++--
reftable/basics.c | 87 +++++++++++++-
reftable/basics.h | 13 +-
reftable/block.c | 23 +++-
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 | 7 +-
reftable/pq.h | 2 +-
reftable/publicbasics.c | 66 -----------
reftable/reader.c | 68 ++++++++---
reftable/reader.h | 6 +-
reftable/record.c | 164 +++++++++++++++++++-------
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 | 177 ++++++++++++++++++++++------
reftable/tree.c | 42 +++++--
reftable/tree.h | 21 +++-
reftable/writer.c | 150 +++++++++++++++--------
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 | 61 ++++++----
t/unit-tests/t-reftable-stack.c | 4 +-
t/unit-tests/t-reftable-tree.c | 10 +-
38 files changed, 848 insertions(+), 388 deletions(-)
delete mode 100644 reftable/publicbasics.c
create mode 100644 reftable/reftable-basics.h
delete mode 100644 reftable/reftable-malloc.h
base-commit: 1755182f5a2e12ff27c230f34f0040a8afee58e4
--
2.46.0.551.gc5ee8f2d1c.dirty
^ permalink raw reply [flat|nested] 152+ messages in thread
* [PATCH 01/22] reftable/error: introduce out-of-memory error code
2024-09-16 12:28 [PATCH 00/22] reftable: handle allocation errors Patrick Steinhardt
@ 2024-09-16 12:28 ` Patrick Steinhardt
2024-09-16 12:28 ` [PATCH 02/22] reftable/basics: merge "publicbasics" into "basics" Patrick Steinhardt
` (24 subsequent siblings)
25 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-16 12:28 UTC (permalink / raw)
To: git; +Cc: Edward Thomson
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.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/error.c | 2 ++
reftable/reftable-error.h | 3 +++
2 files changed, 5 insertions(+)
diff --git a/reftable/error.c b/reftable/error.c
index a25f28a43eb..660d0296170 100644
--- a/reftable/error.c
+++ b/reftable/error.c
@@ -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:
diff --git a/reftable/reftable-error.h b/reftable/reftable-error.h
index 6368cd9ed9d..f4048265629 100644
--- a/reftable/reftable-error.h
+++ b/reftable/reftable-error.h
@@ -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
--
2.46.0.551.gc5ee8f2d1c.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH 02/22] reftable/basics: merge "publicbasics" into "basics"
2024-09-16 12:28 [PATCH 00/22] reftable: handle allocation errors Patrick Steinhardt
2024-09-16 12:28 ` [PATCH 01/22] reftable/error: introduce out-of-memory error code Patrick Steinhardt
@ 2024-09-16 12:28 ` Patrick Steinhardt
2024-09-16 12:28 ` [PATCH 03/22] reftable: introduce `reftable_strdup()` Patrick Steinhardt
` (23 subsequent siblings)
25 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-16 12:28 UTC (permalink / raw)
To: git; +Cc: Edward Thomson
The 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.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
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
diff --git a/Makefile b/Makefile
index e3abf0ba831..39b10923d49 100644
--- a/Makefile
+++ b/Makefile
@@ -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
diff --git a/reftable/basics.c b/reftable/basics.c
index 0058619ca67..cf072935c88 100644
--- a/reftable/basics.c
+++ b/reftable/basics.c
@@ -7,6 +7,49 @@ license that can be found in the LICENSE file or at
*/
#include "basics.h"
+#include "reftable-basics.h"
+
+static void *(*reftable_malloc_ptr)(size_t sz);
+static void *(*reftable_realloc_ptr)(void *, size_t);
+static void (*reftable_free_ptr)(void *);
+
+void *reftable_malloc(size_t sz)
+{
+ if (reftable_malloc_ptr)
+ return (*reftable_malloc_ptr)(sz);
+ return malloc(sz);
+}
+
+void *reftable_realloc(void *p, size_t sz)
+{
+ if (reftable_realloc_ptr)
+ return (*reftable_realloc_ptr)(p, sz);
+ return realloc(p, sz);
+}
+
+void reftable_free(void *p)
+{
+ if (reftable_free_ptr)
+ reftable_free_ptr(p);
+ else
+ free(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);
+ return p;
+}
+
+void reftable_set_alloc(void *(*malloc)(size_t),
+ void *(*realloc)(void *, size_t), void (*free)(void *))
+{
+ reftable_malloc_ptr = malloc;
+ reftable_realloc_ptr = realloc;
+ reftable_free_ptr = free;
+}
void put_be24(uint8_t *out, uint32_t i)
{
@@ -121,3 +164,15 @@ int common_prefix_size(struct strbuf *a, struct strbuf *b)
return p;
}
+
+int hash_size(uint32_t id)
+{
+ switch (id) {
+ case 0:
+ case GIT_SHA1_FORMAT_ID:
+ return GIT_SHA1_RAWSZ;
+ case GIT_SHA256_FORMAT_ID:
+ return GIT_SHA256_RAWSZ;
+ }
+ abort();
+}
diff --git a/reftable/basics.h b/reftable/basics.h
index c8fec68d4e8..4e2e76014aa 100644
--- a/reftable/basics.h
+++ b/reftable/basics.h
@@ -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
diff --git a/reftable/publicbasics.c b/reftable/publicbasics.c
deleted file mode 100644
index 44b84a125e4..00000000000
--- a/reftable/publicbasics.c
+++ /dev/null
@@ -1,66 +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
-*/
-
-#include "system.h"
-#include "reftable-malloc.h"
-
-#include "basics.h"
-
-static void *(*reftable_malloc_ptr)(size_t sz);
-static void *(*reftable_realloc_ptr)(void *, size_t);
-static void (*reftable_free_ptr)(void *);
-
-void *reftable_malloc(size_t sz)
-{
- if (reftable_malloc_ptr)
- return (*reftable_malloc_ptr)(sz);
- return malloc(sz);
-}
-
-void *reftable_realloc(void *p, size_t sz)
-{
- if (reftable_realloc_ptr)
- return (*reftable_realloc_ptr)(p, sz);
- return realloc(p, sz);
-}
-
-void reftable_free(void *p)
-{
- if (reftable_free_ptr)
- reftable_free_ptr(p);
- else
- free(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);
- return p;
-}
-
-void reftable_set_alloc(void *(*malloc)(size_t),
- void *(*realloc)(void *, size_t), void (*free)(void *))
-{
- reftable_malloc_ptr = malloc;
- reftable_realloc_ptr = realloc;
- reftable_free_ptr = free;
-}
-
-int hash_size(uint32_t id)
-{
- switch (id) {
- case 0:
- case GIT_SHA1_FORMAT_ID:
- return GIT_SHA1_RAWSZ;
- case GIT_SHA256_FORMAT_ID:
- return GIT_SHA256_RAWSZ;
- }
- abort();
-}
diff --git a/reftable/reftable-basics.h b/reftable/reftable-basics.h
new file mode 100644
index 00000000000..6e8e636b716
--- /dev/null
+++ b/reftable/reftable-basics.h
@@ -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
diff --git a/reftable/reftable-malloc.h b/reftable/reftable-malloc.h
deleted file mode 100644
index 5f2185f1f34..00000000000
--- a/reftable/reftable-malloc.h
+++ /dev/null
@@ -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
--
2.46.0.551.gc5ee8f2d1c.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH 03/22] reftable: introduce `reftable_strdup()`
2024-09-16 12:28 [PATCH 00/22] reftable: handle allocation errors Patrick Steinhardt
2024-09-16 12:28 ` [PATCH 01/22] reftable/error: introduce out-of-memory error code Patrick Steinhardt
2024-09-16 12:28 ` [PATCH 02/22] reftable/basics: merge "publicbasics" into "basics" Patrick Steinhardt
@ 2024-09-16 12:28 ` Patrick Steinhardt
2024-09-16 12:28 ` [PATCH 04/22] reftable/basics: handle allocation failures in `reftable_calloc()` Patrick Steinhardt
` (22 subsequent siblings)
25 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-16 12:28 UTC (permalink / raw)
To: git; +Cc: Edward Thomson
The 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.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/basics.c | 10 ++++++++++
reftable/basics.h | 1 +
2 files changed, 11 insertions(+)
diff --git a/reftable/basics.c b/reftable/basics.c
index cf072935c88..4adc98cf5de 100644
--- a/reftable/basics.c
+++ b/reftable/basics.c
@@ -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 *))
{
diff --git a/reftable/basics.h b/reftable/basics.h
index 4e2e76014aa..f107e148605 100644
--- a/reftable/basics.h
+++ b/reftable/basics.h
@@ -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)))
--
2.46.0.551.gc5ee8f2d1c.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH 04/22] reftable/basics: handle allocation failures in `reftable_calloc()`
2024-09-16 12:28 [PATCH 00/22] reftable: handle allocation errors Patrick Steinhardt
` (2 preceding siblings ...)
2024-09-16 12:28 ` [PATCH 03/22] reftable: introduce `reftable_strdup()` Patrick Steinhardt
@ 2024-09-16 12:28 ` Patrick Steinhardt
2024-09-21 19:37 ` Junio C Hamano
2024-09-16 12:28 ` [PATCH 05/22] reftable/basics: handle allocation failures in `parse_names()` Patrick Steinhardt
` (21 subsequent siblings)
25 siblings, 1 reply; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-16 12:28 UTC (permalink / raw)
To: git; +Cc: Edward Thomson
Handle allocation failures in `reftable_calloc()`.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/basics.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/reftable/basics.c b/reftable/basics.c
index 4adc98cf5de..b404900b5d9 100644
--- a/reftable/basics.c
+++ b/reftable/basics.c
@@ -39,6 +39,8 @@ void *reftable_calloc(size_t nelem, size_t elsize)
{
size_t sz = st_mult(nelem, elsize);
void *p = reftable_malloc(sz);
+ if (!p)
+ return NULL;
memset(p, 0, sz);
return p;
}
--
2.46.0.551.gc5ee8f2d1c.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH 05/22] reftable/basics: handle allocation failures in `parse_names()`
2024-09-16 12:28 [PATCH 00/22] reftable: handle allocation errors Patrick Steinhardt
` (3 preceding siblings ...)
2024-09-16 12:28 ` [PATCH 04/22] reftable/basics: handle allocation failures in `reftable_calloc()` Patrick Steinhardt
@ 2024-09-16 12:28 ` Patrick Steinhardt
2024-09-16 12:28 ` [PATCH 06/22] reftable/record: handle allocation failures on copy Patrick Steinhardt
` (20 subsequent siblings)
25 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-16 12:28 UTC (permalink / raw)
To: git; +Cc: Edward Thomson
Handle 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.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
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(-)
diff --git a/reftable/basics.c b/reftable/basics.c
index b404900b5d9..b9bbf061e9d 100644
--- a/reftable/basics.c
+++ b/reftable/basics.c
@@ -130,14 +130,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) {
@@ -147,14 +147,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] = 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++)
+ free(names[i]);
+ free(names);
+ return NULL;
}
int names_equal(const char **a, const char **b)
diff --git a/reftable/basics.h b/reftable/basics.h
index f107e148605..69adeab2e4b 100644
--- a/reftable/basics.h
+++ b/reftable/basics.h
@@ -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);
diff --git a/reftable/stack.c b/reftable/stack.c
index ce0a35216ba..498fae846d7 100644
--- a/reftable/stack.c
+++ b/reftable/stack.c
@@ -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);
diff --git a/t/unit-tests/t-reftable-basics.c b/t/unit-tests/t-reftable-basics.c
index e5556ebf527..1fa77b6faff 100644
--- a/t/unit-tests/t-reftable-basics.c
+++ b/t/unit-tests/t-reftable-basics.c
@@ -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");
--
2.46.0.551.gc5ee8f2d1c.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH 06/22] reftable/record: handle allocation failures on copy
2024-09-16 12:28 [PATCH 00/22] reftable: handle allocation errors Patrick Steinhardt
` (4 preceding siblings ...)
2024-09-16 12:28 ` [PATCH 05/22] reftable/basics: handle allocation failures in `parse_names()` Patrick Steinhardt
@ 2024-09-16 12:28 ` Patrick Steinhardt
2024-09-16 12:28 ` [PATCH 07/22] reftable/record: handle allocation failures when decoding records Patrick Steinhardt
` (19 subsequent siblings)
25 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-16 12:28 UTC (permalink / raw)
To: git; +Cc: Edward Thomson
Handle allocation failures when copying records. While at it, convert
from `xstrdup()` to `reftable_strdup()`. Adapt callsites to check for
error codes.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/record.c | 84 +++++++++++++++++++++++++++++++++--------------
reftable/record.h | 6 ++--
2 files changed, 63 insertions(+), 27 deletions(-)
diff --git a/reftable/record.c b/reftable/record.c
index 6b5a075b921..60fd33c9c94 100644
--- a/reftable/record.c
+++ b/reftable/record.c
@@ -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)
diff --git a/reftable/record.h b/reftable/record.h
index 5003bacdb0c..0f53ba54434 100644
--- a/reftable/record.h
+++ b/reftable/record.h
@@ -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);
--
2.46.0.551.gc5ee8f2d1c.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH 07/22] reftable/record: handle allocation failures when decoding records
2024-09-16 12:28 [PATCH 00/22] reftable: handle allocation errors Patrick Steinhardt
` (5 preceding siblings ...)
2024-09-16 12:28 ` [PATCH 06/22] reftable/record: handle allocation failures on copy Patrick Steinhardt
@ 2024-09-16 12:28 ` Patrick Steinhardt
2024-09-16 12:28 ` [PATCH 08/22] reftable/writer: handle allocation failures in `writer_index_hash()` Patrick Steinhardt
` (18 subsequent siblings)
25 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-16 12:28 UTC (permalink / raw)
To: git; +Cc: Edward Thomson
Handle allocation failures when decoding records. While at it, fix some
error codes to be `REFTABLE_FORMAT_ERROR`.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/record.c | 80 +++++++++++++++++++++++++++++++++++++----------
1 file changed, 63 insertions(+), 17 deletions(-)
diff --git a/reftable/record.c b/reftable/record.c
index 60fd33c9c94..787e134c9a0 100644
--- a/reftable/record.c
+++ b/reftable/record.c
@@ -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)
--
2.46.0.551.gc5ee8f2d1c.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH 08/22] reftable/writer: handle allocation failures in `writer_index_hash()`
2024-09-16 12:28 [PATCH 00/22] reftable: handle allocation errors Patrick Steinhardt
` (6 preceding siblings ...)
2024-09-16 12:28 ` [PATCH 07/22] reftable/record: handle allocation failures when decoding records Patrick Steinhardt
@ 2024-09-16 12:28 ` Patrick Steinhardt
2024-09-16 12:28 ` [PATCH 09/22] reftable/writer: handle allocation failures in `reftable_new_writer()` Patrick Steinhardt
` (17 subsequent siblings)
25 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-16 12:28 UTC (permalink / raw)
To: git; +Cc: Edward Thomson
Handle allocation errors in `writer_index_hash()`. Adjust its only
caller in `reftable_writer_add_ref()` accordingly.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/writer.c | 61 +++++++++++++++++++++++++++++------------------
1 file changed, 38 insertions(+), 23 deletions(-)
diff --git a/reftable/writer.c b/reftable/writer.c
index 9d5e6072bc6..ed61aaf59c5 100644
--- a/reftable/writer.c
+++ b/reftable/writer.c
@@ -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,
--
2.46.0.551.gc5ee8f2d1c.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH 09/22] reftable/writer: handle allocation failures in `reftable_new_writer()`
2024-09-16 12:28 [PATCH 00/22] reftable: handle allocation errors Patrick Steinhardt
` (7 preceding siblings ...)
2024-09-16 12:28 ` [PATCH 08/22] reftable/writer: handle allocation failures in `writer_index_hash()` Patrick Steinhardt
@ 2024-09-16 12:28 ` Patrick Steinhardt
2024-09-16 12:28 ` [PATCH 10/22] reftable/merged: handle allocation failures in `merged_table_init_iter()` Patrick Steinhardt
` (16 subsequent siblings)
25 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-16 12:28 UTC (permalink / raw)
To: git; +Cc: Edward Thomson
Handle 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.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
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(-)
diff --git a/reftable/reftable-writer.h b/reftable/reftable-writer.h
index 189b1f4144f..43623dc7c30 100644
--- a/reftable/reftable-writer.h
+++ b/reftable/reftable-writer.h
@@ -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
diff --git a/reftable/stack.c b/reftable/stack.c
index 498fae846d7..ea21ca6e5f5 100644
--- a/reftable/stack.c
+++ b/reftable/stack.c
@@ -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;
diff --git a/reftable/writer.c b/reftable/writer.c
index ed61aaf59c5..54ec822e1cd 100644
--- a/reftable/writer.c
+++ b/reftable/writer.c
@@ -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) {
+ 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,
diff --git a/t/unit-tests/lib-reftable.c b/t/unit-tests/lib-reftable.c
index ab1fa44a282..54c26c43e77 100644
--- a/t/unit-tests/lib-reftable.c
+++ b/t/unit-tests/lib-reftable.c
@@ -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,
--
2.46.0.551.gc5ee8f2d1c.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH 10/22] reftable/merged: handle allocation failures in `merged_table_init_iter()`
2024-09-16 12:28 [PATCH 00/22] reftable: handle allocation errors Patrick Steinhardt
` (8 preceding siblings ...)
2024-09-16 12:28 ` [PATCH 09/22] reftable/writer: handle allocation failures in `reftable_new_writer()` Patrick Steinhardt
@ 2024-09-16 12:28 ` Patrick Steinhardt
2024-09-16 12:28 ` [PATCH 11/22] reftable/reader: handle allocation failures for unindexed reader Patrick Steinhardt
` (15 subsequent siblings)
25 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-16 12:28 UTC (permalink / raw)
To: git; +Cc: Edward Thomson
Handle 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.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
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(-)
diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c
index 5c241097a4e..9c08c46c85e 100644
--- a/refs/reftable-backend.c
+++ b/refs/reftable-backend.c
@@ -1299,7 +1299,9 @@ static int write_transaction_table(struct reftable_writer *writer, void *cb_data
struct 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 reftabl
if (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_da
reftable_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)
diff --git a/reftable/merged.c b/reftable/merged.c
index 128a810c55d..5a8ea8ae779 100644
--- a/reftable/merged.c
+++ b/reftable/merged.c
@@ -30,22 +30,6 @@ struct merged_iter {
ssize_t advance_index;
};
-static void merged_iter_init(struct merged_iter *mi,
- struct reftable_merged_table *mt,
- uint8_t typ)
-{
- memset(mi, 0, sizeof(*mi));
- mi->advance_index = -1;
- mi->suppress_deletions = mt->suppress_deletions;
-
- REFTABLE_CALLOC_ARRAY(mi->subiters, mt->readers_len);
- for (size_t i = 0; i < mt->readers_len; i++) {
- reftable_record_init(&mi->subiters[i].rec, typ);
- reader_init_iter(mt->readers[i], &mi->subiters[i].iter, typ);
- }
- mi->subiters_len = mt->readers_len;
-}
-
static void merged_iter_close(void *p)
{
struct merged_iter *mi = p;
@@ -244,25 +228,61 @@ reftable_merged_table_min_update_index(struct reftable_merged_table *mt)
return mt->min;
}
-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)
{
- struct merged_iter *mi = reftable_malloc(sizeof(*mi));
- merged_iter_init(mi, mt, typ);
+ struct merged_subiter *subiters;
+ struct merged_iter *mi = NULL;
+ int ret;
+
+ REFTABLE_CALLOC_ARRAY(subiters, mt->readers_len);
+ if (!subiters) {
+ ret = REFTABLE_OUT_OF_MEMORY_ERROR;
+ goto out;
+ }
+
+ 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);
+ }
+
+ REFTABLE_CALLOC_ARRAY(mi, 1);
+ if (!mi) {
+ ret = REFTABLE_OUT_OF_MEMORY_ERROR;
+ goto out;
+ }
+ mi->advance_index = -1;
+ mi->suppress_deletions = mt->suppress_deletions;
+ mi->subiters = subiters;
+ mi->subiters_len = mt->readers_len;
+
iterator_from_merged_iter(it, mi);
+ ret = 0;
+
+out:
+ if (ret < 0) {
+ for (size_t i = 0; subiters && i < mt->readers_len; i++) {
+ reftable_iterator_destroy(&subiters[i].iter);
+ reftable_record_release(&subiters[i].rec);
+ }
+ free(subiters);
+ free(mi);
+ }
+
+ return ret;
}
-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)
{
- merged_table_init_iter(mt, it, BLOCK_TYPE_REF);
+ return merged_table_init_iter(mt, it, BLOCK_TYPE_REF);
}
-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)
{
- merged_table_init_iter(mt, it, BLOCK_TYPE_LOG);
+ return merged_table_init_iter(mt, it, BLOCK_TYPE_LOG);
}
uint32_t reftable_merged_table_hash_id(struct reftable_merged_table *mt)
diff --git a/reftable/merged.h b/reftable/merged.h
index de5fd33f010..89bd0c4b35b 100644
--- a/reftable/merged.h
+++ b/reftable/merged.h
@@ -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
diff --git a/reftable/reftable-merged.h b/reftable/reftable-merged.h
index 16d19f8df20..a970d5dd89a 100644
--- a/reftable/reftable-merged.h
+++ b/reftable/reftable-merged.h
@@ -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
diff --git a/reftable/reftable-stack.h b/reftable/reftable-stack.h
index f4f8cabc7fb..e958f911b4e 100644
--- a/reftable/reftable-stack.h
+++ b/reftable/reftable-stack.h
@@ -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.
diff --git a/reftable/stack.c b/reftable/stack.c
index ea21ca6e5f5..bb4d2309189 100644
--- a/reftable/stack.c
+++ b/reftable/stack.c
@@ -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;
diff --git a/t/helper/test-reftable.c b/t/helper/test-reftable.c
index 29d4e9a755d..5c8849d115b 100644
--- a/t/helper/test-reftable.c
+++ b/t/helper/test-reftable.c
@@ -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;
diff --git a/t/unit-tests/t-reftable-merged.c b/t/unit-tests/t-reftable-merged.c
index 19e54bdfb8b..3d2848632db 100644
--- a/t/unit-tests/t-reftable-merged.c
+++ b/t/unit-tests/t-reftable-merged.c
@@ -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]);
diff --git a/t/unit-tests/t-reftable-stack.c b/t/unit-tests/t-reftable-stack.c
index 65e513d5ec8..6e41a4cf7e5 100644
--- a/t/unit-tests/t-reftable-stack.c
+++ b/t/unit-tests/t-reftable-stack.c
@@ -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 };
--
2.46.0.551.gc5ee8f2d1c.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH 11/22] reftable/reader: handle allocation failures for unindexed reader
2024-09-16 12:28 [PATCH 00/22] reftable: handle allocation errors Patrick Steinhardt
` (9 preceding siblings ...)
2024-09-16 12:28 ` [PATCH 10/22] reftable/merged: handle allocation failures in `merged_table_init_iter()` Patrick Steinhardt
@ 2024-09-16 12:28 ` Patrick Steinhardt
2024-09-16 12:28 ` [PATCH 12/22] reftable/reader: handle allocation failures in `reader_init_iter()` Patrick Steinhardt
` (14 subsequent siblings)
25 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-16 12:28 UTC (permalink / raw)
To: git; +Cc: Edward Thomson
Handle allocation failures when creating unindexed readers.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/reader.c | 23 +++++++++++++++++------
1 file changed, 17 insertions(+), 6 deletions(-)
diff --git a/reftable/reader.c b/reftable/reader.c
index 6494ce2e327..485ee085dac 100644
--- a/reftable/reader.c
+++ b/reftable/reader.c
@@ -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,
--
2.46.0.551.gc5ee8f2d1c.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH 12/22] reftable/reader: handle allocation failures in `reader_init_iter()`
2024-09-16 12:28 [PATCH 00/22] reftable: handle allocation errors Patrick Steinhardt
` (10 preceding siblings ...)
2024-09-16 12:28 ` [PATCH 11/22] reftable/reader: handle allocation failures for unindexed reader Patrick Steinhardt
@ 2024-09-16 12:28 ` Patrick Steinhardt
2024-09-16 12:28 ` [PATCH 13/22] reftable/stack: handle allocation failures on reload Patrick Steinhardt
` (13 subsequent siblings)
25 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-16 12:28 UTC (permalink / raw)
To: git; +Cc: Edward Thomson
Handle 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.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
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(-)
diff --git a/reftable/merged.c b/reftable/merged.c
index 5a8ea8ae779..741f62ea638 100644
--- a/reftable/merged.c
+++ b/reftable/merged.c
@@ -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);
diff --git a/reftable/reader.c b/reftable/reader.c
index 485ee085dac..f696e992dfc 100644
--- a/reftable/reader.c
+++ b/reftable/reader.c
@@ -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;
diff --git a/reftable/reader.h b/reftable/reader.h
index 3710ee09b4c..02d10c5d37e 100644
--- a/reftable/reader.h
+++ b/reftable/reader.h
@@ -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,
diff --git a/reftable/reftable-reader.h b/reftable/reftable-reader.h
index a600452b565..6a2d0b693f5 100644
--- a/reftable/reftable-reader.h
+++ b/reftable/reftable-reader.h
@@ -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);
diff --git a/t/unit-tests/t-reftable-readwrite.c b/t/unit-tests/t-reftable-readwrite.c
index e1b235a5f13..acca927a2cf 100644
--- a/t/unit-tests/t-reftable-readwrite.c
+++ b/t/unit-tests/t-reftable-readwrite.c
@@ -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);
--
2.46.0.551.gc5ee8f2d1c.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH 13/22] reftable/stack: handle allocation failures on reload
2024-09-16 12:28 [PATCH 00/22] reftable: handle allocation errors Patrick Steinhardt
` (11 preceding siblings ...)
2024-09-16 12:28 ` [PATCH 12/22] reftable/reader: handle allocation failures in `reader_init_iter()` Patrick Steinhardt
@ 2024-09-16 12:28 ` Patrick Steinhardt
2024-09-16 12:28 ` [PATCH 14/22] reftable/stack: handle allocation failures in `reftable_new_stack()` Patrick Steinhardt
` (12 subsequent siblings)
25 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-16 12:28 UTC (permalink / raw)
To: git; +Cc: Edward Thomson
Handle allocation failures in `reftable_stack_reload_once()`.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/stack.c | 34 +++++++++++++++++++++++++---------
1 file changed, 25 insertions(+), 9 deletions(-)
diff --git a/reftable/stack.c b/reftable/stack.c
index bb4d2309189..060b2c1b90d 100644
--- a/reftable/stack.c
+++ b/reftable/stack.c
@@ -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;
--
2.46.0.551.gc5ee8f2d1c.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH 14/22] reftable/stack: handle allocation failures in `reftable_new_stack()`
2024-09-16 12:28 [PATCH 00/22] reftable: handle allocation errors Patrick Steinhardt
` (12 preceding siblings ...)
2024-09-16 12:28 ` [PATCH 13/22] reftable/stack: handle allocation failures on reload Patrick Steinhardt
@ 2024-09-16 12:28 ` Patrick Steinhardt
2024-09-16 12:29 ` [PATCH 15/22] reftable/stack: handle allocation failures in `stack_compact_range()` Patrick Steinhardt
` (11 subsequent siblings)
25 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-16 12:28 UTC (permalink / raw)
To: git; +Cc: Edward Thomson
Handle allocation failures in `reftable_new_stack()`.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/stack.c | 34 ++++++++++++++++++++++++++--------
1 file changed, 26 insertions(+), 8 deletions(-)
diff --git a/reftable/stack.c b/reftable/stack.c
index 060b2c1b90d..1b77c9d0146 100644
--- a/reftable/stack.c
+++ b/reftable/stack.c
@@ -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;
--
2.46.0.551.gc5ee8f2d1c.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH 15/22] reftable/stack: handle allocation failures in `stack_compact_range()`
2024-09-16 12:28 [PATCH 00/22] reftable: handle allocation errors Patrick Steinhardt
` (13 preceding siblings ...)
2024-09-16 12:28 ` [PATCH 14/22] reftable/stack: handle allocation failures in `reftable_new_stack()` Patrick Steinhardt
@ 2024-09-16 12:29 ` Patrick Steinhardt
2024-09-16 12:29 ` [PATCH 16/22] reftable/stack: handle allocation failures in auto compaction Patrick Steinhardt
` (10 subsequent siblings)
25 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-16 12:29 UTC (permalink / raw)
To: git; +Cc: Edward Thomson
Handle allocation failures in `stack_compact_range()`.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/stack.c | 19 +++++++++++++++++--
1 file changed, 17 insertions(+), 2 deletions(-)
diff --git a/reftable/stack.c b/reftable/stack.c
index 1b77c9d0146..2e6dd513d72 100644
--- a/reftable/stack.c
+++ b/reftable/stack.c
@@ -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;
}
--
2.46.0.551.gc5ee8f2d1c.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH 16/22] reftable/stack: handle allocation failures in auto compaction
2024-09-16 12:28 [PATCH 00/22] reftable: handle allocation errors Patrick Steinhardt
` (14 preceding siblings ...)
2024-09-16 12:29 ` [PATCH 15/22] reftable/stack: handle allocation failures in `stack_compact_range()` Patrick Steinhardt
@ 2024-09-16 12:29 ` Patrick Steinhardt
2024-09-16 12:29 ` [PATCH 17/22] reftable/iter: handle allocation failures when creating indexed table iter Patrick Steinhardt
` (9 subsequent siblings)
25 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-16 12:29 UTC (permalink / raw)
To: git; +Cc: Edward Thomson
Handle allocation failures in `reftable_stack_auto_compact()`.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/stack.c | 16 ++++++++++++----
1 file changed, 12 insertions(+), 4 deletions(-)
diff --git a/reftable/stack.c b/reftable/stack.c
index 2e6dd513d72..990784d9d2f 100644
--- a/reftable/stack.c
+++ b/reftable/stack.c
@@ -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);
--
2.46.0.551.gc5ee8f2d1c.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH 17/22] reftable/iter: handle allocation failures when creating indexed table iter
2024-09-16 12:28 [PATCH 00/22] reftable: handle allocation errors Patrick Steinhardt
` (15 preceding siblings ...)
2024-09-16 12:29 ` [PATCH 16/22] reftable/stack: handle allocation failures in auto compaction Patrick Steinhardt
@ 2024-09-16 12:29 ` Patrick Steinhardt
2024-09-22 6:26 ` Junio C Hamano
2024-09-16 12:29 ` [PATCH 18/22] reftable/blocksource: handle allocation failures Patrick Steinhardt
` (8 subsequent siblings)
25 siblings, 1 reply; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-16 12:29 UTC (permalink / raw)
To: git; +Cc: Edward Thomson
Handle allocation failures in `new_indexed_table_ref_iter()`. While at
it, rename the function to match our coding style.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/iter.c | 22 ++++++++++++++++------
reftable/iter.h | 2 +-
reftable/reader.c | 7 ++++++-
3 files changed, 23 insertions(+), 8 deletions(-)
diff --git a/reftable/iter.c b/reftable/iter.c
index 416a9f6996b..41bdfbb13f9 100644
--- a/reftable/iter.c
+++ b/reftable/iter.c
@@ -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,11 +203,15 @@ 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) {
+ if (err < 0)
+ goto out;
+
+ *dest = itr;
+ err = 0;
+
+out:
+ if (err < 0)
reftable_free(itr);
- } else {
- *dest = itr;
- }
return err;
}
diff --git a/reftable/iter.h b/reftable/iter.h
index befc4597df1..b3225bc7add 100644
--- a/reftable/iter.h
+++ b/reftable/iter.h
@@ -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);
diff --git a/reftable/reader.c b/reftable/reader.c
index f696e992dfc..0179e4e73dd 100644
--- a/reftable/reader.c
+++ b/reftable/reader.c
@@ -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)
--
2.46.0.551.gc5ee8f2d1c.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH 18/22] reftable/blocksource: handle allocation failures
2024-09-16 12:28 [PATCH 00/22] reftable: handle allocation errors Patrick Steinhardt
` (16 preceding siblings ...)
2024-09-16 12:29 ` [PATCH 17/22] reftable/iter: handle allocation failures when creating indexed table iter Patrick Steinhardt
@ 2024-09-16 12:29 ` Patrick Steinhardt
2024-09-16 12:29 ` [PATCH 19/22] reftable/block: " Patrick Steinhardt
` (7 subsequent siblings)
25 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-16 12:29 UTC (permalink / raw)
To: git; +Cc: Edward Thomson
Handle allocation failures in the blocksource code.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/blocksource.c | 25 ++++++++++++++++++++-----
1 file changed, 20 insertions(+), 5 deletions(-)
diff --git a/reftable/blocksource.c b/reftable/blocksource.c
index e93cac9bb6f..6b570751be6 100644
--- a/reftable/blocksource.c
+++ b/reftable/blocksource.c
@@ -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)
+ free(p);
return 0;
}
--
2.46.0.551.gc5ee8f2d1c.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH 19/22] reftable/block: handle allocation failures
2024-09-16 12:28 [PATCH 00/22] reftable: handle allocation errors Patrick Steinhardt
` (17 preceding siblings ...)
2024-09-16 12:29 ` [PATCH 18/22] reftable/blocksource: handle allocation failures Patrick Steinhardt
@ 2024-09-16 12:29 ` Patrick Steinhardt
2024-09-16 12:29 ` [PATCH 20/22] reftable/pq: handle allocation failures when adding entries Patrick Steinhardt
` (6 subsequent siblings)
25 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-16 12:29 UTC (permalink / raw)
To: git; +Cc: Edward Thomson
Handle 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.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
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(-)
diff --git a/reftable/block.c b/reftable/block.c
index 00030eee065..bfa7dc61bf5 100644
--- a/reftable/block.c
+++ b/reftable/block.c
@@ -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);
diff --git a/reftable/block.h b/reftable/block.h
index 1c8f25ee6eb..18d7ea03373 100644
--- a/reftable/block.h
+++ b/reftable/block.h
@@ -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);
diff --git a/reftable/writer.c b/reftable/writer.c
index 54ec822e1cd..712a84791b2 100644
--- a/reftable/writer.c
+++ b/reftable/writer.c
@@ -102,19 +102,24 @@ static int writer_write_header(struct reftable_writer *w, uint8_t *dest)
return header_size(writer_version(w));
}
-static void writer_reinit_block_writer(struct reftable_writer *w, uint8_t typ)
+static int writer_reinit_block_writer(struct reftable_writer *w, uint8_t typ)
{
- int block_start = 0;
- if (w->next == 0) {
+ int block_start = 0, ret;
+
+ if (w->next == 0)
block_start = header_size(writer_version(w));
- }
strbuf_reset(&w->last_key);
- block_writer_init(&w->block_writer_data, typ, w->block,
- w->opts.block_size, block_start,
- hash_size(w->opts.hash_id));
+ ret = block_writer_init(&w->block_writer_data, typ, w->block,
+ w->opts.block_size, block_start,
+ hash_size(w->opts.hash_id));
+ if (ret < 0)
+ return ret;
+
w->block_writer = &w->block_writer_data;
w->block_writer->restart_interval = w->opts.restart_interval;
+
+ return 0;
}
int reftable_writer_new(struct reftable_writer **out,
@@ -247,8 +252,11 @@ static int writer_add_record(struct reftable_writer *w,
strbuf_reset(&w->last_key);
strbuf_addbuf(&w->last_key, &key);
- if (!w->block_writer)
- writer_reinit_block_writer(w, reftable_record_type(rec));
+ if (!w->block_writer) {
+ err = writer_reinit_block_writer(w, reftable_record_type(rec));
+ if (err < 0)
+ goto done;
+ }
if (block_writer_type(w->block_writer) != reftable_record_type(rec))
BUG("record of type %d added to writer of type %d",
@@ -271,7 +279,9 @@ static int writer_add_record(struct reftable_writer *w,
err = writer_flush_block(w);
if (err < 0)
goto done;
- writer_reinit_block_writer(w, reftable_record_type(rec));
+ err = writer_reinit_block_writer(w, reftable_record_type(rec));
+ if (err < 0)
+ goto done;
/*
* Try to add the record to the writer again. If this still fails then
@@ -461,7 +471,9 @@ static int writer_finish_section(struct reftable_writer *w)
max_level++;
index_start = w->next;
- writer_reinit_block_writer(w, BLOCK_TYPE_INDEX);
+ err = writer_reinit_block_writer(w, BLOCK_TYPE_INDEX);
+ if (err < 0)
+ return err;
idx = w->index;
idx_len = w->index_len;
@@ -555,7 +567,10 @@ static void write_object_record(void *void_arg, void *key)
if (arg->err < 0)
goto done;
- writer_reinit_block_writer(arg->w, BLOCK_TYPE_OBJ);
+ arg->err = writer_reinit_block_writer(arg->w, BLOCK_TYPE_OBJ);
+ if (arg->err < 0)
+ goto done;
+
arg->err = block_writer_add(arg->w->block_writer, &rec);
if (arg->err == 0)
goto done;
@@ -584,16 +599,18 @@ static int writer_dump_object_index(struct reftable_writer *w)
struct common_prefix_arg common = {
.max = 1, /* obj_id_len should be >= 2. */
};
- if (w->obj_index_tree) {
+ int err;
+
+ if (w->obj_index_tree)
infix_walk(w->obj_index_tree, &update_common, &common);
- }
w->stats.object_id_len = common.max + 1;
- writer_reinit_block_writer(w, BLOCK_TYPE_OBJ);
+ err = writer_reinit_block_writer(w, BLOCK_TYPE_OBJ);
+ if (err < 0)
+ return err;
- if (w->obj_index_tree) {
+ if (w->obj_index_tree)
infix_walk(w->obj_index_tree, &write_object_record, &closure);
- }
if (closure.err < 0)
return closure.err;
diff --git a/t/unit-tests/t-reftable-block.c b/t/unit-tests/t-reftable-block.c
index f1a49485e23..e52a612e852 100644
--- a/t/unit-tests/t-reftable-block.c
+++ b/t/unit-tests/t-reftable-block.c
@@ -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);
--
2.46.0.551.gc5ee8f2d1c.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH 20/22] reftable/pq: handle allocation failures when adding entries
2024-09-16 12:28 [PATCH 00/22] reftable: handle allocation errors Patrick Steinhardt
` (18 preceding siblings ...)
2024-09-16 12:29 ` [PATCH 19/22] reftable/block: " Patrick Steinhardt
@ 2024-09-16 12:29 ` Patrick Steinhardt
2024-09-16 12:29 ` [PATCH 21/22] reftable/tree: handle allocation failures Patrick Steinhardt
` (5 subsequent siblings)
25 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-16 12:29 UTC (permalink / raw)
To: git; +Cc: Edward Thomson
Handle allocation failures when adding entries to the pqueue. Adapt its
only caller accordingly.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/merged.c | 5 ++++-
reftable/pq.c | 7 ++++++-
reftable/pq.h | 2 +-
3 files changed, 11 insertions(+), 3 deletions(-)
diff --git a/reftable/merged.c b/reftable/merged.c
index 741f62ea638..2c20845d624 100644
--- a/reftable/merged.c
+++ b/reftable/merged.c
@@ -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;
}
diff --git a/reftable/pq.c b/reftable/pq.c
index 2b5b7d1c0e2..03b99122829 100644
--- a/reftable/pq.c
+++ b/reftable/pq.c
@@ -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_entry
SWAP(pq->heap[j], pq->heap[i]);
i = j;
}
+
+ return 0;
}
void merged_iter_pqueue_release(struct merged_iter_pqueue *pq)
diff --git a/reftable/pq.h b/reftable/pq.h
index 707bd26767b..83c062eecac 100644
--- a/reftable/pq.h
+++ b/reftable/pq.h
@@ -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);
--
2.46.0.551.gc5ee8f2d1c.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH 21/22] reftable/tree: handle allocation failures
2024-09-16 12:28 [PATCH 00/22] reftable: handle allocation errors Patrick Steinhardt
` (19 preceding siblings ...)
2024-09-16 12:29 ` [PATCH 20/22] reftable/pq: handle allocation failures when adding entries Patrick Steinhardt
@ 2024-09-16 12:29 ` Patrick Steinhardt
2024-09-16 12:29 ` [PATCH 22/22] reftable: handle trivial " Patrick Steinhardt
` (4 subsequent siblings)
25 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-16 12:29 UTC (permalink / raw)
To: git; +Cc: Edward Thomson
The 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.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
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(-)
diff --git a/reftable/tree.c b/reftable/tree.c
index 5ffb2e0d690..f4dbe720901 100644
--- a/reftable/tree.c
+++ b/reftable/tree.c
@@ -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;
}
diff --git a/reftable/tree.h b/reftable/tree.h
index fbdd002e23a..9604453b6d5 100644
--- a/reftable/tree.h
+++ b/reftable/tree.h
@@ -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),
diff --git a/reftable/writer.c b/reftable/writer.c
index 712a84791b2..44bed92f467 100644
--- a/reftable/writer.c
+++ b/reftable/writer.c
@@ -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;
}
diff --git a/t/unit-tests/t-reftable-tree.c b/t/unit-tests/t-reftable-tree.c
index 700479d34b1..79b175a45a7 100644
--- a/t/unit-tests/t-reftable-tree.c
+++ b/t/unit-tests/t-reftable-tree.c
@@ -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);
--
2.46.0.551.gc5ee8f2d1c.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH 22/22] reftable: handle trivial allocation failures
2024-09-16 12:28 [PATCH 00/22] reftable: handle allocation errors Patrick Steinhardt
` (20 preceding siblings ...)
2024-09-16 12:29 ` [PATCH 21/22] reftable/tree: handle allocation failures Patrick Steinhardt
@ 2024-09-16 12:29 ` Patrick Steinhardt
2024-09-24 6:31 ` [PATCH v2 00/22] reftable: handle allocation errors Patrick Steinhardt
` (3 subsequent siblings)
25 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-16 12:29 UTC (permalink / raw)
To: git; +Cc: Edward Thomson
Handle trivial allocation failures in the reftable library and its unit
tests.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
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(-)
diff --git a/reftable/merged.c b/reftable/merged.c
index 2c20845d624..52bc66c5273 100644
--- a/reftable/merged.c
+++ b/reftable/merged.c
@@ -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;
diff --git a/reftable/reader.c b/reftable/reader.c
index 0179e4e73dd..98e7aa26373 100644
--- a/reftable/reader.c
+++ b/reftable/reader.c
@@ -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;
diff --git a/reftable/stack.c b/reftable/stack.c
index 990784d9d2f..7df28ab3438 100644
--- a/reftable/stack.c
+++ b/reftable/stack.c
@@ -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);
diff --git a/reftable/writer.c b/reftable/writer.c
index 44bed92f467..ad4718edecb 100644
--- a/reftable/writer.c
+++ b/reftable/writer.c
@@ -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);
diff --git a/t/unit-tests/t-reftable-block.c b/t/unit-tests/t-reftable-block.c
index e52a612e852..d470060e8be 100644
--- a/t/unit-tests/t-reftable-block.c
+++ b/t/unit-tests/t-reftable-block.c
@@ -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,
diff --git a/t/unit-tests/t-reftable-merged.c b/t/unit-tests/t-reftable-merged.c
index 3d2848632db..3c84363e980 100644
--- a/t/unit-tests/t-reftable-merged.c
+++ b/t/unit-tests/t-reftable-merged.c
@@ -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);
diff --git a/t/unit-tests/t-reftable-readwrite.c b/t/unit-tests/t-reftable-readwrite.c
index acca927a2cf..bfa069caff7 100644
--- a/t/unit-tests/t-reftable-readwrite.c
+++ b/t/unit-tests/t-reftable-readwrite.c
@@ -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);
--
2.46.0.551.gc5ee8f2d1c.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* Re: [PATCH 04/22] reftable/basics: handle allocation failures in `reftable_calloc()`
2024-09-16 12:28 ` [PATCH 04/22] reftable/basics: handle allocation failures in `reftable_calloc()` Patrick Steinhardt
@ 2024-09-21 19:37 ` Junio C Hamano
2024-09-24 5:48 ` Patrick Steinhardt
0 siblings, 1 reply; 152+ messages in thread
From: Junio C Hamano @ 2024-09-21 19:37 UTC (permalink / raw)
To: Patrick Steinhardt; +Cc: git, Edward Thomson
Patrick Steinhardt <ps@pks.im> writes:
> Handle allocation failures in `reftable_calloc()`.
>
> Signed-off-by: Patrick Steinhardt <ps@pks.im>
> ---
> reftable/basics.c | 2 ++
> 1 file changed, 2 insertions(+)
>
> diff --git a/reftable/basics.c b/reftable/basics.c
> index 4adc98cf5de..b404900b5d9 100644
> --- a/reftable/basics.c
> +++ b/reftable/basics.c
> @@ -39,6 +39,8 @@ void *reftable_calloc(size_t nelem, size_t elsize)
> {
> size_t sz = st_mult(nelem, elsize);
> void *p = reftable_malloc(sz);
> + if (!p)
> + return NULL;
> memset(p, 0, sz);
> return p;
> }
Since this series is not about eradicating all avenues in reftable
library code that can lead to die(), but only about dealing with
allocation errors from the underlying malloc/realloc routines, I
think the code posted is perfectly fine as-is as a part of this
series, but since I noticed something, let me comment before I
forget.
When st_mult() detects overflow, you'd still die(), wouldn't you?
We'd probably want a variant of st_mult() that lets us notice a
condition that would yield too large a result, and code the above
like so,
size_t sz;
void *p;
if (st_mult_gently(nelem, elsize, &sz) ||
!((p = reftable_malloc(sz))))
return NULL;
memset(p, 0, sz);
return p;
or use the underlying helper ourselves, and say
size_t sz;
void *p;
if (unsigned_mult_overflows(nelem, elsize)) ||
!((sz = nelem * elsize, p = reftable_malloc(sz))))
return NULL;
memset(p, 0, sz);
return p;
which lets us without an extra helper but after writing it myself, I
find it a bit too wordy.
In a sense, it is on the borderline to handle st_mult() overflow in
this function for a topic whose theme is about allocation failures.
From the point of view of callers of reftable_calloc(), whether the
arguments they are feeding the function is too large to be
multiplied or whether the request is too big for the underlying
allocator to handle, the end result should be the same: they
requested too large an allocation.
So I wouldn't complain that it is out of scope, if use of st_mult()
that computes the allocation size is fixed as part of this series.
But as I already said, I am also OK if we leave it to a separate
series to tackle other potential callers of die().
Thanks.
^ permalink raw reply [flat|nested] 152+ messages in thread
* Re: [PATCH 17/22] reftable/iter: handle allocation failures when creating indexed table iter
2024-09-16 12:29 ` [PATCH 17/22] reftable/iter: handle allocation failures when creating indexed table iter Patrick Steinhardt
@ 2024-09-22 6:26 ` Junio C Hamano
2024-09-24 5:49 ` Patrick Steinhardt
0 siblings, 1 reply; 152+ messages in thread
From: Junio C Hamano @ 2024-09-22 6:26 UTC (permalink / raw)
To: Patrick Steinhardt; +Cc: git, Edward Thomson
Patrick Steinhardt <ps@pks.im> writes:
> -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,11 +203,15 @@ 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) {
> + if (err < 0)
> + goto out;
> +
> + *dest = itr;
> + err = 0;
> +
> +out:
> + if (err < 0)
> reftable_free(itr);
> - } else {
> - *dest = itr;
> - }
> return err;
> }
Unless the service the helper function offers is to upgrade an
existing resource (e.g., realloc() taking a pointer and give an
enlarged piece of memory), it may be a safer calling convention to
promise that *dest is cleared to NULL when the function fails,
instead of promising that *dest is left intact. The caller, when it
needs to evantually release the resource acquired here, has to
remember what the returned value (i.e., err) was, in order to decide
if it needs to call the release helper on *dest it obtained from us.
The only caller seems to initialize *dest to NULL itself, so it does
not matter in the current code, though.
^ permalink raw reply [flat|nested] 152+ messages in thread
* Re: [PATCH 04/22] reftable/basics: handle allocation failures in `reftable_calloc()`
2024-09-21 19:37 ` Junio C Hamano
@ 2024-09-24 5:48 ` Patrick Steinhardt
2024-09-24 6:02 ` Patrick Steinhardt
2024-09-24 16:39 ` Junio C Hamano
0 siblings, 2 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-24 5:48 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git, Edward Thomson
On Sat, Sep 21, 2024 at 12:37:26PM -0700, Junio C Hamano wrote:
> Patrick Steinhardt <ps@pks.im> writes:
>
> > Handle allocation failures in `reftable_calloc()`.
> >
> > Signed-off-by: Patrick Steinhardt <ps@pks.im>
> > ---
> > reftable/basics.c | 2 ++
> > 1 file changed, 2 insertions(+)
> >
> > diff --git a/reftable/basics.c b/reftable/basics.c
> > index 4adc98cf5de..b404900b5d9 100644
> > --- a/reftable/basics.c
> > +++ b/reftable/basics.c
> > @@ -39,6 +39,8 @@ void *reftable_calloc(size_t nelem, size_t elsize)
> > {
> > size_t sz = st_mult(nelem, elsize);
> > void *p = reftable_malloc(sz);
> > + if (!p)
> > + return NULL;
> > memset(p, 0, sz);
> > return p;
> > }
>
> Since this series is not about eradicating all avenues in reftable
> library code that can lead to die(), but only about dealing with
> allocation errors from the underlying malloc/realloc routines, I
> think the code posted is perfectly fine as-is as a part of this
> series, but since I noticed something, let me comment before I
> forget.
>
> When st_mult() detects overflow, you'd still die(), wouldn't you?
True.
> We'd probably want a variant of st_mult() that lets us notice a
> condition that would yield too large a result, and code the above
> like so,
>
> size_t sz;
> void *p;
>
> if (st_mult_gently(nelem, elsize, &sz) ||
> !((p = reftable_malloc(sz))))
> return NULL;
> memset(p, 0, sz);
> return p;
>
> or use the underlying helper ourselves, and say
>
> size_t sz;
> void *p;
>
> if (unsigned_mult_overflows(nelem, elsize)) ||
> !((sz = nelem * elsize, p = reftable_malloc(sz))))
> return NULL;
> memset(p, 0, sz);
> return p;
>
> which lets us without an extra helper but after writing it myself, I
> find it a bit too wordy.
Yeah, we'll have to have something like this eventually.
> In a sense, it is on the borderline to handle st_mult() overflow in
> this function for a topic whose theme is about allocation failures.
>
> From the point of view of callers of reftable_calloc(), whether the
> arguments they are feeding the function is too large to be
> multiplied or whether the request is too big for the underlying
> allocator to handle, the end result should be the same: they
> requested too large an allocation.
>
> So I wouldn't complain that it is out of scope, if use of st_mult()
> that computes the allocation size is fixed as part of this series.
> But as I already said, I am also OK if we leave it to a separate
> series to tackle other potential callers of die().
I'd leave it as-is for now, but I do have it on my agenda to address
this, as well. I already have it as part of my third patch series in
this context where I completely detangle the reftable library from the
rest of Git to make it a reusable library for libgit2 and the likes.
In any case, there's another, bigger elephant in the room: `struct
strbuf`. I will have to introduce a reftable-specific buffer type for
this such that we can handle allocation failures here, too. While the
alternative would be to amend `struct strbuf` itself to do that, I don't
quite think that we should do it in "core" Git itself for now. And it is
another excuse for me for why the reftable code should have its own type
instead of relying on `struct strbuf` in the context of making it a
standalone library again.
Patrick
^ permalink raw reply [flat|nested] 152+ messages in thread
* Re: [PATCH 17/22] reftable/iter: handle allocation failures when creating indexed table iter
2024-09-22 6:26 ` Junio C Hamano
@ 2024-09-24 5:49 ` Patrick Steinhardt
0 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-24 5:49 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git, Edward Thomson
On Sat, Sep 21, 2024 at 11:26:09PM -0700, Junio C Hamano wrote:
> Patrick Steinhardt <ps@pks.im> writes:
>
> > -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,11 +203,15 @@ 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) {
> > + if (err < 0)
> > + goto out;
> > +
> > + *dest = itr;
> > + err = 0;
> > +
> > +out:
> > + if (err < 0)
> > reftable_free(itr);
> > - } else {
> > - *dest = itr;
> > - }
> > return err;
> > }
>
> Unless the service the helper function offers is to upgrade an
> existing resource (e.g., realloc() taking a pointer and give an
> enlarged piece of memory), it may be a safer calling convention to
> promise that *dest is cleared to NULL when the function fails,
> instead of promising that *dest is left intact. The caller, when it
> needs to evantually release the resource acquired here, has to
> remember what the returned value (i.e., err) was, in order to decide
> if it needs to call the release helper on *dest it obtained from us.
>
> The only caller seems to initialize *dest to NULL itself, so it does
> not matter in the current code, though.
I don't really see it as much of a problem here, mostly because this
function is internal to the reftable library anyway. Also, callers
essentially have to NULL-initialize the variable anyway once there are
multiple error paths and if they want to free it, because otherwise they
could end up freeing a uninitialized pointer.
On the other hand it doesn't hurt much to assign `NULL` on the error
path either, so I'll just do that.
Patrick
^ permalink raw reply [flat|nested] 152+ messages in thread
* Re: [PATCH 04/22] reftable/basics: handle allocation failures in `reftable_calloc()`
2024-09-24 5:48 ` Patrick Steinhardt
@ 2024-09-24 6:02 ` Patrick Steinhardt
2024-09-24 16:39 ` Junio C Hamano
1 sibling, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-24 6:02 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git, Edward Thomson
On Tue, Sep 24, 2024 at 07:48:57AM +0200, Patrick Steinhardt wrote:
> On Sat, Sep 21, 2024 at 12:37:26PM -0700, Junio C Hamano wrote:
> > Patrick Steinhardt <ps@pks.im> writes:
> > In a sense, it is on the borderline to handle st_mult() overflow in
> > this function for a topic whose theme is about allocation failures.
> >
> > From the point of view of callers of reftable_calloc(), whether the
> > arguments they are feeding the function is too large to be
> > multiplied or whether the request is too big for the underlying
> > allocator to handle, the end result should be the same: they
> > requested too large an allocation.
> >
> > So I wouldn't complain that it is out of scope, if use of st_mult()
> > that computes the allocation size is fixed as part of this series.
> > But as I already said, I am also OK if we leave it to a separate
> > series to tackle other potential callers of die().
>
> I'd leave it as-is for now, but I do have it on my agenda to address
> this, as well. I already have it as part of my third patch series in
> this context where I completely detangle the reftable library from the
> rest of Git to make it a reusable library for libgit2 and the likes.
Well, you know. I reroll the series anyway, so I'll just make the change
now.
Patrick
^ permalink raw reply [flat|nested] 152+ messages in thread
* [PATCH v2 00/22] reftable: handle allocation errors
2024-09-16 12:28 [PATCH 00/22] reftable: handle allocation errors Patrick Steinhardt
` (21 preceding siblings ...)
2024-09-16 12:29 ` [PATCH 22/22] reftable: handle trivial " Patrick Steinhardt
@ 2024-09-24 6:31 ` Patrick Steinhardt
2024-09-24 6:31 ` [PATCH v2 01/22] reftable/error: introduce out-of-memory error code Patrick Steinhardt
` (21 more replies)
2024-09-30 8:08 ` [PATCH v3 00/22] refatble: handle allocation errors Patrick Steinhardt
` (2 subsequent siblings)
25 siblings, 22 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-24 6:31 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano
Hi,
this is the second 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 v1:
- Remove our use of `st_mult()` in `reftable_calloc()`.
- Initialize the `*dest` pointer with `NULL` in case creating a new
indexed table iterator fails.
Thanks!
Patrick
Patrick Steinhardt (22):
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
Makefile | 1 -
refs/reftable-backend.c | 39 ++++--
reftable/basics.c | 92 ++++++++++++++-
reftable/basics.h | 13 +-
reftable/block.c | 23 +++-
reftable/block.h | 4 +-
reftable/blocksource.c | 25 +++-
reftable/error.c | 2 +
reftable/iter.c | 20 +++-
reftable/iter.h | 2 +-
reftable/merged.c | 84 ++++++++-----
reftable/merged.h | 6 +-
reftable/pq.c | 7 +-
reftable/pq.h | 2 +-
reftable/publicbasics.c | 66 -----------
reftable/reader.c | 68 ++++++++---
reftable/reader.h | 6 +-
reftable/record.c | 164 +++++++++++++++++++-------
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 | 177 ++++++++++++++++++++++------
reftable/tree.c | 42 +++++--
reftable/tree.h | 21 +++-
reftable/writer.c | 150 +++++++++++++++--------
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 | 61 ++++++----
t/unit-tests/t-reftable-stack.c | 4 +-
t/unit-tests/t-reftable-tree.c | 10 +-
38 files changed, 853 insertions(+), 386 deletions(-)
delete mode 100644 reftable/publicbasics.c
create mode 100644 reftable/reftable-basics.h
delete mode 100644 reftable/reftable-malloc.h
Range-diff against v1:
1: 8c99ecc3255 = 1: 8c99ecc3255 reftable/error: introduce out-of-memory error code
2: 4dcdf1d48ec = 2: 4dcdf1d48ec reftable/basics: merge "publicbasics" into "basics"
3: 21fa9b15d96 = 3: 21fa9b15d96 reftable: introduce `reftable_strdup()`
4: e6ded75f630 ! 4: f6ad92ffd01 reftable/basics: handle allocation failures in `reftable_calloc()`
@@ Commit message
Handle 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.
+
Signed-off-by: Patrick Steinhardt <ps@pks.im>
## reftable/basics.c ##
-@@ reftable/basics.c: void *reftable_calloc(size_t nelem, size_t elsize)
+@@ reftable/basics.c: 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);
+- 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, sz);
++
++ memset(p, 0, nelem * elsize);
return p;
}
+
5: 1f98abe9812 = 5: ad028020df7 reftable/basics: handle allocation failures in `parse_names()`
6: fa32be01e50 = 6: df713fbe08c reftable/record: handle allocation failures on copy
7: 372e36f880e = 7: 870bb003c0a reftable/record: handle allocation failures when decoding records
8: 7404d648a9d = 8: 1d47e425009 reftable/writer: handle allocation failures in `writer_index_hash()`
9: d6afb30221e = 9: caa71f0a775 reftable/writer: handle allocation failures in `reftable_new_writer()`
10: 349fd785a81 = 10: a84e9cadae4 reftable/merged: handle allocation failures in `merged_table_init_iter()`
11: c4985e64ce2 = 11: 20d38330141 reftable/reader: handle allocation failures for unindexed reader
12: 94c85ffd5ec = 12: e35c3a705d5 reftable/reader: handle allocation failures in `reader_init_iter()`
13: 6ef25b6c655 = 13: ca3b57f151e reftable/stack: handle allocation failures on reload
14: f5583cc2300 = 14: 7377421a632 reftable/stack: handle allocation failures in `reftable_new_stack()`
15: 3393a59ce42 = 15: 244e8667c5d reftable/stack: handle allocation failures in `stack_compact_range()`
16: 2c19f258fe7 = 16: 99f4868c38f reftable/stack: handle allocation failures in auto compaction
17: 32fead57de9 ! 17: 271839a6260 reftable/iter: handle allocation failures when creating indexed table iter
@@ reftable/iter.c: int new_indexed_table_ref_iter(struct indexed_table_ref_iter **
itr->offset_len = offset_len;
err = indexed_table_ref_iter_next_block(itr);
-- if (err < 0) {
+ if (err < 0)
+ goto out;
+
@@ reftable/iter.c: int new_indexed_table_ref_iter(struct indexed_table_ref_iter **
+ err = 0;
+
+out:
-+ if (err < 0)
+ if (err < 0) {
++ *dest = NULL;
reftable_free(itr);
- } else {
- *dest = itr;
-- }
+ }
return err;
}
-
## reftable/iter.h ##
@@ reftable/iter.h: void iterator_from_indexed_table_ref_iter(struct reftable_iterator *it,
18: 4d672ab9b15 = 18: c41808e9d75 reftable/blocksource: handle allocation failures
19: 4fa47a559b2 = 19: 9348b0a4575 reftable/block: handle allocation failures
20: a569c39fd8e = 20: 445daf9464d reftable/pq: handle allocation failures when adding entries
21: 99bca688f2a = 21: c31f9e53f52 reftable/tree: handle allocation failures
22: 02073dafb96 = 22: d0fe9993716 reftable: handle trivial allocation failures
--
2.46.0.551.gc5ee8f2d1c.dirty
^ permalink raw reply [flat|nested] 152+ messages in thread
* [PATCH v2 01/22] reftable/error: introduce out-of-memory error code
2024-09-24 6:31 ` [PATCH v2 00/22] reftable: handle allocation errors Patrick Steinhardt
@ 2024-09-24 6:31 ` Patrick Steinhardt
2024-09-24 6:31 ` [PATCH v2 02/22] reftable/basics: merge "publicbasics" into "basics" Patrick Steinhardt
` (20 subsequent siblings)
21 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-24 6:31 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano
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.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/error.c | 2 ++
reftable/reftable-error.h | 3 +++
2 files changed, 5 insertions(+)
diff --git a/reftable/error.c b/reftable/error.c
index a25f28a43eb..660d0296170 100644
--- a/reftable/error.c
+++ b/reftable/error.c
@@ -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:
diff --git a/reftable/reftable-error.h b/reftable/reftable-error.h
index 6368cd9ed9d..f4048265629 100644
--- a/reftable/reftable-error.h
+++ b/reftable/reftable-error.h
@@ -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
--
2.46.0.551.gc5ee8f2d1c.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v2 02/22] reftable/basics: merge "publicbasics" into "basics"
2024-09-24 6:31 ` [PATCH v2 00/22] reftable: handle allocation errors Patrick Steinhardt
2024-09-24 6:31 ` [PATCH v2 01/22] reftable/error: introduce out-of-memory error code Patrick Steinhardt
@ 2024-09-24 6:31 ` Patrick Steinhardt
2024-09-24 6:31 ` [PATCH v2 03/22] reftable: introduce `reftable_strdup()` Patrick Steinhardt
` (19 subsequent siblings)
21 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-24 6:31 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano
The 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.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
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
diff --git a/Makefile b/Makefile
index e3abf0ba831..39b10923d49 100644
--- a/Makefile
+++ b/Makefile
@@ -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
diff --git a/reftable/basics.c b/reftable/basics.c
index 0058619ca67..cf072935c88 100644
--- a/reftable/basics.c
+++ b/reftable/basics.c
@@ -7,6 +7,49 @@ license that can be found in the LICENSE file or at
*/
#include "basics.h"
+#include "reftable-basics.h"
+
+static void *(*reftable_malloc_ptr)(size_t sz);
+static void *(*reftable_realloc_ptr)(void *, size_t);
+static void (*reftable_free_ptr)(void *);
+
+void *reftable_malloc(size_t sz)
+{
+ if (reftable_malloc_ptr)
+ return (*reftable_malloc_ptr)(sz);
+ return malloc(sz);
+}
+
+void *reftable_realloc(void *p, size_t sz)
+{
+ if (reftable_realloc_ptr)
+ return (*reftable_realloc_ptr)(p, sz);
+ return realloc(p, sz);
+}
+
+void reftable_free(void *p)
+{
+ if (reftable_free_ptr)
+ reftable_free_ptr(p);
+ else
+ free(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);
+ return p;
+}
+
+void reftable_set_alloc(void *(*malloc)(size_t),
+ void *(*realloc)(void *, size_t), void (*free)(void *))
+{
+ reftable_malloc_ptr = malloc;
+ reftable_realloc_ptr = realloc;
+ reftable_free_ptr = free;
+}
void put_be24(uint8_t *out, uint32_t i)
{
@@ -121,3 +164,15 @@ int common_prefix_size(struct strbuf *a, struct strbuf *b)
return p;
}
+
+int hash_size(uint32_t id)
+{
+ switch (id) {
+ case 0:
+ case GIT_SHA1_FORMAT_ID:
+ return GIT_SHA1_RAWSZ;
+ case GIT_SHA256_FORMAT_ID:
+ return GIT_SHA256_RAWSZ;
+ }
+ abort();
+}
diff --git a/reftable/basics.h b/reftable/basics.h
index c8fec68d4e8..4e2e76014aa 100644
--- a/reftable/basics.h
+++ b/reftable/basics.h
@@ -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
diff --git a/reftable/publicbasics.c b/reftable/publicbasics.c
deleted file mode 100644
index 44b84a125e4..00000000000
--- a/reftable/publicbasics.c
+++ /dev/null
@@ -1,66 +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
-*/
-
-#include "system.h"
-#include "reftable-malloc.h"
-
-#include "basics.h"
-
-static void *(*reftable_malloc_ptr)(size_t sz);
-static void *(*reftable_realloc_ptr)(void *, size_t);
-static void (*reftable_free_ptr)(void *);
-
-void *reftable_malloc(size_t sz)
-{
- if (reftable_malloc_ptr)
- return (*reftable_malloc_ptr)(sz);
- return malloc(sz);
-}
-
-void *reftable_realloc(void *p, size_t sz)
-{
- if (reftable_realloc_ptr)
- return (*reftable_realloc_ptr)(p, sz);
- return realloc(p, sz);
-}
-
-void reftable_free(void *p)
-{
- if (reftable_free_ptr)
- reftable_free_ptr(p);
- else
- free(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);
- return p;
-}
-
-void reftable_set_alloc(void *(*malloc)(size_t),
- void *(*realloc)(void *, size_t), void (*free)(void *))
-{
- reftable_malloc_ptr = malloc;
- reftable_realloc_ptr = realloc;
- reftable_free_ptr = free;
-}
-
-int hash_size(uint32_t id)
-{
- switch (id) {
- case 0:
- case GIT_SHA1_FORMAT_ID:
- return GIT_SHA1_RAWSZ;
- case GIT_SHA256_FORMAT_ID:
- return GIT_SHA256_RAWSZ;
- }
- abort();
-}
diff --git a/reftable/reftable-basics.h b/reftable/reftable-basics.h
new file mode 100644
index 00000000000..6e8e636b716
--- /dev/null
+++ b/reftable/reftable-basics.h
@@ -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
diff --git a/reftable/reftable-malloc.h b/reftable/reftable-malloc.h
deleted file mode 100644
index 5f2185f1f34..00000000000
--- a/reftable/reftable-malloc.h
+++ /dev/null
@@ -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
--
2.46.0.551.gc5ee8f2d1c.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v2 03/22] reftable: introduce `reftable_strdup()`
2024-09-24 6:31 ` [PATCH v2 00/22] reftable: handle allocation errors Patrick Steinhardt
2024-09-24 6:31 ` [PATCH v2 01/22] reftable/error: introduce out-of-memory error code Patrick Steinhardt
2024-09-24 6:31 ` [PATCH v2 02/22] reftable/basics: merge "publicbasics" into "basics" Patrick Steinhardt
@ 2024-09-24 6:31 ` Patrick Steinhardt
2024-09-24 6:32 ` [PATCH v2 04/22] reftable/basics: handle allocation failures in `reftable_calloc()` Patrick Steinhardt
` (18 subsequent siblings)
21 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-24 6:31 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano
The 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.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/basics.c | 10 ++++++++++
reftable/basics.h | 1 +
2 files changed, 11 insertions(+)
diff --git a/reftable/basics.c b/reftable/basics.c
index cf072935c88..4adc98cf5de 100644
--- a/reftable/basics.c
+++ b/reftable/basics.c
@@ -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 *))
{
diff --git a/reftable/basics.h b/reftable/basics.h
index 4e2e76014aa..f107e148605 100644
--- a/reftable/basics.h
+++ b/reftable/basics.h
@@ -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)))
--
2.46.0.551.gc5ee8f2d1c.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v2 04/22] reftable/basics: handle allocation failures in `reftable_calloc()`
2024-09-24 6:31 ` [PATCH v2 00/22] reftable: handle allocation errors Patrick Steinhardt
` (2 preceding siblings ...)
2024-09-24 6:31 ` [PATCH v2 03/22] reftable: introduce `reftable_strdup()` Patrick Steinhardt
@ 2024-09-24 6:32 ` Patrick Steinhardt
2024-09-24 16:59 ` Junio C Hamano
2024-09-24 6:32 ` [PATCH v2 05/22] reftable/basics: handle allocation failures in `parse_names()` Patrick Steinhardt
` (17 subsequent siblings)
21 siblings, 1 reply; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-24 6:32 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano
Handle 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.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/basics.c | 13 ++++++++++---
1 file changed, 10 insertions(+), 3 deletions(-)
diff --git a/reftable/basics.c b/reftable/basics.c
index 4adc98cf5de..3350bbffa23 100644
--- a/reftable/basics.c
+++ b/reftable/basics.c
@@ -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;
}
--
2.46.0.551.gc5ee8f2d1c.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v2 05/22] reftable/basics: handle allocation failures in `parse_names()`
2024-09-24 6:31 ` [PATCH v2 00/22] reftable: handle allocation errors Patrick Steinhardt
` (3 preceding siblings ...)
2024-09-24 6:32 ` [PATCH v2 04/22] reftable/basics: handle allocation failures in `reftable_calloc()` Patrick Steinhardt
@ 2024-09-24 6:32 ` Patrick Steinhardt
2024-09-24 22:19 ` René Scharfe
2024-09-24 6:32 ` [PATCH v2 06/22] reftable/record: handle allocation failures on copy Patrick Steinhardt
` (16 subsequent siblings)
21 siblings, 1 reply; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-24 6:32 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano
Handle 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.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
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(-)
diff --git a/reftable/basics.c b/reftable/basics.c
index 3350bbffa23..82bfb807631 100644
--- a/reftable/basics.c
+++ b/reftable/basics.c
@@ -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] = 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++)
+ free(names[i]);
+ free(names);
+ return NULL;
}
int names_equal(const char **a, const char **b)
diff --git a/reftable/basics.h b/reftable/basics.h
index f107e148605..69adeab2e4b 100644
--- a/reftable/basics.h
+++ b/reftable/basics.h
@@ -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);
diff --git a/reftable/stack.c b/reftable/stack.c
index ce0a35216ba..498fae846d7 100644
--- a/reftable/stack.c
+++ b/reftable/stack.c
@@ -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);
diff --git a/t/unit-tests/t-reftable-basics.c b/t/unit-tests/t-reftable-basics.c
index e5556ebf527..1fa77b6faff 100644
--- a/t/unit-tests/t-reftable-basics.c
+++ b/t/unit-tests/t-reftable-basics.c
@@ -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");
--
2.46.0.551.gc5ee8f2d1c.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v2 06/22] reftable/record: handle allocation failures on copy
2024-09-24 6:31 ` [PATCH v2 00/22] reftable: handle allocation errors Patrick Steinhardt
` (4 preceding siblings ...)
2024-09-24 6:32 ` [PATCH v2 05/22] reftable/basics: handle allocation failures in `parse_names()` Patrick Steinhardt
@ 2024-09-24 6:32 ` Patrick Steinhardt
2024-09-24 6:32 ` [PATCH v2 07/22] reftable/record: handle allocation failures when decoding records Patrick Steinhardt
` (15 subsequent siblings)
21 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-24 6:32 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano
Handle allocation failures when copying records. While at it, convert
from `xstrdup()` to `reftable_strdup()`. Adapt callsites to check for
error codes.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/record.c | 84 +++++++++++++++++++++++++++++++++--------------
reftable/record.h | 6 ++--
2 files changed, 63 insertions(+), 27 deletions(-)
diff --git a/reftable/record.c b/reftable/record.c
index 6b5a075b921..60fd33c9c94 100644
--- a/reftable/record.c
+++ b/reftable/record.c
@@ -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)
diff --git a/reftable/record.h b/reftable/record.h
index 5003bacdb0c..0f53ba54434 100644
--- a/reftable/record.h
+++ b/reftable/record.h
@@ -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);
--
2.46.0.551.gc5ee8f2d1c.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v2 07/22] reftable/record: handle allocation failures when decoding records
2024-09-24 6:31 ` [PATCH v2 00/22] reftable: handle allocation errors Patrick Steinhardt
` (5 preceding siblings ...)
2024-09-24 6:32 ` [PATCH v2 06/22] reftable/record: handle allocation failures on copy Patrick Steinhardt
@ 2024-09-24 6:32 ` Patrick Steinhardt
2024-09-24 6:32 ` [PATCH v2 08/22] reftable/writer: handle allocation failures in `writer_index_hash()` Patrick Steinhardt
` (14 subsequent siblings)
21 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-24 6:32 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano
Handle allocation failures when decoding records. While at it, fix some
error codes to be `REFTABLE_FORMAT_ERROR`.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/record.c | 80 +++++++++++++++++++++++++++++++++++++----------
1 file changed, 63 insertions(+), 17 deletions(-)
diff --git a/reftable/record.c b/reftable/record.c
index 60fd33c9c94..787e134c9a0 100644
--- a/reftable/record.c
+++ b/reftable/record.c
@@ -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)
--
2.46.0.551.gc5ee8f2d1c.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v2 08/22] reftable/writer: handle allocation failures in `writer_index_hash()`
2024-09-24 6:31 ` [PATCH v2 00/22] reftable: handle allocation errors Patrick Steinhardt
` (6 preceding siblings ...)
2024-09-24 6:32 ` [PATCH v2 07/22] reftable/record: handle allocation failures when decoding records Patrick Steinhardt
@ 2024-09-24 6:32 ` Patrick Steinhardt
2024-09-24 6:32 ` [PATCH v2 09/22] reftable/writer: handle allocation failures in `reftable_new_writer()` Patrick Steinhardt
` (13 subsequent siblings)
21 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-24 6:32 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano
Handle allocation errors in `writer_index_hash()`. Adjust its only
caller in `reftable_writer_add_ref()` accordingly.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/writer.c | 61 +++++++++++++++++++++++++++++------------------
1 file changed, 38 insertions(+), 23 deletions(-)
diff --git a/reftable/writer.c b/reftable/writer.c
index 9d5e6072bc6..ed61aaf59c5 100644
--- a/reftable/writer.c
+++ b/reftable/writer.c
@@ -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,
--
2.46.0.551.gc5ee8f2d1c.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v2 09/22] reftable/writer: handle allocation failures in `reftable_new_writer()`
2024-09-24 6:31 ` [PATCH v2 00/22] reftable: handle allocation errors Patrick Steinhardt
` (7 preceding siblings ...)
2024-09-24 6:32 ` [PATCH v2 08/22] reftable/writer: handle allocation failures in `writer_index_hash()` Patrick Steinhardt
@ 2024-09-24 6:32 ` Patrick Steinhardt
2024-09-24 6:32 ` [PATCH v2 10/22] reftable/merged: handle allocation failures in `merged_table_init_iter()` Patrick Steinhardt
` (12 subsequent siblings)
21 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-24 6:32 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano
Handle 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.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
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(-)
diff --git a/reftable/reftable-writer.h b/reftable/reftable-writer.h
index 189b1f4144f..43623dc7c30 100644
--- a/reftable/reftable-writer.h
+++ b/reftable/reftable-writer.h
@@ -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
diff --git a/reftable/stack.c b/reftable/stack.c
index 498fae846d7..ea21ca6e5f5 100644
--- a/reftable/stack.c
+++ b/reftable/stack.c
@@ -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;
diff --git a/reftable/writer.c b/reftable/writer.c
index ed61aaf59c5..54ec822e1cd 100644
--- a/reftable/writer.c
+++ b/reftable/writer.c
@@ -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) {
+ 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,
diff --git a/t/unit-tests/lib-reftable.c b/t/unit-tests/lib-reftable.c
index ab1fa44a282..54c26c43e77 100644
--- a/t/unit-tests/lib-reftable.c
+++ b/t/unit-tests/lib-reftable.c
@@ -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,
--
2.46.0.551.gc5ee8f2d1c.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v2 10/22] reftable/merged: handle allocation failures in `merged_table_init_iter()`
2024-09-24 6:31 ` [PATCH v2 00/22] reftable: handle allocation errors Patrick Steinhardt
` (8 preceding siblings ...)
2024-09-24 6:32 ` [PATCH v2 09/22] reftable/writer: handle allocation failures in `reftable_new_writer()` Patrick Steinhardt
@ 2024-09-24 6:32 ` Patrick Steinhardt
2024-09-24 6:32 ` [PATCH v2 11/22] reftable/reader: handle allocation failures for unindexed reader Patrick Steinhardt
` (11 subsequent siblings)
21 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-24 6:32 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano
Handle 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.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
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(-)
diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c
index 5c241097a4e..9c08c46c85e 100644
--- a/refs/reftable-backend.c
+++ b/refs/reftable-backend.c
@@ -1299,7 +1299,9 @@ static int write_transaction_table(struct reftable_writer *writer, void *cb_data
struct 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 reftabl
if (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_da
reftable_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)
diff --git a/reftable/merged.c b/reftable/merged.c
index 128a810c55d..5a8ea8ae779 100644
--- a/reftable/merged.c
+++ b/reftable/merged.c
@@ -30,22 +30,6 @@ struct merged_iter {
ssize_t advance_index;
};
-static void merged_iter_init(struct merged_iter *mi,
- struct reftable_merged_table *mt,
- uint8_t typ)
-{
- memset(mi, 0, sizeof(*mi));
- mi->advance_index = -1;
- mi->suppress_deletions = mt->suppress_deletions;
-
- REFTABLE_CALLOC_ARRAY(mi->subiters, mt->readers_len);
- for (size_t i = 0; i < mt->readers_len; i++) {
- reftable_record_init(&mi->subiters[i].rec, typ);
- reader_init_iter(mt->readers[i], &mi->subiters[i].iter, typ);
- }
- mi->subiters_len = mt->readers_len;
-}
-
static void merged_iter_close(void *p)
{
struct merged_iter *mi = p;
@@ -244,25 +228,61 @@ reftable_merged_table_min_update_index(struct reftable_merged_table *mt)
return mt->min;
}
-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)
{
- struct merged_iter *mi = reftable_malloc(sizeof(*mi));
- merged_iter_init(mi, mt, typ);
+ struct merged_subiter *subiters;
+ struct merged_iter *mi = NULL;
+ int ret;
+
+ REFTABLE_CALLOC_ARRAY(subiters, mt->readers_len);
+ if (!subiters) {
+ ret = REFTABLE_OUT_OF_MEMORY_ERROR;
+ goto out;
+ }
+
+ 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);
+ }
+
+ REFTABLE_CALLOC_ARRAY(mi, 1);
+ if (!mi) {
+ ret = REFTABLE_OUT_OF_MEMORY_ERROR;
+ goto out;
+ }
+ mi->advance_index = -1;
+ mi->suppress_deletions = mt->suppress_deletions;
+ mi->subiters = subiters;
+ mi->subiters_len = mt->readers_len;
+
iterator_from_merged_iter(it, mi);
+ ret = 0;
+
+out:
+ if (ret < 0) {
+ for (size_t i = 0; subiters && i < mt->readers_len; i++) {
+ reftable_iterator_destroy(&subiters[i].iter);
+ reftable_record_release(&subiters[i].rec);
+ }
+ free(subiters);
+ free(mi);
+ }
+
+ return ret;
}
-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)
{
- merged_table_init_iter(mt, it, BLOCK_TYPE_REF);
+ return merged_table_init_iter(mt, it, BLOCK_TYPE_REF);
}
-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)
{
- merged_table_init_iter(mt, it, BLOCK_TYPE_LOG);
+ return merged_table_init_iter(mt, it, BLOCK_TYPE_LOG);
}
uint32_t reftable_merged_table_hash_id(struct reftable_merged_table *mt)
diff --git a/reftable/merged.h b/reftable/merged.h
index de5fd33f010..89bd0c4b35b 100644
--- a/reftable/merged.h
+++ b/reftable/merged.h
@@ -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
diff --git a/reftable/reftable-merged.h b/reftable/reftable-merged.h
index 16d19f8df20..a970d5dd89a 100644
--- a/reftable/reftable-merged.h
+++ b/reftable/reftable-merged.h
@@ -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
diff --git a/reftable/reftable-stack.h b/reftable/reftable-stack.h
index f4f8cabc7fb..e958f911b4e 100644
--- a/reftable/reftable-stack.h
+++ b/reftable/reftable-stack.h
@@ -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.
diff --git a/reftable/stack.c b/reftable/stack.c
index ea21ca6e5f5..bb4d2309189 100644
--- a/reftable/stack.c
+++ b/reftable/stack.c
@@ -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;
diff --git a/t/helper/test-reftable.c b/t/helper/test-reftable.c
index 29d4e9a755d..5c8849d115b 100644
--- a/t/helper/test-reftable.c
+++ b/t/helper/test-reftable.c
@@ -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;
diff --git a/t/unit-tests/t-reftable-merged.c b/t/unit-tests/t-reftable-merged.c
index 19e54bdfb8b..3d2848632db 100644
--- a/t/unit-tests/t-reftable-merged.c
+++ b/t/unit-tests/t-reftable-merged.c
@@ -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]);
diff --git a/t/unit-tests/t-reftable-stack.c b/t/unit-tests/t-reftable-stack.c
index 65e513d5ec8..6e41a4cf7e5 100644
--- a/t/unit-tests/t-reftable-stack.c
+++ b/t/unit-tests/t-reftable-stack.c
@@ -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 };
--
2.46.0.551.gc5ee8f2d1c.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v2 11/22] reftable/reader: handle allocation failures for unindexed reader
2024-09-24 6:31 ` [PATCH v2 00/22] reftable: handle allocation errors Patrick Steinhardt
` (9 preceding siblings ...)
2024-09-24 6:32 ` [PATCH v2 10/22] reftable/merged: handle allocation failures in `merged_table_init_iter()` Patrick Steinhardt
@ 2024-09-24 6:32 ` Patrick Steinhardt
2024-09-24 6:32 ` [PATCH v2 12/22] reftable/reader: handle allocation failures in `reader_init_iter()` Patrick Steinhardt
` (10 subsequent siblings)
21 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-24 6:32 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano
Handle allocation failures when creating unindexed readers.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/reader.c | 23 +++++++++++++++++------
1 file changed, 17 insertions(+), 6 deletions(-)
diff --git a/reftable/reader.c b/reftable/reader.c
index 6494ce2e327..485ee085dac 100644
--- a/reftable/reader.c
+++ b/reftable/reader.c
@@ -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,
--
2.46.0.551.gc5ee8f2d1c.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v2 12/22] reftable/reader: handle allocation failures in `reader_init_iter()`
2024-09-24 6:31 ` [PATCH v2 00/22] reftable: handle allocation errors Patrick Steinhardt
` (10 preceding siblings ...)
2024-09-24 6:32 ` [PATCH v2 11/22] reftable/reader: handle allocation failures for unindexed reader Patrick Steinhardt
@ 2024-09-24 6:32 ` Patrick Steinhardt
2024-09-24 6:32 ` [PATCH v2 13/22] reftable/stack: handle allocation failures on reload Patrick Steinhardt
` (9 subsequent siblings)
21 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-24 6:32 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano
Handle 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.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
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(-)
diff --git a/reftable/merged.c b/reftable/merged.c
index 5a8ea8ae779..741f62ea638 100644
--- a/reftable/merged.c
+++ b/reftable/merged.c
@@ -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);
diff --git a/reftable/reader.c b/reftable/reader.c
index 485ee085dac..f696e992dfc 100644
--- a/reftable/reader.c
+++ b/reftable/reader.c
@@ -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;
diff --git a/reftable/reader.h b/reftable/reader.h
index 3710ee09b4c..02d10c5d37e 100644
--- a/reftable/reader.h
+++ b/reftable/reader.h
@@ -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,
diff --git a/reftable/reftable-reader.h b/reftable/reftable-reader.h
index a600452b565..6a2d0b693f5 100644
--- a/reftable/reftable-reader.h
+++ b/reftable/reftable-reader.h
@@ -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);
diff --git a/t/unit-tests/t-reftable-readwrite.c b/t/unit-tests/t-reftable-readwrite.c
index e1b235a5f13..acca927a2cf 100644
--- a/t/unit-tests/t-reftable-readwrite.c
+++ b/t/unit-tests/t-reftable-readwrite.c
@@ -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);
--
2.46.0.551.gc5ee8f2d1c.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v2 13/22] reftable/stack: handle allocation failures on reload
2024-09-24 6:31 ` [PATCH v2 00/22] reftable: handle allocation errors Patrick Steinhardt
` (11 preceding siblings ...)
2024-09-24 6:32 ` [PATCH v2 12/22] reftable/reader: handle allocation failures in `reader_init_iter()` Patrick Steinhardt
@ 2024-09-24 6:32 ` Patrick Steinhardt
2024-09-24 6:32 ` [PATCH v2 14/22] reftable/stack: handle allocation failures in `reftable_new_stack()` Patrick Steinhardt
` (8 subsequent siblings)
21 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-24 6:32 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano
Handle allocation failures in `reftable_stack_reload_once()`.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/stack.c | 34 +++++++++++++++++++++++++---------
1 file changed, 25 insertions(+), 9 deletions(-)
diff --git a/reftable/stack.c b/reftable/stack.c
index bb4d2309189..060b2c1b90d 100644
--- a/reftable/stack.c
+++ b/reftable/stack.c
@@ -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;
--
2.46.0.551.gc5ee8f2d1c.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v2 14/22] reftable/stack: handle allocation failures in `reftable_new_stack()`
2024-09-24 6:31 ` [PATCH v2 00/22] reftable: handle allocation errors Patrick Steinhardt
` (12 preceding siblings ...)
2024-09-24 6:32 ` [PATCH v2 13/22] reftable/stack: handle allocation failures on reload Patrick Steinhardt
@ 2024-09-24 6:32 ` Patrick Steinhardt
2024-09-24 6:32 ` [PATCH v2 15/22] reftable/stack: handle allocation failures in `stack_compact_range()` Patrick Steinhardt
` (7 subsequent siblings)
21 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-24 6:32 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano
Handle allocation failures in `reftable_new_stack()`.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/stack.c | 34 ++++++++++++++++++++++++++--------
1 file changed, 26 insertions(+), 8 deletions(-)
diff --git a/reftable/stack.c b/reftable/stack.c
index 060b2c1b90d..1b77c9d0146 100644
--- a/reftable/stack.c
+++ b/reftable/stack.c
@@ -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;
--
2.46.0.551.gc5ee8f2d1c.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v2 15/22] reftable/stack: handle allocation failures in `stack_compact_range()`
2024-09-24 6:31 ` [PATCH v2 00/22] reftable: handle allocation errors Patrick Steinhardt
` (13 preceding siblings ...)
2024-09-24 6:32 ` [PATCH v2 14/22] reftable/stack: handle allocation failures in `reftable_new_stack()` Patrick Steinhardt
@ 2024-09-24 6:32 ` Patrick Steinhardt
2024-09-24 6:32 ` [PATCH v2 16/22] reftable/stack: handle allocation failures in auto compaction Patrick Steinhardt
` (6 subsequent siblings)
21 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-24 6:32 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano
Handle allocation failures in `stack_compact_range()`.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/stack.c | 19 +++++++++++++++++--
1 file changed, 17 insertions(+), 2 deletions(-)
diff --git a/reftable/stack.c b/reftable/stack.c
index 1b77c9d0146..2e6dd513d72 100644
--- a/reftable/stack.c
+++ b/reftable/stack.c
@@ -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;
}
--
2.46.0.551.gc5ee8f2d1c.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v2 16/22] reftable/stack: handle allocation failures in auto compaction
2024-09-24 6:31 ` [PATCH v2 00/22] reftable: handle allocation errors Patrick Steinhardt
` (14 preceding siblings ...)
2024-09-24 6:32 ` [PATCH v2 15/22] reftable/stack: handle allocation failures in `stack_compact_range()` Patrick Steinhardt
@ 2024-09-24 6:32 ` Patrick Steinhardt
2024-09-24 6:32 ` [PATCH v2 17/22] reftable/iter: handle allocation failures when creating indexed table iter Patrick Steinhardt
` (5 subsequent siblings)
21 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-24 6:32 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano
Handle allocation failures in `reftable_stack_auto_compact()`.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/stack.c | 16 ++++++++++++----
1 file changed, 12 insertions(+), 4 deletions(-)
diff --git a/reftable/stack.c b/reftable/stack.c
index 2e6dd513d72..990784d9d2f 100644
--- a/reftable/stack.c
+++ b/reftable/stack.c
@@ -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);
--
2.46.0.551.gc5ee8f2d1c.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v2 17/22] reftable/iter: handle allocation failures when creating indexed table iter
2024-09-24 6:31 ` [PATCH v2 00/22] reftable: handle allocation errors Patrick Steinhardt
` (15 preceding siblings ...)
2024-09-24 6:32 ` [PATCH v2 16/22] reftable/stack: handle allocation failures in auto compaction Patrick Steinhardt
@ 2024-09-24 6:32 ` Patrick Steinhardt
2024-09-24 6:32 ` [PATCH v2 18/22] reftable/blocksource: handle allocation failures Patrick Steinhardt
` (4 subsequent siblings)
21 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-24 6:32 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano
Handle allocation failures in `new_indexed_table_ref_iter()`. While at
it, rename the function to match our coding style.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/iter.c | 20 ++++++++++++++++----
reftable/iter.h | 2 +-
reftable/reader.c | 7 ++++++-
3 files changed, 23 insertions(+), 6 deletions(-)
diff --git a/reftable/iter.c b/reftable/iter.c
index 416a9f6996b..d319538f804 100644
--- a/reftable/iter.c
+++ b/reftable/iter.c
@@ -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;
}
diff --git a/reftable/iter.h b/reftable/iter.h
index befc4597df1..b3225bc7add 100644
--- a/reftable/iter.h
+++ b/reftable/iter.h
@@ -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);
diff --git a/reftable/reader.c b/reftable/reader.c
index f696e992dfc..0179e4e73dd 100644
--- a/reftable/reader.c
+++ b/reftable/reader.c
@@ -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)
--
2.46.0.551.gc5ee8f2d1c.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v2 18/22] reftable/blocksource: handle allocation failures
2024-09-24 6:31 ` [PATCH v2 00/22] reftable: handle allocation errors Patrick Steinhardt
` (16 preceding siblings ...)
2024-09-24 6:32 ` [PATCH v2 17/22] reftable/iter: handle allocation failures when creating indexed table iter Patrick Steinhardt
@ 2024-09-24 6:32 ` Patrick Steinhardt
2024-09-24 6:32 ` [PATCH v2 19/22] reftable/block: " Patrick Steinhardt
` (3 subsequent siblings)
21 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-24 6:32 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano
Handle allocation failures in the blocksource code.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/blocksource.c | 25 ++++++++++++++++++++-----
1 file changed, 20 insertions(+), 5 deletions(-)
diff --git a/reftable/blocksource.c b/reftable/blocksource.c
index e93cac9bb6f..6b570751be6 100644
--- a/reftable/blocksource.c
+++ b/reftable/blocksource.c
@@ -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)
+ free(p);
return 0;
}
--
2.46.0.551.gc5ee8f2d1c.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v2 19/22] reftable/block: handle allocation failures
2024-09-24 6:31 ` [PATCH v2 00/22] reftable: handle allocation errors Patrick Steinhardt
` (17 preceding siblings ...)
2024-09-24 6:32 ` [PATCH v2 18/22] reftable/blocksource: handle allocation failures Patrick Steinhardt
@ 2024-09-24 6:32 ` Patrick Steinhardt
2024-09-24 6:32 ` [PATCH v2 20/22] reftable/pq: handle allocation failures when adding entries Patrick Steinhardt
` (2 subsequent siblings)
21 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-24 6:32 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano
Handle 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.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
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(-)
diff --git a/reftable/block.c b/reftable/block.c
index 00030eee065..bfa7dc61bf5 100644
--- a/reftable/block.c
+++ b/reftable/block.c
@@ -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);
diff --git a/reftable/block.h b/reftable/block.h
index 1c8f25ee6eb..18d7ea03373 100644
--- a/reftable/block.h
+++ b/reftable/block.h
@@ -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);
diff --git a/reftable/writer.c b/reftable/writer.c
index 54ec822e1cd..712a84791b2 100644
--- a/reftable/writer.c
+++ b/reftable/writer.c
@@ -102,19 +102,24 @@ static int writer_write_header(struct reftable_writer *w, uint8_t *dest)
return header_size(writer_version(w));
}
-static void writer_reinit_block_writer(struct reftable_writer *w, uint8_t typ)
+static int writer_reinit_block_writer(struct reftable_writer *w, uint8_t typ)
{
- int block_start = 0;
- if (w->next == 0) {
+ int block_start = 0, ret;
+
+ if (w->next == 0)
block_start = header_size(writer_version(w));
- }
strbuf_reset(&w->last_key);
- block_writer_init(&w->block_writer_data, typ, w->block,
- w->opts.block_size, block_start,
- hash_size(w->opts.hash_id));
+ ret = block_writer_init(&w->block_writer_data, typ, w->block,
+ w->opts.block_size, block_start,
+ hash_size(w->opts.hash_id));
+ if (ret < 0)
+ return ret;
+
w->block_writer = &w->block_writer_data;
w->block_writer->restart_interval = w->opts.restart_interval;
+
+ return 0;
}
int reftable_writer_new(struct reftable_writer **out,
@@ -247,8 +252,11 @@ static int writer_add_record(struct reftable_writer *w,
strbuf_reset(&w->last_key);
strbuf_addbuf(&w->last_key, &key);
- if (!w->block_writer)
- writer_reinit_block_writer(w, reftable_record_type(rec));
+ if (!w->block_writer) {
+ err = writer_reinit_block_writer(w, reftable_record_type(rec));
+ if (err < 0)
+ goto done;
+ }
if (block_writer_type(w->block_writer) != reftable_record_type(rec))
BUG("record of type %d added to writer of type %d",
@@ -271,7 +279,9 @@ static int writer_add_record(struct reftable_writer *w,
err = writer_flush_block(w);
if (err < 0)
goto done;
- writer_reinit_block_writer(w, reftable_record_type(rec));
+ err = writer_reinit_block_writer(w, reftable_record_type(rec));
+ if (err < 0)
+ goto done;
/*
* Try to add the record to the writer again. If this still fails then
@@ -461,7 +471,9 @@ static int writer_finish_section(struct reftable_writer *w)
max_level++;
index_start = w->next;
- writer_reinit_block_writer(w, BLOCK_TYPE_INDEX);
+ err = writer_reinit_block_writer(w, BLOCK_TYPE_INDEX);
+ if (err < 0)
+ return err;
idx = w->index;
idx_len = w->index_len;
@@ -555,7 +567,10 @@ static void write_object_record(void *void_arg, void *key)
if (arg->err < 0)
goto done;
- writer_reinit_block_writer(arg->w, BLOCK_TYPE_OBJ);
+ arg->err = writer_reinit_block_writer(arg->w, BLOCK_TYPE_OBJ);
+ if (arg->err < 0)
+ goto done;
+
arg->err = block_writer_add(arg->w->block_writer, &rec);
if (arg->err == 0)
goto done;
@@ -584,16 +599,18 @@ static int writer_dump_object_index(struct reftable_writer *w)
struct common_prefix_arg common = {
.max = 1, /* obj_id_len should be >= 2. */
};
- if (w->obj_index_tree) {
+ int err;
+
+ if (w->obj_index_tree)
infix_walk(w->obj_index_tree, &update_common, &common);
- }
w->stats.object_id_len = common.max + 1;
- writer_reinit_block_writer(w, BLOCK_TYPE_OBJ);
+ err = writer_reinit_block_writer(w, BLOCK_TYPE_OBJ);
+ if (err < 0)
+ return err;
- if (w->obj_index_tree) {
+ if (w->obj_index_tree)
infix_walk(w->obj_index_tree, &write_object_record, &closure);
- }
if (closure.err < 0)
return closure.err;
diff --git a/t/unit-tests/t-reftable-block.c b/t/unit-tests/t-reftable-block.c
index f1a49485e23..e52a612e852 100644
--- a/t/unit-tests/t-reftable-block.c
+++ b/t/unit-tests/t-reftable-block.c
@@ -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);
--
2.46.0.551.gc5ee8f2d1c.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v2 20/22] reftable/pq: handle allocation failures when adding entries
2024-09-24 6:31 ` [PATCH v2 00/22] reftable: handle allocation errors Patrick Steinhardt
` (18 preceding siblings ...)
2024-09-24 6:32 ` [PATCH v2 19/22] reftable/block: " Patrick Steinhardt
@ 2024-09-24 6:32 ` Patrick Steinhardt
2024-09-24 6:33 ` [PATCH v2 21/22] reftable/tree: handle allocation failures Patrick Steinhardt
2024-09-24 6:33 ` [PATCH v2 22/22] reftable: handle trivial " Patrick Steinhardt
21 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-24 6:32 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano
Handle allocation failures when adding entries to the pqueue. Adapt its
only caller accordingly.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/merged.c | 5 ++++-
reftable/pq.c | 7 ++++++-
reftable/pq.h | 2 +-
3 files changed, 11 insertions(+), 3 deletions(-)
diff --git a/reftable/merged.c b/reftable/merged.c
index 741f62ea638..2c20845d624 100644
--- a/reftable/merged.c
+++ b/reftable/merged.c
@@ -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;
}
diff --git a/reftable/pq.c b/reftable/pq.c
index 2b5b7d1c0e2..03b99122829 100644
--- a/reftable/pq.c
+++ b/reftable/pq.c
@@ -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_entry
SWAP(pq->heap[j], pq->heap[i]);
i = j;
}
+
+ return 0;
}
void merged_iter_pqueue_release(struct merged_iter_pqueue *pq)
diff --git a/reftable/pq.h b/reftable/pq.h
index 707bd26767b..83c062eecac 100644
--- a/reftable/pq.h
+++ b/reftable/pq.h
@@ -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);
--
2.46.0.551.gc5ee8f2d1c.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v2 21/22] reftable/tree: handle allocation failures
2024-09-24 6:31 ` [PATCH v2 00/22] reftable: handle allocation errors Patrick Steinhardt
` (19 preceding siblings ...)
2024-09-24 6:32 ` [PATCH v2 20/22] reftable/pq: handle allocation failures when adding entries Patrick Steinhardt
@ 2024-09-24 6:33 ` Patrick Steinhardt
2024-09-24 6:33 ` [PATCH v2 22/22] reftable: handle trivial " Patrick Steinhardt
21 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-24 6:33 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano
The 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.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
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(-)
diff --git a/reftable/tree.c b/reftable/tree.c
index 5ffb2e0d690..f4dbe720901 100644
--- a/reftable/tree.c
+++ b/reftable/tree.c
@@ -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;
}
diff --git a/reftable/tree.h b/reftable/tree.h
index fbdd002e23a..9604453b6d5 100644
--- a/reftable/tree.h
+++ b/reftable/tree.h
@@ -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),
diff --git a/reftable/writer.c b/reftable/writer.c
index 712a84791b2..44bed92f467 100644
--- a/reftable/writer.c
+++ b/reftable/writer.c
@@ -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;
}
diff --git a/t/unit-tests/t-reftable-tree.c b/t/unit-tests/t-reftable-tree.c
index 700479d34b1..79b175a45a7 100644
--- a/t/unit-tests/t-reftable-tree.c
+++ b/t/unit-tests/t-reftable-tree.c
@@ -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);
--
2.46.0.551.gc5ee8f2d1c.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v2 22/22] reftable: handle trivial allocation failures
2024-09-24 6:31 ` [PATCH v2 00/22] reftable: handle allocation errors Patrick Steinhardt
` (20 preceding siblings ...)
2024-09-24 6:33 ` [PATCH v2 21/22] reftable/tree: handle allocation failures Patrick Steinhardt
@ 2024-09-24 6:33 ` Patrick Steinhardt
21 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-24 6:33 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano
Handle trivial allocation failures in the reftable library and its unit
tests.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
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(-)
diff --git a/reftable/merged.c b/reftable/merged.c
index 2c20845d624..52bc66c5273 100644
--- a/reftable/merged.c
+++ b/reftable/merged.c
@@ -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;
diff --git a/reftable/reader.c b/reftable/reader.c
index 0179e4e73dd..98e7aa26373 100644
--- a/reftable/reader.c
+++ b/reftable/reader.c
@@ -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;
diff --git a/reftable/stack.c b/reftable/stack.c
index 990784d9d2f..7df28ab3438 100644
--- a/reftable/stack.c
+++ b/reftable/stack.c
@@ -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);
diff --git a/reftable/writer.c b/reftable/writer.c
index 44bed92f467..ad4718edecb 100644
--- a/reftable/writer.c
+++ b/reftable/writer.c
@@ -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);
diff --git a/t/unit-tests/t-reftable-block.c b/t/unit-tests/t-reftable-block.c
index e52a612e852..d470060e8be 100644
--- a/t/unit-tests/t-reftable-block.c
+++ b/t/unit-tests/t-reftable-block.c
@@ -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,
diff --git a/t/unit-tests/t-reftable-merged.c b/t/unit-tests/t-reftable-merged.c
index 3d2848632db..3c84363e980 100644
--- a/t/unit-tests/t-reftable-merged.c
+++ b/t/unit-tests/t-reftable-merged.c
@@ -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);
diff --git a/t/unit-tests/t-reftable-readwrite.c b/t/unit-tests/t-reftable-readwrite.c
index acca927a2cf..bfa069caff7 100644
--- a/t/unit-tests/t-reftable-readwrite.c
+++ b/t/unit-tests/t-reftable-readwrite.c
@@ -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);
--
2.46.0.551.gc5ee8f2d1c.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* Re: [PATCH 04/22] reftable/basics: handle allocation failures in `reftable_calloc()`
2024-09-24 5:48 ` Patrick Steinhardt
2024-09-24 6:02 ` Patrick Steinhardt
@ 2024-09-24 16:39 ` Junio C Hamano
1 sibling, 0 replies; 152+ messages in thread
From: Junio C Hamano @ 2024-09-24 16:39 UTC (permalink / raw)
To: Patrick Steinhardt; +Cc: git, Edward Thomson, Emily Shaffer
Patrick Steinhardt <ps@pks.im> writes:
> In any case, there's another, bigger elephant in the room: `struct
> strbuf`. I will have to introduce a reftable-specific buffer type for
> this such that we can handle allocation failures here, too.
Oh, that one.
If you are letting your callers to register allocation callbacks,
you'd want to make sure any code you use, in cluding strbuf, would
take pieces of memory from and return them to the registered
allocation routines.
> While the
> alternative would be to amend `struct strbuf` itself to do that, I don't
> quite think that we should do it in "core" Git itself for now.
Punting is of course an option, but what do the "libification" folks
want to see happen (Emily Cc'ed, not necessarily because she
personally did work in libification, but she knows who is responding
to the list this week)? Wouldn't we eventually want these common
things not to die but to gracefully fail?
^ permalink raw reply [flat|nested] 152+ messages in thread
* Re: [PATCH v2 04/22] reftable/basics: handle allocation failures in `reftable_calloc()`
2024-09-24 6:32 ` [PATCH v2 04/22] reftable/basics: handle allocation failures in `reftable_calloc()` Patrick Steinhardt
@ 2024-09-24 16:59 ` Junio C Hamano
2024-09-26 12:11 ` Patrick Steinhardt
0 siblings, 1 reply; 152+ messages in thread
From: Junio C Hamano @ 2024-09-24 16:59 UTC (permalink / raw)
To: Patrick Steinhardt; +Cc: git, Edward Thomson
Patrick Steinhardt <ps@pks.im> writes:
> 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;
Now it is open coded, it strikes me that the check is a bit overly
conservative.
If we are trying to allocate slightly than half of SIZE_MAX by
asking elsize==1 and nelem==(SIZE_MAX / 2 + 10), we'd say that
(elsize * nelem) would not fit size_t and fail the allocation.
For the purpose of this caller, it is not a practical issue, as it
is likely that you'd not be able to obtain slightly more than half
your address space out of a single allocation anyway.
But it illustrates why open coding is not necessarily an excellent
idea in the longer term, doesn't it? When unsigned_mult_overflows()
is updated to avoid such a false positive, how would we remember
that we need to update this copy we?
> + p = reftable_malloc(nelem * elsize);
> + if (!p)
> + return NULL;
> +
> + memset(p, 0, nelem * elsize);
> return p;
> }
^ permalink raw reply [flat|nested] 152+ messages in thread
* Re: [PATCH v2 05/22] reftable/basics: handle allocation failures in `parse_names()`
2024-09-24 6:32 ` [PATCH v2 05/22] reftable/basics: handle allocation failures in `parse_names()` Patrick Steinhardt
@ 2024-09-24 22:19 ` René Scharfe
2024-09-26 12:09 ` Patrick Steinhardt
0 siblings, 1 reply; 152+ messages in thread
From: René Scharfe @ 2024-09-24 22:19 UTC (permalink / raw)
To: Patrick Steinhardt, git; +Cc: Edward Thomson, Junio C Hamano
Am 24.09.24 um 08:32 schrieb Patrick Steinhardt:
> diff --git a/reftable/basics.c b/reftable/basics.c
> index 3350bbffa23..82bfb807631 100644
> --- a/reftable/basics.c
> +++ b/reftable/basics.c
> @@ -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] = 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++)
> + free(names[i]);
> + free(names);
These are allocated with reftable_realloc() under the hood, so they'd
better be passed to reftable_free() instead, no?
René
^ permalink raw reply [flat|nested] 152+ messages in thread
* Re: [PATCH v2 05/22] reftable/basics: handle allocation failures in `parse_names()`
2024-09-24 22:19 ` René Scharfe
@ 2024-09-26 12:09 ` Patrick Steinhardt
0 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-26 12:09 UTC (permalink / raw)
To: René Scharfe; +Cc: git, Edward Thomson, Junio C Hamano
On Wed, Sep 25, 2024 at 12:19:29AM +0200, René Scharfe wrote:
> Am 24.09.24 um 08:32 schrieb Patrick Steinhardt:
> > diff --git a/reftable/basics.c b/reftable/basics.c
> > index 3350bbffa23..82bfb807631 100644
> > --- a/reftable/basics.c
> > +++ b/reftable/basics.c
> > @@ -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] = 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++)
> > + free(names[i]);
> > + free(names);
>
> These are allocated with reftable_realloc() under the hood, so they'd
> better be passed to reftable_free() instead, no?
Oh yeah, good catch! I'll wait a bit longer for feedback before sending
out a revised version.
Patrick
^ permalink raw reply [flat|nested] 152+ messages in thread
* Re: [PATCH v2 04/22] reftable/basics: handle allocation failures in `reftable_calloc()`
2024-09-24 16:59 ` Junio C Hamano
@ 2024-09-26 12:11 ` Patrick Steinhardt
2024-09-26 16:13 ` Junio C Hamano
0 siblings, 1 reply; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-26 12:11 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git, Edward Thomson
On Tue, Sep 24, 2024 at 09:59:24AM -0700, Junio C Hamano wrote:
> Patrick Steinhardt <ps@pks.im> writes:
>
> > 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;
>
> Now it is open coded, it strikes me that the check is a bit overly
> conservative.
>
> If we are trying to allocate slightly than half of SIZE_MAX by
> asking elsize==1 and nelem==(SIZE_MAX / 2 + 10), we'd say that
> (elsize * nelem) would not fit size_t and fail the allocation.
>
> For the purpose of this caller, it is not a practical issue, as it
> is likely that you'd not be able to obtain slightly more than half
> your address space out of a single allocation anyway.
>
> But it illustrates why open coding is not necessarily an excellent
> idea in the longer term, doesn't it? When unsigned_mult_overflows()
> is updated to avoid such a false positive, how would we remember
> that we need to update this copy we?
I agree in general, but with the reftable library I'm stuck between a
rock and a hard place. My goal is to make it fully reusable by other
projects without first having to do surgery on their side. While having
something like `st_mult()` is simple enough to port over, the biggest
problem I have is that over time we sneak in more and more code from the
Git codebase. The result is death by a thousand cuts.
Now if we had a single header that exposes a small set of functions
without _anything_ else it could work alright. But I rather doubt that
we really want to have a standalone header for `st_mult()` that we can
include. But without such a standalone header it is simply too easy to
start building on top of Git features by accident.
So I'm instead aiming to go a different way and fully cut the reftable
code loose from Git. So even if we e.g. eventually want `struct strbuf`
return errors on failure, it would only address one part of my problem.
A couple months ago I said that I'll try to write something like shims
that wrap our types in reftable types such that other projects can
provide implementations for such shims themselves. I tried to make that
work, but the result was an unholy mess that really didn't make any
sense whatsoever. Most of the features that I need from the Git codebase
can be provided in a couple of lines of code (`struct strbuf` is roughly
50 lines for example), plus maybe a callback function that can be
provided to wire things up on the user side (`register_tempfiles()` for
example). So once I saw that those wrappers are harder to use and result
in roughly the same lines of code I decided to scrap that approach and
instead try to convert it fully.
So yeah, overall we shouldn't open-code things like this. But I don't
really see another way to do this for the reftable library.
Patrick
^ permalink raw reply [flat|nested] 152+ messages in thread
* Re: [PATCH v2 04/22] reftable/basics: handle allocation failures in `reftable_calloc()`
2024-09-26 12:11 ` Patrick Steinhardt
@ 2024-09-26 16:13 ` Junio C Hamano
2024-09-27 5:28 ` Patrick Steinhardt
0 siblings, 1 reply; 152+ messages in thread
From: Junio C Hamano @ 2024-09-26 16:13 UTC (permalink / raw)
To: Patrick Steinhardt
Cc: git, Edward Thomson, Josh Steadmon, Calvin Wan, Kyle Lippincott
Patrick Steinhardt <ps@pks.im> writes:
>> But it illustrates why open coding is not necessarily an excellent
>> idea in the longer term, doesn't it? When unsigned_mult_overflows()
>> is updated to avoid such a false positive, how would we remember
>> that we need to update this copy we?
>
> I agree in general, but with the reftable library I'm stuck between a
> rock and a hard place. My goal is to make it fully reusable by other
> projects without first having to do surgery on their side. While having
> something like `st_mult()` is simple enough to port over, the biggest
> problem I have is that over time we sneak in more and more code from the
> Git codebase. The result is death by a thousand cuts.
> Now if we had a single header that exposes a small set of functions
> without _anything_ else it could work alright. But I rather doubt that
> we really want to have a standalone header for `st_mult()` that we can
> include. But without such a standalone header it is simply too easy to
> start building on top of Git features by accident.
>
> So I'm instead aiming to go a different way and fully cut the reftable
> code loose from Git. So even if we e.g. eventually want `struct strbuf`
> return errors on failure, it would only address one part of my problem.
The dependency to "strbuf" (just as an example) was added initially
fairly early. Soon after 27f7ed2a (reftable: add LICENSE,
2021-10-07) added the reftable/ hierarchy, e303bf22 (reftable:
(de)serialization for the polymorphic record type., 2021-10-07). I
somehow had an impression that reftable "library" started without
any Git dependency and then use of our helper functions seemed
through from the shim layer, but it was pretty much part of the
library from day one, it seems.
> A couple months ago I said that I'll try to write something like shims
> that wrap our types in reftable types such that other projects can
> provide implementations for such shims themselves. I tried to make that
> work, but the result was an unholy mess that really didn't make any
> sense whatsoever. Most of the features that I need from the Git codebase
> can be provided in a couple of lines of code (`struct strbuf` is roughly
> 50 lines for example), plus maybe a callback function that can be
> provided to wire things up on the user side (`register_tempfiles()` for
> example). So once I saw that those wrappers are harder to use and result
> in roughly the same lines of code I decided to scrap that approach and
> instead try to convert it fully.
>
> So yeah, overall we shouldn't open-code things like this. But I don't
> really see another way to do this for the reftable library.
But isn't all of the above what Libification ought to be about? I
was hoping that the reftable polishing would not have to be done by
you alone, and you would recruit those who are into libification of
other parts of Git codebase to help cleaning up these fringe (from
the point of view of reftable) interfaces.
What do the libification folks feel about this (folks involved in
libgit-sys CC'ed; of course all others who are interested in the
libification topic are welcome to comment)?
Thanks.
^ permalink raw reply [flat|nested] 152+ messages in thread
* Re: [PATCH v2 04/22] reftable/basics: handle allocation failures in `reftable_calloc()`
2024-09-26 16:13 ` Junio C Hamano
@ 2024-09-27 5:28 ` Patrick Steinhardt
2024-09-27 12:21 ` Han-Wen Nienhuys
0 siblings, 1 reply; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-27 5:28 UTC (permalink / raw)
To: Junio C Hamano
Cc: git, Edward Thomson, Josh Steadmon, Calvin Wan, Kyle Lippincott,
Han-Wen Nienhuys
On Thu, Sep 26, 2024 at 09:13:23AM -0700, Junio C Hamano wrote:
> Patrick Steinhardt <ps@pks.im> writes:
>
> >> But it illustrates why open coding is not necessarily an excellent
> >> idea in the longer term, doesn't it? When unsigned_mult_overflows()
> >> is updated to avoid such a false positive, how would we remember
> >> that we need to update this copy we?
> >
> > I agree in general, but with the reftable library I'm stuck between a
> > rock and a hard place. My goal is to make it fully reusable by other
> > projects without first having to do surgery on their side. While having
> > something like `st_mult()` is simple enough to port over, the biggest
> > problem I have is that over time we sneak in more and more code from the
> > Git codebase. The result is death by a thousand cuts.
>
> > Now if we had a single header that exposes a small set of functions
> > without _anything_ else it could work alright. But I rather doubt that
> > we really want to have a standalone header for `st_mult()` that we can
> > include. But without such a standalone header it is simply too easy to
> > start building on top of Git features by accident.
> >
> > So I'm instead aiming to go a different way and fully cut the reftable
> > code loose from Git. So even if we e.g. eventually want `struct strbuf`
> > return errors on failure, it would only address one part of my problem.
>
> The dependency to "strbuf" (just as an example) was added initially
> fairly early. Soon after 27f7ed2a (reftable: add LICENSE,
> 2021-10-07) added the reftable/ hierarchy, e303bf22 (reftable:
> (de)serialization for the polymorphic record type., 2021-10-07). I
> somehow had an impression that reftable "library" started without
> any Git dependency and then use of our helper functions seemed
> through from the shim layer, but it was pretty much part of the
> library from day one, it seems.
I think the history goes a bit different. Initially, the reftable
library was developed completely outside of the Git tree in [1]. It did
not have any external dependencies and didn't use any of the Git code.
During the upstreaming process the discussion rather quickly swayed into
the direction of making Git the canonical upstream instead of using a
separate repository that hosts the code. See e.g. [2], which was still
tracking a VERSION file to keep track of the upstream version that the
code was pulled from. In [3] the discussion started about upstream
requiring a CLA, which was an (understandable) non-starter. I cannot
find the statement, but eventually we decided that canonical upstream
should be Git, and the VERSION file was never committed.
But with that decision, now that the reftable library was part of Git,
the reviews also started to note that it really should use functions
provided by Git. The initial version of the patch series didn't yet have
any of that. From my point of view that was a big mistake, as the result
is that Git is _not_ reusable by other projects in its current state.
[1]: https://github.com/google/reftable/tree/master/c
[2]: <2106ff286b1135f9428529d9fc392edc127e960c.1580134944.git.gitgitgadget@gmail.com>
[3]: <20200207001612.GF6573@camp.crustytoothpaste.net>
> > A couple months ago I said that I'll try to write something like shims
> > that wrap our types in reftable types such that other projects can
> > provide implementations for such shims themselves. I tried to make that
> > work, but the result was an unholy mess that really didn't make any
> > sense whatsoever. Most of the features that I need from the Git codebase
> > can be provided in a couple of lines of code (`struct strbuf` is roughly
> > 50 lines for example), plus maybe a callback function that can be
> > provided to wire things up on the user side (`register_tempfiles()` for
> > example). So once I saw that those wrappers are harder to use and result
> > in roughly the same lines of code I decided to scrap that approach and
> > instead try to convert it fully.
> >
> > So yeah, overall we shouldn't open-code things like this. But I don't
> > really see another way to do this for the reftable library.
>
> But isn't all of the above what Libification ought to be about? I
> was hoping that the reftable polishing would not have to be done by
> you alone, and you would recruit those who are into libification of
> other parts of Git codebase to help cleaning up these fringe (from
> the point of view of reftable) interfaces.
>
> What do the libification folks feel about this (folks involved in
> libgit-sys CC'ed; of course all others who are interested in the
> libification topic are welcome to comment)?
I think it's orthogonal to that. The libification effort doesn't really
care about exposing low-level details like `st_mult()` as a library, or
even the `strbuf` interface. It cares about making Git functionality
available, which is on a much higher conceptual level.
Furthermore, to the best of my knowledge the intent wasn't ever to
provide the ability to take a couple of C files and have them be easy to
build as part of another project. The goal is to have proper interfaces
to our code, but whether the code itself has internal interdependencies
is not really much of a concern (globals are a different story).
My goal is different: I want to `cp -r` the "reftable/" directory into
libgit2 or any other project that wants to implement a reftable backend
without having to care about all the different dependencies that it may
pull in or having to carefully massage all of the includes.
Now if it was a single standalone file that one would have to copy in
addition to that I'd be fine. But "strbuf.c' isn't that at all: it ropes
in "gettext.h", "hex-ll.h", "string-list.h", "utf-8.h", "date.h". All of
which is not needed at all, but the person who does the porting now has
to carefully figure out what can and cannot be removed from both
"strbuf.h" and "strbuf.c".
So... it gets out of hand really fast. We would have to significantly
change the way we develop the Git codebase if we wanted to make this a
good experience. But enforcing very strict coding guidelines onto all of
our codebase doesn't feel reasonable to me, which is why I am instead
trying to reinstate the state where the reftable library was decoupled
from the rest of the Git codebase.
The difference here is roughly 100 to 200 lines of code, which I don't
think is much of a maintenance burden by itself. In fact, we'll even
start to share the maintenance burden with libgit2, because they would
be able to use the exact same copy as we do and thus contribute back
bugfixes and improvements for discoveries that their additional test
coverage brings us.
I hope that this all clarifies my point of view :) I've also Cc'd
Han-Wen in case I've made any mistakes regarding the original intent.
Patrick
^ permalink raw reply [flat|nested] 152+ messages in thread
* Re: [PATCH v2 04/22] reftable/basics: handle allocation failures in `reftable_calloc()`
2024-09-27 5:28 ` Patrick Steinhardt
@ 2024-09-27 12:21 ` Han-Wen Nienhuys
2024-09-27 15:21 ` Junio C Hamano
0 siblings, 1 reply; 152+ messages in thread
From: Han-Wen Nienhuys @ 2024-09-27 12:21 UTC (permalink / raw)
To: Patrick Steinhardt
Cc: Junio C Hamano, git, Edward Thomson, Josh Steadmon, Calvin Wan,
Kyle Lippincott
On Fri, Sep 27, 2024 at 11:31 AM Patrick Steinhardt <ps@pks.im> wrote:
> > The dependency to "strbuf" (just as an example) was added initially
> > fairly early. Soon after 27f7ed2a (reftable: add LICENSE,
> > 2021-10-07) added the reftable/ hierarchy, e303bf22 (reftable:
> > (de)serialization for the polymorphic record type., 2021-10-07). I
For historical understanding, these commits are meaningless because
they were a bulk import. You should look at
https://github.com/hanwen/reftable instead.
> > somehow had an impression that reftable "library" started without
> > any Git dependency and then use of our helper functions seemed
> > through from the shim layer, but it was pretty much part of the
> > library from day one, it seems.
>
> I think the history goes a bit different. Initially, the reftable
> library was developed completely outside of the Git tree in [1]. It did
> not have any external dependencies and didn't use any of the Git code.
Correct. strbuf was originally called 'slice', because it was a direct
translation of the []byte type in Go. I renamed it to avoid offending
the sensibilities of Git reviewers. See here
https://github.com/hanwen/reftable/commit/06d9728b4fedb9ed996ac1ae48343e3879114e1b
. It was a change that made me a little sad, because the strbuf type
only works with explicit initialization (STRBUF_INIT),
which means that every struct that transitively includes a strbuf must
now have a XXX_INIT macro. It just required 12 strbuf functions, but I
suppose more have crept in over time.
> The difference here is roughly 100 to 200 lines of code, which I don't
> think is much of a maintenance burden by itself. In fact, we'll even
I think Patrick's plan is sound. The amount of code to make reftable
work both standalone and in Git is small. It just needs some
discipline on both the Git and libgit2 side to not add additional
dependencies.
--
Han-Wen Nienhuys - hanwenn@gmail.com - http://www.xs4all.nl/~hanwen
^ permalink raw reply [flat|nested] 152+ messages in thread
* Re: [PATCH v2 04/22] reftable/basics: handle allocation failures in `reftable_calloc()`
2024-09-27 12:21 ` Han-Wen Nienhuys
@ 2024-09-27 15:21 ` Junio C Hamano
0 siblings, 0 replies; 152+ messages in thread
From: Junio C Hamano @ 2024-09-27 15:21 UTC (permalink / raw)
To: Han-Wen Nienhuys
Cc: Patrick Steinhardt, git, Edward Thomson, Josh Steadmon,
Calvin Wan, Kyle Lippincott
Han-Wen Nienhuys <hanwenn@gmail.com> writes:
> I think Patrick's plan is sound. The amount of code to make reftable
> work both standalone and in Git is small. It just needs some
> discipline on both the Git and libgit2 side to not add additional
> dependencies.
Thanks for a sanity check. Very much appreciated.
^ permalink raw reply [flat|nested] 152+ messages in thread
* Re: [PATCH v2 05/22] reftable/basics: handle allocation failures in `parse_names()`
@ 2024-09-29 6:53 Crystal M Baker
0 siblings, 0 replies; 152+ messages in thread
From: Crystal M Baker @ 2024-09-29 6:53 UTC (permalink / raw)
To: l.s.r; +Cc: ethomson, git, gitster, ps
Sent from my iPhone
^ permalink raw reply [flat|nested] 152+ messages in thread
* [PATCH v3 00/22] refatble: handle allocation errors
2024-09-16 12:28 [PATCH 00/22] reftable: handle allocation errors Patrick Steinhardt
` (22 preceding siblings ...)
2024-09-24 6:31 ` [PATCH v2 00/22] reftable: handle allocation errors Patrick Steinhardt
@ 2024-09-30 8:08 ` Patrick Steinhardt
2024-09-30 8:08 ` [PATCH v3 01/22] reftable/error: introduce out-of-memory error code Patrick Steinhardt
` (22 more replies)
2024-10-01 9:41 ` [PATCH v4 00/25] reftable: " Patrick Steinhardt
2024-10-02 10:55 ` [PATCH v5 " Patrick Steinhardt
25 siblings, 23 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-30 8:08 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
Hi,
this is the third 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.
There is only a single change compared to v1, namely that we use
`reftable_free()` instead of `free()`.
Thanks!
Patrick
Patrick Steinhardt (22):
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
Makefile | 1 -
refs/reftable-backend.c | 39 ++++--
reftable/basics.c | 92 ++++++++++++++-
reftable/basics.h | 13 +-
reftable/block.c | 23 +++-
reftable/block.h | 4 +-
reftable/blocksource.c | 25 +++-
reftable/error.c | 2 +
reftable/iter.c | 20 +++-
reftable/iter.h | 2 +-
reftable/merged.c | 84 ++++++++-----
reftable/merged.h | 6 +-
reftable/pq.c | 7 +-
reftable/pq.h | 2 +-
reftable/publicbasics.c | 66 -----------
reftable/reader.c | 68 ++++++++---
reftable/reader.h | 6 +-
reftable/record.c | 164 +++++++++++++++++++-------
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 | 177 ++++++++++++++++++++++------
reftable/tree.c | 42 +++++--
reftable/tree.h | 21 +++-
reftable/writer.c | 150 +++++++++++++++--------
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 | 61 ++++++----
t/unit-tests/t-reftable-stack.c | 4 +-
t/unit-tests/t-reftable-tree.c | 10 +-
38 files changed, 853 insertions(+), 386 deletions(-)
delete mode 100644 reftable/publicbasics.c
create mode 100644 reftable/reftable-basics.h
delete mode 100644 reftable/reftable-malloc.h
Range-diff against v2:
1: 8c99ecc325 = 1: 8c99ecc325 reftable/error: introduce out-of-memory error code
2: 4dcdf1d48e = 2: 4dcdf1d48e reftable/basics: merge "publicbasics" into "basics"
3: 21fa9b15d9 = 3: 21fa9b15d9 reftable: introduce `reftable_strdup()`
4: f6ad92ffd0 = 4: f6ad92ffd0 reftable/basics: handle allocation failures in `reftable_calloc()`
5: ad028020df ! 5: 922783708d reftable/basics: handle allocation failures in `parse_names()`
@@ reftable/basics.c: void parse_names(char *buf, int size, char ***namesp)
+
+err:
+ for (size_t i = 0; i < names_len; i++)
-+ free(names[i]);
-+ free(names);
++ reftable_free(names[i]);
++ reftable_free(names);
+ return NULL;
}
6: df713fbe08 = 6: d4676e8a6a reftable/record: handle allocation failures on copy
7: 870bb003c0 = 7: 874e6a1c20 reftable/record: handle allocation failures when decoding records
8: 1d47e42500 = 8: 42bc57fd00 reftable/writer: handle allocation failures in `writer_index_hash()`
9: caa71f0a77 = 9: 9edd1d84cd reftable/writer: handle allocation failures in `reftable_new_writer()`
10: a84e9cadae = 10: d4004a7f43 reftable/merged: handle allocation failures in `merged_table_init_iter()`
11: 20d3833014 = 11: b2bd142021 reftable/reader: handle allocation failures for unindexed reader
12: e35c3a705d = 12: 9b5fd52862 reftable/reader: handle allocation failures in `reader_init_iter()`
13: ca3b57f151 = 13: 0f3e3d1585 reftable/stack: handle allocation failures on reload
14: 7377421a63 = 14: c88645a251 reftable/stack: handle allocation failures in `reftable_new_stack()`
15: 244e8667c5 = 15: 92ad4fc934 reftable/stack: handle allocation failures in `stack_compact_range()`
16: 99f4868c38 = 16: 19acebd919 reftable/stack: handle allocation failures in auto compaction
17: 271839a626 = 17: 620658bffc reftable/iter: handle allocation failures when creating indexed table iter
18: c41808e9d7 = 18: 48047dd4c9 reftable/blocksource: handle allocation failures
19: 9348b0a457 = 19: 08685605ba reftable/block: handle allocation failures
20: 445daf9464 = 20: a66937b7af reftable/pq: handle allocation failures when adding entries
21: c31f9e53f5 = 21: c291114553 reftable/tree: handle allocation failures
22: d0fe999371 = 22: 20f50c446a reftable: handle trivial allocation failures
--
2.46.2.852.g229c0bf0e5.dirty
^ permalink raw reply [flat|nested] 152+ messages in thread
* [PATCH v3 01/22] reftable/error: introduce out-of-memory error code
2024-09-30 8:08 ` [PATCH v3 00/22] refatble: handle allocation errors Patrick Steinhardt
@ 2024-09-30 8:08 ` Patrick Steinhardt
2024-09-30 8:08 ` [PATCH v3 02/22] reftable/basics: merge "publicbasics" into "basics" Patrick Steinhardt
` (21 subsequent siblings)
22 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-30 8:08 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
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.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/error.c | 2 ++
reftable/reftable-error.h | 3 +++
2 files changed, 5 insertions(+)
diff --git a/reftable/error.c b/reftable/error.c
index a25f28a43e..660d029617 100644
--- a/reftable/error.c
+++ b/reftable/error.c
@@ -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:
diff --git a/reftable/reftable-error.h b/reftable/reftable-error.h
index 6368cd9ed9..f404826562 100644
--- a/reftable/reftable-error.h
+++ b/reftable/reftable-error.h
@@ -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
--
2.46.2.852.g229c0bf0e5.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v3 02/22] reftable/basics: merge "publicbasics" into "basics"
2024-09-30 8:08 ` [PATCH v3 00/22] refatble: handle allocation errors Patrick Steinhardt
2024-09-30 8:08 ` [PATCH v3 01/22] reftable/error: introduce out-of-memory error code Patrick Steinhardt
@ 2024-09-30 8:08 ` Patrick Steinhardt
2024-09-30 8:08 ` [PATCH v3 03/22] reftable: introduce `reftable_strdup()` Patrick Steinhardt
` (20 subsequent siblings)
22 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-30 8:08 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
The 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.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
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
diff --git a/Makefile b/Makefile
index e3abf0ba83..39b10923d4 100644
--- a/Makefile
+++ b/Makefile
@@ -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
diff --git a/reftable/basics.c b/reftable/basics.c
index 0058619ca6..cf072935c8 100644
--- a/reftable/basics.c
+++ b/reftable/basics.c
@@ -7,6 +7,49 @@ license that can be found in the LICENSE file or at
*/
#include "basics.h"
+#include "reftable-basics.h"
+
+static void *(*reftable_malloc_ptr)(size_t sz);
+static void *(*reftable_realloc_ptr)(void *, size_t);
+static void (*reftable_free_ptr)(void *);
+
+void *reftable_malloc(size_t sz)
+{
+ if (reftable_malloc_ptr)
+ return (*reftable_malloc_ptr)(sz);
+ return malloc(sz);
+}
+
+void *reftable_realloc(void *p, size_t sz)
+{
+ if (reftable_realloc_ptr)
+ return (*reftable_realloc_ptr)(p, sz);
+ return realloc(p, sz);
+}
+
+void reftable_free(void *p)
+{
+ if (reftable_free_ptr)
+ reftable_free_ptr(p);
+ else
+ free(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);
+ return p;
+}
+
+void reftable_set_alloc(void *(*malloc)(size_t),
+ void *(*realloc)(void *, size_t), void (*free)(void *))
+{
+ reftable_malloc_ptr = malloc;
+ reftable_realloc_ptr = realloc;
+ reftable_free_ptr = free;
+}
void put_be24(uint8_t *out, uint32_t i)
{
@@ -121,3 +164,15 @@ int common_prefix_size(struct strbuf *a, struct strbuf *b)
return p;
}
+
+int hash_size(uint32_t id)
+{
+ switch (id) {
+ case 0:
+ case GIT_SHA1_FORMAT_ID:
+ return GIT_SHA1_RAWSZ;
+ case GIT_SHA256_FORMAT_ID:
+ return GIT_SHA256_RAWSZ;
+ }
+ abort();
+}
diff --git a/reftable/basics.h b/reftable/basics.h
index c8fec68d4e..4e2e76014a 100644
--- a/reftable/basics.h
+++ b/reftable/basics.h
@@ -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
diff --git a/reftable/publicbasics.c b/reftable/publicbasics.c
deleted file mode 100644
index 44b84a125e..0000000000
--- a/reftable/publicbasics.c
+++ /dev/null
@@ -1,66 +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
-*/
-
-#include "system.h"
-#include "reftable-malloc.h"
-
-#include "basics.h"
-
-static void *(*reftable_malloc_ptr)(size_t sz);
-static void *(*reftable_realloc_ptr)(void *, size_t);
-static void (*reftable_free_ptr)(void *);
-
-void *reftable_malloc(size_t sz)
-{
- if (reftable_malloc_ptr)
- return (*reftable_malloc_ptr)(sz);
- return malloc(sz);
-}
-
-void *reftable_realloc(void *p, size_t sz)
-{
- if (reftable_realloc_ptr)
- return (*reftable_realloc_ptr)(p, sz);
- return realloc(p, sz);
-}
-
-void reftable_free(void *p)
-{
- if (reftable_free_ptr)
- reftable_free_ptr(p);
- else
- free(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);
- return p;
-}
-
-void reftable_set_alloc(void *(*malloc)(size_t),
- void *(*realloc)(void *, size_t), void (*free)(void *))
-{
- reftable_malloc_ptr = malloc;
- reftable_realloc_ptr = realloc;
- reftable_free_ptr = free;
-}
-
-int hash_size(uint32_t id)
-{
- switch (id) {
- case 0:
- case GIT_SHA1_FORMAT_ID:
- return GIT_SHA1_RAWSZ;
- case GIT_SHA256_FORMAT_ID:
- return GIT_SHA256_RAWSZ;
- }
- abort();
-}
diff --git a/reftable/reftable-basics.h b/reftable/reftable-basics.h
new file mode 100644
index 0000000000..6e8e636b71
--- /dev/null
+++ b/reftable/reftable-basics.h
@@ -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
diff --git a/reftable/reftable-malloc.h b/reftable/reftable-malloc.h
deleted file mode 100644
index 5f2185f1f3..0000000000
--- a/reftable/reftable-malloc.h
+++ /dev/null
@@ -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
--
2.46.2.852.g229c0bf0e5.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v3 03/22] reftable: introduce `reftable_strdup()`
2024-09-30 8:08 ` [PATCH v3 00/22] refatble: handle allocation errors Patrick Steinhardt
2024-09-30 8:08 ` [PATCH v3 01/22] reftable/error: introduce out-of-memory error code Patrick Steinhardt
2024-09-30 8:08 ` [PATCH v3 02/22] reftable/basics: merge "publicbasics" into "basics" Patrick Steinhardt
@ 2024-09-30 8:08 ` Patrick Steinhardt
2024-09-30 8:08 ` [PATCH v3 04/22] reftable/basics: handle allocation failures in `reftable_calloc()` Patrick Steinhardt
` (19 subsequent siblings)
22 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-30 8:08 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
The 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.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/basics.c | 10 ++++++++++
reftable/basics.h | 1 +
2 files changed, 11 insertions(+)
diff --git a/reftable/basics.c b/reftable/basics.c
index cf072935c8..4adc98cf5d 100644
--- a/reftable/basics.c
+++ b/reftable/basics.c
@@ -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 *))
{
diff --git a/reftable/basics.h b/reftable/basics.h
index 4e2e76014a..f107e14860 100644
--- a/reftable/basics.h
+++ b/reftable/basics.h
@@ -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)))
--
2.46.2.852.g229c0bf0e5.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v3 04/22] reftable/basics: handle allocation failures in `reftable_calloc()`
2024-09-30 8:08 ` [PATCH v3 00/22] refatble: handle allocation errors Patrick Steinhardt
` (2 preceding siblings ...)
2024-09-30 8:08 ` [PATCH v3 03/22] reftable: introduce `reftable_strdup()` Patrick Steinhardt
@ 2024-09-30 8:08 ` Patrick Steinhardt
2024-09-30 8:08 ` [PATCH v3 05/22] reftable/basics: handle allocation failures in `parse_names()` Patrick Steinhardt
` (18 subsequent siblings)
22 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-30 8:08 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
Handle 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.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/basics.c | 13 ++++++++++---
1 file changed, 10 insertions(+), 3 deletions(-)
diff --git a/reftable/basics.c b/reftable/basics.c
index 4adc98cf5d..3350bbffa2 100644
--- a/reftable/basics.c
+++ b/reftable/basics.c
@@ -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;
}
--
2.46.2.852.g229c0bf0e5.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v3 05/22] reftable/basics: handle allocation failures in `parse_names()`
2024-09-30 8:08 ` [PATCH v3 00/22] refatble: handle allocation errors Patrick Steinhardt
` (3 preceding siblings ...)
2024-09-30 8:08 ` [PATCH v3 04/22] reftable/basics: handle allocation failures in `reftable_calloc()` Patrick Steinhardt
@ 2024-09-30 8:08 ` Patrick Steinhardt
2024-09-30 17:40 ` René Scharfe
2024-09-30 8:08 ` [PATCH v3 06/22] reftable/record: handle allocation failures on copy Patrick Steinhardt
` (17 subsequent siblings)
22 siblings, 1 reply; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-30 8:08 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
Handle 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.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
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(-)
diff --git a/reftable/basics.c b/reftable/basics.c
index 3350bbffa2..5ae6e6acdd 100644
--- a/reftable/basics.c
+++ b/reftable/basics.c
@@ -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] = 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)
diff --git a/reftable/basics.h b/reftable/basics.h
index f107e14860..69adeab2e4 100644
--- a/reftable/basics.h
+++ b/reftable/basics.h
@@ -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);
diff --git a/reftable/stack.c b/reftable/stack.c
index ce0a35216b..498fae846d 100644
--- a/reftable/stack.c
+++ b/reftable/stack.c
@@ -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);
diff --git a/t/unit-tests/t-reftable-basics.c b/t/unit-tests/t-reftable-basics.c
index e5556ebf52..1fa77b6faf 100644
--- a/t/unit-tests/t-reftable-basics.c
+++ b/t/unit-tests/t-reftable-basics.c
@@ -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");
--
2.46.2.852.g229c0bf0e5.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v3 06/22] reftable/record: handle allocation failures on copy
2024-09-30 8:08 ` [PATCH v3 00/22] refatble: handle allocation errors Patrick Steinhardt
` (4 preceding siblings ...)
2024-09-30 8:08 ` [PATCH v3 05/22] reftable/basics: handle allocation failures in `parse_names()` Patrick Steinhardt
@ 2024-09-30 8:08 ` Patrick Steinhardt
2024-09-30 8:08 ` [PATCH v3 07/22] reftable/record: handle allocation failures when decoding records Patrick Steinhardt
` (16 subsequent siblings)
22 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-30 8:08 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
Handle allocation failures when copying records. While at it, convert
from `xstrdup()` to `reftable_strdup()`. Adapt callsites to check for
error codes.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/record.c | 84 +++++++++++++++++++++++++++++++++--------------
reftable/record.h | 6 ++--
2 files changed, 63 insertions(+), 27 deletions(-)
diff --git a/reftable/record.c b/reftable/record.c
index 6b5a075b92..60fd33c9c9 100644
--- a/reftable/record.c
+++ b/reftable/record.c
@@ -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)
diff --git a/reftable/record.h b/reftable/record.h
index 5003bacdb0..0f53ba5443 100644
--- a/reftable/record.h
+++ b/reftable/record.h
@@ -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);
--
2.46.2.852.g229c0bf0e5.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v3 07/22] reftable/record: handle allocation failures when decoding records
2024-09-30 8:08 ` [PATCH v3 00/22] refatble: handle allocation errors Patrick Steinhardt
` (5 preceding siblings ...)
2024-09-30 8:08 ` [PATCH v3 06/22] reftable/record: handle allocation failures on copy Patrick Steinhardt
@ 2024-09-30 8:08 ` Patrick Steinhardt
2024-09-30 8:08 ` [PATCH v3 08/22] reftable/writer: handle allocation failures in `writer_index_hash()` Patrick Steinhardt
` (15 subsequent siblings)
22 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-30 8:08 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
Handle allocation failures when decoding records. While at it, fix some
error codes to be `REFTABLE_FORMAT_ERROR`.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/record.c | 80 +++++++++++++++++++++++++++++++++++++----------
1 file changed, 63 insertions(+), 17 deletions(-)
diff --git a/reftable/record.c b/reftable/record.c
index 60fd33c9c9..787e134c9a 100644
--- a/reftable/record.c
+++ b/reftable/record.c
@@ -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)
--
2.46.2.852.g229c0bf0e5.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v3 08/22] reftable/writer: handle allocation failures in `writer_index_hash()`
2024-09-30 8:08 ` [PATCH v3 00/22] refatble: handle allocation errors Patrick Steinhardt
` (6 preceding siblings ...)
2024-09-30 8:08 ` [PATCH v3 07/22] reftable/record: handle allocation failures when decoding records Patrick Steinhardt
@ 2024-09-30 8:08 ` Patrick Steinhardt
2024-09-30 8:08 ` [PATCH v3 09/22] reftable/writer: handle allocation failures in `reftable_new_writer()` Patrick Steinhardt
` (14 subsequent siblings)
22 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-30 8:08 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
Handle allocation errors in `writer_index_hash()`. Adjust its only
caller in `reftable_writer_add_ref()` accordingly.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/writer.c | 61 +++++++++++++++++++++++++++++------------------
1 file changed, 38 insertions(+), 23 deletions(-)
diff --git a/reftable/writer.c b/reftable/writer.c
index 9d5e6072bc..ed61aaf59c 100644
--- a/reftable/writer.c
+++ b/reftable/writer.c
@@ -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,
--
2.46.2.852.g229c0bf0e5.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v3 09/22] reftable/writer: handle allocation failures in `reftable_new_writer()`
2024-09-30 8:08 ` [PATCH v3 00/22] refatble: handle allocation errors Patrick Steinhardt
` (7 preceding siblings ...)
2024-09-30 8:08 ` [PATCH v3 08/22] reftable/writer: handle allocation failures in `writer_index_hash()` Patrick Steinhardt
@ 2024-09-30 8:08 ` Patrick Steinhardt
2024-09-30 17:40 ` René Scharfe
2024-09-30 8:08 ` [PATCH v3 10/22] reftable/merged: handle allocation failures in `merged_table_init_iter()` Patrick Steinhardt
` (13 subsequent siblings)
22 siblings, 1 reply; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-30 8:08 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
Handle 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.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
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(-)
diff --git a/reftable/reftable-writer.h b/reftable/reftable-writer.h
index 189b1f4144..43623dc7c3 100644
--- a/reftable/reftable-writer.h
+++ b/reftable/reftable-writer.h
@@ -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
diff --git a/reftable/stack.c b/reftable/stack.c
index 498fae846d..ea21ca6e5f 100644
--- a/reftable/stack.c
+++ b/reftable/stack.c
@@ -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;
diff --git a/reftable/writer.c b/reftable/writer.c
index ed61aaf59c..54ec822e1c 100644
--- a/reftable/writer.c
+++ b/reftable/writer.c
@@ -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) {
+ 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,
diff --git a/t/unit-tests/lib-reftable.c b/t/unit-tests/lib-reftable.c
index ab1fa44a28..54c26c43e7 100644
--- a/t/unit-tests/lib-reftable.c
+++ b/t/unit-tests/lib-reftable.c
@@ -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,
--
2.46.2.852.g229c0bf0e5.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v3 10/22] reftable/merged: handle allocation failures in `merged_table_init_iter()`
2024-09-30 8:08 ` [PATCH v3 00/22] refatble: handle allocation errors Patrick Steinhardt
` (8 preceding siblings ...)
2024-09-30 8:08 ` [PATCH v3 09/22] reftable/writer: handle allocation failures in `reftable_new_writer()` Patrick Steinhardt
@ 2024-09-30 8:08 ` Patrick Steinhardt
2024-09-30 8:08 ` [PATCH v3 11/22] reftable/reader: handle allocation failures for unindexed reader Patrick Steinhardt
` (12 subsequent siblings)
22 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-30 8:08 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
Handle 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.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
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(-)
diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c
index 5c241097a4..9c08c46c85 100644
--- a/refs/reftable-backend.c
+++ b/refs/reftable-backend.c
@@ -1299,7 +1299,9 @@ static int write_transaction_table(struct reftable_writer *writer, void *cb_data
struct 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 reftabl
if (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_da
reftable_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)
diff --git a/reftable/merged.c b/reftable/merged.c
index 128a810c55..5a8ea8ae77 100644
--- a/reftable/merged.c
+++ b/reftable/merged.c
@@ -30,22 +30,6 @@ struct merged_iter {
ssize_t advance_index;
};
-static void merged_iter_init(struct merged_iter *mi,
- struct reftable_merged_table *mt,
- uint8_t typ)
-{
- memset(mi, 0, sizeof(*mi));
- mi->advance_index = -1;
- mi->suppress_deletions = mt->suppress_deletions;
-
- REFTABLE_CALLOC_ARRAY(mi->subiters, mt->readers_len);
- for (size_t i = 0; i < mt->readers_len; i++) {
- reftable_record_init(&mi->subiters[i].rec, typ);
- reader_init_iter(mt->readers[i], &mi->subiters[i].iter, typ);
- }
- mi->subiters_len = mt->readers_len;
-}
-
static void merged_iter_close(void *p)
{
struct merged_iter *mi = p;
@@ -244,25 +228,61 @@ reftable_merged_table_min_update_index(struct reftable_merged_table *mt)
return mt->min;
}
-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)
{
- struct merged_iter *mi = reftable_malloc(sizeof(*mi));
- merged_iter_init(mi, mt, typ);
+ struct merged_subiter *subiters;
+ struct merged_iter *mi = NULL;
+ int ret;
+
+ REFTABLE_CALLOC_ARRAY(subiters, mt->readers_len);
+ if (!subiters) {
+ ret = REFTABLE_OUT_OF_MEMORY_ERROR;
+ goto out;
+ }
+
+ 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);
+ }
+
+ REFTABLE_CALLOC_ARRAY(mi, 1);
+ if (!mi) {
+ ret = REFTABLE_OUT_OF_MEMORY_ERROR;
+ goto out;
+ }
+ mi->advance_index = -1;
+ mi->suppress_deletions = mt->suppress_deletions;
+ mi->subiters = subiters;
+ mi->subiters_len = mt->readers_len;
+
iterator_from_merged_iter(it, mi);
+ ret = 0;
+
+out:
+ if (ret < 0) {
+ for (size_t i = 0; subiters && i < mt->readers_len; i++) {
+ reftable_iterator_destroy(&subiters[i].iter);
+ reftable_record_release(&subiters[i].rec);
+ }
+ free(subiters);
+ free(mi);
+ }
+
+ return ret;
}
-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)
{
- merged_table_init_iter(mt, it, BLOCK_TYPE_REF);
+ return merged_table_init_iter(mt, it, BLOCK_TYPE_REF);
}
-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)
{
- merged_table_init_iter(mt, it, BLOCK_TYPE_LOG);
+ return merged_table_init_iter(mt, it, BLOCK_TYPE_LOG);
}
uint32_t reftable_merged_table_hash_id(struct reftable_merged_table *mt)
diff --git a/reftable/merged.h b/reftable/merged.h
index de5fd33f01..89bd0c4b35 100644
--- a/reftable/merged.h
+++ b/reftable/merged.h
@@ -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
diff --git a/reftable/reftable-merged.h b/reftable/reftable-merged.h
index 16d19f8df2..a970d5dd89 100644
--- a/reftable/reftable-merged.h
+++ b/reftable/reftable-merged.h
@@ -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
diff --git a/reftable/reftable-stack.h b/reftable/reftable-stack.h
index f4f8cabc7f..e958f911b4 100644
--- a/reftable/reftable-stack.h
+++ b/reftable/reftable-stack.h
@@ -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.
diff --git a/reftable/stack.c b/reftable/stack.c
index ea21ca6e5f..bb4d230918 100644
--- a/reftable/stack.c
+++ b/reftable/stack.c
@@ -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;
diff --git a/t/helper/test-reftable.c b/t/helper/test-reftable.c
index 29d4e9a755..5c8849d115 100644
--- a/t/helper/test-reftable.c
+++ b/t/helper/test-reftable.c
@@ -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;
diff --git a/t/unit-tests/t-reftable-merged.c b/t/unit-tests/t-reftable-merged.c
index 19e54bdfb8..3d2848632d 100644
--- a/t/unit-tests/t-reftable-merged.c
+++ b/t/unit-tests/t-reftable-merged.c
@@ -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]);
diff --git a/t/unit-tests/t-reftable-stack.c b/t/unit-tests/t-reftable-stack.c
index 65e513d5ec..6e41a4cf7e 100644
--- a/t/unit-tests/t-reftable-stack.c
+++ b/t/unit-tests/t-reftable-stack.c
@@ -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 };
--
2.46.2.852.g229c0bf0e5.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v3 11/22] reftable/reader: handle allocation failures for unindexed reader
2024-09-30 8:08 ` [PATCH v3 00/22] refatble: handle allocation errors Patrick Steinhardt
` (9 preceding siblings ...)
2024-09-30 8:08 ` [PATCH v3 10/22] reftable/merged: handle allocation failures in `merged_table_init_iter()` Patrick Steinhardt
@ 2024-09-30 8:08 ` Patrick Steinhardt
2024-09-30 8:08 ` [PATCH v3 12/22] reftable/reader: handle allocation failures in `reader_init_iter()` Patrick Steinhardt
` (11 subsequent siblings)
22 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-30 8:08 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
Handle allocation failures when creating unindexed readers.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/reader.c | 23 +++++++++++++++++------
1 file changed, 17 insertions(+), 6 deletions(-)
diff --git a/reftable/reader.c b/reftable/reader.c
index 6494ce2e32..485ee085da 100644
--- a/reftable/reader.c
+++ b/reftable/reader.c
@@ -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,
--
2.46.2.852.g229c0bf0e5.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v3 12/22] reftable/reader: handle allocation failures in `reader_init_iter()`
2024-09-30 8:08 ` [PATCH v3 00/22] refatble: handle allocation errors Patrick Steinhardt
` (10 preceding siblings ...)
2024-09-30 8:08 ` [PATCH v3 11/22] reftable/reader: handle allocation failures for unindexed reader Patrick Steinhardt
@ 2024-09-30 8:08 ` Patrick Steinhardt
2024-09-30 8:08 ` [PATCH v3 13/22] reftable/stack: handle allocation failures on reload Patrick Steinhardt
` (10 subsequent siblings)
22 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-30 8:08 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
Handle 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.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
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(-)
diff --git a/reftable/merged.c b/reftable/merged.c
index 5a8ea8ae77..741f62ea63 100644
--- a/reftable/merged.c
+++ b/reftable/merged.c
@@ -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);
diff --git a/reftable/reader.c b/reftable/reader.c
index 485ee085da..f696e992df 100644
--- a/reftable/reader.c
+++ b/reftable/reader.c
@@ -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;
diff --git a/reftable/reader.h b/reftable/reader.h
index 3710ee09b4..02d10c5d37 100644
--- a/reftable/reader.h
+++ b/reftable/reader.h
@@ -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,
diff --git a/reftable/reftable-reader.h b/reftable/reftable-reader.h
index a600452b56..6a2d0b693f 100644
--- a/reftable/reftable-reader.h
+++ b/reftable/reftable-reader.h
@@ -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);
diff --git a/t/unit-tests/t-reftable-readwrite.c b/t/unit-tests/t-reftable-readwrite.c
index e1b235a5f1..acca927a2c 100644
--- a/t/unit-tests/t-reftable-readwrite.c
+++ b/t/unit-tests/t-reftable-readwrite.c
@@ -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);
--
2.46.2.852.g229c0bf0e5.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v3 13/22] reftable/stack: handle allocation failures on reload
2024-09-30 8:08 ` [PATCH v3 00/22] refatble: handle allocation errors Patrick Steinhardt
` (11 preceding siblings ...)
2024-09-30 8:08 ` [PATCH v3 12/22] reftable/reader: handle allocation failures in `reader_init_iter()` Patrick Steinhardt
@ 2024-09-30 8:08 ` Patrick Steinhardt
2024-09-30 8:08 ` [PATCH v3 14/22] reftable/stack: handle allocation failures in `reftable_new_stack()` Patrick Steinhardt
` (9 subsequent siblings)
22 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-30 8:08 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
Handle allocation failures in `reftable_stack_reload_once()`.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/stack.c | 34 +++++++++++++++++++++++++---------
1 file changed, 25 insertions(+), 9 deletions(-)
diff --git a/reftable/stack.c b/reftable/stack.c
index bb4d230918..060b2c1b90 100644
--- a/reftable/stack.c
+++ b/reftable/stack.c
@@ -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;
--
2.46.2.852.g229c0bf0e5.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v3 14/22] reftable/stack: handle allocation failures in `reftable_new_stack()`
2024-09-30 8:08 ` [PATCH v3 00/22] refatble: handle allocation errors Patrick Steinhardt
` (12 preceding siblings ...)
2024-09-30 8:08 ` [PATCH v3 13/22] reftable/stack: handle allocation failures on reload Patrick Steinhardt
@ 2024-09-30 8:08 ` Patrick Steinhardt
2024-09-30 8:08 ` [PATCH v3 15/22] reftable/stack: handle allocation failures in `stack_compact_range()` Patrick Steinhardt
` (8 subsequent siblings)
22 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-30 8:08 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
Handle allocation failures in `reftable_new_stack()`.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/stack.c | 34 ++++++++++++++++++++++++++--------
1 file changed, 26 insertions(+), 8 deletions(-)
diff --git a/reftable/stack.c b/reftable/stack.c
index 060b2c1b90..1b77c9d014 100644
--- a/reftable/stack.c
+++ b/reftable/stack.c
@@ -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;
--
2.46.2.852.g229c0bf0e5.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v3 15/22] reftable/stack: handle allocation failures in `stack_compact_range()`
2024-09-30 8:08 ` [PATCH v3 00/22] refatble: handle allocation errors Patrick Steinhardt
` (13 preceding siblings ...)
2024-09-30 8:08 ` [PATCH v3 14/22] reftable/stack: handle allocation failures in `reftable_new_stack()` Patrick Steinhardt
@ 2024-09-30 8:08 ` Patrick Steinhardt
2024-09-30 8:08 ` [PATCH v3 16/22] reftable/stack: handle allocation failures in auto compaction Patrick Steinhardt
` (7 subsequent siblings)
22 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-30 8:08 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
Handle allocation failures in `stack_compact_range()`.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/stack.c | 19 +++++++++++++++++--
1 file changed, 17 insertions(+), 2 deletions(-)
diff --git a/reftable/stack.c b/reftable/stack.c
index 1b77c9d014..2e6dd513d7 100644
--- a/reftable/stack.c
+++ b/reftable/stack.c
@@ -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;
}
--
2.46.2.852.g229c0bf0e5.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v3 16/22] reftable/stack: handle allocation failures in auto compaction
2024-09-30 8:08 ` [PATCH v3 00/22] refatble: handle allocation errors Patrick Steinhardt
` (14 preceding siblings ...)
2024-09-30 8:08 ` [PATCH v3 15/22] reftable/stack: handle allocation failures in `stack_compact_range()` Patrick Steinhardt
@ 2024-09-30 8:08 ` Patrick Steinhardt
2024-09-30 8:09 ` [PATCH v3 17/22] reftable/iter: handle allocation failures when creating indexed table iter Patrick Steinhardt
` (6 subsequent siblings)
22 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-30 8:08 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
Handle allocation failures in `reftable_stack_auto_compact()`.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/stack.c | 16 ++++++++++++----
1 file changed, 12 insertions(+), 4 deletions(-)
diff --git a/reftable/stack.c b/reftable/stack.c
index 2e6dd513d7..990784d9d2 100644
--- a/reftable/stack.c
+++ b/reftable/stack.c
@@ -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);
--
2.46.2.852.g229c0bf0e5.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v3 17/22] reftable/iter: handle allocation failures when creating indexed table iter
2024-09-30 8:08 ` [PATCH v3 00/22] refatble: handle allocation errors Patrick Steinhardt
` (15 preceding siblings ...)
2024-09-30 8:08 ` [PATCH v3 16/22] reftable/stack: handle allocation failures in auto compaction Patrick Steinhardt
@ 2024-09-30 8:09 ` Patrick Steinhardt
2024-09-30 8:09 ` [PATCH v3 18/22] reftable/blocksource: handle allocation failures Patrick Steinhardt
` (5 subsequent siblings)
22 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-30 8:09 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
Handle allocation failures in `new_indexed_table_ref_iter()`. While at
it, rename the function to match our coding style.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/iter.c | 20 ++++++++++++++++----
reftable/iter.h | 2 +-
reftable/reader.c | 7 ++++++-
3 files changed, 23 insertions(+), 6 deletions(-)
diff --git a/reftable/iter.c b/reftable/iter.c
index 416a9f6996..d319538f80 100644
--- a/reftable/iter.c
+++ b/reftable/iter.c
@@ -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;
}
diff --git a/reftable/iter.h b/reftable/iter.h
index befc4597df..b3225bc7ad 100644
--- a/reftable/iter.h
+++ b/reftable/iter.h
@@ -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);
diff --git a/reftable/reader.c b/reftable/reader.c
index f696e992df..0179e4e73d 100644
--- a/reftable/reader.c
+++ b/reftable/reader.c
@@ -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)
--
2.46.2.852.g229c0bf0e5.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v3 18/22] reftable/blocksource: handle allocation failures
2024-09-30 8:08 ` [PATCH v3 00/22] refatble: handle allocation errors Patrick Steinhardt
` (16 preceding siblings ...)
2024-09-30 8:09 ` [PATCH v3 17/22] reftable/iter: handle allocation failures when creating indexed table iter Patrick Steinhardt
@ 2024-09-30 8:09 ` Patrick Steinhardt
2024-09-30 8:09 ` [PATCH v3 19/22] reftable/block: " Patrick Steinhardt
` (4 subsequent siblings)
22 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-30 8:09 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
Handle allocation failures in the blocksource code.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/blocksource.c | 25 ++++++++++++++++++++-----
1 file changed, 20 insertions(+), 5 deletions(-)
diff --git a/reftable/blocksource.c b/reftable/blocksource.c
index e93cac9bb6..6b570751be 100644
--- a/reftable/blocksource.c
+++ b/reftable/blocksource.c
@@ -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)
+ free(p);
return 0;
}
--
2.46.2.852.g229c0bf0e5.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v3 19/22] reftable/block: handle allocation failures
2024-09-30 8:08 ` [PATCH v3 00/22] refatble: handle allocation errors Patrick Steinhardt
` (17 preceding siblings ...)
2024-09-30 8:09 ` [PATCH v3 18/22] reftable/blocksource: handle allocation failures Patrick Steinhardt
@ 2024-09-30 8:09 ` Patrick Steinhardt
2024-09-30 8:09 ` [PATCH v3 20/22] reftable/pq: handle allocation failures when adding entries Patrick Steinhardt
` (3 subsequent siblings)
22 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-30 8:09 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
Handle 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.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
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(-)
diff --git a/reftable/block.c b/reftable/block.c
index 00030eee06..bfa7dc61bf 100644
--- a/reftable/block.c
+++ b/reftable/block.c
@@ -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);
diff --git a/reftable/block.h b/reftable/block.h
index 1c8f25ee6e..18d7ea0337 100644
--- a/reftable/block.h
+++ b/reftable/block.h
@@ -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);
diff --git a/reftable/writer.c b/reftable/writer.c
index 54ec822e1c..712a84791b 100644
--- a/reftable/writer.c
+++ b/reftable/writer.c
@@ -102,19 +102,24 @@ static int writer_write_header(struct reftable_writer *w, uint8_t *dest)
return header_size(writer_version(w));
}
-static void writer_reinit_block_writer(struct reftable_writer *w, uint8_t typ)
+static int writer_reinit_block_writer(struct reftable_writer *w, uint8_t typ)
{
- int block_start = 0;
- if (w->next == 0) {
+ int block_start = 0, ret;
+
+ if (w->next == 0)
block_start = header_size(writer_version(w));
- }
strbuf_reset(&w->last_key);
- block_writer_init(&w->block_writer_data, typ, w->block,
- w->opts.block_size, block_start,
- hash_size(w->opts.hash_id));
+ ret = block_writer_init(&w->block_writer_data, typ, w->block,
+ w->opts.block_size, block_start,
+ hash_size(w->opts.hash_id));
+ if (ret < 0)
+ return ret;
+
w->block_writer = &w->block_writer_data;
w->block_writer->restart_interval = w->opts.restart_interval;
+
+ return 0;
}
int reftable_writer_new(struct reftable_writer **out,
@@ -247,8 +252,11 @@ static int writer_add_record(struct reftable_writer *w,
strbuf_reset(&w->last_key);
strbuf_addbuf(&w->last_key, &key);
- if (!w->block_writer)
- writer_reinit_block_writer(w, reftable_record_type(rec));
+ if (!w->block_writer) {
+ err = writer_reinit_block_writer(w, reftable_record_type(rec));
+ if (err < 0)
+ goto done;
+ }
if (block_writer_type(w->block_writer) != reftable_record_type(rec))
BUG("record of type %d added to writer of type %d",
@@ -271,7 +279,9 @@ static int writer_add_record(struct reftable_writer *w,
err = writer_flush_block(w);
if (err < 0)
goto done;
- writer_reinit_block_writer(w, reftable_record_type(rec));
+ err = writer_reinit_block_writer(w, reftable_record_type(rec));
+ if (err < 0)
+ goto done;
/*
* Try to add the record to the writer again. If this still fails then
@@ -461,7 +471,9 @@ static int writer_finish_section(struct reftable_writer *w)
max_level++;
index_start = w->next;
- writer_reinit_block_writer(w, BLOCK_TYPE_INDEX);
+ err = writer_reinit_block_writer(w, BLOCK_TYPE_INDEX);
+ if (err < 0)
+ return err;
idx = w->index;
idx_len = w->index_len;
@@ -555,7 +567,10 @@ static void write_object_record(void *void_arg, void *key)
if (arg->err < 0)
goto done;
- writer_reinit_block_writer(arg->w, BLOCK_TYPE_OBJ);
+ arg->err = writer_reinit_block_writer(arg->w, BLOCK_TYPE_OBJ);
+ if (arg->err < 0)
+ goto done;
+
arg->err = block_writer_add(arg->w->block_writer, &rec);
if (arg->err == 0)
goto done;
@@ -584,16 +599,18 @@ static int writer_dump_object_index(struct reftable_writer *w)
struct common_prefix_arg common = {
.max = 1, /* obj_id_len should be >= 2. */
};
- if (w->obj_index_tree) {
+ int err;
+
+ if (w->obj_index_tree)
infix_walk(w->obj_index_tree, &update_common, &common);
- }
w->stats.object_id_len = common.max + 1;
- writer_reinit_block_writer(w, BLOCK_TYPE_OBJ);
+ err = writer_reinit_block_writer(w, BLOCK_TYPE_OBJ);
+ if (err < 0)
+ return err;
- if (w->obj_index_tree) {
+ if (w->obj_index_tree)
infix_walk(w->obj_index_tree, &write_object_record, &closure);
- }
if (closure.err < 0)
return closure.err;
diff --git a/t/unit-tests/t-reftable-block.c b/t/unit-tests/t-reftable-block.c
index f1a49485e2..e52a612e85 100644
--- a/t/unit-tests/t-reftable-block.c
+++ b/t/unit-tests/t-reftable-block.c
@@ -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);
--
2.46.2.852.g229c0bf0e5.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v3 20/22] reftable/pq: handle allocation failures when adding entries
2024-09-30 8:08 ` [PATCH v3 00/22] refatble: handle allocation errors Patrick Steinhardt
` (18 preceding siblings ...)
2024-09-30 8:09 ` [PATCH v3 19/22] reftable/block: " Patrick Steinhardt
@ 2024-09-30 8:09 ` Patrick Steinhardt
2024-09-30 8:09 ` [PATCH v3 21/22] reftable/tree: handle allocation failures Patrick Steinhardt
` (2 subsequent siblings)
22 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-30 8:09 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
Handle allocation failures when adding entries to the pqueue. Adapt its
only caller accordingly.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/merged.c | 5 ++++-
reftable/pq.c | 7 ++++++-
reftable/pq.h | 2 +-
3 files changed, 11 insertions(+), 3 deletions(-)
diff --git a/reftable/merged.c b/reftable/merged.c
index 741f62ea63..2c20845d62 100644
--- a/reftable/merged.c
+++ b/reftable/merged.c
@@ -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;
}
diff --git a/reftable/pq.c b/reftable/pq.c
index 2b5b7d1c0e..03b9912282 100644
--- a/reftable/pq.c
+++ b/reftable/pq.c
@@ -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_entry
SWAP(pq->heap[j], pq->heap[i]);
i = j;
}
+
+ return 0;
}
void merged_iter_pqueue_release(struct merged_iter_pqueue *pq)
diff --git a/reftable/pq.h b/reftable/pq.h
index 707bd26767..83c062eeca 100644
--- a/reftable/pq.h
+++ b/reftable/pq.h
@@ -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);
--
2.46.2.852.g229c0bf0e5.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v3 21/22] reftable/tree: handle allocation failures
2024-09-30 8:08 ` [PATCH v3 00/22] refatble: handle allocation errors Patrick Steinhardt
` (19 preceding siblings ...)
2024-09-30 8:09 ` [PATCH v3 20/22] reftable/pq: handle allocation failures when adding entries Patrick Steinhardt
@ 2024-09-30 8:09 ` Patrick Steinhardt
2024-09-30 8:09 ` [PATCH v3 22/22] reftable: handle trivial " Patrick Steinhardt
2024-09-30 18:18 ` [PATCH v3 00/22] refatble: handle allocation errors Junio C Hamano
22 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-30 8:09 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
The 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.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
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(-)
diff --git a/reftable/tree.c b/reftable/tree.c
index 5ffb2e0d69..f4dbe72090 100644
--- a/reftable/tree.c
+++ b/reftable/tree.c
@@ -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;
}
diff --git a/reftable/tree.h b/reftable/tree.h
index fbdd002e23..9604453b6d 100644
--- a/reftable/tree.h
+++ b/reftable/tree.h
@@ -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),
diff --git a/reftable/writer.c b/reftable/writer.c
index 712a84791b..44bed92f46 100644
--- a/reftable/writer.c
+++ b/reftable/writer.c
@@ -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;
}
diff --git a/t/unit-tests/t-reftable-tree.c b/t/unit-tests/t-reftable-tree.c
index 700479d34b..79b175a45a 100644
--- a/t/unit-tests/t-reftable-tree.c
+++ b/t/unit-tests/t-reftable-tree.c
@@ -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);
--
2.46.2.852.g229c0bf0e5.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v3 22/22] reftable: handle trivial allocation failures
2024-09-30 8:08 ` [PATCH v3 00/22] refatble: handle allocation errors Patrick Steinhardt
` (20 preceding siblings ...)
2024-09-30 8:09 ` [PATCH v3 21/22] reftable/tree: handle allocation failures Patrick Steinhardt
@ 2024-09-30 8:09 ` Patrick Steinhardt
2024-09-30 18:18 ` [PATCH v3 00/22] refatble: handle allocation errors Junio C Hamano
22 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-30 8:09 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
Handle trivial allocation failures in the reftable library and its unit
tests.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
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(-)
diff --git a/reftable/merged.c b/reftable/merged.c
index 2c20845d62..52bc66c527 100644
--- a/reftable/merged.c
+++ b/reftable/merged.c
@@ -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;
diff --git a/reftable/reader.c b/reftable/reader.c
index 0179e4e73d..98e7aa2637 100644
--- a/reftable/reader.c
+++ b/reftable/reader.c
@@ -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;
diff --git a/reftable/stack.c b/reftable/stack.c
index 990784d9d2..7df28ab343 100644
--- a/reftable/stack.c
+++ b/reftable/stack.c
@@ -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);
diff --git a/reftable/writer.c b/reftable/writer.c
index 44bed92f46..ad4718edec 100644
--- a/reftable/writer.c
+++ b/reftable/writer.c
@@ -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);
diff --git a/t/unit-tests/t-reftable-block.c b/t/unit-tests/t-reftable-block.c
index e52a612e85..d470060e8b 100644
--- a/t/unit-tests/t-reftable-block.c
+++ b/t/unit-tests/t-reftable-block.c
@@ -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,
diff --git a/t/unit-tests/t-reftable-merged.c b/t/unit-tests/t-reftable-merged.c
index 3d2848632d..3c84363e98 100644
--- a/t/unit-tests/t-reftable-merged.c
+++ b/t/unit-tests/t-reftable-merged.c
@@ -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);
diff --git a/t/unit-tests/t-reftable-readwrite.c b/t/unit-tests/t-reftable-readwrite.c
index acca927a2c..bfa069caff 100644
--- a/t/unit-tests/t-reftable-readwrite.c
+++ b/t/unit-tests/t-reftable-readwrite.c
@@ -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);
--
2.46.2.852.g229c0bf0e5.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* Re: [PATCH v3 05/22] reftable/basics: handle allocation failures in `parse_names()`
2024-09-30 8:08 ` [PATCH v3 05/22] reftable/basics: handle allocation failures in `parse_names()` Patrick Steinhardt
@ 2024-09-30 17:40 ` René Scharfe
0 siblings, 0 replies; 152+ messages in thread
From: René Scharfe @ 2024-09-30 17:40 UTC (permalink / raw)
To: Patrick Steinhardt, git; +Cc: Edward Thomson, Junio C Hamano
Am 30.09.24 um 10:08 schrieb Patrick Steinhardt:
> diff --git a/reftable/basics.c b/reftable/basics.c
> index 3350bbffa2..5ae6e6acdd 100644
> --- a/reftable/basics.c
> +++ b/reftable/basics.c
> @@ -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] = 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]);
If you use reftable_free() here, you'd better use reftable_strdup()
instead of strdup(3) above.
René
^ permalink raw reply [flat|nested] 152+ messages in thread
* Re: [PATCH v3 09/22] reftable/writer: handle allocation failures in `reftable_new_writer()`
2024-09-30 8:08 ` [PATCH v3 09/22] reftable/writer: handle allocation failures in `reftable_new_writer()` Patrick Steinhardt
@ 2024-09-30 17:40 ` René Scharfe
2024-09-30 18:22 ` Patrick Steinhardt
0 siblings, 1 reply; 152+ messages in thread
From: René Scharfe @ 2024-09-30 17:40 UTC (permalink / raw)
To: Patrick Steinhardt, git; +Cc: Edward Thomson, Junio C Hamano
Am 30.09.24 um 10:08 schrieb Patrick Steinhardt:
> diff --git a/reftable/writer.c b/reftable/writer.c
> index ed61aaf59c..54ec822e1c 100644
> --- a/reftable/writer.c
> +++ b/reftable/writer.c
> @@ -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) {
> + free(wp);
Better use reftable_free() to free it, since you use reftable_calloc()
to allocate it above.
Perhaps ban free(3), strdup(3) etc. at the end of reftable/basics.h,
banned.h style?
René
^ permalink raw reply [flat|nested] 152+ messages in thread
* Re: [PATCH v3 00/22] refatble: handle allocation errors
2024-09-30 8:08 ` [PATCH v3 00/22] refatble: handle allocation errors Patrick Steinhardt
` (21 preceding siblings ...)
2024-09-30 8:09 ` [PATCH v3 22/22] reftable: handle trivial " Patrick Steinhardt
@ 2024-09-30 18:18 ` Junio C Hamano
22 siblings, 0 replies; 152+ messages in thread
From: Junio C Hamano @ 2024-09-30 18:18 UTC (permalink / raw)
To: Patrick Steinhardt; +Cc: git, Edward Thomson, René Scharfe
Patrick Steinhardt <ps@pks.im> writes:
> this is the third 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.
>
> There is only a single change compared to v1, namely that we use
> `reftable_free()` instead of `free()`.
Indeed that is the only change in this iteration.
Looking good.
Thanks.
^ permalink raw reply [flat|nested] 152+ messages in thread
* Re: [PATCH v3 09/22] reftable/writer: handle allocation failures in `reftable_new_writer()`
2024-09-30 17:40 ` René Scharfe
@ 2024-09-30 18:22 ` Patrick Steinhardt
2024-09-30 19:11 ` Junio C Hamano
0 siblings, 1 reply; 152+ messages in thread
From: Patrick Steinhardt @ 2024-09-30 18:22 UTC (permalink / raw)
To: René Scharfe; +Cc: git, Edward Thomson, Junio C Hamano
On Mon, Sep 30, 2024 at 07:40:48PM +0200, René Scharfe wrote:
> Am 30.09.24 um 10:08 schrieb Patrick Steinhardt:
> > diff --git a/reftable/writer.c b/reftable/writer.c
> > index ed61aaf59c..54ec822e1c 100644
> > --- a/reftable/writer.c
> > +++ b/reftable/writer.c
> > @@ -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) {
> > + free(wp);
>
> Better use reftable_free() to free it, since you use reftable_calloc()
> to allocate it above.
>
> Perhaps ban free(3), strdup(3) etc. at the end of reftable/basics.h,
> banned.h style?
Ugh. I was relying too much on your review having mentioned all cases,
but I really should've double-checked the other patches, too. Mind you,
I really don't mean to blame you here, I blame myself.
In any case, banning these functions via "reftable/basics.h" certainly
seems like a good idea. It's just too easy to screw up by accident. Will
fix tomorrow.
Thanks!
Patrick
^ permalink raw reply [flat|nested] 152+ messages in thread
* Re: [PATCH v3 09/22] reftable/writer: handle allocation failures in `reftable_new_writer()`
2024-09-30 18:22 ` Patrick Steinhardt
@ 2024-09-30 19:11 ` Junio C Hamano
0 siblings, 0 replies; 152+ messages in thread
From: Junio C Hamano @ 2024-09-30 19:11 UTC (permalink / raw)
To: Patrick Steinhardt; +Cc: René Scharfe, git, Edward Thomson
Patrick Steinhardt <ps@pks.im> writes:
>> Perhaps ban free(3), strdup(3) etc. at the end of reftable/basics.h,
>> banned.h style?
>
> Ugh. I was relying too much on your review having mentioned all cases,
> but I really should've double-checked the other patches, too. Mind you,
> I really don't mean to blame you here, I blame myself.
>
> In any case, banning these functions via "reftable/basics.h" certainly
> seems like a good idea. It's just too easy to screw up by accident. Will
> fix tomorrow.
Sounds like a good plan to do the banned.h-style annotations.
Thanks.
^ permalink raw reply [flat|nested] 152+ messages in thread
* [PATCH v4 00/25] reftable: handle allocation errors
2024-09-16 12:28 [PATCH 00/22] reftable: handle allocation errors Patrick Steinhardt
` (23 preceding siblings ...)
2024-09-30 8:08 ` [PATCH v3 00/22] refatble: handle allocation errors Patrick Steinhardt
@ 2024-10-01 9:41 ` Patrick Steinhardt
2024-10-01 9:41 ` [PATCH v4 01/25] reftable/error: introduce out-of-memory error code Patrick Steinhardt
` (25 more replies)
2024-10-02 10:55 ` [PATCH v5 " Patrick Steinhardt
25 siblings, 26 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-10-01 9:41 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
Hi,
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:
- 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.
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 | 23 +++-
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, 885 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 v3:
1: 8c99ecc325 = 1: 94eaef3ae5 reftable/error: introduce out-of-memory error code
2: 4dcdf1d48e = 2: fe55051cb7 reftable/basics: merge "publicbasics" into "basics"
3: 21fa9b15d9 = 3: b1a10d41d3 reftable: introduce `reftable_strdup()`
4: f6ad92ffd0 = 4: fd9141e985 reftable/basics: handle allocation failures in `reftable_calloc()`
5: 922783708d ! 5: bdfddbebce reftable/basics: handle allocation failures in `parse_names()`
@@ reftable/basics.c: void parse_names(char *buf, int size, char ***namesp)
+ if (!names)
+ goto err;
+
-+ names[names_len] = strdup(p);
++ names[names_len] = reftable_strdup(p);
+ if (!names[names_len++])
+ goto err;
}
6: d4676e8a6a = 6: 756a32c285 reftable/record: handle allocation failures on copy
7: 874e6a1c20 = 7: ca64971e7b reftable/record: handle allocation failures when decoding records
8: 42bc57fd00 = 8: fc2f113cba reftable/writer: handle allocation failures in `writer_index_hash()`
9: 9edd1d84cd ! 9: 0ed99e0bdf reftable/writer: handle allocation failures in `reftable_new_writer()`
@@ reftable/writer.c: reftable_new_writer(ssize_t (*writer_func)(void *, const void
strbuf_init(&wp->last_key, 0);
REFTABLE_CALLOC_ARRAY(wp->block, opts.block_size);
+ if (!wp->block) {
-+ free(wp);
++ reftable_free(wp);
+ return REFTABLE_OUT_OF_MEMORY_ERROR;
+ }
wp->write = writer_func;
10: d4004a7f43 ! 10: 8dfbfd9286 reftable/merged: handle allocation failures in `merged_table_init_iter()`
@@ reftable/merged.c: reftable_merged_table_min_update_index(struct reftable_merged
+ reftable_iterator_destroy(&subiters[i].iter);
+ reftable_record_release(&subiters[i].rec);
+ }
-+ free(subiters);
-+ free(mi);
++ reftable_free(subiters);
++ reftable_free(mi);
+ }
+
+ return ret;
11: b2bd142021 = 11: 7b592a6c6b reftable/reader: handle allocation failures for unindexed reader
12: 9b5fd52862 = 12: ceed838265 reftable/reader: handle allocation failures in `reader_init_iter()`
13: 0f3e3d1585 = 13: 1e997a5766 reftable/stack: handle allocation failures on reload
14: c88645a251 = 14: 40d4d81378 reftable/stack: handle allocation failures in `reftable_new_stack()`
15: 92ad4fc934 = 15: 6aaae4baa6 reftable/stack: handle allocation failures in `stack_compact_range()`
16: 19acebd919 = 16: 103a59ef0a reftable/stack: handle allocation failures in auto compaction
17: 620658bffc = 17: 6799d299fe reftable/iter: handle allocation failures when creating indexed table iter
18: 48047dd4c9 ! 18: c7e54d71d7 reftable/blocksource: handle allocation failures
@@ reftable/blocksource.c: int reftable_block_source_from_file(struct reftable_bloc
+ if (fd >= 0)
+ close(fd);
+ if (err < 0)
-+ free(p);
++ reftable_free(p);
return 0;
}
19: 08685605ba = 19: 92d39b9021 reftable/block: handle allocation failures
20: a66937b7af = 20: 3416004e0d reftable/pq: handle allocation failures when adding entries
21: c291114553 = 21: a363930488 reftable/tree: handle allocation failures
22: 20f50c446a = 22: 28661500ff reftable: handle trivial allocation failures
-: ---------- > 23: 228cc81263 reftable: fix calls to free(3P)
-: ---------- > 24: 1c66f6ef8d reftable: introduce `REFTABLE_FREE_AND_NULL()`
-: ---------- > 25: 764961e6f0 reftable/basics: ban standard allocator functions
--
2.47.0.rc0.dirty
^ permalink raw reply [flat|nested] 152+ messages in thread
* [PATCH v4 01/25] reftable/error: introduce out-of-memory error code
2024-10-01 9:41 ` [PATCH v4 00/25] reftable: " Patrick Steinhardt
@ 2024-10-01 9:41 ` Patrick Steinhardt
2024-10-01 9:41 ` [PATCH v4 02/25] reftable/basics: merge "publicbasics" into "basics" Patrick Steinhardt
` (24 subsequent siblings)
25 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-10-01 9:41 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
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.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/error.c | 2 ++
reftable/reftable-error.h | 3 +++
2 files changed, 5 insertions(+)
diff --git a/reftable/error.c b/reftable/error.c
index a25f28a43e..660d029617 100644
--- a/reftable/error.c
+++ b/reftable/error.c
@@ -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:
diff --git a/reftable/reftable-error.h b/reftable/reftable-error.h
index 6368cd9ed9..f404826562 100644
--- a/reftable/reftable-error.h
+++ b/reftable/reftable-error.h
@@ -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
--
2.47.0.rc0.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v4 02/25] reftable/basics: merge "publicbasics" into "basics"
2024-10-01 9:41 ` [PATCH v4 00/25] reftable: " Patrick Steinhardt
2024-10-01 9:41 ` [PATCH v4 01/25] reftable/error: introduce out-of-memory error code Patrick Steinhardt
@ 2024-10-01 9:41 ` Patrick Steinhardt
2024-10-01 9:41 ` [PATCH v4 03/25] reftable: introduce `reftable_strdup()` Patrick Steinhardt
` (23 subsequent siblings)
25 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-10-01 9:41 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
The 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.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
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
diff --git a/Makefile b/Makefile
index e3abf0ba83..39b10923d4 100644
--- a/Makefile
+++ b/Makefile
@@ -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
diff --git a/reftable/basics.c b/reftable/basics.c
index 0058619ca6..cf072935c8 100644
--- a/reftable/basics.c
+++ b/reftable/basics.c
@@ -7,6 +7,49 @@ license that can be found in the LICENSE file or at
*/
#include "basics.h"
+#include "reftable-basics.h"
+
+static void *(*reftable_malloc_ptr)(size_t sz);
+static void *(*reftable_realloc_ptr)(void *, size_t);
+static void (*reftable_free_ptr)(void *);
+
+void *reftable_malloc(size_t sz)
+{
+ if (reftable_malloc_ptr)
+ return (*reftable_malloc_ptr)(sz);
+ return malloc(sz);
+}
+
+void *reftable_realloc(void *p, size_t sz)
+{
+ if (reftable_realloc_ptr)
+ return (*reftable_realloc_ptr)(p, sz);
+ return realloc(p, sz);
+}
+
+void reftable_free(void *p)
+{
+ if (reftable_free_ptr)
+ reftable_free_ptr(p);
+ else
+ free(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);
+ return p;
+}
+
+void reftable_set_alloc(void *(*malloc)(size_t),
+ void *(*realloc)(void *, size_t), void (*free)(void *))
+{
+ reftable_malloc_ptr = malloc;
+ reftable_realloc_ptr = realloc;
+ reftable_free_ptr = free;
+}
void put_be24(uint8_t *out, uint32_t i)
{
@@ -121,3 +164,15 @@ int common_prefix_size(struct strbuf *a, struct strbuf *b)
return p;
}
+
+int hash_size(uint32_t id)
+{
+ switch (id) {
+ case 0:
+ case GIT_SHA1_FORMAT_ID:
+ return GIT_SHA1_RAWSZ;
+ case GIT_SHA256_FORMAT_ID:
+ return GIT_SHA256_RAWSZ;
+ }
+ abort();
+}
diff --git a/reftable/basics.h b/reftable/basics.h
index c8fec68d4e..4e2e76014a 100644
--- a/reftable/basics.h
+++ b/reftable/basics.h
@@ -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
diff --git a/reftable/publicbasics.c b/reftable/publicbasics.c
deleted file mode 100644
index 44b84a125e..0000000000
--- a/reftable/publicbasics.c
+++ /dev/null
@@ -1,66 +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
-*/
-
-#include "system.h"
-#include "reftable-malloc.h"
-
-#include "basics.h"
-
-static void *(*reftable_malloc_ptr)(size_t sz);
-static void *(*reftable_realloc_ptr)(void *, size_t);
-static void (*reftable_free_ptr)(void *);
-
-void *reftable_malloc(size_t sz)
-{
- if (reftable_malloc_ptr)
- return (*reftable_malloc_ptr)(sz);
- return malloc(sz);
-}
-
-void *reftable_realloc(void *p, size_t sz)
-{
- if (reftable_realloc_ptr)
- return (*reftable_realloc_ptr)(p, sz);
- return realloc(p, sz);
-}
-
-void reftable_free(void *p)
-{
- if (reftable_free_ptr)
- reftable_free_ptr(p);
- else
- free(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);
- return p;
-}
-
-void reftable_set_alloc(void *(*malloc)(size_t),
- void *(*realloc)(void *, size_t), void (*free)(void *))
-{
- reftable_malloc_ptr = malloc;
- reftable_realloc_ptr = realloc;
- reftable_free_ptr = free;
-}
-
-int hash_size(uint32_t id)
-{
- switch (id) {
- case 0:
- case GIT_SHA1_FORMAT_ID:
- return GIT_SHA1_RAWSZ;
- case GIT_SHA256_FORMAT_ID:
- return GIT_SHA256_RAWSZ;
- }
- abort();
-}
diff --git a/reftable/reftable-basics.h b/reftable/reftable-basics.h
new file mode 100644
index 0000000000..6e8e636b71
--- /dev/null
+++ b/reftable/reftable-basics.h
@@ -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
diff --git a/reftable/reftable-malloc.h b/reftable/reftable-malloc.h
deleted file mode 100644
index 5f2185f1f3..0000000000
--- a/reftable/reftable-malloc.h
+++ /dev/null
@@ -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
--
2.47.0.rc0.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v4 03/25] reftable: introduce `reftable_strdup()`
2024-10-01 9:41 ` [PATCH v4 00/25] reftable: " Patrick Steinhardt
2024-10-01 9:41 ` [PATCH v4 01/25] reftable/error: introduce out-of-memory error code Patrick Steinhardt
2024-10-01 9:41 ` [PATCH v4 02/25] reftable/basics: merge "publicbasics" into "basics" Patrick Steinhardt
@ 2024-10-01 9:41 ` Patrick Steinhardt
2024-10-01 9:41 ` [PATCH v4 04/25] reftable/basics: handle allocation failures in `reftable_calloc()` Patrick Steinhardt
` (22 subsequent siblings)
25 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-10-01 9:41 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
The 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.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/basics.c | 10 ++++++++++
reftable/basics.h | 1 +
2 files changed, 11 insertions(+)
diff --git a/reftable/basics.c b/reftable/basics.c
index cf072935c8..4adc98cf5d 100644
--- a/reftable/basics.c
+++ b/reftable/basics.c
@@ -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 *))
{
diff --git a/reftable/basics.h b/reftable/basics.h
index 4e2e76014a..f107e14860 100644
--- a/reftable/basics.h
+++ b/reftable/basics.h
@@ -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)))
--
2.47.0.rc0.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v4 04/25] reftable/basics: handle allocation failures in `reftable_calloc()`
2024-10-01 9:41 ` [PATCH v4 00/25] reftable: " Patrick Steinhardt
` (2 preceding siblings ...)
2024-10-01 9:41 ` [PATCH v4 03/25] reftable: introduce `reftable_strdup()` Patrick Steinhardt
@ 2024-10-01 9:41 ` Patrick Steinhardt
2024-10-01 9:41 ` [PATCH v4 05/25] reftable/basics: handle allocation failures in `parse_names()` Patrick Steinhardt
` (21 subsequent siblings)
25 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-10-01 9:41 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
Handle 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.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/basics.c | 13 ++++++++++---
1 file changed, 10 insertions(+), 3 deletions(-)
diff --git a/reftable/basics.c b/reftable/basics.c
index 4adc98cf5d..3350bbffa2 100644
--- a/reftable/basics.c
+++ b/reftable/basics.c
@@ -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;
}
--
2.47.0.rc0.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v4 05/25] reftable/basics: handle allocation failures in `parse_names()`
2024-10-01 9:41 ` [PATCH v4 00/25] reftable: " Patrick Steinhardt
` (3 preceding siblings ...)
2024-10-01 9:41 ` [PATCH v4 04/25] reftable/basics: handle allocation failures in `reftable_calloc()` Patrick Steinhardt
@ 2024-10-01 9:41 ` Patrick Steinhardt
2024-10-01 9:42 ` [PATCH v4 06/25] reftable/record: handle allocation failures on copy Patrick Steinhardt
` (20 subsequent siblings)
25 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-10-01 9:41 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
Handle 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.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
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(-)
diff --git a/reftable/basics.c b/reftable/basics.c
index 3350bbffa2..ea53cf102a 100644
--- a/reftable/basics.c
+++ b/reftable/basics.c
@@ -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)
diff --git a/reftable/basics.h b/reftable/basics.h
index f107e14860..69adeab2e4 100644
--- a/reftable/basics.h
+++ b/reftable/basics.h
@@ -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);
diff --git a/reftable/stack.c b/reftable/stack.c
index ce0a35216b..498fae846d 100644
--- a/reftable/stack.c
+++ b/reftable/stack.c
@@ -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);
diff --git a/t/unit-tests/t-reftable-basics.c b/t/unit-tests/t-reftable-basics.c
index e5556ebf52..1fa77b6faf 100644
--- a/t/unit-tests/t-reftable-basics.c
+++ b/t/unit-tests/t-reftable-basics.c
@@ -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");
--
2.47.0.rc0.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v4 06/25] reftable/record: handle allocation failures on copy
2024-10-01 9:41 ` [PATCH v4 00/25] reftable: " Patrick Steinhardt
` (4 preceding siblings ...)
2024-10-01 9:41 ` [PATCH v4 05/25] reftable/basics: handle allocation failures in `parse_names()` Patrick Steinhardt
@ 2024-10-01 9:42 ` Patrick Steinhardt
2024-10-01 9:42 ` [PATCH v4 07/25] reftable/record: handle allocation failures when decoding records Patrick Steinhardt
` (19 subsequent siblings)
25 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-10-01 9:42 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
Handle allocation failures when copying records. While at it, convert
from `xstrdup()` to `reftable_strdup()`. Adapt callsites to check for
error codes.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/record.c | 84 +++++++++++++++++++++++++++++++++--------------
reftable/record.h | 6 ++--
2 files changed, 63 insertions(+), 27 deletions(-)
diff --git a/reftable/record.c b/reftable/record.c
index 6b5a075b92..60fd33c9c9 100644
--- a/reftable/record.c
+++ b/reftable/record.c
@@ -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)
diff --git a/reftable/record.h b/reftable/record.h
index 5003bacdb0..0f53ba5443 100644
--- a/reftable/record.h
+++ b/reftable/record.h
@@ -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);
--
2.47.0.rc0.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v4 07/25] reftable/record: handle allocation failures when decoding records
2024-10-01 9:41 ` [PATCH v4 00/25] reftable: " Patrick Steinhardt
` (5 preceding siblings ...)
2024-10-01 9:42 ` [PATCH v4 06/25] reftable/record: handle allocation failures on copy Patrick Steinhardt
@ 2024-10-01 9:42 ` Patrick Steinhardt
2024-10-01 9:42 ` [PATCH v4 08/25] reftable/writer: handle allocation failures in `writer_index_hash()` Patrick Steinhardt
` (18 subsequent siblings)
25 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-10-01 9:42 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
Handle allocation failures when decoding records. While at it, fix some
error codes to be `REFTABLE_FORMAT_ERROR`.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/record.c | 80 +++++++++++++++++++++++++++++++++++++----------
1 file changed, 63 insertions(+), 17 deletions(-)
diff --git a/reftable/record.c b/reftable/record.c
index 60fd33c9c9..787e134c9a 100644
--- a/reftable/record.c
+++ b/reftable/record.c
@@ -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)
--
2.47.0.rc0.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v4 08/25] reftable/writer: handle allocation failures in `writer_index_hash()`
2024-10-01 9:41 ` [PATCH v4 00/25] reftable: " Patrick Steinhardt
` (6 preceding siblings ...)
2024-10-01 9:42 ` [PATCH v4 07/25] reftable/record: handle allocation failures when decoding records Patrick Steinhardt
@ 2024-10-01 9:42 ` Patrick Steinhardt
2024-10-01 9:42 ` [PATCH v4 09/25] reftable/writer: handle allocation failures in `reftable_new_writer()` Patrick Steinhardt
` (17 subsequent siblings)
25 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-10-01 9:42 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
Handle allocation errors in `writer_index_hash()`. Adjust its only
caller in `reftable_writer_add_ref()` accordingly.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/writer.c | 61 +++++++++++++++++++++++++++++------------------
1 file changed, 38 insertions(+), 23 deletions(-)
diff --git a/reftable/writer.c b/reftable/writer.c
index 9d5e6072bc..ed61aaf59c 100644
--- a/reftable/writer.c
+++ b/reftable/writer.c
@@ -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,
--
2.47.0.rc0.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v4 09/25] reftable/writer: handle allocation failures in `reftable_new_writer()`
2024-10-01 9:41 ` [PATCH v4 00/25] reftable: " Patrick Steinhardt
` (7 preceding siblings ...)
2024-10-01 9:42 ` [PATCH v4 08/25] reftable/writer: handle allocation failures in `writer_index_hash()` Patrick Steinhardt
@ 2024-10-01 9:42 ` Patrick Steinhardt
2024-10-01 9:42 ` [PATCH v4 10/25] reftable/merged: handle allocation failures in `merged_table_init_iter()` Patrick Steinhardt
` (16 subsequent siblings)
25 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-10-01 9:42 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
Handle 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.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
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(-)
diff --git a/reftable/reftable-writer.h b/reftable/reftable-writer.h
index 189b1f4144..43623dc7c3 100644
--- a/reftable/reftable-writer.h
+++ b/reftable/reftable-writer.h
@@ -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
diff --git a/reftable/stack.c b/reftable/stack.c
index 498fae846d..ea21ca6e5f 100644
--- a/reftable/stack.c
+++ b/reftable/stack.c
@@ -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;
diff --git a/reftable/writer.c b/reftable/writer.c
index ed61aaf59c..8ab2e916d3 100644
--- a/reftable/writer.c
+++ b/reftable/writer.c
@@ -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,
diff --git a/t/unit-tests/lib-reftable.c b/t/unit-tests/lib-reftable.c
index ab1fa44a28..54c26c43e7 100644
--- a/t/unit-tests/lib-reftable.c
+++ b/t/unit-tests/lib-reftable.c
@@ -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,
--
2.47.0.rc0.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v4 10/25] reftable/merged: handle allocation failures in `merged_table_init_iter()`
2024-10-01 9:41 ` [PATCH v4 00/25] reftable: " Patrick Steinhardt
` (8 preceding siblings ...)
2024-10-01 9:42 ` [PATCH v4 09/25] reftable/writer: handle allocation failures in `reftable_new_writer()` Patrick Steinhardt
@ 2024-10-01 9:42 ` Patrick Steinhardt
2024-10-01 9:42 ` [PATCH v4 11/25] reftable/reader: handle allocation failures for unindexed reader Patrick Steinhardt
` (15 subsequent siblings)
25 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-10-01 9:42 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
Handle 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.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
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(-)
diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c
index 5c241097a4..9c08c46c85 100644
--- a/refs/reftable-backend.c
+++ b/refs/reftable-backend.c
@@ -1299,7 +1299,9 @@ static int write_transaction_table(struct reftable_writer *writer, void *cb_data
struct 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 reftabl
if (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_da
reftable_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)
diff --git a/reftable/merged.c b/reftable/merged.c
index 128a810c55..de4f81abaf 100644
--- a/reftable/merged.c
+++ b/reftable/merged.c
@@ -30,22 +30,6 @@ struct merged_iter {
ssize_t advance_index;
};
-static void merged_iter_init(struct merged_iter *mi,
- struct reftable_merged_table *mt,
- uint8_t typ)
-{
- memset(mi, 0, sizeof(*mi));
- mi->advance_index = -1;
- mi->suppress_deletions = mt->suppress_deletions;
-
- REFTABLE_CALLOC_ARRAY(mi->subiters, mt->readers_len);
- for (size_t i = 0; i < mt->readers_len; i++) {
- reftable_record_init(&mi->subiters[i].rec, typ);
- reader_init_iter(mt->readers[i], &mi->subiters[i].iter, typ);
- }
- mi->subiters_len = mt->readers_len;
-}
-
static void merged_iter_close(void *p)
{
struct merged_iter *mi = p;
@@ -244,25 +228,61 @@ reftable_merged_table_min_update_index(struct reftable_merged_table *mt)
return mt->min;
}
-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)
{
- struct merged_iter *mi = reftable_malloc(sizeof(*mi));
- merged_iter_init(mi, mt, typ);
+ struct merged_subiter *subiters;
+ struct merged_iter *mi = NULL;
+ int ret;
+
+ REFTABLE_CALLOC_ARRAY(subiters, mt->readers_len);
+ if (!subiters) {
+ ret = REFTABLE_OUT_OF_MEMORY_ERROR;
+ goto out;
+ }
+
+ 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);
+ }
+
+ REFTABLE_CALLOC_ARRAY(mi, 1);
+ if (!mi) {
+ ret = REFTABLE_OUT_OF_MEMORY_ERROR;
+ goto out;
+ }
+ mi->advance_index = -1;
+ mi->suppress_deletions = mt->suppress_deletions;
+ mi->subiters = subiters;
+ mi->subiters_len = mt->readers_len;
+
iterator_from_merged_iter(it, mi);
+ ret = 0;
+
+out:
+ if (ret < 0) {
+ for (size_t i = 0; subiters && i < mt->readers_len; i++) {
+ reftable_iterator_destroy(&subiters[i].iter);
+ reftable_record_release(&subiters[i].rec);
+ }
+ reftable_free(subiters);
+ reftable_free(mi);
+ }
+
+ return ret;
}
-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)
{
- merged_table_init_iter(mt, it, BLOCK_TYPE_REF);
+ return merged_table_init_iter(mt, it, BLOCK_TYPE_REF);
}
-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)
{
- merged_table_init_iter(mt, it, BLOCK_TYPE_LOG);
+ return merged_table_init_iter(mt, it, BLOCK_TYPE_LOG);
}
uint32_t reftable_merged_table_hash_id(struct reftable_merged_table *mt)
diff --git a/reftable/merged.h b/reftable/merged.h
index de5fd33f01..89bd0c4b35 100644
--- a/reftable/merged.h
+++ b/reftable/merged.h
@@ -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
diff --git a/reftable/reftable-merged.h b/reftable/reftable-merged.h
index 16d19f8df2..a970d5dd89 100644
--- a/reftable/reftable-merged.h
+++ b/reftable/reftable-merged.h
@@ -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
diff --git a/reftable/reftable-stack.h b/reftable/reftable-stack.h
index f4f8cabc7f..e958f911b4 100644
--- a/reftable/reftable-stack.h
+++ b/reftable/reftable-stack.h
@@ -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.
diff --git a/reftable/stack.c b/reftable/stack.c
index ea21ca6e5f..bb4d230918 100644
--- a/reftable/stack.c
+++ b/reftable/stack.c
@@ -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;
diff --git a/t/helper/test-reftable.c b/t/helper/test-reftable.c
index 29d4e9a755..5c8849d115 100644
--- a/t/helper/test-reftable.c
+++ b/t/helper/test-reftable.c
@@ -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;
diff --git a/t/unit-tests/t-reftable-merged.c b/t/unit-tests/t-reftable-merged.c
index 19e54bdfb8..3d2848632d 100644
--- a/t/unit-tests/t-reftable-merged.c
+++ b/t/unit-tests/t-reftable-merged.c
@@ -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]);
diff --git a/t/unit-tests/t-reftable-stack.c b/t/unit-tests/t-reftable-stack.c
index 65e513d5ec..6e41a4cf7e 100644
--- a/t/unit-tests/t-reftable-stack.c
+++ b/t/unit-tests/t-reftable-stack.c
@@ -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 };
--
2.47.0.rc0.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v4 11/25] reftable/reader: handle allocation failures for unindexed reader
2024-10-01 9:41 ` [PATCH v4 00/25] reftable: " Patrick Steinhardt
` (9 preceding siblings ...)
2024-10-01 9:42 ` [PATCH v4 10/25] reftable/merged: handle allocation failures in `merged_table_init_iter()` Patrick Steinhardt
@ 2024-10-01 9:42 ` Patrick Steinhardt
2024-10-01 9:42 ` [PATCH v4 12/25] reftable/reader: handle allocation failures in `reader_init_iter()` Patrick Steinhardt
` (14 subsequent siblings)
25 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-10-01 9:42 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
Handle allocation failures when creating unindexed readers.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/reader.c | 23 +++++++++++++++++------
1 file changed, 17 insertions(+), 6 deletions(-)
diff --git a/reftable/reader.c b/reftable/reader.c
index 6494ce2e32..485ee085da 100644
--- a/reftable/reader.c
+++ b/reftable/reader.c
@@ -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,
--
2.47.0.rc0.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v4 12/25] reftable/reader: handle allocation failures in `reader_init_iter()`
2024-10-01 9:41 ` [PATCH v4 00/25] reftable: " Patrick Steinhardt
` (10 preceding siblings ...)
2024-10-01 9:42 ` [PATCH v4 11/25] reftable/reader: handle allocation failures for unindexed reader Patrick Steinhardt
@ 2024-10-01 9:42 ` Patrick Steinhardt
2024-10-01 9:42 ` [PATCH v4 13/25] reftable/stack: handle allocation failures on reload Patrick Steinhardt
` (13 subsequent siblings)
25 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-10-01 9:42 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
Handle 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.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
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(-)
diff --git a/reftable/merged.c b/reftable/merged.c
index de4f81abaf..69790c345c 100644
--- a/reftable/merged.c
+++ b/reftable/merged.c
@@ -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);
diff --git a/reftable/reader.c b/reftable/reader.c
index 485ee085da..f696e992df 100644
--- a/reftable/reader.c
+++ b/reftable/reader.c
@@ -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;
diff --git a/reftable/reader.h b/reftable/reader.h
index 3710ee09b4..02d10c5d37 100644
--- a/reftable/reader.h
+++ b/reftable/reader.h
@@ -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,
diff --git a/reftable/reftable-reader.h b/reftable/reftable-reader.h
index a600452b56..6a2d0b693f 100644
--- a/reftable/reftable-reader.h
+++ b/reftable/reftable-reader.h
@@ -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);
diff --git a/t/unit-tests/t-reftable-readwrite.c b/t/unit-tests/t-reftable-readwrite.c
index e1b235a5f1..acca927a2c 100644
--- a/t/unit-tests/t-reftable-readwrite.c
+++ b/t/unit-tests/t-reftable-readwrite.c
@@ -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);
--
2.47.0.rc0.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v4 13/25] reftable/stack: handle allocation failures on reload
2024-10-01 9:41 ` [PATCH v4 00/25] reftable: " Patrick Steinhardt
` (11 preceding siblings ...)
2024-10-01 9:42 ` [PATCH v4 12/25] reftable/reader: handle allocation failures in `reader_init_iter()` Patrick Steinhardt
@ 2024-10-01 9:42 ` Patrick Steinhardt
2024-10-01 9:42 ` [PATCH v4 14/25] reftable/stack: handle allocation failures in `reftable_new_stack()` Patrick Steinhardt
` (12 subsequent siblings)
25 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-10-01 9:42 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
Handle allocation failures in `reftable_stack_reload_once()`.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/stack.c | 34 +++++++++++++++++++++++++---------
1 file changed, 25 insertions(+), 9 deletions(-)
diff --git a/reftable/stack.c b/reftable/stack.c
index bb4d230918..060b2c1b90 100644
--- a/reftable/stack.c
+++ b/reftable/stack.c
@@ -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;
--
2.47.0.rc0.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v4 14/25] reftable/stack: handle allocation failures in `reftable_new_stack()`
2024-10-01 9:41 ` [PATCH v4 00/25] reftable: " Patrick Steinhardt
` (12 preceding siblings ...)
2024-10-01 9:42 ` [PATCH v4 13/25] reftable/stack: handle allocation failures on reload Patrick Steinhardt
@ 2024-10-01 9:42 ` Patrick Steinhardt
2024-10-01 9:42 ` [PATCH v4 15/25] reftable/stack: handle allocation failures in `stack_compact_range()` Patrick Steinhardt
` (11 subsequent siblings)
25 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-10-01 9:42 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
Handle allocation failures in `reftable_new_stack()`.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/stack.c | 34 ++++++++++++++++++++++++++--------
1 file changed, 26 insertions(+), 8 deletions(-)
diff --git a/reftable/stack.c b/reftable/stack.c
index 060b2c1b90..1b77c9d014 100644
--- a/reftable/stack.c
+++ b/reftable/stack.c
@@ -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;
--
2.47.0.rc0.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v4 15/25] reftable/stack: handle allocation failures in `stack_compact_range()`
2024-10-01 9:41 ` [PATCH v4 00/25] reftable: " Patrick Steinhardt
` (13 preceding siblings ...)
2024-10-01 9:42 ` [PATCH v4 14/25] reftable/stack: handle allocation failures in `reftable_new_stack()` Patrick Steinhardt
@ 2024-10-01 9:42 ` Patrick Steinhardt
2024-10-01 9:42 ` [PATCH v4 16/25] reftable/stack: handle allocation failures in auto compaction Patrick Steinhardt
` (10 subsequent siblings)
25 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-10-01 9:42 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
Handle allocation failures in `stack_compact_range()`.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/stack.c | 19 +++++++++++++++++--
1 file changed, 17 insertions(+), 2 deletions(-)
diff --git a/reftable/stack.c b/reftable/stack.c
index 1b77c9d014..2e6dd513d7 100644
--- a/reftable/stack.c
+++ b/reftable/stack.c
@@ -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;
}
--
2.47.0.rc0.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v4 16/25] reftable/stack: handle allocation failures in auto compaction
2024-10-01 9:41 ` [PATCH v4 00/25] reftable: " Patrick Steinhardt
` (14 preceding siblings ...)
2024-10-01 9:42 ` [PATCH v4 15/25] reftable/stack: handle allocation failures in `stack_compact_range()` Patrick Steinhardt
@ 2024-10-01 9:42 ` Patrick Steinhardt
2024-10-01 9:42 ` [PATCH v4 17/25] reftable/iter: handle allocation failures when creating indexed table iter Patrick Steinhardt
` (9 subsequent siblings)
25 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-10-01 9:42 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
Handle allocation failures in `reftable_stack_auto_compact()`.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/stack.c | 16 ++++++++++++----
1 file changed, 12 insertions(+), 4 deletions(-)
diff --git a/reftable/stack.c b/reftable/stack.c
index 2e6dd513d7..990784d9d2 100644
--- a/reftable/stack.c
+++ b/reftable/stack.c
@@ -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);
--
2.47.0.rc0.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v4 17/25] reftable/iter: handle allocation failures when creating indexed table iter
2024-10-01 9:41 ` [PATCH v4 00/25] reftable: " Patrick Steinhardt
` (15 preceding siblings ...)
2024-10-01 9:42 ` [PATCH v4 16/25] reftable/stack: handle allocation failures in auto compaction Patrick Steinhardt
@ 2024-10-01 9:42 ` Patrick Steinhardt
2024-10-01 9:42 ` [PATCH v4 18/25] reftable/blocksource: handle allocation failures Patrick Steinhardt
` (8 subsequent siblings)
25 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-10-01 9:42 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
Handle allocation failures in `new_indexed_table_ref_iter()`. While at
it, rename the function to match our coding style.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/iter.c | 20 ++++++++++++++++----
reftable/iter.h | 2 +-
reftable/reader.c | 7 ++++++-
3 files changed, 23 insertions(+), 6 deletions(-)
diff --git a/reftable/iter.c b/reftable/iter.c
index 416a9f6996..d319538f80 100644
--- a/reftable/iter.c
+++ b/reftable/iter.c
@@ -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;
}
diff --git a/reftable/iter.h b/reftable/iter.h
index befc4597df..b3225bc7ad 100644
--- a/reftable/iter.h
+++ b/reftable/iter.h
@@ -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);
diff --git a/reftable/reader.c b/reftable/reader.c
index f696e992df..0179e4e73d 100644
--- a/reftable/reader.c
+++ b/reftable/reader.c
@@ -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)
--
2.47.0.rc0.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v4 18/25] reftable/blocksource: handle allocation failures
2024-10-01 9:41 ` [PATCH v4 00/25] reftable: " Patrick Steinhardt
` (16 preceding siblings ...)
2024-10-01 9:42 ` [PATCH v4 17/25] reftable/iter: handle allocation failures when creating indexed table iter Patrick Steinhardt
@ 2024-10-01 9:42 ` Patrick Steinhardt
2024-10-01 9:42 ` [PATCH v4 19/25] reftable/block: " Patrick Steinhardt
` (7 subsequent siblings)
25 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-10-01 9:42 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
Handle allocation failures in the blocksource code.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/blocksource.c | 25 ++++++++++++++++++++-----
1 file changed, 20 insertions(+), 5 deletions(-)
diff --git a/reftable/blocksource.c b/reftable/blocksource.c
index e93cac9bb6..a2a6a196d5 100644
--- a/reftable/blocksource.c
+++ b/reftable/blocksource.c
@@ -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;
}
--
2.47.0.rc0.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v4 19/25] reftable/block: handle allocation failures
2024-10-01 9:41 ` [PATCH v4 00/25] reftable: " Patrick Steinhardt
` (17 preceding siblings ...)
2024-10-01 9:42 ` [PATCH v4 18/25] reftable/blocksource: handle allocation failures Patrick Steinhardt
@ 2024-10-01 9:42 ` Patrick Steinhardt
2024-10-01 9:42 ` [PATCH v4 20/25] reftable/pq: handle allocation failures when adding entries Patrick Steinhardt
` (6 subsequent siblings)
25 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-10-01 9:42 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
Handle 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.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
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(-)
diff --git a/reftable/block.c b/reftable/block.c
index 00030eee06..bfa7dc61bf 100644
--- a/reftable/block.c
+++ b/reftable/block.c
@@ -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);
diff --git a/reftable/block.h b/reftable/block.h
index 1c8f25ee6e..18d7ea0337 100644
--- a/reftable/block.h
+++ b/reftable/block.h
@@ -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);
diff --git a/reftable/writer.c b/reftable/writer.c
index 8ab2e916d3..791e246337 100644
--- a/reftable/writer.c
+++ b/reftable/writer.c
@@ -102,19 +102,24 @@ static int writer_write_header(struct reftable_writer *w, uint8_t *dest)
return header_size(writer_version(w));
}
-static void writer_reinit_block_writer(struct reftable_writer *w, uint8_t typ)
+static int writer_reinit_block_writer(struct reftable_writer *w, uint8_t typ)
{
- int block_start = 0;
- if (w->next == 0) {
+ int block_start = 0, ret;
+
+ if (w->next == 0)
block_start = header_size(writer_version(w));
- }
strbuf_reset(&w->last_key);
- block_writer_init(&w->block_writer_data, typ, w->block,
- w->opts.block_size, block_start,
- hash_size(w->opts.hash_id));
+ ret = block_writer_init(&w->block_writer_data, typ, w->block,
+ w->opts.block_size, block_start,
+ hash_size(w->opts.hash_id));
+ if (ret < 0)
+ return ret;
+
w->block_writer = &w->block_writer_data;
w->block_writer->restart_interval = w->opts.restart_interval;
+
+ return 0;
}
int reftable_writer_new(struct reftable_writer **out,
@@ -247,8 +252,11 @@ static int writer_add_record(struct reftable_writer *w,
strbuf_reset(&w->last_key);
strbuf_addbuf(&w->last_key, &key);
- if (!w->block_writer)
- writer_reinit_block_writer(w, reftable_record_type(rec));
+ if (!w->block_writer) {
+ err = writer_reinit_block_writer(w, reftable_record_type(rec));
+ if (err < 0)
+ goto done;
+ }
if (block_writer_type(w->block_writer) != reftable_record_type(rec))
BUG("record of type %d added to writer of type %d",
@@ -271,7 +279,9 @@ static int writer_add_record(struct reftable_writer *w,
err = writer_flush_block(w);
if (err < 0)
goto done;
- writer_reinit_block_writer(w, reftable_record_type(rec));
+ err = writer_reinit_block_writer(w, reftable_record_type(rec));
+ if (err < 0)
+ goto done;
/*
* Try to add the record to the writer again. If this still fails then
@@ -461,7 +471,9 @@ static int writer_finish_section(struct reftable_writer *w)
max_level++;
index_start = w->next;
- writer_reinit_block_writer(w, BLOCK_TYPE_INDEX);
+ err = writer_reinit_block_writer(w, BLOCK_TYPE_INDEX);
+ if (err < 0)
+ return err;
idx = w->index;
idx_len = w->index_len;
@@ -555,7 +567,10 @@ static void write_object_record(void *void_arg, void *key)
if (arg->err < 0)
goto done;
- writer_reinit_block_writer(arg->w, BLOCK_TYPE_OBJ);
+ arg->err = writer_reinit_block_writer(arg->w, BLOCK_TYPE_OBJ);
+ if (arg->err < 0)
+ goto done;
+
arg->err = block_writer_add(arg->w->block_writer, &rec);
if (arg->err == 0)
goto done;
@@ -584,16 +599,18 @@ static int writer_dump_object_index(struct reftable_writer *w)
struct common_prefix_arg common = {
.max = 1, /* obj_id_len should be >= 2. */
};
- if (w->obj_index_tree) {
+ int err;
+
+ if (w->obj_index_tree)
infix_walk(w->obj_index_tree, &update_common, &common);
- }
w->stats.object_id_len = common.max + 1;
- writer_reinit_block_writer(w, BLOCK_TYPE_OBJ);
+ err = writer_reinit_block_writer(w, BLOCK_TYPE_OBJ);
+ if (err < 0)
+ return err;
- if (w->obj_index_tree) {
+ if (w->obj_index_tree)
infix_walk(w->obj_index_tree, &write_object_record, &closure);
- }
if (closure.err < 0)
return closure.err;
diff --git a/t/unit-tests/t-reftable-block.c b/t/unit-tests/t-reftable-block.c
index f1a49485e2..e52a612e85 100644
--- a/t/unit-tests/t-reftable-block.c
+++ b/t/unit-tests/t-reftable-block.c
@@ -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);
--
2.47.0.rc0.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v4 20/25] reftable/pq: handle allocation failures when adding entries
2024-10-01 9:41 ` [PATCH v4 00/25] reftable: " Patrick Steinhardt
` (18 preceding siblings ...)
2024-10-01 9:42 ` [PATCH v4 19/25] reftable/block: " Patrick Steinhardt
@ 2024-10-01 9:42 ` Patrick Steinhardt
2024-10-01 9:42 ` [PATCH v4 21/25] reftable/tree: handle allocation failures Patrick Steinhardt
` (5 subsequent siblings)
25 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-10-01 9:42 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
Handle allocation failures when adding entries to the pqueue. Adapt its
only caller accordingly.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/merged.c | 5 ++++-
reftable/pq.c | 7 ++++++-
reftable/pq.h | 2 +-
3 files changed, 11 insertions(+), 3 deletions(-)
diff --git a/reftable/merged.c b/reftable/merged.c
index 69790c345c..8e202a8efd 100644
--- a/reftable/merged.c
+++ b/reftable/merged.c
@@ -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;
}
diff --git a/reftable/pq.c b/reftable/pq.c
index 2b5b7d1c0e..03b9912282 100644
--- a/reftable/pq.c
+++ b/reftable/pq.c
@@ -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_entry
SWAP(pq->heap[j], pq->heap[i]);
i = j;
}
+
+ return 0;
}
void merged_iter_pqueue_release(struct merged_iter_pqueue *pq)
diff --git a/reftable/pq.h b/reftable/pq.h
index 707bd26767..83c062eeca 100644
--- a/reftable/pq.h
+++ b/reftable/pq.h
@@ -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);
--
2.47.0.rc0.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v4 21/25] reftable/tree: handle allocation failures
2024-10-01 9:41 ` [PATCH v4 00/25] reftable: " Patrick Steinhardt
` (19 preceding siblings ...)
2024-10-01 9:42 ` [PATCH v4 20/25] reftable/pq: handle allocation failures when adding entries Patrick Steinhardt
@ 2024-10-01 9:42 ` Patrick Steinhardt
2024-10-01 9:42 ` [PATCH v4 22/25] reftable: handle trivial " Patrick Steinhardt
` (4 subsequent siblings)
25 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-10-01 9:42 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
The 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.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
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(-)
diff --git a/reftable/tree.c b/reftable/tree.c
index 5ffb2e0d69..f4dbe72090 100644
--- a/reftable/tree.c
+++ b/reftable/tree.c
@@ -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;
}
diff --git a/reftable/tree.h b/reftable/tree.h
index fbdd002e23..9604453b6d 100644
--- a/reftable/tree.h
+++ b/reftable/tree.h
@@ -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),
diff --git a/reftable/writer.c b/reftable/writer.c
index 791e246337..e180c10840 100644
--- a/reftable/writer.c
+++ b/reftable/writer.c
@@ -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;
}
diff --git a/t/unit-tests/t-reftable-tree.c b/t/unit-tests/t-reftable-tree.c
index 700479d34b..79b175a45a 100644
--- a/t/unit-tests/t-reftable-tree.c
+++ b/t/unit-tests/t-reftable-tree.c
@@ -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);
--
2.47.0.rc0.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v4 22/25] reftable: handle trivial allocation failures
2024-10-01 9:41 ` [PATCH v4 00/25] reftable: " Patrick Steinhardt
` (20 preceding siblings ...)
2024-10-01 9:42 ` [PATCH v4 21/25] reftable/tree: handle allocation failures Patrick Steinhardt
@ 2024-10-01 9:42 ` Patrick Steinhardt
2024-10-01 9:42 ` [PATCH v4 23/25] reftable: fix calls to free(3P) Patrick Steinhardt
` (3 subsequent siblings)
25 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-10-01 9:42 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
Handle trivial allocation failures in the reftable library and its unit
tests.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
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(-)
diff --git a/reftable/merged.c b/reftable/merged.c
index 8e202a8efd..514d6facf4 100644
--- a/reftable/merged.c
+++ b/reftable/merged.c
@@ -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;
diff --git a/reftable/reader.c b/reftable/reader.c
index 0179e4e73d..98e7aa2637 100644
--- a/reftable/reader.c
+++ b/reftable/reader.c
@@ -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;
diff --git a/reftable/stack.c b/reftable/stack.c
index 990784d9d2..7df28ab343 100644
--- a/reftable/stack.c
+++ b/reftable/stack.c
@@ -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);
diff --git a/reftable/writer.c b/reftable/writer.c
index e180c10840..550172e65c 100644
--- a/reftable/writer.c
+++ b/reftable/writer.c
@@ -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);
diff --git a/t/unit-tests/t-reftable-block.c b/t/unit-tests/t-reftable-block.c
index e52a612e85..d470060e8b 100644
--- a/t/unit-tests/t-reftable-block.c
+++ b/t/unit-tests/t-reftable-block.c
@@ -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,
diff --git a/t/unit-tests/t-reftable-merged.c b/t/unit-tests/t-reftable-merged.c
index 3d2848632d..3c84363e98 100644
--- a/t/unit-tests/t-reftable-merged.c
+++ b/t/unit-tests/t-reftable-merged.c
@@ -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);
diff --git a/t/unit-tests/t-reftable-readwrite.c b/t/unit-tests/t-reftable-readwrite.c
index acca927a2c..bfa069caff 100644
--- a/t/unit-tests/t-reftable-readwrite.c
+++ b/t/unit-tests/t-reftable-readwrite.c
@@ -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);
--
2.47.0.rc0.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v4 23/25] reftable: fix calls to free(3P)
2024-10-01 9:41 ` [PATCH v4 00/25] reftable: " Patrick Steinhardt
` (21 preceding siblings ...)
2024-10-01 9:42 ` [PATCH v4 22/25] reftable: handle trivial " Patrick Steinhardt
@ 2024-10-01 9:42 ` Patrick Steinhardt
2024-10-01 9:43 ` [PATCH v4 24/25] reftable: introduce `REFTABLE_FREE_AND_NULL()` Patrick Steinhardt
` (2 subsequent siblings)
25 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-10-01 9:42 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
There 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.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/stack.c | 2 +-
t/unit-tests/t-reftable-readwrite.c | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/reftable/stack.c b/reftable/stack.c
index 7df28ab343..b2babe8e3d 100644
--- a/reftable/stack.c
+++ b/reftable/stack.c
@@ -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:
diff --git a/t/unit-tests/t-reftable-readwrite.c b/t/unit-tests/t-reftable-readwrite.c
index bfa069caff..27ce84445e 100644
--- a/t/unit-tests/t-reftable-readwrite.c
+++ b/t/unit-tests/t-reftable-readwrite.c
@@ -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)
--
2.47.0.rc0.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v4 24/25] reftable: introduce `REFTABLE_FREE_AND_NULL()`
2024-10-01 9:41 ` [PATCH v4 00/25] reftable: " Patrick Steinhardt
` (22 preceding siblings ...)
2024-10-01 9:42 ` [PATCH v4 23/25] reftable: fix calls to free(3P) Patrick Steinhardt
@ 2024-10-01 9:43 ` Patrick Steinhardt
2024-10-01 9:43 ` [PATCH v4 25/25] reftable/basics: ban standard allocator functions Patrick Steinhardt
2024-10-01 17:52 ` [PATCH v4 00/25] reftable: handle allocation errors Junio C Hamano
25 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-10-01 9:43 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
We 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.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
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(-)
diff --git a/reftable/basics.h b/reftable/basics.h
index 69adeab2e4..7f0f20e50c 100644
--- a/reftable/basics.h
+++ b/reftable/basics.h
@@ -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;
diff --git a/reftable/block.c b/reftable/block.c
index bfa7dc61bf..8d41a2f99e 100644
--- a/reftable/block.c
+++ b/reftable/block.c
@@ -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. */
}
diff --git a/reftable/iter.c b/reftable/iter.c
index d319538f80..d926db653b 100644
--- a/reftable/iter.c
+++ b/reftable/iter.c
@@ -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,
diff --git a/reftable/pq.c b/reftable/pq.c
index 03b9912282..6ee1164dd3 100644
--- a/reftable/pq.c
+++ b/reftable/pq.c
@@ -68,6 +68,6 @@ int merged_iter_pqueue_add(struct merged_iter_pqueue *pq, const struct pq_entry
void 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));
}
diff --git a/reftable/reader.c b/reftable/reader.c
index 98e7aa2637..8d37253922 100644
--- a/reftable/reader.c
+++ b/reftable/reader.c
@@ -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);
}
diff --git a/reftable/record.c b/reftable/record.c
index 787e134c9a..30d563e16d 100644
--- a/reftable/record.c
+++ b/reftable/record.c
@@ -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;
diff --git a/reftable/stack.c b/reftable/stack.c
index b2babe8e3d..63976e5cea 100644
--- a/reftable/stack.c
+++ b/reftable/stack.c
@@ -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);
}
diff --git a/reftable/writer.c b/reftable/writer.c
index 550172e65c..b032a47dec 100644
--- a/reftable/writer.c
+++ b/reftable/writer.c
@@ -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;
}
diff --git a/t/unit-tests/t-reftable-stack.c b/t/unit-tests/t-reftable-stack.c
index 6e41a4cf7e..cc2db2cdef 100644
--- a/t/unit-tests/t-reftable-stack.c
+++ b/t/unit-tests/t-reftable-stack.c
@@ -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)
--
2.47.0.rc0.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v4 25/25] reftable/basics: ban standard allocator functions
2024-10-01 9:41 ` [PATCH v4 00/25] reftable: " Patrick Steinhardt
` (23 preceding siblings ...)
2024-10-01 9:43 ` [PATCH v4 24/25] reftable: introduce `REFTABLE_FREE_AND_NULL()` Patrick Steinhardt
@ 2024-10-01 9:43 ` Patrick Steinhardt
2024-10-01 22:50 ` Junio C Hamano
2024-10-01 17:52 ` [PATCH v4 00/25] reftable: handle allocation errors Junio C Hamano
25 siblings, 1 reply; 152+ messages in thread
From: Patrick Steinhardt @ 2024-10-01 9:43 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
The 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".
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/basics.c | 1 +
reftable/basics.h | 9 +++++++++
2 files changed, 10 insertions(+)
diff --git a/reftable/basics.c b/reftable/basics.c
index ea53cf102a..c8396dc525 100644
--- a/reftable/basics.c
+++ b/reftable/basics.c
@@ -6,6 +6,7 @@ license that can be found in the LICENSE file or at
https://developers.google.com/open-source/licenses/bsd
*/
+#define REFTABLE_ALLOW_BANNED_ALLOCATORS
#include "basics.h"
#include "reftable-basics.h"
diff --git a/reftable/basics.h b/reftable/basics.h
index 7f0f20e50c..428e8d1e57 100644
--- a/reftable/basics.h
+++ b/reftable/basics.h
@@ -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);
--
2.47.0.rc0.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* Re: [PATCH v4 00/25] reftable: handle allocation errors
2024-10-01 9:41 ` [PATCH v4 00/25] reftable: " Patrick Steinhardt
` (24 preceding siblings ...)
2024-10-01 9:43 ` [PATCH v4 25/25] reftable/basics: ban standard allocator functions Patrick Steinhardt
@ 2024-10-01 17:52 ` Junio C Hamano
2024-10-01 18:30 ` René Scharfe
25 siblings, 1 reply; 152+ messages in thread
From: Junio C Hamano @ 2024-10-01 17:52 UTC (permalink / raw)
To: Patrick Steinhardt; +Cc: git, Edward Thomson, René Scharfe
Patrick Steinhardt <ps@pks.im> writes:
> 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:
>
> - 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.
^ permalink raw reply [flat|nested] 152+ messages in thread
* Re: [PATCH v4 00/25] reftable: handle allocation errors
2024-10-01 17:52 ` [PATCH v4 00/25] reftable: handle allocation errors Junio C Hamano
@ 2024-10-01 18:30 ` René Scharfe
2024-10-01 19:25 ` Junio C Hamano
0 siblings, 1 reply; 152+ messages in thread
From: René Scharfe @ 2024-10-01 18:30 UTC (permalink / raw)
To: Junio C Hamano, Patrick Steinhardt; +Cc: git, Edward Thomson
Am 01.10.24 um 19:52 schrieb Junio C Hamano:
> 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é
^ permalink raw reply [flat|nested] 152+ messages in thread
* Re: [PATCH v4 00/25] reftable: handle allocation errors
2024-10-01 18:30 ` René Scharfe
@ 2024-10-01 19:25 ` Junio C Hamano
2024-10-02 4:29 ` Patrick Steinhardt
0 siblings, 1 reply; 152+ messages in thread
From: Junio C Hamano @ 2024-10-01 19:25 UTC (permalink / raw)
To: René Scharfe; +Cc: Patrick Steinhardt, git, Edward Thomson
René Scharfe <l.s.r@web.de> writes:
> Am 01.10.24 um 19:52 schrieb Junio C Hamano:
>> 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.
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.
^ permalink raw reply [flat|nested] 152+ messages in thread
* Re: [PATCH v4 25/25] reftable/basics: ban standard allocator functions
2024-10-01 9:43 ` [PATCH v4 25/25] reftable/basics: ban standard allocator functions Patrick Steinhardt
@ 2024-10-01 22:50 ` Junio C Hamano
2024-10-02 4:30 ` Patrick Steinhardt
0 siblings, 1 reply; 152+ messages in thread
From: Junio C Hamano @ 2024-10-01 22:50 UTC (permalink / raw)
To: Patrick Steinhardt; +Cc: git, Edward Thomson, René Scharfe
Patrick Steinhardt <ps@pks.im> writes:
> +#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.
> +# 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
^ permalink raw reply [flat|nested] 152+ messages in thread
* Re: [PATCH v4 00/25] reftable: handle allocation errors
2024-10-01 19:25 ` Junio C Hamano
@ 2024-10-02 4:29 ` Patrick Steinhardt
2024-10-02 18:04 ` Junio C Hamano
0 siblings, 1 reply; 152+ messages in thread
From: Patrick Steinhardt @ 2024-10-02 4:29 UTC (permalink / raw)
To: Junio C Hamano; +Cc: René Scharfe, git, Edward Thomson
On Tue, Oct 01, 2024 at 12:25:02PM -0700, Junio C Hamano wrote:
> René Scharfe <l.s.r@web.de> writes:
>
> > Am 01.10.24 um 19:52 schrieb Junio C Hamano:
> >> 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.
>
> 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.
`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
^ permalink raw reply [flat|nested] 152+ messages in thread
* Re: [PATCH v4 25/25] reftable/basics: ban standard allocator functions
2024-10-01 22:50 ` Junio C Hamano
@ 2024-10-02 4:30 ` Patrick Steinhardt
0 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-10-02 4:30 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git, Edward Thomson, René Scharfe
On Tue, Oct 01, 2024 at 03:50:42PM -0700, Junio C Hamano wrote:
> Patrick Steinhardt <ps@pks.im> writes:
>
> > +#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.
>
> > +# 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
^ permalink raw reply [flat|nested] 152+ messages in thread
* [PATCH v5 00/25] reftable: handle allocation errors
2024-09-16 12:28 [PATCH 00/22] reftable: handle allocation errors Patrick Steinhardt
` (24 preceding siblings ...)
2024-10-01 9:41 ` [PATCH v4 00/25] reftable: " Patrick Steinhardt
@ 2024-10-02 10:55 ` Patrick Steinhardt
2024-10-02 10:55 ` [PATCH v5 01/25] reftable/error: introduce out-of-memory error code Patrick Steinhardt
` (25 more replies)
25 siblings, 26 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-10-02 10:55 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
Hi,
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.
+
Signed-off-by: Patrick Steinhardt <ps@pks.im>
## 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
+
--
2.47.0.rc0.dirty
^ permalink raw reply [flat|nested] 152+ messages in thread
* [PATCH v5 01/25] reftable/error: introduce out-of-memory error code
2024-10-02 10:55 ` [PATCH v5 " Patrick Steinhardt
@ 2024-10-02 10:55 ` Patrick Steinhardt
2024-10-02 10:55 ` [PATCH v5 02/25] reftable/basics: merge "publicbasics" into "basics" Patrick Steinhardt
` (24 subsequent siblings)
25 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-10-02 10:55 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
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.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/error.c | 2 ++
reftable/reftable-error.h | 3 +++
2 files changed, 5 insertions(+)
diff --git a/reftable/error.c b/reftable/error.c
index a25f28a43e..660d029617 100644
--- a/reftable/error.c
+++ b/reftable/error.c
@@ -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:
diff --git a/reftable/reftable-error.h b/reftable/reftable-error.h
index 6368cd9ed9..f404826562 100644
--- a/reftable/reftable-error.h
+++ b/reftable/reftable-error.h
@@ -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
--
2.47.0.rc0.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v5 02/25] reftable/basics: merge "publicbasics" into "basics"
2024-10-02 10:55 ` [PATCH v5 " Patrick Steinhardt
2024-10-02 10:55 ` [PATCH v5 01/25] reftable/error: introduce out-of-memory error code Patrick Steinhardt
@ 2024-10-02 10:55 ` Patrick Steinhardt
2024-10-02 10:55 ` [PATCH v5 03/25] reftable: introduce `reftable_strdup()` Patrick Steinhardt
` (23 subsequent siblings)
25 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-10-02 10:55 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
The 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.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
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
diff --git a/Makefile b/Makefile
index e3abf0ba83..39b10923d4 100644
--- a/Makefile
+++ b/Makefile
@@ -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
diff --git a/reftable/basics.c b/reftable/basics.c
index 0058619ca6..cf072935c8 100644
--- a/reftable/basics.c
+++ b/reftable/basics.c
@@ -7,6 +7,49 @@ license that can be found in the LICENSE file or at
*/
#include "basics.h"
+#include "reftable-basics.h"
+
+static void *(*reftable_malloc_ptr)(size_t sz);
+static void *(*reftable_realloc_ptr)(void *, size_t);
+static void (*reftable_free_ptr)(void *);
+
+void *reftable_malloc(size_t sz)
+{
+ if (reftable_malloc_ptr)
+ return (*reftable_malloc_ptr)(sz);
+ return malloc(sz);
+}
+
+void *reftable_realloc(void *p, size_t sz)
+{
+ if (reftable_realloc_ptr)
+ return (*reftable_realloc_ptr)(p, sz);
+ return realloc(p, sz);
+}
+
+void reftable_free(void *p)
+{
+ if (reftable_free_ptr)
+ reftable_free_ptr(p);
+ else
+ free(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);
+ return p;
+}
+
+void reftable_set_alloc(void *(*malloc)(size_t),
+ void *(*realloc)(void *, size_t), void (*free)(void *))
+{
+ reftable_malloc_ptr = malloc;
+ reftable_realloc_ptr = realloc;
+ reftable_free_ptr = free;
+}
void put_be24(uint8_t *out, uint32_t i)
{
@@ -121,3 +164,15 @@ int common_prefix_size(struct strbuf *a, struct strbuf *b)
return p;
}
+
+int hash_size(uint32_t id)
+{
+ switch (id) {
+ case 0:
+ case GIT_SHA1_FORMAT_ID:
+ return GIT_SHA1_RAWSZ;
+ case GIT_SHA256_FORMAT_ID:
+ return GIT_SHA256_RAWSZ;
+ }
+ abort();
+}
diff --git a/reftable/basics.h b/reftable/basics.h
index c8fec68d4e..4e2e76014a 100644
--- a/reftable/basics.h
+++ b/reftable/basics.h
@@ -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
diff --git a/reftable/publicbasics.c b/reftable/publicbasics.c
deleted file mode 100644
index 44b84a125e..0000000000
--- a/reftable/publicbasics.c
+++ /dev/null
@@ -1,66 +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
-*/
-
-#include "system.h"
-#include "reftable-malloc.h"
-
-#include "basics.h"
-
-static void *(*reftable_malloc_ptr)(size_t sz);
-static void *(*reftable_realloc_ptr)(void *, size_t);
-static void (*reftable_free_ptr)(void *);
-
-void *reftable_malloc(size_t sz)
-{
- if (reftable_malloc_ptr)
- return (*reftable_malloc_ptr)(sz);
- return malloc(sz);
-}
-
-void *reftable_realloc(void *p, size_t sz)
-{
- if (reftable_realloc_ptr)
- return (*reftable_realloc_ptr)(p, sz);
- return realloc(p, sz);
-}
-
-void reftable_free(void *p)
-{
- if (reftable_free_ptr)
- reftable_free_ptr(p);
- else
- free(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);
- return p;
-}
-
-void reftable_set_alloc(void *(*malloc)(size_t),
- void *(*realloc)(void *, size_t), void (*free)(void *))
-{
- reftable_malloc_ptr = malloc;
- reftable_realloc_ptr = realloc;
- reftable_free_ptr = free;
-}
-
-int hash_size(uint32_t id)
-{
- switch (id) {
- case 0:
- case GIT_SHA1_FORMAT_ID:
- return GIT_SHA1_RAWSZ;
- case GIT_SHA256_FORMAT_ID:
- return GIT_SHA256_RAWSZ;
- }
- abort();
-}
diff --git a/reftable/reftable-basics.h b/reftable/reftable-basics.h
new file mode 100644
index 0000000000..6e8e636b71
--- /dev/null
+++ b/reftable/reftable-basics.h
@@ -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
diff --git a/reftable/reftable-malloc.h b/reftable/reftable-malloc.h
deleted file mode 100644
index 5f2185f1f3..0000000000
--- a/reftable/reftable-malloc.h
+++ /dev/null
@@ -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
--
2.47.0.rc0.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v5 03/25] reftable: introduce `reftable_strdup()`
2024-10-02 10:55 ` [PATCH v5 " Patrick Steinhardt
2024-10-02 10:55 ` [PATCH v5 01/25] reftable/error: introduce out-of-memory error code Patrick Steinhardt
2024-10-02 10:55 ` [PATCH v5 02/25] reftable/basics: merge "publicbasics" into "basics" Patrick Steinhardt
@ 2024-10-02 10:55 ` Patrick Steinhardt
2024-10-02 10:55 ` [PATCH v5 04/25] reftable/basics: handle allocation failures in `reftable_calloc()` Patrick Steinhardt
` (22 subsequent siblings)
25 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-10-02 10:55 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
The 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.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/basics.c | 10 ++++++++++
reftable/basics.h | 1 +
2 files changed, 11 insertions(+)
diff --git a/reftable/basics.c b/reftable/basics.c
index cf072935c8..4adc98cf5d 100644
--- a/reftable/basics.c
+++ b/reftable/basics.c
@@ -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 *))
{
diff --git a/reftable/basics.h b/reftable/basics.h
index 4e2e76014a..f107e14860 100644
--- a/reftable/basics.h
+++ b/reftable/basics.h
@@ -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)))
--
2.47.0.rc0.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v5 04/25] reftable/basics: handle allocation failures in `reftable_calloc()`
2024-10-02 10:55 ` [PATCH v5 " Patrick Steinhardt
` (2 preceding siblings ...)
2024-10-02 10:55 ` [PATCH v5 03/25] reftable: introduce `reftable_strdup()` Patrick Steinhardt
@ 2024-10-02 10:55 ` Patrick Steinhardt
2024-10-02 10:55 ` [PATCH v5 05/25] reftable/basics: handle allocation failures in `parse_names()` Patrick Steinhardt
` (21 subsequent siblings)
25 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-10-02 10:55 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
Handle 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.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/basics.c | 13 ++++++++++---
1 file changed, 10 insertions(+), 3 deletions(-)
diff --git a/reftable/basics.c b/reftable/basics.c
index 4adc98cf5d..3350bbffa2 100644
--- a/reftable/basics.c
+++ b/reftable/basics.c
@@ -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;
}
--
2.47.0.rc0.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v5 05/25] reftable/basics: handle allocation failures in `parse_names()`
2024-10-02 10:55 ` [PATCH v5 " Patrick Steinhardt
` (3 preceding siblings ...)
2024-10-02 10:55 ` [PATCH v5 04/25] reftable/basics: handle allocation failures in `reftable_calloc()` Patrick Steinhardt
@ 2024-10-02 10:55 ` Patrick Steinhardt
2024-10-02 22:07 ` Eric Sunshine
2024-10-02 10:55 ` [PATCH v5 06/25] reftable/record: handle allocation failures on copy Patrick Steinhardt
` (20 subsequent siblings)
25 siblings, 1 reply; 152+ messages in thread
From: Patrick Steinhardt @ 2024-10-02 10:55 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
Handle 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.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
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(-)
diff --git a/reftable/basics.c b/reftable/basics.c
index 3350bbffa2..ea53cf102a 100644
--- a/reftable/basics.c
+++ b/reftable/basics.c
@@ -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)
diff --git a/reftable/basics.h b/reftable/basics.h
index f107e14860..69adeab2e4 100644
--- a/reftable/basics.h
+++ b/reftable/basics.h
@@ -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);
diff --git a/reftable/stack.c b/reftable/stack.c
index ce0a35216b..498fae846d 100644
--- a/reftable/stack.c
+++ b/reftable/stack.c
@@ -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);
diff --git a/t/unit-tests/t-reftable-basics.c b/t/unit-tests/t-reftable-basics.c
index e5556ebf52..1fa77b6faf 100644
--- a/t/unit-tests/t-reftable-basics.c
+++ b/t/unit-tests/t-reftable-basics.c
@@ -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");
--
2.47.0.rc0.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v5 06/25] reftable/record: handle allocation failures on copy
2024-10-02 10:55 ` [PATCH v5 " Patrick Steinhardt
` (4 preceding siblings ...)
2024-10-02 10:55 ` [PATCH v5 05/25] reftable/basics: handle allocation failures in `parse_names()` Patrick Steinhardt
@ 2024-10-02 10:55 ` Patrick Steinhardt
2024-10-02 10:55 ` [PATCH v5 07/25] reftable/record: handle allocation failures when decoding records Patrick Steinhardt
` (19 subsequent siblings)
25 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-10-02 10:55 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
Handle allocation failures when copying records. While at it, convert
from `xstrdup()` to `reftable_strdup()`. Adapt callsites to check for
error codes.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/record.c | 84 +++++++++++++++++++++++++++++++++--------------
reftable/record.h | 6 ++--
2 files changed, 63 insertions(+), 27 deletions(-)
diff --git a/reftable/record.c b/reftable/record.c
index 6b5a075b92..60fd33c9c9 100644
--- a/reftable/record.c
+++ b/reftable/record.c
@@ -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)
diff --git a/reftable/record.h b/reftable/record.h
index 5003bacdb0..0f53ba5443 100644
--- a/reftable/record.h
+++ b/reftable/record.h
@@ -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);
--
2.47.0.rc0.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v5 07/25] reftable/record: handle allocation failures when decoding records
2024-10-02 10:55 ` [PATCH v5 " Patrick Steinhardt
` (5 preceding siblings ...)
2024-10-02 10:55 ` [PATCH v5 06/25] reftable/record: handle allocation failures on copy Patrick Steinhardt
@ 2024-10-02 10:55 ` Patrick Steinhardt
2024-10-02 10:55 ` [PATCH v5 08/25] reftable/writer: handle allocation failures in `writer_index_hash()` Patrick Steinhardt
` (18 subsequent siblings)
25 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-10-02 10:55 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
Handle allocation failures when decoding records. While at it, fix some
error codes to be `REFTABLE_FORMAT_ERROR`.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/record.c | 80 +++++++++++++++++++++++++++++++++++++----------
1 file changed, 63 insertions(+), 17 deletions(-)
diff --git a/reftable/record.c b/reftable/record.c
index 60fd33c9c9..787e134c9a 100644
--- a/reftable/record.c
+++ b/reftable/record.c
@@ -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)
--
2.47.0.rc0.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v5 08/25] reftable/writer: handle allocation failures in `writer_index_hash()`
2024-10-02 10:55 ` [PATCH v5 " Patrick Steinhardt
` (6 preceding siblings ...)
2024-10-02 10:55 ` [PATCH v5 07/25] reftable/record: handle allocation failures when decoding records Patrick Steinhardt
@ 2024-10-02 10:55 ` Patrick Steinhardt
2024-10-02 10:55 ` [PATCH v5 09/25] reftable/writer: handle allocation failures in `reftable_new_writer()` Patrick Steinhardt
` (17 subsequent siblings)
25 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-10-02 10:55 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
Handle allocation errors in `writer_index_hash()`. Adjust its only
caller in `reftable_writer_add_ref()` accordingly.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/writer.c | 61 +++++++++++++++++++++++++++++------------------
1 file changed, 38 insertions(+), 23 deletions(-)
diff --git a/reftable/writer.c b/reftable/writer.c
index 9d5e6072bc..ed61aaf59c 100644
--- a/reftable/writer.c
+++ b/reftable/writer.c
@@ -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,
--
2.47.0.rc0.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v5 09/25] reftable/writer: handle allocation failures in `reftable_new_writer()`
2024-10-02 10:55 ` [PATCH v5 " Patrick Steinhardt
` (7 preceding siblings ...)
2024-10-02 10:55 ` [PATCH v5 08/25] reftable/writer: handle allocation failures in `writer_index_hash()` Patrick Steinhardt
@ 2024-10-02 10:55 ` Patrick Steinhardt
2024-10-02 10:55 ` [PATCH v5 10/25] reftable/merged: handle allocation failures in `merged_table_init_iter()` Patrick Steinhardt
` (16 subsequent siblings)
25 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-10-02 10:55 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
Handle 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.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
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(-)
diff --git a/reftable/reftable-writer.h b/reftable/reftable-writer.h
index 189b1f4144..43623dc7c3 100644
--- a/reftable/reftable-writer.h
+++ b/reftable/reftable-writer.h
@@ -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
diff --git a/reftable/stack.c b/reftable/stack.c
index 498fae846d..ea21ca6e5f 100644
--- a/reftable/stack.c
+++ b/reftable/stack.c
@@ -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;
diff --git a/reftable/writer.c b/reftable/writer.c
index ed61aaf59c..8ab2e916d3 100644
--- a/reftable/writer.c
+++ b/reftable/writer.c
@@ -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,
diff --git a/t/unit-tests/lib-reftable.c b/t/unit-tests/lib-reftable.c
index ab1fa44a28..54c26c43e7 100644
--- a/t/unit-tests/lib-reftable.c
+++ b/t/unit-tests/lib-reftable.c
@@ -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,
--
2.47.0.rc0.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v5 10/25] reftable/merged: handle allocation failures in `merged_table_init_iter()`
2024-10-02 10:55 ` [PATCH v5 " Patrick Steinhardt
` (8 preceding siblings ...)
2024-10-02 10:55 ` [PATCH v5 09/25] reftable/writer: handle allocation failures in `reftable_new_writer()` Patrick Steinhardt
@ 2024-10-02 10:55 ` Patrick Steinhardt
2024-10-02 10:55 ` [PATCH v5 11/25] reftable/reader: handle allocation failures for unindexed reader Patrick Steinhardt
` (15 subsequent siblings)
25 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-10-02 10:55 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
Handle 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.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
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(-)
diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c
index 5c241097a4..9c08c46c85 100644
--- a/refs/reftable-backend.c
+++ b/refs/reftable-backend.c
@@ -1299,7 +1299,9 @@ static int write_transaction_table(struct reftable_writer *writer, void *cb_data
struct 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 reftabl
if (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_da
reftable_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)
diff --git a/reftable/merged.c b/reftable/merged.c
index 128a810c55..de4f81abaf 100644
--- a/reftable/merged.c
+++ b/reftable/merged.c
@@ -30,22 +30,6 @@ struct merged_iter {
ssize_t advance_index;
};
-static void merged_iter_init(struct merged_iter *mi,
- struct reftable_merged_table *mt,
- uint8_t typ)
-{
- memset(mi, 0, sizeof(*mi));
- mi->advance_index = -1;
- mi->suppress_deletions = mt->suppress_deletions;
-
- REFTABLE_CALLOC_ARRAY(mi->subiters, mt->readers_len);
- for (size_t i = 0; i < mt->readers_len; i++) {
- reftable_record_init(&mi->subiters[i].rec, typ);
- reader_init_iter(mt->readers[i], &mi->subiters[i].iter, typ);
- }
- mi->subiters_len = mt->readers_len;
-}
-
static void merged_iter_close(void *p)
{
struct merged_iter *mi = p;
@@ -244,25 +228,61 @@ reftable_merged_table_min_update_index(struct reftable_merged_table *mt)
return mt->min;
}
-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)
{
- struct merged_iter *mi = reftable_malloc(sizeof(*mi));
- merged_iter_init(mi, mt, typ);
+ struct merged_subiter *subiters;
+ struct merged_iter *mi = NULL;
+ int ret;
+
+ REFTABLE_CALLOC_ARRAY(subiters, mt->readers_len);
+ if (!subiters) {
+ ret = REFTABLE_OUT_OF_MEMORY_ERROR;
+ goto out;
+ }
+
+ 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);
+ }
+
+ REFTABLE_CALLOC_ARRAY(mi, 1);
+ if (!mi) {
+ ret = REFTABLE_OUT_OF_MEMORY_ERROR;
+ goto out;
+ }
+ mi->advance_index = -1;
+ mi->suppress_deletions = mt->suppress_deletions;
+ mi->subiters = subiters;
+ mi->subiters_len = mt->readers_len;
+
iterator_from_merged_iter(it, mi);
+ ret = 0;
+
+out:
+ if (ret < 0) {
+ for (size_t i = 0; subiters && i < mt->readers_len; i++) {
+ reftable_iterator_destroy(&subiters[i].iter);
+ reftable_record_release(&subiters[i].rec);
+ }
+ reftable_free(subiters);
+ reftable_free(mi);
+ }
+
+ return ret;
}
-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)
{
- merged_table_init_iter(mt, it, BLOCK_TYPE_REF);
+ return merged_table_init_iter(mt, it, BLOCK_TYPE_REF);
}
-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)
{
- merged_table_init_iter(mt, it, BLOCK_TYPE_LOG);
+ return merged_table_init_iter(mt, it, BLOCK_TYPE_LOG);
}
uint32_t reftable_merged_table_hash_id(struct reftable_merged_table *mt)
diff --git a/reftable/merged.h b/reftable/merged.h
index de5fd33f01..89bd0c4b35 100644
--- a/reftable/merged.h
+++ b/reftable/merged.h
@@ -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
diff --git a/reftable/reftable-merged.h b/reftable/reftable-merged.h
index 16d19f8df2..a970d5dd89 100644
--- a/reftable/reftable-merged.h
+++ b/reftable/reftable-merged.h
@@ -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
diff --git a/reftable/reftable-stack.h b/reftable/reftable-stack.h
index f4f8cabc7f..e958f911b4 100644
--- a/reftable/reftable-stack.h
+++ b/reftable/reftable-stack.h
@@ -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.
diff --git a/reftable/stack.c b/reftable/stack.c
index ea21ca6e5f..bb4d230918 100644
--- a/reftable/stack.c
+++ b/reftable/stack.c
@@ -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;
diff --git a/t/helper/test-reftable.c b/t/helper/test-reftable.c
index 29d4e9a755..5c8849d115 100644
--- a/t/helper/test-reftable.c
+++ b/t/helper/test-reftable.c
@@ -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;
diff --git a/t/unit-tests/t-reftable-merged.c b/t/unit-tests/t-reftable-merged.c
index 19e54bdfb8..3d2848632d 100644
--- a/t/unit-tests/t-reftable-merged.c
+++ b/t/unit-tests/t-reftable-merged.c
@@ -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]);
diff --git a/t/unit-tests/t-reftable-stack.c b/t/unit-tests/t-reftable-stack.c
index 65e513d5ec..6e41a4cf7e 100644
--- a/t/unit-tests/t-reftable-stack.c
+++ b/t/unit-tests/t-reftable-stack.c
@@ -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 };
--
2.47.0.rc0.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v5 11/25] reftable/reader: handle allocation failures for unindexed reader
2024-10-02 10:55 ` [PATCH v5 " Patrick Steinhardt
` (9 preceding siblings ...)
2024-10-02 10:55 ` [PATCH v5 10/25] reftable/merged: handle allocation failures in `merged_table_init_iter()` Patrick Steinhardt
@ 2024-10-02 10:55 ` Patrick Steinhardt
2024-10-02 10:55 ` [PATCH v5 12/25] reftable/reader: handle allocation failures in `reader_init_iter()` Patrick Steinhardt
` (14 subsequent siblings)
25 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-10-02 10:55 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
Handle allocation failures when creating unindexed readers.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/reader.c | 23 +++++++++++++++++------
1 file changed, 17 insertions(+), 6 deletions(-)
diff --git a/reftable/reader.c b/reftable/reader.c
index 6494ce2e32..485ee085da 100644
--- a/reftable/reader.c
+++ b/reftable/reader.c
@@ -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,
--
2.47.0.rc0.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v5 12/25] reftable/reader: handle allocation failures in `reader_init_iter()`
2024-10-02 10:55 ` [PATCH v5 " Patrick Steinhardt
` (10 preceding siblings ...)
2024-10-02 10:55 ` [PATCH v5 11/25] reftable/reader: handle allocation failures for unindexed reader Patrick Steinhardt
@ 2024-10-02 10:55 ` Patrick Steinhardt
2024-10-02 10:56 ` [PATCH v5 13/25] reftable/stack: handle allocation failures on reload Patrick Steinhardt
` (13 subsequent siblings)
25 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-10-02 10:55 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
Handle 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.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
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(-)
diff --git a/reftable/merged.c b/reftable/merged.c
index de4f81abaf..69790c345c 100644
--- a/reftable/merged.c
+++ b/reftable/merged.c
@@ -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);
diff --git a/reftable/reader.c b/reftable/reader.c
index 485ee085da..f696e992df 100644
--- a/reftable/reader.c
+++ b/reftable/reader.c
@@ -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;
diff --git a/reftable/reader.h b/reftable/reader.h
index 3710ee09b4..02d10c5d37 100644
--- a/reftable/reader.h
+++ b/reftable/reader.h
@@ -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,
diff --git a/reftable/reftable-reader.h b/reftable/reftable-reader.h
index a600452b56..6a2d0b693f 100644
--- a/reftable/reftable-reader.h
+++ b/reftable/reftable-reader.h
@@ -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);
diff --git a/t/unit-tests/t-reftable-readwrite.c b/t/unit-tests/t-reftable-readwrite.c
index e1b235a5f1..acca927a2c 100644
--- a/t/unit-tests/t-reftable-readwrite.c
+++ b/t/unit-tests/t-reftable-readwrite.c
@@ -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);
--
2.47.0.rc0.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v5 13/25] reftable/stack: handle allocation failures on reload
2024-10-02 10:55 ` [PATCH v5 " Patrick Steinhardt
` (11 preceding siblings ...)
2024-10-02 10:55 ` [PATCH v5 12/25] reftable/reader: handle allocation failures in `reader_init_iter()` Patrick Steinhardt
@ 2024-10-02 10:56 ` Patrick Steinhardt
2024-10-02 10:56 ` [PATCH v5 14/25] reftable/stack: handle allocation failures in `reftable_new_stack()` Patrick Steinhardt
` (12 subsequent siblings)
25 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-10-02 10:56 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
Handle allocation failures in `reftable_stack_reload_once()`.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/stack.c | 34 +++++++++++++++++++++++++---------
1 file changed, 25 insertions(+), 9 deletions(-)
diff --git a/reftable/stack.c b/reftable/stack.c
index bb4d230918..060b2c1b90 100644
--- a/reftable/stack.c
+++ b/reftable/stack.c
@@ -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;
--
2.47.0.rc0.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v5 14/25] reftable/stack: handle allocation failures in `reftable_new_stack()`
2024-10-02 10:55 ` [PATCH v5 " Patrick Steinhardt
` (12 preceding siblings ...)
2024-10-02 10:56 ` [PATCH v5 13/25] reftable/stack: handle allocation failures on reload Patrick Steinhardt
@ 2024-10-02 10:56 ` Patrick Steinhardt
2024-10-02 10:56 ` [PATCH v5 15/25] reftable/stack: handle allocation failures in `stack_compact_range()` Patrick Steinhardt
` (11 subsequent siblings)
25 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-10-02 10:56 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
Handle allocation failures in `reftable_new_stack()`.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/stack.c | 34 ++++++++++++++++++++++++++--------
1 file changed, 26 insertions(+), 8 deletions(-)
diff --git a/reftable/stack.c b/reftable/stack.c
index 060b2c1b90..1b77c9d014 100644
--- a/reftable/stack.c
+++ b/reftable/stack.c
@@ -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;
--
2.47.0.rc0.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v5 15/25] reftable/stack: handle allocation failures in `stack_compact_range()`
2024-10-02 10:55 ` [PATCH v5 " Patrick Steinhardt
` (13 preceding siblings ...)
2024-10-02 10:56 ` [PATCH v5 14/25] reftable/stack: handle allocation failures in `reftable_new_stack()` Patrick Steinhardt
@ 2024-10-02 10:56 ` Patrick Steinhardt
2024-10-02 10:56 ` [PATCH v5 16/25] reftable/stack: handle allocation failures in auto compaction Patrick Steinhardt
` (10 subsequent siblings)
25 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-10-02 10:56 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
Handle allocation failures in `stack_compact_range()`.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/stack.c | 19 +++++++++++++++++--
1 file changed, 17 insertions(+), 2 deletions(-)
diff --git a/reftable/stack.c b/reftable/stack.c
index 1b77c9d014..2e6dd513d7 100644
--- a/reftable/stack.c
+++ b/reftable/stack.c
@@ -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;
}
--
2.47.0.rc0.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v5 16/25] reftable/stack: handle allocation failures in auto compaction
2024-10-02 10:55 ` [PATCH v5 " Patrick Steinhardt
` (14 preceding siblings ...)
2024-10-02 10:56 ` [PATCH v5 15/25] reftable/stack: handle allocation failures in `stack_compact_range()` Patrick Steinhardt
@ 2024-10-02 10:56 ` Patrick Steinhardt
2024-10-02 10:56 ` [PATCH v5 17/25] reftable/iter: handle allocation failures when creating indexed table iter Patrick Steinhardt
` (9 subsequent siblings)
25 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-10-02 10:56 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
Handle allocation failures in `reftable_stack_auto_compact()`.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/stack.c | 16 ++++++++++++----
1 file changed, 12 insertions(+), 4 deletions(-)
diff --git a/reftable/stack.c b/reftable/stack.c
index 2e6dd513d7..990784d9d2 100644
--- a/reftable/stack.c
+++ b/reftable/stack.c
@@ -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);
--
2.47.0.rc0.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v5 17/25] reftable/iter: handle allocation failures when creating indexed table iter
2024-10-02 10:55 ` [PATCH v5 " Patrick Steinhardt
` (15 preceding siblings ...)
2024-10-02 10:56 ` [PATCH v5 16/25] reftable/stack: handle allocation failures in auto compaction Patrick Steinhardt
@ 2024-10-02 10:56 ` Patrick Steinhardt
2024-10-02 10:56 ` [PATCH v5 18/25] reftable/blocksource: handle allocation failures Patrick Steinhardt
` (8 subsequent siblings)
25 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-10-02 10:56 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
Handle allocation failures in `new_indexed_table_ref_iter()`. While at
it, rename the function to match our coding style.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/iter.c | 20 ++++++++++++++++----
reftable/iter.h | 2 +-
reftable/reader.c | 7 ++++++-
3 files changed, 23 insertions(+), 6 deletions(-)
diff --git a/reftable/iter.c b/reftable/iter.c
index 416a9f6996..d319538f80 100644
--- a/reftable/iter.c
+++ b/reftable/iter.c
@@ -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;
}
diff --git a/reftable/iter.h b/reftable/iter.h
index befc4597df..b3225bc7ad 100644
--- a/reftable/iter.h
+++ b/reftable/iter.h
@@ -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);
diff --git a/reftable/reader.c b/reftable/reader.c
index f696e992df..0179e4e73d 100644
--- a/reftable/reader.c
+++ b/reftable/reader.c
@@ -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)
--
2.47.0.rc0.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v5 18/25] reftable/blocksource: handle allocation failures
2024-10-02 10:55 ` [PATCH v5 " Patrick Steinhardt
` (16 preceding siblings ...)
2024-10-02 10:56 ` [PATCH v5 17/25] reftable/iter: handle allocation failures when creating indexed table iter Patrick Steinhardt
@ 2024-10-02 10:56 ` Patrick Steinhardt
2024-10-02 10:56 ` [PATCH v5 19/25] reftable/block: " Patrick Steinhardt
` (7 subsequent siblings)
25 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-10-02 10:56 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
Handle allocation failures in the blocksource code.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/blocksource.c | 25 ++++++++++++++++++++-----
1 file changed, 20 insertions(+), 5 deletions(-)
diff --git a/reftable/blocksource.c b/reftable/blocksource.c
index e93cac9bb6..a2a6a196d5 100644
--- a/reftable/blocksource.c
+++ b/reftable/blocksource.c
@@ -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;
}
--
2.47.0.rc0.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v5 19/25] reftable/block: handle allocation failures
2024-10-02 10:55 ` [PATCH v5 " Patrick Steinhardt
` (17 preceding siblings ...)
2024-10-02 10:56 ` [PATCH v5 18/25] reftable/blocksource: handle allocation failures Patrick Steinhardt
@ 2024-10-02 10:56 ` Patrick Steinhardt
2024-10-02 10:56 ` [PATCH v5 20/25] reftable/pq: handle allocation failures when adding entries Patrick Steinhardt
` (6 subsequent siblings)
25 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-10-02 10:56 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
Handle 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.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
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(-)
diff --git a/reftable/block.c b/reftable/block.c
index 00030eee06..bfa7dc61bf 100644
--- a/reftable/block.c
+++ b/reftable/block.c
@@ -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);
diff --git a/reftable/block.h b/reftable/block.h
index 1c8f25ee6e..18d7ea0337 100644
--- a/reftable/block.h
+++ b/reftable/block.h
@@ -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);
diff --git a/reftable/writer.c b/reftable/writer.c
index 8ab2e916d3..791e246337 100644
--- a/reftable/writer.c
+++ b/reftable/writer.c
@@ -102,19 +102,24 @@ static int writer_write_header(struct reftable_writer *w, uint8_t *dest)
return header_size(writer_version(w));
}
-static void writer_reinit_block_writer(struct reftable_writer *w, uint8_t typ)
+static int writer_reinit_block_writer(struct reftable_writer *w, uint8_t typ)
{
- int block_start = 0;
- if (w->next == 0) {
+ int block_start = 0, ret;
+
+ if (w->next == 0)
block_start = header_size(writer_version(w));
- }
strbuf_reset(&w->last_key);
- block_writer_init(&w->block_writer_data, typ, w->block,
- w->opts.block_size, block_start,
- hash_size(w->opts.hash_id));
+ ret = block_writer_init(&w->block_writer_data, typ, w->block,
+ w->opts.block_size, block_start,
+ hash_size(w->opts.hash_id));
+ if (ret < 0)
+ return ret;
+
w->block_writer = &w->block_writer_data;
w->block_writer->restart_interval = w->opts.restart_interval;
+
+ return 0;
}
int reftable_writer_new(struct reftable_writer **out,
@@ -247,8 +252,11 @@ static int writer_add_record(struct reftable_writer *w,
strbuf_reset(&w->last_key);
strbuf_addbuf(&w->last_key, &key);
- if (!w->block_writer)
- writer_reinit_block_writer(w, reftable_record_type(rec));
+ if (!w->block_writer) {
+ err = writer_reinit_block_writer(w, reftable_record_type(rec));
+ if (err < 0)
+ goto done;
+ }
if (block_writer_type(w->block_writer) != reftable_record_type(rec))
BUG("record of type %d added to writer of type %d",
@@ -271,7 +279,9 @@ static int writer_add_record(struct reftable_writer *w,
err = writer_flush_block(w);
if (err < 0)
goto done;
- writer_reinit_block_writer(w, reftable_record_type(rec));
+ err = writer_reinit_block_writer(w, reftable_record_type(rec));
+ if (err < 0)
+ goto done;
/*
* Try to add the record to the writer again. If this still fails then
@@ -461,7 +471,9 @@ static int writer_finish_section(struct reftable_writer *w)
max_level++;
index_start = w->next;
- writer_reinit_block_writer(w, BLOCK_TYPE_INDEX);
+ err = writer_reinit_block_writer(w, BLOCK_TYPE_INDEX);
+ if (err < 0)
+ return err;
idx = w->index;
idx_len = w->index_len;
@@ -555,7 +567,10 @@ static void write_object_record(void *void_arg, void *key)
if (arg->err < 0)
goto done;
- writer_reinit_block_writer(arg->w, BLOCK_TYPE_OBJ);
+ arg->err = writer_reinit_block_writer(arg->w, BLOCK_TYPE_OBJ);
+ if (arg->err < 0)
+ goto done;
+
arg->err = block_writer_add(arg->w->block_writer, &rec);
if (arg->err == 0)
goto done;
@@ -584,16 +599,18 @@ static int writer_dump_object_index(struct reftable_writer *w)
struct common_prefix_arg common = {
.max = 1, /* obj_id_len should be >= 2. */
};
- if (w->obj_index_tree) {
+ int err;
+
+ if (w->obj_index_tree)
infix_walk(w->obj_index_tree, &update_common, &common);
- }
w->stats.object_id_len = common.max + 1;
- writer_reinit_block_writer(w, BLOCK_TYPE_OBJ);
+ err = writer_reinit_block_writer(w, BLOCK_TYPE_OBJ);
+ if (err < 0)
+ return err;
- if (w->obj_index_tree) {
+ if (w->obj_index_tree)
infix_walk(w->obj_index_tree, &write_object_record, &closure);
- }
if (closure.err < 0)
return closure.err;
diff --git a/t/unit-tests/t-reftable-block.c b/t/unit-tests/t-reftable-block.c
index f1a49485e2..e52a612e85 100644
--- a/t/unit-tests/t-reftable-block.c
+++ b/t/unit-tests/t-reftable-block.c
@@ -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);
--
2.47.0.rc0.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v5 20/25] reftable/pq: handle allocation failures when adding entries
2024-10-02 10:55 ` [PATCH v5 " Patrick Steinhardt
` (18 preceding siblings ...)
2024-10-02 10:56 ` [PATCH v5 19/25] reftable/block: " Patrick Steinhardt
@ 2024-10-02 10:56 ` Patrick Steinhardt
2024-10-02 10:56 ` [PATCH v5 21/25] reftable/tree: handle allocation failures Patrick Steinhardt
` (5 subsequent siblings)
25 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-10-02 10:56 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
Handle allocation failures when adding entries to the pqueue. Adapt its
only caller accordingly.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/merged.c | 5 ++++-
reftable/pq.c | 7 ++++++-
reftable/pq.h | 2 +-
3 files changed, 11 insertions(+), 3 deletions(-)
diff --git a/reftable/merged.c b/reftable/merged.c
index 69790c345c..8e202a8efd 100644
--- a/reftable/merged.c
+++ b/reftable/merged.c
@@ -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;
}
diff --git a/reftable/pq.c b/reftable/pq.c
index 2b5b7d1c0e..03b9912282 100644
--- a/reftable/pq.c
+++ b/reftable/pq.c
@@ -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_entry
SWAP(pq->heap[j], pq->heap[i]);
i = j;
}
+
+ return 0;
}
void merged_iter_pqueue_release(struct merged_iter_pqueue *pq)
diff --git a/reftable/pq.h b/reftable/pq.h
index 707bd26767..83c062eeca 100644
--- a/reftable/pq.h
+++ b/reftable/pq.h
@@ -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);
--
2.47.0.rc0.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v5 21/25] reftable/tree: handle allocation failures
2024-10-02 10:55 ` [PATCH v5 " Patrick Steinhardt
` (19 preceding siblings ...)
2024-10-02 10:56 ` [PATCH v5 20/25] reftable/pq: handle allocation failures when adding entries Patrick Steinhardt
@ 2024-10-02 10:56 ` Patrick Steinhardt
2024-10-02 10:56 ` [PATCH v5 22/25] reftable: handle trivial " Patrick Steinhardt
` (4 subsequent siblings)
25 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-10-02 10:56 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
The 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.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
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(-)
diff --git a/reftable/tree.c b/reftable/tree.c
index 5ffb2e0d69..f4dbe72090 100644
--- a/reftable/tree.c
+++ b/reftable/tree.c
@@ -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;
}
diff --git a/reftable/tree.h b/reftable/tree.h
index fbdd002e23..9604453b6d 100644
--- a/reftable/tree.h
+++ b/reftable/tree.h
@@ -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),
diff --git a/reftable/writer.c b/reftable/writer.c
index 791e246337..e180c10840 100644
--- a/reftable/writer.c
+++ b/reftable/writer.c
@@ -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;
}
diff --git a/t/unit-tests/t-reftable-tree.c b/t/unit-tests/t-reftable-tree.c
index 700479d34b..79b175a45a 100644
--- a/t/unit-tests/t-reftable-tree.c
+++ b/t/unit-tests/t-reftable-tree.c
@@ -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);
--
2.47.0.rc0.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v5 22/25] reftable: handle trivial allocation failures
2024-10-02 10:55 ` [PATCH v5 " Patrick Steinhardt
` (20 preceding siblings ...)
2024-10-02 10:56 ` [PATCH v5 21/25] reftable/tree: handle allocation failures Patrick Steinhardt
@ 2024-10-02 10:56 ` Patrick Steinhardt
2024-10-02 10:56 ` [PATCH v5 23/25] reftable: fix calls to free(3P) Patrick Steinhardt
` (3 subsequent siblings)
25 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-10-02 10:56 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
Handle trivial allocation failures in the reftable library and its unit
tests.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
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(-)
diff --git a/reftable/merged.c b/reftable/merged.c
index 8e202a8efd..514d6facf4 100644
--- a/reftable/merged.c
+++ b/reftable/merged.c
@@ -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;
diff --git a/reftable/reader.c b/reftable/reader.c
index 0179e4e73d..98e7aa2637 100644
--- a/reftable/reader.c
+++ b/reftable/reader.c
@@ -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;
diff --git a/reftable/stack.c b/reftable/stack.c
index 990784d9d2..7df28ab343 100644
--- a/reftable/stack.c
+++ b/reftable/stack.c
@@ -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);
diff --git a/reftable/writer.c b/reftable/writer.c
index e180c10840..550172e65c 100644
--- a/reftable/writer.c
+++ b/reftable/writer.c
@@ -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);
diff --git a/t/unit-tests/t-reftable-block.c b/t/unit-tests/t-reftable-block.c
index e52a612e85..d470060e8b 100644
--- a/t/unit-tests/t-reftable-block.c
+++ b/t/unit-tests/t-reftable-block.c
@@ -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,
diff --git a/t/unit-tests/t-reftable-merged.c b/t/unit-tests/t-reftable-merged.c
index 3d2848632d..3c84363e98 100644
--- a/t/unit-tests/t-reftable-merged.c
+++ b/t/unit-tests/t-reftable-merged.c
@@ -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);
diff --git a/t/unit-tests/t-reftable-readwrite.c b/t/unit-tests/t-reftable-readwrite.c
index acca927a2c..bfa069caff 100644
--- a/t/unit-tests/t-reftable-readwrite.c
+++ b/t/unit-tests/t-reftable-readwrite.c
@@ -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);
--
2.47.0.rc0.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v5 23/25] reftable: fix calls to free(3P)
2024-10-02 10:55 ` [PATCH v5 " Patrick Steinhardt
` (21 preceding siblings ...)
2024-10-02 10:56 ` [PATCH v5 22/25] reftable: handle trivial " Patrick Steinhardt
@ 2024-10-02 10:56 ` Patrick Steinhardt
2024-10-02 10:56 ` [PATCH v5 24/25] reftable: introduce `REFTABLE_FREE_AND_NULL()` Patrick Steinhardt
` (2 subsequent siblings)
25 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-10-02 10:56 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
There 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.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/stack.c | 2 +-
t/unit-tests/t-reftable-readwrite.c | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/reftable/stack.c b/reftable/stack.c
index 7df28ab343..b2babe8e3d 100644
--- a/reftable/stack.c
+++ b/reftable/stack.c
@@ -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:
diff --git a/t/unit-tests/t-reftable-readwrite.c b/t/unit-tests/t-reftable-readwrite.c
index bfa069caff..27ce84445e 100644
--- a/t/unit-tests/t-reftable-readwrite.c
+++ b/t/unit-tests/t-reftable-readwrite.c
@@ -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)
--
2.47.0.rc0.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v5 24/25] reftable: introduce `REFTABLE_FREE_AND_NULL()`
2024-10-02 10:55 ` [PATCH v5 " Patrick Steinhardt
` (22 preceding siblings ...)
2024-10-02 10:56 ` [PATCH v5 23/25] reftable: fix calls to free(3P) Patrick Steinhardt
@ 2024-10-02 10:56 ` Patrick Steinhardt
2024-10-02 10:56 ` [PATCH v5 25/25] reftable/basics: ban standard allocator functions Patrick Steinhardt
2024-10-02 19:32 ` [PATCH v5 00/25] reftable: handle allocation errors Junio C Hamano
25 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-10-02 10:56 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
We 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.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
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(-)
diff --git a/reftable/basics.h b/reftable/basics.h
index 69adeab2e4..7f0f20e50c 100644
--- a/reftable/basics.h
+++ b/reftable/basics.h
@@ -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;
diff --git a/reftable/block.c b/reftable/block.c
index bfa7dc61bf..8d41a2f99e 100644
--- a/reftable/block.c
+++ b/reftable/block.c
@@ -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. */
}
diff --git a/reftable/iter.c b/reftable/iter.c
index d319538f80..d926db653b 100644
--- a/reftable/iter.c
+++ b/reftable/iter.c
@@ -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,
diff --git a/reftable/pq.c b/reftable/pq.c
index 03b9912282..6ee1164dd3 100644
--- a/reftable/pq.c
+++ b/reftable/pq.c
@@ -68,6 +68,6 @@ int merged_iter_pqueue_add(struct merged_iter_pqueue *pq, const struct pq_entry
void 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));
}
diff --git a/reftable/reader.c b/reftable/reader.c
index 98e7aa2637..8d37253922 100644
--- a/reftable/reader.c
+++ b/reftable/reader.c
@@ -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);
}
diff --git a/reftable/record.c b/reftable/record.c
index 787e134c9a..30d563e16d 100644
--- a/reftable/record.c
+++ b/reftable/record.c
@@ -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;
diff --git a/reftable/stack.c b/reftable/stack.c
index b2babe8e3d..63976e5cea 100644
--- a/reftable/stack.c
+++ b/reftable/stack.c
@@ -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);
}
diff --git a/reftable/writer.c b/reftable/writer.c
index 550172e65c..b032a47dec 100644
--- a/reftable/writer.c
+++ b/reftable/writer.c
@@ -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;
}
diff --git a/t/unit-tests/t-reftable-stack.c b/t/unit-tests/t-reftable-stack.c
index 6e41a4cf7e..cc2db2cdef 100644
--- a/t/unit-tests/t-reftable-stack.c
+++ b/t/unit-tests/t-reftable-stack.c
@@ -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)
--
2.47.0.rc0.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* [PATCH v5 25/25] reftable/basics: ban standard allocator functions
2024-10-02 10:55 ` [PATCH v5 " Patrick Steinhardt
` (23 preceding siblings ...)
2024-10-02 10:56 ` [PATCH v5 24/25] reftable: introduce `REFTABLE_FREE_AND_NULL()` Patrick Steinhardt
@ 2024-10-02 10:56 ` Patrick Steinhardt
2024-10-02 19:32 ` [PATCH v5 00/25] reftable: handle allocation errors Junio C Hamano
25 siblings, 0 replies; 152+ messages in thread
From: Patrick Steinhardt @ 2024-10-02 10:56 UTC (permalink / raw)
To: git; +Cc: Edward Thomson, Junio C Hamano, René Scharfe
The 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.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/basics.c | 1 +
reftable/basics.h | 14 ++++++++++++++
2 files changed, 15 insertions(+)
diff --git a/reftable/basics.c b/reftable/basics.c
index ea53cf102a..c8396dc525 100644
--- a/reftable/basics.c
+++ b/reftable/basics.c
@@ -6,6 +6,7 @@ license that can be found in the LICENSE file or at
https://developers.google.com/open-source/licenses/bsd
*/
+#define REFTABLE_ALLOW_BANNED_ALLOCATORS
#include "basics.h"
#include "reftable-basics.h"
diff --git a/reftable/basics.h b/reftable/basics.h
index 7f0f20e50c..4c9ef0fe6c 100644
--- a/reftable/basics.h
+++ b/reftable/basics.h
@@ -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);
--
2.47.0.rc0.dirty
^ permalink raw reply related [flat|nested] 152+ messages in thread
* Re: [PATCH v4 00/25] reftable: handle allocation errors
2024-10-02 4:29 ` Patrick Steinhardt
@ 2024-10-02 18:04 ` Junio C Hamano
0 siblings, 0 replies; 152+ messages in thread
From: Junio C Hamano @ 2024-10-02 18:04 UTC (permalink / raw)
To: Patrick Steinhardt; +Cc: René Scharfe, git, Edward Thomson
Patrick Steinhardt <ps@pks.im> writes:
> `FREE_AND_NULL()` would already be detected because it expands to code
> that contains `free()`. Same for `REALLOC_ARRAY()` and the likes.
Ah, that is very true.
^ permalink raw reply [flat|nested] 152+ messages in thread
* Re: [PATCH v5 00/25] reftable: handle allocation errors
2024-10-02 10:55 ` [PATCH v5 " Patrick Steinhardt
` (24 preceding siblings ...)
2024-10-02 10:56 ` [PATCH v5 25/25] reftable/basics: ban standard allocator functions Patrick Steinhardt
@ 2024-10-02 19:32 ` Junio C Hamano
25 siblings, 0 replies; 152+ messages in thread
From: Junio C Hamano @ 2024-10-02 19:32 UTC (permalink / raw)
To: Patrick Steinhardt; +Cc: git, Edward Thomson, René Scharfe
Patrick Steinhardt <ps@pks.im> writes:
> 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.
Looking good.
Let me mark the topic for 'next' so that it can graduate in the
first batch after the upcoming release.
Thanks.
^ permalink raw reply [flat|nested] 152+ messages in thread
* Re: [PATCH v5 05/25] reftable/basics: handle allocation failures in `parse_names()`
2024-10-02 10:55 ` [PATCH v5 05/25] reftable/basics: handle allocation failures in `parse_names()` Patrick Steinhardt
@ 2024-10-02 22:07 ` Eric Sunshine
2024-10-04 4:58 ` Patrick Steinhardt
0 siblings, 1 reply; 152+ messages in thread
From: Eric Sunshine @ 2024-10-02 22:07 UTC (permalink / raw)
To: Patrick Steinhardt; +Cc: git, Edward Thomson, Junio C Hamano, René Scharfe
On Wed, Oct 2, 2024 at 6:56 AM Patrick Steinhardt <ps@pks.im> wrote:
> Handle 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.
>
> Signed-off-by: Patrick Steinhardt <ps@pks.im>
> ---
> diff --git a/reftable/basics.c b/reftable/basics.c
> @@ -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...
> + 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]);
... and then we potentially index into names[] because `names_len` is
non-zero, thus crash because `names` is NULL.
> + reftable_free(names);
> + return NULL;
> }
^ permalink raw reply [flat|nested] 152+ messages in thread
* Re: [PATCH v5 05/25] reftable/basics: handle allocation failures in `parse_names()`
2024-10-02 22:07 ` Eric Sunshine
@ 2024-10-04 4:58 ` Patrick Steinhardt
2024-10-04 5:43 ` Eric Sunshine
0 siblings, 1 reply; 152+ messages in thread
From: Patrick Steinhardt @ 2024-10-04 4:58 UTC (permalink / raw)
To: Eric Sunshine; +Cc: git, Edward Thomson, Junio C Hamano, René Scharfe
On Wed, Oct 02, 2024 at 06:07:14PM -0400, Eric Sunshine wrote:
> On Wed, Oct 2, 2024 at 6:56 AM Patrick Steinhardt <ps@pks.im> wrote:
> > Handle 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.
> >
> > Signed-off-by: Patrick Steinhardt <ps@pks.im>
> > ---
> > diff --git a/reftable/basics.c b/reftable/basics.c
> > @@ -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...
>
> > + 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]);
>
> ... and then we potentially index into names[] because `names_len` is
> non-zero, thus crash because `names` is NULL.
>
> > + 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.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
reftable/basics.c | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/reftable/basics.c b/reftable/basics.c
index c8396dc525..df49cc8ef2 100644
--- a/reftable/basics.c
+++ b/reftable/basics.c
@@ -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++])
^ permalink raw reply related [flat|nested] 152+ messages in thread
* Re: [PATCH v5 05/25] reftable/basics: handle allocation failures in `parse_names()`
2024-10-04 4:58 ` Patrick Steinhardt
@ 2024-10-04 5:43 ` Eric Sunshine
0 siblings, 0 replies; 152+ messages in thread
From: Eric Sunshine @ 2024-10-04 5:43 UTC (permalink / raw)
To: Patrick Steinhardt; +Cc: git, Edward Thomson, Junio C Hamano, René Scharfe
On Fri, Oct 4, 2024 at 12:59 AM Patrick Steinhardt <ps@pks.im> wrote:
> On Wed, Oct 02, 2024 at 06:07:14PM -0400, Eric Sunshine wrote:
> > 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...
> > ... and then we potentially index into names[] because `names_len` is
> > non-zero, thus crash because `names` is NULL.
>
> Good catch! I think we should queue something like the below on top of
> what we already have in `next` now.
>
> -- >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.
>
> Signed-off-by: Patrick Steinhardt <ps@pks.im>
> ---
> reftable/basics.c | 8 +++++---
> 1 file changed, 5 insertions(+), 3 deletions(-)
>
> diff --git a/reftable/basics.c b/reftable/basics.c
> @@ -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.
^ permalink raw reply [flat|nested] 152+ messages in thread
end of thread, other threads:[~2024-10-04 5:43 UTC | newest]
Thread overview: 152+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-09-16 12:28 [PATCH 00/22] reftable: handle allocation errors Patrick Steinhardt
2024-09-16 12:28 ` [PATCH 01/22] reftable/error: introduce out-of-memory error code Patrick Steinhardt
2024-09-16 12:28 ` [PATCH 02/22] reftable/basics: merge "publicbasics" into "basics" Patrick Steinhardt
2024-09-16 12:28 ` [PATCH 03/22] reftable: introduce `reftable_strdup()` Patrick Steinhardt
2024-09-16 12:28 ` [PATCH 04/22] reftable/basics: handle allocation failures in `reftable_calloc()` Patrick Steinhardt
2024-09-21 19:37 ` Junio C Hamano
2024-09-24 5:48 ` Patrick Steinhardt
2024-09-24 6:02 ` Patrick Steinhardt
2024-09-24 16:39 ` Junio C Hamano
2024-09-16 12:28 ` [PATCH 05/22] reftable/basics: handle allocation failures in `parse_names()` Patrick Steinhardt
2024-09-16 12:28 ` [PATCH 06/22] reftable/record: handle allocation failures on copy Patrick Steinhardt
2024-09-16 12:28 ` [PATCH 07/22] reftable/record: handle allocation failures when decoding records Patrick Steinhardt
2024-09-16 12:28 ` [PATCH 08/22] reftable/writer: handle allocation failures in `writer_index_hash()` Patrick Steinhardt
2024-09-16 12:28 ` [PATCH 09/22] reftable/writer: handle allocation failures in `reftable_new_writer()` Patrick Steinhardt
2024-09-16 12:28 ` [PATCH 10/22] reftable/merged: handle allocation failures in `merged_table_init_iter()` Patrick Steinhardt
2024-09-16 12:28 ` [PATCH 11/22] reftable/reader: handle allocation failures for unindexed reader Patrick Steinhardt
2024-09-16 12:28 ` [PATCH 12/22] reftable/reader: handle allocation failures in `reader_init_iter()` Patrick Steinhardt
2024-09-16 12:28 ` [PATCH 13/22] reftable/stack: handle allocation failures on reload Patrick Steinhardt
2024-09-16 12:28 ` [PATCH 14/22] reftable/stack: handle allocation failures in `reftable_new_stack()` Patrick Steinhardt
2024-09-16 12:29 ` [PATCH 15/22] reftable/stack: handle allocation failures in `stack_compact_range()` Patrick Steinhardt
2024-09-16 12:29 ` [PATCH 16/22] reftable/stack: handle allocation failures in auto compaction Patrick Steinhardt
2024-09-16 12:29 ` [PATCH 17/22] reftable/iter: handle allocation failures when creating indexed table iter Patrick Steinhardt
2024-09-22 6:26 ` Junio C Hamano
2024-09-24 5:49 ` Patrick Steinhardt
2024-09-16 12:29 ` [PATCH 18/22] reftable/blocksource: handle allocation failures Patrick Steinhardt
2024-09-16 12:29 ` [PATCH 19/22] reftable/block: " Patrick Steinhardt
2024-09-16 12:29 ` [PATCH 20/22] reftable/pq: handle allocation failures when adding entries Patrick Steinhardt
2024-09-16 12:29 ` [PATCH 21/22] reftable/tree: handle allocation failures Patrick Steinhardt
2024-09-16 12:29 ` [PATCH 22/22] reftable: handle trivial " Patrick Steinhardt
2024-09-24 6:31 ` [PATCH v2 00/22] reftable: handle allocation errors Patrick Steinhardt
2024-09-24 6:31 ` [PATCH v2 01/22] reftable/error: introduce out-of-memory error code Patrick Steinhardt
2024-09-24 6:31 ` [PATCH v2 02/22] reftable/basics: merge "publicbasics" into "basics" Patrick Steinhardt
2024-09-24 6:31 ` [PATCH v2 03/22] reftable: introduce `reftable_strdup()` Patrick Steinhardt
2024-09-24 6:32 ` [PATCH v2 04/22] reftable/basics: handle allocation failures in `reftable_calloc()` Patrick Steinhardt
2024-09-24 16:59 ` Junio C Hamano
2024-09-26 12:11 ` Patrick Steinhardt
2024-09-26 16:13 ` Junio C Hamano
2024-09-27 5:28 ` Patrick Steinhardt
2024-09-27 12:21 ` Han-Wen Nienhuys
2024-09-27 15:21 ` Junio C Hamano
2024-09-24 6:32 ` [PATCH v2 05/22] reftable/basics: handle allocation failures in `parse_names()` Patrick Steinhardt
2024-09-24 22:19 ` René Scharfe
2024-09-26 12:09 ` Patrick Steinhardt
2024-09-24 6:32 ` [PATCH v2 06/22] reftable/record: handle allocation failures on copy Patrick Steinhardt
2024-09-24 6:32 ` [PATCH v2 07/22] reftable/record: handle allocation failures when decoding records Patrick Steinhardt
2024-09-24 6:32 ` [PATCH v2 08/22] reftable/writer: handle allocation failures in `writer_index_hash()` Patrick Steinhardt
2024-09-24 6:32 ` [PATCH v2 09/22] reftable/writer: handle allocation failures in `reftable_new_writer()` Patrick Steinhardt
2024-09-24 6:32 ` [PATCH v2 10/22] reftable/merged: handle allocation failures in `merged_table_init_iter()` Patrick Steinhardt
2024-09-24 6:32 ` [PATCH v2 11/22] reftable/reader: handle allocation failures for unindexed reader Patrick Steinhardt
2024-09-24 6:32 ` [PATCH v2 12/22] reftable/reader: handle allocation failures in `reader_init_iter()` Patrick Steinhardt
2024-09-24 6:32 ` [PATCH v2 13/22] reftable/stack: handle allocation failures on reload Patrick Steinhardt
2024-09-24 6:32 ` [PATCH v2 14/22] reftable/stack: handle allocation failures in `reftable_new_stack()` Patrick Steinhardt
2024-09-24 6:32 ` [PATCH v2 15/22] reftable/stack: handle allocation failures in `stack_compact_range()` Patrick Steinhardt
2024-09-24 6:32 ` [PATCH v2 16/22] reftable/stack: handle allocation failures in auto compaction Patrick Steinhardt
2024-09-24 6:32 ` [PATCH v2 17/22] reftable/iter: handle allocation failures when creating indexed table iter Patrick Steinhardt
2024-09-24 6:32 ` [PATCH v2 18/22] reftable/blocksource: handle allocation failures Patrick Steinhardt
2024-09-24 6:32 ` [PATCH v2 19/22] reftable/block: " Patrick Steinhardt
2024-09-24 6:32 ` [PATCH v2 20/22] reftable/pq: handle allocation failures when adding entries Patrick Steinhardt
2024-09-24 6:33 ` [PATCH v2 21/22] reftable/tree: handle allocation failures Patrick Steinhardt
2024-09-24 6:33 ` [PATCH v2 22/22] reftable: handle trivial " Patrick Steinhardt
2024-09-30 8:08 ` [PATCH v3 00/22] refatble: handle allocation errors Patrick Steinhardt
2024-09-30 8:08 ` [PATCH v3 01/22] reftable/error: introduce out-of-memory error code Patrick Steinhardt
2024-09-30 8:08 ` [PATCH v3 02/22] reftable/basics: merge "publicbasics" into "basics" Patrick Steinhardt
2024-09-30 8:08 ` [PATCH v3 03/22] reftable: introduce `reftable_strdup()` Patrick Steinhardt
2024-09-30 8:08 ` [PATCH v3 04/22] reftable/basics: handle allocation failures in `reftable_calloc()` Patrick Steinhardt
2024-09-30 8:08 ` [PATCH v3 05/22] reftable/basics: handle allocation failures in `parse_names()` Patrick Steinhardt
2024-09-30 17:40 ` René Scharfe
2024-09-30 8:08 ` [PATCH v3 06/22] reftable/record: handle allocation failures on copy Patrick Steinhardt
2024-09-30 8:08 ` [PATCH v3 07/22] reftable/record: handle allocation failures when decoding records Patrick Steinhardt
2024-09-30 8:08 ` [PATCH v3 08/22] reftable/writer: handle allocation failures in `writer_index_hash()` Patrick Steinhardt
2024-09-30 8:08 ` [PATCH v3 09/22] reftable/writer: handle allocation failures in `reftable_new_writer()` Patrick Steinhardt
2024-09-30 17:40 ` René Scharfe
2024-09-30 18:22 ` Patrick Steinhardt
2024-09-30 19:11 ` Junio C Hamano
2024-09-30 8:08 ` [PATCH v3 10/22] reftable/merged: handle allocation failures in `merged_table_init_iter()` Patrick Steinhardt
2024-09-30 8:08 ` [PATCH v3 11/22] reftable/reader: handle allocation failures for unindexed reader Patrick Steinhardt
2024-09-30 8:08 ` [PATCH v3 12/22] reftable/reader: handle allocation failures in `reader_init_iter()` Patrick Steinhardt
2024-09-30 8:08 ` [PATCH v3 13/22] reftable/stack: handle allocation failures on reload Patrick Steinhardt
2024-09-30 8:08 ` [PATCH v3 14/22] reftable/stack: handle allocation failures in `reftable_new_stack()` Patrick Steinhardt
2024-09-30 8:08 ` [PATCH v3 15/22] reftable/stack: handle allocation failures in `stack_compact_range()` Patrick Steinhardt
2024-09-30 8:08 ` [PATCH v3 16/22] reftable/stack: handle allocation failures in auto compaction Patrick Steinhardt
2024-09-30 8:09 ` [PATCH v3 17/22] reftable/iter: handle allocation failures when creating indexed table iter Patrick Steinhardt
2024-09-30 8:09 ` [PATCH v3 18/22] reftable/blocksource: handle allocation failures Patrick Steinhardt
2024-09-30 8:09 ` [PATCH v3 19/22] reftable/block: " Patrick Steinhardt
2024-09-30 8:09 ` [PATCH v3 20/22] reftable/pq: handle allocation failures when adding entries Patrick Steinhardt
2024-09-30 8:09 ` [PATCH v3 21/22] reftable/tree: handle allocation failures Patrick Steinhardt
2024-09-30 8:09 ` [PATCH v3 22/22] reftable: handle trivial " Patrick Steinhardt
2024-09-30 18:18 ` [PATCH v3 00/22] refatble: handle allocation errors Junio C Hamano
2024-10-01 9:41 ` [PATCH v4 00/25] reftable: " Patrick Steinhardt
2024-10-01 9:41 ` [PATCH v4 01/25] reftable/error: introduce out-of-memory error code Patrick Steinhardt
2024-10-01 9:41 ` [PATCH v4 02/25] reftable/basics: merge "publicbasics" into "basics" Patrick Steinhardt
2024-10-01 9:41 ` [PATCH v4 03/25] reftable: introduce `reftable_strdup()` Patrick Steinhardt
2024-10-01 9:41 ` [PATCH v4 04/25] reftable/basics: handle allocation failures in `reftable_calloc()` Patrick Steinhardt
2024-10-01 9:41 ` [PATCH v4 05/25] reftable/basics: handle allocation failures in `parse_names()` Patrick Steinhardt
2024-10-01 9:42 ` [PATCH v4 06/25] reftable/record: handle allocation failures on copy Patrick Steinhardt
2024-10-01 9:42 ` [PATCH v4 07/25] reftable/record: handle allocation failures when decoding records Patrick Steinhardt
2024-10-01 9:42 ` [PATCH v4 08/25] reftable/writer: handle allocation failures in `writer_index_hash()` Patrick Steinhardt
2024-10-01 9:42 ` [PATCH v4 09/25] reftable/writer: handle allocation failures in `reftable_new_writer()` Patrick Steinhardt
2024-10-01 9:42 ` [PATCH v4 10/25] reftable/merged: handle allocation failures in `merged_table_init_iter()` Patrick Steinhardt
2024-10-01 9:42 ` [PATCH v4 11/25] reftable/reader: handle allocation failures for unindexed reader Patrick Steinhardt
2024-10-01 9:42 ` [PATCH v4 12/25] reftable/reader: handle allocation failures in `reader_init_iter()` Patrick Steinhardt
2024-10-01 9:42 ` [PATCH v4 13/25] reftable/stack: handle allocation failures on reload Patrick Steinhardt
2024-10-01 9:42 ` [PATCH v4 14/25] reftable/stack: handle allocation failures in `reftable_new_stack()` Patrick Steinhardt
2024-10-01 9:42 ` [PATCH v4 15/25] reftable/stack: handle allocation failures in `stack_compact_range()` Patrick Steinhardt
2024-10-01 9:42 ` [PATCH v4 16/25] reftable/stack: handle allocation failures in auto compaction Patrick Steinhardt
2024-10-01 9:42 ` [PATCH v4 17/25] reftable/iter: handle allocation failures when creating indexed table iter Patrick Steinhardt
2024-10-01 9:42 ` [PATCH v4 18/25] reftable/blocksource: handle allocation failures Patrick Steinhardt
2024-10-01 9:42 ` [PATCH v4 19/25] reftable/block: " Patrick Steinhardt
2024-10-01 9:42 ` [PATCH v4 20/25] reftable/pq: handle allocation failures when adding entries Patrick Steinhardt
2024-10-01 9:42 ` [PATCH v4 21/25] reftable/tree: handle allocation failures Patrick Steinhardt
2024-10-01 9:42 ` [PATCH v4 22/25] reftable: handle trivial " Patrick Steinhardt
2024-10-01 9:42 ` [PATCH v4 23/25] reftable: fix calls to free(3P) Patrick Steinhardt
2024-10-01 9:43 ` [PATCH v4 24/25] reftable: introduce `REFTABLE_FREE_AND_NULL()` Patrick Steinhardt
2024-10-01 9:43 ` [PATCH v4 25/25] reftable/basics: ban standard allocator functions Patrick Steinhardt
2024-10-01 22:50 ` Junio C Hamano
2024-10-02 4:30 ` Patrick Steinhardt
2024-10-01 17:52 ` [PATCH v4 00/25] reftable: handle allocation errors Junio C Hamano
2024-10-01 18:30 ` René Scharfe
2024-10-01 19:25 ` Junio C Hamano
2024-10-02 4:29 ` Patrick Steinhardt
2024-10-02 18:04 ` Junio C Hamano
2024-10-02 10:55 ` [PATCH v5 " Patrick Steinhardt
2024-10-02 10:55 ` [PATCH v5 01/25] reftable/error: introduce out-of-memory error code Patrick Steinhardt
2024-10-02 10:55 ` [PATCH v5 02/25] reftable/basics: merge "publicbasics" into "basics" Patrick Steinhardt
2024-10-02 10:55 ` [PATCH v5 03/25] reftable: introduce `reftable_strdup()` Patrick Steinhardt
2024-10-02 10:55 ` [PATCH v5 04/25] reftable/basics: handle allocation failures in `reftable_calloc()` Patrick Steinhardt
2024-10-02 10:55 ` [PATCH v5 05/25] reftable/basics: handle allocation failures in `parse_names()` Patrick Steinhardt
2024-10-02 22:07 ` Eric Sunshine
2024-10-04 4:58 ` Patrick Steinhardt
2024-10-04 5:43 ` Eric Sunshine
2024-10-02 10:55 ` [PATCH v5 06/25] reftable/record: handle allocation failures on copy Patrick Steinhardt
2024-10-02 10:55 ` [PATCH v5 07/25] reftable/record: handle allocation failures when decoding records Patrick Steinhardt
2024-10-02 10:55 ` [PATCH v5 08/25] reftable/writer: handle allocation failures in `writer_index_hash()` Patrick Steinhardt
2024-10-02 10:55 ` [PATCH v5 09/25] reftable/writer: handle allocation failures in `reftable_new_writer()` Patrick Steinhardt
2024-10-02 10:55 ` [PATCH v5 10/25] reftable/merged: handle allocation failures in `merged_table_init_iter()` Patrick Steinhardt
2024-10-02 10:55 ` [PATCH v5 11/25] reftable/reader: handle allocation failures for unindexed reader Patrick Steinhardt
2024-10-02 10:55 ` [PATCH v5 12/25] reftable/reader: handle allocation failures in `reader_init_iter()` Patrick Steinhardt
2024-10-02 10:56 ` [PATCH v5 13/25] reftable/stack: handle allocation failures on reload Patrick Steinhardt
2024-10-02 10:56 ` [PATCH v5 14/25] reftable/stack: handle allocation failures in `reftable_new_stack()` Patrick Steinhardt
2024-10-02 10:56 ` [PATCH v5 15/25] reftable/stack: handle allocation failures in `stack_compact_range()` Patrick Steinhardt
2024-10-02 10:56 ` [PATCH v5 16/25] reftable/stack: handle allocation failures in auto compaction Patrick Steinhardt
2024-10-02 10:56 ` [PATCH v5 17/25] reftable/iter: handle allocation failures when creating indexed table iter Patrick Steinhardt
2024-10-02 10:56 ` [PATCH v5 18/25] reftable/blocksource: handle allocation failures Patrick Steinhardt
2024-10-02 10:56 ` [PATCH v5 19/25] reftable/block: " Patrick Steinhardt
2024-10-02 10:56 ` [PATCH v5 20/25] reftable/pq: handle allocation failures when adding entries Patrick Steinhardt
2024-10-02 10:56 ` [PATCH v5 21/25] reftable/tree: handle allocation failures Patrick Steinhardt
2024-10-02 10:56 ` [PATCH v5 22/25] reftable: handle trivial " Patrick Steinhardt
2024-10-02 10:56 ` [PATCH v5 23/25] reftable: fix calls to free(3P) Patrick Steinhardt
2024-10-02 10:56 ` [PATCH v5 24/25] reftable: introduce `REFTABLE_FREE_AND_NULL()` Patrick Steinhardt
2024-10-02 10:56 ` [PATCH v5 25/25] reftable/basics: ban standard allocator functions Patrick Steinhardt
2024-10-02 19:32 ` [PATCH v5 00/25] reftable: handle allocation errors Junio C Hamano
-- strict thread matches above, loose matches on Subject: below --
2024-09-29 6:53 [PATCH v2 05/22] reftable/basics: handle allocation failures in `parse_names()` Crystal M Baker
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).