This version includes fixes for: * CVE-2020-15801 - Fixes python3x._pth being ignored on Windows * CVE-2019-20907 - Avoid infinite loop when reading specially crafted TAR files using the tarfile module This also: * Remove patches that are included in the update * Add a dependency in python3-distutils for python3-email[1] [1]: https://github.com/python/cpython/blob/v3.8.5/Lib/distutils/dist.py#L10 Signed-off-by: Jeffery To <jeffery.to@gmail.com>lilik-openwrt-22.03
@ -1,111 +0,0 @@ | |||
From f56c75ed53dcad4d59dff4377ae463d6b96acd3e Mon Sep 17 00:00:00 2001 | |||
From: "Miss Islington (bot)" | |||
<31488909+miss-islington@users.noreply.github.com> | |||
Date: Mon, 13 Jul 2020 06:05:44 -0700 | |||
Subject: [PATCH] bpo-41288: Fix a crash in unpickling invalid NEWOBJ_EX. | |||
(GH-21458) | |||
Automerge-Triggered-By: @tiran | |||
(cherry picked from commit 4f309abf55f0e6f8950ac13d6ec83c22b8d47bf8) | |||
Co-authored-by: Serhiy Storchaka <storchaka@gmail.com> | |||
--- | |||
Lib/test/pickletester.py | 18 ++++++++++++ | |||
.../2020-07-13-15-06-35.bpo-41288.8mn5P-.rst | 2 ++ | |||
Modules/_pickle.c | 29 ++++++++++++++----- | |||
3 files changed, 41 insertions(+), 8 deletions(-) | |||
create mode 100644 Misc/NEWS.d/next/Library/2020-07-13-15-06-35.bpo-41288.8mn5P-.rst | |||
diff --git a/Lib/test/pickletester.py b/Lib/test/pickletester.py | |||
index 9401043d78d18..ff7bbb0c8a9bf 100644 | |||
--- a/Lib/test/pickletester.py | |||
+++ b/Lib/test/pickletester.py | |||
@@ -1170,6 +1170,24 @@ def test_compat_unpickle(self): | |||
self.assertIs(type(unpickled), collections.UserDict) | |||
self.assertEqual(unpickled, collections.UserDict({1: 2})) | |||
+ def test_bad_reduce(self): | |||
+ self.assertEqual(self.loads(b'cbuiltins\nint\n)R.'), 0) | |||
+ self.check_unpickling_error(TypeError, b'N)R.') | |||
+ self.check_unpickling_error(TypeError, b'cbuiltins\nint\nNR.') | |||
+ | |||
+ def test_bad_newobj(self): | |||
+ error = (pickle.UnpicklingError, TypeError) | |||
+ self.assertEqual(self.loads(b'cbuiltins\nint\n)\x81.'), 0) | |||
+ self.check_unpickling_error(error, b'cbuiltins\nlen\n)\x81.') | |||
+ self.check_unpickling_error(error, b'cbuiltins\nint\nN\x81.') | |||
+ | |||
+ def test_bad_newobj_ex(self): | |||
+ error = (pickle.UnpicklingError, TypeError) | |||
+ self.assertEqual(self.loads(b'cbuiltins\nint\n)}\x92.'), 0) | |||
+ self.check_unpickling_error(error, b'cbuiltins\nlen\n)}\x92.') | |||
+ self.check_unpickling_error(error, b'cbuiltins\nint\nN}\x92.') | |||
+ self.check_unpickling_error(error, b'cbuiltins\nint\n)N\x92.') | |||
+ | |||
def test_bad_stack(self): | |||
badpickles = [ | |||
b'.', # STOP | |||
diff --git a/Misc/NEWS.d/next/Library/2020-07-13-15-06-35.bpo-41288.8mn5P-.rst b/Misc/NEWS.d/next/Library/2020-07-13-15-06-35.bpo-41288.8mn5P-.rst | |||
new file mode 100644 | |||
index 0000000000000..3c3adbabf16ff | |||
--- /dev/null | |||
+++ b/Misc/NEWS.d/next/Library/2020-07-13-15-06-35.bpo-41288.8mn5P-.rst | |||
@@ -0,0 +1,2 @@ | |||
+Unpickling invalid NEWOBJ_EX opcode with the C implementation raises now | |||
+UnpicklingError instead of crashing. | |||
diff --git a/Modules/_pickle.c b/Modules/_pickle.c | |||
index 55affb2c7c479..42ce62fc7cdf4 100644 | |||
--- a/Modules/_pickle.c | |||
+++ b/Modules/_pickle.c | |||
@@ -5988,23 +5988,30 @@ load_newobj_ex(UnpicklerObject *self) | |||
} | |||
if (!PyType_Check(cls)) { | |||
- Py_DECREF(kwargs); | |||
- Py_DECREF(args); | |||
PyErr_Format(st->UnpicklingError, | |||
"NEWOBJ_EX class argument must be a type, not %.200s", | |||
Py_TYPE(cls)->tp_name); | |||
- Py_DECREF(cls); | |||
- return -1; | |||
+ goto error; | |||
} | |||
if (((PyTypeObject *)cls)->tp_new == NULL) { | |||
- Py_DECREF(kwargs); | |||
- Py_DECREF(args); | |||
- Py_DECREF(cls); | |||
PyErr_SetString(st->UnpicklingError, | |||
"NEWOBJ_EX class argument doesn't have __new__"); | |||
- return -1; | |||
+ goto error; | |||
+ } | |||
+ if (!PyTuple_Check(args)) { | |||
+ PyErr_Format(st->UnpicklingError, | |||
+ "NEWOBJ_EX args argument must be a tuple, not %.200s", | |||
+ Py_TYPE(args)->tp_name); | |||
+ goto error; | |||
+ } | |||
+ if (!PyDict_Check(kwargs)) { | |||
+ PyErr_Format(st->UnpicklingError, | |||
+ "NEWOBJ_EX kwargs argument must be a dict, not %.200s", | |||
+ Py_TYPE(kwargs)->tp_name); | |||
+ goto error; | |||
} | |||
+ | |||
obj = ((PyTypeObject *)cls)->tp_new((PyTypeObject *)cls, args, kwargs); | |||
Py_DECREF(kwargs); | |||
Py_DECREF(args); | |||
@@ -6014,6 +6021,12 @@ load_newobj_ex(UnpicklerObject *self) | |||
} | |||
PDATA_PUSH(self->stack, obj, -1); | |||
return 0; | |||
+ | |||
+error: | |||
+ Py_DECREF(kwargs); | |||
+ Py_DECREF(args); | |||
+ Py_DECREF(cls); | |||
+ return -1; | |||
} | |||
static int |
@ -1,62 +0,0 @@ | |||
From c55479556db015f48fc8bbca17f64d3e65598559 Mon Sep 17 00:00:00 2001 | |||
From: "Miss Islington (bot)" | |||
<31488909+miss-islington@users.noreply.github.com> | |||
Date: Wed, 15 Jul 2020 05:30:53 -0700 | |||
Subject: [PATCH] [3.8] bpo-39017: Avoid infinite loop in the tarfile module | |||
(GH-21454) (GH-21483) | |||
Avoid infinite loop when reading specially crafted TAR files using the tarfile module | |||
(CVE-2019-20907). | |||
(cherry picked from commit 5a8d121a1f3ef5ad7c105ee378cc79a3eac0c7d4) | |||
Co-authored-by: Rishi <rishi_devan@mail.com> | |||
Automerge-Triggered-By: @encukou | |||
--- | |||
Lib/tarfile.py | 2 ++ | |||
Lib/test/recursion.tar | Bin 0 -> 516 bytes | |||
Lib/test/test_tarfile.py | 7 +++++++ | |||
.../2020-07-12-22-16-58.bpo-39017.x3Cg-9.rst | 1 + | |||
4 files changed, 10 insertions(+) | |||
create mode 100644 Lib/test/recursion.tar | |||
create mode 100644 Misc/NEWS.d/next/Library/2020-07-12-22-16-58.bpo-39017.x3Cg-9.rst | |||
diff --git a/Lib/tarfile.py b/Lib/tarfile.py | |||
index d31b9cbb51d65..7a69e1b1aa544 100755 | |||
--- a/Lib/tarfile.py | |||
+++ b/Lib/tarfile.py | |||
@@ -1241,6 +1241,8 @@ def _proc_pax(self, tarfile): | |||
length, keyword = match.groups() | |||
length = int(length) | |||
+ if length == 0: | |||
+ raise InvalidHeaderError("invalid header") | |||
value = buf[match.end(2) + 1:match.start(1) + length - 1] | |||
# Normally, we could just use "utf-8" as the encoding and "strict" | |||
diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py | |||
index 15324a4e48819..b512168d6ea87 100644 | |||
--- a/Lib/test/test_tarfile.py | |||
+++ b/Lib/test/test_tarfile.py | |||
@@ -397,6 +397,13 @@ def test_premature_end_of_archive(self): | |||
with self.assertRaisesRegex(tarfile.ReadError, "unexpected end of data"): | |||
tar.extractfile(t).read() | |||
+ def test_length_zero_header(self): | |||
+ # bpo-39017 (CVE-2019-20907): reading a zero-length header should fail | |||
+ # with an exception | |||
+ with self.assertRaisesRegex(tarfile.ReadError, "file could not be opened successfully"): | |||
+ with tarfile.open(support.findfile('recursion.tar')) as tar: | |||
+ pass | |||
+ | |||
class MiscReadTestBase(CommonReadTest): | |||
def requires_name_attribute(self): | |||
pass | |||
diff --git a/Misc/NEWS.d/next/Library/2020-07-12-22-16-58.bpo-39017.x3Cg-9.rst b/Misc/NEWS.d/next/Library/2020-07-12-22-16-58.bpo-39017.x3Cg-9.rst | |||
new file mode 100644 | |||
index 0000000000000..ad26676f8b856 | |||
--- /dev/null | |||
+++ b/Misc/NEWS.d/next/Library/2020-07-12-22-16-58.bpo-39017.x3Cg-9.rst | |||
@@ -0,0 +1 @@ | |||
+Avoid infinite loop when reading specially crafted TAR files using the tarfile module (CVE-2019-20907). |
@ -1,99 +0,0 @@ | |||
From 668d321476d974c4f51476b33aaca870272523bf Mon Sep 17 00:00:00 2001 | |||
From: "Miss Islington (bot)" | |||
<31488909+miss-islington@users.noreply.github.com> | |||
Date: Sat, 18 Jul 2020 13:39:12 -0700 | |||
Subject: [PATCH] bpo-39603: Prevent header injection in http methods | |||
(GH-18485) | |||
reject control chars in http method in http.client.putrequest to prevent http header injection | |||
(cherry picked from commit 8ca8a2e8fb068863c1138f07e3098478ef8be12e) | |||
Co-authored-by: AMIR <31338382+amiremohamadi@users.noreply.github.com> | |||
--- | |||
Lib/http/client.py | 15 +++++++++++++ | |||
Lib/test/test_httplib.py | 22 +++++++++++++++++++ | |||
.../2020-02-12-14-17-39.bpo-39603.Gt3RSg.rst | 2 ++ | |||
3 files changed, 39 insertions(+) | |||
create mode 100644 Misc/NEWS.d/next/Security/2020-02-12-14-17-39.bpo-39603.Gt3RSg.rst | |||
diff --git a/Lib/http/client.py b/Lib/http/client.py | |||
index 019380a720318..c2ad0471bfee5 100644 | |||
--- a/Lib/http/client.py | |||
+++ b/Lib/http/client.py | |||
@@ -147,6 +147,10 @@ | |||
# _is_allowed_url_pchars_re = re.compile(r"^[/!$&'()*+,;=:@%a-zA-Z0-9._~-]+$") | |||
# We are more lenient for assumed real world compatibility purposes. | |||
+# These characters are not allowed within HTTP method names | |||
+# to prevent http header injection. | |||
+_contains_disallowed_method_pchar_re = re.compile('[\x00-\x1f]') | |||
+ | |||
# We always set the Content-Length header for these methods because some | |||
# servers will otherwise respond with a 411 | |||
_METHODS_EXPECTING_BODY = {'PATCH', 'POST', 'PUT'} | |||
@@ -1087,6 +1091,8 @@ def putrequest(self, method, url, skip_host=False, | |||
else: | |||
raise CannotSendRequest(self.__state) | |||
+ self._validate_method(method) | |||
+ | |||
# Save the method for use later in the response phase | |||
self._method = method | |||
@@ -1177,6 +1183,15 @@ def _encode_request(self, request): | |||
# ASCII also helps prevent CVE-2019-9740. | |||
return request.encode('ascii') | |||
+ def _validate_method(self, method): | |||
+ """Validate a method name for putrequest.""" | |||
+ # prevent http header injection | |||
+ match = _contains_disallowed_method_pchar_re.search(method) | |||
+ if match: | |||
+ raise ValueError( | |||
+ f"method can't contain control characters. {method!r} " | |||
+ f"(found at least {match.group()!r})") | |||
+ | |||
def _validate_path(self, url): | |||
"""Validate a url for putrequest.""" | |||
# Prevent CVE-2019-9740. | |||
diff --git a/Lib/test/test_httplib.py b/Lib/test/test_httplib.py | |||
index 8f0e27a1fb836..5a5fcecbc9c15 100644 | |||
--- a/Lib/test/test_httplib.py | |||
+++ b/Lib/test/test_httplib.py | |||
@@ -364,6 +364,28 @@ def test_headers_debuglevel(self): | |||
self.assertEqual(lines[3], "header: Second: val2") | |||
+class HttpMethodTests(TestCase): | |||
+ def test_invalid_method_names(self): | |||
+ methods = ( | |||
+ 'GET\r', | |||
+ 'POST\n', | |||
+ 'PUT\n\r', | |||
+ 'POST\nValue', | |||
+ 'POST\nHOST:abc', | |||
+ 'GET\nrHost:abc\n', | |||
+ 'POST\rRemainder:\r', | |||
+ 'GET\rHOST:\n', | |||
+ '\nPUT' | |||
+ ) | |||
+ | |||
+ for method in methods: | |||
+ with self.assertRaisesRegex( | |||
+ ValueError, "method can't contain control characters"): | |||
+ conn = client.HTTPConnection('example.com') | |||
+ conn.sock = FakeSocket(None) | |||
+ conn.request(method=method, url="/") | |||
+ | |||
+ | |||
class TransferEncodingTest(TestCase): | |||
expected_body = b"It's just a flesh wound" | |||
diff --git a/Misc/NEWS.d/next/Security/2020-02-12-14-17-39.bpo-39603.Gt3RSg.rst b/Misc/NEWS.d/next/Security/2020-02-12-14-17-39.bpo-39603.Gt3RSg.rst | |||
new file mode 100644 | |||
index 0000000000000..990affc3edd9d | |||
--- /dev/null | |||
+++ b/Misc/NEWS.d/next/Security/2020-02-12-14-17-39.bpo-39603.Gt3RSg.rst | |||
@@ -0,0 +1,2 @@ | |||
+Prevent http header injection by rejecting control characters in | |||
+http.client.putrequest(...). |