Commit 554b3b9aa594e5df6273115dbee8f6035837120b

Patrick Steinhardt 2019-02-21T10:31:21

Merge pull request #4996 from eaigner/master Prevent reading out of bounds memory

diff --git a/AUTHORS b/AUTHORS
index 458ff06..784bab3 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -23,6 +23,7 @@ Dmitry Kovega
 Emeric Fermas
 Emmanuel Rodriguez
 Eric Myhre
+Erik Aigner
 Florian Forster
 Holger Weiss
 Ingmar Vanhassel
diff --git a/src/apply.c b/src/apply.c
index d72aa83..0becf94 100644
--- a/src/apply.c
+++ b/src/apply.c
@@ -59,7 +59,7 @@ static int patch_image_init_fromstr(
 	git_pool_init(&out->pool, sizeof(git_diff_line));
 
 	for (start = in; start < in + in_len; start = end) {
-		end = memchr(start, '\n', in_len);
+		end = memchr(start, '\n', in_len - (start - in));
 
 		if (end == NULL)
 			end = in + in_len;
diff --git a/tests/apply/fromdiff.c b/tests/apply/fromdiff.c
index 8a6d8fa..832415d 100644
--- a/tests/apply/fromdiff.c
+++ b/tests/apply/fromdiff.c
@@ -333,3 +333,36 @@ void test_apply_fromdiff__binary_delete(void)
 		NULL, NULL,
 		NULL, &binary_opts));
 }
+
+void test_apply_fromdiff__patching_correctly_truncates_source(void)
+{
+	git_buf original = GIT_BUF_INIT, patched = GIT_BUF_INIT;
+	git_patch *patch;
+	unsigned int mode;
+	char *path;
+
+	cl_git_pass(git_patch_from_buffers(&patch,
+					   "foo\nbar", 7, "file.txt",
+					   "foo\nfoo", 7, "file.txt", NULL));
+
+	/*
+	 * Previously, we would fail to correctly truncate the source buffer if
+	 * the source has more than one line and ends with a non-newline
+	 * character. In the following call, we thus truncate the source string
+	 * in the middle of the second line. Without the bug fixed, we would
+	 * successfully apply the patch to the source and return success. With
+	 * the overflow being fixed, we should return an error.
+	 */
+	cl_git_fail_with(GIT_EAPPLYFAIL,
+			 git_apply__patch(&patched, &path, &mode,
+					  "foo\nbar\n", 5, patch, NULL));
+
+	/* Verify that the patch succeeds if we do not truncate */
+	cl_git_pass(git_apply__patch(&patched, &path, &mode,
+				     "foo\nbar\n", 7, patch, NULL));
+
+	git_buf_dispose(&original);
+	git_buf_dispose(&patched);
+	git_patch_free(patch);
+	git__free(path);
+}