Compare commits

...
Sign in to create a new pull request.

6 commits

Author SHA1 Message Date
Todd Zullinger
1e50591a09 Fix git clone memory exhaustion (CVE-2017-15298)
Cherry-pick upstream patch from a937b37e76 (revision: quit pruning diff
more quickly when possible, 2017-10-13)¹.

Resolves: #1510455, #1510457

¹ a937b37e76
2017-11-15 23:42:26 -05:00
Todd Zullinger
a958e0ad2a Cherry-pick patch set to harden git shell and git cvsserver
Harden "git shell" against an unsafe user input, which "git cvsserver"
copes with poorly.

References:

http://seclists.org/oss-sec/2017/q3/534
https://public-inbox.org/git/xmqqy3p29ekj.fsf@gitster.mtv.corp.google.com/
2017-09-26 10:48:46 -04:00
Todd Zullinger
f7391883e6 Update to 2.9.5 (resolves CVE-2017-1000117)
From the release announcement¹

    A malicious third-party can give a crafted "ssh://..." URL to an
    unsuspecting victim, and an attempt to visit the URL can result in
    any program that exists on the victim's machine being executed.
    Such a URL could be placed in the .gitmodules file of a malicious
    project, and an unsuspecting victim could be tricked into running
    "git clone --recurse-submodules" to trigger the vulnerability.

    Credits to find and fix the issue go to Brian Neel at GitLab, Joern
    Schneeweisz of Recurity Labs and Jeff King at GitHub.

