diff options
Diffstat (limited to 'src')
54 files changed, 791 insertions, 296 deletions
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/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/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..b0ffd6116 100644 --- a/src/config_file.c +++ b/src/config_file.c @@ -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..e4d9a0699 100644 --- a/src/diff_driver.c +++ b/src/diff_driver.c @@ -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..9509b27bb 100644 --- a/src/fileops.c +++ b/src/fileops.c @@ -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..75700a719 100644 --- a/src/index.c +++ b/src/index.c @@ -770,7 +770,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 +779,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 +828,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; 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..e4b60c847 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)); 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); @@ -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..32bc84442 100644 --- a/src/odb_mempack.c +++ b/src/odb_mempack.c @@ -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..9471e7b91 100644 --- a/src/offmap.h +++ b/src/offmap.h @@ -13,6 +13,7 @@  #define kmalloc git__malloc  #define kcalloc git__calloc  #define krealloc git__realloc +#define kreallocarray git__reallocarray  #define kfree git__free  #include "khash.h" @@ -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..5e3b44bfb 100644 --- a/src/oidmap.h +++ b/src/oidmap.h @@ -13,6 +13,7 @@  #define kmalloc git__malloc  #define kcalloc git__calloc  #define krealloc git__realloc +#define kreallocarray git__reallocarray  #define kfree git__free  #include "khash.h" 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/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/refs.c b/src/refs.c index 43c7333f2..2e88c26c3 100644 --- a/src/refs.c +++ b/src/refs.c @@ -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/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..1195ea339 100644 --- a/src/sortedcache.c +++ b/src/sortedcache.c @@ -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..dfbe5639f 100644 --- a/src/strmap.h +++ b/src/strmap.h @@ -12,6 +12,7 @@  #define kmalloc git__malloc  #define kcalloc git__calloc  #define krealloc git__realloc +#define kreallocarray git__reallocarray  #define kfree git__free  #include "khash.h" @@ -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/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..9fd4e0a07 100644 --- a/src/tree.c +++ b/src/tree.c @@ -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;  | 
