merge with stable

This commit is contained in:
Matt Mackall 2016-03-29 12:29:00 -05:00
commit 4a96519522
8 changed files with 129 additions and 82 deletions

View File

@ -122,3 +122,4 @@ ea389970c08449440587712117f178d33bab3f1e 0 iQIVAwUAVociGyBXgaxoKi1yAQJx9Q//TzMyp
2408645de650d8a29a6ce9e7dce601d8dd0d1474 0 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

View File

@ -352,6 +352,9 @@ class commandline(object):
def _run2(self, cmd, *args, **kwargs):
return self._dorun(util.popen2, cmd, *args, **kwargs)
def _run3(self, cmd, *args, **kwargs):
return self._dorun(util.popen3, cmd, *args, **kwargs)
def _dorun(self, openfunc, cmd, *args, **kwargs):
cmdline = self._cmdline(cmd, *args, **kwargs)
self.ui.debug('running: %s\n' % (cmdline,))

View File

@ -32,61 +32,28 @@ class submodule(object):
def hgsubstate(self):
return "%s %s" % (self.node, self.path)
class convert_git(common.converter_source):
class convert_git(common.converter_source, common.commandline):
# Windows does not support GIT_DIR= construct while other systems
# cannot remove environment variable. Just assume none have
# both issues.
if util.safehasattr(os, 'unsetenv'):
def gitopen(self, s, err=None):
prevgitdir = os.environ.get('GIT_DIR')
os.environ['GIT_DIR'] = self.path
try:
if err == subprocess.PIPE:
(stdin, stdout, stderr) = util.popen3(s)
return stdout
elif err == subprocess.STDOUT:
return self.popen_with_stderr(s)
else:
return util.popen(s, 'rb')
finally:
if prevgitdir is None:
del os.environ['GIT_DIR']
else:
os.environ['GIT_DIR'] = prevgitdir
def gitpipe(self, s):
prevgitdir = os.environ.get('GIT_DIR')
os.environ['GIT_DIR'] = self.path
try:
return util.popen3(s)
finally:
if prevgitdir is None:
del os.environ['GIT_DIR']
else:
os.environ['GIT_DIR'] = prevgitdir
def _gitcmd(self, cmd, *args, **kwargs):
return cmd('--git-dir=%s' % self.path, *args, **kwargs)
else:
def gitopen(self, s, err=None):
if err == subprocess.PIPE:
(sin, so, se) = util.popen3('GIT_DIR=%s %s' % (self.path, s))
return so
elif err == subprocess.STDOUT:
return self.popen_with_stderr(s)
else:
return util.popen('GIT_DIR=%s %s' % (self.path, s), 'rb')
def gitrun0(self, *args, **kwargs):
return self._gitcmd(self.run0, *args, **kwargs)
def gitpipe(self, s):
return util.popen3('GIT_DIR=%s %s' % (self.path, s))
def gitrun(self, *args, **kwargs):
return self._gitcmd(self.run, *args, **kwargs)
def popen_with_stderr(self, s):
p = subprocess.Popen(s, shell=True, bufsize=-1,
close_fds=util.closefds,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
universal_newlines=False,
env=None)
return p.stdout
def gitrunlines0(self, *args, **kwargs):
return self._gitcmd(self.runlines0, *args, **kwargs)
def gitrunlines(self, *args, **kwargs):
return self._gitcmd(self.runlines, *args, **kwargs)
def gitpipe(self, *args, **kwargs):
return self._gitcmd(self._run3, *args, **kwargs)
def gitread(self, s):
fh = self.gitopen(s)
@ -95,6 +62,7 @@ class convert_git(common.converter_source):
def __init__(self, ui, path, revs=None):
super(convert_git, self).__init__(ui, path, revs=revs)
common.commandline.__init__(self, ui, 'git')
if os.path.isdir(path + "/.git"):
path += "/.git"
@ -107,20 +75,20 @@ class convert_git(common.converter_source):
if similarity < 0 or similarity > 100:
raise error.Abort(_('similarity must be between 0 and 100'))
if similarity > 0:
self.simopt = '-C%d%%' % similarity
self.simopt = ['-C%d%%' % similarity]
findcopiesharder = ui.configbool('convert', 'git.findcopiesharder',
False)
if findcopiesharder:
self.simopt += ' --find-copies-harder'
self.simopt.append('--find-copies-harder')
else:
self.simopt = ''
self.simopt = []
common.checktool('git', 'git')
self.path = path
self.submodules = []
self.catfilepipe = self.gitpipe('git cat-file --batch')
self.catfilepipe = self.gitpipe('cat-file', '--batch')
def after(self):
for f in self.catfilepipe:
@ -128,14 +96,14 @@ class convert_git(common.converter_source):
def getheads(self):
if not self.revs:
heads, ret = self.gitread('git rev-parse --branches --remotes')
heads = heads.splitlines()
if ret:
output, status = self.gitrun('rev-parse', '--branches', '--remotes')
heads = output.splitlines()
if status:
raise error.Abort(_('cannot retrieve git heads'))
else:
heads = []
for rev in self.revs:
rawhead, ret = self.gitread("git rev-parse --verify %s" % rev)
rawhead, ret = self.gitrun('rev-parse', '--verify', rev)
heads.append(rawhead[:-1])
if ret:
raise error.Abort(_('cannot retrieve git head "%s"') % rev)
@ -195,7 +163,7 @@ class convert_git(common.converter_source):
self.submodules.append(submodule(s['path'], '', s['url']))
def retrievegitmodules(self, version):
modules, ret = self.gitread("git show %s:%s" % (version, '.gitmodules'))
modules, ret = self.gitrun('show', '%s:%s' % (version, '.gitmodules'))
if ret:
# This can happen if a file is in the repo that has permissions
# 160000, but there is no .gitmodules file.
@ -211,7 +179,7 @@ class convert_git(common.converter_source):
return
for m in self.submodules:
node, ret = self.gitread("git rev-parse %s:%s" % (version, m.path))
node, ret = self.gitrun('rev-parse', '%s:%s' % (version, m.path))
if ret:
continue
m.node = node.strip()
@ -220,15 +188,17 @@ class convert_git(common.converter_source):
if full:
raise error.Abort(_("convert from git does not support --full"))
self.modecache = {}
fh = self.gitopen("git diff-tree -z --root -m -r %s %s" % (
self.simopt, version))
cmd = ['diff-tree','-z', '--root', '-m', '-r'] + self.simopt + [version]
output, status = self.gitrun(*cmd)
if status:
raise error.Abort(_('cannot read changes in %s') % version)
changes = []
copies = {}
seen = set()
entry = None
subexists = [False]
subdeleted = [False]
difftree = fh.read().split('\x00')
difftree = output.split('\x00')
lcount = len(difftree)
i = 0
@ -290,8 +260,6 @@ class convert_git(common.converter_source):
if f != '.gitmodules' and fdest != '.gitmodules':
copies[fdest] = f
entry = None
if fh.close():
raise error.Abort(_('cannot read changes in %s') % version)
if subexists[0]:
if subdeleted[0]:
@ -338,17 +306,23 @@ class convert_git(common.converter_source):
return c
def numcommits(self):
return len([None for _ in self.gitopen('git rev-list --all')])
output, ret = self.gitrunlines('rev-list', '--all')
if ret:
raise error.Abort(_('cannot retrieve number of commits in %s') \
% self.path)
return len(output)
def gettags(self):
tags = {}
alltags = {}
fh = self.gitopen('git ls-remote --tags "%s"' % self.path,
err=subprocess.STDOUT)
output, status = self.gitrunlines('ls-remote', '--tags', self.path)
if status:
raise error.Abort(_('cannot read tags from %s') % self.path)
prefix = 'refs/tags/'
# Build complete list of tags, both annotated and bare ones
for line in fh:
for line in output:
line = line.strip()
if line.startswith("error:") or line.startswith("fatal:"):
raise error.Abort(_('cannot read tags from %s') % self.path)
@ -356,8 +330,6 @@ class convert_git(common.converter_source):
if not tag.startswith(prefix):
continue
alltags[tag[len(prefix):]] = node
if fh.close():
raise error.Abort(_('cannot read tags from %s') % self.path)
# Filter out tag objects for annotated tag refs
for tag in alltags:
@ -374,18 +346,20 @@ class convert_git(common.converter_source):
def getchangedfiles(self, version, i):
changes = []
if i is None:
fh = self.gitopen("git diff-tree --root -m -r %s" % version)
for l in fh:
output, status = self.gitrunlines('diff-tree', '--root', '-m',
'-r', version)
if status:
raise error.Abort(_('cannot read changes in %s') % version)
for l in output:
if "\t" not in l:
continue
m, f = l[:-1].split("\t")
changes.append(f)
else:
fh = self.gitopen('git diff-tree --name-only --root -r %s '
'"%s^%s" --' % (version, version, i + 1))
changes = [f.rstrip('\n') for f in fh]
if fh.close():
raise error.Abort(_('cannot read changes in %s') % version)
output, status = self.gitrunlines('diff-tree', '--name-only',
'--root', '-r', version,
'%s^%s' % (version, i + 1), '--')
changes = [f.rstrip('\n') for f in output]
return changes
@ -405,8 +379,8 @@ class convert_git(common.converter_source):
])
try:
fh = self.gitopen('git show-ref', err=subprocess.PIPE)
for line in fh:
output, status = self.gitrunlines('show-ref')
for line in output:
line = line.strip()
rev, name = line.split(None, 1)
# Process each type of branch

