diff --git a/SOURCES/00408-CVE-2022-48560.patch b/SOURCES/00408-CVE-2022-48560.patch new file mode 100644 index 0000000..9b6cfb1 --- /dev/null +++ b/SOURCES/00408-CVE-2022-48560.patch @@ -0,0 +1,143 @@ +From c563f409ea30bcb0623d785428c9257917371b76 Mon Sep 17 00:00:00 2001 +From: "Miss Islington (bot)" + <31488909+miss-islington@users.noreply.github.com> +Date: Thu, 23 Jan 2020 06:49:19 -0800 +Subject: [PATCH] bpo-39421: Fix posible crash in heapq with custom comparison + operators (GH-18118) (GH-18146) + +(cherry picked from commit 79f89e6e5a659846d1068e8b1bd8e491ccdef861) + +Co-authored-by: Pablo Galindo +--- + Lib/test/test_heapq.py | 31 ++++++++++++++++ + .../2020-01-22-15-53-37.bpo-39421.O3nG7u.rst | 2 ++ + Modules/_heapqmodule.c | 35 ++++++++++++++----- + 3 files changed, 59 insertions(+), 9 deletions(-) + create mode 100644 Misc/NEWS.d/next/Core and Builtins/2020-01-22-15-53-37.bpo-39421.O3nG7u.rst + +diff --git a/Lib/test/test_heapq.py b/Lib/test/test_heapq.py +index 2f8c648d84a58..7c3fb0210f69b 100644 +--- a/Lib/test/test_heapq.py ++++ b/Lib/test/test_heapq.py +@@ -414,6 +414,37 @@ def test_heappop_mutating_heap(self): + with self.assertRaises((IndexError, RuntimeError)): + self.module.heappop(heap) + ++ def test_comparison_operator_modifiying_heap(self): ++ # See bpo-39421: Strong references need to be taken ++ # when comparing objects as they can alter the heap ++ class EvilClass(int): ++ def __lt__(self, o): ++ heap.clear() ++ return NotImplemented ++ ++ heap = [] ++ self.module.heappush(heap, EvilClass(0)) ++ self.assertRaises(IndexError, self.module.heappushpop, heap, 1) ++ ++ def test_comparison_operator_modifiying_heap_two_heaps(self): ++ ++ class h(int): ++ def __lt__(self, o): ++ list2.clear() ++ return NotImplemented ++ ++ class g(int): ++ def __lt__(self, o): ++ list1.clear() ++ return NotImplemented ++ ++ list1, list2 = [], [] ++ ++ self.module.heappush(list1, h(0)) ++ self.module.heappush(list2, g(0)) ++ ++ self.assertRaises((IndexError, RuntimeError), self.module.heappush, list1, g(1)) ++ self.assertRaises((IndexError, RuntimeError), self.module.heappush, list2, h(1)) + + class TestErrorHandlingPython(TestErrorHandling, TestCase): + module = py_heapq +diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-01-22-15-53-37.bpo-39421.O3nG7u.rst b/Misc/NEWS.d/next/Core and Builtins/2020-01-22-15-53-37.bpo-39421.O3nG7u.rst +new file mode 100644 +index 0000000000000..bae008150ee12 +--- /dev/null ++++ b/Misc/NEWS.d/next/Core and Builtins/2020-01-22-15-53-37.bpo-39421.O3nG7u.rst +@@ -0,0 +1,2 @@ ++Fix possible crashes when operating with the functions in the :mod:`heapq` ++module and custom comparison operators. +diff --git a/Modules/_heapqmodule.c b/Modules/_heapqmodule.c +index b499e1f668aae..0fb35ffe5ec48 100644 +--- a/Modules/_heapqmodule.c ++++ b/Modules/_heapqmodule.c +@@ -29,7 +29,11 @@ siftdown(PyListObject *heap, Py_ssize_t startpos, Py_ssize_t pos) + while (pos > startpos) { + parentpos = (pos - 1) >> 1; + parent = arr[parentpos]; ++ Py_INCREF(newitem); ++ Py_INCREF(parent); + cmp = PyObject_RichCompareBool(newitem, parent, Py_LT); ++ Py_DECREF(parent); ++ Py_DECREF(newitem); + if (cmp < 0) + return -1; + if (size != PyList_GET_SIZE(heap)) { +@@ -71,10 +75,13 @@ siftup(PyListObject *heap, Py_ssize_t pos) + /* Set childpos to index of smaller child. */ + childpos = 2*pos + 1; /* leftmost child position */ + if (childpos + 1 < endpos) { +- cmp = PyObject_RichCompareBool( +- arr[childpos], +- arr[childpos + 1], +- Py_LT); ++ PyObject* a = arr[childpos]; ++ PyObject* b = arr[childpos + 1]; ++ Py_INCREF(a); ++ Py_INCREF(b); ++ cmp = PyObject_RichCompareBool(a, b, Py_LT); ++ Py_DECREF(a); ++ Py_DECREF(b); + if (cmp < 0) + return -1; + childpos += ((unsigned)cmp ^ 1); /* increment when cmp==0 */ +@@ -229,7 +236,10 @@ heappushpop(PyObject *self, PyObject *args) + return item; + } + +- cmp = PyObject_RichCompareBool(PyList_GET_ITEM(heap, 0), item, Py_LT); ++ PyObject* top = PyList_GET_ITEM(heap, 0); ++ Py_INCREF(top); ++ cmp = PyObject_RichCompareBool(top, item, Py_LT); ++ Py_DECREF(top); + if (cmp < 0) + return NULL; + if (cmp == 0) { +@@ -383,7 +393,11 @@ siftdown_max(PyListObject *heap, Py_ssize_t startpos, Py_ssize_t pos) + while (pos > startpos) { + parentpos = (pos - 1) >> 1; + parent = arr[parentpos]; ++ Py_INCREF(parent); ++ Py_INCREF(newitem); + cmp = PyObject_RichCompareBool(parent, newitem, Py_LT); ++ Py_DECREF(parent); ++ Py_DECREF(newitem); + if (cmp < 0) + return -1; + if (size != PyList_GET_SIZE(heap)) { +@@ -425,10 +439,13 @@ siftup_max(PyListObject *heap, Py_ssize_t pos) + /* Set childpos to index of smaller child. */ + childpos = 2*pos + 1; /* leftmost child position */ + if (childpos + 1 < endpos) { +- cmp = PyObject_RichCompareBool( +- arr[childpos + 1], +- arr[childpos], +- Py_LT); ++ PyObject* a = arr[childpos + 1]; ++ PyObject* b = arr[childpos]; ++ Py_INCREF(a); ++ Py_INCREF(b); ++ cmp = PyObject_RichCompareBool(a, b, Py_LT); ++ Py_DECREF(a); ++ Py_DECREF(b); + if (cmp < 0) + return -1; + childpos += ((unsigned)cmp ^ 1); /* increment when cmp==0 */ diff --git a/SOURCES/00413-CVE-2022-48564.patch b/SOURCES/00413-CVE-2022-48564.patch new file mode 100644 index 0000000..3fc1cf8 --- /dev/null +++ b/SOURCES/00413-CVE-2022-48564.patch @@ -0,0 +1,560 @@ +From cec66eefbee76ee95f252a52de0f5abc1b677f5b Mon Sep 17 00:00:00 2001 +From: Lumir Balhar +Date: Tue, 12 Dec 2023 13:03:42 +0100 +Subject: [PATCH] [3.6] bpo-42103: Improve validation of Plist files. + (GH-22882) (GH-23118) + +* Prevent some possible DoS attacks via providing invalid Plist files + with extremely large number of objects or collection sizes. +* Raise InvalidFileException for too large bytes and string size instead of returning garbage. +* Raise InvalidFileException instead of ValueError for specific invalid datetime (NaN). +* Raise InvalidFileException instead of TypeError for non-hashable dict keys. +* Add more tests for invalid Plist files.. +(cherry picked from commit 34637a0ce21e7261b952fbd9d006474cc29b681f) + +Co-authored-by: Serhiy Storchaka +--- + Lib/plistlib.py | 34 +- + Lib/test/test_plistlib.py | 394 +++++++++++++++--- + .../2020-10-23-19-20-14.bpo-42103.C5obK2.rst | 3 + + .../2020-10-23-19-19-30.bpo-42103.cILT66.rst | 2 + + 4 files changed, 366 insertions(+), 67 deletions(-) + create mode 100644 Misc/NEWS.d/next/Library/2020-10-23-19-20-14.bpo-42103.C5obK2.rst + create mode 100644 Misc/NEWS.d/next/Security/2020-10-23-19-19-30.bpo-42103.cILT66.rst + +diff --git a/Lib/plistlib.py b/Lib/plistlib.py +index a918643..df1f346 100644 +--- a/Lib/plistlib.py ++++ b/Lib/plistlib.py +@@ -626,7 +626,7 @@ class _BinaryPlistParser: + return self._read_object(top_object) + + except (OSError, IndexError, struct.error, OverflowError, +- UnicodeDecodeError): ++ ValueError): + raise InvalidFileException() + + def _get_size(self, tokenL): +@@ -642,7 +642,7 @@ class _BinaryPlistParser: + def _read_ints(self, n, size): + data = self._fp.read(size * n) + if size in _BINARY_FORMAT: +- return struct.unpack('>' + _BINARY_FORMAT[size] * n, data) ++ return struct.unpack(f'>{n}{_BINARY_FORMAT[size]}', data) + else: + if not size or len(data) != size * n: + raise InvalidFileException() +@@ -701,19 +701,25 @@ class _BinaryPlistParser: + + elif tokenH == 0x40: # data + s = self._get_size(tokenL) +- if self._use_builtin_types: +- result = self._fp.read(s) +- else: +- result = Data(self._fp.read(s)) ++ result = self._fp.read(s) ++ if len(result) != s: ++ raise InvalidFileException() ++ if not self._use_builtin_types: ++ result = Data(result) + + elif tokenH == 0x50: # ascii string + s = self._get_size(tokenL) +- result = self._fp.read(s).decode('ascii') +- result = result ++ data = self._fp.read(s) ++ if len(data) != s: ++ raise InvalidFileException() ++ result = data.decode('ascii') + + elif tokenH == 0x60: # unicode string +- s = self._get_size(tokenL) +- result = self._fp.read(s * 2).decode('utf-16be') ++ s = self._get_size(tokenL) * 2 ++ data = self._fp.read(s) ++ if len(data) != s: ++ raise InvalidFileException() ++ result = data.decode('utf-16be') + + # tokenH == 0x80 is documented as 'UID' and appears to be used for + # keyed-archiving, not in plists. +@@ -737,9 +743,11 @@ class _BinaryPlistParser: + obj_refs = self._read_refs(s) + result = self._dict_type() + self._objects[ref] = result +- for k, o in zip(key_refs, obj_refs): +- result[self._read_object(k)] = self._read_object(o) +- ++ try: ++ for k, o in zip(key_refs, obj_refs): ++ result[self._read_object(k)] = self._read_object(o) ++ except TypeError: ++ raise InvalidFileException() + else: + raise InvalidFileException() + +diff --git a/Lib/test/test_plistlib.py b/Lib/test/test_plistlib.py +index d47c607..f71245d 100644 +--- a/Lib/test/test_plistlib.py ++++ b/Lib/test/test_plistlib.py +@@ -1,5 +1,6 @@ + # Copyright (C) 2003-2013 Python Software Foundation + ++import struct + import unittest + import plistlib + import os +@@ -90,6 +91,284 @@ TESTDATA={ + xQHHAsQC0gAAAAAAAAIBAAAAAAAAADkAAAAAAAAAAAAAAAAAAALs'''), + } + ++INVALID_BINARY_PLISTS = [ ++ ('too short data', ++ b'' ++ ), ++ ('too large offset_table_offset and offset_size = 1', ++ b'\x00\x08' ++ b'\x00\x00\x00\x00\x00\x00\x01\x01' ++ b'\x00\x00\x00\x00\x00\x00\x00\x01' ++ b'\x00\x00\x00\x00\x00\x00\x00\x00' ++ b'\x00\x00\x00\x00\x00\x00\x00\x2a' ++ ), ++ ('too large offset_table_offset and nonstandard offset_size', ++ b'\x00\x00\x00\x08' ++ b'\x00\x00\x00\x00\x00\x00\x03\x01' ++ b'\x00\x00\x00\x00\x00\x00\x00\x01' ++ b'\x00\x00\x00\x00\x00\x00\x00\x00' ++ b'\x00\x00\x00\x00\x00\x00\x00\x2c' ++ ), ++ ('integer overflow in offset_table_offset', ++ b'\x00\x08' ++ b'\x00\x00\x00\x00\x00\x00\x01\x01' ++ b'\x00\x00\x00\x00\x00\x00\x00\x01' ++ b'\x00\x00\x00\x00\x00\x00\x00\x00' ++ b'\xff\xff\xff\xff\xff\xff\xff\xff' ++ ), ++ ('too large top_object', ++ b'\x00\x08' ++ b'\x00\x00\x00\x00\x00\x00\x01\x01' ++ b'\x00\x00\x00\x00\x00\x00\x00\x01' ++ b'\x00\x00\x00\x00\x00\x00\x00\x01' ++ b'\x00\x00\x00\x00\x00\x00\x00\x09' ++ ), ++ ('integer overflow in top_object', ++ b'\x00\x08' ++ b'\x00\x00\x00\x00\x00\x00\x01\x01' ++ b'\x00\x00\x00\x00\x00\x00\x00\x01' ++ b'\xff\xff\xff\xff\xff\xff\xff\xff' ++ b'\x00\x00\x00\x00\x00\x00\x00\x09' ++ ), ++ ('too large num_objects and offset_size = 1', ++ b'\x00\x08' ++ b'\x00\x00\x00\x00\x00\x00\x01\x01' ++ b'\x00\x00\x00\x00\x00\x00\x00\xff' ++ b'\x00\x00\x00\x00\x00\x00\x00\x00' ++ b'\x00\x00\x00\x00\x00\x00\x00\x09' ++ ), ++ ('too large num_objects and nonstandard offset_size', ++ b'\x00\x00\x00\x08' ++ b'\x00\x00\x00\x00\x00\x00\x03\x01' ++ b'\x00\x00\x00\x00\x00\x00\x00\xff' ++ b'\x00\x00\x00\x00\x00\x00\x00\x00' ++ b'\x00\x00\x00\x00\x00\x00\x00\x09' ++ ), ++ ('extremally large num_objects (32 bit)', ++ b'\x00\x08' ++ b'\x00\x00\x00\x00\x00\x00\x01\x01' ++ b'\x00\x00\x00\x00\x7f\xff\xff\xff' ++ b'\x00\x00\x00\x00\x00\x00\x00\x00' ++ b'\x00\x00\x00\x00\x00\x00\x00\x09' ++ ), ++ ('extremally large num_objects (64 bit)', ++ b'\x00\x08' ++ b'\x00\x00\x00\x00\x00\x00\x01\x01' ++ b'\x00\x00\x00\xff\xff\xff\xff\xff' ++ b'\x00\x00\x00\x00\x00\x00\x00\x00' ++ b'\x00\x00\x00\x00\x00\x00\x00\x09' ++ ), ++ ('integer overflow in num_objects', ++ b'\x00\x08' ++ b'\x00\x00\x00\x00\x00\x00\x01\x01' ++ b'\xff\xff\xff\xff\xff\xff\xff\xff' ++ b'\x00\x00\x00\x00\x00\x00\x00\x00' ++ b'\x00\x00\x00\x00\x00\x00\x00\x09' ++ ), ++ ('offset_size = 0', ++ b'\x00\x08' ++ b'\x00\x00\x00\x00\x00\x00\x00\x01' ++ b'\x00\x00\x00\x00\x00\x00\x00\x01' ++ b'\x00\x00\x00\x00\x00\x00\x00\x00' ++ b'\x00\x00\x00\x00\x00\x00\x00\x09' ++ ), ++ ('ref_size = 0', ++ b'\xa1\x01\x00\x08\x0a' ++ b'\x00\x00\x00\x00\x00\x00\x01\x00' ++ b'\x00\x00\x00\x00\x00\x00\x00\x02' ++ b'\x00\x00\x00\x00\x00\x00\x00\x00' ++ b'\x00\x00\x00\x00\x00\x00\x00\x0b' ++ ), ++ ('too large offset', ++ b'\x00\x2a' ++ b'\x00\x00\x00\x00\x00\x00\x01\x01' ++ b'\x00\x00\x00\x00\x00\x00\x00\x01' ++ b'\x00\x00\x00\x00\x00\x00\x00\x00' ++ b'\x00\x00\x00\x00\x00\x00\x00\x09' ++ ), ++ ('integer overflow in offset', ++ b'\x00\xff\xff\xff\xff\xff\xff\xff\xff' ++ b'\x00\x00\x00\x00\x00\x00\x08\x01' ++ b'\x00\x00\x00\x00\x00\x00\x00\x01' ++ b'\x00\x00\x00\x00\x00\x00\x00\x00' ++ b'\x00\x00\x00\x00\x00\x00\x00\x09' ++ ), ++ ('too large array size', ++ b'\xaf\x00\x01\xff\x00\x08\x0c' ++ b'\x00\x00\x00\x00\x00\x00\x01\x01' ++ b'\x00\x00\x00\x00\x00\x00\x00\x02' ++ b'\x00\x00\x00\x00\x00\x00\x00\x00' ++ b'\x00\x00\x00\x00\x00\x00\x00\x0d' ++ ), ++ ('extremally large array size (32-bit)', ++ b'\xaf\x02\x7f\xff\xff\xff\x01\x00\x08\x0f' ++ b'\x00\x00\x00\x00\x00\x00\x01\x01' ++ b'\x00\x00\x00\x00\x00\x00\x00\x02' ++ b'\x00\x00\x00\x00\x00\x00\x00\x00' ++ b'\x00\x00\x00\x00\x00\x00\x00\x10' ++ ), ++ ('extremally large array size (64-bit)', ++ b'\xaf\x03\x00\x00\x00\xff\xff\xff\xff\xff\x01\x00\x08\x13' ++ b'\x00\x00\x00\x00\x00\x00\x01\x01' ++ b'\x00\x00\x00\x00\x00\x00\x00\x02' ++ b'\x00\x00\x00\x00\x00\x00\x00\x00' ++ b'\x00\x00\x00\x00\x00\x00\x00\x14' ++ ), ++ ('integer overflow in array size', ++ b'\xaf\x03\xff\xff\xff\xff\xff\xff\xff\xff\x01\x00\x08\x13' ++ b'\x00\x00\x00\x00\x00\x00\x01\x01' ++ b'\x00\x00\x00\x00\x00\x00\x00\x02' ++ b'\x00\x00\x00\x00\x00\x00\x00\x00' ++ b'\x00\x00\x00\x00\x00\x00\x00\x14' ++ ), ++ ('too large reference index', ++ b'\xa1\x02\x00\x08\x0a' ++ b'\x00\x00\x00\x00\x00\x00\x01\x01' ++ b'\x00\x00\x00\x00\x00\x00\x00\x02' ++ b'\x00\x00\x00\x00\x00\x00\x00\x00' ++ b'\x00\x00\x00\x00\x00\x00\x00\x0b' ++ ), ++ ('integer overflow in reference index', ++ b'\xa1\xff\xff\xff\xff\xff\xff\xff\xff\x00\x08\x11' ++ b'\x00\x00\x00\x00\x00\x00\x01\x08' ++ b'\x00\x00\x00\x00\x00\x00\x00\x02' ++ b'\x00\x00\x00\x00\x00\x00\x00\x00' ++ b'\x00\x00\x00\x00\x00\x00\x00\x12' ++ ), ++ ('too large bytes size', ++ b'\x4f\x00\x23\x41\x08' ++ b'\x00\x00\x00\x00\x00\x00\x01\x01' ++ b'\x00\x00\x00\x00\x00\x00\x00\x01' ++ b'\x00\x00\x00\x00\x00\x00\x00\x00' ++ b'\x00\x00\x00\x00\x00\x00\x00\x0c' ++ ), ++ ('extremally large bytes size (32-bit)', ++ b'\x4f\x02\x7f\xff\xff\xff\x41\x08' ++ b'\x00\x00\x00\x00\x00\x00\x01\x01' ++ b'\x00\x00\x00\x00\x00\x00\x00\x01' ++ b'\x00\x00\x00\x00\x00\x00\x00\x00' ++ b'\x00\x00\x00\x00\x00\x00\x00\x0f' ++ ), ++ ('extremally large bytes size (64-bit)', ++ b'\x4f\x03\x00\x00\x00\xff\xff\xff\xff\xff\x41\x08' ++ b'\x00\x00\x00\x00\x00\x00\x01\x01' ++ b'\x00\x00\x00\x00\x00\x00\x00\x01' ++ b'\x00\x00\x00\x00\x00\x00\x00\x00' ++ b'\x00\x00\x00\x00\x00\x00\x00\x13' ++ ), ++ ('integer overflow in bytes size', ++ b'\x4f\x03\xff\xff\xff\xff\xff\xff\xff\xff\x41\x08' ++ b'\x00\x00\x00\x00\x00\x00\x01\x01' ++ b'\x00\x00\x00\x00\x00\x00\x00\x01' ++ b'\x00\x00\x00\x00\x00\x00\x00\x00' ++ b'\x00\x00\x00\x00\x00\x00\x00\x13' ++ ), ++ ('too large ASCII size', ++ b'\x5f\x00\x23\x41\x08' ++ b'\x00\x00\x00\x00\x00\x00\x01\x01' ++ b'\x00\x00\x00\x00\x00\x00\x00\x01' ++ b'\x00\x00\x00\x00\x00\x00\x00\x00' ++ b'\x00\x00\x00\x00\x00\x00\x00\x0c' ++ ), ++ ('extremally large ASCII size (32-bit)', ++ b'\x5f\x02\x7f\xff\xff\xff\x41\x08' ++ b'\x00\x00\x00\x00\x00\x00\x01\x01' ++ b'\x00\x00\x00\x00\x00\x00\x00\x01' ++ b'\x00\x00\x00\x00\x00\x00\x00\x00' ++ b'\x00\x00\x00\x00\x00\x00\x00\x0f' ++ ), ++ ('extremally large ASCII size (64-bit)', ++ b'\x5f\x03\x00\x00\x00\xff\xff\xff\xff\xff\x41\x08' ++ b'\x00\x00\x00\x00\x00\x00\x01\x01' ++ b'\x00\x00\x00\x00\x00\x00\x00\x01' ++ b'\x00\x00\x00\x00\x00\x00\x00\x00' ++ b'\x00\x00\x00\x00\x00\x00\x00\x13' ++ ), ++ ('integer overflow in ASCII size', ++ b'\x5f\x03\xff\xff\xff\xff\xff\xff\xff\xff\x41\x08' ++ b'\x00\x00\x00\x00\x00\x00\x01\x01' ++ b'\x00\x00\x00\x00\x00\x00\x00\x01' ++ b'\x00\x00\x00\x00\x00\x00\x00\x00' ++ b'\x00\x00\x00\x00\x00\x00\x00\x13' ++ ), ++ ('invalid ASCII', ++ b'\x51\xff\x08' ++ b'\x00\x00\x00\x00\x00\x00\x01\x01' ++ b'\x00\x00\x00\x00\x00\x00\x00\x01' ++ b'\x00\x00\x00\x00\x00\x00\x00\x00' ++ b'\x00\x00\x00\x00\x00\x00\x00\x0a' ++ ), ++ ('too large UTF-16 size', ++ b'\x6f\x00\x13\x20\xac\x00\x08' ++ b'\x00\x00\x00\x00\x00\x00\x01\x01' ++ b'\x00\x00\x00\x00\x00\x00\x00\x01' ++ b'\x00\x00\x00\x00\x00\x00\x00\x00' ++ b'\x00\x00\x00\x00\x00\x00\x00\x0e' ++ ), ++ ('extremally large UTF-16 size (32-bit)', ++ b'\x6f\x02\x4f\xff\xff\xff\x20\xac\x00\x08' ++ b'\x00\x00\x00\x00\x00\x00\x01\x01' ++ b'\x00\x00\x00\x00\x00\x00\x00\x01' ++ b'\x00\x00\x00\x00\x00\x00\x00\x00' ++ b'\x00\x00\x00\x00\x00\x00\x00\x11' ++ ), ++ ('extremally large UTF-16 size (64-bit)', ++ b'\x6f\x03\x00\x00\x00\xff\xff\xff\xff\xff\x20\xac\x00\x08' ++ b'\x00\x00\x00\x00\x00\x00\x01\x01' ++ b'\x00\x00\x00\x00\x00\x00\x00\x01' ++ b'\x00\x00\x00\x00\x00\x00\x00\x00' ++ b'\x00\x00\x00\x00\x00\x00\x00\x15' ++ ), ++ ('integer overflow in UTF-16 size', ++ b'\x6f\x03\xff\xff\xff\xff\xff\xff\xff\xff\x20\xac\x00\x08' ++ b'\x00\x00\x00\x00\x00\x00\x01\x01' ++ b'\x00\x00\x00\x00\x00\x00\x00\x01' ++ b'\x00\x00\x00\x00\x00\x00\x00\x00' ++ b'\x00\x00\x00\x00\x00\x00\x00\x15' ++ ), ++ ('invalid UTF-16', ++ b'\x61\xd8\x00\x08' ++ b'\x00\x00\x00\x00\x00\x00\x01\x01' ++ b'\x00\x00\x00\x00\x00\x00\x00\x01' ++ b'\x00\x00\x00\x00\x00\x00\x00\x00' ++ b'\x00\x00\x00\x00\x00\x00\x00\x0b' ++ ), ++ ('non-hashable key', ++ b'\xd1\x01\x01\xa0\x08\x0b' ++ b'\x00\x00\x00\x00\x00\x00\x01\x01' ++ b'\x00\x00\x00\x00\x00\x00\x00\x02' ++ b'\x00\x00\x00\x00\x00\x00\x00\x00' ++ b'\x00\x00\x00\x00\x00\x00\x00\x0c' ++ ), ++ ('too large datetime (datetime overflow)', ++ b'\x33\x42\x50\x00\x00\x00\x00\x00\x00\x08' ++ b'\x00\x00\x00\x00\x00\x00\x01\x01' ++ b'\x00\x00\x00\x00\x00\x00\x00\x01' ++ b'\x00\x00\x00\x00\x00\x00\x00\x00' ++ b'\x00\x00\x00\x00\x00\x00\x00\x11' ++ ), ++ ('too large datetime (timedelta overflow)', ++ b'\x33\x42\xe0\x00\x00\x00\x00\x00\x00\x08' ++ b'\x00\x00\x00\x00\x00\x00\x01\x01' ++ b'\x00\x00\x00\x00\x00\x00\x00\x01' ++ b'\x00\x00\x00\x00\x00\x00\x00\x00' ++ b'\x00\x00\x00\x00\x00\x00\x00\x11' ++ ), ++ ('invalid datetime (Infinity)', ++ b'\x33\x7f\xf0\x00\x00\x00\x00\x00\x00\x08' ++ b'\x00\x00\x00\x00\x00\x00\x01\x01' ++ b'\x00\x00\x00\x00\x00\x00\x00\x01' ++ b'\x00\x00\x00\x00\x00\x00\x00\x00' ++ b'\x00\x00\x00\x00\x00\x00\x00\x11' ++ ), ++ ('invalid datetime (NaN)', ++ b'\x33\x7f\xf8\x00\x00\x00\x00\x00\x00\x08' ++ b'\x00\x00\x00\x00\x00\x00\x01\x01' ++ b'\x00\x00\x00\x00\x00\x00\x00\x01' ++ b'\x00\x00\x00\x00\x00\x00\x00\x00' ++ b'\x00\x00\x00\x00\x00\x00\x00\x11' ++ ), ++] + + class TestPlistlib(unittest.TestCase): + +@@ -447,6 +726,21 @@ class TestPlistlib(unittest.TestCase): + + class TestBinaryPlistlib(unittest.TestCase): + ++ @staticmethod ++ def decode(*objects, offset_size=1, ref_size=1): ++ data = [b'bplist00'] ++ offset = 8 ++ offsets = [] ++ for x in objects: ++ offsets.append(offset.to_bytes(offset_size, 'big')) ++ data.append(x) ++ offset += len(x) ++ tail = struct.pack('>6xBBQQQ', offset_size, ref_size, ++ len(objects), 0, offset) ++ data.extend(offsets) ++ data.append(tail) ++ return plistlib.loads(b''.join(data), fmt=plistlib.FMT_BINARY) ++ + def test_nonstandard_refs_size(self): + # Issue #21538: Refs and offsets are 24-bit integers + data = (b'bplist00' +@@ -461,7 +755,7 @@ class TestBinaryPlistlib(unittest.TestCase): + + def test_dump_duplicates(self): + # Test effectiveness of saving duplicated objects +- for x in (None, False, True, 12345, 123.45, 'abcde', b'abcde', ++ for x in (None, False, True, 12345, 123.45, 'abcde', 'абвгд', b'abcde', + datetime.datetime(2004, 10, 26, 10, 33, 33), + plistlib.Data(b'abcde'), bytearray(b'abcde'), + [12, 345], (12, 345), {'12': 345}): +@@ -500,6 +794,20 @@ class TestBinaryPlistlib(unittest.TestCase): + b = plistlib.loads(plistlib.dumps(a, fmt=plistlib.FMT_BINARY)) + self.assertIs(b['x'], b) + ++ def test_deep_nesting(self): ++ for N in [300, 100000]: ++ chunks = [b'\xa1' + (i + 1).to_bytes(4, 'big') for i in range(N)] ++ try: ++ result = self.decode(*chunks, b'\x54seed', offset_size=4, ref_size=4) ++ except RecursionError: ++ pass ++ else: ++ for i in range(N): ++ self.assertIsInstance(result, list) ++ self.assertEqual(len(result), 1) ++ result = result[0] ++ self.assertEqual(result, 'seed') ++ + def test_large_timestamp(self): + # Issue #26709: 32-bit timestamp out of range + for ts in -2**31-1, 2**31: +@@ -509,55 +817,37 @@ class TestBinaryPlistlib(unittest.TestCase): + data = plistlib.dumps(d, fmt=plistlib.FMT_BINARY) + self.assertEqual(plistlib.loads(data), d) + ++ def test_load_singletons(self): ++ self.assertIs(self.decode(b'\x00'), None) ++ self.assertIs(self.decode(b'\x08'), False) ++ self.assertIs(self.decode(b'\x09'), True) ++ self.assertEqual(self.decode(b'\x0f'), b'') ++ ++ def test_load_int(self): ++ self.assertEqual(self.decode(b'\x10\x00'), 0) ++ self.assertEqual(self.decode(b'\x10\xfe'), 0xfe) ++ self.assertEqual(self.decode(b'\x11\xfe\xdc'), 0xfedc) ++ self.assertEqual(self.decode(b'\x12\xfe\xdc\xba\x98'), 0xfedcba98) ++ self.assertEqual(self.decode(b'\x13\x01\x23\x45\x67\x89\xab\xcd\xef'), ++ 0x0123456789abcdef) ++ self.assertEqual(self.decode(b'\x13\xfe\xdc\xba\x98\x76\x54\x32\x10'), ++ -0x123456789abcdf0) ++ ++ def test_unsupported(self): ++ unsupported = [*range(1, 8), *range(10, 15), ++ 0x20, 0x21, *range(0x24, 0x33), *range(0x34, 0x40)] ++ for i in [0x70, 0x90, 0xb0, 0xc0, 0xe0, 0xf0]: ++ unsupported.extend(i + j for j in range(16)) ++ for token in unsupported: ++ with self.subTest(f'token {token:02x}'): ++ with self.assertRaises(plistlib.InvalidFileException): ++ self.decode(bytes([token]) + b'\x00'*16) ++ + def test_invalid_binary(self): +- for data in [ +- # too short data +- b'', +- # too large offset_table_offset and nonstandard offset_size +- b'\x00\x08' +- b'\x00\x00\x00\x00\x00\x00\x03\x01' +- b'\x00\x00\x00\x00\x00\x00\x00\x01' +- b'\x00\x00\x00\x00\x00\x00\x00\x00' +- b'\x00\x00\x00\x00\x00\x00\x00\x2a', +- # integer overflow in offset_table_offset +- b'\x00\x08' +- b'\x00\x00\x00\x00\x00\x00\x01\x01' +- b'\x00\x00\x00\x00\x00\x00\x00\x01' +- b'\x00\x00\x00\x00\x00\x00\x00\x00' +- b'\xff\xff\xff\xff\xff\xff\xff\xff', +- # offset_size = 0 +- b'\x00\x08' +- b'\x00\x00\x00\x00\x00\x00\x00\x01' +- b'\x00\x00\x00\x00\x00\x00\x00\x01' +- b'\x00\x00\x00\x00\x00\x00\x00\x00' +- b'\x00\x00\x00\x00\x00\x00\x00\x09', +- # ref_size = 0 +- b'\xa1\x01\x00\x08\x0a' +- b'\x00\x00\x00\x00\x00\x00\x01\x00' +- b'\x00\x00\x00\x00\x00\x00\x00\x02' +- b'\x00\x00\x00\x00\x00\x00\x00\x00' +- b'\x00\x00\x00\x00\x00\x00\x00\x0b', +- # integer overflow in offset +- b'\x00\xff\xff\xff\xff\xff\xff\xff\xff' +- b'\x00\x00\x00\x00\x00\x00\x08\x01' +- b'\x00\x00\x00\x00\x00\x00\x00\x01' +- b'\x00\x00\x00\x00\x00\x00\x00\x00' +- b'\x00\x00\x00\x00\x00\x00\x00\x09', +- # invalid ASCII +- b'\x51\xff\x08' +- b'\x00\x00\x00\x00\x00\x00\x01\x01' +- b'\x00\x00\x00\x00\x00\x00\x00\x01' +- b'\x00\x00\x00\x00\x00\x00\x00\x00' +- b'\x00\x00\x00\x00\x00\x00\x00\x0a', +- # invalid UTF-16 +- b'\x61\xd8\x00\x08' +- b'\x00\x00\x00\x00\x00\x00\x01\x01' +- b'\x00\x00\x00\x00\x00\x00\x00\x01' +- b'\x00\x00\x00\x00\x00\x00\x00\x00' +- b'\x00\x00\x00\x00\x00\x00\x00\x0b', +- ]: +- with self.assertRaises(plistlib.InvalidFileException): +- plistlib.loads(b'bplist00' + data, fmt=plistlib.FMT_BINARY) ++ for name, data in INVALID_BINARY_PLISTS: ++ with self.subTest(name): ++ with self.assertRaises(plistlib.InvalidFileException): ++ plistlib.loads(b'bplist00' + data, fmt=plistlib.FMT_BINARY) + + + class TestPlistlibDeprecated(unittest.TestCase): +@@ -655,9 +945,5 @@ class MiscTestCase(unittest.TestCase): + support.check__all__(self, plistlib, blacklist=blacklist) + + +-def test_main(): +- support.run_unittest(TestPlistlib, TestPlistlibDeprecated, MiscTestCase) +- +- + if __name__ == '__main__': +- test_main() ++ unittest.main() +diff --git a/Misc/NEWS.d/next/Library/2020-10-23-19-20-14.bpo-42103.C5obK2.rst b/Misc/NEWS.d/next/Library/2020-10-23-19-20-14.bpo-42103.C5obK2.rst +new file mode 100644 +index 0000000..4eb694c +--- /dev/null ++++ b/Misc/NEWS.d/next/Library/2020-10-23-19-20-14.bpo-42103.C5obK2.rst +@@ -0,0 +1,3 @@ ++:exc:`~plistlib.InvalidFileException` and :exc:`RecursionError` are now ++the only errors caused by loading malformed binary Plist file (previously ++ValueError and TypeError could be raised in some specific cases). +diff --git a/Misc/NEWS.d/next/Security/2020-10-23-19-19-30.bpo-42103.cILT66.rst b/Misc/NEWS.d/next/Security/2020-10-23-19-19-30.bpo-42103.cILT66.rst +new file mode 100644 +index 0000000..15d7b65 +--- /dev/null ++++ b/Misc/NEWS.d/next/Security/2020-10-23-19-19-30.bpo-42103.cILT66.rst +@@ -0,0 +1,2 @@ ++Prevented potential DoS attack via CPU and RAM exhaustion when processing ++malformed Apple Property List files in binary format. +-- +2.43.0 + diff --git a/SOURCES/00414-skip_test_zlib_s390x.patch b/SOURCES/00414-skip_test_zlib_s390x.patch new file mode 100644 index 0000000..05e3fbd --- /dev/null +++ b/SOURCES/00414-skip_test_zlib_s390x.patch @@ -0,0 +1,88 @@ +From 0d02ff99721f7650e39ba4c7d8fe06f412bbb591 Mon Sep 17 00:00:00 2001 +From: Victor Stinner +Date: Wed, 13 Dec 2023 11:50:26 +0100 +Subject: [PATCH] bpo-46623: Skip two test_zlib tests on s390x (GH-31096) + +Skip test_pair() and test_speech128() of test_zlib on s390x since +they fail if zlib uses the s390x hardware accelerator. +--- + Lib/test/test_zlib.py | 32 +++++++++++++++++++ + .../2022-02-03-09-45-26.bpo-46623.vxzuhV.rst | 2 ++ + 2 files changed, 34 insertions(+) + create mode 100644 Misc/NEWS.d/next/Tests/2022-02-03-09-45-26.bpo-46623.vxzuhV.rst + +diff --git a/Lib/test/test_zlib.py b/Lib/test/test_zlib.py +index b7170b4..770a425 100644 +--- a/Lib/test/test_zlib.py ++++ b/Lib/test/test_zlib.py +@@ -1,6 +1,7 @@ + import unittest + from test import support + import binascii ++import os + import pickle + import random + import sys +@@ -15,6 +16,35 @@ requires_Decompress_copy = unittest.skipUnless( + hasattr(zlib.decompressobj(), "copy"), + 'requires Decompress.copy()') + ++# bpo-46623: On s390x, when a hardware accelerator is used, using different ++# ways to compress data with zlib can produce different compressed data. ++# Simplified test_pair() code: ++# ++# def func1(data): ++# return zlib.compress(data) ++# ++# def func2(data) ++# co = zlib.compressobj() ++# x1 = co.compress(data) ++# x2 = co.flush() ++# return x1 + x2 ++# ++# On s390x if zlib uses a hardware accelerator, func1() creates a single ++# "final" compressed block whereas func2() produces 3 compressed blocks (the ++# last one is a final block). On other platforms with no accelerator, func1() ++# and func2() produce the same compressed data made of a single (final) ++# compressed block. ++# ++# Only the compressed data is different, the decompression returns the original ++# data: ++# ++# zlib.decompress(func1(data)) == zlib.decompress(func2(data)) == data ++# ++# Make the assumption that s390x always has an accelerator to simplify the skip ++# condition. Windows doesn't have os.uname() but it doesn't support s390x. ++skip_on_s390x = unittest.skipIf(hasattr(os, 'uname') and os.uname().machine == 's390x', ++ 'skipped on s390x') ++ + + class VersionTestCase(unittest.TestCase): + +@@ -174,6 +204,7 @@ class CompressTestCase(BaseCompressTestCase, unittest.TestCase): + bufsize=zlib.DEF_BUF_SIZE), + HAMLET_SCENE) + ++ @skip_on_s390x + def test_speech128(self): + # compress more data + data = HAMLET_SCENE * 128 +@@ -225,6 +256,7 @@ class CompressTestCase(BaseCompressTestCase, unittest.TestCase): + + class CompressObjectTestCase(BaseCompressTestCase, unittest.TestCase): + # Test compression object ++ @skip_on_s390x + def test_pair(self): + # straightforward compress/decompress objects + datasrc = HAMLET_SCENE * 128 +diff --git a/Misc/NEWS.d/next/Tests/2022-02-03-09-45-26.bpo-46623.vxzuhV.rst b/Misc/NEWS.d/next/Tests/2022-02-03-09-45-26.bpo-46623.vxzuhV.rst +new file mode 100644 +index 0000000..be085c0 +--- /dev/null ++++ b/Misc/NEWS.d/next/Tests/2022-02-03-09-45-26.bpo-46623.vxzuhV.rst +@@ -0,0 +1,2 @@ ++Skip test_pair() and test_speech128() of test_zlib on s390x since they fail ++if zlib uses the s390x hardware accelerator. Patch by Victor Stinner. +-- +2.43.0 + diff --git a/SPECS/python3.spec b/SPECS/python3.spec index 706c8b4..b9117fb 100644 --- a/SPECS/python3.spec +++ b/SPECS/python3.spec @@ -14,7 +14,7 @@ URL: https://www.python.org/ # WARNING When rebasing to a new Python version, # remember to update the python3-docs package as well Version: %{pybasever}.8 -Release: 56%{?dist} +Release: 56%{?dist}.2 License: Python @@ -802,6 +802,27 @@ Patch399: 00399-cve-2023-24329.patch # Backported from Python 3.8 Patch404: 00404-cve-2023-40217.patch +# 00408 # +# CVE-2022-48560 +# +# Security fix for CVE-2022-48560: python3: use after free in heappushpop() +# of heapq module +# Resolved upstream: https://github.com/python/cpython/issues/83602 +Patch408: 00408-CVE-2022-48560.patch + +# 00413 # +# CVE-2022-48564 +# +# DoS when processing malformed Apple Property List files in binary format +# Resolved upstream: https://github.com/python/cpython/commit/a63234c49b2fbfb6f0aca32525e525ce3d43b2b4 +Patch413: 00413-CVE-2022-48564.patch + +# 00414 # +# +# Skip test_pair() and test_speech128() of test_zlib on s390x since +# they fail if zlib uses the s390x hardware accelerator. +Patch414: 00414-skip_test_zlib_s390x.patch + # (New patches go here ^^^) # # When adding new patches to "python" and "python3" in Fedora, EL, etc., @@ -1151,6 +1172,9 @@ git apply %{PATCH351} %patch397 -p1 %patch399 -p1 %patch404 -p1 +%patch408 -p1 +%patch413 -p1 +%patch414 -p1 # Remove files that should be generated by the build # (This is after patching, so that we can use patches directly from upstream) @@ -2082,6 +2106,16 @@ fi # ====================================================== %changelog +* Tue Dec 12 2023 Lumír Balhar - 3.6.8-56.2 +- Security fix for CVE-2022-48564 +Resolves: RHEL-16673 +- Skip tests failing on s390x +Resolves: RHEL-19251 + +* Thu Nov 23 2023 Lumír Balhar - 3.6.8-56.1 +- Security fix for CVE-2022-48560 +Resolves: RHEL-16706 + * Thu Sep 07 2023 Charalampos Stratakis - 3.6.8-56 - Security fix for CVE-2023-40217 Resolves: RHEL-3041