diff --git a/bpo-37596-make-set-marshalling.patch b/bpo-37596-make-set-marshalling.patch new file mode 100644 index 0000000..44bc85f --- /dev/null +++ b/bpo-37596-make-set-marshalling.patch @@ -0,0 +1,103 @@ +From 33d95c6facdfda3c8c0feffa7a99184e4abc2f63 Mon Sep 17 00:00:00 2001 +From: Brandt Bucher +Date: Wed, 25 Aug 2021 04:14:34 -0700 +Subject: [PATCH] bpo-37596: Make `set` and `frozenset` marshalling + deterministic (GH-27926) + +--- + Lib/test/test_marshal.py | 26 ++++++++ + Misc/NEWS.d/next/Library/2021-08-23-21-39-59.bpo-37596.ojRcwB.rst | 2 + Python/marshal.c | 32 ++++++++++ + 3 files changed, 60 insertions(+) + create mode 100644 Misc/NEWS.d/next/Library/2021-08-23-21-39-59.bpo-37596.ojRcwB.rst + +--- a/Lib/test/test_marshal.py ++++ b/Lib/test/test_marshal.py +@@ -1,5 +1,6 @@ + from test import support + from test.support import os_helper ++from test.support.script_helper import assert_python_ok + import array + import io + import marshal +@@ -318,6 +319,31 @@ class BugsTestCase(unittest.TestCase): + for i in range(len(data)): + self.assertRaises(EOFError, marshal.loads, data[0: i]) + ++ def test_deterministic_sets(self): ++ # bpo-37596: To support reproducible builds, sets and frozensets need to ++ # have their elements serialized in a consistent order (even when they ++ # have been scrambled by hash randomization): ++ for kind in ("set", "frozenset"): ++ for elements in ( ++ "float('nan'), b'a', b'b', b'c', 'x', 'y', 'z'", ++ # Also test for bad interactions with backreferencing: ++ "('string', 1), ('string', 2), ('string', 3)", ++ ): ++ s = f"{kind}([{elements}])" ++ with self.subTest(s): ++ # First, make sure that our test case still has different ++ # orders under hash seeds 0 and 1. If this check fails, we ++ # need to update this test with different elements: ++ args = ["-c", f"print({s})"] ++ _, repr_0, _ = assert_python_ok(*args, PYTHONHASHSEED="0") ++ _, repr_1, _ = assert_python_ok(*args, PYTHONHASHSEED="1") ++ self.assertNotEqual(repr_0, repr_1) ++ # Then, perform the actual test: ++ args = ["-c", f"import marshal; print(marshal.dumps({s}))"] ++ _, dump_0, _ = assert_python_ok(*args, PYTHONHASHSEED="0") ++ _, dump_1, _ = assert_python_ok(*args, PYTHONHASHSEED="1") ++ self.assertEqual(dump_0, dump_1) ++ + LARGE_SIZE = 2**31 + pointer_size = 8 if sys.maxsize > 0xFFFFFFFF else 4 + +--- /dev/null ++++ b/Misc/NEWS.d/next/Library/2021-08-23-21-39-59.bpo-37596.ojRcwB.rst +@@ -0,0 +1,2 @@ ++Ensure that :class:`set` and :class:`frozenset` objects are always ++:mod:`marshalled ` reproducibly. +--- a/Python/marshal.c ++++ b/Python/marshal.c +@@ -502,9 +502,41 @@ w_complex_object(PyObject *v, char flag, + W_TYPE(TYPE_SET, p); + n = PySet_GET_SIZE(v); + W_SIZE(n, p); ++ // bpo-37596: To support reproducible builds, sets and frozensets need ++ // to have their elements serialized in a consistent order (even when ++ // they have been scrambled by hash randomization). To ensure this, we ++ // use an order equivalent to sorted(v, key=marshal.dumps): ++ PyObject *pairs = PyList_New(0); ++ if (pairs == NULL) { ++ p->error = WFERR_NOMEMORY; ++ return; ++ } + while (_PySet_NextEntry(v, &pos, &value, &hash)) { ++ PyObject *dump = PyMarshal_WriteObjectToString(value, p->version); ++ if (dump == NULL) { ++ p->error = WFERR_UNMARSHALLABLE; ++ goto anyset_done; ++ } ++ PyObject *pair = PyTuple_Pack(2, dump, value); ++ Py_DECREF(dump); ++ if (pair == NULL || PyList_Append(pairs, pair)) { ++ p->error = WFERR_NOMEMORY; ++ Py_XDECREF(pair); ++ goto anyset_done; ++ } ++ Py_DECREF(pair); ++ } ++ if (PyList_Sort(pairs)) { ++ p->error = WFERR_NOMEMORY; ++ goto anyset_done; ++ } ++ for (Py_ssize_t i = 0; i < n; i++) { ++ PyObject *pair = PyList_GET_ITEM(pairs, i); ++ value = PyTuple_GET_ITEM(pair, 1); + w_object(value, p); + } ++ anyset_done: ++ Py_DECREF(pairs); + } + else if (PyCode_Check(v)) { + PyCodeObject *co = (PyCodeObject *)v; diff --git a/python39.changes b/python39.changes index ea75588..2453db2 100644 --- a/python39.changes +++ b/python39.changes @@ -1,3 +1,9 @@ +------------------------------------------------------------------- +Sat Jul 29 20:18:14 UTC 2023 - Matej Cepl + +- Add bpo-37596-make-set-marshalling.patch making marshalling of + `set` and `frozenset` deterministic (bsc#1211765). + ------------------------------------------------------------------- Wed Jul 19 11:15:39 UTC 2023 - Matej Cepl diff --git a/python39.spec b/python39.spec index f6e85fb..0fc1094 100644 --- a/python39.spec +++ b/python39.spec @@ -161,6 +161,9 @@ Patch35: support-expat-CVE-2022-25236-patched.patch # PATCH-FIX-UPSTREAM 98437-sphinx.locale._-as-gettext-in-pyspecific.patch gh#python/cpython#98366 mcepl@suse.com # this patch makes things totally awesome Patch37: 98437-sphinx.locale._-as-gettext-in-pyspecific.patch +# PATCH-FIX-UPSTREAM bpo-37596-make-set-marshalling.patch bsc#1211765 mcepl@suse.com +# Make `set` and `frozenset` marshalling deterministic +Patch38: bpo-37596-make-set-marshalling.patch # PATCH-FIX-UPSTREAM gh-78214-marshal_stabilize_FLAG_REF.patch bsc#1213463 mcepl@suse.com # marshal: Stabilize FLAG_REF usage Patch39: gh-78214-marshal_stabilize_FLAG_REF.patch @@ -410,7 +413,6 @@ other applications. %prep %setup -q -n %{tarname} %patch02 -p1 - %patch06 -p1 %patch07 -p1 %patch08 -p1 @@ -428,6 +430,7 @@ other applications. %endif %patch35 -p1 %patch37 -p1 +%patch38 -p1 %patch39 -p1 %patch40 -p1 %if 0%{?sle_version} && 0%{?sle_version} <= 150500