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(...). |