Commit 08f28ff50e3f6fabcc1469fdf4a53c3a3a33eff4

Edward Thomson 2020-07-10T08:57:05

alloc: set up an allocator that fails before library init We require the library to be initialized with git_libgit2_init before it is functional. However, if a user tries to uses the library without doing so - as they might when getting started with the library for the first time - we will likely crash. This commit introduces some guard rails - now instead of having _no_ allocator by default, we'll have an allocator that always fails, and never tries to set an error message (since the thread-local state is set up by git_libgit2_init). We've modified the error retrieval function to (try to) ensure that the library has been initialized before getting the thread-local error message. (Unfortunately, we cannot determine if the thread local storage has actually been configured, this does require initialization by git_libgit2_init. But a naive attempt should be good enough for most cases.)

diff --git a/src/alloc.c b/src/alloc.c
index 6efa104..2820d84 100644
--- a/src/alloc.c
+++ b/src/alloc.c
@@ -8,10 +8,22 @@
 #include "alloc.h"
 #include "runtime.h"
 
+#include "allocators/failalloc.h"
 #include "allocators/stdalloc.h"
 #include "allocators/win32_leakcheck.h"
 
-git_allocator git__allocator;
+/* Fail any allocation until git_libgit2_init is called. */
+git_allocator git__allocator = {
+	git_failalloc_malloc,
+	git_failalloc_calloc,
+	git_failalloc_strdup,
+	git_failalloc_strndup,
+	git_failalloc_substrdup,
+	git_failalloc_realloc,
+	git_failalloc_reallocarray,
+	git_failalloc_mallocarray,
+	git_failalloc_free
+};
 
 static int setup_default_allocator(void)
 {
@@ -25,10 +37,10 @@ static int setup_default_allocator(void)
 int git_allocator_global_init(void)
 {
 	/*
-	 * We don't want to overwrite any allocator which has been set before
-	 * the init function is called.
+	 * We don't want to overwrite any allocator which has been set
+	 * before the init function is called.
 	 */
-	if (git__allocator.gmalloc != NULL)
+	if (git__allocator.gmalloc != git_failalloc_malloc)
 		return 0;
 
 	return setup_default_allocator();
diff --git a/src/allocators/failalloc.c b/src/allocators/failalloc.c
new file mode 100644
index 0000000..5257d1d
--- /dev/null
+++ b/src/allocators/failalloc.c
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "failalloc.h"
+
+void *git_failalloc_malloc(size_t len, const char *file, int line)
+{
+	GIT_UNUSED(len);
+	GIT_UNUSED(file);
+	GIT_UNUSED(line);
+
+	return NULL;
+}
+
+void *git_failalloc_calloc(size_t nelem, size_t elsize, const char *file, int line)
+{
+	GIT_UNUSED(nelem);
+	GIT_UNUSED(elsize);
+	GIT_UNUSED(file);
+	GIT_UNUSED(line);
+
+	return NULL;
+}
+
+char *git_failalloc_strdup(const char *str, const char *file, int line)
+{
+	GIT_UNUSED(str);
+	GIT_UNUSED(file);
+	GIT_UNUSED(line);
+
+	return NULL;
+}
+
+char *git_failalloc_strndup(const char *str, size_t n, const char *file, int line)
+{
+	GIT_UNUSED(str);
+	GIT_UNUSED(n);
+	GIT_UNUSED(file);
+	GIT_UNUSED(line);
+
+	return NULL;
+}
+
+char *git_failalloc_substrdup(const char *start, size_t n, const char *file, int line)
+{
+	GIT_UNUSED(start);
+	GIT_UNUSED(n);
+	GIT_UNUSED(file);
+	GIT_UNUSED(line);
+
+	return NULL;
+}
+
+void *git_failalloc_realloc(void *ptr, size_t size, const char *file, int line)
+{
+	GIT_UNUSED(ptr);
+	GIT_UNUSED(size);
+	GIT_UNUSED(file);
+	GIT_UNUSED(line);
+
+	return NULL;
+}
+
+void *git_failalloc_reallocarray(void *ptr, size_t nelem, size_t elsize, const char *file, int line)
+{
+	GIT_UNUSED(ptr);
+	GIT_UNUSED(nelem);
+	GIT_UNUSED(elsize);
+	GIT_UNUSED(file);
+	GIT_UNUSED(line);
+
+	return NULL;
+}
+
+void *git_failalloc_mallocarray(size_t nelem, size_t elsize, const char *file, int line)
+{
+	GIT_UNUSED(nelem);
+	GIT_UNUSED(elsize);
+	GIT_UNUSED(file);
+	GIT_UNUSED(line);
+
+	return NULL;
+}
+
+void git_failalloc_free(void *ptr)
+{
+	GIT_UNUSED(ptr);
+}
diff --git a/src/allocators/failalloc.h b/src/allocators/failalloc.h
new file mode 100644
index 0000000..6115e51
--- /dev/null
+++ b/src/allocators/failalloc.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifndef INCLUDE_allocators_failalloc_h__
+#define INCLUDE_allocators_failalloc_h__
+
+#include "common.h"
+
+extern void *git_failalloc_malloc(size_t len, const char *file, int line);
+extern void *git_failalloc_calloc(size_t nelem, size_t elsize, const char *file, int line);
+extern char *git_failalloc_strdup(const char *str, const char *file, int line);
+extern char *git_failalloc_strndup(const char *str, size_t n, const char *file, int line);
+extern char *git_failalloc_substrdup(const char *start, size_t n, const char *file, int line);
+extern void *git_failalloc_realloc(void *ptr, size_t size, const char *file, int line);
+extern void *git_failalloc_reallocarray(void *ptr, size_t nelem, size_t elsize, const char *file, int line);
+extern void *git_failalloc_mallocarray(size_t nelem, size_t elsize, const char *file, int line);
+extern void git_failalloc_free(void *ptr);
+
+#endif
diff --git a/src/errors.c b/src/errors.c
index 5b68dd9..3d1d1c9 100644
--- a/src/errors.c
+++ b/src/errors.c
@@ -10,6 +10,7 @@
 #include "threadstate.h"
 #include "posix.h"
 #include "buffer.h"
+#include "libgit2.h"
 
 /********************************************
  * New error handling
@@ -20,6 +21,11 @@ static git_error g_git_oom_error = {
 	GIT_ERROR_NOMEMORY
 };
 
+static git_error g_git_uninitialized_error = {
+	"libgit2 has not been initialized; you must call git_libgit2_init",
+	GIT_ERROR_INVALID
+};
+
 static void set_error_from_buffer(int error_class)
 {
 	git_error *error = &GIT_THREADSTATE->error_t;
@@ -131,6 +137,10 @@ void git_error_clear(void)
 
 const git_error *git_error_last(void)
 {
+	/* If the library is not initialized, return a static error. */
+	if (!git_libgit2_init_count())
+		return &g_git_uninitialized_error;
+
 	return GIT_THREADSTATE->last_error;
 }