diff options
| author | Edward Thomson <ethomson@microsoft.com> | 2015-02-09 23:41:13 -0500 |
|---|---|---|
| committer | Edward Thomson <ethomson@edwardthomson.com> | 2015-02-12 22:54:46 -0500 |
| commit | 392702ee2c88d7d8aaff25f7a84acb73606f9094 (patch) | |
| tree | 97a66fe6e488797c6a9c2680ccb31964f61fe340 /src | |
| parent | d24a5312d8ab6d3cdb259e450ec9f1e2e6f3399d (diff) | |
| download | libgit2-392702ee2c88d7d8aaff25f7a84acb73606f9094.tar.gz | |
allocations: test for overflow of requested size
Introduce some helper macros to test integer overflow from arithmetic
and set error message appropriately.
Diffstat (limited to 'src')
47 files changed, 488 insertions, 117 deletions
diff --git a/src/array.h b/src/array.h index af9eafa43..ca5a35ffe 100644 --- a/src/array.h +++ b/src/array.h @@ -45,15 +45,28 @@ 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; + uint32_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_ALLOC_OVERFLOW_MULTIPLY(a->size, 3 / 2)) + goto on_oom; + + new_size = a->size * 3 / 2; } + + if (GIT_ALLOC_OVERFLOW_MULTIPLY(new_size, item_size) || + (new_array = git__realloc(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/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..05aef5d99 100644 --- a/src/blame_git.c +++ b/src/blame_git.c @@ -35,10 +35,14 @@ 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); + int error = 0; + + GITERR_CHECK_ALLOC_ADD(sizeof(*o), path_len); + GITERR_CHECK_ALLOC_ADD(sizeof(*o) + path_len, 1); - o = git__calloc(1, sizeof(*o) + strlen(path) + 1); + o = git__calloc(1, sizeof(*o) + path_len + 1); GITERR_CHECK_ALLOC(o); o->commit = commit; o->refcnt = 1; diff --git a/src/buf_text.c b/src/buf_text.c index cb3661edb..ace54d725 100644 --- a/src/buf_text.c +++ b/src/buf_text.c @@ -29,6 +29,8 @@ int git_buf_text_puts_escaped( scan += count; } + GITERR_CHECK_ALLOC_ADD(buf->size, total); + GITERR_CHECK_ALLOC_ADD(buf->size + total, 1); if (git_buf_grow(buf, buf->size + total + 1) < 0) return -1; @@ -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 */ + GITERR_CHECK_ALLOC_ADD(src->size, 1); if (git_buf_grow(tgt, src->size + 1) < 0) return -1; + out = tgt->ptr; tgt->size = 0; @@ -117,13 +121,15 @@ 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 */ + GITERR_CHECK_ALLOC_ADD(src->size, src->size >> 4); + GITERR_CHECK_ALLOC_ADD(src->size + (src->size >> 4), 1); if (git_buf_grow(tgt, src->size + (src->size >> 4) + 1) < 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; + size_t needsize; /* if we find mixed line endings, bail */ if (next > start && next[-1] == '\r') { @@ -131,6 +137,10 @@ int git_buf_text_lf_to_crlf(git_buf *tgt, const git_buf *src) return GIT_PASSTHROUGH; } + GITERR_CHECK_ALLOC_ADD(tgt->size, copylen); + GITERR_CHECK_ALLOC_ADD(tgt->size + copylen, 3); + needsize = tgt->size + copylen + 3; + if (tgt->asize < needsize && git_buf_grow(tgt, needsize) < 0) return -1; diff --git a/src/buffer.c b/src/buffer.c index 8013457c5..ee8bba4ec 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) { @@ -131,6 +139,7 @@ int git_buf_set(git_buf *buf, const void *data, size_t len) git_buf_clear(buf); } else { if (data != buf->ptr) { + GITERR_CHECK_ALLOC_ADD(len, 1); ENSURE_SIZE(buf, len + 1); memmove(buf->ptr, data, len); } @@ -160,6 +169,7 @@ int git_buf_sets(git_buf *buf, const char *string) int git_buf_putc(git_buf *buf, char c) { + GITERR_CHECK_ALLOC_ADD(buf->size, 2); ENSURE_SIZE(buf, buf->size + 2); buf->ptr[buf->size++] = c; buf->ptr[buf->size] = '\0'; @@ -168,6 +178,8 @@ int git_buf_putc(git_buf *buf, char c) int git_buf_putcn(git_buf *buf, char c, size_t len) { + GITERR_CHECK_ALLOC_ADD(buf->size, len); + GITERR_CHECK_ALLOC_ADD(buf->size + len, 1); ENSURE_SIZE(buf, buf->size + len + 1); memset(buf->ptr + buf->size, c, len); buf->size += len; @@ -179,6 +191,8 @@ int git_buf_put(git_buf *buf, const char *data, size_t len) { if (len) { assert(data); + GITERR_CHECK_ALLOC_ADD(buf->size, len); + GITERR_CHECK_ALLOC_ADD(buf->size + len, 1); ENSURE_SIZE(buf, buf->size + len + 1); memmove(buf->ptr + buf->size, data, len); buf->size += len; @@ -201,8 +215,12 @@ 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; - ENSURE_SIZE(buf, buf->size + 4 * ((len / 3) + !!extra) + 1); + GITERR_CHECK_ALLOC_MULTIPLY(blocks, 4); + GITERR_CHECK_ALLOC_ADD(buf->size, 4 * blocks); + GITERR_CHECK_ALLOC_ADD(buf->size + 4 * blocks, 1); + ENSURE_SIZE(buf, buf->size + 4 * blocks + 1); write = (uint8_t *)&buf->ptr[buf->size]; /* convert each run of 3 bytes into 4 output bytes */ @@ -256,6 +274,8 @@ int git_buf_decode_base64(git_buf *buf, const char *base64, size_t len) size_t orig_size = buf->size; assert(len % 4 == 0); + GITERR_CHECK_ALLOC_ADD(buf->size, len / 4 * 3); + GITERR_CHECK_ALLOC_ADD(buf->size + (len / 4 * 3), 1); ENSURE_SIZE(buf, buf->size + (len / 4 * 3) + 1); for (i = 0; i < len; i += 4) { @@ -284,7 +304,12 @@ 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); + + GITERR_CHECK_ALLOC_MULTIPLY(blocks, 5); + GITERR_CHECK_ALLOC_ADD(buf->size, 5 * blocks); + GITERR_CHECK_ALLOC_ADD(buf->size + 5 * blocks, 1); + ENSURE_SIZE(buf, buf->size + blocks * 5 + 1); while (len) { uint32_t acc = 0; @@ -317,8 +342,14 @@ 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 = strlen(format); int len; - const size_t expected_size = buf->size + (strlen(format) * 2); + + GITERR_CHECK_ALLOC_MULTIPLY(expected_size, 2); + expected_size *= 2; + + GITERR_CHECK_ALLOC_ADD(expected_size, buf->size); + expected_size += buf->size; ENSURE_SIZE(buf, expected_size); @@ -345,6 +376,8 @@ int git_buf_vprintf(git_buf *buf, const char *format, va_list ap) break; } + GITERR_CHECK_ALLOC_ADD(buf->size, len); + GITERR_CHECK_ALLOC_ADD(buf->size + len, 1); ENSURE_SIZE(buf, buf->size + len + 1); } @@ -481,6 +514,9 @@ int git_buf_join_n(git_buf *buf, char separator, int nbuf, ...) /* expand buffer if needed */ if (total_size == 0) return 0; + + GITERR_CHECK_ALLOC_ADD(buf->size, total_size); + GITERR_CHECK_ALLOC_ADD(buf->size + total_size, 1); if (git_buf_grow(buf, buf->size + total_size + 1) < 0) return -1; @@ -559,6 +595,9 @@ int git_buf_join( if (str_a >= buf->ptr && str_a < buf->ptr + buf->size) offset_a = str_a - buf->ptr; + GITERR_CHECK_ALLOC_ADD(strlen_a, strlen_b); + GITERR_CHECK_ALLOC_ADD(strlen_a + strlen_b, need_sep); + GITERR_CHECK_ALLOC_ADD(strlen_a + strlen_b + need_sep, 1); if (git_buf_grow(buf, strlen_a + strlen_b + need_sep + 1) < 0) return -1; assert(buf->ptr); @@ -607,6 +646,11 @@ int git_buf_join3( sep_b = (str_b[len_b - 1] != separator); } + GITERR_CHECK_ALLOC_ADD(len_a, sep_a); + GITERR_CHECK_ALLOC_ADD(len_a + sep_a, len_b); + GITERR_CHECK_ALLOC_ADD(len_a + sep_a + len_b, sep_b); + GITERR_CHECK_ALLOC_ADD(len_a + sep_a + len_b + sep_b, len_c); + GITERR_CHECK_ALLOC_ADD(len_a + sep_a + len_b + sep_b + len_c, 1); if (git_buf_grow(buf, len_a + sep_a + len_b + sep_b + len_c + 1) < 0) return -1; @@ -660,6 +704,8 @@ int git_buf_splice( const char *data, size_t nb_to_insert) { + size_t new_size; + assert(buf && where <= git_buf_len(buf) && where + nb_to_remove <= git_buf_len(buf)); @@ -667,7 +713,13 @@ int git_buf_splice( /* 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); + new_size = buf->size - nb_to_remove; + + GITERR_CHECK_ALLOC_ADD(new_size, nb_to_insert); + new_size += nb_to_insert; + + GITERR_CHECK_ALLOC_ADD(new_size, 1); + ENSURE_SIZE(buf, new_size + 1); memmove(buf->ptr + where + nb_to_insert, buf->ptr + where + nb_to_remove, @@ -675,7 +727,7 @@ int git_buf_splice( memcpy(buf->ptr + where, 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/common.h b/src/common.h index 4b4a99775..b53798eaf 100644 --- a/src/common.h +++ b/src/common.h @@ -174,6 +174,28 @@ 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 integer overflow from addition or multiplication */ +#define GIT_ALLOC_OVERFLOW_ADD(one, two) \ + ((one) + (two) < (one)) + +/** Check for integer overflow from multiplication */ +#define GIT_ALLOC_OVERFLOW_MULTIPLY(one, two) \ + (one && ((one) * (two)) / (one) != (two)) + +/** Check for additive overflow, failing if it would occur. */ +#define GITERR_CHECK_ALLOC_ADD(one, two) \ + if (GIT_ALLOC_OVERFLOW_ADD(one, two)) { \ + giterr_set_oom(); \ + return -1; \ + } + +/** Check for multiplicative overflow, failing if it would occur. */ +#define GITERR_CHECK_ALLOC_MULTIPLY(nelem, elsize) \ + if (GIT_ALLOC_OVERFLOW_MULTIPLY(nelem, elsize)) { \ + giterr_set_oom(); \ + 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 4f041e7e3..36f78563b 100644 --- a/src/config_file.c +++ b/src/config_file.c @@ -903,9 +903,11 @@ 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_ALLOC_OVERFLOW_ADD(line_len, 1) || + (line = git__malloc(line_len + 1)) == NULL) { + giterr_set_oom(); return NULL; + } memcpy(line, line_src, line_len); @@ -958,6 +960,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, 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 +970,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(base_name_len, quoted_len); + GITERR_CHECK_ALLOC_ADD(base_name_len + quoted_len, 2); + + git_buf_grow(&buf, base_name_len + quoted_len + 2); git_buf_printf(&buf, "%s.", base_name); rpos = 0; @@ -1029,6 +1037,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 +1051,10 @@ static int parse_section_header(struct reader *reader, char **section_out) return -1; } - name = (char *)git__malloc((size_t)(name_end - line) + 1); + line_len = (size_t)(name_end - line); + + GITERR_CHECK_ALLOC_ADD(line_len, 1); + name = git__malloc(line_len); GITERR_CHECK_ALLOC(name); name_length = 0; @@ -1603,11 +1615,16 @@ 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); - if (str == NULL) + if (GIT_ALLOC_OVERFLOW_ADD(ptr_len, 1) || + (str = git__malloc(ptr_len + 1)) == NULL) { + giterr_set_oom(); return NULL; + } + + out = str; while (*ptr != '\0') { if (*ptr == '"') { diff --git a/src/delta-apply.c b/src/delta-apply.c index a39c7af5c..e46c9631c 100644 --- a/src/delta-apply.c +++ b/src/delta-apply.c @@ -74,6 +74,7 @@ int git__delta_apply( return -1; } + GITERR_CHECK_ALLOC_ADD(res_sz, 1); res_dp = git__malloc(res_sz + 1); GITERR_CHECK_ALLOC(res_dp); diff --git a/src/delta.c b/src/delta.c index 8375a2c4d..f704fdfb1 100644 --- a/src/delta.c +++ b/src/delta.c @@ -119,6 +119,36 @@ 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 = sizeof(struct git_delta_index); + + GITERR_CHECK_ALLOC_MULTIPLY(entries, sizeof(struct index_entry)); + entries_len = entries * sizeof(struct index_entry); + + GITERR_CHECK_ALLOC_ADD(index_len, entries_len); + index_len += entries_len; + + GITERR_CHECK_ALLOC_MULTIPLY(hash_count, sizeof(struct index_entry *)); + hash_len = hash_count * sizeof(struct index_entry *); + + GITERR_CHECK_ALLOC_ADD(index_len, hash_len); + index_len += hash_len; + + if (!git__is_ulong(index_len)) { + giterr_set_oom(); + 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 +178,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..75e9ae9a3 100644 --- a/src/diff.c +++ b/src/diff.c @@ -1558,8 +1558,10 @@ int git_diff_format_email( goto on_error; } + GITERR_CHECK_ALLOC_ADD(offset, 1); summary = git__calloc(offset + 1, 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..67f1c591d 100644 --- a/src/diff_driver.c +++ b/src/diff_driver.c @@ -158,6 +158,29 @@ 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); + + GITERR_CHECK_ALLOC_ADD(driverlen, namelen); + GITERR_CHECK_ALLOC_ADD(driverlen + namelen, 1); + + driver = git__calloc(1, driverlen + namelen + 1); + 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 +189,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 +200,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 +237,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 +253,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..f5eecae66 100644 --- a/src/diff_patch.c +++ b/src/diff_patch.c @@ -388,8 +388,18 @@ 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 = sizeof(*pd); - *out = pd = git__calloc(1, sizeof(*pd) + old_len + new_len + 2); + GITERR_CHECK_ALLOC_ADD(alloc_len, old_len); + alloc_len += old_len; + + GITERR_CHECK_ALLOC_ADD(alloc_len, new_len); + alloc_len += new_len; + + GITERR_CHECK_ALLOC_ADD(alloc_len, 2); + 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..cad1356c3 100644 --- a/src/diff_tform.c +++ b/src/diff_tform.c @@ -831,6 +831,7 @@ int git_diff_find_similar( if ((opts.flags & GIT_DIFF_FIND_ALL) == 0) goto cleanup; + GITERR_CHECK_ALLOC_MULTIPLY(num_deltas, 2); sigcache = git__calloc(num_deltas * 2, sizeof(void *)); GITERR_CHECK_ALLOC(sigcache); diff --git a/src/filebuf.c b/src/filebuf.c index 25f6e52ef..1a9157558 100644 --- a/src/filebuf.c +++ b/src/filebuf.c @@ -271,6 +271,7 @@ 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 */ + GITERR_CHECK_ALLOC_ADD(path_len, GIT_FILELOCK_EXTLENGTH); file->path_lock = git__malloc(path_len + GIT_FILELOCK_EXTLENGTH); GITERR_CHECK_ALLOC(file->path_lock); @@ -437,8 +438,8 @@ int git_filebuf_printf(git_filebuf *file, const char *format, ...) } while ((size_t)len + 1 <= space_left); - tmp_buffer = git__malloc(len + 1); - if (!tmp_buffer) { + if (GIT_ALLOC_OVERFLOW_ADD(len, 1) || + !(tmp_buffer = git__malloc(len + 1))) { file->last_error = BUFERR_MEM; return -1; } diff --git a/src/fileops.c b/src/fileops.c index eddd5a804..eb24013e8 100644 --- a/src/fileops.c +++ b/src/fileops.c @@ -127,6 +127,7 @@ int git_futils_readbuffer_fd(git_buf *buf, git_file fd, size_t len) git_buf_clear(buf); + GITERR_CHECK_ALLOC_ADD(len, 1); if (git_buf_grow(buf, len + 1) < 0) return -1; @@ -708,7 +709,10 @@ 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; + + GITERR_CHECK_ALLOC_ADD(link_size, 1); + link_data = git__malloc(link_size + 1); 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..d5c669f01 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,16 @@ 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(nattr, 2); + alloc_len = nattr * 2; + + GITERR_CHECK_ALLOC_MULTIPLY(alloc_len, sizeof(char *)); + alloc_len *= sizeof(char *); + + GITERR_CHECK_ALLOC_ADD(alloc_len, sizeof(git_filter_def)); + alloc_len += sizeof(git_filter_def); + + fdef = git__calloc(1, alloc_len); GITERR_CHECK_ALLOC(fdef); fdef->filter_name = git__strdup(name); @@ -379,6 +387,9 @@ static int filter_list_new( git_filter_list *fl = NULL; size_t pathlen = src->path ? strlen(src->path) : 0; + GITERR_CHECK_ALLOC_ADD(sizeof(git_filter_list), pathlen); + GITERR_CHECK_ALLOC_ADD(sizeof(git_filter_list) + pathlen, 1); + fl = git__calloc(1, sizeof(git_filter_list) + pathlen + 1); GITERR_CHECK_ALLOC(fl); diff --git a/src/index.c b/src/index.c index cbace3606..70090d12c 100644 --- a/src/index.c +++ b/src/index.c @@ -779,7 +779,9 @@ static int index_entry_create( return -1; } - entry = git__calloc(sizeof(struct entry_internal) + pathlen + 1, 1); + GITERR_CHECK_ALLOC_ADD(sizeof(struct entry_internal), pathlen); + GITERR_CHECK_ALLOC_ADD(sizeof(struct entry_internal) + pathlen, 1); + entry = git__calloc(1, sizeof(struct entry_internal) + pathlen + 1); GITERR_CHECK_ALLOC(entry); entry->pathlen = pathlen; @@ -826,9 +828,17 @@ 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); + struct reuc_entry_internal *entry; + + if (GIT_ALLOC_OVERFLOW_ADD(structlen, pathlen) || + GIT_ALLOC_OVERFLOW_ADD(structlen + pathlen, 1)) { + giterr_set_oom(); + return NULL; + } + + entry = git__calloc(1, structlen + pathlen + 1); if (!entry) return NULL; diff --git a/src/iterator.c b/src/iterator.c index 196adddbf..e90cf30ff 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,13 @@ 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(sizeof(tree_iterator_entry *), n_entries); + alloclen = sizeof(tree_iterator_entry *) * n_entries; + + GITERR_CHECK_ALLOC_ADD(alloclen, sizeof(tree_iterator_frame)); + alloclen += sizeof(tree_iterator_frame); + + tf = git__calloc(1, alloclen); GITERR_CHECK_ALLOC(tf); tf->n_entries = n_entries; diff --git a/src/merge.c b/src/merge.c index 7c38b5692..25d7bd7aa 100644 --- a/src/merge.c +++ b/src/merge.c @@ -1169,6 +1169,7 @@ int git_merge_diff_list__find_renames( goto done; if (diff_list->conflicts.length <= opts->target_limit) { + GITERR_CHECK_ALLOC_MULTIPLY(diff_list->conflicts.length, 3); cache_size = diff_list->conflicts.length * 3; cache = git__calloc(cache_size, sizeof(void *)); GITERR_CHECK_ALLOC(cache); @@ -2228,6 +2229,7 @@ static int merge_ancestor_head( assert(repo && our_head && their_heads); + GITERR_CHECK_ALLOC_ADD(their_heads_len, 1); oids = git__calloc(their_heads_len + 1, sizeof(git_oid)); GITERR_CHECK_ALLOC(oids); 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(¬e->id, note_oid); @@ -233,6 +233,7 @@ int git_odb__hashlink(git_oid *out, const char *path) char *link_data; ssize_t read_len; + GITERR_CHECK_ALLOC_ADD(size, 1); link_data = git__malloc((size_t)(size + 1)); GITERR_CHECK_ALLOC(link_data); diff --git a/src/odb_loose.c b/src/odb_loose.c index ea9bdc4a0..e43b261fa 100644 --- a/src/odb_loose.c +++ b/src/odb_loose.c @@ -64,6 +64,8 @@ static int object_file_name( git_buf *name, const loose_backend *be, const git_oid *id) { /* expand length for object root + 40 hex sha1 chars + 2 * '/' + '\0' */ + GITERR_CHECK_ALLOC_ADD(be->objects_dirlen, GIT_OID_HEXSZ); + GITERR_CHECK_ALLOC_ADD(be->objects_dirlen + GIT_OID_HEXSZ, 3); if (git_buf_grow(name, be->objects_dirlen + GIT_OID_HEXSZ + 3) < 0) return -1; @@ -268,7 +270,8 @@ static void *inflate_tail(z_stream *s, void *hb, size_t used, obj_hdr *hdr) * initial sequence of inflated data from the tail of the * head buffer, if any. */ - if ((buf = git__malloc(hdr->size + 1)) == NULL) { + if (GIT_ALLOC_OVERFLOW_ADD(hdr->size, 1) || + (buf = git__malloc(hdr->size + 1)) == NULL) { inflateEnd(s); return NULL; } @@ -321,6 +324,7 @@ static int inflate_packlike_loose_disk_obj(git_rawobj *out, git_buf *obj) /* * allocate a buffer and inflate the data into it */ + GITERR_CHECK_ALLOC_ADD(hdr.size, 1); buf = git__malloc(hdr.size + 1); GITERR_CHECK_ALLOC(buf); @@ -520,6 +524,8 @@ static int locate_object_short_oid( int error; /* prealloc memory for OBJ_DIR/xx/xx..38x..xx */ + GITERR_CHECK_ALLOC_ADD(dir_len, GIT_OID_HEXSZ); + GITERR_CHECK_ALLOC_ADD(dir_len + GIT_OID_HEXSZ, 3); if (git_buf_grow(object_location, dir_len + 3 + GIT_OID_HEXSZ) < 0) return -1; @@ -563,6 +569,8 @@ static int locate_object_short_oid( return error; /* Update the location according to the oid obtained */ + GITERR_CHECK_ALLOC_ADD(dir_len, GIT_OID_HEXSZ); + GITERR_CHECK_ALLOC_ADD(dir_len + GIT_OID_HEXSZ, 2); git_buf_truncate(object_location, dir_len); if (git_buf_grow(object_location, dir_len + GIT_OID_HEXSZ + 2) < 0) @@ -928,6 +936,8 @@ int git_odb_backend_loose( objects_dirlen = strlen(objects_dir); + GITERR_CHECK_ALLOC_ADD(sizeof(loose_backend), objects_dirlen); + GITERR_CHECK_ALLOC_ADD(sizeof(loose_backend) + objects_dirlen, 2); backend = git__calloc(1, sizeof(loose_backend) + objects_dirlen + 2); GITERR_CHECK_ALLOC(backend); diff --git a/src/odb_mempack.c b/src/odb_mempack.c index d9b3a1824..a71d8db4b 100644 --- a/src/odb_mempack.c +++ b/src/odb_mempack.c @@ -47,6 +47,7 @@ static int impl__write(git_odb_backend *_backend, const git_oid *oid, const void if (rval == 0) return 0; + GITERR_CHECK_ALLOC_ADD(sizeof(struct memobject), len); obj = git__malloc(sizeof(struct memobject) + len); GITERR_CHECK_ALLOC(obj); @@ -261,6 +261,7 @@ struct git_oid_shorten { static int resize_trie(git_oid_shorten *self, size_t new_size) { + GITERR_CHECK_ALLOC_MULTIPLY(new_size, sizeof(trie_node)); self->nodes = git__realloc(self->nodes, new_size * sizeof(trie_node)); GITERR_CHECK_ALLOC(self->nodes); diff --git a/src/pack-objects.c b/src/pack-objects.c index 0040a826b..aea4770af 100644 --- a/src/pack-objects.c +++ b/src/pack-objects.c @@ -201,7 +201,11 @@ int git_packbuilder_insert(git_packbuilder *pb, const git_oid *oid, return 0; if (pb->nr_objects >= pb->nr_alloc) { + GITERR_CHECK_ALLOC_ADD(pb->nr_alloc, 1024); + GITERR_CHECK_ALLOC_MULTIPLY(pb->nr_alloc + 1024, 3 / 2); pb->nr_alloc = (pb->nr_alloc + 1024) * 3 / 2; + + GITERR_CHECK_ALLOC_MULTIPLY(pb->nr_alloc, sizeof(*po)); pb->object_list = git__realloc(pb->object_list, pb->nr_alloc * sizeof(*po)); GITERR_CHECK_ALLOC(pb->object_list); @@ -499,8 +503,13 @@ 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 (GIT_ALLOC_OVERFLOW_MULTIPLY(pb->nr_objects, sizeof(*wo)) || + (wo = git__malloc(pb->nr_objects * sizeof(*wo))) == NULL) { + giterr_set_oom(); + return NULL; + } for (i = 0; i < pb->nr_objects; i++) { git_pobject *po = pb->object_list + i; @@ -770,10 +779,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,7 +829,9 @@ 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)) { + GITERR_CHECK_ALLOC_ADD(pb->delta_cache_size, delta_size); pb->delta_cache_size += delta_size; + git_packbuilder__cache_unlock(pb); trg_object->delta_data = git__realloc(delta_buf, delta_size); @@ -1088,6 +1102,7 @@ static int ll_find_deltas(git_packbuilder *pb, git_pobject **list, return 0; } + GITERR_CHECK_ALLOC_MULTIPLY(pb->nr_threads, sizeof(*p)); p = git__malloc(pb->nr_threads * sizeof(*p)); GITERR_CHECK_ALLOC(p); @@ -1239,6 +1254,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); + GITERR_CHECK_ALLOC_MULTIPLY(pb->nr_objects, sizeof(*delta_list)); delta_list = git__malloc(pb->nr_objects * sizeof(*delta_list)); GITERR_CHECK_ALLOC(delta_list); diff --git a/src/pack.c b/src/pack.c index 47ce854c4..d475b28ee 100644 --- a/src/pack.c +++ b/src/pack.c @@ -683,8 +683,11 @@ int git_packfile_unpack( */ if (cached && stack_size == 1) { void *data = obj->data; + + GITERR_CHECK_ALLOC_ADD(obj->len, 1); obj->data = git__malloc(obj->len + 1); GITERR_CHECK_ALLOC(obj->data); + memcpy(obj->data, data, obj->len + 1); git_atomic_dec(&cached->refcount); goto cleanup; @@ -841,6 +844,7 @@ int packfile_unpack_compressed( z_stream stream; unsigned char *buffer, *in; + GITERR_CHECK_ALLOC_ADD(size, 1); buffer = git__calloc(1, size + 1); GITERR_CHECK_ALLOC(buffer); @@ -1092,6 +1096,9 @@ int git_packfile_alloc(struct git_pack_file **pack_out, const char *path) if (path_len < strlen(".idx")) return git_odb__error_notfound("invalid packfile path", NULL); + GITERR_CHECK_ALLOC_ADD(sizeof(*p), path_len); + GITERR_CHECK_ALLOC_ADD(sizeof(*p) + path_len, 2); + p = git__calloc(1, sizeof(*p) + path_len + 2); GITERR_CHECK_ALLOC(p); diff --git a/src/path.c b/src/path.c index 58d71921b..3dbd7187b 100644 --- a/src/path.c +++ b/src/path.c @@ -622,7 +622,9 @@ static bool _check_dir_contents( size_t sub_size = strlen(sub); /* 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_ALLOC_OVERFLOW_ADD(dir_size, sub_size) || + GIT_ALLOC_OVERFLOW_ADD(dir_size + sub_size, 2) || + git_buf_try_grow(dir, dir_size + sub_size + 2, false, false) < 0) return false; /* save excursion */ @@ -822,6 +824,9 @@ int git_path_make_relative(git_buf *path, const char *parent) for (; (q = strchr(q, '/')) && *(q + 1); q++) depth++; + GITERR_CHECK_ALLOC_MULTIPLY(depth, 3); + GITERR_CHECK_ALLOC_ADD(depth * 3, plen); + GITERR_CHECK_ALLOC_ADD(depth * 3, 1); newlen = (depth * 3) + plen; /* save the offset as we might realllocate the pointer */ @@ -881,6 +886,7 @@ int git_path_iconv(git_path_iconv_t *ic, char **in, size_t *inlen) git_buf_clear(&ic->buf); while (1) { + GITERR_CHECK_ALLOC_ADD(wantlen, 1); if (git_buf_grow(&ic->buf, wantlen + 1) < 0) return -1; @@ -1057,6 +1063,45 @@ 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 = path_len; + char *entry_path; + + GITERR_CHECK_ALLOC_ADD(alloc_size, de_len); + alloc_size += de_len; + + GITERR_CHECK_ALLOC_ADD(alloc_size, need_slash); + alloc_size += need_slash; + + GITERR_CHECK_ALLOC_ADD(alloc_size, 1); + alloc_size++; + + GITERR_CHECK_ALLOC_ADD(alloc_size, alloc_extra); + 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 +1109,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 +1141,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 +1154,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..1599fc7a2 100644 --- a/src/pool.c +++ b/src/pool.c @@ -116,9 +116,11 @@ static void *pool_alloc_page(git_pool *pool, uint32_t size) pool->has_large_page_alloc = 1; } - page = git__calloc(1, alloc_size + sizeof(git_pool_page)); - if (!page) + if (GIT_ALLOC_OVERFLOW_ADD(alloc_size, sizeof(git_pool_page)) || + !(page = git__calloc(1, alloc_size + sizeof(git_pool_page)))) { + giterr_set_oom(); return NULL; + } page->size = alloc_size; page->avail = alloc_size - size; diff --git a/src/refs.c b/src/refs.c index 43c7333f2..33e931db5 100644 --- a/src/refs.c +++ b/src/refs.c @@ -37,10 +37,14 @@ enum { static git_reference *alloc_ref(const char *name) { git_reference *ref; - size_t namelen = strlen(name); + size_t namelen = strlen(name), reflen = sizeof(git_reference); - if ((ref = git__calloc(1, sizeof(git_reference) + namelen + 1)) == NULL) + if (GIT_ALLOC_OVERFLOW_ADD(reflen, namelen) || + GIT_ALLOC_OVERFLOW_ADD(reflen + namelen, 1) || + (ref = git__calloc(1, reflen + namelen + 1)) == NULL) { + giterr_set_oom(); return NULL; + } memcpy(ref->name, name, namelen + 1); @@ -94,10 +98,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 = sizeof(git_reference); + git_reference *rewrite = NULL; + + if (!GIT_ALLOC_OVERFLOW_ADD(reflen, namelen) && + !GIT_ALLOC_OVERFLOW_ADD(reflen + namelen, 1) && + (rewrite = git__realloc(ref, reflen + namelen + 1)) != 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/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/sortedcache.c b/src/sortedcache.c index c6b226153..021f79632 100644 --- a/src/sortedcache.c +++ b/src/sortedcache.c @@ -15,7 +15,9 @@ int git_sortedcache_new( pathlen = path ? strlen(path) : 0; - sc = git__calloc(sizeof(git_sortedcache) + pathlen + 1, 1); + GITERR_CHECK_ALLOC_ADD(sizeof(git_sortedcache), pathlen); + GITERR_CHECK_ALLOC_ADD(sizeof(git_sortedcache) + pathlen, 1); + sc = git__calloc(1, sizeof(git_sortedcache) + pathlen + 1); GITERR_CHECK_ALLOC(sc); if (git_pool_init(&sc->pool, 1, 0) < 0 || @@ -117,6 +117,7 @@ static int tag_parse(git_tag *tag, const char *buffer, const char *buffer_end) text_len = search - buffer; + GITERR_CHECK_ALLOC_ADD(text_len, 1); tag->tag_name = git__malloc(text_len + 1); GITERR_CHECK_ALLOC(tag->tag_name); @@ -141,6 +142,7 @@ static int tag_parse(git_tag *tag, const char *buffer, const char *buffer_end) text_len = buffer_end - ++buffer; + GITERR_CHECK_ALLOC_ADD(text_len, 1); tag->message = git__malloc(text_len + 1); GITERR_CHECK_ALLOC(tag->message); diff --git a/src/transports/cred.c b/src/transports/cred.c index 1b4d29c0a..8e5447d18 100644 --- a/src/transports/cred.c +++ b/src/transports/cred.c @@ -311,6 +311,9 @@ int git_cred_username_new(git_cred **cred, const char *username) assert(cred); len = strlen(username); + + GITERR_CHECK_ALLOC_ADD(sizeof(git_cred_username), len); + GITERR_CHECK_ALLOC_ADD(sizeof(git_cred_username) + len, 1); c = git__malloc(sizeof(git_cred_username) + len + 1); GITERR_CHECK_ALLOC(c); 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..2f83e0d7b 100644 --- a/src/transports/smart_pkt.c +++ b/src/transports/smart_pkt.c @@ -103,6 +103,8 @@ static int comment_pkt(git_pkt **out, const char *line, size_t len) { git_pkt_comment *pkt; + GITERR_CHECK_ALLOC_ADD(sizeof(git_pkt_comment), len); + GITERR_CHECK_ALLOC_ADD(sizeof(git_pkt_comment) + len, 1); pkt = git__malloc(sizeof(git_pkt_comment) + len + 1); GITERR_CHECK_ALLOC(pkt); @@ -122,6 +124,9 @@ static int err_pkt(git_pkt **out, const char *line, size_t len) /* Remove "ERR " from the line */ line += 4; len -= 4; + + GITERR_CHECK_ALLOC_ADD(sizeof(git_pkt_progress), len); + GITERR_CHECK_ALLOC_ADD(sizeof(git_pkt_progress) + len, 1); pkt = git__malloc(sizeof(git_pkt_err) + len + 1); GITERR_CHECK_ALLOC(pkt); @@ -141,6 +146,8 @@ static int data_pkt(git_pkt **out, const char *line, size_t len) line++; len--; + + GITERR_CHECK_ALLOC_ADD(sizeof(git_pkt_progress), len); pkt = git__malloc(sizeof(git_pkt_data) + len); GITERR_CHECK_ALLOC(pkt); @@ -159,6 +166,8 @@ static int sideband_progress_pkt(git_pkt **out, const char *line, size_t len) line++; len--; + + GITERR_CHECK_ALLOC_ADD(sizeof(git_pkt_progress), len); pkt = git__malloc(sizeof(git_pkt_progress) + len); GITERR_CHECK_ALLOC(pkt); @@ -177,6 +186,9 @@ static int sideband_error_pkt(git_pkt **out, const char *line, size_t len) line++; len--; + + GITERR_CHECK_ALLOC_ADD(sizeof(git_pkt_err), len); + GITERR_CHECK_ALLOC_ADD(sizeof(git_pkt_err) + len, 1); pkt = git__malloc(sizeof(git_pkt_err) + len + 1); GITERR_CHECK_ALLOC(pkt); @@ -220,6 +232,7 @@ static int ref_pkt(git_pkt **out, const char *line, size_t len) if (line[len - 1] == '\n') --len; + GITERR_CHECK_ALLOC_ADD(len, 1); pkt->head.name = git__malloc(len + 1); GITERR_CHECK_ALLOC(pkt->head.name); @@ -249,9 +262,13 @@ 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; + GITERR_CHECK_ALLOC_ADD(len, 1); pkt->ref = git__malloc(len + 1); GITERR_CHECK_ALLOC(pkt->ref); @@ -273,9 +290,13 @@ 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; + GITERR_CHECK_ALLOC_ADD(len, 1); pkt->ref = git__malloc(len + 1); GITERR_CHECK_ALLOC(pkt->ref); @@ -283,9 +304,13 @@ static int ng_pkt(git_pkt **out, const char *line, size_t 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; + GITERR_CHECK_ALLOC_ADD(len, 1); pkt->msg = git__malloc(len + 1); GITERR_CHECK_ALLOC(pkt->msg); 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..2c8b89291 100644 --- a/src/tree.c +++ b/src/tree.c @@ -84,11 +84,15 @@ 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 = sizeof(git_tree_entry); - entry = git__malloc(sizeof(git_tree_entry) + filename_len + 1); - if (!entry) + if (GIT_ALLOC_OVERFLOW_ADD(tree_len, filename_len) || + GIT_ALLOC_OVERFLOW_ADD(tree_len + filename_len, 1) || + !(entry = git__malloc(tree_len + filename_len + 1))) { + giterr_set_oom(); return NULL; + } memset(entry, 0x0, sizeof(git_tree_entry)); memcpy(entry->filename, filename, filename_len); @@ -205,12 +209,16 @@ void git_tree_entry_free(git_tree_entry *entry) int git_tree_entry_dup(git_tree_entry **dest, const git_tree_entry *source) { - size_t total_size; + size_t total_size = sizeof(git_tree_entry); git_tree_entry *copy; assert(source); - total_size = sizeof(git_tree_entry) + source->filename_len + 1; + GITERR_CHECK_ALLOC_ADD(total_size, source->filename_len); + total_size += source->filename_len; + + GITERR_CHECK_ALLOC_ADD(total_size, 1); + total_size++; copy = git__malloc(total_size); GITERR_CHECK_ALLOC(copy); diff --git a/src/tsort.c b/src/tsort.c index 4885e435b..b92e52056 100644 --- a/src/tsort.c +++ b/src/tsort.c @@ -184,7 +184,10 @@ 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; + + GITERR_CHECK_ALLOC_MULTIPLY(new_size, sizeof(void *)); + tempstore = git__realloc(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..6c94a5aa0 100644 --- a/src/util.h +++ b/src/util.h @@ -64,7 +64,12 @@ GIT_INLINE(char *) git__strndup(const char *str, size_t n) length = p_strnlen(str, n); - ptr = (char*)git__malloc(length + 1); + if (GIT_ALLOC_OVERFLOW_ADD(length, 1)) { + giterr_set_oom(); + return NULL; + } + + ptr = git__malloc(length + 1); if (!ptr) return NULL; @@ -80,7 +85,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; + + if (GIT_ALLOC_OVERFLOW_ADD(n, 1) || !(ptr = git__malloc(n+1))) { + giterr_set_oom(); + return NULL; + } + memcpy(ptr, start, n); ptr[n] = '\0'; return ptr; diff --git a/src/vector.c b/src/vector.c index c769b696a..b636032b1 100644 --- a/src/vector.c +++ b/src/vector.c @@ -29,12 +29,12 @@ 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 *); + size_t new_bytes; void *new_contents; /* Check for overflow */ - if (new_bytes / sizeof(void *) != new_size) - GITERR_CHECK_ALLOC(NULL); + GITERR_CHECK_ALLOC_MULTIPLY(new_size, sizeof(void *)); + new_bytes = new_size * sizeof(void *); new_contents = git__realloc(v->contents, new_bytes); GITERR_CHECK_ALLOC(new_contents); @@ -51,6 +51,7 @@ int git_vector_dup(git_vector *v, const git_vector *src, git_vector_cmp cmp) assert(v && src); + GITERR_CHECK_ALLOC_MULTIPLY(src->length, sizeof(void *)); bytes = src->length * sizeof(void *); v->_alloc_size = src->length; diff --git a/src/win32/dir.c b/src/win32/dir.c index c7427ea54..9953289f6 100644 --- a/src/win32/dir.c +++ b/src/win32/dir.c @@ -18,9 +18,13 @@ git__DIR *git__opendir(const char *dir) dirlen = strlen(dir); - new = git__calloc(sizeof(*new) + dirlen + 1, 1); - if (!new) + if (GIT_ALLOC_OVERFLOW_ADD(sizeof(*new), dirlen) || + GIT_ALLOC_OVERFLOW_ADD(sizeof(*new) + dirlen, 1) || + !(new = git__calloc(1, sizeof(*new) + dirlen + 1))) { + giterr_set_oom(); 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..624611205 100644 --- a/src/win32/utf-conv.c +++ b/src/win32/utf-conv.c @@ -99,9 +99,8 @@ 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 (GIT_ALLOC_OVERFLOW_MULTIPLY(utf16_size, sizeof(wchar_t)) || + !(*dest = git__malloc(utf16_size * sizeof(wchar_t)))) { errno = ENOMEM; return -1; } diff --git a/src/zstream.c b/src/zstream.c index e75fb265e..06660e981 100644 --- a/src/zstream.c +++ b/src/zstream.c @@ -134,6 +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; + GITERR_CHECK_ALLOC_ADD(out->size, step); if ((error = git_buf_grow(out, out->size + step)) < 0) goto done; |