¹ https://public-inbox.org/git/xmqqh8xf482j.fsf@gitster.mtv.corp.google.com/
2017-08-10 17:47:48 -04:00
Todd Zullinger
b1b17a63f3 Update to 2.9.4 (resolves CVE-2017-8386) 2017-05-09 21:51:02 -04:00
Todd Zullinger
663c11a548 Apply upstream fix for blame segfault (#1438801)
Upstream commit: bc6b13a7d
2017-04-04 12:27:29 -04:00
Petr Stodulka
5a8bcd3824 fix infinite loop of "git ls-tree" on broken symlink
Resolves: #1414792
2017-01-19 21:37:37 +01:00
7 changed files with 865 additions and 8 deletions

View file

@ -0,0 +1,33 @@
From 45eb1ba86022558d71a9946ccbe173ba5d8f1e37 Mon Sep 17 00:00:00 2001
From: Petr Stodulka <pstodulk@redhat.com>
Date: Thu, 13 Oct 2016 13:23:47 +0200
Subject: [PATCH 1/2] Add test for ls-tree with broken symlink under refs/heads
git ls-tree goes into an infinite loop while serving pretty vanilla requests,
if the refs/heads/ directory contains a symlink that's broken. Added test
which check if git ends with expected exit code or timeout expires.
---
t/t3103-ls-tree-misc.sh | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/t/t3103-ls-tree-misc.sh b/t/t3103-ls-tree-misc.sh
index 09dcf04..faf79c4 100755
--- a/t/t3103-ls-tree-misc.sh
+++ b/t/t3103-ls-tree-misc.sh
@@ -21,4 +21,13 @@ test_expect_success 'ls-tree fails with non-zero exit code on broken tree' '
test_must_fail git ls-tree -r HEAD
'
+test_expect_success 'ls-tree fails due to broken symlink instead of infinite loop' '
+ mkdir foo_infinit &&
+ cd foo_infinit &&
+ git init testrepo &&
+ cd testrepo &&
+ mkdir -p .git/refs/remotes &&
+ ln -s ../remotes/foo .git/refs/heads/bar &&
+ test_expect_code 128 timeout 2 git ls-tree bar
+'
test_done
--
2.5.5

View file

@ -0,0 +1,63 @@
From bc6b13a7d2300e982dd3a3aeef2f3ad4d39cf149 Mon Sep 17 00:00:00 2001
From: Thomas Gummerer <t.gummerer@gmail.com>
Date: Sat, 27 Aug 2016 21:01:50 +0100
Subject: [PATCH] blame: fix segfault on untracked files
Since 3b75ee9 ("blame: allow to blame paths freshly added to the index",
2016-07-16) git blame also looks at the index to determine if there is a
file that was freshly added to the index.
cache_name_pos returns -pos - 1 in case there is no match is found, or
if the name matches, but the entry has a stage other than 0. As git
blame should work for unmerged files, it uses strcmp to determine
whether the name of the returned position matches, in which case the
file exists, but is merely unmerged, or if the file actually doesn't
exist in the index.
If the repository is empty, or if the file would lexicographically be
sorted as the last file in the repository, -cache_name_pos - 1 is
outside of the length of the active_cache array, causing git blame to
segfault. Guard against that, and die() normally to restore the old
behaviour.
Reported-by: Simon Ruderich <simon@ruderich.org>
Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
builtin/blame.c | 3 ++-
t/t8002-blame.sh | 5 +++++
2 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/builtin/blame.c b/builtin/blame.c
index 12c765acfe..4d52f9602e 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -2245,7 +2245,8 @@ static void verify_working_tree_path(struct commit *work_tree, const char *path)
pos = cache_name_pos(path, strlen(path));
if (pos >= 0)
; /* path is in the index */
- else if (!strcmp(active_cache[-1 - pos]->name, path))
+ else if (-1 - pos < active_nr &&
+ !strcmp(active_cache[-1 - pos]->name, path))
; /* path is in the index, unmerged */
else
die("no such path '%s' in HEAD", path);
diff --git a/t/t8002-blame.sh b/t/t8002-blame.sh
index ff09aced68..ab79de9544 100755
--- a/t/t8002-blame.sh
+++ b/t/t8002-blame.sh
@@ -6,6 +6,11 @@ test_description='git blame'
PROG='git blame -c'
. "$TEST_DIRECTORY"/annotate-tests.sh
+test_expect_success 'blame untracked file in empty repo' '
+ >untracked &&
+ test_must_fail git blame untracked
+'
+
PROG='git blame -c -e'
test_expect_success 'blame --show-email' '
check_count \
--
2.11.0

View file

@ -0,0 +1,129 @@
From 0523d90a901e5cb6116e725f02c7f80a444405b6 Mon Sep 17 00:00:00 2001
From: Jeff King <peff@peff.net>
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 </dev/null`);
for my $a (1..1000) {
for my $b (1..1000) {
print "100644 $blob\t$a/$b\n";
}
}
' | git update-index --index-info
git commit -qm add
git rev-list HEAD -- .
This case takes about 100ms now, but after this patch only
needs 6ms. That's not a huge improvement, but it's easy to
get and it protects us against even more pathological cases
(e.g., going from 1 million to 10 million files would take
ten times as long with the current code, but not increase at
all after this patch).
This is reported to minorly speed-up pathspec limiting in
real world repositories (like the 100-million-file Windows
repository), but probably won't make a noticeable difference
outside of pathological setups.
This patch actually covers the case without --remove-empty,
and the case where we see only deletions. See the in-code
comment for details.
Note that we have to add a new member to the diff_options
struct so that our callback can see the value of
revs->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 <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
(cherry picked from commit a937b37e766479c8e780b17cce9c4b252fd97e40)
---
diff.h | 1 +
revision.c | 16 +++++++++++++---
2 files changed, 14 insertions(+), 3 deletions(-)
diff --git a/diff.h b/diff.h
index 125447be09..e37d93ec0a 100644
--- a/diff.h
+++ b/diff.h
@@ -171,6 +171,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 fe0f3a4f41..361ae5eb16 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);
}
@@ -1348,6 +1357,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

View file

@ -0,0 +1,74 @@
From 5bc590cd3ce202243cd1c3ee22104bcb4f3b6c4e Mon Sep 17 00:00:00 2001
From: Michael Haggerty <mhagger@alum.mit.edu>
Date: Wed, 25 Mar 2015 22:33:11 +0100
Subject: [PATCH 2/2] resolve_ref_unsafe(): limit the number of "stat_ref"
retries
If there is a broken symlink where a loose reference file is expected,
then the attempt to open() it fails with ENOENT. This error is
misinterpreted to mean that the loose reference file itself has
disappeared due to a race, causing the lookup to be retried. But in
this scenario, the retries all suffer from the same problem, causing
an infinite loop.
So put a limit (of 5) on the number of times that the stat_ref step
can be retried.
Based-on-patch-by: Petr Stodulka <pstodulk@redhat.com>
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
refs/files-backend.c | 6 ++++--
refs/refs-internal.h | 6 ++++++
2 files changed, 10 insertions(+), 2 deletions(-)
diff --git a/refs/files-backend.c b/refs/files-backend.c
index d16feb1..245a0b5 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -1353,6 +1353,7 @@ static int files_read_raw_ref(struct ref_store *ref_store,
int fd;
int ret = -1;
int save_errno;
+ int retries = 0;
strbuf_reset(&sb_path);
strbuf_git_path(&sb_path, "%s", refname);
@@ -1390,7 +1391,8 @@ static int files_read_raw_ref(struct ref_store *ref_store,
if (S_ISLNK(st.st_mode)) {
strbuf_reset(&sb_contents);
if (strbuf_readlink(&sb_contents, path, 0) < 0) {
- if (errno == ENOENT || errno == EINVAL)
+ if ((errno == ENOENT || errno == EINVAL) &&
+ retries++ < MAXRETRIES)
/* inconsistent with lstat; retry */
goto stat_ref;
else
@@ -1426,7 +1428,7 @@ static int files_read_raw_ref(struct ref_store *ref_store,
*/
fd = open(path, O_RDONLY);
if (fd < 0) {
- if (errno == ENOENT)
+ if (errno == ENOENT && retries++ < MAXRETRIES)
/* inconsistent with lstat; retry */
goto stat_ref;
else
diff --git a/refs/refs-internal.h b/refs/refs-internal.h
index 708b260..37e6b99 100644
--- a/refs/refs-internal.h
+++ b/refs/refs-internal.h
@@ -255,6 +255,12 @@ int rename_ref_available(const char *old_refname, const char *new_refname);
/* We allow "recursive" symbolic refs. Only within reason, though */
#define SYMREF_MAXDEPTH 5
+/*
+ * We allow only MAXRETRIES tries to jump on stat_ref, because of possible
+ * infinite loop
+ */
+#define MAXRETRIES 5
+
/* Include broken references in a do_for_each_ref*() iteration: */
#define DO_FOR_EACH_INCLUDE_BROKEN 0x01
--
2.5.5

View file

@ -0,0 +1,518 @@
From 9a1c497f7f3fbbd21673a6023871b891a6225ebe Mon Sep 17 00:00:00 2001
From: Junio C Hamano <gitster@pobox.com>
Date: Mon, 11 Sep 2017 14:44:24 +0900
Subject: [PATCH 1/6] cvsserver: move safe_pipe_capture() to the main package
As a preparation for replacing `command` with a call to this
function from outside GITCVS::updater package, move it to the main
package.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
(cherry picked from commit fce13af5d20cad8dcb2d0e47bcf01b6960f08e55)
---
git-cvsserver.perl | 47 ++++++++++++++++++++++-------------------------
1 file changed, 22 insertions(+), 25 deletions(-)
diff --git a/git-cvsserver.perl b/git-cvsserver.perl
index d50c85ed7b..8229d9d198 100755
--- a/git-cvsserver.perl
+++ b/git-cvsserver.perl
@@ -3406,6 +3406,22 @@ sub refHashEqual
return $out;
}
+# an alternative to `command` that allows input to be passed as an array
+# to work around shell problems with weird characters in arguments
+
+sub safe_pipe_capture {
+
+ my @output;
+
+ if (my $pid = open my $child, '-|') {
+ @output = (<$child>);
+ close $child or die join(' ',@_).": $! $?";
+ } else {
+ exec(@_) or die "$! $?"; # exec() can fail the executable can't be found
+ }
+ return wantarray ? @output : join('',@output);
+}
+
package GITCVS::log;
@@ -3882,7 +3898,7 @@ sub update
# several candidate merge bases. let's assume
# that the first one is the best one.
my $base = eval {
- safe_pipe_capture('git', 'merge-base',
+ ::safe_pipe_capture('git', 'merge-base',
$lastpicked, $parent);
};
# The two branches may not be related at all,
@@ -4749,7 +4765,7 @@ sub getMetaFromCommithash
return $retVal;
}
- my($fileHash)=safe_pipe_capture("git","rev-parse","$revCommit:$filename");
+ my($fileHash) = ::safe_pipe_capture("git","rev-parse","$revCommit:$filename");
chomp $fileHash;
if(!($fileHash=~/^[0-9a-f]{40}$/))
{
@@ -4844,8 +4860,8 @@ sub lookupCommitRef
return $commitHash;
}
- $commitHash=safe_pipe_capture("git","rev-parse","--verify","--quiet",
- $self->unescapeRefName($ref));
+ $commitHash = ::safe_pipe_capture("git","rev-parse","--verify","--quiet",
+ $self->unescapeRefName($ref));
$commitHash=~s/\s*$//;
if(!($commitHash=~/^[0-9a-f]{40}$/))
{
@@ -4854,7 +4870,7 @@ sub lookupCommitRef
if( defined($commitHash) )
{
- my $type=safe_pipe_capture("git","cat-file","-t",$commitHash);
+ my $type = ::safe_pipe_capture("git","cat-file","-t",$commitHash);
if( ! ($type=~/^commit\s*$/ ) )
{
$commitHash=undef;
@@ -4907,7 +4923,7 @@ sub commitmessage
return $message;
}
- my @lines = safe_pipe_capture("git", "cat-file", "commit", $commithash);
+ my @lines = ::safe_pipe_capture("git", "cat-file", "commit", $commithash);
shift @lines while ( $lines[0] =~ /\S/ );
$message = join("",@lines);
$message .= " " if ( $message =~ /\n$/ );
@@ -5056,25 +5072,6 @@ sub in_array
return $retval;
}
-=head2 safe_pipe_capture
-
-an alternative to `command` that allows input to be passed as an array
-to work around shell problems with weird characters in arguments
-
-=cut
-sub safe_pipe_capture {
-
- my @output;
-
- if (my $pid = open my $child, '-|') {
- @output = (<$child>);
- close $child or die join(' ',@_).": $! $?";
- } else {
- exec(@_) or die "$! $?"; # exec() can fail the executable can't be found
- }
- return wantarray ? @output : join('',@output);
-}
-
=head2 mangle_dirname
create a string from a directory name that is suitable to use as
--
2.14.1
From a871ec30ae5d7b472b35641a2a1b35685b6d41bb Mon Sep 17 00:00:00 2001
From: joernchen <joernchen@phenoelit.de>
Date: Mon, 11 Sep 2017 14:45:09 +0900
Subject: [PATCH 2/6] cvsserver: use safe_pipe_capture instead of backticks
This makes the script pass arguments that are derived from end-user
input in safer way when invoking subcommands.
Reported-by: joernchen <joernchen@phenoelit.de>
Signed-off-by: joernchen <joernchen@phenoelit.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
(cherry picked from commit 27dd73871f814062737c327103ee43f1eb7f30d9)
---
git-cvsserver.perl | 22 +++++++++++-----------
1 file changed, 11 insertions(+), 11 deletions(-)
diff --git a/git-cvsserver.perl b/git-cvsserver.perl
index 8229d9d198..bd29b26cc2 100755
--- a/git-cvsserver.perl
+++ b/git-cvsserver.perl
@@ -841,7 +841,7 @@ sub req_Modified
# Save the file data in $state
$state->{entries}{$state->{directory}.$data}{modified_filename} = $filename;
$state->{entries}{$state->{directory}.$data}{modified_mode} = $mode;
- $state->{entries}{$state->{directory}.$data}{modified_hash} = `git hash-object $filename`;
+ $state->{entries}{$state->{directory}.$data}{modified_hash} = safe_pipe_capture('git','hash-object',$filename);
$state->{entries}{$state->{directory}.$data}{modified_hash} =~ s/\s.*$//s;
#$log->debug("req_Modified : file=$data mode=$mode size=$size");
@@ -1463,7 +1463,7 @@ sub req_update
# transmit file, format is single integer on a line by itself (file
# size) followed by the file contents
# TODO : we should copy files in blocks
- my $data = `cat $mergedFile`;
+ my $data = safe_pipe_capture('cat', $mergedFile);
$log->debug("File size : " . length($data));
print length($data) . "\n";
print $data;
@@ -1579,7 +1579,7 @@ sub req_ci
$branchRef = "refs/heads/$stickyInfo->{tag}";
}
- $parenthash = `git show-ref -s $branchRef`;
+ $parenthash = safe_pipe_capture('git', 'show-ref', '-s', $branchRef);
chomp $parenthash;
if ($parenthash !~ /^[0-9a-f]{40}$/)
{
@@ -1704,7 +1704,7 @@ sub req_ci
}
close $msg_fh;
- my $commithash = `git commit-tree $treehash -p $parenthash < $msg_filename`;
+ my $commithash = safe_pipe_capture('git', 'commit-tree', $treehash, '-p', $parenthash, '-F', $msg_filename);
chomp($commithash);
$log->info("Commit hash : $commithash");
@@ -2854,12 +2854,12 @@ sub transmitfile
die "Need filehash" unless ( defined ( $filehash ) and $filehash =~ /^[a-zA-Z0-9]{40}$/ );
- my $type = `git cat-file -t $filehash`;
+ my $type = safe_pipe_capture('git', 'cat-file', '-t', $filehash);
chomp $type;
die ( "Invalid type '$type' (expected 'blob')" ) unless ( defined ( $type ) and $type eq "blob" );
- my $size = `git cat-file -s $filehash`;
+ my $size = safe_pipe_capture('git', 'cat-file', '-s', $filehash);
chomp $size;
$log->debug("transmitfile($filehash) size=$size, type=$type");
@@ -3040,7 +3040,7 @@ sub ensureWorkTree
chdir $work->{emptyDir} or
die "Unable to chdir to $work->{emptyDir}\n";
- my $ver = `git show-ref -s refs/heads/$state->{module}`;
+ my $ver = safe_pipe_capture('git', 'show-ref', '-s', "refs/heads/$state->{module}");
chomp $ver;
if ($ver !~ /^[0-9a-f]{40}$/)
{
@@ -3287,7 +3287,7 @@ sub open_blob_or_die
die "Need filehash\n";
}
- my $type = `git cat-file -t $name`;
+ my $type = safe_pipe_capture('git', 'cat-file', '-t', $name);
chomp $type;
unless ( defined ( $type ) and $type eq "blob" )
@@ -3296,7 +3296,7 @@ sub open_blob_or_die
die ( "Invalid type '$type' (expected 'blob')" )
}
- my $size = `git cat-file -s $name`;
+ my $size = safe_pipe_capture('git', 'cat-file', '-s', $name);
chomp $size;
$log->debug("open_blob_or_die($name) size=$size, type=$type");
@@ -3813,10 +3813,10 @@ sub update
# first lets get the commit list
$ENV{GIT_DIR} = $self->{git_path};
- my $commitsha1 = `git rev-parse $self->{module}`;
+ my $commitsha1 = ::safe_pipe_capture('git', 'rev-parse', $self->{module});
chomp $commitsha1;
- my $commitinfo = `git cat-file commit $self->{module} 2>&1`;
+ my $commitinfo = ::safe_pipe_capture('git', 'cat-file', 'commit', $self->{module});
unless ( $commitinfo =~ /tree\s+[a-zA-Z0-9]{40}/ )
{
die("Invalid module '$self->{module}'");
--
2.14.1
From d213a1c065b12601e3147363f6e0b11d85f5d514 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <gitster@pobox.com>
Date: Mon, 11 Sep 2017 14:45:54 +0900
Subject: [PATCH 3/6] cvsserver: use safe_pipe_capture for `constant commands`
as well
This is not strictly necessary, but it is a good code hygiene.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
(cherry picked from commit 46203ac24dc7e6b5a8d4f1b024ed93591705d47b)
---
git-cvsserver.perl | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/git-cvsserver.perl b/git-cvsserver.perl
index bd29b26cc2..ae1044273d 100755
--- a/git-cvsserver.perl
+++ b/git-cvsserver.perl
@@ -356,7 +356,7 @@ sub req_Root
return 0;
}
- my @gitvars = `git config -l`;
+ my @gitvars = safe_pipe_capture(qw(git config -l));
if ($?) {
print "E problems executing git-config on the server -- this is not a git repository or the PATH is not set correctly.\n";
print "E \n";
@@ -943,7 +943,7 @@ sub req_co
# Provide list of modules, if -c was used.
if (exists $state->{opt}{c}) {
- my $showref = `git show-ref --heads`;
+ my $showref = safe_pipe_capture(qw(git show-ref --heads));
for my $line (split '\n', $showref) {
if ( $line =~ m% refs/heads/(.*)$% ) {
print "M $1\t$1\n";
@@ -1181,7 +1181,7 @@ sub req_update
# projects (heads in this case) to checkout.
#
if ($state->{module} eq '') {
- my $showref = `git show-ref --heads`;
+ my $showref = safe_pipe_capture(qw(git show-ref --heads));
print "E cvs update: Updating .\n";
for my $line (split '\n', $showref) {
if ( $line =~ m% refs/heads/(.*)$% ) {
@@ -1687,7 +1687,7 @@ sub req_ci
return;
}
- my $treehash = `git write-tree`;
+ my $treehash = safe_pipe_capture(qw(git write-tree));
chomp $treehash;
$log->debug("Treehash : $treehash, Parenthash : $parenthash");
--
2.14.1
From 7b1cfcc88d29d28133f6864c9ef7173d1edffa60 Mon Sep 17 00:00:00 2001
From: Jeff King <peff@peff.net>
Date: Mon, 11 Sep 2017 11:27:51 -0400
Subject: [PATCH 4/6] shell: drop git-cvsserver support by default
The git-cvsserver script is old and largely unmaintained
these days. But git-shell allows untrusted users to run it
out of the box, significantly increasing its attack surface.
Let's drop it from git-shell's list of internal handlers so
that it cannot be run by default. This is not backwards
compatible. But given the age and development activity on
CVS-related parts of Git, this is likely to impact very few
users, while helping many more (i.e., anybody who runs
git-shell and had no intention of supporting CVS).
There's no configuration mechanism in git-shell for us to
add a boolean and flip it to "off". But there is a mechanism
for adding custom commands, and adding CVS support here is
fairly trivial. Let's document it to give guidance to
anybody who really is still running cvsserver.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
(cherry picked from commit 9a42c03cb71eaa9d41ba67275de38c997a791c32)
---
Documentation/git-shell.txt | 16 ++++++++++++++
shell.c | 14 ------------
t/t9400-git-cvsserver-server.sh | 48 +++++++++++++++++++++++++++++++++++++++++
3 files changed, 64 insertions(+), 14 deletions(-)
diff --git a/Documentation/git-shell.txt b/Documentation/git-shell.txt
index 2e30a3e42d..54cf2560be 100644
--- a/Documentation/git-shell.txt
+++ b/Documentation/git-shell.txt
@@ -79,6 +79,22 @@ EOF
$ chmod +x $HOME/git-shell-commands/no-interactive-login
----------------
+To enable git-cvsserver access (which should generally have the
+`no-interactive-login` example above as a prerequisite, as creating
+the git-shell-commands directory allows interactive logins):
+
+----------------
+$ cat >$HOME/git-shell-commands/cvs <<\EOF
+if ! test $# = 1 && test "$1" = "server"
+then
+ echo >&2 "git-cvsserver only handles \"server\""
+ exit 1
+fi
+exec git cvsserver server
+EOF
+$ chmod +x $HOME/git-shell-commands/cvs
+----------------
+
SEE ALSO
--------
ssh(1),
diff --git a/shell.c b/shell.c
index fe2d314593..234b2d4f16 100644
--- a/shell.c
+++ b/shell.c
@@ -25,19 +25,6 @@ static int do_generic_cmd(const char *me, char *arg)
return execv_git_cmd(my_argv);
}
-static int do_cvs_cmd(const char *me, char *arg)
-{
- const char *cvsserver_argv[3] = {
- "cvsserver", "server", NULL
- };
-
- if (!arg || strcmp(arg, "server"))
- die("git-cvsserver only handles server: %s", arg);
-
- setup_path();
- return execv_git_cmd(cvsserver_argv);
-}
-
static int is_valid_cmd_name(const char *cmd)
{
/* Test command contains no . or / characters */
@@ -134,7 +121,6 @@ static struct commands {
{ "git-receive-pack", do_generic_cmd },
{ "git-upload-pack", do_generic_cmd },
{ "git-upload-archive", do_generic_cmd },
- { "cvs", do_cvs_cmd },
{ NULL },
};
diff --git a/t/t9400-git-cvsserver-server.sh b/t/t9400-git-cvsserver-server.sh
index 432c61d246..c30660d606 100755
--- a/t/t9400-git-cvsserver-server.sh
+++ b/t/t9400-git-cvsserver-server.sh
@@ -588,4 +588,52 @@ test_expect_success 'cvs annotate' '
test_cmp ../expect ../actual
'
+#------------
+# running via git-shell
+#------------
+
+cd "$WORKDIR"
+
+test_expect_success 'create remote-cvs helper' '
+ write_script remote-cvs <<-\EOF
+ exec git shell -c "cvs server"
+ EOF
+'
+
+test_expect_success 'cvs server does not run with vanilla git-shell' '
+ (
+ cd cvswork &&
+ CVS_SERVER=$WORKDIR/remote-cvs &&
+ export CVS_SERVER &&
+ test_must_fail cvs log merge
+ )
+'
+
+test_expect_success 'configure git shell to run cvs server' '
+ mkdir "$HOME"/git-shell-commands &&
+
+ write_script "$HOME"/git-shell-commands/cvs <<-\EOF &&
+ if ! test $# = 1 && test "$1" = "server"
+ then
+ echo >&2 "git-cvsserver only handles \"server\""
+ exit 1
+ fi
+ exec git cvsserver server
+ EOF
+
+ # Should not be used, but part of the recommended setup
+ write_script "$HOME"/git-shell-commands/no-interactive-login <<-\EOF
+ echo Interactive login forbidden
+ EOF
+'
+
+test_expect_success 'cvs server can run with recommended config' '
+ (
+ cd cvswork &&
+ CVS_SERVER=$WORKDIR/remote-cvs &&
+ export CVS_SERVER &&
+ cvs log merge
+ )
+'
+
test_done
--
2.14.1
From 974a1f5703091e07891cfb743c50cb66634ef8cf Mon Sep 17 00:00:00 2001
From: Jeff King <peff@peff.net>
Date: Mon, 11 Sep 2017 10:24:11 -0400
Subject: [PATCH 5/6] archimport: use safe_pipe_capture for user input
Refnames can contain shell metacharacters which need to be
passed verbatim to sub-processes. Using safe_pipe_capture
skips the shell entirely.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
(cherry picked from commit 8d0fad0a7a6ba34fd706c148fa7ed1f8eb2b8b26)
---
git-archimport.perl | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/git-archimport.perl b/git-archimport.perl
index 9cb123a07d..b7c173c345 100755
--- a/git-archimport.perl
+++ b/git-archimport.perl
@@ -983,7 +983,7 @@ sub find_parents {
# check that we actually know about the branch
next unless -e "$git_dir/refs/heads/$branch";
- my $mergebase = `git-merge-base $branch $ps->{branch}`;
+ my $mergebase = safe_pipe_capture(qw(git-merge-base), $branch, $ps->{branch});
if ($?) {
# Don't die here, Arch supports one-way cherry-picking
# between branches with no common base (or any relationship
@@ -1074,7 +1074,7 @@ sub find_parents {
sub git_rev_parse {
my $name = shift;
- my $val = `git-rev-parse $name`;
+ my $val = safe_pipe_capture(qw(git-rev-parse), $name);
die "Error: git-rev-parse $name" if $?;
chomp $val;
return $val;
--
2.14.1
From c6d8647e2857f1ffbe1c6ff0c006b8da0710f9d4 Mon Sep 17 00:00:00 2001
From: Jeff King <peff@peff.net>
Date: Mon, 11 Sep 2017 10:24:26 -0400
Subject: [PATCH 6/6] cvsimport: shell-quote variable used in backticks
We run `git rev-parse` though the shell, and quote its
argument only with single-quotes. This prevents most
metacharacters from being a problem, but misses the obvious
case when $name itself has single-quotes in it. We can fix
this by applying the usual shell-quoting formula.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
(cherry picked from commit 5b4efea666951efe0770f8d5a301f8917015315f)
---
git-cvsimport.perl | 1 +
1 file changed, 1 insertion(+)
diff --git a/git-cvsimport.perl b/git-cvsimport.perl
index 1e4e65a45d..36929921ea 100755
--- a/git-cvsimport.perl
+++ b/git-cvsimport.perl
@@ -642,6 +642,7 @@ sub is_sha1 {
sub get_headref ($) {
my $name = shift;
+ $name =~ s/'/'\\''/;
my $r = `git rev-parse --verify '$name' 2>/dev/null`;
return undef unless $? == 0;
chomp $r;
--
2.14.1

View file

@ -54,8 +54,8 @@
%endif
Name: git
Version: 2.9.3
Release: 1%{?dist}
Version: 2.9.5
Release: 3%{?dist}
Summary: Fast Version Control System
License: GPLv2
Group: Development/Tools
@ -92,6 +92,21 @@ Patch1: git-cvsimport-Ignore-cvsps-2.2b1-Branches-output.patch
# https://bugzilla.redhat.com/600411
Patch3: git-1.7-el5-emacs-support.patch
# fix infinite loop + test
Patch4: 0001-Add-test-for-ls-tree-with-broken-symlink-under-refs-.patch
Patch5: 0002-resolve_ref_unsafe-limit-the-number-of-stat_ref-retr.patch
# https://bugzilla.redhat.com/1438801
# https://github.com/git/git/commit/bc6b13a7d
Patch6: 0001-blame-fix-segfault-on-untracked-files.patch
# Cherry-picked patch set from 2.10.5 to harden git shell and git cvsserver
Patch7: git-2.9.5-git-shell-and-cvsserver-hardening.patch
# https://bugzilla.redhat.com/1510455 (CVE-2017-15298)
# https://github.com/git/git/commit/a937b37e76
Patch8: 0001-revision-quit-pruning-diff-more-quickly-when-possibl.patch
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
%if ! %{use_prebuilt_docs} && ! 0%{?_without_docs}
@ -376,6 +391,11 @@ rm -rf "$gpghome" # Cleanup tmp gpg home dir
%if %{emacs_old}
%patch3 -p1
%endif
%patch4 -p1
%patch5 -p1
%patch6 -p1
%patch7 -p1
%patch8 -p1
%if %{use_prebuilt_docs}
mkdir -p prebuilt_docs/{html,man}
@ -736,6 +756,26 @@ rm -rf %{buildroot}
# No files for you!
%changelog
* Tue Nov 07 2017 Todd Zullinger <tmz@pobox.com> - 2.9.5-3
- Fix git-clone memory exhaustion (CVE-2017-15298)
Resolves: #1510455, #1510457
* Tue Sep 26 2017 Todd Zullinger <tmz@pobox.com> - 2.9.5-2
- Cherry-pick patch set to harden git shell and git cvsserver
* Thu Aug 10 2017 Todd Zullinger <tmz@pobox.com> - 2.9.5-1
- Update to 2.9.5 (resolves CVE-2017-1000117)
* Tue May 09 2017 Todd Zullinger <tmz@pobox.com> - 2.9.4-1
- Update to 2.9.4 (resolves CVE-2017-8386)
* Tue Apr 04 2017 Todd Zullinger <tmz@pobox.com> - 2.9.3-3
- Apply upstream fix for blame segfault (#1438801)
* Thu Jan 19 2017 Petr Stodulka <pstodulk@redhat.com> -2.9.3-2
- fix infinite loop of "git ls-tree" on broken symlink
Resolves: #1414792
* Mon Aug 15 2016 Jon Ciesla <limburgher@gmail.com> - 2.9.3-1
- Update to 2.9.3.

12
sources
View file

@ -1,6 +1,6 @@
316776336775d25d58888686a4893c86 git-2.9.3.tar.sign
3d32cb5cb5b4f29bd2eb1cde596fc542 git-2.9.3.tar.xz
cf0bf766505aa0674006dba6561c153b git-htmldocs-2.9.3.tar.sign
29378f5a360d86b9f5838ad74680accd git-htmldocs-2.9.3.tar.xz
1b7e97104f3f98cbae9fb475ab37aa19 git-manpages-2.9.3.tar.sign
337165a3b2bbe4814c73075cb6854ca2 git-manpages-2.9.3.tar.xz
SHA512 (git-2.9.5.tar.xz) = 5a3f62b9640a477bfd1a299f365ddb36c69ce5fb92ecf3ba7176686836a3057de7c74078b46526abd7bee0204c49e36bbd7d77d4c2f69c6524eca592ab6c365d
SHA512 (git-2.9.5.tar.sign) = ae799e503c9648fffd9c18ce7bf826f8e28cec2bce196f67275f49eeba3c250457c2a4aa8316079e43f06a6374bbc2c3cc196555f58452aecef8be8727b2edcd
SHA512 (git-htmldocs-2.9.5.tar.xz) = 8ac48cc1bd9635ede960fb2dc5e18590fa0c743e0892e754cade2b5b2833a24f463059f2992e74a281b5ae3ff48309187d06f2e546676ceb3c38cf5ef32fb555
SHA512 (git-htmldocs-2.9.5.tar.sign) = 9239fda9b02adf4232048d259fe133cf67999836e3bb9ea76e96b18721916af5b22b375afbd857cfbb49dbafd9a9b8e1a02b0e53d21ecf04f2e5831a6b9fcdea
SHA512 (git-manpages-2.9.5.tar.xz) = e4daa7b481c1e14da76dac04348843240c9ece80123f6e6e10835d74737605c03ca07c60b90b94f59ba5fc91b2608115db632f3636a6eec6c95df682191ea9fb
SHA512 (git-manpages-2.9.5.tar.sign) = 48f9395ea09200275fe9836f27bfea1672151b351ae79a6554d9817d72e97d0db8d4656079bd1af3eeb2fda2c1d7e0ec9ce3f16b0fc3dda659cfd6fc836c6495