Fix crash in free() when git_buf_grow() fails.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
diff --git a/src/buffer.c b/src/buffer.c
index 7744d8f..8013457 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -67,7 +67,8 @@ int git_buf_try_grow(
if (!new_ptr) {
if (mark_oom) {
- if (buf->ptr) git__free(buf->ptr);
+ if (buf->ptr && (buf->ptr != git_buf__initbuf))
+ git__free(buf->ptr);
buf->ptr = git_buf__oom;
}
return -1;
diff --git a/tests/buf/oom.c b/tests/buf/oom.c
new file mode 100644
index 0000000..709439a
--- /dev/null
+++ b/tests/buf/oom.c
@@ -0,0 +1,31 @@
+#include "clar_libgit2.h"
+#include "buffer.h"
+
+#if defined(GIT_ARCH_64)
+#define TOOBIG 0xffffffffffffff00
+#else
+#define TOOBIG 0xffffff00
+#endif
+
+/**
+ * If we make a ridiculously large request the first time we
+ * actually allocate some space in the git_buf, the realloc()
+ * will fail. And because the git_buf_grow() wrapper always
+ * sets mark_oom, the code in git_buf_try_grow() will free
+ * the internal buffer and set it to git_buf__oom.
+ *
+ * We initialized the internal buffer to (the static variable)
+ * git_buf__initbuf. The purpose of this test is to make sure
+ * that we don't try to free the static buffer.
+ */
+void test_buf_oom__grow(void)
+{
+ git_buf buf = GIT_BUF_INIT;
+
+ git_buf_clear(&buf);
+
+ cl_assert(git_buf_grow(&buf, TOOBIG) == -1);
+ cl_assert(git_buf_oom(&buf));
+
+ git_buf_free(&buf);
+}