summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/git2/checkout.h8
-rw-r--r--include/git2/rebase.h3
-rw-r--r--src/array.h27
-rw-r--r--src/attr.c2
-rw-r--r--src/attrcache.c2
-rw-r--r--src/blame.c28
-rw-r--r--src/blame_git.c8
-rw-r--r--src/buf_text.c19
-rw-r--r--src/buffer.c121
-rw-r--r--src/buffer.h12
-rw-r--r--src/checkout.c7
-rw-r--r--src/cherrypick.c18
-rw-r--r--src/commit.c2
-rw-r--r--src/common.h23
-rw-r--r--src/config_file.c33
-rw-r--r--src/delta-apply.c5
-rw-r--r--src/delta.c31
-rw-r--r--src/diff.c5
-rw-r--r--src/diff_driver.c45
-rw-r--r--src/diff_patch.c7
-rw-r--r--src/diff_tform.c4
-rw-r--r--src/filebuf.c26
-rw-r--r--src/fileops.c25
-rw-r--r--src/filter.c16
-rw-r--r--src/index.c141
-rw-r--r--src/index.h29
-rw-r--r--src/integer.h96
-rw-r--r--src/iterator.c8
-rw-r--r--src/khash.h70
-rw-r--r--src/merge.c32
-rw-r--r--src/mwindow.c2
-rw-r--r--src/notes.c2
-rw-r--r--src/odb.c24
-rw-r--r--src/odb_loose.c32
-rw-r--r--src/odb_mempack.c6
-rw-r--r--src/offmap.h5
-rw-r--r--src/oid.c2
-rw-r--r--src/oidmap.h3
-rw-r--r--src/pack-objects.c40
-rw-r--r--src/pack.c20
-rw-r--r--src/pack.h4
-rw-r--r--src/path.c67
-rw-r--r--src/pool.c15
-rw-r--r--src/posix.c12
-rw-r--r--src/rebase.c90
-rw-r--r--src/refdb_fs.c2
-rw-r--r--src/refs.c24
-rw-r--r--src/remote.c3
-rw-r--r--src/revert.c17
-rw-r--r--src/revwalk.c6
-rw-r--r--src/revwalk.h2
-rw-r--r--src/sortedcache.c8
-rw-r--r--src/strmap.h3
-rw-r--r--src/submodule.c2
-rw-r--r--src/tag.c8
-rw-r--r--src/transaction.c2
-rw-r--r--src/transports/cred.c7
-rw-r--r--src/transports/git.c4
-rw-r--r--src/transports/local.c2
-rw-r--r--src/transports/smart.c2
-rw-r--r--src/transports/smart_pkt.c76
-rw-r--r--src/transports/smart_protocol.c4
-rw-r--r--src/transports/winhttp.c4
-rw-r--r--src/tree.c12
-rw-r--r--src/tsort.c4
-rw-r--r--src/util.h56
-rw-r--r--src/vector.c9
-rw-r--r--src/win32/dir.c8
-rw-r--r--src/win32/utf-conv.c4
-rw-r--r--src/zstream.c2
-rw-r--r--tests/buf/basic.c22
-rw-r--r--tests/buf/oom.c10
-rw-r--r--tests/checkout/tree.c80
-rw-r--r--tests/core/errors.c65
-rw-r--r--tests/core/oidmap.c2
-rw-r--r--tests/core/strmap.c2
-rw-r--r--tests/index/tests.c21
-rw-r--r--tests/merge/workdir/setup.c56
-rw-r--r--tests/rebase/iterator.c1
-rw-r--r--tests/rebase/setup.c49
80 files changed, 1352 insertions, 404 deletions
diff --git a/include/git2/checkout.h b/include/git2/checkout.h
index 8314c623d..4fe1340b9 100644
--- a/include/git2/checkout.h
+++ b/include/git2/checkout.h
@@ -135,7 +135,10 @@ typedef enum {
/** Only update existing files, don't create new ones */
GIT_CHECKOUT_UPDATE_ONLY = (1u << 7),
- /** Normally checkout updates index entries as it goes; this stops that */
+ /**
+ * Normally checkout updates index entries as it goes; this stops that.
+ * Implies `GIT_CHECKOUT_DONT_WRITE_INDEX`.
+ */
GIT_CHECKOUT_DONT_UPDATE_INDEX = (1u << 8),
/** Don't refresh index/config/etc before doing checkout */
@@ -166,6 +169,9 @@ typedef enum {
/** Don't overwrite existing files or folders */
GIT_CHECKOUT_DONT_REMOVE_EXISTING = (1u << 22),
+ /** Normally checkout writes the index upon completion; this prevents that. */
+ GIT_CHECKOUT_DONT_WRITE_INDEX = (1u << 23),
+
/**
* THE FOLLOWING OPTIONS ARE NOT YET IMPLEMENTED
*/
diff --git a/include/git2/rebase.h b/include/git2/rebase.h
index 1da3fb5aa..19f5cb8c9 100644
--- a/include/git2/rebase.h
+++ b/include/git2/rebase.h
@@ -133,7 +133,8 @@ GIT_EXTERN(int) git_rebase_init_options(
*
* @param out Pointer to store the rebase object
* @param repo The repository to perform the rebase
- * @param branch The terminal commit to rebase
+ * @param branch The terminal commit to rebase, or NULL to rebase the
+ * current branch
* @param upstream The commit to begin rebasing from, or NULL to rebase all
* reachable commits
* @param onto The branch to rebase onto, or NULL to rebase onto the given
diff --git a/src/array.h b/src/array.h
index af9eafa43..7cd9b7153 100644
--- a/src/array.h
+++ b/src/array.h
@@ -23,7 +23,7 @@
*
* typedef git_array_t(my_struct) my_struct_array_t;
*/
-#define git_array_t(type) struct { type *ptr; uint32_t size, asize; }
+#define git_array_t(type) struct { type *ptr; size_t size, asize; }
#define GIT_ARRAY_INIT { NULL, 0, 0 }
@@ -45,15 +45,26 @@ typedef git_array_t(char) git_array_generic_t;
GIT_INLINE(void *) git_array_grow(void *_a, size_t item_size)
{
volatile git_array_generic_t *a = _a;
- uint32_t new_size = (a->size < 8) ? 8 : a->asize * 3 / 2;
- char *new_array = git__realloc(a->ptr, new_size * item_size);
- if (!new_array) {
- git_array_clear(*a);
- return NULL;
+ size_t new_size;
+ char *new_array;
+
+ if (a->size < 8) {
+ new_size = 8;
} else {
- a->ptr = new_array; a->asize = new_size; a->size++;
- return a->ptr + (a->size - 1) * item_size;
+ if (GIT_MULTIPLY_SIZET_OVERFLOW(&new_size, a->size, 3))
+ goto on_oom;
+ new_size /= 2;
}
+
+ if ((new_array = git__reallocarray(a->ptr, new_size, item_size)) == NULL)
+ goto on_oom;
+
+ a->ptr = new_array; a->asize = new_size; a->size++;
+ return a->ptr + (a->size - 1) * item_size;
+
+on_oom:
+ git_array_clear(*a);
+ return NULL;
}
#define git_array_alloc(a) \
diff --git a/src/attr.c b/src/attr.c
index 2e1ef7c2a..44593da81 100644
--- a/src/attr.c
+++ b/src/attr.c
@@ -7,7 +7,7 @@
#include "git2/oid.h"
#include <ctype.h>
-GIT__USE_STRMAP;
+GIT__USE_STRMAP
const char *git_attr__true = "[internal]__TRUE__";
const char *git_attr__false = "[internal]__FALSE__";
diff --git a/src/attrcache.c b/src/attrcache.c
index 9a9b9d09d..018fa4874 100644
--- a/src/attrcache.c
+++ b/src/attrcache.c
@@ -5,7 +5,7 @@
#include "sysdir.h"
#include "ignore.h"
-GIT__USE_STRMAP;
+GIT__USE_STRMAP
GIT_INLINE(int) attr_cache_lock(git_attr_cache *cache)
{
diff --git a/src/blame.c b/src/blame.c
index 2cc5e552b..4a12cb85b 100644
--- a/src/blame.c
+++ b/src/blame.c
@@ -76,6 +76,10 @@ static git_blame_hunk* dup_hunk(git_blame_hunk *hunk)
hunk->lines_in_hunk,
hunk->orig_start_line_number,
hunk->orig_path);
+
+ if (!newhunk)
+ return NULL;
+
git_oid_cpy(&newhunk->orig_commit_id, &hunk->orig_commit_id);
git_oid_cpy(&newhunk->final_commit_id, &hunk->final_commit_id);
newhunk->boundary = hunk->boundary;
@@ -221,6 +225,10 @@ static git_blame_hunk *split_hunk_in_vector(
new_line_count = hunk->lines_in_hunk - rel_line;
nh = new_hunk((uint16_t)(hunk->final_start_line_number+rel_line), (uint16_t)new_line_count,
(uint16_t)(hunk->orig_start_line_number+rel_line), hunk->orig_path);
+
+ if (!nh)
+ return NULL;
+
git_oid_cpy(&nh->final_commit_id, &hunk->final_commit_id);
git_oid_cpy(&nh->orig_commit_id, &hunk->orig_commit_id);
@@ -270,6 +278,10 @@ static git_blame_hunk* hunk_from_entry(git_blame__entry *e)
{
git_blame_hunk *h = new_hunk(
e->lno+1, e->num_lines, e->s_lno+1, e->suspect->path);
+
+ if (!h)
+ return NULL;
+
git_oid_cpy(&h->final_commit_id, git_commit_id(e->suspect->commit));
git_oid_cpy(&h->orig_commit_id, git_commit_id(e->suspect->commit));
git_signature_dup(&h->final_signature, git_commit_author(e->suspect->commit));
@@ -307,6 +319,8 @@ static int blame_internal(git_blame *blame)
blame->final_buf_size = git_blob_rawsize(blame->final_blob);
ent = git__calloc(1, sizeof(git_blame__entry));
+ GITERR_CHECK_ALLOC(ent);
+
ent->num_lines = index_blob_lines(blame);
ent->lno = blame->options.min_line - 1;
ent->num_lines = ent->num_lines - blame->options.min_line + 1;
@@ -322,8 +336,9 @@ static int blame_internal(git_blame *blame)
cleanup:
for (ent = blame->ent; ent; ) {
git_blame__entry *e = ent->next;
+ git_blame_hunk *h = hunk_from_entry(ent);
- git_vector_insert(&blame->hunks, hunk_from_entry(ent));
+ git_vector_insert(&blame->hunks, h);
git_blame__free_entry(ent);
ent = e;
@@ -392,11 +407,14 @@ static int buffer_hunk_cb(
if (!blame->current_hunk) {
/* Line added at the end of the file */
blame->current_hunk = new_hunk(wedge_line, 0, wedge_line, blame->path);
+ GITERR_CHECK_ALLOC(blame->current_hunk);
+
git_vector_insert(&blame->hunks, blame->current_hunk);
} else if (!hunk_starts_at_or_after_line(blame->current_hunk, wedge_line)){
/* If this hunk doesn't start between existing hunks, split a hunk up so it does */
blame->current_hunk = split_hunk_in_vector(&blame->hunks, blame->current_hunk,
wedge_line - blame->current_hunk->orig_start_line_number, true);
+ GITERR_CHECK_ALLOC(blame->current_hunk);
}
return 0;
@@ -425,6 +443,8 @@ static int buffer_line_cb(
/* Create a new buffer-blame hunk with this line */
shift_hunks_by(&blame->hunks, blame->current_diff_line, 1);
blame->current_hunk = new_hunk((uint16_t)blame->current_diff_line, 1, 0, blame->path);
+ GITERR_CHECK_ALLOC(blame->current_hunk);
+
git_vector_insert_sorted(&blame->hunks, blame->current_hunk, NULL);
}
blame->current_diff_line++;
@@ -464,10 +484,14 @@ int git_blame_buffer(
assert(out && reference && buffer && buffer_len);
blame = git_blame__alloc(reference->repository, reference->options, reference->path);
+ GITERR_CHECK_ALLOC(blame);
/* Duplicate all of the hunk structures in the reference blame */
git_vector_foreach(&reference->hunks, i, hunk) {
- git_vector_insert(&blame->hunks, dup_hunk(hunk));
+ git_blame_hunk *h = dup_hunk(hunk);
+ GITERR_CHECK_ALLOC(h);
+
+ git_vector_insert(&blame->hunks, h);
}
/* Diff to the reference blob */
diff --git a/src/blame_git.c b/src/blame_git.c
index 72afb852b..e863efe2e 100644
--- a/src/blame_git.c
+++ b/src/blame_git.c
@@ -35,11 +35,15 @@ static void origin_decref(git_blame__origin *o)
/* Given a commit and a path in it, create a new origin structure. */
static int make_origin(git_blame__origin **out, git_commit *commit, const char *path)
{
- int error = 0;
git_blame__origin *o;
+ size_t path_len = strlen(path), alloc_len;
+ int error = 0;
- o = git__calloc(1, sizeof(*o) + strlen(path) + 1);
+ GITERR_CHECK_ALLOC_ADD(&alloc_len, sizeof(*o), path_len);
+ GITERR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 1);
+ o = git__calloc(1, alloc_len);
GITERR_CHECK_ALLOC(o);
+
o->commit = commit;
o->refcnt = 1;
strcpy(o->path, path);
diff --git a/src/buf_text.c b/src/buf_text.c
index cb3661edb..864e39cab 100644
--- a/src/buf_text.c
+++ b/src/buf_text.c
@@ -13,7 +13,7 @@ int git_buf_text_puts_escaped(
const char *esc_with)
{
const char *scan;
- size_t total = 0, esc_len = strlen(esc_with), count;
+ size_t total = 0, esc_len = strlen(esc_with), count, alloclen;
if (!string)
return 0;
@@ -29,7 +29,8 @@ int git_buf_text_puts_escaped(
scan += count;
}
- if (git_buf_grow(buf, buf->size + total + 1) < 0)
+ GITERR_CHECK_ALLOC_ADD(&alloclen, total, 1);
+ if (git_buf_grow_by(buf, alloclen) < 0)
return -1;
for (scan = string; *scan; ) {
@@ -65,6 +66,7 @@ int git_buf_text_crlf_to_lf(git_buf *tgt, const git_buf *src)
const char *scan = src->ptr;
const char *scan_end = src->ptr + src->size;
const char *next = memchr(scan, '\r', src->size);
+ size_t new_size;
char *out;
assert(tgt != src);
@@ -73,8 +75,10 @@ int git_buf_text_crlf_to_lf(git_buf *tgt, const git_buf *src)
return git_buf_set(tgt, src->ptr, src->size);
/* reduce reallocs while in the loop */
- if (git_buf_grow(tgt, src->size + 1) < 0)
+ GITERR_CHECK_ALLOC_ADD(&new_size, src->size, 1);
+ if (git_buf_grow(tgt, new_size) < 0)
return -1;
+
out = tgt->ptr;
tgt->size = 0;
@@ -110,6 +114,7 @@ int git_buf_text_lf_to_crlf(git_buf *tgt, const git_buf *src)
const char *end = start + src->size;
const char *scan = start;
const char *next = memchr(scan, '\n', src->size);
+ size_t alloclen;
assert(tgt != src);
@@ -117,13 +122,14 @@ int git_buf_text_lf_to_crlf(git_buf *tgt, const git_buf *src)
return git_buf_set(tgt, src->ptr, src->size);
/* attempt to reduce reallocs while in the loop */
- if (git_buf_grow(tgt, src->size + (src->size >> 4) + 1) < 0)
+ GITERR_CHECK_ALLOC_ADD(&alloclen, src->size, src->size >> 4);
+ GITERR_CHECK_ALLOC_ADD(&alloclen, alloclen, 1);
+ if (git_buf_grow(tgt, alloclen) < 0)
return -1;
tgt->size = 0;
for (; next; scan = next + 1, next = memchr(scan, '\n', end - scan)) {
size_t copylen = next - scan;
- size_t needsize = tgt->size + copylen + 2 + 1;
/* if we find mixed line endings, bail */
if (next > start && next[-1] == '\r') {
@@ -131,7 +137,8 @@ int git_buf_text_lf_to_crlf(git_buf *tgt, const git_buf *src)
return GIT_PASSTHROUGH;
}
- if (tgt->asize < needsize && git_buf_grow(tgt, needsize) < 0)
+ GITERR_CHECK_ALLOC_ADD(&alloclen, copylen, 3);
+ if (git_buf_grow_by(tgt, alloclen) < 0)
return -1;
if (next > scan) {
diff --git a/src/buffer.c b/src/buffer.c
index 8013457c5..3deb0329c 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -63,6 +63,14 @@ int git_buf_try_grow(
/* round allocation up to multiple of 8 */
new_size = (new_size + 7) & ~7;
+ if (new_size < buf->size) {
+ if (mark_oom)
+ buf->ptr = git_buf__oom;
+
+ giterr_set_oom();
+ return -1;
+ }
+
new_ptr = git__realloc(new_ptr, new_size);
if (!new_ptr) {
@@ -93,6 +101,18 @@ int git_buf_grow(git_buf *buffer, size_t target_size)
return git_buf_try_grow(buffer, target_size, true, true);
}
+int git_buf_grow_by(git_buf *buffer, size_t additional_size)
+{
+ size_t newsize;
+
+ if (GIT_ADD_SIZET_OVERFLOW(&newsize, buffer->size, additional_size)) {
+ buffer->ptr = git_buf__oom;
+ return -1;
+ }
+
+ return git_buf_try_grow(buffer, newsize, true, true);
+}
+
void git_buf_free(git_buf *buf)
{
if (!buf) return;
@@ -127,11 +147,14 @@ void git_buf_clear(git_buf *buf)
int git_buf_set(git_buf *buf, const void *data, size_t len)
{
+ size_t alloclen;
+
if (len == 0 || data == NULL) {
git_buf_clear(buf);
} else {
if (data != buf->ptr) {
- ENSURE_SIZE(buf, len + 1);
+ GITERR_CHECK_ALLOC_ADD(&alloclen, len, 1);
+ ENSURE_SIZE(buf, alloclen);
memmove(buf->ptr, data, len);
}
@@ -160,7 +183,9 @@ int git_buf_sets(git_buf *buf, const char *string)
int git_buf_putc(git_buf *buf, char c)
{
- ENSURE_SIZE(buf, buf->size + 2);
+ size_t new_size;
+ GITERR_CHECK_ALLOC_ADD(&new_size, buf->size, 2);
+ ENSURE_SIZE(buf, new_size);
buf->ptr[buf->size++] = c;
buf->ptr[buf->size] = '\0';
return 0;
@@ -168,7 +193,10 @@ int git_buf_putc(git_buf *buf, char c)
int git_buf_putcn(git_buf *buf, char c, size_t len)
{
- ENSURE_SIZE(buf, buf->size + len + 1);
+ size_t new_size;
+ GITERR_CHECK_ALLOC_ADD(&new_size, buf->size, len);
+ GITERR_CHECK_ALLOC_ADD(&new_size, new_size, 1);
+ ENSURE_SIZE(buf, new_size);
memset(buf->ptr + buf->size, c, len);
buf->size += len;
buf->ptr[buf->size] = '\0';
@@ -178,8 +206,13 @@ int git_buf_putcn(git_buf *buf, char c, size_t len)
int git_buf_put(git_buf *buf, const char *data, size_t len)
{
if (len) {
+ size_t new_size;
+
assert(data);
- ENSURE_SIZE(buf, buf->size + len + 1);
+
+ GITERR_CHECK_ALLOC_ADD(&new_size, buf->size, len);
+ GITERR_CHECK_ALLOC_ADD(&new_size, new_size, 1);
+ ENSURE_SIZE(buf, new_size);
memmove(buf->ptr + buf->size, data, len);
buf->size += len;
buf->ptr[buf->size] = '\0';
@@ -201,8 +234,13 @@ int git_buf_encode_base64(git_buf *buf, const char *data, size_t len)
size_t extra = len % 3;
uint8_t *write, a, b, c;
const uint8_t *read = (const uint8_t *)data;
+ size_t blocks = (len / 3) + !!extra, alloclen;
+
+ GITERR_CHECK_ALLOC_ADD(&blocks, blocks, 1);
+ GITERR_CHECK_ALLOC_MULTIPLY(&alloclen, blocks, 4);
+ GITERR_CHECK_ALLOC_ADD(&alloclen, alloclen, buf->size);
- ENSURE_SIZE(buf, buf->size + 4 * ((len / 3) + !!extra) + 1);
+ ENSURE_SIZE(buf, alloclen);
write = (uint8_t *)&buf->ptr[buf->size];
/* convert each run of 3 bytes into 4 output bytes */
@@ -253,10 +291,12 @@ int git_buf_decode_base64(git_buf *buf, const char *base64, size_t len)
{
size_t i;
int8_t a, b, c, d;
- size_t orig_size = buf->size;
+ size_t orig_size = buf->size, new_size;
assert(len % 4 == 0);
- ENSURE_SIZE(buf, buf->size + (len / 4 * 3) + 1);
+ GITERR_CHECK_ALLOC_ADD(&new_size, (len / 4 * 3), buf->size);
+ GITERR_CHECK_ALLOC_ADD(&new_size, new_size, 1);
+ ENSURE_SIZE(buf, new_size);
for (i = 0; i < len; i += 4) {
if ((a = BASE64_DECODE_VALUE(base64[i])) < 0 ||
@@ -284,7 +324,13 @@ static const char b85str[] =
int git_buf_encode_base85(git_buf *buf, const char *data, size_t len)
{
- ENSURE_SIZE(buf, buf->size + (5 * ((len / 4) + !!(len % 4))) + 1);
+ size_t blocks = (len / 4) + !!(len % 4), alloclen;
+
+ GITERR_CHECK_ALLOC_MULTIPLY(&alloclen, blocks, 5);
+ GITERR_CHECK_ALLOC_ADD(&alloclen, alloclen, buf->size);
+ GITERR_CHECK_ALLOC_ADD(&alloclen, alloclen, 1);
+
+ ENSURE_SIZE(buf, alloclen);
while (len) {
uint32_t acc = 0;
@@ -317,9 +363,11 @@ int git_buf_encode_base85(git_buf *buf, const char *data, size_t len)
int git_buf_vprintf(git_buf *buf, const char *format, va_list ap)
{
+ size_t expected_size, new_size;
int len;
- const size_t expected_size = buf->size + (strlen(format) * 2);
+ GITERR_CHECK_ALLOC_MULTIPLY(&expected_size, strlen(format), 2);
+ GITERR_CHECK_ALLOC_ADD(&expected_size, expected_size, buf->size);
ENSURE_SIZE(buf, expected_size);
while (1) {
@@ -345,7 +393,9 @@ int git_buf_vprintf(git_buf *buf, const char *format, va_list ap)
break;
}
- ENSURE_SIZE(buf, buf->size + len + 1);
+ GITERR_CHECK_ALLOC_ADD(&new_size, buf->size, len);
+ GITERR_CHECK_ALLOC_ADD(&new_size, new_size, 1);
+ ENSURE_SIZE(buf, new_size);
}
return 0;
@@ -472,16 +522,20 @@ int git_buf_join_n(git_buf *buf, char separator, int nbuf, ...)
continue;
segment_len = strlen(segment);
- total_size += segment_len;
+
+ GITERR_CHECK_ALLOC_ADD(&total_size, total_size, segment_len);
+
if (segment_len == 0 || segment[segment_len - 1] != separator)
- ++total_size; /* space for separator */
+ GITERR_CHECK_ALLOC_ADD(&total_size, total_size, 1);
}
va_end(ap);
/* expand buffer if needed */
if (total_size == 0)
return 0;
- if (git_buf_grow(buf, buf->size + total_size + 1) < 0)
+
+ GITERR_CHECK_ALLOC_ADD(&total_size, total_size, 1);
+ if (git_buf_grow_by(buf, total_size) < 0)
return -1;
out = buf->ptr + buf->size;
@@ -542,6 +596,7 @@ int git_buf_join(
{
size_t strlen_a = str_a ? strlen(str_a) : 0;
size_t strlen_b = strlen(str_b);
+ size_t alloc_len;
int need_sep = 0;
ssize_t offset_a = -1;
@@ -559,7 +614,10 @@ int git_buf_join(
if (str_a >= buf->ptr && str_a < buf->ptr + buf->size)
offset_a = str_a - buf->ptr;
- if (git_buf_grow(buf, strlen_a + strlen_b + need_sep + 1) < 0)
+ GITERR_CHECK_ALLOC_ADD(&alloc_len, strlen_a, strlen_b);
+ GITERR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, need_sep);
+ GITERR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 1);
+ if (git_buf_grow(buf, alloc_len) < 0)
return -1;
assert(buf->ptr);
@@ -587,7 +645,10 @@ int git_buf_join3(
const char *str_b,
const char *str_c)
{
- size_t len_a = strlen(str_a), len_b = strlen(str_b), len_c = strlen(str_c);
+ size_t len_a = strlen(str_a),
+ len_b = strlen(str_b),
+ len_c = strlen(str_c),
+ len_total;
int sep_a = 0, sep_b = 0;
char *tgt;
@@ -607,7 +668,12 @@ int git_buf_join3(
sep_b = (str_b[len_b - 1] != separator);
}
- if (git_buf_grow(buf, len_a + sep_a + len_b + sep_b + len_c + 1) < 0)
+ GITERR_CHECK_ALLOC_ADD(&len_total, len_a, sep_a);
+ GITERR_CHECK_ALLOC_ADD(&len_total, len_total, len_b);
+ GITERR_CHECK_ALLOC_ADD(&len_total, len_total, sep_b);
+ GITERR_CHECK_ALLOC_ADD(&len_total, len_total, len_c);
+ GITERR_CHECK_ALLOC_ADD(&len_total, len_total, 1);
+ if (git_buf_grow(buf, len_total) < 0)
return -1;
tgt = buf->ptr;
@@ -660,22 +726,27 @@ int git_buf_splice(
const char *data,
size_t nb_to_insert)
{
- assert(buf &&
- where <= git_buf_len(buf) &&
- where + nb_to_remove <= git_buf_len(buf));
+ char *splice_loc;
+ size_t new_size, alloc_size;
+
+ assert(buf && where <= buf->size && nb_to_remove <= buf->size - where);
+
+ splice_loc = buf->ptr + where;
/* Ported from git.git
* https://github.com/git/git/blob/16eed7c/strbuf.c#L159-176
*/
- ENSURE_SIZE(buf, buf->size + nb_to_insert - nb_to_insert + 1);
+ GITERR_CHECK_ALLOC_ADD(&new_size, (buf->size - nb_to_remove), nb_to_insert);
+ GITERR_CHECK_ALLOC_ADD(&alloc_size, new_size, 1);
+ ENSURE_SIZE(buf, alloc_size);
- memmove(buf->ptr + where + nb_to_insert,
- buf->ptr + where + nb_to_remove,
- buf->size - where - nb_to_remove);
+ memmove(splice_loc + nb_to_insert,
+ splice_loc + nb_to_remove,
+ buf->size - where - nb_to_remove);
- memcpy(buf->ptr + where, data, nb_to_insert);
+ memcpy(splice_loc, data, nb_to_insert);
- buf->size = buf->size + nb_to_insert - nb_to_remove;
+ buf->size = new_size;
buf->ptr[buf->size] = '\0';
return 0;
}
diff --git a/src/buffer.h b/src/buffer.h
index 8ee4b532c..52342e309 100644
--- a/src/buffer.h
+++ b/src/buffer.h
@@ -37,6 +37,18 @@ GIT_INLINE(bool) git_buf_is_allocated(const git_buf *buf)
extern void git_buf_init(git_buf *buf, size_t initial_size);
/**
+ * Resize the buffer allocation to make more space.
+ *
+ * This will attempt to grow the buffer to accommodate the additional size.
+ * It is similar to `git_buf_grow`, but performs the new size calculation,
+ * checking for overflow.
+ *
+ * Like `git_buf_grow`, if this is a user-supplied buffer, this will allocate
+ * a new buffer.
+ */
+extern int git_buf_grow_by(git_buf *buffer, size_t additional_size);
+
+/**
* Attempt to grow the buffer to hold at least `target_size` bytes.
*
* If the allocation fails, this will return an error. If `mark_oom` is true,
diff --git a/src/checkout.c b/src/checkout.c
index 3f65a9ed7..880af3dff 100644
--- a/src/checkout.c
+++ b/src/checkout.c
@@ -32,7 +32,7 @@
#include "pool.h"
#include "strmap.h"
-GIT__USE_STRMAP;
+GIT__USE_STRMAP
/* See docs/checkout-internals.md for more information */
@@ -2375,6 +2375,9 @@ cleanup:
return error;
}
+#define CHECKOUT_INDEX_DONT_WRITE_MASK \
+ (GIT_CHECKOUT_DONT_UPDATE_INDEX | GIT_CHECKOUT_DONT_WRITE_INDEX)
+
int git_checkout_iterator(
git_iterator *target,
git_index *index,
@@ -2481,7 +2484,7 @@ int git_checkout_iterator(
cleanup:
if (!error && data.index != NULL &&
- (data.strategy & GIT_CHECKOUT_DONT_UPDATE_INDEX) == 0)
+ (data.strategy & CHECKOUT_INDEX_DONT_WRITE_MASK) == 0)
error = git_index_write(data.index);
git_diff_free(data.diff);
diff --git a/src/cherrypick.c b/src/cherrypick.c
index e58d0ab4c..ebc9fcdd8 100644
--- a/src/cherrypick.c
+++ b/src/cherrypick.c
@@ -10,6 +10,7 @@
#include "filebuf.h"
#include "merge.h"
#include "vector.h"
+#include "index.h"
#include "git2/types.h"
#include "git2/merge.h"
@@ -171,7 +172,8 @@ int git_cherrypick(
char commit_oidstr[GIT_OID_HEXSZ + 1];
const char *commit_msg, *commit_summary;
git_buf their_label = GIT_BUF_INIT;
- git_index *index_new = NULL;
+ git_index *index = NULL;
+ git_indexwriter indexwriter = GIT_INDEXWRITER_INIT;
int error = 0;
assert(repo && commit);
@@ -192,21 +194,25 @@ int git_cherrypick(
if ((error = write_merge_msg(repo, commit_msg)) < 0 ||
(error = git_buf_printf(&their_label, "%.7s... %s", commit_oidstr, commit_summary)) < 0 ||
(error = cherrypick_normalize_opts(repo, &opts, given_opts, git_buf_cstr(&their_label))) < 0 ||
+ (error = git_indexwriter_init_for_operation(&indexwriter, repo, &opts.checkout_opts.checkout_strategy)) < 0 ||
(error = write_cherrypick_head(repo, commit_oidstr)) < 0 ||
(error = git_repository_head(&our_ref, repo)) < 0 ||
(error = git_reference_peel((git_object **)&our_commit, our_ref, GIT_OBJ_COMMIT)) < 0 ||
- (error = git_cherrypick_commit(&index_new, repo, commit, our_commit, opts.mainline, &opts.merge_opts)) < 0 ||
- (error = git_merge__check_result(repo, index_new)) < 0 ||
- (error = git_merge__append_conflicts_to_merge_msg(repo, index_new)) < 0 ||
- (error = git_checkout_index(repo, index_new, &opts.checkout_opts)) < 0)
+ (error = git_cherrypick_commit(&index, repo, commit, our_commit, opts.mainline, &opts.merge_opts)) < 0 ||
+ (error = git_merge__check_result(repo, index)) < 0 ||
+ (error = git_merge__append_conflicts_to_merge_msg(repo, index)) < 0 ||
+ (error = git_checkout_index(repo, index, &opts.checkout_opts)) < 0 ||
+ (error = git_indexwriter_commit(&indexwriter)) < 0)
goto on_error;
+
goto done;
on_error:
cherrypick_state_cleanup(repo);
done:
- git_index_free(index_new);
+ git_indexwriter_cleanup(&indexwriter);
+ git_index_free(index);
git_commit_free(our_commit);
git_reference_free(our_ref);
git_buf_free(&their_label);
diff --git a/src/commit.c b/src/commit.c
index 78c4b9de3..beb2c64c3 100644
--- a/src/commit.c
+++ b/src/commit.c
@@ -400,7 +400,7 @@ GIT_COMMIT_GETTER(const char *, raw_header, commit->raw_header)
GIT_COMMIT_GETTER(git_time_t, time, commit->committer->when.time)
GIT_COMMIT_GETTER(int, time_offset, commit->committer->when.offset)
GIT_COMMIT_GETTER(unsigned int, parentcount, (unsigned int)git_array_size(commit->parent_ids))
-GIT_COMMIT_GETTER(const git_oid *, tree_id, &commit->tree_id);
+GIT_COMMIT_GETTER(const git_oid *, tree_id, &commit->tree_id)
const char *git_commit_message(const git_commit *commit)
{
diff --git a/src/common.h b/src/common.h
index 4b4a99775..98109ae3a 100644
--- a/src/common.h
+++ b/src/common.h
@@ -17,6 +17,11 @@
# define GIT_INLINE(type) static inline type
#endif
+/** Support for gcc/clang __has_builtin intrinsic */
+#ifndef __has_builtin
+# define __has_builtin(x) 0
+#endif
+
#include <assert.h>
#include <errno.h>
#include <limits.h>
@@ -58,6 +63,7 @@
#include "git2/types.h"
#include "git2/errors.h"
#include "thread-utils.h"
+#include "integer.h"
#include <regex.h>
@@ -174,6 +180,23 @@ GIT_INLINE(void) git__init_structure(void *structure, size_t len, unsigned int v
GITERR_CHECK_VERSION(&(VERSION), _tmpl.version, #TYPE); \
memcpy((PTR), &_tmpl, sizeof(_tmpl)); } while (0)
+
+/** Check for additive overflow, setting an error if would occur. */
+#define GIT_ADD_SIZET_OVERFLOW(out, one, two) \
+ (git__add_sizet_overflow(out, one, two) ? (giterr_set_oom(), 1) : 0)
+
+/** Check for additive overflow, setting an error if would occur. */
+#define GIT_MULTIPLY_SIZET_OVERFLOW(out, nelem, elsize) \
+ (git__multiply_sizet_overflow(out, nelem, elsize) ? (giterr_set_oom(), 1) : 0)
+
+/** Check for additive overflow, failing if it would occur. */
+#define GITERR_CHECK_ALLOC_ADD(out, one, two) \
+ if (GIT_ADD_SIZET_OVERFLOW(out, one, two)) { return -1; }
+
+/** Check for multiplicative overflow, failing if it would occur. */
+#define GITERR_CHECK_ALLOC_MULTIPLY(out, nelem, elsize) \
+ if (GIT_MULTIPLY_SIZET_OVERFLOW(out, nelem, elsize)) { return -1; }
+
/* NOTE: other giterr functions are in the public errors.h header file */
#include "util.h"
diff --git a/src/config_file.c b/src/config_file.c
index 268cced09..168dd5483 100644
--- a/src/config_file.c
+++ b/src/config_file.c
@@ -21,7 +21,7 @@
#include <sys/types.h>
#include <regex.h>
-GIT__USE_STRMAP;
+GIT__USE_STRMAP
typedef struct cvar_t {
struct cvar_t *next;
@@ -885,7 +885,7 @@ static char *reader_readline(struct reader *reader, bool skip_whitespace)
{
char *line = NULL;
char *line_src, *line_end;
- size_t line_len;
+ size_t line_len, alloc_len;
line_src = reader->read_ptr;
@@ -903,9 +903,10 @@ static char *reader_readline(struct reader *reader, bool skip_whitespace)
line_len = line_end - line_src;
- line = git__malloc(line_len + 1);
- if (line == NULL)
+ if (GIT_ADD_SIZET_OVERFLOW(&alloc_len, line_len, 1) ||
+ (line = git__malloc(alloc_len)) == NULL) {
return NULL;
+ }
memcpy(line, line_src, line_len);
@@ -958,6 +959,8 @@ static int parse_section_header_ext(struct reader *reader, const char *line, con
int c, rpos;
char *first_quote, *last_quote;
git_buf buf = GIT_BUF_INIT;
+ size_t quoted_len, alloc_len, base_name_len = strlen(base_name);
+
/*
* base_name is what came before the space. We should be at the
* first quotation mark, except for now, line isn't being kept in
@@ -966,13 +969,17 @@ static int parse_section_header_ext(struct reader *reader, const char *line, con
first_quote = strchr(line, '"');
last_quote = strrchr(line, '"');
+ quoted_len = last_quote - first_quote;
- if (last_quote - first_quote == 0) {
+ if (quoted_len == 0) {
set_parse_error(reader, 0, "Missing closing quotation mark in section header");
return -1;
}
- git_buf_grow(&buf, strlen(base_name) + last_quote - first_quote + 2);
+ GITERR_CHECK_ALLOC_ADD(&alloc_len, base_name_len, quoted_len);
+ GITERR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 2);
+
+ git_buf_grow(&buf, alloc_len);
git_buf_printf(&buf, "%s.", base_name);
rpos = 0;
@@ -1029,6 +1036,7 @@ static int parse_section_header(struct reader *reader, char **section_out)
int name_length, c, pos;
int result;
char *line;
+ size_t line_len;
line = reader_readline(reader, true);
if (line == NULL)
@@ -1042,7 +1050,8 @@ static int parse_section_header(struct reader *reader, char **section_out)
return -1;
}
- name = (char *)git__malloc((size_t)(name_end - line) + 1);
+ GITERR_CHECK_ALLOC_ADD(&line_len, (size_t)(name_end - line), 1);
+ name = git__malloc(line_len);
GITERR_CHECK_ALLOC(name);
name_length = 0;
@@ -1604,11 +1613,15 @@ static char *escape_value(const char *ptr)
/* '\"' -> '"' etc */
static char *fixup_line(const char *ptr, int quote_count)
{
- char *str = git__malloc(strlen(ptr) + 1);
- char *out = str, *esc;
+ char *str, *out, *esc;
+ size_t ptr_len = strlen(ptr), alloc_len;
- if (str == NULL)
+ if (GIT_ADD_SIZET_OVERFLOW(&alloc_len, ptr_len, 1) ||
+ (str = git__malloc(alloc_len)) == NULL) {
return NULL;
+ }
+
+ out = str;
while (*ptr != '\0') {
if (*ptr == '"') {
diff --git a/src/delta-apply.c b/src/delta-apply.c
index a39c7af5c..89745faa0 100644
--- a/src/delta-apply.c
+++ b/src/delta-apply.c
@@ -57,7 +57,7 @@ int git__delta_apply(
size_t delta_len)
{
const unsigned char *delta_end = delta + delta_len;
- size_t base_sz, res_sz;
+ size_t base_sz, res_sz, alloc_sz;
unsigned char *res_dp;
/* Check that the base size matches the data we were given;
@@ -74,7 +74,8 @@ int git__delta_apply(
return -1;
}
- res_dp = git__malloc(res_sz + 1);
+ GITERR_CHECK_ALLOC_ADD(&alloc_sz, res_sz, 1);
+ res_dp = git__malloc(alloc_sz);
GITERR_CHECK_ALLOC(res_dp);
res_dp[res_sz] = '\0';
diff --git a/src/delta.c b/src/delta.c
index 8375a2c4d..d72d820d8 100644
--- a/src/delta.c
+++ b/src/delta.c
@@ -119,6 +119,29 @@ struct git_delta_index {
struct index_entry *hash[GIT_FLEX_ARRAY];
};
+static int lookup_index_alloc(
+ void **out, unsigned long *out_len, size_t entries, size_t hash_count)
+{
+ size_t entries_len, hash_len, index_len;
+
+ GITERR_CHECK_ALLOC_MULTIPLY(&entries_len, entries, sizeof(struct index_entry));
+ GITERR_CHECK_ALLOC_MULTIPLY(&hash_len, hash_count, sizeof(struct index_entry *));
+
+ GITERR_CHECK_ALLOC_ADD(&index_len, sizeof(struct git_delta_index), entries_len);
+ GITERR_CHECK_ALLOC_ADD(&index_len, index_len, hash_len);
+
+ if (!git__is_ulong(index_len)) {
+ giterr_set(GITERR_NOMEMORY, "Overly large delta");
+ return -1;
+ }
+
+ *out = git__malloc(index_len);
+ GITERR_CHECK_ALLOC(*out);
+
+ *out_len = index_len;
+ return 0;
+}
+
struct git_delta_index *
git_delta_create_index(const void *buf, unsigned long bufsize)
{
@@ -148,13 +171,9 @@ git_delta_create_index(const void *buf, unsigned long bufsize)
hsize = 1 << i;
hmask = hsize - 1;
- /* allocate lookup index */
- memsize = sizeof(*index) +
- sizeof(*hash) * hsize +
- sizeof(*entry) * entries;
- mem = git__malloc(memsize);
- if (!mem)
+ if (lookup_index_alloc(&mem, &memsize, entries, hsize) < 0)
return NULL;
+
index = mem;
mem = index->hash;
hash = mem;
diff --git a/src/diff.c b/src/diff.c
index e23d3891f..07eae03e7 100644
--- a/src/diff.c
+++ b/src/diff.c
@@ -1527,6 +1527,7 @@ int git_diff_format_email(
char *summary = NULL, *loc = NULL;
bool ignore_marker;
unsigned int format_flags = 0;
+ size_t allocsize;
int error;
assert(out && diff && opts);
@@ -1558,8 +1559,10 @@ int git_diff_format_email(
goto on_error;
}
- summary = git__calloc(offset + 1, sizeof(char));
+ GITERR_CHECK_ALLOC_ADD(&allocsize, offset, 1);
+ summary = git__calloc(allocsize, sizeof(char));
GITERR_CHECK_ALLOC(summary);
+
strncpy(summary, opts->summary, offset);
}
diff --git a/src/diff_driver.c b/src/diff_driver.c
index c3c5f365b..7313ab573 100644
--- a/src/diff_driver.c
+++ b/src/diff_driver.c
@@ -17,7 +17,7 @@
#include "config.h"
#include "repository.h"
-GIT__USE_STRMAP;
+GIT__USE_STRMAP
typedef enum {
DIFF_DRIVER_AUTO = 0,
@@ -158,6 +158,30 @@ static git_diff_driver_registry *git_repository_driver_registry(
return repo->diff_drivers;
}
+static int diff_driver_alloc(
+ git_diff_driver **out, size_t *namelen_out, const char *name)
+{
+ git_diff_driver *driver;
+ size_t driverlen = sizeof(git_diff_driver),
+ namelen = strlen(name),
+ alloclen;
+
+ GITERR_CHECK_ALLOC_ADD(&alloclen, driverlen, namelen);
+ GITERR_CHECK_ALLOC_ADD(&alloclen, alloclen, 1);
+
+ driver = git__calloc(1, alloclen);
+ GITERR_CHECK_ALLOC(driver);
+
+ memcpy(driver->name, name, namelen);
+
+ *out = driver;
+
+ if (namelen_out)
+ *namelen_out = namelen;
+
+ return 0;
+}
+
static int git_diff_driver_builtin(
git_diff_driver **out,
git_diff_driver_registry *reg,
@@ -166,7 +190,7 @@ static int git_diff_driver_builtin(
int error = 0;
git_diff_driver_definition *ddef = NULL;
git_diff_driver *drv = NULL;
- size_t namelen, idx;
+ size_t idx;
for (idx = 0; idx < ARRAY_SIZE(builtin_defs); ++idx) {
if (!strcasecmp(driver_name, builtin_defs[idx].name)) {
@@ -177,13 +201,10 @@ static int git_diff_driver_builtin(
if (!ddef)
goto done;
- namelen = strlen(ddef->name);
-
- drv = git__calloc(1, sizeof(git_diff_driver) + namelen + 1);
- GITERR_CHECK_ALLOC(drv);
+ if ((error = diff_driver_alloc(&drv, NULL, ddef->name)) < 0)
+ goto done;
drv->type = DIFF_DRIVER_PATTERNLIST;
- memcpy(drv->name, ddef->name, namelen);
if (ddef->fns &&
(error = diff_driver_add_patterns(
@@ -217,9 +238,9 @@ static int git_diff_driver_load(
int error = 0;
git_diff_driver_registry *reg;
git_diff_driver *drv = NULL;
- size_t namelen = strlen(driver_name);
+ size_t namelen;
khiter_t pos;
- git_config *cfg;
+ git_config *cfg = NULL;
git_buf name = GIT_BUF_INIT;
const git_config_entry *ce;
bool found_driver = false;
@@ -233,10 +254,10 @@ static int git_diff_driver_load(
return 0;
}
- drv = git__calloc(1, sizeof(git_diff_driver) + namelen + 1);
- GITERR_CHECK_ALLOC(drv);
+ if ((error = diff_driver_alloc(&drv, &namelen, driver_name)) < 0)
+ goto done;
+
drv->type = DIFF_DRIVER_AUTO;
- memcpy(drv->name, driver_name, namelen);
/* if you can't read config for repo, just use default driver */
if (git_repository_config_snapshot(&cfg, repo) < 0) {
diff --git a/src/diff_patch.c b/src/diff_patch.c
index a15107753..1c4c0e8b8 100644
--- a/src/diff_patch.c
+++ b/src/diff_patch.c
@@ -388,8 +388,13 @@ static int diff_patch_with_delta_alloc(
diff_patch_with_delta *pd;
size_t old_len = *old_path ? strlen(*old_path) : 0;
size_t new_len = *new_path ? strlen(*new_path) : 0;
+ size_t alloc_len;
- *out = pd = git__calloc(1, sizeof(*pd) + old_len + new_len + 2);
+ GITERR_CHECK_ALLOC_ADD(&alloc_len, sizeof(*pd), old_len);
+ GITERR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, new_len);
+ GITERR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 2);
+
+ *out = pd = git__calloc(1, alloc_len);
GITERR_CHECK_ALLOC(pd);
pd->patch.flags = GIT_DIFF_PATCH_ALLOCATED;
diff --git a/src/diff_tform.c b/src/diff_tform.c
index 9133a9b14..8ee568cf4 100644
--- a/src/diff_tform.c
+++ b/src/diff_tform.c
@@ -811,6 +811,7 @@ int git_diff_find_similar(
size_t num_deltas, num_srcs = 0, num_tgts = 0;
size_t tried_srcs = 0, tried_tgts = 0;
size_t num_rewrites = 0, num_updates = 0, num_bumped = 0;
+ size_t sigcache_size;
void **sigcache = NULL; /* cache of similarity metric file signatures */
diff_find_match *tgt2src = NULL;
diff_find_match *src2tgt = NULL;
@@ -831,7 +832,8 @@ int git_diff_find_similar(
if ((opts.flags & GIT_DIFF_FIND_ALL) == 0)
goto cleanup;
- sigcache = git__calloc(num_deltas * 2, sizeof(void *));
+ GITERR_CHECK_ALLOC_MULTIPLY(&sigcache_size, num_deltas, 2);
+ sigcache = git__calloc(sigcache_size, sizeof(void *));
GITERR_CHECK_ALLOC(sigcache);
/* Label rename sources and targets
diff --git a/src/filebuf.c b/src/filebuf.c
index 25f6e52ef..a35b59cf0 100644
--- a/src/filebuf.c
+++ b/src/filebuf.c
@@ -194,7 +194,7 @@ static int write_deflate(git_filebuf *file, void *source, size_t len)
int git_filebuf_open(git_filebuf *file, const char *path, int flags, mode_t mode)
{
int compression, error = -1;
- size_t path_len;
+ size_t path_len, alloc_len;
/* opening an already open buffer is a programming error;
* assert that this never happens instead of returning
@@ -271,7 +271,8 @@ int git_filebuf_open(git_filebuf *file, const char *path, int flags, mode_t mode
GITERR_CHECK_ALLOC(file->path_original);
/* create the locking path by appending ".lock" to the original */
- file->path_lock = git__malloc(path_len + GIT_FILELOCK_EXTLENGTH);
+ GITERR_CHECK_ALLOC_ADD(&alloc_len, path_len, GIT_FILELOCK_EXTLENGTH);
+ file->path_lock = git__malloc(alloc_len);
GITERR_CHECK_ALLOC(file->path_lock);
memcpy(file->path_lock, file->path_original, path_len);
@@ -407,8 +408,8 @@ int git_filebuf_reserve(git_filebuf *file, void **buffer, size_t len)
int git_filebuf_printf(git_filebuf *file, const char *format, ...)
{
va_list arglist;
- size_t space_left;
- int len, res;
+ size_t space_left, len, alloclen;
+ int written, res;
char *tmp_buffer;
ENSURE_BUF_OK(file);
@@ -417,15 +418,16 @@ int git_filebuf_printf(git_filebuf *file, const char *format, ...)
do {
va_start(arglist, format);
- len = p_vsnprintf((char *)file->buffer + file->buf_pos, space_left, format, arglist);
+ written = p_vsnprintf((char *)file->buffer + file->buf_pos, space_left, format, arglist);
va_end(arglist);
- if (len < 0) {
+ if (written < 0) {
file->last_error = BUFERR_MEM;
return -1;
}
- if ((size_t)len + 1 <= space_left) {
+ len = written;
+ if (len + 1 <= space_left) {
file->buf_pos += len;
return 0;
}
@@ -435,19 +437,19 @@ int git_filebuf_printf(git_filebuf *file, const char *format, ...)
space_left = file->buf_size - file->buf_pos;
- } while ((size_t)len + 1 <= space_left);
+ } while (len + 1 <= space_left);
- tmp_buffer = git__malloc(len + 1);
- if (!tmp_buffer) {
+ if (GIT_ADD_SIZET_OVERFLOW(&alloclen, len, 1) ||
+ !(tmp_buffer = git__malloc(alloclen))) {
file->last_error = BUFERR_MEM;
return -1;
}
va_start(arglist, format);
- len = p_vsnprintf(tmp_buffer, len + 1, format, arglist);
+ written = p_vsnprintf(tmp_buffer, len + 1, format, arglist);
va_end(arglist);
- if (len < 0) {
+ if (written < 0) {
git__free(tmp_buffer);
file->last_error = BUFERR_MEM;
return -1;
diff --git a/src/fileops.c b/src/fileops.c
index 73da4304b..ec536bf29 100644
--- a/src/fileops.c
+++ b/src/fileops.c
@@ -13,7 +13,7 @@
#include "win32/findfile.h"
#endif
-GIT__USE_STRMAP;
+GIT__USE_STRMAP
int git_futils_mkpath2file(const char *file_path, const mode_t mode)
{
@@ -124,10 +124,17 @@ mode_t git_futils_canonical_mode(mode_t raw_mode)
int git_futils_readbuffer_fd(git_buf *buf, git_file fd, size_t len)
{
ssize_t read_size = 0;
+ size_t alloc_len;
git_buf_clear(buf);
- if (git_buf_grow(buf, len + 1) < 0)
+ if (!git__is_ssizet(len)) {
+ giterr_set(GITERR_INVALID, "Read too large.");
+ return -1;
+ }
+
+ GITERR_CHECK_ALLOC_ADD(&alloc_len, len, 1);
+ if (git_buf_grow(buf, alloc_len) < 0)
return -1;
/* p_read loops internally to read len bytes */
@@ -449,7 +456,13 @@ int git_futils_mkdir_ext(
}
if (opts->dir_map && opts->pool) {
- char *cache_path = git_pool_malloc(opts->pool, make_path.size + 1);
+ char *cache_path;
+ size_t alloc_size;
+
+ GITERR_CHECK_ALLOC_ADD(&alloc_size, make_path.size, 1);
+ if (!git__is_uint32(alloc_size))
+ return -1;
+ cache_path = git_pool_malloc(opts->pool, (uint32_t)alloc_size);
GITERR_CHECK_ALLOC(cache_path);
memcpy(cache_path, make_path.ptr, make_path.size + 1);
@@ -708,7 +721,11 @@ static int cp_link(const char *from, const char *to, size_t link_size)
{
int error = 0;
ssize_t read_len;
- char *link_data = git__malloc(link_size + 1);
+ char *link_data;
+ size_t alloc_size;
+
+ GITERR_CHECK_ALLOC_ADD(&alloc_size, link_size, 1);
+ link_data = git__malloc(alloc_size);
GITERR_CHECK_ALLOC(link_data);
read_len = p_readlink(from, link_data, link_size);
diff --git a/src/filter.c b/src/filter.c
index 22eaf51a2..7b54a76c0 100644
--- a/src/filter.c
+++ b/src/filter.c
@@ -228,7 +228,7 @@ int git_filter_register(
const char *name, git_filter *filter, int priority)
{
git_filter_def *fdef;
- size_t nattr = 0, nmatch = 0;
+ size_t nattr = 0, nmatch = 0, alloc_len;
git_buf attrs = GIT_BUF_INIT;
assert(name && filter);
@@ -245,8 +245,11 @@ int git_filter_register(
if (filter_def_scan_attrs(&attrs, &nattr, &nmatch, filter->attributes) < 0)
return -1;
- fdef = git__calloc(
- sizeof(git_filter_def) + 2 * nattr * sizeof(char *), 1);
+ GITERR_CHECK_ALLOC_MULTIPLY(&alloc_len, nattr, 2);
+ GITERR_CHECK_ALLOC_MULTIPLY(&alloc_len, alloc_len, sizeof(char *));
+ GITERR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, sizeof(git_filter_def));
+
+ fdef = git__calloc(1, alloc_len);
GITERR_CHECK_ALLOC(fdef);
fdef->filter_name = git__strdup(name);
@@ -377,9 +380,12 @@ static int filter_list_new(
git_filter_list **out, const git_filter_source *src)
{
git_filter_list *fl = NULL;
- size_t pathlen = src->path ? strlen(src->path) : 0;
+ size_t pathlen = src->path ? strlen(src->path) : 0, alloclen;
+
+ GITERR_CHECK_ALLOC_ADD(&alloclen, sizeof(git_filter_list), pathlen);
+ GITERR_CHECK_ALLOC_ADD(&alloclen, alloclen, 1);
- fl = git__calloc(1, sizeof(git_filter_list) + pathlen + 1);
+ fl = git__calloc(1, alloclen);
GITERR_CHECK_ALLOC(fl);
if (src->path)
diff --git a/src/index.c b/src/index.c
index cbace3606..c122cdf6d 100644
--- a/src/index.c
+++ b/src/index.c
@@ -656,39 +656,15 @@ int git_index__changed_relative_to(
int git_index_write(git_index *index)
{
- git_filebuf file = GIT_FILEBUF_INIT;
+ git_indexwriter writer = GIT_INDEXWRITER_INIT;
int error;
- if (!index->index_file_path)
- return create_index_error(-1,
- "Failed to read index: The index is in-memory only");
+ if ((error = git_indexwriter_init(&writer, index)) == 0)
+ error = git_indexwriter_commit(&writer);
- if (index_sort_if_needed(index, true) < 0)
- return -1;
- git_vector_sort(&index->reuc);
+ git_indexwriter_cleanup(&writer);
- if ((error = git_filebuf_open(
- &file, index->index_file_path, GIT_FILEBUF_HASH_CONTENTS, GIT_INDEX_FILE_MODE)) < 0) {
- if (error == GIT_ELOCKED)
- giterr_set(GITERR_INDEX, "The index is locked. This might be due to a concurrent or crashed process");
-
- return error;
- }
-
- if ((error = write_index(index, &file)) < 0) {
- git_filebuf_cleanup(&file);
- return error;
- }
-
- if ((error = git_filebuf_commit(&file)) < 0)
- return error;
-
- if (git_futils_filestamp_check(&index->stamp, index->index_file_path) < 0)
- /* index could not be read from disk! */;
- else
- index->on_disk = 1;
-
- return 0;
+ return error;
}
const char * git_index_path(const git_index *index)
@@ -770,7 +746,7 @@ static int index_entry_create(
git_repository *repo,
const char *path)
{
- size_t pathlen = strlen(path);
+ size_t pathlen = strlen(path), alloclen;
struct entry_internal *entry;
if (!git_path_isvalid(repo, path,
@@ -779,7 +755,9 @@ static int index_entry_create(
return -1;
}
- entry = git__calloc(sizeof(struct entry_internal) + pathlen + 1, 1);
+ GITERR_CHECK_ALLOC_ADD(&alloclen, sizeof(struct entry_internal), pathlen);
+ GITERR_CHECK_ALLOC_ADD(&alloclen, alloclen, 1);
+ entry = git__calloc(1, alloclen);
GITERR_CHECK_ALLOC(entry);
entry->pathlen = pathlen;
@@ -826,9 +804,16 @@ static int index_entry_init(
static git_index_reuc_entry *reuc_entry_alloc(const char *path)
{
- size_t pathlen = strlen(path);
- struct reuc_entry_internal *entry =
- git__calloc(sizeof(struct reuc_entry_internal) + pathlen + 1, 1);
+ size_t pathlen = strlen(path),
+ structlen = sizeof(struct reuc_entry_internal),
+ alloclen;
+ struct reuc_entry_internal *entry;
+
+ if (GIT_ADD_SIZET_OVERFLOW(&alloclen, structlen, pathlen) ||
+ GIT_ADD_SIZET_OVERFLOW(&alloclen, alloclen, 1))
+ return NULL;
+
+ entry = git__calloc(1, alloclen);
if (!entry)
return NULL;
@@ -2686,3 +2671,91 @@ int git_index_snapshot_find(
{
return index_find_in_entries(out, entries, entry_srch, path, path_len, stage);
}
+
+int git_indexwriter_init(
+ git_indexwriter *writer,
+ git_index *index)
+{
+ int error;
+
+ GIT_REFCOUNT_INC(index);
+
+ writer->index = index;
+
+ if (!index->index_file_path)
+ return create_index_error(-1,
+ "Failed to write index: The index is in-memory only");
+
+ if ((error = git_filebuf_open(
+ &writer->file, index->index_file_path, GIT_FILEBUF_HASH_CONTENTS, GIT_INDEX_FILE_MODE)) < 0) {
+
+ if (error == GIT_ELOCKED)
+ giterr_set(GITERR_INDEX, "The index is locked. This might be due to a concurrent or crashed process");
+
+ return error;
+ }
+
+ writer->should_write = 1;
+
+ return 0;
+}
+
+int git_indexwriter_init_for_operation(
+ git_indexwriter *writer,
+ git_repository *repo,
+ unsigned int *checkout_strategy)
+{
+ git_index *index;
+ int error;
+
+ if ((error = git_repository_index__weakptr(&index, repo)) < 0 ||
+ (error = git_indexwriter_init(writer, index)) < 0)
+ return error;
+
+ writer->should_write = (*checkout_strategy & GIT_CHECKOUT_DONT_WRITE_INDEX) == 0;
+ *checkout_strategy |= GIT_CHECKOUT_DONT_WRITE_INDEX;
+
+ return 0;
+}
+
+int git_indexwriter_commit(git_indexwriter *writer)
+{
+ int error;
+
+ if (!writer->should_write)
+ return 0;
+
+ if (index_sort_if_needed(writer->index, true) < 0)
+ return -1;
+
+ git_vector_sort(&writer->index->reuc);
+
+ if ((error = write_index(writer->index, &writer->file)) < 0) {
+ git_indexwriter_cleanup(writer);
+ return error;
+ }
+
+ if ((error = git_filebuf_commit(&writer->file)) < 0)
+ return error;
+
+ if ((error = git_futils_filestamp_check(
+ &writer->index->stamp, writer->index->index_file_path)) < 0) {
+ giterr_set(GITERR_OS, "Could not read index timestamp");
+ return -1;
+ }
+
+ writer->index->on_disk = 1;
+
+ git_index_free(writer->index);
+ writer->index = NULL;
+
+ return 0;
+}
+
+void git_indexwriter_cleanup(git_indexwriter *writer)
+{
+ git_filebuf_cleanup(&writer->file);
+
+ git_index_free(writer->index);
+ writer->index = NULL;
+}
diff --git a/src/index.h b/src/index.h
index 2eb93fb17..6d2904fdc 100644
--- a/src/index.h
+++ b/src/index.h
@@ -94,4 +94,33 @@ extern int git_index_snapshot_find(
const char *path, size_t path_len, int stage);
+typedef struct {
+ git_index *index;
+ git_filebuf file;
+ unsigned int should_write:1;
+} git_indexwriter;
+
+#define GIT_INDEXWRITER_INIT { NULL, GIT_FILEBUF_INIT }
+
+/* Lock the index for eventual writing. */
+extern int git_indexwriter_init(git_indexwriter *writer, git_index *index);
+
+/* Lock the index for eventual writing by a repository operation: a merge,
+ * revert, cherry-pick or a rebase. Note that the given checkout strategy
+ * will be updated for the operation's use so that checkout will not write
+ * the index.
+ */
+extern int git_indexwriter_init_for_operation(
+ git_indexwriter *writer,
+ git_repository *repo,
+ unsigned int *checkout_strategy);
+
+/* Write the index and unlock it. */
+extern int git_indexwriter_commit(git_indexwriter *writer);
+
+/* Cleanup an index writing session, unlocking the file (if it is still
+ * locked and freeing any data structures.
+ */
+extern void git_indexwriter_cleanup(git_indexwriter *writer);
+
#endif
diff --git a/src/integer.h b/src/integer.h
new file mode 100644
index 000000000..8e86a48a5
--- /dev/null
+++ b/src/integer.h
@@ -0,0 +1,96 @@
+/*
+ * 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_integer_h__
+#define INCLUDE_integer_h__
+
+/** @return true if p fits into the range of a size_t */
+GIT_INLINE(int) git__is_sizet(git_off_t p)
+{
+ size_t r = (size_t)p;
+ return p == (git_off_t)r;
+}
+
+/** @return true if p fits into the range of an ssize_t */
+GIT_INLINE(int) git__is_ssizet(size_t p)
+{
+ ssize_t r = (ssize_t)p;
+ return p == (size_t)r;
+}
+
+/** @return true if p fits into the range of a uint32_t */
+GIT_INLINE(int) git__is_uint32(size_t p)
+{
+ uint32_t r = (uint32_t)p;
+ return p == (size_t)r;
+}
+
+/** @return true if p fits into the range of an unsigned long */
+GIT_INLINE(int) git__is_ulong(git_off_t p)
+{
+ unsigned long r = (unsigned long)p;
+ return p == (git_off_t)r;
+}
+
+/** @return true if p fits into the range of an int */
+GIT_INLINE(int) git__is_int(long long p)
+{
+ int r = (int)p;
+ return p == (long long)r;
+}
+
+/**
+ * Sets `one + two` into `out`, unless the arithmetic would overflow.
+ * @return true if the result fits in a `uint64_t`, false on overflow.
+ */
+GIT_INLINE(bool) git__add_uint64_overflow(uint64_t *out, uint64_t one, uint64_t two)
+{
+ if (UINT64_MAX - one < two)
+ return true;
+ *out = one + two;
+ return false;
+}
+
+/* Use clang/gcc compiler intrinsics whenever possible */
+#if (SIZE_MAX == UINT_MAX) && __has_builtin(__builtin_uadd_overflow)
+# define git__add_sizet_overflow(out, one, two) \
+ __builtin_uadd_overflow(one, two, out)
+# define git__multiply_sizet_overflow(out, one, two)
+ __builtin_umul_overflow(one, two, out)
+#elif (SIZE_MAX == ULONG_MAX) && __has_builtin(__builtin_uaddl_overflow)
+# define git__add_sizet_overflow(out, one, two) \
+ __builtin_uaddl_overflow(one, two, out)
+# define git__multiply_sizet_overflow(out, one, two) \
+ __builtin_umull_overflow(one, two, out)
+#else
+
+/**
+ * Sets `one + two` into `out`, unless the arithmetic would overflow.
+ * @return true if the result fits in a `size_t`, false on overflow.
+ */
+GIT_INLINE(bool) git__add_sizet_overflow(size_t *out, size_t one, size_t two)
+{
+ if (SIZE_MAX - one < two)
+ return true;
+ *out = one + two;
+ return false;
+}
+
+/**
+ * Sets `one * two` into `out`, unless the arithmetic would overflow.
+ * @return true if the result fits in a `size_t`, false on overflow.
+ */
+GIT_INLINE(bool) git__multiply_sizet_overflow(size_t *out, size_t one, size_t two)
+{
+ if (one && SIZE_MAX / one < two)
+ return true;
+ *out = one * two;
+ return false;
+}
+
+#endif
+
+#endif /* INCLUDE_integer_h__ */
diff --git a/src/iterator.c b/src/iterator.c
index 196adddbf..9ddacebd1 100644
--- a/src/iterator.c
+++ b/src/iterator.c
@@ -336,7 +336,7 @@ static int tree_iterator__push_frame(tree_iterator *ti)
{
int error = 0;
tree_iterator_frame *head = ti->head, *tf = NULL;
- size_t i, n_entries = 0;
+ size_t i, n_entries = 0, alloclen;
if (head->current >= head->n_entries || !head->entries[head->current]->tree)
return GIT_ITEROVER;
@@ -344,8 +344,10 @@ static int tree_iterator__push_frame(tree_iterator *ti)
for (i = head->current; i < head->next; ++i)
n_entries += git_tree_entrycount(head->entries[i]->tree);
- tf = git__calloc(sizeof(tree_iterator_frame) +
- n_entries * sizeof(tree_iterator_entry *), 1);
+ GITERR_CHECK_ALLOC_MULTIPLY(&alloclen, sizeof(tree_iterator_entry *), n_entries);
+ GITERR_CHECK_ALLOC_ADD(&alloclen, alloclen, sizeof(tree_iterator_frame));
+
+ tf = git__calloc(1, alloclen);
GITERR_CHECK_ALLOC(tf);
tf->n_entries = n_entries;
diff --git a/src/khash.h b/src/khash.h
index 242204464..818ac833b 100644
--- a/src/khash.h
+++ b/src/khash.h
@@ -46,6 +46,19 @@ int main() {
*/
/*
+ 2013-05-02 (0.2.8):
+
+ * Use quadratic probing. When the capacity is power of 2, stepping function
+ i*(i+1)/2 guarantees to traverse each bucket. It is better than double
+ hashing on cache performance and is more robust than linear probing.
+
+ In theory, double hashing should be more robust than quadratic probing.
+ However, my implementation is probably not for large hash tables, because
+ the second hash function is closely tied to the first hash function,
+ which reduce the effectiveness of double hashing.
+
+ Reference: http://research.cs.vt.edu/AVresearch/hashing/quadratic.php
+
2011-12-29 (0.2.7):
* Minor code clean up; no actual effect.
@@ -110,13 +123,13 @@ int main() {
Generic hash table library.
*/
-#define AC_VERSION_KHASH_H "0.2.6"
+#define AC_VERSION_KHASH_H "0.2.8"
#include <stdlib.h>
#include <string.h>
#include <limits.h>
-/* compipler specific configuration */
+/* compiler specific configuration */
#if UINT_MAX == 0xffffffffu
typedef unsigned int khint32_t;
@@ -130,11 +143,13 @@ typedef unsigned long khint64_t;
typedef unsigned long long khint64_t;
#endif
+#ifndef kh_inline
#ifdef _MSC_VER
#define kh_inline __inline
#else
#define kh_inline inline
#endif
+#endif /* kh_inline */
typedef khint32_t khint_t;
typedef khint_t khiter_t;
@@ -147,12 +162,6 @@ typedef khint_t khiter_t;
#define __ac_set_isboth_false(flag, i) (flag[i>>4]&=~(3ul<<((i&0xfU)<<1)))
#define __ac_set_isdel_true(flag, i) (flag[i>>4]|=1ul<<((i&0xfU)<<1))
-#ifdef KHASH_LINEAR
-#define __ac_inc(k, m) 1
-#else
-#define __ac_inc(k, m) (((k)>>3 ^ (k)<<3) | 1) & (m)
-#endif
-
#define __ac_fsize(m) ((m) < 16? 1 : (m)>>4)
#ifndef kroundup32
@@ -168,6 +177,9 @@ typedef khint_t khiter_t;
#ifndef krealloc
#define krealloc(P,Z) realloc(P,Z)
#endif
+#ifndef kreallocarray
+#define kreallocarray(P,N,Z) ((SIZE_MAX - N < Z) ? NULL : krealloc(P, (N*Z)))
+#endif
#ifndef kfree
#define kfree(P) free(P)
#endif
@@ -175,7 +187,7 @@ typedef khint_t khiter_t;
static const double __ac_HASH_UPPER = 0.77;
#define __KHASH_TYPE(name, khkey_t, khval_t) \
- typedef struct { \
+ typedef struct kh_##name##_s { \
khint_t n_buckets, size, n_occupied, upper_bound; \
khint32_t *flags; \
khkey_t *keys; \
@@ -213,19 +225,19 @@ static const double __ac_HASH_UPPER = 0.77;
SCOPE khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key) \
{ \
if (h->n_buckets) { \
- khint_t inc, k, i, last, mask; \
+ khint_t k, i, last, mask, step = 0; \
mask = h->n_buckets - 1; \
k = __hash_func(key); i = k & mask; \
- inc = __ac_inc(k, mask); last = i; /* inc==1 for linear probing */ \
+ last = i; \
while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \
- i = (i + inc) & mask; \
+ i = (i + (++step)) & mask; \
if (i == last) return h->n_buckets; \
} \
return __ac_iseither(h->flags, i)? h->n_buckets : i; \
} else return 0; \
} \
SCOPE int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets) \
- { /* This function uses 0.25*n_bucktes bytes of working space instead of [sizeof(key_t+val_t)+.25]*n_buckets. */ \
+ { /* This function uses 0.25*n_buckets bytes of working space instead of [sizeof(key_t+val_t)+.25]*n_buckets. */ \
khint32_t *new_flags = 0; \
khint_t j = 1; \
{ \
@@ -233,16 +245,16 @@ static const double __ac_HASH_UPPER = 0.77;
if (new_n_buckets < 4) new_n_buckets = 4; \
if (h->size >= (khint_t)(new_n_buckets * __ac_HASH_UPPER + 0.5)) j = 0; /* requested size is too small */ \
else { /* hash table size to be changed (shrink or expand); rehash */ \
- new_flags = (khint32_t*)kmalloc(__ac_fsize(new_n_buckets) * sizeof(khint32_t)); \
+ new_flags = (khint32_t*)kreallocarray(NULL, __ac_fsize(new_n_buckets), sizeof(khint32_t)); \
if (!new_flags) return -1; \
memset(new_flags, 0xaa, __ac_fsize(new_n_buckets) * sizeof(khint32_t)); \
if (h->n_buckets < new_n_buckets) { /* expand */ \
- khkey_t *new_keys = (khkey_t*)krealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \
- if (!new_keys) return -1; \
+ khkey_t *new_keys = (khkey_t*)kreallocarray((void *)h->keys, new_n_buckets, sizeof(khkey_t)); \
+ if (!new_keys) { kfree(new_flags); return -1; } \
h->keys = new_keys; \
if (kh_is_map) { \
- khval_t *new_vals = (khval_t*)krealloc((void *)h->vals, new_n_buckets * sizeof(khval_t)); \
- if (!new_vals) return -1; \
+ khval_t *new_vals = (khval_t*)kreallocarray((void *)h->vals, new_n_buckets, sizeof(khval_t)); \
+ if (!new_vals) { kfree(new_flags); return -1; } \
h->vals = new_vals; \
} \
} /* otherwise shrink */ \
@@ -258,11 +270,10 @@ static const double __ac_HASH_UPPER = 0.77;
if (kh_is_map) val = h->vals[j]; \
__ac_set_isdel_true(h->flags, j); \
while (1) { /* kick-out process; sort of like in Cuckoo hashing */ \
- khint_t inc, k, i; \
+ khint_t k, i, step = 0; \
k = __hash_func(key); \
i = k & new_mask; \
- inc = __ac_inc(k, new_mask); \
- while (!__ac_isempty(new_flags, i)) i = (i + inc) & new_mask; \
+ while (!__ac_isempty(new_flags, i)) i = (i + (++step)) & new_mask; \
__ac_set_isempty_false(new_flags, i); \
if (i < h->n_buckets && __ac_iseither(h->flags, i) == 0) { /* kick out the existing element */ \
{ khkey_t tmp = h->keys[i]; h->keys[i] = key; key = tmp; } \
@@ -277,8 +288,8 @@ static const double __ac_HASH_UPPER = 0.77;
} \
} \
if (h->n_buckets > new_n_buckets) { /* shrink the hash table */ \
- h->keys = (khkey_t*)krealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \
- if (kh_is_map) h->vals = (khval_t*)krealloc((void *)h->vals, new_n_buckets * sizeof(khval_t)); \
+ h->keys = (khkey_t*)kreallocarray((void *)h->keys, new_n_buckets, sizeof(khkey_t)); \
+ if (kh_is_map) h->vals = (khval_t*)kreallocarray((void *)h->vals, new_n_buckets, sizeof(khval_t)); \
} \
kfree(h->flags); /* free the working space */ \
h->flags = new_flags; \
@@ -301,14 +312,14 @@ static const double __ac_HASH_UPPER = 0.77;
} \
} /* TODO: to implement automatically shrinking; resize() already support shrinking */ \
{ \
- khint_t inc, k, i, site, last, mask = h->n_buckets - 1; \
+ khint_t k, i, site, last, mask = h->n_buckets - 1, step = 0; \
x = site = h->n_buckets; k = __hash_func(key); i = k & mask; \
if (__ac_isempty(h->flags, i)) x = i; /* for speed up */ \
else { \
- inc = __ac_inc(k, mask); last = i; \
+ last = i; \
while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \
if (__ac_isdel(h->flags, i)) site = i; \
- i = (i + inc) & mask; \
+ i = (i + (++step)) & mask; \
if (i == last) { x = site; break; } \
} \
if (x == h->n_buckets) { \
@@ -449,7 +460,8 @@ static kh_inline khint_t __ac_Wang_hash(khint_t key)
@param name Name of the hash table [symbol]
@param h Pointer to the hash table [khash_t(name)*]
@param k Key [type of keys]
- @param r Extra return code: 0 if the key is present in the hash table;
+ @param r Extra return code: -1 if the operation failed;
+ 0 if the key is present in the hash table;
1 if the bucket is empty (never used); 2 if the element in
the bucket has been deleted [int*]
@return Iterator to the inserted element [khint_t]
@@ -461,7 +473,7 @@ static kh_inline khint_t __ac_Wang_hash(khint_t key)
@param name Name of the hash table [symbol]
@param h Pointer to the hash table [khash_t(name)*]
@param k Key [type of keys]
- @return Iterator to the found element, or kh_end(h) is the element is absent [khint_t]
+ @return Iterator to the found element, or kh_end(h) if the element is absent [khint_t]
*/
#define kh_get(name, h, k) kh_get_##name(h, k)
@@ -607,4 +619,4 @@ typedef const char *kh_cstr_t;
#define KHASH_MAP_INIT_STR(name, khval_t) \
KHASH_INIT(name, kh_cstr_t, khval_t, 1, kh_str_hash_func, kh_str_hash_equal)
-#endif /* __AC_KHASH_H */
+#endif /* __AC_KHASH_H */ \ No newline at end of file
diff --git a/src/merge.c b/src/merge.c
index 7c38b5692..75ad0915e 100644
--- a/src/merge.c
+++ b/src/merge.c
@@ -1169,7 +1169,7 @@ int git_merge_diff_list__find_renames(
goto done;
if (diff_list->conflicts.length <= opts->target_limit) {
- cache_size = diff_list->conflicts.length * 3;
+ GITERR_CHECK_ALLOC_MULTIPLY(&cache_size, diff_list->conflicts.length, 3);
cache = git__calloc(cache_size, sizeof(void *));
GITERR_CHECK_ALLOC(cache);
@@ -2223,12 +2223,13 @@ static int merge_ancestor_head(
size_t their_heads_len)
{
git_oid *oids, ancestor_oid;
- size_t i;
+ size_t i, alloc_len;
int error = 0;
assert(repo && our_head && their_heads);
- oids = git__calloc(their_heads_len + 1, sizeof(git_oid));
+ GITERR_CHECK_ALLOC_ADD(&alloc_len, their_heads_len, 1);
+ oids = git__calloc(alloc_len, sizeof(git_oid));
GITERR_CHECK_ALLOC(oids);
git_oid_cpy(&oids[0], git_commit_id(our_head->commit));
@@ -2651,7 +2652,8 @@ int git_merge(
git_checkout_options checkout_opts;
git_annotated_commit *ancestor_head = NULL, *our_head = NULL;
git_tree *ancestor_tree = NULL, *our_tree = NULL, **their_trees = NULL;
- git_index *index_new = NULL;
+ git_index *index = NULL;
+ git_indexwriter indexwriter = GIT_INDEXWRITER_INIT;
size_t i;
int error = 0;
@@ -2665,11 +2667,10 @@ int git_merge(
their_trees = git__calloc(their_heads_len, sizeof(git_tree *));
GITERR_CHECK_ALLOC(their_trees);
- if ((error = merge_heads(&ancestor_head, &our_head, repo, their_heads, their_heads_len)) < 0)
- goto on_error;
-
- if ((error = merge_normalize_checkout_opts(repo, &checkout_opts, given_checkout_opts,
- ancestor_head, our_head, their_heads_len, their_heads)) < 0)
+ if ((error = merge_heads(&ancestor_head, &our_head, repo, their_heads, their_heads_len)) < 0 ||
+ (error = merge_normalize_checkout_opts(repo, &checkout_opts, given_checkout_opts,
+ ancestor_head, our_head, their_heads_len, their_heads)) < 0 ||
+ (error = git_indexwriter_init_for_operation(&indexwriter, repo, &checkout_opts.checkout_strategy)) < 0)
goto on_error;
/* Write the merge files to the repository. */
@@ -2690,10 +2691,11 @@ int git_merge(
/* TODO: recursive, octopus, etc... */
- if ((error = git_merge_trees(&index_new, repo, ancestor_tree, our_tree, their_trees[0], merge_opts)) < 0 ||
- (error = git_merge__check_result(repo, index_new)) < 0 ||
- (error = git_merge__append_conflicts_to_merge_msg(repo, index_new)) < 0 ||
- (error = git_checkout_index(repo, index_new, &checkout_opts)) < 0)
+ if ((error = git_merge_trees(&index, repo, ancestor_tree, our_tree, their_trees[0], merge_opts)) < 0 ||
+ (error = git_merge__check_result(repo, index)) < 0 ||
+ (error = git_merge__append_conflicts_to_merge_msg(repo, index)) < 0 ||
+ (error = git_checkout_index(repo, index, &checkout_opts)) < 0 ||
+ (error = git_indexwriter_commit(&indexwriter)) < 0)
goto on_error;
goto done;
@@ -2702,7 +2704,9 @@ on_error:
merge_state_cleanup(repo);
done:
- git_index_free(index_new);
+ git_indexwriter_cleanup(&indexwriter);
+
+ git_index_free(index);
git_tree_free(ancestor_tree);
git_tree_free(our_tree);
diff --git a/src/mwindow.c b/src/mwindow.c
index 0444cd98c..55c8d894b 100644
--- a/src/mwindow.c
+++ b/src/mwindow.c
@@ -14,7 +14,7 @@
#include "strmap.h"
#include "pack.h"
-GIT__USE_STRMAP;
+GIT__USE_STRMAP
#define DEFAULT_WINDOW_SIZE \
(sizeof(void*) >= 8 \
diff --git a/src/notes.c b/src/notes.c
index f44c0bd95..1850e81c7 100644
--- a/src/notes.c
+++ b/src/notes.c
@@ -314,7 +314,7 @@ static int note_new(
{
git_note *note = NULL;
- note = (git_note *)git__malloc(sizeof(git_note));
+ note = git__malloc(sizeof(git_note));
GITERR_CHECK_ALLOC(note);
git_oid_cpy(&note->id, note_oid);
diff --git a/src/odb.c b/src/odb.c
index d2dff7178..b1d606b4d 100644
--- a/src/odb.c
+++ b/src/odb.c
@@ -216,41 +216,43 @@ int git_odb__hashfd_filtered(
int git_odb__hashlink(git_oid *out, const char *path)
{
struct stat st;
- git_off_t size;
+ int size;
int result;
if (git_path_lstat(path, &st) < 0)
return -1;
- size = st.st_size;
-
- if (!git__is_sizet(size)) {
- giterr_set(GITERR_OS, "File size overflow for 32-bit systems");
+ if (!git__is_int(st.st_size) || (int)st.st_size < 0) {
+ giterr_set(GITERR_FILESYSTEM, "File size overflow for 32-bit systems");
return -1;
}
+ size = (int)st.st_size;
+
if (S_ISLNK(st.st_mode)) {
char *link_data;
- ssize_t read_len;
+ int read_len;
+ size_t alloc_size;
- link_data = git__malloc((size_t)(size + 1));
+ GITERR_CHECK_ALLOC_ADD(&alloc_size, size, 1);
+ link_data = git__malloc(alloc_size);
GITERR_CHECK_ALLOC(link_data);
- read_len = p_readlink(path, link_data, (size_t)size);
+ read_len = p_readlink(path, link_data, size);
link_data[size] = '\0';
- if (read_len != (ssize_t)size) {
+ if (read_len != size) {
giterr_set(GITERR_OS, "Failed to read symlink data for '%s'", path);
git__free(link_data);
return -1;
}
- result = git_odb_hash(out, link_data, (size_t)size, GIT_OBJ_BLOB);
+ result = git_odb_hash(out, link_data, size, GIT_OBJ_BLOB);
git__free(link_data);
} else {
int fd = git_futils_open_ro(path);
if (fd < 0)
return -1;
- result = git_odb__hashfd(out, fd, (size_t)size, GIT_OBJ_BLOB);
+ result = git_odb__hashfd(out, fd, size, GIT_OBJ_BLOB);
p_close(fd);
}
diff --git a/src/odb_loose.c b/src/odb_loose.c
index ea9bdc4a0..bfd95588b 100644
--- a/src/odb_loose.c
+++ b/src/odb_loose.c
@@ -63,8 +63,12 @@ typedef struct {
static int object_file_name(
git_buf *name, const loose_backend *be, const git_oid *id)
{
+ size_t alloclen;
+
/* expand length for object root + 40 hex sha1 chars + 2 * '/' + '\0' */
- if (git_buf_grow(name, be->objects_dirlen + GIT_OID_HEXSZ + 3) < 0)
+ GITERR_CHECK_ALLOC_ADD(&alloclen, be->objects_dirlen, GIT_OID_HEXSZ);
+ GITERR_CHECK_ALLOC_ADD(&alloclen, alloclen, 3);
+ if (git_buf_grow(name, alloclen) < 0)
return -1;
git_buf_set(name, be->objects_dir, be->objects_dirlen);
@@ -261,14 +265,15 @@ static int inflate_buffer(void *in, size_t inlen, void *out, size_t outlen)
static void *inflate_tail(z_stream *s, void *hb, size_t used, obj_hdr *hdr)
{
unsigned char *buf, *head = hb;
- size_t tail;
+ size_t tail, alloc_size;
/*
* allocate a buffer to hold the inflated data and copy the
* initial sequence of inflated data from the tail of the
* head buffer, if any.
*/
- if ((buf = git__malloc(hdr->size + 1)) == NULL) {
+ if (GIT_ADD_SIZET_OVERFLOW(&alloc_size, hdr->size, 1) ||
+ (buf = git__malloc(alloc_size)) == NULL) {
inflateEnd(s);
return NULL;
}
@@ -306,7 +311,7 @@ static int inflate_packlike_loose_disk_obj(git_rawobj *out, git_buf *obj)
{
unsigned char *in, *buf;
obj_hdr hdr;
- size_t len, used;
+ size_t len, used, alloclen;
/*
* read the object header, which is an (uncompressed)
@@ -321,7 +326,8 @@ static int inflate_packlike_loose_disk_obj(git_rawobj *out, git_buf *obj)
/*
* allocate a buffer and inflate the data into it
*/
- buf = git__malloc(hdr.size + 1);
+ GITERR_CHECK_ALLOC_ADD(&alloclen, hdr.size, 1);
+ buf = git__malloc(alloclen);
GITERR_CHECK_ALLOC(buf);
in = ((unsigned char *)obj->ptr) + used;
@@ -515,12 +521,14 @@ static int locate_object_short_oid(
size_t len)
{
char *objects_dir = backend->objects_dir;
- size_t dir_len = strlen(objects_dir);
+ size_t dir_len = strlen(objects_dir), alloc_len;
loose_locate_object_state state;
int error;
/* prealloc memory for OBJ_DIR/xx/xx..38x..xx */
- if (git_buf_grow(object_location, dir_len + 3 + GIT_OID_HEXSZ) < 0)
+ GITERR_CHECK_ALLOC_ADD(&alloc_len, dir_len, GIT_OID_HEXSZ);
+ GITERR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 3);
+ if (git_buf_grow(object_location, alloc_len) < 0)
return -1;
git_buf_set(object_location, objects_dir, dir_len);
@@ -563,9 +571,11 @@ static int locate_object_short_oid(
return error;
/* Update the location according to the oid obtained */
+ GITERR_CHECK_ALLOC_ADD(&alloc_len, dir_len, GIT_OID_HEXSZ);
+ GITERR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 2);
git_buf_truncate(object_location, dir_len);
- if (git_buf_grow(object_location, dir_len + GIT_OID_HEXSZ + 2) < 0)
+ if (git_buf_grow(object_location, alloc_len) < 0)
return -1;
git_oid_pathfmt(object_location->ptr + dir_len, res_oid);
@@ -922,13 +932,15 @@ int git_odb_backend_loose(
unsigned int file_mode)
{
loose_backend *backend;
- size_t objects_dirlen;
+ size_t objects_dirlen, alloclen;
assert(backend_out && objects_dir);
objects_dirlen = strlen(objects_dir);
- backend = git__calloc(1, sizeof(loose_backend) + objects_dirlen + 2);
+ GITERR_CHECK_ALLOC_ADD(&alloclen, sizeof(loose_backend), objects_dirlen);
+ GITERR_CHECK_ALLOC_ADD(&alloclen, alloclen, 2);
+ backend = git__calloc(1, alloclen);
GITERR_CHECK_ALLOC(backend);
backend->parent.version = GIT_ODB_BACKEND_VERSION;
diff --git a/src/odb_mempack.c b/src/odb_mempack.c
index d9b3a1824..34355270f 100644
--- a/src/odb_mempack.c
+++ b/src/odb_mempack.c
@@ -18,7 +18,7 @@
#include "git2/types.h"
#include "git2/pack.h"
-GIT__USE_OIDMAP;
+GIT__USE_OIDMAP
struct memobject {
git_oid oid;
@@ -38,6 +38,7 @@ static int impl__write(git_odb_backend *_backend, const git_oid *oid, const void
struct memory_packer_db *db = (struct memory_packer_db *)_backend;
struct memobject *obj = NULL;
khiter_t pos;
+ size_t alloc_len;
int rval;
pos = kh_put(oid, db->objects, oid, &rval);
@@ -47,7 +48,8 @@ static int impl__write(git_odb_backend *_backend, const git_oid *oid, const void
if (rval == 0)
return 0;
- obj = git__malloc(sizeof(struct memobject) + len);
+ GITERR_CHECK_ALLOC_ADD(&alloc_len, sizeof(struct memobject), len);
+ obj = git__malloc(alloc_len);
GITERR_CHECK_ALLOC(obj);
memcpy(obj->data, data, len);
diff --git a/src/offmap.h b/src/offmap.h
index cd46fd687..0d0e51272 100644
--- a/src/offmap.h
+++ b/src/offmap.h
@@ -13,14 +13,15 @@
#define kmalloc git__malloc
#define kcalloc git__calloc
#define krealloc git__realloc
+#define kreallocarray git__reallocarray
#define kfree git__free
#include "khash.h"
-__KHASH_TYPE(off, git_off_t, void *);
+__KHASH_TYPE(off, git_off_t, void *)
typedef khash_t(off) git_offmap;
#define GIT__USE_OFFMAP \
- __KHASH_IMPL(off, static kh_inline, git_off_t, void *, 1, kh_int64_hash_func, kh_int64_hash_equal);
+ __KHASH_IMPL(off, static kh_inline, git_off_t, void *, 1, kh_int64_hash_func, kh_int64_hash_equal)
#define git_offmap_alloc() kh_init(off)
#define git_offmap_free(h) kh_destroy(off, h), h = NULL
diff --git a/src/oid.c b/src/oid.c
index 5d29200b6..9fe2ebb65 100644
--- a/src/oid.c
+++ b/src/oid.c
@@ -261,7 +261,7 @@ struct git_oid_shorten {
static int resize_trie(git_oid_shorten *self, size_t new_size)
{
- self->nodes = git__realloc(self->nodes, new_size * sizeof(trie_node));
+ self->nodes = git__reallocarray(self->nodes, new_size, sizeof(trie_node));
GITERR_CHECK_ALLOC(self->nodes);
if (new_size > self->size) {
diff --git a/src/oidmap.h b/src/oidmap.h
index b871a7926..d2c451e7f 100644
--- a/src/oidmap.h
+++ b/src/oidmap.h
@@ -13,10 +13,11 @@
#define kmalloc git__malloc
#define kcalloc git__calloc
#define krealloc git__realloc
+#define kreallocarray git__reallocarray
#define kfree git__free
#include "khash.h"
-__KHASH_TYPE(oid, const git_oid *, void *);
+__KHASH_TYPE(oid, const git_oid *, void *)
typedef khash_t(oid) git_oidmap;
GIT_INLINE(khint_t) git_oidmap_hash(const git_oid *oid)
diff --git a/src/pack-objects.c b/src/pack-objects.c
index 0040a826b..f644520ac 100644
--- a/src/pack-objects.c
+++ b/src/pack-objects.c
@@ -190,6 +190,7 @@ int git_packbuilder_insert(git_packbuilder *pb, const git_oid *oid,
{
git_pobject *po;
khiter_t pos;
+ size_t newsize;
int ret;
assert(pb && oid);
@@ -201,9 +202,18 @@ int git_packbuilder_insert(git_packbuilder *pb, const git_oid *oid,
return 0;
if (pb->nr_objects >= pb->nr_alloc) {
- pb->nr_alloc = (pb->nr_alloc + 1024) * 3 / 2;
- pb->object_list = git__realloc(pb->object_list,
- pb->nr_alloc * sizeof(*po));
+ GITERR_CHECK_ALLOC_ADD(&newsize, pb->nr_alloc, 1024);
+ GITERR_CHECK_ALLOC_MULTIPLY(&newsize, newsize, 3 / 2);
+
+ if (!git__is_uint32(newsize)) {
+ giterr_set(GITERR_NOMEMORY, "Packfile too large to fit in memory.");
+ return -1;
+ }
+
+ pb->nr_alloc = (uint32_t)newsize;
+
+ pb->object_list = git__reallocarray(pb->object_list,
+ pb->nr_alloc, sizeof(*po));
GITERR_CHECK_ALLOC(pb->object_list);
rehash(pb);
}
@@ -499,8 +509,10 @@ static int cb_tag_foreach(const char *name, git_oid *oid, void *data)
static git_pobject **compute_write_order(git_packbuilder *pb)
{
unsigned int i, wo_end, last_untagged;
+ git_pobject **wo;
- git_pobject **wo = git__malloc(sizeof(*wo) * pb->nr_objects);
+ if ((wo = git__mallocarray(pb->nr_objects, sizeof(*wo))) == NULL)
+ return NULL;
for (i = 0; i < pb->nr_objects; i++) {
git_pobject *po = pb->object_list + i;
@@ -770,10 +782,13 @@ static int try_delta(git_packbuilder *pb, struct unpacked *trg,
*mem_usage += sz;
}
if (!src->data) {
- if (git_odb_read(&obj, pb->odb, &src_object->id) < 0)
+ size_t obj_sz;
+
+ if (git_odb_read(&obj, pb->odb, &src_object->id) < 0 ||
+ !git__is_ulong(obj_sz = git_odb_object_size(obj)))
return -1;
- sz = (unsigned long)git_odb_object_size(obj);
+ sz = (unsigned long)obj_sz;
src->data = git__malloc(sz);
GITERR_CHECK_ALLOC(src->data);
memcpy(src->data, git_odb_object_data(obj), sz);
@@ -817,11 +832,14 @@ static int try_delta(git_packbuilder *pb, struct unpacked *trg,
trg_object->delta_data = NULL;
}
if (delta_cacheable(pb, src_size, trg_size, delta_size)) {
- pb->delta_cache_size += delta_size;
+ bool overflow = git__add_uint64_overflow(
+ &pb->delta_cache_size, pb->delta_cache_size, delta_size);
+
git_packbuilder__cache_unlock(pb);
- trg_object->delta_data = git__realloc(delta_buf, delta_size);
- GITERR_CHECK_ALLOC(trg_object->delta_data);
+ if (overflow ||
+ !(trg_object->delta_data = git__realloc(delta_buf, delta_size)))
+ return -1;
} else {
/* create delta when writing the pack */
git_packbuilder__cache_unlock(pb);
@@ -1088,7 +1106,7 @@ static int ll_find_deltas(git_packbuilder *pb, git_pobject **list,
return 0;
}
- p = git__malloc(pb->nr_threads * sizeof(*p));
+ p = git__mallocarray(pb->nr_threads, sizeof(*p));
GITERR_CHECK_ALLOC(p);
/* Partition the work among the threads */
@@ -1239,7 +1257,7 @@ static int prepare_pack(git_packbuilder *pb)
if (pb->progress_cb)
pb->progress_cb(GIT_PACKBUILDER_DELTAFICATION, 0, pb->nr_objects, pb->progress_cb_payload);
- delta_list = git__malloc(pb->nr_objects * sizeof(*delta_list));
+ delta_list = git__mallocarray(pb->nr_objects, sizeof(*delta_list));
GITERR_CHECK_ALLOC(delta_list);
for (i = 0; i < pb->nr_objects; ++i) {
diff --git a/src/pack.c b/src/pack.c
index 47ce854c4..26a6036c2 100644
--- a/src/pack.c
+++ b/src/pack.c
@@ -624,7 +624,7 @@ int git_packfile_unpack(
struct pack_chain_elem *elem = NULL, *stack;
git_pack_cache_entry *cached = NULL;
struct pack_chain_elem small_stack[SMALL_STACK_SIZE];
- size_t stack_size = 0, elem_pos;
+ size_t stack_size = 0, elem_pos, alloclen;
git_otype base_type;
/*
@@ -683,8 +683,11 @@ int git_packfile_unpack(
*/
if (cached && stack_size == 1) {
void *data = obj->data;
- obj->data = git__malloc(obj->len + 1);
+
+ GITERR_CHECK_ALLOC_ADD(&alloclen, obj->len, 1);
+ obj->data = git__malloc(alloclen);
GITERR_CHECK_ALLOC(obj->data);
+
memcpy(obj->data, data, obj->len + 1);
git_atomic_dec(&cached->refcount);
goto cleanup;
@@ -837,16 +840,18 @@ int packfile_unpack_compressed(
size_t size,
git_otype type)
{
+ size_t buf_size;
int st;
z_stream stream;
unsigned char *buffer, *in;
- buffer = git__calloc(1, size + 1);
+ GITERR_CHECK_ALLOC_ADD(&buf_size, size, 1);
+ buffer = git__calloc(1, buf_size);
GITERR_CHECK_ALLOC(buffer);
memset(&stream, 0, sizeof(stream));
stream.next_out = buffer;
- stream.avail_out = (uInt)size + 1;
+ stream.avail_out = (uInt)buf_size;
stream.zalloc = use_git_alloc;
stream.zfree = use_git_free;
@@ -1085,14 +1090,17 @@ int git_packfile_alloc(struct git_pack_file **pack_out, const char *path)
{
struct stat st;
struct git_pack_file *p;
- size_t path_len = path ? strlen(path) : 0;
+ size_t path_len = path ? strlen(path) : 0, alloc_len;
*pack_out = NULL;
if (path_len < strlen(".idx"))
return git_odb__error_notfound("invalid packfile path", NULL);
- p = git__calloc(1, sizeof(*p) + path_len + 2);
+ GITERR_CHECK_ALLOC_ADD(&alloc_len, sizeof(*p), path_len);
+ GITERR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 2);
+
+ p = git__calloc(1, alloc_len);
GITERR_CHECK_ALLOC(p);
memcpy(p->pack_name, path, path_len + 1);
diff --git a/src/pack.h b/src/pack.h
index 34d37d907..3cebd10ac 100644
--- a/src/pack.h
+++ b/src/pack.h
@@ -72,8 +72,8 @@ typedef git_array_t(struct pack_chain_elem) git_dependency_chain;
#include "offmap.h"
-GIT__USE_OFFMAP;
-GIT__USE_OIDMAP;
+GIT__USE_OFFMAP
+GIT__USE_OIDMAP
#define GIT_PACK_CACHE_MEMORY_LIMIT 16 * 1024 * 1024
#define GIT_PACK_CACHE_SIZE_LIMIT 1024 * 1024 /* don't bother caching anything over 1MB */
diff --git a/src/path.c b/src/path.c
index 58d71921b..64c7b7e12 100644
--- a/src/path.c
+++ b/src/path.c
@@ -620,9 +620,12 @@ static bool _check_dir_contents(
bool result;
size_t dir_size = git_buf_len(dir);
size_t sub_size = strlen(sub);
+ size_t alloc_size;
/* leave base valid even if we could not make space for subdir */
- if (git_buf_try_grow(dir, dir_size + sub_size + 2, false, false) < 0)
+ if (GIT_ADD_SIZET_OVERFLOW(&alloc_size, dir_size, sub_size) ||
+ GIT_ADD_SIZET_OVERFLOW(&alloc_size, alloc_size, 2) ||
+ git_buf_try_grow(dir, alloc_size, false, false) < 0)
return false;
/* save excursion */
@@ -784,7 +787,7 @@ int git_path_cmp(
int git_path_make_relative(git_buf *path, const char *parent)
{
const char *p, *q, *p_dirsep, *q_dirsep;
- size_t plen = path->size, newlen, depth = 1, i, offset;
+ size_t plen = path->size, newlen, alloclen, depth = 1, i, offset;
for (p_dirsep = p = path->ptr, q_dirsep = q = parent; *p && *q; p++, q++) {
if (*p == '/' && *q == '/') {
@@ -822,11 +825,14 @@ int git_path_make_relative(git_buf *path, const char *parent)
for (; (q = strchr(q, '/')) && *(q + 1); q++)
depth++;
- newlen = (depth * 3) + plen;
+ GITERR_CHECK_ALLOC_MULTIPLY(&newlen, depth, 3);
+ GITERR_CHECK_ALLOC_ADD(&newlen, newlen, plen);
+
+ GITERR_CHECK_ALLOC_ADD(&alloclen, newlen, 1);
/* save the offset as we might realllocate the pointer */
offset = p - path->ptr;
- if (git_buf_try_grow(path, newlen + 1, 1, 0) < 0)
+ if (git_buf_try_grow(path, alloclen, 1, 0) < 0)
return -1;
p = path->ptr + offset;
@@ -871,7 +877,7 @@ void git_path_iconv_clear(git_path_iconv_t *ic)
int git_path_iconv(git_path_iconv_t *ic, char **in, size_t *inlen)
{
char *nfd = *in, *nfc;
- size_t nfdlen = *inlen, nfclen, wantlen = nfdlen, rv;
+ size_t nfdlen = *inlen, nfclen, wantlen = nfdlen, alloclen, rv;
int retry = 1;
if (!ic || ic->map == (iconv_t)-1 ||
@@ -881,7 +887,8 @@ int git_path_iconv(git_path_iconv_t *ic, char **in, size_t *inlen)
git_buf_clear(&ic->buf);
while (1) {
- if (git_buf_grow(&ic->buf, wantlen + 1) < 0)
+ GITERR_CHECK_ALLOC_ADD(&alloclen, wantlen, 1);
+ if (git_buf_grow(&ic->buf, alloclen) < 0)
return -1;
nfc = ic->buf.ptr + ic->buf.size;
@@ -1057,6 +1064,37 @@ int git_path_direach(
return error;
}
+static int entry_path_alloc(
+ char **out,
+ const char *path,
+ size_t path_len,
+ const char *de_path,
+ size_t de_len,
+ size_t alloc_extra)
+{
+ int need_slash = (path_len > 0 && path[path_len-1] != '/') ? 1 : 0;
+ size_t alloc_size;
+ char *entry_path;
+
+ GITERR_CHECK_ALLOC_ADD(&alloc_size, path_len, de_len);
+ GITERR_CHECK_ALLOC_ADD(&alloc_size, alloc_size, need_slash);
+ GITERR_CHECK_ALLOC_ADD(&alloc_size, alloc_size, 1);
+ GITERR_CHECK_ALLOC_ADD(&alloc_size, alloc_size, alloc_extra);
+ entry_path = git__calloc(1, alloc_size);
+ GITERR_CHECK_ALLOC(entry_path);
+
+ if (path_len)
+ memcpy(entry_path, path, path_len);
+
+ if (need_slash)
+ entry_path[path_len] = '/';
+
+ memcpy(&entry_path[path_len + need_slash], de_path, de_len);
+
+ *out = entry_path;
+ return 0;
+}
+
int git_path_dirload(
const char *path,
size_t prefix_len,
@@ -1064,7 +1102,7 @@ int git_path_dirload(
unsigned int flags,
git_vector *contents)
{
- int error, need_slash;
+ int error;
DIR *dir;
size_t path_len;
path_dirent_data de_data;
@@ -1096,11 +1134,10 @@ int git_path_dirload(
path += prefix_len;
path_len -= prefix_len;
- need_slash = (path_len > 0 && path[path_len-1] != '/') ? 1 : 0;
while ((error = p_readdir_r(dir, de_buf, &de)) == 0 && de != NULL) {
char *entry_path, *de_path = de->d_name;
- size_t alloc_size, de_len = strlen(de_path);
+ size_t de_len = strlen(de_path);
if (git_path_is_dot_or_dotdot(de_path))
continue;
@@ -1110,17 +1147,9 @@ int git_path_dirload(
break;
#endif
- alloc_size = path_len + need_slash + de_len + 1 + alloc_extra;
- if ((entry_path = git__calloc(alloc_size, 1)) == NULL) {
- error = -1;
+ if ((error = entry_path_alloc(&entry_path,
+ path, path_len, de_path, de_len, alloc_extra)) < 0)
break;
- }
-
- if (path_len)
- memcpy(entry_path, path, path_len);
- if (need_slash)
- entry_path[path_len] = '/';
- memcpy(&entry_path[path_len + need_slash], de_path, de_len);
if ((error = git_vector_insert(contents, entry_path)) < 0) {
git__free(entry_path);
diff --git a/src/pool.c b/src/pool.c
index 7350c04c1..c93d78182 100644
--- a/src/pool.c
+++ b/src/pool.c
@@ -107,21 +107,22 @@ static void pool_insert_page(git_pool *pool, git_pool_page *page)
static void *pool_alloc_page(git_pool *pool, uint32_t size)
{
git_pool_page *page;
- uint32_t alloc_size;
+ uint32_t new_page_size;
+ size_t alloc_size;
if (size <= pool->page_size)
- alloc_size = pool->page_size;
+ new_page_size = pool->page_size;
else {
- alloc_size = size;
+ new_page_size = size;
pool->has_large_page_alloc = 1;
}
- page = git__calloc(1, alloc_size + sizeof(git_pool_page));
- if (!page)
+ if (GIT_ADD_SIZET_OVERFLOW(&alloc_size, new_page_size, sizeof(git_pool_page)) ||
+ !(page = git__calloc(1, alloc_size)))
return NULL;
- page->size = alloc_size;
- page->avail = alloc_size - size;
+ page->size = new_page_size;
+ page->avail = new_page_size - size;
if (page->avail > 0)
pool_insert_page(pool, page);
diff --git a/src/posix.c b/src/posix.c
index d5e6875b5..8d86aa8bf 100644
--- a/src/posix.c
+++ b/src/posix.c
@@ -155,6 +155,14 @@ ssize_t p_read(git_file fd, void *buf, size_t cnt)
{
char *b = buf;
+ if (!git__is_ssizet(cnt)) {
+#ifdef GIT_WIN32
+ SetLastError(ERROR_INVALID_PARAMETER);
+#endif
+ errno = EINVAL;
+ return -1;
+ }
+
while (cnt) {
ssize_t r;
#ifdef GIT_WIN32
@@ -229,7 +237,9 @@ int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offs
out->data = malloc(len);
GITERR_CHECK_ALLOC(out->data);
- if ((p_lseek(fd, offset, SEEK_SET) < 0) || ((size_t)p_read(fd, out->data, len) != len)) {
+ if (!git__is_ssizet(len) ||
+ (p_lseek(fd, offset, SEEK_SET) < 0) ||
+ (p_read(fd, out->data, len) != (ssize_t)len)) {
giterr_set(GITERR_OS, "mmap emulation failed");
return -1;
}
diff --git a/src/rebase.c b/src/rebase.c
index 2e805929e..8078eedc3 100644
--- a/src/rebase.c
+++ b/src/rebase.c
@@ -14,6 +14,7 @@
#include "array.h"
#include "config.h"
#include "annotated_commit.h"
+#include "index.h"
#include <git2/types.h>
#include <git2/annotated_commit.h>
@@ -168,10 +169,31 @@ GIT_INLINE(int) rebase_readoid(
return 0;
}
+static git_rebase_operation *rebase_operation_alloc(
+ git_rebase *rebase,
+ git_rebase_operation_t type,
+ git_oid *id,
+ const char *exec)
+{
+ git_rebase_operation *operation;
+
+ assert((type == GIT_REBASE_OPERATION_EXEC) == !id);
+ assert((type == GIT_REBASE_OPERATION_EXEC) == !!exec);
+
+ if ((operation = git_array_alloc(rebase->operations)) == NULL)
+ return NULL;
+
+ operation->type = type;
+ git_oid_cpy((git_oid *)&operation->id, id);
+ operation->exec = exec;
+
+ return operation;
+}
+
static int rebase_open_merge(git_rebase *rebase)
{
git_buf state_path = GIT_BUF_INIT, buf = GIT_BUF_INIT, cmt = GIT_BUF_INIT;
- git_oid current_id = {{0}};
+ git_oid id;
git_rebase_operation *operation;
size_t i, msgnum = 0, end;
int error;
@@ -194,7 +216,7 @@ static int rebase_open_merge(git_rebase *rebase)
goto done;
/* Read 'current' if it exists */
- if ((error = rebase_readoid(&current_id, &buf, &state_path, CURRENT_FILE)) < 0 &&
+ if ((error = rebase_readoid(&id, &buf, &state_path, CURRENT_FILE)) < 0 &&
error != GIT_ENOTFOUND)
goto done;
@@ -203,14 +225,14 @@ static int rebase_open_merge(git_rebase *rebase)
GITERR_CHECK_ARRAY(rebase->operations);
for (i = 0; i < end; i++) {
- operation = git_array_alloc(rebase->operations);
- GITERR_CHECK_ALLOC(operation);
-
git_buf_clear(&cmt);
if ((error = git_buf_printf(&cmt, "cmt.%" PRIuZ, (i+1))) < 0 ||
- (error = rebase_readoid((git_oid *)&operation->id, &buf, &state_path, cmt.ptr)) < 0)
+ (error = rebase_readoid(&id, &buf, &state_path, cmt.ptr)) < 0)
goto done;
+
+ operation = rebase_operation_alloc(rebase, GIT_REBASE_OPERATION_PICK, &id, NULL);
+ GITERR_CHECK_ALLOC(operation);
}
/* Read 'onto_name' */
@@ -553,9 +575,7 @@ static int rebase_init_operations(
if (merge)
continue;
- operation = git_array_alloc(rebase->operations);
- operation->type = GIT_REBASE_OPERATION_PICK;
- git_oid_cpy((git_oid *)&operation->id, &id);
+ operation = rebase_operation_alloc(rebase, GIT_REBASE_OPERATION_PICK, &id, NULL);
}
error = 0;
@@ -589,11 +609,21 @@ static int rebase_init(
const git_annotated_commit *onto,
const git_rebase_options *opts)
{
+ git_reference *head_ref = NULL;
+ git_annotated_commit *head_branch = NULL;
git_buf state_path = GIT_BUF_INIT;
int error;
if ((error = git_buf_joinpath(&state_path, repo->path_repository, REBASE_MERGE_DIR)) < 0)
- return error;
+ goto done;
+
+ if (!branch) {
+ if ((error = git_repository_head(&head_ref, repo)) < 0 ||
+ (error = git_annotated_commit_from_ref(&head_branch, repo, head_ref)) < 0)
+ goto done;
+
+ branch = head_branch;
+ }
rebase->repo = repo;
rebase->type = GIT_REBASE_TYPE_MERGE;
@@ -611,6 +641,10 @@ static int rebase_init(
git_buf_free(&state_path);
+done:
+ git_reference_free(head_ref);
+ git_annotated_commit_free(head_branch);
+
return error;
}
@@ -625,12 +659,12 @@ int git_rebase_init(
{
git_rebase *rebase = NULL;
git_rebase_options opts;
- git_reference *head_ref = NULL;
git_buf reflog = GIT_BUF_INIT;
+ git_commit *onto_commit = NULL;
git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
int error;
- assert(repo && branch && (upstream || onto));
+ assert(repo && (upstream || onto));
*out = NULL;
@@ -639,24 +673,28 @@ int git_rebase_init(
if (!onto)
onto = upstream;
- checkout_opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+ checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE;
if ((error = rebase_normalize_opts(repo, &opts, given_opts)) < 0 ||
(error = git_repository__ensure_not_bare(repo, "rebase")) < 0 ||
(error = rebase_ensure_not_in_progress(repo)) < 0 ||
- (error = rebase_ensure_not_dirty(repo)) < 0)
+ (error = rebase_ensure_not_dirty(repo)) < 0 ||
+ (error = git_commit_lookup(
+ &onto_commit, repo, git_annotated_commit_id(onto))) < 0)
return error;
rebase = git__calloc(1, sizeof(git_rebase));
GITERR_CHECK_ALLOC(rebase);
- if ((error = rebase_init(rebase, repo, branch, upstream, onto, &opts)) < 0 ||
+ if ((error = rebase_init(
+ rebase, repo, branch, upstream, onto, &opts)) < 0 ||
(error = rebase_setupfiles(rebase)) < 0 ||
(error = git_buf_printf(&reflog,
"rebase: checkout %s", rebase_onto_name(onto))) < 0 ||
- (error = git_reference_create(&head_ref, repo, GIT_HEAD_FILE,
- git_annotated_commit_id(onto), 1, signature, reflog.ptr)) < 0 ||
- (error = git_checkout_head(repo, &checkout_opts)) < 0)
+ (error = git_checkout_tree(
+ repo, (git_object *)onto_commit, &checkout_opts)) < 0 ||
+ (error = git_repository_set_head_detached(
+ repo, git_annotated_commit_id(onto), signature, reflog.ptr)) < 0)
goto done;
*out = rebase;
@@ -667,7 +705,7 @@ done:
git_rebase_free(rebase);
}
- git_reference_free(head_ref);
+ git_commit_free(onto_commit);
git_buf_free(&reflog);
rebase_opts_free(&opts);
@@ -726,6 +764,7 @@ static int rebase_next_merge(
git_commit *current_commit = NULL, *parent_commit = NULL;
git_tree *current_tree = NULL, *head_tree = NULL, *parent_tree = NULL;
git_index *index = NULL;
+ git_indexwriter indexwriter = GIT_INDEXWRITER_INIT;
git_rebase_operation *operation;
char current_idstr[GIT_OID_HEXSZ];
unsigned int parent_count;
@@ -755,20 +794,21 @@ static int rebase_next_merge(
git_oid_fmt(current_idstr, &operation->id);
- if ((error = rebase_setupfile(rebase, MSGNUM_FILE, -1, "%d\n", rebase->current+1)) < 0 ||
- (error = rebase_setupfile(rebase, CURRENT_FILE, -1, "%.*s\n", GIT_OID_HEXSZ, current_idstr)) < 0)
- goto done;
-
normalize_checkout_opts(rebase, current_commit, &checkout_opts, given_checkout_opts);
- if ((error = git_merge_trees(&index, rebase->repo, parent_tree, head_tree, current_tree, NULL)) < 0 ||
+ if ((error = git_indexwriter_init_for_operation(&indexwriter, rebase->repo, &checkout_opts.checkout_strategy)) < 0 ||
+ (error = rebase_setupfile(rebase, MSGNUM_FILE, -1, "%d\n", rebase->current+1)) < 0 ||
+ (error = rebase_setupfile(rebase, CURRENT_FILE, -1, "%.*s\n", GIT_OID_HEXSZ, current_idstr)) < 0 ||
+ (error = git_merge_trees(&index, rebase->repo, parent_tree, head_tree, current_tree, NULL)) < 0 ||
(error = git_merge__check_result(rebase->repo, index)) < 0 ||
- (error = git_checkout_index(rebase->repo, index, &checkout_opts)) < 0)
+ (error = git_checkout_index(rebase->repo, index, &checkout_opts)) < 0 ||
+ (error = git_indexwriter_commit(&indexwriter)) < 0)
goto done;
*out = operation;
done:
+ git_indexwriter_cleanup(&indexwriter);
git_index_free(index);
git_tree_free(current_tree);
git_tree_free(head_tree);
diff --git a/src/refdb_fs.c b/src/refdb_fs.c
index 5cd2112fc..77cb1a8ce 100644
--- a/src/refdb_fs.c
+++ b/src/refdb_fs.c
@@ -26,7 +26,7 @@
#include <git2/sys/refs.h>
#include <git2/sys/reflog.h>
-GIT__USE_STRMAP;
+GIT__USE_STRMAP
#define DEFAULT_NESTING_LEVEL 5
#define MAX_NESTING_LEVEL 10
diff --git a/src/refs.c b/src/refs.c
index 43c7333f2..e3157529b 100644
--- a/src/refs.c
+++ b/src/refs.c
@@ -24,7 +24,7 @@
#include <git2/signature.h>
#include <git2/commit.h>
-GIT__USE_STRMAP;
+GIT__USE_STRMAP
#define DEFAULT_NESTING_LEVEL 5
#define MAX_NESTING_LEVEL 10
@@ -36,13 +36,13 @@ enum {
static git_reference *alloc_ref(const char *name)
{
- git_reference *ref;
- size_t namelen = strlen(name);
-
- if ((ref = git__calloc(1, sizeof(git_reference) + namelen + 1)) == NULL)
- return NULL;
+ git_reference *ref = NULL;
+ size_t namelen = strlen(name), reflen;
- memcpy(ref->name, name, namelen + 1);
+ if (!GIT_ADD_SIZET_OVERFLOW(&reflen, sizeof(git_reference), namelen) &&
+ !GIT_ADD_SIZET_OVERFLOW(&reflen, reflen, 1) &&
+ (ref = git__calloc(1, reflen)) != NULL)
+ memcpy(ref->name, name, namelen + 1);
return ref;
}
@@ -94,10 +94,14 @@ git_reference *git_reference__set_name(
git_reference *ref, const char *name)
{
size_t namelen = strlen(name);
- git_reference *rewrite =
- git__realloc(ref, sizeof(git_reference) + namelen + 1);
- if (rewrite != NULL)
+ size_t reflen;
+ git_reference *rewrite = NULL;
+
+ if (!GIT_ADD_SIZET_OVERFLOW(&reflen, sizeof(git_reference), namelen) &&
+ !GIT_ADD_SIZET_OVERFLOW(&reflen, reflen, 1) &&
+ (rewrite = git__realloc(ref, reflen)) != NULL)
memcpy(rewrite->name, name, namelen + 1);
+
return rewrite;
}
diff --git a/src/remote.c b/src/remote.c
index 5ba7735ae..d96274f1d 100644
--- a/src/remote.c
+++ b/src/remote.c
@@ -383,10 +383,9 @@ int git_remote_lookup(git_remote **out, git_repository *repo, const char *name)
if ((error = git_repository_config_snapshot(&config, repo)) < 0)
return error;
- remote = git__malloc(sizeof(git_remote));
+ remote = git__calloc(1, sizeof(git_remote));
GITERR_CHECK_ALLOC(remote);
- memset(remote, 0x0, sizeof(git_remote));
remote->update_fetchhead = 1;
remote->name = git__strdup(name);
GITERR_CHECK_ALLOC(remote->name);
diff --git a/src/revert.c b/src/revert.c
index 36560a77c..f8a7f4333 100644
--- a/src/revert.c
+++ b/src/revert.c
@@ -9,6 +9,7 @@
#include "repository.h"
#include "filebuf.h"
#include "merge.h"
+#include "index.h"
#include "git2/types.h"
#include "git2/merge.h"
@@ -174,7 +175,8 @@ int git_revert(
char commit_oidstr[GIT_OID_HEXSZ + 1];
const char *commit_msg;
git_buf their_label = GIT_BUF_INIT;
- git_index *index_new = NULL;
+ git_index *index = NULL;
+ git_indexwriter indexwriter = GIT_INDEXWRITER_INIT;
int error;
assert(repo && commit);
@@ -194,14 +196,16 @@ int git_revert(
if ((error = git_buf_printf(&their_label, "parent of %.7s... %s", commit_oidstr, commit_msg)) < 0 ||
(error = revert_normalize_opts(repo, &opts, given_opts, git_buf_cstr(&their_label))) < 0 ||
+ (error = git_indexwriter_init_for_operation(&indexwriter, repo, &opts.checkout_opts.checkout_strategy)) < 0 ||
(error = write_revert_head(repo, commit_oidstr)) < 0 ||
(error = write_merge_msg(repo, commit_oidstr, commit_msg)) < 0 ||
(error = git_repository_head(&our_ref, repo)) < 0 ||
(error = git_reference_peel((git_object **)&our_commit, our_ref, GIT_OBJ_COMMIT)) < 0 ||
- (error = git_revert_commit(&index_new, repo, commit, our_commit, opts.mainline, &opts.merge_opts)) < 0 ||
- (error = git_merge__check_result(repo, index_new)) < 0 ||
- (error = git_merge__append_conflicts_to_merge_msg(repo, index_new)) < 0 ||
- (error = git_checkout_index(repo, index_new, &opts.checkout_opts)) < 0)
+ (error = git_revert_commit(&index, repo, commit, our_commit, opts.mainline, &opts.merge_opts)) < 0 ||
+ (error = git_merge__check_result(repo, index)) < 0 ||
+ (error = git_merge__append_conflicts_to_merge_msg(repo, index)) < 0 ||
+ (error = git_checkout_index(repo, index, &opts.checkout_opts)) < 0 ||
+ (error = git_indexwriter_commit(&indexwriter)) < 0)
goto on_error;
goto done;
@@ -210,7 +214,8 @@ on_error:
revert_state_cleanup(repo);
done:
- git_index_free(index_new);
+ git_indexwriter_cleanup(&indexwriter);
+ git_index_free(index);
git_commit_free(our_commit);
git_reference_free(our_ref);
git_buf_free(&their_label);
diff --git a/src/revwalk.c b/src/revwalk.c
index e44385d48..2ba000c6b 100644
--- a/src/revwalk.c
+++ b/src/revwalk.c
@@ -503,13 +503,9 @@ static int prepare_walk(git_revwalk *walk)
int git_revwalk_new(git_revwalk **revwalk_out, git_repository *repo)
{
- git_revwalk *walk;
-
- walk = git__malloc(sizeof(git_revwalk));
+ git_revwalk *walk = git__calloc(1, sizeof(git_revwalk));
GITERR_CHECK_ALLOC(walk);
- memset(walk, 0x0, sizeof(git_revwalk));
-
walk->commits = git_oidmap_alloc();
GITERR_CHECK_ALLOC(walk->commits);
diff --git a/src/revwalk.h b/src/revwalk.h
index 72ddedc75..1148a2ac9 100644
--- a/src/revwalk.h
+++ b/src/revwalk.h
@@ -14,7 +14,7 @@
#include "pool.h"
#include "vector.h"
-GIT__USE_OIDMAP;
+GIT__USE_OIDMAP
struct git_revwalk {
git_repository *repo;
diff --git a/src/sortedcache.c b/src/sortedcache.c
index c6b226153..115175724 100644
--- a/src/sortedcache.c
+++ b/src/sortedcache.c
@@ -1,6 +1,6 @@
#include "sortedcache.h"
-GIT__USE_STRMAP;
+GIT__USE_STRMAP
int git_sortedcache_new(
git_sortedcache **out,
@@ -11,11 +11,13 @@ int git_sortedcache_new(
const char *path)
{
git_sortedcache *sc;
- size_t pathlen;
+ size_t pathlen, alloclen;
pathlen = path ? strlen(path) : 0;
- sc = git__calloc(sizeof(git_sortedcache) + pathlen + 1, 1);
+ GITERR_CHECK_ALLOC_ADD(&alloclen, sizeof(git_sortedcache), pathlen);
+ GITERR_CHECK_ALLOC_ADD(&alloclen, alloclen, 1);
+ sc = git__calloc(1, alloclen);
GITERR_CHECK_ALLOC(sc);
if (git_pool_init(&sc->pool, 1, 0) < 0 ||
diff --git a/src/strmap.h b/src/strmap.h
index 8985aaf7e..520984744 100644
--- a/src/strmap.h
+++ b/src/strmap.h
@@ -12,10 +12,11 @@
#define kmalloc git__malloc
#define kcalloc git__calloc
#define krealloc git__realloc
+#define kreallocarray git__reallocarray
#define kfree git__free
#include "khash.h"
-__KHASH_TYPE(str, const char *, void *);
+__KHASH_TYPE(str, const char *, void *)
typedef khash_t(str) git_strmap;
typedef khiter_t git_strmap_iter;
diff --git a/src/submodule.c b/src/submodule.c
index 03714b43b..80f1b3789 100644
--- a/src/submodule.c
+++ b/src/submodule.c
@@ -85,7 +85,7 @@ static kh_inline int str_equal_no_trailing_slash(const char *a, const char *b)
__KHASH_IMPL(
str, static kh_inline, const char *, void *, 1,
- str_hash_no_trailing_slash, str_equal_no_trailing_slash);
+ str_hash_no_trailing_slash, str_equal_no_trailing_slash)
static int submodule_cache_init(git_repository *repo, int refresh);
static void submodule_cache_free(git_submodule_cache *cache);
diff --git a/src/tag.c b/src/tag.c
index dc0827b8f..3b8d684ed 100644
--- a/src/tag.c
+++ b/src/tag.c
@@ -72,7 +72,7 @@ static int tag_parse(git_tag *tag, const char *buffer, const char *buffer_end)
};
unsigned int i;
- size_t text_len;
+ size_t text_len, alloc_len;
char *search;
if (git_oid__parse(&tag->target, &buffer, buffer_end, "object ") < 0)
@@ -117,7 +117,8 @@ static int tag_parse(git_tag *tag, const char *buffer, const char *buffer_end)
text_len = search - buffer;
- tag->tag_name = git__malloc(text_len + 1);
+ GITERR_CHECK_ALLOC_ADD(&alloc_len, text_len, 1);
+ tag->tag_name = git__malloc(alloc_len);
GITERR_CHECK_ALLOC(tag->tag_name);
memcpy(tag->tag_name, buffer, text_len);
@@ -141,7 +142,8 @@ static int tag_parse(git_tag *tag, const char *buffer, const char *buffer_end)
text_len = buffer_end - ++buffer;
- tag->message = git__malloc(text_len + 1);
+ GITERR_CHECK_ALLOC_ADD(&alloc_len, text_len, 1);
+ tag->message = git__malloc(alloc_len);
GITERR_CHECK_ALLOC(tag->message);
memcpy(tag->message, buffer, text_len);
diff --git a/src/transaction.c b/src/transaction.c
index 1a4fffb36..7b45b8bf2 100644
--- a/src/transaction.c
+++ b/src/transaction.c
@@ -17,7 +17,7 @@
#include "git2/sys/refs.h"
#include "git2/sys/refdb_backend.h"
-GIT__USE_STRMAP;
+GIT__USE_STRMAP
typedef struct {
const char *name;
diff --git a/src/transports/cred.c b/src/transports/cred.c
index 1b4d29c0a..8163d3115 100644
--- a/src/transports/cred.c
+++ b/src/transports/cred.c
@@ -306,12 +306,15 @@ int git_cred_default_new(git_cred **cred)
int git_cred_username_new(git_cred **cred, const char *username)
{
git_cred_username *c;
- size_t len;
+ size_t len, allocsize;
assert(cred);
len = strlen(username);
- c = git__malloc(sizeof(git_cred_username) + len + 1);
+
+ GITERR_CHECK_ALLOC_ADD(&allocsize, sizeof(git_cred_username), len);
+ GITERR_CHECK_ALLOC_ADD(&allocsize, allocsize, 1);
+ c = git__malloc(allocsize);
GITERR_CHECK_ALLOC(c);
c->parent.credtype = GIT_CREDTYPE_USERNAME;
diff --git a/src/transports/git.c b/src/transports/git.c
index 6f25736b1..5ec98d867 100644
--- a/src/transports/git.c
+++ b/src/transports/git.c
@@ -154,7 +154,7 @@ static int git_proto_stream_alloc(
if (!stream)
return -1;
- s = git__calloc(sizeof(git_proto_stream), 1);
+ s = git__calloc(1, sizeof(git_proto_stream));
GITERR_CHECK_ALLOC(s);
s->parent.subtransport = &t->parent;
@@ -347,7 +347,7 @@ int git_smart_subtransport_git(git_smart_subtransport **out, git_transport *owne
if (!out)
return -1;
- t = git__calloc(sizeof(git_subtransport), 1);
+ t = git__calloc(1, sizeof(git_subtransport));
GITERR_CHECK_ALLOC(t);
t->owner = owner;
diff --git a/src/transports/local.c b/src/transports/local.c
index c01755e34..89f2651cd 100644
--- a/src/transports/local.c
+++ b/src/transports/local.c
@@ -420,7 +420,7 @@ static int local_push(
const git_error *last;
char *ref = spec->refspec.dst;
- status = git__calloc(sizeof(push_status), 1);
+ status = git__calloc(1, sizeof(push_status));
if (!status)
goto on_error;
diff --git a/src/transports/smart.c b/src/transports/smart.c
index ec0ba3784..69b9d22cc 100644
--- a/src/transports/smart.c
+++ b/src/transports/smart.c
@@ -380,7 +380,7 @@ int git_transport_smart(git_transport **out, git_remote *owner, void *param)
if (!param)
return -1;
- t = git__calloc(sizeof(transport_smart), 1);
+ t = git__calloc(1, sizeof(transport_smart));
GITERR_CHECK_ALLOC(t);
t->parent.version = GIT_TRANSPORT_VERSION;
diff --git a/src/transports/smart_pkt.c b/src/transports/smart_pkt.c
index b5f9d6dbe..d214c9fa5 100644
--- a/src/transports/smart_pkt.c
+++ b/src/transports/smart_pkt.c
@@ -102,8 +102,11 @@ static int pack_pkt(git_pkt **out)
static int comment_pkt(git_pkt **out, const char *line, size_t len)
{
git_pkt_comment *pkt;
+ size_t alloclen;
- pkt = git__malloc(sizeof(git_pkt_comment) + len + 1);
+ GITERR_CHECK_ALLOC_ADD(&alloclen, sizeof(git_pkt_comment), len);
+ GITERR_CHECK_ALLOC_ADD(&alloclen, alloclen, 1);
+ pkt = git__malloc(alloclen);
GITERR_CHECK_ALLOC(pkt);
pkt->type = GIT_PKT_COMMENT;
@@ -118,11 +121,15 @@ static int comment_pkt(git_pkt **out, const char *line, size_t len)
static int err_pkt(git_pkt **out, const char *line, size_t len)
{
git_pkt_err *pkt;
+ size_t alloclen;
/* Remove "ERR " from the line */
line += 4;
len -= 4;
- pkt = git__malloc(sizeof(git_pkt_err) + len + 1);
+
+ GITERR_CHECK_ALLOC_ADD(&alloclen, sizeof(git_pkt_progress), len);
+ GITERR_CHECK_ALLOC_ADD(&alloclen, alloclen, 1);
+ pkt = git__malloc(alloclen);
GITERR_CHECK_ALLOC(pkt);
pkt->type = GIT_PKT_ERR;
@@ -138,10 +145,13 @@ static int err_pkt(git_pkt **out, const char *line, size_t len)
static int data_pkt(git_pkt **out, const char *line, size_t len)
{
git_pkt_data *pkt;
+ size_t alloclen;
line++;
len--;
- pkt = git__malloc(sizeof(git_pkt_data) + len);
+
+ GITERR_CHECK_ALLOC_ADD(&alloclen, sizeof(git_pkt_progress), len);
+ pkt = git__malloc(alloclen);
GITERR_CHECK_ALLOC(pkt);
pkt->type = GIT_PKT_DATA;
@@ -156,10 +166,13 @@ static int data_pkt(git_pkt **out, const char *line, size_t len)
static int sideband_progress_pkt(git_pkt **out, const char *line, size_t len)
{
git_pkt_progress *pkt;
+ size_t alloclen;
line++;
len--;
- pkt = git__malloc(sizeof(git_pkt_progress) + len);
+
+ GITERR_CHECK_ALLOC_ADD(&alloclen, sizeof(git_pkt_progress), len);
+ pkt = git__malloc(alloclen);
GITERR_CHECK_ALLOC(pkt);
pkt->type = GIT_PKT_PROGRESS;
@@ -174,10 +187,14 @@ static int sideband_progress_pkt(git_pkt **out, const char *line, size_t len)
static int sideband_error_pkt(git_pkt **out, const char *line, size_t len)
{
git_pkt_err *pkt;
+ size_t alloc_len;
line++;
len--;
- pkt = git__malloc(sizeof(git_pkt_err) + len + 1);
+
+ GITERR_CHECK_ALLOC_ADD(&alloc_len, sizeof(git_pkt_err), len);
+ GITERR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 1);
+ pkt = git__malloc(alloc_len);
GITERR_CHECK_ALLOC(pkt);
pkt->type = GIT_PKT_ERR;
@@ -197,6 +214,7 @@ static int ref_pkt(git_pkt **out, const char *line, size_t len)
{
int error;
git_pkt_ref *pkt;
+ size_t alloclen;
pkt = git__malloc(sizeof(git_pkt_ref));
GITERR_CHECK_ALLOC(pkt);
@@ -220,7 +238,8 @@ static int ref_pkt(git_pkt **out, const char *line, size_t len)
if (line[len - 1] == '\n')
--len;
- pkt->head.name = git__malloc(len + 1);
+ GITERR_CHECK_ALLOC_ADD(&alloclen, len, 1);
+ pkt->head.name = git__malloc(alloclen);
GITERR_CHECK_ALLOC(pkt->head.name);
memcpy(pkt->head.name, line, len);
@@ -242,6 +261,7 @@ static int ok_pkt(git_pkt **out, const char *line, size_t len)
{
git_pkt_ok *pkt;
const char *ptr;
+ size_t alloc_len;
pkt = git__malloc(sizeof(*pkt));
GITERR_CHECK_ALLOC(pkt);
@@ -249,10 +269,14 @@ static int ok_pkt(git_pkt **out, const char *line, size_t len)
pkt->type = GIT_PKT_OK;
line += 3; /* skip "ok " */
- ptr = strchr(line, '\n');
+ if (!(ptr = strchr(line, '\n'))) {
+ giterr_set(GITERR_NET, "Invalid packet line");
+ return -1;
+ }
len = ptr - line;
- pkt->ref = git__malloc(len + 1);
+ GITERR_CHECK_ALLOC_ADD(&alloc_len, len, 1);
+ pkt->ref = git__malloc(alloc_len);
GITERR_CHECK_ALLOC(pkt->ref);
memcpy(pkt->ref, line, len);
@@ -266,6 +290,7 @@ static int ng_pkt(git_pkt **out, const char *line, size_t len)
{
git_pkt_ng *pkt;
const char *ptr;
+ size_t alloclen;
pkt = git__malloc(sizeof(*pkt));
GITERR_CHECK_ALLOC(pkt);
@@ -273,20 +298,28 @@ static int ng_pkt(git_pkt **out, const char *line, size_t len)
pkt->type = GIT_PKT_NG;
line += 3; /* skip "ng " */
- ptr = strchr(line, ' ');
+ if (!(ptr = strchr(line, ' '))) {
+ giterr_set(GITERR_NET, "Invalid packet line");
+ return -1;
+ }
len = ptr - line;
- pkt->ref = git__malloc(len + 1);
+ GITERR_CHECK_ALLOC_ADD(&alloclen, len, 1);
+ pkt->ref = git__malloc(alloclen);
GITERR_CHECK_ALLOC(pkt->ref);
memcpy(pkt->ref, line, len);
pkt->ref[len] = '\0';
line = ptr + 1;
- ptr = strchr(line, '\n');
+ if (!(ptr = strchr(line, '\n'))) {
+ giterr_set(GITERR_NET, "Invalid packet line");
+ return -1;
+ }
len = ptr - line;
- pkt->msg = git__malloc(len + 1);
+ GITERR_CHECK_ALLOC_ADD(&alloclen, len, 1);
+ pkt->msg = git__malloc(alloclen);
GITERR_CHECK_ALLOC(pkt->msg);
memcpy(pkt->msg, line, len);
@@ -459,7 +492,7 @@ static int buffer_want_with_caps(const git_remote_head *head, transport_smart_ca
{
git_buf str = GIT_BUF_INIT;
char oid[GIT_OID_HEXSZ +1] = {0};
- unsigned int len;
+ size_t len;
/* Prefer multi_ack_detailed */
if (caps->multi_ack_detailed)
@@ -485,12 +518,19 @@ static int buffer_want_with_caps(const git_remote_head *head, transport_smart_ca
if (git_buf_oom(&str))
return -1;
- len = (unsigned int)
- (strlen("XXXXwant ") + GIT_OID_HEXSZ + 1 /* NUL */ +
- git_buf_len(&str) + 1 /* LF */);
- git_buf_grow(buf, git_buf_len(buf) + len);
+ len = strlen("XXXXwant ") + GIT_OID_HEXSZ + 1 /* NUL */ +
+ git_buf_len(&str) + 1 /* LF */;
+
+ if (len > 0xffff) {
+ giterr_set(GITERR_NET,
+ "Tried to produce packet with invalid length %d", len);
+ return -1;
+ }
+
+ git_buf_grow_by(buf, len);
git_oid_fmt(oid, &head->oid);
- git_buf_printf(buf, "%04xwant %s %s\n", len, oid, git_buf_cstr(&str));
+ git_buf_printf(buf,
+ "%04xwant %s %s\n", (unsigned int)len, oid, git_buf_cstr(&str));
git_buf_free(&str);
return git_buf_oom(buf);
diff --git a/src/transports/smart_protocol.c b/src/transports/smart_protocol.c
index 5f1b99892..f023db4df 100644
--- a/src/transports/smart_protocol.c
+++ b/src/transports/smart_protocol.c
@@ -685,7 +685,7 @@ static int add_push_report_pkt(git_push *push, git_pkt *pkt)
switch (pkt->type) {
case GIT_PKT_OK:
- status = git__calloc(sizeof(push_status), 1);
+ status = git__calloc(1, sizeof(push_status));
GITERR_CHECK_ALLOC(status);
status->msg = NULL;
status->ref = git__strdup(((git_pkt_ok *)pkt)->ref);
@@ -696,7 +696,7 @@ static int add_push_report_pkt(git_push *push, git_pkt *pkt)
}
break;
case GIT_PKT_NG:
- status = git__calloc(sizeof(push_status), 1);
+ status = git__calloc(1, sizeof(push_status));
GITERR_CHECK_ALLOC(status);
status->ref = git__strdup(((git_pkt_ng *)pkt)->ref);
status->msg = git__strdup(((git_pkt_ng *)pkt)->msg);
diff --git a/src/transports/winhttp.c b/src/transports/winhttp.c
index 56b7c99f9..278ef22c4 100644
--- a/src/transports/winhttp.c
+++ b/src/transports/winhttp.c
@@ -1178,7 +1178,7 @@ static int winhttp_stream_alloc(winhttp_subtransport *t, winhttp_stream **stream
if (!stream)
return -1;
- s = git__calloc(sizeof(winhttp_stream), 1);
+ s = git__calloc(1, sizeof(winhttp_stream));
GITERR_CHECK_ALLOC(s);
s->parent.subtransport = &t->parent;
@@ -1329,7 +1329,7 @@ int git_smart_subtransport_http(git_smart_subtransport **out, git_transport *own
if (!out)
return -1;
- t = git__calloc(sizeof(winhttp_subtransport), 1);
+ t = git__calloc(1, sizeof(winhttp_subtransport));
GITERR_CHECK_ALLOC(t);
t->owner = (transport_smart *)owner;
diff --git a/src/tree.c b/src/tree.c
index 9693f4eca..47ed20dbb 100644
--- a/src/tree.c
+++ b/src/tree.c
@@ -17,7 +17,7 @@
#define DEFAULT_TREE_SIZE 16
#define MAX_FILEMODE_BYTES 6
-GIT__USE_STRMAP;
+GIT__USE_STRMAP
static bool valid_filemode(const int filemode)
{
@@ -84,10 +84,11 @@ int git_tree_entry_icmp(const git_tree_entry *e1, const git_tree_entry *e2)
static git_tree_entry *alloc_entry(const char *filename)
{
git_tree_entry *entry = NULL;
- size_t filename_len = strlen(filename);
+ size_t filename_len = strlen(filename), tree_len;
- entry = git__malloc(sizeof(git_tree_entry) + filename_len + 1);
- if (!entry)
+ if (GIT_ADD_SIZET_OVERFLOW(&tree_len, sizeof(git_tree_entry), filename_len) ||
+ GIT_ADD_SIZET_OVERFLOW(&tree_len, tree_len, 1) ||
+ !(entry = git__malloc(tree_len)))
return NULL;
memset(entry, 0x0, sizeof(git_tree_entry));
@@ -210,7 +211,8 @@ int git_tree_entry_dup(git_tree_entry **dest, const git_tree_entry *source)
assert(source);
- total_size = sizeof(git_tree_entry) + source->filename_len + 1;
+ GITERR_CHECK_ALLOC_ADD(&total_size, sizeof(git_tree_entry), source->filename_len);
+ GITERR_CHECK_ALLOC_ADD(&total_size, total_size, 1);
copy = git__malloc(total_size);
GITERR_CHECK_ALLOC(copy);
diff --git a/src/tsort.c b/src/tsort.c
index 4885e435b..e59819204 100644
--- a/src/tsort.c
+++ b/src/tsort.c
@@ -184,7 +184,9 @@ static int check_invariant(struct tsort_run *stack, ssize_t stack_curr)
static int resize(struct tsort_store *store, size_t new_size)
{
if (store->alloc < new_size) {
- void **tempstore = git__realloc(store->storage, new_size * sizeof(void *));
+ void **tempstore;
+
+ tempstore = git__reallocarray(store->storage, new_size, sizeof(void *));
/**
* Do not propagate on OOM; this will abort the sort and
diff --git a/src/util.h b/src/util.h
index 89816a8c9..38dcae79b 100644
--- a/src/util.h
+++ b/src/util.h
@@ -59,14 +59,13 @@ GIT_INLINE(char *) git__strdup(const char *str)
GIT_INLINE(char *) git__strndup(const char *str, size_t n)
{
- size_t length = 0;
+ size_t length = 0, alloclength;
char *ptr;
length = p_strnlen(str, n);
- ptr = (char*)git__malloc(length + 1);
-
- if (!ptr)
+ if (GIT_ADD_SIZET_OVERFLOW(&alloclength, length, 1) ||
+ !(ptr = git__malloc(alloclength)))
return NULL;
if (length)
@@ -80,7 +79,13 @@ GIT_INLINE(char *) git__strndup(const char *str, size_t n)
/* NOTE: This doesn't do null or '\0' checking. Watch those boundaries! */
GIT_INLINE(char *) git__substrdup(const char *start, size_t n)
{
- char *ptr = (char*)git__malloc(n+1);
+ char *ptr;
+ size_t alloclen;
+
+ if (GIT_ADD_SIZET_OVERFLOW(&alloclen, n, 1) ||
+ !(ptr = git__malloc(alloclen)))
+ return NULL;
+
memcpy(ptr, start, n);
ptr[n] = '\0';
return ptr;
@@ -93,6 +98,26 @@ GIT_INLINE(void *) git__realloc(void *ptr, size_t size)
return new_ptr;
}
+/**
+ * Similar to `git__realloc`, except that it is suitable for reallocing an
+ * array to a new number of elements of `nelem`, each of size `elsize`.
+ * The total size calculation is checked for overflow.
+ */
+GIT_INLINE(void *) git__reallocarray(void *ptr, size_t nelem, size_t elsize)
+{
+ size_t newsize;
+ return GIT_MULTIPLY_SIZET_OVERFLOW(&newsize, nelem, elsize) ?
+ NULL : realloc(ptr, newsize);
+}
+
+/**
+ * Similar to `git__calloc`, except that it does not zero memory.
+ */
+GIT_INLINE(void *) git__mallocarray(size_t nelem, size_t elsize)
+{
+ return git__reallocarray(NULL, nelem, elsize);
+}
+
GIT_INLINE(void) git__free(void *ptr)
{
free(ptr);
@@ -120,27 +145,6 @@ extern int git__strtol64(int64_t *n, const char *buff, const char **end_buf, int
extern void git__hexdump(const char *buffer, size_t n);
extern uint32_t git__hash(const void *key, int len, uint32_t seed);
-/** @return true if p fits into the range of a size_t */
-GIT_INLINE(int) git__is_sizet(git_off_t p)
-{
- size_t r = (size_t)p;
- return p == (git_off_t)r;
-}
-
-/** @return true if p fits into the range of a uint32_t */
-GIT_INLINE(int) git__is_uint32(size_t p)
-{
- uint32_t r = (uint32_t)p;
- return p == (size_t)r;
-}
-
-/** @return true if p fits into the range of an unsigned long */
-GIT_INLINE(int) git__is_ulong(git_off_t p)
-{
- unsigned long r = (unsigned long)p;
- return p == (git_off_t)r;
-}
-
/* 32-bit cross-platform rotl */
#ifdef _MSC_VER /* use built-in method in MSVC */
# define git__rotl(v, s) (uint32_t)_rotl(v, s)
diff --git a/src/vector.c b/src/vector.c
index c769b696a..93d09bb5b 100644
--- a/src/vector.c
+++ b/src/vector.c
@@ -29,14 +29,9 @@ GIT_INLINE(size_t) compute_new_size(git_vector *v)
GIT_INLINE(int) resize_vector(git_vector *v, size_t new_size)
{
- size_t new_bytes = new_size * sizeof(void *);
void *new_contents;
- /* Check for overflow */
- if (new_bytes / sizeof(void *) != new_size)
- GITERR_CHECK_ALLOC(NULL);
-
- new_contents = git__realloc(v->contents, new_bytes);
+ new_contents = git__reallocarray(v->contents, new_size, sizeof(void *));
GITERR_CHECK_ALLOC(new_contents);
v->_alloc_size = new_size;
@@ -51,7 +46,7 @@ int git_vector_dup(git_vector *v, const git_vector *src, git_vector_cmp cmp)
assert(v && src);
- bytes = src->length * sizeof(void *);
+ GITERR_CHECK_ALLOC_MULTIPLY(&bytes, src->length, sizeof(void *));
v->_alloc_size = src->length;
v->_cmp = cmp ? cmp : src->_cmp;
diff --git a/src/win32/dir.c b/src/win32/dir.c
index c7427ea54..c15757085 100644
--- a/src/win32/dir.c
+++ b/src/win32/dir.c
@@ -11,16 +11,18 @@ git__DIR *git__opendir(const char *dir)
{
git_win32_path filter_w;
git__DIR *new = NULL;
- size_t dirlen;
+ size_t dirlen, alloclen;
if (!dir || !git_win32__findfirstfile_filter(filter_w, dir))
return NULL;
dirlen = strlen(dir);
- new = git__calloc(sizeof(*new) + dirlen + 1, 1);
- if (!new)
+ if (GIT_ADD_SIZET_OVERFLOW(&alloclen, sizeof(*new), dirlen) ||
+ GIT_ADD_SIZET_OVERFLOW(&alloclen, alloclen, 1) ||
+ !(new = git__calloc(1, alloclen)))
return NULL;
+
memcpy(new->dir, dir, dirlen);
new->h = FindFirstFileW(filter_w, &new->f);
diff --git a/src/win32/utf-conv.c b/src/win32/utf-conv.c
index b0205b019..0dad4eab0 100644
--- a/src/win32/utf-conv.c
+++ b/src/win32/utf-conv.c
@@ -99,9 +99,7 @@ int git__utf8_to_16_alloc(wchar_t **dest, const char *src)
return -1;
}
- *dest = git__malloc(utf16_size * sizeof(wchar_t));
-
- if (!*dest) {
+ if (!(*dest = git__mallocarray(utf16_size, sizeof(wchar_t)))) {
errno = ENOMEM;
return -1;
}
diff --git a/src/zstream.c b/src/zstream.c
index e75fb265e..2130bc3ca 100644
--- a/src/zstream.c
+++ b/src/zstream.c
@@ -134,7 +134,7 @@ int git_zstream_deflatebuf(git_buf *out, const void *in, size_t in_len)
while (!git_zstream_done(&zs)) {
size_t step = git_zstream_suggest_output_len(&zs), written;
- if ((error = git_buf_grow(out, out->size + step)) < 0)
+ if ((error = git_buf_grow_by(out, step)) < 0)
goto done;
written = out->asize - out->size;
diff --git a/tests/buf/basic.c b/tests/buf/basic.c
index d558757a9..14ea3e7ce 100644
--- a/tests/buf/basic.c
+++ b/tests/buf/basic.c
@@ -15,6 +15,28 @@ void test_buf_basic__resize(void)
git_buf_free(&buf1);
}
+void test_buf_basic__resize_incremental(void)
+{
+ git_buf buf1 = GIT_BUF_INIT;
+
+ /* Presently, asking for 6 bytes will round up to 8. */
+ cl_git_pass(git_buf_puts(&buf1, "Hello"));
+ cl_assert_equal_i(5, buf1.size);
+ cl_assert_equal_i(8, buf1.asize);
+
+ /* Ensure an additional byte does not realloc. */
+ cl_git_pass(git_buf_grow_by(&buf1, 1));
+ cl_assert_equal_i(5, buf1.size);
+ cl_assert_equal_i(8, buf1.asize);
+
+ /* But requesting many does. */
+ cl_git_pass(git_buf_grow_by(&buf1, 16));
+ cl_assert_equal_i(5, buf1.size);
+ cl_assert(buf1.asize > 8);
+
+ git_buf_free(&buf1);
+}
+
void test_buf_basic__printf(void)
{
git_buf buf2 = GIT_BUF_INIT;
diff --git a/tests/buf/oom.c b/tests/buf/oom.c
index 709439aa5..b9fd29cbb 100644
--- a/tests/buf/oom.c
+++ b/tests/buf/oom.c
@@ -29,3 +29,13 @@ void test_buf_oom__grow(void)
git_buf_free(&buf);
}
+
+void test_buf_oom__grow_by(void)
+{
+ git_buf buf = GIT_BUF_INIT;
+
+ buf.size = SIZE_MAX-10;
+
+ cl_assert(git_buf_grow_by(&buf, 50) == -1);
+ cl_assert(git_buf_oom(&buf));
+}
diff --git a/tests/checkout/tree.c b/tests/checkout/tree.c
index 239ee55bc..0fabadc8d 100644
--- a/tests/checkout/tree.c
+++ b/tests/checkout/tree.c
@@ -1184,3 +1184,83 @@ void test_checkout_tree__caches_attributes_during_checkout(void)
git_buf_free(&ident2);
git_object_free(obj);
}
+
+void test_checkout_tree__can_not_update_index(void)
+{
+ git_oid oid;
+ git_object *head;
+ unsigned int status;
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ git_index *index;
+
+ opts.checkout_strategy |=
+ GIT_CHECKOUT_FORCE | GIT_CHECKOUT_DONT_UPDATE_INDEX;
+
+ cl_git_pass(git_reference_name_to_id(&oid, g_repo, "HEAD"));
+ cl_git_pass(git_object_lookup(&head, g_repo, &oid, GIT_OBJ_ANY));
+
+ cl_git_pass(git_reset(g_repo, head, GIT_RESET_HARD, &g_opts, NULL, NULL));
+
+ cl_assert_equal_i(false, git_path_isdir("./testrepo/ab/"));
+
+ cl_git_pass(git_revparse_single(&g_object, g_repo, "subtrees"));
+
+ cl_git_pass(git_checkout_tree(g_repo, g_object, &opts));
+
+ cl_assert_equal_i(true, git_path_isfile("./testrepo/ab/de/2.txt"));
+ cl_git_pass(git_status_file(&status, g_repo, "ab/de/2.txt"));
+ cl_assert_equal_i(GIT_STATUS_WT_NEW, status);
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(git_status_file(&status, g_repo, "ab/de/2.txt"));
+ cl_assert_equal_i(GIT_STATUS_WT_NEW, status);
+
+ git_object_free(head);
+ git_index_free(index);
+}
+
+void test_checkout_tree__can_update_but_not_write_index(void)
+{
+ git_oid oid;
+ git_object *head;
+ unsigned int status;
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ git_index *index;
+ git_repository *other;
+
+ opts.checkout_strategy |=
+ GIT_CHECKOUT_FORCE | GIT_CHECKOUT_DONT_WRITE_INDEX;
+
+ cl_git_pass(git_reference_name_to_id(&oid, g_repo, "HEAD"));
+ cl_git_pass(git_object_lookup(&head, g_repo, &oid, GIT_OBJ_ANY));
+
+ cl_git_pass(git_reset(g_repo, head, GIT_RESET_HARD, &g_opts, NULL, NULL));
+
+ cl_assert_equal_i(false, git_path_isdir("./testrepo/ab/"));
+
+ cl_git_pass(git_revparse_single(&g_object, g_repo, "subtrees"));
+
+ cl_git_pass(git_checkout_tree(g_repo, g_object, &opts));
+
+ cl_assert_equal_i(true, git_path_isfile("./testrepo/ab/de/2.txt"));
+ cl_git_pass(git_status_file(&status, g_repo, "ab/de/2.txt"));
+ cl_assert_equal_i(GIT_STATUS_INDEX_NEW, status);
+
+ cl_git_pass(git_repository_open(&other, "testrepo"));
+ cl_git_pass(git_status_file(&status, other, "ab/de/2.txt"));
+ cl_assert_equal_i(GIT_STATUS_WT_NEW, status);
+ git_repository_free(other);
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(git_repository_open(&other, "testrepo"));
+ cl_git_pass(git_status_file(&status, other, "ab/de/2.txt"));
+ cl_assert_equal_i(GIT_STATUS_INDEX_NEW, status);
+ git_repository_free(other);
+
+ git_object_free(head);
+ git_index_free(index);
+}
diff --git a/tests/core/errors.c b/tests/core/errors.c
index 366d8f16a..a06ec4abc 100644
--- a/tests/core/errors.c
+++ b/tests/core/errors.c
@@ -109,3 +109,68 @@ void test_core_errors__restore(void)
cl_assert_equal_i(42, giterr_last()->klass);
cl_assert_equal_s("Foo: bar", giterr_last()->message);
}
+
+static int test_arraysize_multiply(size_t nelem, size_t size)
+{
+ size_t out;
+ GITERR_CHECK_ALLOC_MULTIPLY(&out, nelem, size);
+ return 0;
+}
+
+void test_core_errors__integer_overflow_alloc_multiply(void)
+{
+ cl_git_pass(test_arraysize_multiply(10, 10));
+ cl_git_pass(test_arraysize_multiply(1000, 1000));
+ cl_git_pass(test_arraysize_multiply(SIZE_MAX/sizeof(void *), sizeof(void *)));
+ cl_git_pass(test_arraysize_multiply(0, 10));
+ cl_git_pass(test_arraysize_multiply(10, 0));
+
+ cl_git_fail(test_arraysize_multiply(SIZE_MAX-1, sizeof(void *)));
+ cl_git_fail(test_arraysize_multiply((SIZE_MAX/sizeof(void *))+1, sizeof(void *)));
+
+ cl_assert_equal_i(GITERR_NOMEMORY, giterr_last()->klass);
+ cl_assert_equal_s("Out of memory", giterr_last()->message);
+}
+
+static int test_arraysize_add(size_t one, size_t two)
+{
+ size_t out;
+ GITERR_CHECK_ALLOC_ADD(&out, one, two);
+ return 0;
+}
+
+void test_core_errors__integer_overflow_alloc_add(void)
+{
+ cl_git_pass(test_arraysize_add(10, 10));
+ cl_git_pass(test_arraysize_add(1000, 1000));
+ cl_git_pass(test_arraysize_add(SIZE_MAX-10, 10));
+
+ cl_git_fail(test_arraysize_multiply(SIZE_MAX-1, 2));
+ cl_git_fail(test_arraysize_multiply(SIZE_MAX, SIZE_MAX));
+
+ cl_assert_equal_i(GITERR_NOMEMORY, giterr_last()->klass);
+ cl_assert_equal_s("Out of memory", giterr_last()->message);
+}
+
+void test_core_errors__integer_overflow_sets_oom(void)
+{
+ size_t out;
+
+ giterr_clear();
+ cl_assert(!GIT_ADD_SIZET_OVERFLOW(&out, SIZE_MAX-1, 1));
+ cl_assert_equal_p(NULL, giterr_last());
+
+ giterr_clear();
+ cl_assert(!GIT_ADD_SIZET_OVERFLOW(&out, 42, 69));
+ cl_assert_equal_p(NULL, giterr_last());
+
+ giterr_clear();
+ cl_assert(GIT_ADD_SIZET_OVERFLOW(&out, SIZE_MAX, SIZE_MAX));
+ cl_assert_equal_i(GITERR_NOMEMORY, giterr_last()->klass);
+ cl_assert_equal_s("Out of memory", giterr_last()->message);
+
+ giterr_clear();
+ cl_assert(GIT_ADD_SIZET_OVERFLOW(&out, SIZE_MAX, SIZE_MAX));
+ cl_assert_equal_i(GITERR_NOMEMORY, giterr_last()->klass);
+ cl_assert_equal_s("Out of memory", giterr_last()->message);
+}
diff --git a/tests/core/oidmap.c b/tests/core/oidmap.c
index ec4b5e775..556a6ca4a 100644
--- a/tests/core/oidmap.c
+++ b/tests/core/oidmap.c
@@ -1,7 +1,7 @@
#include "clar_libgit2.h"
#include "oidmap.h"
-GIT__USE_OIDMAP;
+GIT__USE_OIDMAP
typedef struct {
git_oid oid;
diff --git a/tests/core/strmap.c b/tests/core/strmap.c
index a120f1feb..3b4276aea 100644
--- a/tests/core/strmap.c
+++ b/tests/core/strmap.c
@@ -1,7 +1,7 @@
#include "clar_libgit2.h"
#include "strmap.h"
-GIT__USE_STRMAP;
+GIT__USE_STRMAP
git_strmap *g_table;
diff --git a/tests/index/tests.c b/tests/index/tests.c
index a63183e10..4cf705127 100644
--- a/tests/index/tests.c
+++ b/tests/index/tests.c
@@ -677,3 +677,24 @@ void test_index_tests__reload_while_ignoring_case(void)
git_index_free(index);
}
+
+void test_index_tests__can_lock_index(void)
+{
+ git_index *index;
+ git_indexwriter one = GIT_INDEXWRITER_INIT,
+ two = GIT_INDEXWRITER_INIT;
+
+ cl_git_pass(git_index_open(&index, TEST_INDEX_PATH));
+ cl_git_pass(git_indexwriter_init(&one, index));
+
+ cl_git_fail_with(GIT_ELOCKED, git_indexwriter_init(&two, index));
+ cl_git_fail_with(GIT_ELOCKED, git_index_write(index));
+
+ cl_git_pass(git_indexwriter_commit(&one));
+
+ cl_git_pass(git_index_write(index));
+
+ git_indexwriter_cleanup(&one);
+ git_indexwriter_cleanup(&two);
+ git_index_free(index);
+}
diff --git a/tests/merge/workdir/setup.c b/tests/merge/workdir/setup.c
index 099bc1211..4aebf8701 100644
--- a/tests/merge/workdir/setup.c
+++ b/tests/merge/workdir/setup.c
@@ -1018,6 +1018,7 @@ void test_merge_workdir_setup__retained_after_success(void)
git_annotated_commit_free(their_heads[0]);
}
+
void test_merge_workdir_setup__removed_after_failure(void)
{
git_oid our_oid;
@@ -1030,16 +1031,63 @@ void test_merge_workdir_setup__removed_after_failure(void)
cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH));
cl_git_pass(git_annotated_commit_from_ref(&their_heads[0], repo, octo1_ref));
+ cl_git_write2file("merge-resolve/.git/index.lock", "foo\n", 4, O_RDWR|O_CREAT, 0666);
+
+ cl_git_fail(git_merge(
+ repo, (const git_annotated_commit **)&their_heads[0], 1, NULL, NULL));
+
+ cl_assert(!git_path_exists("merge-resolve/.git/" GIT_MERGE_HEAD_FILE));
+ cl_assert(!git_path_exists("merge-resolve/.git/" GIT_MERGE_MODE_FILE));
+ cl_assert(!git_path_exists("merge-resolve/.git/" GIT_MERGE_MSG_FILE));
+
+ git_reference_free(octo1_ref);
+
+ git_annotated_commit_free(our_head);
+ git_annotated_commit_free(their_heads[0]);
+}
+
+void test_merge_workdir_setup__unlocked_after_success(void)
+{
+ git_oid our_oid;
+ git_reference *octo1_ref;
+ git_annotated_commit *our_head, *their_heads[1];
+
+ cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD));
+ cl_git_pass(git_annotated_commit_lookup(&our_head, repo, &our_oid));
+
+ cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH));
+ cl_git_pass(git_annotated_commit_from_ref(&their_heads[0], repo, octo1_ref));
+
+ cl_git_pass(git_merge(
+ repo, (const git_annotated_commit **)&their_heads[0], 1, NULL, NULL));
+
+ cl_assert(!git_path_exists("merge-resolve/.git/index.lock"));
+
+ git_reference_free(octo1_ref);
+
+ git_annotated_commit_free(our_head);
+ git_annotated_commit_free(their_heads[0]);
+}
+
+void test_merge_workdir_setup__unlocked_after_conflict(void)
+{
+ git_oid our_oid;
+ git_reference *octo1_ref;
+ git_annotated_commit *our_head, *their_heads[1];
+
+ cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD));
+ cl_git_pass(git_annotated_commit_lookup(&our_head, repo, &our_oid));
+
+ cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH));
+ cl_git_pass(git_annotated_commit_from_ref(&their_heads[0], repo, octo1_ref));
+
cl_git_rewritefile("merge-resolve/new-in-octo1.txt",
"Conflicting file!\n\nMerge will fail!\n");
cl_git_fail(git_merge(
repo, (const git_annotated_commit **)&their_heads[0], 1, NULL, NULL));
- cl_assert(!git_path_exists("merge-resolve/" GIT_MERGE_HEAD_FILE));
- cl_assert(!git_path_exists("merge-resolve/" GIT_ORIG_HEAD_FILE));
- cl_assert(!git_path_exists("merge-resolve/" GIT_MERGE_MODE_FILE));
- cl_assert(!git_path_exists("merge-resolve/" GIT_MERGE_MSG_FILE));
+ cl_assert(!git_path_exists("merge-resolve/.git/index.lock"));
git_reference_free(octo1_ref);
diff --git a/tests/rebase/iterator.c b/tests/rebase/iterator.c
index ddf4413d3..8117a094a 100644
--- a/tests/rebase/iterator.c
+++ b/tests/rebase/iterator.c
@@ -42,6 +42,7 @@ static void test_operations(git_rebase *rebase, size_t expected_current)
operation = git_rebase_operation_byindex(rebase, i);
cl_assert_equal_i(GIT_REBASE_OPERATION_PICK, operation->type);
cl_assert_equal_oid(&expected_oid[i], &operation->id);
+ cl_assert_equal_p(NULL, operation->exec);
}
}
diff --git a/tests/rebase/setup.c b/tests/rebase/setup.c
index c81ca1245..f1eb6a47d 100644
--- a/tests/rebase/setup.c
+++ b/tests/rebase/setup.c
@@ -293,6 +293,55 @@ void test_rebase_setup__orphan_branch(void)
git_rebase_free(rebase);
}
+/* git checkout beef && git rebase --merge master */
+void test_rebase_setup__merge_null_branch_uses_HEAD(void)
+{
+ git_rebase *rebase;
+ git_reference *upstream_ref;
+ git_annotated_commit *upstream_head;
+ git_reference *head;
+ git_commit *head_commit;
+ git_oid head_id;
+ git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
+
+ checkout_opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ cl_assert_equal_i(GIT_REPOSITORY_STATE_NONE, git_repository_state(repo));
+
+ cl_git_pass(git_repository_set_head(repo, "refs/heads/beef", NULL, NULL));
+ cl_git_pass(git_checkout_head(repo, &checkout_opts));
+
+ cl_git_pass(git_reference_lookup(&upstream_ref, repo, "refs/heads/master"));
+ cl_git_pass(git_annotated_commit_from_ref(&upstream_head, repo, upstream_ref));
+
+ cl_git_pass(git_rebase_init(&rebase, repo, NULL, upstream_head, NULL, signature, NULL));
+
+ cl_assert_equal_i(GIT_REPOSITORY_STATE_REBASE_MERGE, git_repository_state(repo));
+
+ git_oid_fromstr(&head_id, "efad0b11c47cb2f0220cbd6f5b0f93bb99064b00");
+ cl_git_pass(git_repository_head(&head, repo));
+ cl_git_pass(git_reference_peel((git_object **)&head_commit, head, GIT_OBJ_COMMIT));
+ cl_assert_equal_oid(&head_id, git_commit_id(head_commit));
+
+ cl_assert_equal_file("b146bd7608eac53d9bf9e1a6963543588b555c64\n", 41, "rebase/.git/ORIG_HEAD");
+
+ cl_assert_equal_file("da9c51a23d02d931a486f45ad18cda05cf5d2b94\n", 41, "rebase/.git/rebase-merge/cmt.1");
+ cl_assert_equal_file("8d1f13f93c4995760ac07d129246ac1ff64c0be9\n", 41, "rebase/.git/rebase-merge/cmt.2");
+ cl_assert_equal_file("3069cc907e6294623e5917ef6de663928c1febfb\n", 41, "rebase/.git/rebase-merge/cmt.3");
+ cl_assert_equal_file("588e5d2f04d49707fe4aab865e1deacaf7ef6787\n", 41, "rebase/.git/rebase-merge/cmt.4");
+ cl_assert_equal_file("b146bd7608eac53d9bf9e1a6963543588b555c64\n", 41, "rebase/.git/rebase-merge/cmt.5");
+ cl_assert_equal_file("5\n", 2, "rebase/.git/rebase-merge/end");
+ cl_assert_equal_file("efad0b11c47cb2f0220cbd6f5b0f93bb99064b00\n", 41, "rebase/.git/rebase-merge/onto");
+ cl_assert_equal_file("master\n", 7, "rebase/.git/rebase-merge/onto_name");
+ cl_assert_equal_file("b146bd7608eac53d9bf9e1a6963543588b555c64\n", 41, "rebase/.git/rebase-merge/orig-head");
+
+ git_commit_free(head_commit);
+ git_reference_free(head);
+ git_annotated_commit_free(upstream_head);
+ git_reference_free(upstream_ref);
+ git_rebase_free(rebase);
+}
+
static int rebase_is_blocked(void)
{
git_rebase *rebase = NULL;