From 7dbcd332fcc00efc962aea20d93c27f73466c4aa Mon Sep 17 00:00:00 2001 From: Todd Zullinger Date: Tue, 7 Nov 2017 12:08:47 -0500 Subject: [PATCH] Fix git-clone memory exhaustion (CVE-2017-15298) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cherry-pick upstream patch from a937b37e76 (revision: quit pruning diff more quickly when possible, 2017-10-13)¹. Resolves: #1510455, #1510457 ¹ https://github.com/git/git/commit/a937b37e76 --- ...uning-diff-more-quickly-when-possibl.patch | 128 ++++++++++++++++++ git.spec | 11 +- 2 files changed, 138 insertions(+), 1 deletion(-) create mode 100644 0001-revision-quit-pruning-diff-more-quickly-when-possibl.patch diff --git a/0001-revision-quit-pruning-diff-more-quickly-when-possibl.patch b/0001-revision-quit-pruning-diff-more-quickly-when-possibl.patch new file mode 100644 index 0000000..f2136f6 --- /dev/null +++ b/0001-revision-quit-pruning-diff-more-quickly-when-possibl.patch @@ -0,0 +1,128 @@ +From a937b37e766479c8e780b17cce9c4b252fd97e40 Mon Sep 17 00:00:00 2001 +From: Jeff King +Date: Fri, 13 Oct 2017 11:27:45 -0400 +Subject: [PATCH] revision: quit pruning diff more quickly when possible + +When the revision traversal machinery is given a pathspec, +we must compute the parent-diff for each commit to determine +which ones are TREESAME. We set the QUICK diff flag to avoid +looking at more entries than we need; we really just care +whether there are any changes at all. + +But there is one case where we want to know a bit more: if +--remove-empty is set, we care about finding cases where the +change consists only of added entries (in which case we may +prune the parent in try_to_simplify_commit()). To cover that +case, our file_add_remove() callback does not quit the diff +upon seeing an added entry; it keeps looking for other types +of entries. + +But this means when --remove-empty is not set (and it is not +by default), we compute more of the diff than is necessary. +You can see this in a pathological case where a commit adds +a very large number of entries, and we limit based on a +broad pathspec. E.g.: + + perl -e ' + chomp(my $blob = `git hash-object -w --stdin remove_empty_trees. This callback parameter could be +passed to the "add_remove" and "change" callbacks, but +there's not much point. They already receive the +diff_options struct, and doing it this way avoids having to +update the function signature of the other callbacks +(arguably the format_callback and output_prefix functions +could benefit from the same simplification). + +Signed-off-by: Jeff King +Signed-off-by: Junio C Hamano +--- + diff.h | 1 + + revision.c | 16 +++++++++++++--- + 2 files changed, 14 insertions(+), 3 deletions(-) + +diff --git a/diff.h b/diff.h +index e9ccb38c26..fe5c287a70 100644 +--- a/diff.h ++++ b/diff.h +@@ -180,6 +180,7 @@ struct diff_options { + pathchange_fn_t pathchange; + change_fn_t change; + add_remove_fn_t add_remove; ++ void *change_fn_data; + diff_format_fn_t format_callback; + void *format_callback_data; + diff_prefix_fn_t output_prefix; +diff --git a/revision.c b/revision.c +index 771d079f6e..7c23ab7afe 100644 +--- a/revision.c ++++ b/revision.c +@@ -394,8 +394,16 @@ static struct commit *one_relevant_parent(const struct rev_info *revs, + * if the whole diff is removal of old data, and otherwise + * REV_TREE_DIFFERENT (of course if the trees are the same we + * want REV_TREE_SAME). +- * That means that once we get to REV_TREE_DIFFERENT, we do not +- * have to look any further. ++ * ++ * The only time we care about the distinction is when ++ * remove_empty_trees is in effect, in which case we care only about ++ * whether the whole change is REV_TREE_NEW, or if there's another type ++ * of change. Which means we can stop the diff early in either of these ++ * cases: ++ * ++ * 1. We're not using remove_empty_trees at all. ++ * ++ * 2. We saw anything except REV_TREE_NEW. + */ + static int tree_difference = REV_TREE_SAME; + +@@ -406,9 +414,10 @@ static void file_add_remove(struct diff_options *options, + const char *fullpath, unsigned dirty_submodule) + { + int diff = addremove == '+' ? REV_TREE_NEW : REV_TREE_OLD; ++ struct rev_info *revs = options->change_fn_data; + + tree_difference |= diff; +- if (tree_difference == REV_TREE_DIFFERENT) ++ if (!revs->remove_empty_trees || tree_difference != REV_TREE_NEW) + DIFF_OPT_SET(options, HAS_CHANGES); + } + +@@ -1346,6 +1355,7 @@ void init_revisions(struct rev_info *revs, const char *prefix) + DIFF_OPT_SET(&revs->pruning, QUICK); + revs->pruning.add_remove = file_add_remove; + revs->pruning.change = file_change; ++ revs->pruning.change_fn_data = revs; + revs->sort_order = REV_SORT_IN_GRAPH_ORDER; + revs->dense = 1; + revs->prefix = prefix; +-- +2.15.0 + diff --git a/git.spec b/git.spec index 694e91f..5e595f9 100644 --- a/git.spec +++ b/git.spec @@ -45,7 +45,7 @@ Name: git Version: 2.15.0 -Release: 1%{?dist} +Release: 2%{?dist} Summary: Fast Version Control System License: GPLv2 Group: Development/Tools @@ -76,6 +76,10 @@ Patch0: git-1.8-gitweb-home-link.patch # https://bugzilla.redhat.com/490602 Patch1: git-cvsimport-Ignore-cvsps-2.2b1-Branches-output.patch +# https://bugzilla.redhat.com/1510455 (CVE-2017-15298) +# https://github.com/git/git/commit/a937b37e76 +Patch2: 0001-revision-quit-pruning-diff-more-quickly-when-possibl.patch + BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) %if ! 0%{?_without_docs} @@ -346,6 +350,7 @@ rm -rf "$tar" "$gpghome" # Cleanup tar files and tmp gpg home dir %setup -q %patch0 -p1 %patch1 -p1 +%patch2 -p1 # Remove git-archimport from command list sed -i '/^git-archimport/d' command-list.txt @@ -730,6 +735,10 @@ rm -rf %{buildroot} # No files for you! %changelog +* Tue Nov 07 2017 Todd Zullinger - 2.15.0-2 +- Fix git-clone memory exhaustion (CVE-2017-15298) + Resolves: #1510455, #1510457 + * Mon Oct 30 2017 Todd Zullinger - 2.15.0-1 - Update to 2.15.0