Commit 2054fe50ee8d3a8798e63acb7c603b273c5f79fc

Patrick Steinhardt 2018-08-30T12:41:15

Merge pull request #4781 from nelhage/multiline-loop config: convert unbounded recursion into a loop

diff --git a/src/config_parse.c b/src/config_parse.c
index eed6cf3..d40d47f 100644
--- a/src/config_parse.c
+++ b/src/config_parse.c
@@ -317,48 +317,43 @@ static int parse_multiline_variable(git_config_parser *reader, git_buf *value, i
 {
 	char *line = NULL, *proc_line = NULL;
 	int quote_count;
-	bool multiline;
+	bool multiline = true;
 
-	/* Check that the next line exists */
-	git_parse_advance_line(&reader->ctx);
-	line = git__strndup(reader->ctx.line, reader->ctx.line_len);
-	if (line == NULL)
-		return -1;
+	while (multiline) {
+		/* Check that the next line exists */
+		git_parse_advance_line(&reader->ctx);
+		line = git__strndup(reader->ctx.line, reader->ctx.line_len);
+		if (line == NULL)
+			return -1;
 
-	/* We've reached the end of the file, there is no continuation.
-	 * (this is not an error).
-	 */
-	if (line[0] == '\0') {
-		git__free(line);
-		return 0;
-	}
-
-	quote_count = strip_comments(line, !!in_quotes);
+		/* We've reached the end of the file, there is no continuation.
+		 * (this is not an error).
+		 */
+		if (line[0] == '\0') {
+			git__free(line);
+			return 0;
+		}
 
-	/* If it was just a comment, pretend it didn't exist */
-	if (line[0] == '\0') {
-		git__free(line);
-		return parse_multiline_variable(reader, value, quote_count);
-		/* TODO: unbounded recursion. This **could** be exploitable */
-	}
+		quote_count = strip_comments(line, !!in_quotes);
 
-	if (unescape_line(&proc_line, &multiline, line, in_quotes) < 0) {
-		git__free(line);
-		return -1;
-	}
-	/* add this line to the multiline var */
+		/* If it was just a comment, pretend it didn't exist */
+		if (line[0] == '\0') {
+			in_quotes = quote_count;
+			continue;
+		}
 
-	git_buf_puts(value, proc_line);
-	git__free(line);
-	git__free(proc_line);
+		if (unescape_line(&proc_line, &multiline, line, in_quotes) < 0) {
+			git__free(line);
+			return -1;
+		}
+		/* add this line to the multiline var */
 
-	/*
-	 * If we need to continue reading the next line, let's just
-	 * keep putting stuff in the buffer
-	 */
-	if (multiline)
-		return parse_multiline_variable(reader, value, quote_count);
+		git_buf_puts(value, proc_line);
+		git__free(line);
+		git__free(proc_line);
 
+		in_quotes = quote_count;
+	}
 	return 0;
 }