forked from jelmer/dulwich
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add 02_cve_2015-0838: Fix buffer overflow in C implementation of apply_delta (CVE-2015-0838). Closes: #780958 * Add 03_cve_2014-9706: Don't allow writing to files under .git/ when checking out working trees (CVE-2014-9706). Closes: #780989
- Loading branch information
Showing
8 changed files
with
291 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
commit b25e8390074060ea2aed25cf070b8e98b85a3875 | ||
Author: Jelmer Vernooij <jelmer@google.com> | ||
Date: Fri Mar 6 12:29:07 2015 +0000 | ||
|
||
Fix buffer overflow in C version of apply_delta(). | ||
|
||
This is CVE-2015-0838. | ||
|
||
Thanks to Ivan Fratric of the Google Security Team for | ||
reporting this issue. | ||
|
||
diff --git a/dulwich/_pack.c b/dulwich/_pack.c | ||
index d1534a5..440a9a9 100644 | ||
--- a/dulwich/_pack.c | ||
+++ b/dulwich/_pack.c | ||
@@ -20,6 +20,8 @@ | ||
#include <Python.h> | ||
#include <stdint.h> | ||
|
||
+static PyObject *PyExc_ApplyDeltaError = NULL; | ||
+ | ||
static int py_is_sha(PyObject *sha) | ||
{ | ||
if (!PyString_CheckExact(sha)) | ||
@@ -103,7 +105,7 @@ static PyObject *py_apply_delta(PyObject *self, PyObject *args) | ||
index = 0; | ||
src_size = get_delta_header_size(delta, &index, delta_len); | ||
if (src_size != src_buf_len) { | ||
- PyErr_Format(PyExc_ValueError, | ||
+ PyErr_Format(PyExc_ApplyDeltaError, | ||
"Unexpected source buffer size: %lu vs %d", src_size, src_buf_len); | ||
Py_DECREF(py_src_buf); | ||
Py_DECREF(py_delta); | ||
@@ -146,12 +148,16 @@ static PyObject *py_apply_delta(PyObject *self, PyObject *args) | ||
break; | ||
memcpy(out+outindex, src_buf+cp_off, cp_size); | ||
outindex += cp_size; | ||
+ dest_size -= cp_size; | ||
} else if (cmd != 0) { | ||
+ if (cmd > dest_size) | ||
+ break; | ||
memcpy(out+outindex, delta+index, cmd); | ||
outindex += cmd; | ||
index += cmd; | ||
+ dest_size -= cmd; | ||
} else { | ||
- PyErr_SetString(PyExc_ValueError, "Invalid opcode 0"); | ||
+ PyErr_SetString(PyExc_ApplyDeltaError, "Invalid opcode 0"); | ||
Py_DECREF(ret); | ||
Py_DECREF(py_delta); | ||
Py_DECREF(py_src_buf); | ||
@@ -162,13 +168,13 @@ static PyObject *py_apply_delta(PyObject *self, PyObject *args) | ||
Py_DECREF(py_delta); | ||
|
||
if (index != delta_len) { | ||
- PyErr_SetString(PyExc_ValueError, "delta not empty"); | ||
+ PyErr_SetString(PyExc_ApplyDeltaError, "delta not empty"); | ||
Py_DECREF(ret); | ||
return NULL; | ||
} | ||
|
||
- if (dest_size != outindex) { | ||
- PyErr_SetString(PyExc_ValueError, "dest size incorrect"); | ||
+ if (dest_size != 0) { | ||
+ PyErr_SetString(PyExc_ApplyDeltaError, "dest size incorrect"); | ||
Py_DECREF(ret); | ||
return NULL; | ||
} | ||
@@ -236,6 +242,15 @@ static PyMethodDef py_pack_methods[] = { | ||
void init_pack(void) | ||
{ | ||
PyObject *m; | ||
+ PyObject *errors_module; | ||
+ | ||
+ errors_module = PyImport_ImportModule("dulwich.errors"); | ||
+ if (errors_module == NULL) | ||
+ return; | ||
+ | ||
+ PyExc_ApplyDeltaError = PyObject_GetAttrString(errors_module, "ApplyDeltaError"); | ||
+ if (PyExc_ApplyDeltaError == NULL) | ||
+ return; | ||
|
||
m = Py_InitModule3("_pack", py_pack_methods, NULL); | ||
if (m == NULL) | ||
diff --git a/dulwich/tests/test_pack.py b/dulwich/tests/test_pack.py | ||
index d33104f..393b931 100644 | ||
--- a/dulwich/tests/test_pack.py | ||
+++ b/dulwich/tests/test_pack.py | ||
@@ -28,6 +28,7 @@ import tempfile | ||
import zlib | ||
|
||
from dulwich.errors import ( | ||
+ ApplyDeltaError, | ||
ChecksumMismatch, | ||
) | ||
from dulwich.file import ( | ||
@@ -181,6 +182,14 @@ class TestPackDeltas(TestCase): | ||
self.skipTest("big strings don't work yet") | ||
self._test_roundtrip(self.test_string_huge, self.test_string_huge) | ||
|
||
+ def test_dest_overflow(self): | ||
+ self.assertRaises( | ||
+ ApplyDeltaError, | ||
+ apply_delta, 'a'*0x10000, '\x80\x80\x04\x80\x80\x04\x80' + 'a'*0x10000) | ||
+ self.assertRaises( | ||
+ ApplyDeltaError, | ||
+ apply_delta, '', '\x00\x80\x02\xb0\x11\x11') | ||
+ | ||
|
||
class TestPackData(PackTests): | ||
"""Tests getting the data from the packfile.""" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
commit 598be9170c5e21ba408ba139a2b7bd7da6a04c70 | ||
Author: Jelmer Vernooij <jelmer@samba.org> | ||
Date: Thu Jan 15 23:30:28 2015 +0100 | ||
|
||
By default refuse to create index entries with a path starting with .git/. | ||
|
||
diff --git a/dulwich/index.py b/dulwich/index.py | ||
index f32d70f..3c7488e 100644 | ||
--- a/dulwich/index.py | ||
+++ b/dulwich/index.py | ||
@@ -402,8 +402,14 @@ def index_entry_from_stat(stat_val, hex_sha, flags, mode=None): | ||
stat_val.st_gid, stat_val.st_size, hex_sha, flags) | ||
|
||
|
||
+def validate_path_default(path): | ||
+ """Default path validator that just checks for .git/.""" | ||
+ return not path.startswith(".git/") | ||
+ | ||
+ | ||
def build_index_from_tree(prefix, index_path, object_store, tree_id, | ||
- honor_filemode=True): | ||
+ honor_filemode=True, | ||
+ validate_path=validate_path_default): | ||
"""Generate and materialize index from a tree | ||
|
||
:param tree_id: Tree to materialize | ||
@@ -412,6 +418,8 @@ def build_index_from_tree(prefix, index_path, object_store, tree_id, | ||
:param object_store: Non-empty object store holding tree contents | ||
:param honor_filemode: An optional flag to honor core.filemode setting in | ||
config file, default is core.filemode=True, change executable bit | ||
+ :param validate_path: Function to validate paths to check out; | ||
+ default just refuses filenames starting with .git/. | ||
|
||
:note:: existing index is wiped and contents are not merged | ||
in a working dir. Suiteable only for fresh clones. | ||
@@ -420,6 +428,8 @@ def build_index_from_tree(prefix, index_path, object_store, tree_id, | ||
index = Index(index_path) | ||
|
||
for entry in object_store.iter_tree_contents(tree_id): | ||
+ if not validate_path(entry.path): | ||
+ continue | ||
full_path = os.path.join(prefix, entry.path) | ||
|
||
if not os.path.exists(os.path.dirname(full_path)): | ||
diff --git a/dulwich/tests/test_index.py b/dulwich/tests/test_index.py | ||
index 89b5d54..1852b9a 100644 | ||
--- a/dulwich/tests/test_index.py | ||
+++ b/dulwich/tests/test_index.py | ||
@@ -284,6 +284,43 @@ class BuildIndexTests(TestCase): | ||
# Verify no files | ||
self.assertEqual(['.git'], os.listdir(repo.path)) | ||
|
||
+ def test_git_dir(self): | ||
+ if os.name != 'posix': | ||
+ self.skipTest("test depends on POSIX shell") | ||
+ | ||
+ repo_dir = tempfile.mkdtemp() | ||
+ repo = Repo.init(repo_dir) | ||
+ self.addCleanup(shutil.rmtree, repo_dir) | ||
+ | ||
+ # Populate repo | ||
+ filea = Blob.from_string('file a') | ||
+ filee = Blob.from_string('d') | ||
+ | ||
+ tree = Tree() | ||
+ tree['.git/a'] = (stat.S_IFREG | 0o644, filea.id) | ||
+ tree['c/e'] = (stat.S_IFREG | 0o644, filee.id) | ||
+ | ||
+ repo.object_store.add_objects([(o, None) | ||
+ for o in [filea, filee, tree]]) | ||
+ | ||
+ build_index_from_tree(repo.path, repo.index_path(), | ||
+ repo.object_store, tree.id) | ||
+ | ||
+ # Verify index entries | ||
+ index = repo.open_index() | ||
+ self.assertEqual(len(index), 1) | ||
+ | ||
+ # filea | ||
+ apath = os.path.join(repo.path, '.git', 'a') | ||
+ self.assertFalse(os.path.exists(apath)) | ||
+ | ||
+ # filee | ||
+ epath = os.path.join(repo.path, 'c', 'e') | ||
+ self.assertTrue(os.path.exists(epath)) | ||
+ self.assertReasonableIndexEntry(index['c/e'], | ||
+ stat.S_IFREG | 0o644, 1, filee.id) | ||
+ self.assertFileContents(epath, 'd') | ||
+ | ||
def test_nonempty(self): | ||
if os.name != 'posix': | ||
self.skipTest("test depends on POSIX shell") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,3 @@ | ||
01_setup_cfg | ||
02_cve_2015-0838 | ||
03_cve_2014-9706 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters