From 5179d30710e6185dc7e6a6098ab23fe068b0d85b Mon Sep 17 00:00:00 2001 From: Lumir Balhar Date: Thu, 23 Nov 2023 13:25:44 +0100 Subject: [PATCH] 00408-cve-2022-48560.patch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Security fix for CVE-2022-48560: python3: use after free in heappushpop() of heapq module Resolved upstream: https://github.com/python/cpython/issues/83602 Backported from Python 3.6.11. Co-authored-by: Pablo Galindo Co-authored-by: Lumír Balhar --- Lib/test/test_heapq.py | 32 ++++++++++++++++++++++++++++++++ Modules/_heapqmodule.c | 31 ++++++++++++++++++++++++------- 2 files changed, 56 insertions(+), 7 deletions(-) --- a/Lib/test/test_heapq.py +++ b/Lib/test/test_heapq.py @@ -396,6 +396,38 @@ class TestErrorHandling(TestCase): 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[:] = [] + 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[:] = [] + return NotImplemented + + class g(int): + def __lt__(self, o): + list1[:] = [] + 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): module = py_heapq --- a/Modules/_heapqmodule.c +++ b/Modules/_heapqmodule.c @@ -52,7 +52,11 @@ _siftdown(PyListObject *heap, Py_ssize_t while (pos > startpos) { parentpos = (pos - 1) >> 1; parent = PyList_GET_ITEM(heap, parentpos); + Py_INCREF(newitem); + Py_INCREF(parent); cmp = cmp_lt(newitem, parent); + Py_DECREF(parent); + Py_DECREF(newitem); if (cmp == -1) return -1; if (size != PyList_GET_SIZE(heap)) { @@ -93,9 +97,13 @@ _siftup(PyListObject *heap, Py_ssize_t p childpos = 2*pos + 1; /* leftmost child position */ rightpos = childpos + 1; if (rightpos < endpos) { - cmp = cmp_lt( - PyList_GET_ITEM(heap, childpos), - PyList_GET_ITEM(heap, rightpos)); + PyObject* a = PyList_GET_ITEM(heap, childpos); + PyObject* b = PyList_GET_ITEM(heap, rightpos); + Py_INCREF(a); + Py_INCREF(b); + cmp = cmp_lt(a, b); + Py_DECREF(a); + Py_DECREF(b); if (cmp == -1) return -1; if (cmp == 0) @@ -236,7 +244,10 @@ heappushpop(PyObject *self, PyObject *ar return item; } - cmp = cmp_lt(PyList_GET_ITEM(heap, 0), item); + PyObject* top = PyList_GET_ITEM(heap, 0); + Py_INCREF(top); + cmp = cmp_lt(top, item); + Py_DECREF(top); if (cmp == -1) return NULL; if (cmp == 0) { @@ -395,7 +406,9 @@ _siftdownmax(PyListObject *heap, Py_ssiz while (pos > startpos){ parentpos = (pos - 1) >> 1; parent = PyList_GET_ITEM(heap, parentpos); + Py_INCREF(parent); cmp = cmp_lt(parent, newitem); + Py_DECREF(parent); if (cmp == -1) { Py_DECREF(newitem); return -1; @@ -436,9 +449,13 @@ _siftupmax(PyListObject *heap, Py_ssize_ childpos = 2*pos + 1; /* leftmost child position */ rightpos = childpos + 1; if (rightpos < endpos) { - cmp = cmp_lt( - PyList_GET_ITEM(heap, rightpos), - PyList_GET_ITEM(heap, childpos)); + PyObject* a = PyList_GET_ITEM(heap, rightpos); + PyObject* b = PyList_GET_ITEM(heap, childpos); + Py_INCREF(a); + Py_INCREF(b); + cmp = cmp_lt(a, b); + Py_DECREF(a); + Py_DECREF(b); if (cmp == -1) { Py_DECREF(newitem); return -1;