Commit bea65980c7a42e34edfafbdc40b199ba7b2a564e

Patrick Steinhardt 2018-10-25T11:21:14

Merge pull request #4851 from pks-t/pks/strtol-removal strtol removal

diff --git a/src/commit_list.c b/src/commit_list.c
index 96bd9dc..b4313ee 100644
--- a/src/commit_list.c
+++ b/src/commit_list.c
@@ -171,7 +171,9 @@ static int commit_quick_parse(
 			buffer--;
 	}
 
-	if ((buffer == committer_start) || (git__strtol64(&commit_time, (char *)(buffer + 1), NULL, 10) < 0))
+	if ((buffer == committer_start) ||
+	    (git__strntol64(&commit_time, (char *)(buffer + 1),
+			    buffer_end - buffer + 1, NULL, 10) < 0))
 		return commit_error(commit, "cannot parse commit time");
 
 	commit->time = commit_time;
diff --git a/src/config.c b/src/config.c
index 8d2e12f..0837500 100644
--- a/src/config.c
+++ b/src/config.c
@@ -1300,7 +1300,7 @@ int git_config_parse_int64(int64_t *out, const char *value)
 	const char *num_end;
 	int64_t num;
 
-	if (!value || git__strtol64(&num, value, &num_end, 0) < 0)
+	if (!value || git__strntol64(&num, value, strlen(value), &num_end, 0) < 0)
 		goto fail_parse;
 
 	switch (*num_end) {
diff --git a/src/index.c b/src/index.c
index 465efaa..8858d23 100644
--- a/src/index.c
+++ b/src/index.c
@@ -2243,7 +2243,7 @@ static int read_reuc(git_index *index, const char *buffer, size_t size)
 		for (i = 0; i < 3; i++) {
 			int64_t tmp;
 
-			if (git__strtol64(&tmp, buffer, &endptr, 8) < 0 ||
+			if (git__strntol64(&tmp, buffer, size, &endptr, 8) < 0 ||
 				!endptr || endptr == buffer || *endptr ||
 				tmp < 0 || tmp > UINT32_MAX) {
 				index_entry_reuc_free(lost);
diff --git a/src/rebase.c b/src/rebase.c
index bc3c599..6503e5f 100644
--- a/src/rebase.c
+++ b/src/rebase.c
@@ -152,7 +152,7 @@ GIT_INLINE(int) rebase_readint(
 	if ((error = rebase_readfile(asc_out, state_path, filename)) < 0)
 		return error;
 
-	if (git__strtol32(&num, asc_out->ptr, &eol, 10) < 0 || num < 0 || *eol) {
+	if (git__strntol32(&num, asc_out->ptr, asc_out->size, &eol, 10) < 0 || num < 0 || *eol) {
 		giterr_set(GITERR_REBASE, "the file '%s' contains an invalid numeric value", filename);
 		return -1;
 	}
diff --git a/src/revparse.c b/src/revparse.c
index bdbf875..df96f9d 100644
--- a/src/revparse.c
+++ b/src/revparse.c
@@ -128,7 +128,8 @@ static int try_parse_numeric(int *n, const char *curly_braces_content)
 	int32_t content;
 	const char *end_ptr;
 
-	if (git__strtol32(&content, curly_braces_content, &end_ptr, 10) < 0)
+	if (git__strntol32(&content, curly_braces_content, strlen(curly_braces_content),
+			   &end_ptr, 10) < 0)
 		return -1;
 
 	if (*end_ptr != '\0')
@@ -578,7 +579,7 @@ static int extract_how_many(int *n, const char *spec, size_t *pos)
 		} while (spec[(*pos)] == kind && kind == '~');
 
 		if (git__isdigit(spec[*pos])) {
-			if (git__strtol32(&parsed, spec + *pos, &end_ptr, 10) < 0)
+			if (git__strntol32(&parsed, spec + *pos, strlen(spec + *pos), &end_ptr, 10) < 0)
 				return GIT_EINVALIDSPEC;
 
 			accumulated += (parsed - 1);
diff --git a/src/signature.c b/src/signature.c
index 286d0a6..91864bb 100644
--- a/src/signature.c
+++ b/src/signature.c
@@ -231,7 +231,8 @@ int git_signature__parse(git_signature *sig, const char **buffer_out,
 		const char *time_start = email_end + 2;
 		const char *time_end;
 
-		if (git__strtol64(&sig->when.time, time_start, &time_end, 10) < 0) {
+		if (git__strntol64(&sig->when.time, time_start,
+				   buffer_end - time_start, &time_end, 10) < 0) {
 			git__free(sig->name);
 			git__free(sig->email);
 			sig->name = sig->email = NULL;
@@ -246,7 +247,8 @@ int git_signature__parse(git_signature *sig, const char **buffer_out,
 			tz_start = time_end + 1;
 
 			if ((tz_start[0] != '-' && tz_start[0] != '+') ||
-				git__strtol32(&offset, tz_start + 1, &tz_end, 10) < 0) {
+			    git__strntol32(&offset, tz_start + 1,
+					   buffer_end - tz_start + 1, &tz_end, 10) < 0) {
 				/* malformed timezone, just assume it's zero */
 				offset = 0;
 			}
diff --git a/src/streams/curl.c b/src/streams/curl.c
index ee13be1..3c0af3b 100644
--- a/src/streams/curl.c
+++ b/src/streams/curl.c
@@ -330,7 +330,7 @@ int git_curl_stream_new(git_stream **out, const char *host, const char *port)
 		return -1;
 	}
 
-	if ((error = git__strtol32(&iport, port, NULL, 10)) < 0) {
+	if ((error = git__strntol32(&iport, port, strlen(port), NULL, 10)) < 0) {
 		git__free(st);
 		return error;
 	}
diff --git a/src/transports/smart_pkt.c b/src/transports/smart_pkt.c
index 6a404ef..fb59c70 100644
--- a/src/transports/smart_pkt.c
+++ b/src/transports/smart_pkt.c
@@ -391,7 +391,7 @@ static int parse_len(size_t *out, const char *line, size_t linelen)
 		}
 	}
 
-	if ((error = git__strtol32(&len, num, &num_end, 16)) < 0)
+	if ((error = git__strntol32(&len, num, PKT_LEN_SIZE, &num_end, 16)) < 0)
 		return error;
 
 	if (len < 0)
diff --git a/src/transports/winhttp.c b/src/transports/winhttp.c
index 3df892d..e925dbd 100644
--- a/src/transports/winhttp.c
+++ b/src/transports/winhttp.c
@@ -766,7 +766,8 @@ static int winhttp_connect(
 	t->connection = NULL;
 
 	/* Prepare port */
-	if (git__strtol32(&port, t->connection_data.port, NULL, 10) < 0)
+	if (git__strntol32(&port, t->connection_data.port,
+			   strlen(t->connection_data.port), NULL, 10) < 0)
 		return -1;
 
 	/* Prepare host */
diff --git a/src/tree-cache.c b/src/tree-cache.c
index b331d22..c33e6af 100644
--- a/src/tree-cache.c
+++ b/src/tree-cache.c
@@ -91,7 +91,7 @@ static int read_tree_internal(git_tree_cache **out,
 		return -1;
 
 	/* Blank-terminated ASCII decimal number of entries in this tree */
-	if (git__strtol32(&count, buffer, &buffer, 10) < 0)
+	if (git__strntol32(&count, buffer, buffer_end - buffer, &buffer, 10) < 0)
 		goto corrupted;
 
 	tree->entry_count = count;
@@ -100,7 +100,7 @@ static int read_tree_internal(git_tree_cache **out,
 		goto corrupted;
 
 	 /* Number of children of the tree, newline-terminated */
-	if (git__strtol32(&count, buffer, &buffer, 10) < 0 || count < 0)
+	if (git__strntol32(&count, buffer, buffer_end - buffer, &buffer, 10) < 0 || count < 0)
 		goto corrupted;
 
 	tree->children_count = count;
diff --git a/src/util.c b/src/util.c
index 79b362f..20c88a1 100644
--- a/src/util.c
+++ b/src/util.c
@@ -68,12 +68,6 @@ int git_strarray_copy(git_strarray *tgt, const git_strarray *src)
 	return 0;
 }
 
-int git__strtol64(int64_t *result, const char *nptr, const char **endptr, int base)
-{
-
-	return git__strntol64(result, nptr, (size_t)-1, endptr, base);
-}
-
 int git__strntol64(int64_t *result, const char *nptr, size_t nptr_len, const char **endptr, int base)
 {
 	const char *p;
@@ -132,10 +126,20 @@ int git__strntol64(int64_t *result, const char *nptr, size_t nptr_len, const cha
 			v = c - 'A' + 10;
 		if (v >= base)
 			break;
-		nn = n * base + (neg ? -v : v);
-		if ((!neg && nn < n) || (neg && nn > n))
+		v = neg ? -v : v;
+		if (n > INT64_MAX / base || n < INT64_MIN / base) {
 			ovfl = 1;
-		n = nn;
+			/* Keep on iterating until the end of this number */
+			continue;
+		}
+		nn = n * base;
+		if ((v > 0 && nn > INT64_MAX - v) ||
+		    (v < 0 && nn < INT64_MIN - v)) {
+			ovfl = 1;
+			/* Keep on iterating until the end of this number */
+			continue;
+		}
+		n = nn + v;
 	}
 
 Return:
@@ -156,28 +160,26 @@ Return:
 	return 0;
 }
 
-int git__strtol32(int32_t *result, const char *nptr, const char **endptr, int base)
-{
-
-	return git__strntol32(result, nptr, (size_t)-1, endptr, base);
-}
-
 int git__strntol32(int32_t *result, const char *nptr, size_t nptr_len, const char **endptr, int base)
 {
-	int error;
+	const char *tmp_endptr;
 	int32_t tmp_int;
 	int64_t tmp_long;
+	int error;
 
-	if ((error = git__strntol64(&tmp_long, nptr, nptr_len, endptr, base)) < 0)
+	if ((error = git__strntol64(&tmp_long, nptr, nptr_len, &tmp_endptr, base)) < 0)
 		return error;
 
 	tmp_int = tmp_long & 0xFFFFFFFF;
 	if (tmp_int != tmp_long) {
-		giterr_set(GITERR_INVALID, "failed to convert: '%s' is too large", nptr);
+		int len = tmp_endptr - nptr;
+		giterr_set(GITERR_INVALID, "failed to convert: '%.*s' is too large", len, nptr);
 		return -1;
 	}
 
 	*result = tmp_int;
+	if (endptr)
+		*endptr = tmp_endptr;
 
 	return error;
 }
diff --git a/src/util.h b/src/util.h
index b6f5b75..4000243 100644
--- a/src/util.h
+++ b/src/util.h
@@ -58,9 +58,7 @@ GIT_INLINE(int) git__signum(int val)
 	return ((val > 0) - (val < 0));
 }
 
-extern int git__strtol32(int32_t *n, const char *buff, const char **end_buf, int base);
 extern int git__strntol32(int32_t *n, const char *buff, size_t buff_len, const char **end_buf, int base);
-extern int git__strtol64(int64_t *n, const char *buff, const char **end_buf, int base);
 extern int git__strntol64(int64_t *n, const char *buff, size_t buff_len, const char **end_buf, int base);
 
 
diff --git a/tests/core/strtol.c b/tests/core/strtol.c
index 0d3b6a5..ba79fba 100644
--- a/tests/core/strtol.c
+++ b/tests/core/strtol.c
@@ -1,45 +1,84 @@
 #include "clar_libgit2.h"
 
-void test_core_strtol__int32(void)
+static void assert_l32_parses(const char *string, int32_t expected, int base)
 {
 	int32_t i;
+	cl_git_pass(git__strntol32(&i, string, strlen(string), NULL, base));
+	cl_assert_equal_i(i, expected);
+}
 
-	cl_git_pass(git__strtol32(&i, "123", NULL, 10));
-	cl_assert(i == 123);
-	cl_git_pass(git__strtol32(&i, "  +123 ", NULL, 10));
-	cl_assert(i == 123);
-	cl_git_pass(git__strtol32(&i, "  +2147483647 ", NULL, 10));
-	cl_assert(i == 2147483647);
-	cl_git_pass(git__strtol32(&i, "  -2147483648 ", NULL, 10));
-	cl_assert(i == -2147483648LL);
-	
-	cl_git_fail(git__strtol32(&i, "  2147483657 ", NULL, 10));
-	cl_git_fail(git__strtol32(&i, "  -2147483657 ", NULL, 10));
+static void assert_l32_fails(const char *string, int base)
+{
+	int32_t i;
+	cl_git_fail(git__strntol32(&i, string, strlen(string), NULL, base));
 }
 
-void test_core_strtol__int64(void)
+static void assert_l64_parses(const char *string, int64_t expected, int base)
 {
 	int64_t i;
+	cl_git_pass(git__strntol64(&i, string, strlen(string), NULL, base));
+	cl_assert_equal_i(i, expected);
+}
+
+static void assert_l64_fails(const char *string, int base)
+{
+	int64_t i;
+	cl_git_fail(git__strntol64(&i, string, strlen(string), NULL, base));
+}
+
+void test_core_strtol__int32(void)
+{
+	assert_l32_parses("123", 123, 10);
+	assert_l32_parses("  +123 ", 123, 10);
+	assert_l32_parses("  +2147483647 ", 2147483647, 10);
+	assert_l32_parses("  -2147483648 ", -2147483648LL, 10);
+	assert_l32_parses("A", 10, 16);
+	assert_l32_parses("1x1", 1, 10);
 
-	cl_git_pass(git__strtol64(&i, "123", NULL, 10));
-	cl_assert(i == 123);
-	cl_git_pass(git__strtol64(&i, "  +123 ", NULL, 10));
-	cl_assert(i == 123);
-	cl_git_pass(git__strtol64(&i, "  +2147483647 ", NULL, 10));
-	cl_assert(i == 2147483647);
-	cl_git_pass(git__strtol64(&i, "  -2147483648 ", NULL, 10));
-	cl_assert(i == -2147483648LL);
-	cl_git_pass(git__strtol64(&i, "  2147483657 ", NULL, 10));
-	cl_assert(i == 2147483657LL);
-	cl_git_pass(git__strtol64(&i, "  -2147483657 ", NULL, 10));
-	cl_assert(i == -2147483657LL);
-	cl_git_pass(git__strtol64(&i, " 9223372036854775807  ", NULL, 10));
-	cl_assert(i == INT64_MAX);
-	cl_git_pass(git__strtol64(&i, "   -9223372036854775808  ", NULL, 10));
-	cl_assert(i == INT64_MIN);
-	cl_git_pass(git__strtol64(&i, "   0x7fffffffffffffff  ", NULL, 16));
-	cl_assert(i == INT64_MAX);
-	cl_git_pass(git__strtol64(&i, "   -0x8000000000000000   ", NULL, 16));
-	cl_assert(i == INT64_MIN);
+	assert_l32_fails("", 10);
+	assert_l32_fails("a", 10);
+	assert_l32_fails("x10x", 10);
+	assert_l32_fails("  2147483657 ", 10);
+	assert_l32_fails("  -2147483657 ", 10);
 }
 
+void test_core_strtol__int64(void)
+{
+	assert_l64_parses("123", 123, 10);
+	assert_l64_parses("  +123 ", 123, 10);
+	assert_l64_parses("  +2147483647 ", 2147483647, 10);
+	assert_l64_parses("  -2147483648 ", -2147483648LL, 10);
+	assert_l64_parses("  2147483657 ", 2147483657LL, 10);
+	assert_l64_parses("  -2147483657 ", -2147483657LL, 10);
+	assert_l64_parses(" 9223372036854775807  ", INT64_MAX, 10);
+	assert_l64_parses("   -9223372036854775808  ", INT64_MIN, 10);
+	assert_l64_parses("   0x7fffffffffffffff  ", INT64_MAX, 16);
+	assert_l64_parses("   -0x8000000000000000   ", INT64_MIN, 16);
+	assert_l64_parses("1a", 26, 16);
+	assert_l64_parses("1A", 26, 16);
+
+	assert_l64_fails("", 10);
+	assert_l64_fails("a", 10);
+	assert_l64_fails("x10x", 10);
+	assert_l64_fails("0x8000000000000000", 16);
+	assert_l64_fails("-0x8000000000000001", 16);
+}
+
+void test_core_strtol__buffer_length_truncates(void)
+{
+	int32_t i32;
+	int64_t i64;
+
+	cl_git_pass(git__strntol32(&i32, "11", 1, NULL, 10));
+	cl_assert_equal_i(i32, 1);
+
+	cl_git_pass(git__strntol64(&i64, "11", 1, NULL, 10));
+	cl_assert_equal_i(i64, 1);
+}
+
+void test_core_strtol__error_message_cuts_off(void)
+{
+	assert_l32_fails("2147483657foobar", 10);
+	cl_assert(strstr(giterr_last()->message, "2147483657") != NULL);
+	cl_assert(strstr(giterr_last()->message, "foobar") == NULL);
+}