View File

@ -205,7 +205,7 @@ static struct flist *decode(const char *bin, Py_ssize_t len)
int pos = 0;
/* assume worst case size, we won't have many of these lists */
l = lalloc(len / 12);
l = lalloc(len / 12 + 1);
if (!l)
return NULL;
@ -215,10 +215,10 @@ static struct flist *decode(const char *bin, Py_ssize_t len)
lt->start = getbe32(bin + pos);
lt->end = getbe32(bin + pos + 4);
lt->len = getbe32(bin + pos + 8);
if (lt->start > lt->end)
break; /* sanity check */
lt->data = bin + pos + 12;
pos += 12 + lt->len;
if (lt->start > lt->end || lt->len < 0)
break; /* sanity check */
lt++;
}

View File

@ -1385,6 +1385,11 @@ class gitsubrepo(abstractsubrepo):
are not supported and very probably fail.
"""
self.ui.debug('%s: git %s\n' % (self._relpath, ' '.join(commands)))
if env is None:
env = os.environ.copy()
# fix for Git CVE-2015-7545
if 'GIT_ALLOW_PROTOCOL' not in env:
env['GIT_ALLOW_PROTOCOL'] = 'file:git:http:https:ssh'
# unless ui.quiet is set, print git's stderr,
# which is mostly progress and useful info
errpipe = None

View File

@ -714,7 +714,7 @@ damage git repository by renaming a commit object
$ COMMIT_OBJ=1c/0ce3c5886f83a1d78a7b517cdff5cf9ca17bdd
$ mv git-repo4/.git/objects/$COMMIT_OBJ git-repo4/.git/objects/$COMMIT_OBJ.tmp
$ hg convert git-repo4 git-repo4-broken-hg 2>&1 | grep 'abort:'
abort: cannot read tags from git-repo4/.git
abort: cannot retrieve number of commits in git-repo4/.git
$ mv git-repo4/.git/objects/$COMMIT_OBJ.tmp git-repo4/.git/objects/$COMMIT_OBJ
damage git repository by renaming a blob object
@ -729,3 +729,20 @@ damage git repository by renaming a tree object
$ mv git-repo4/.git/objects/$TREE_OBJ git-repo4/.git/objects/$TREE_OBJ.tmp
$ hg convert git-repo4 git-repo4-broken-hg 2>&1 | grep 'abort:'
abort: cannot read changes in 1c0ce3c5886f83a1d78a7b517cdff5cf9ca17bdd
test for escaping the repo name (CVE-2016-3069)
$ git init '`echo pwned >COMMAND-INJECTION`'
Initialized empty Git repository in $TESTTMP/`echo pwned >COMMAND-INJECTION`/.git/
$ cd '`echo pwned >COMMAND-INJECTION`'
$ git commit -q --allow-empty -m 'empty'
$ cd ..
$ hg convert '`echo pwned >COMMAND-INJECTION`' 'converted'
initializing destination converted repository
scanning source...
sorting...
converting...
0 empty
updating bookmarks
$ test -f COMMAND-INJECTION
[1]

15
tests/test-revlog.t Normal file
View File

@ -0,0 +1,15 @@
Test for CVE-2016-3630
$ hg init
>>> open("a.i", "w").write(
... """eJxjYGZgZIAAYQYGxhgom+k/FMx8YKx9ZUaKSOyqo4cnuKb8mbqHV5cBCVTMWb1Cwqkhe4Gsg9AD
... Joa3dYtcYYYBAQ8Qr4OqZAYRICPTSr5WKd/42rV36d+8/VmrNpv7NP1jQAXrQE4BqQUARngwVA=="""
... .decode("base64").decode("zlib"))
$ hg debugindex a.i
rev offset length delta linkrev nodeid p1 p2
0 0 19 -1 2 99e0332bd498 000000000000 000000000000
1 19 12 0 3 6674f57a23d8 99e0332bd498 000000000000
$ hg debugdata a.i 1 2>&1 | grep decoded
mpatch.mpatchError: patch cannot be decoded

View File

@ -1132,4 +1132,36 @@ make sure we show changed files, rather than changed subtrees
? s/foobar.orig
? s/snake.python.orig
test for Git CVE-2016-3068
$ hg init malicious-subrepository
$ cd malicious-subrepository
$ echo "s = [git]ext::sh -c echo% pwned% >&2" > .hgsub
$ git init s
Initialized empty Git repository in $TESTTMP/tc/malicious-subrepository/s/.git/
$ cd s
$ git commit --allow-empty -m 'empty'
[master (root-commit) 153f934] empty
$ cd ..
$ hg add .hgsub
$ hg commit -m "add subrepo"
$ cd ..
$ env -u GIT_ALLOW_PROTOCOL hg clone malicious-subrepository malicious-subrepository-protected
Cloning into '$TESTTMP/tc/malicious-subrepository-protected/s'...
fatal: transport 'ext' not allowed
updating to branch default
cloning subrepo s from ext::sh -c echo% pwned% >&2
abort: git clone error 128 in s (in subrepo s)
[255]
whitelisting of ext should be respected (that's the git submodule behaviour)
$ env GIT_ALLOW_PROTOCOL=ext hg clone malicious-subrepository malicious-subrepository-clone-allowed
Cloning into '$TESTTMP/tc/malicious-subrepository-clone-allowed/s'...
pwned
fatal: Could not read from remote repository.
Please make sure you have the correct access rights
and the repository exists.
updating to branch default
cloning subrepo s from ext::sh -c echo% pwned% >&2
abort: git clone error 128 in s (in subrepo s)
[255]