Skip to content

Commit af19777

Browse files
committed
Serialize frozenset elements deterministically
1 parent fa6304a commit af19777

File tree

1 file changed

+33
-0
lines changed

1 file changed

+33
-0
lines changed

Python/marshal.c

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -503,9 +503,42 @@ w_complex_object(PyObject *v, char flag, WFILE *p)
503503
W_TYPE(TYPE_SET, p);
504504
n = PySet_GET_SIZE(v);
505505
W_SIZE(n, p);
506+
// bpo-37596: We need to write the elements in a deterministic order!
507+
// Since we know that they all need marshal support anyways, this can
508+
// be conveniently accomplished by using their marshal serializations
509+
// as sort keys:
510+
PyObject *pairs = PyList_New(0);
511+
if (pairs == NULL) {
512+
p->error = WFERR_NOMEMORY;
513+
return;
514+
}
515+
PyObject *pair = NULL;
506516
while (_PySet_NextEntry(v, &pos, &value, &hash)) {
517+
PyObject *dump = PyMarshal_WriteObjectToString(value, p->version);
518+
if (dump == NULL) {
519+
p->error = WFERR_UNMARSHALLABLE;
520+
goto anyset_done;
521+
}
522+
pair = PyTuple_Pack(2, dump, value);
523+
Py_DECREF(dump);
524+
if (pair == NULL || PyList_Append(pairs, pair)) {
525+
p->error = WFERR_NOMEMORY;
526+
goto anyset_done;
527+
}
528+
Py_CLEAR(pair);
529+
}
530+
if (PyList_Sort(pairs)) {
531+
p->error = WFERR_UNMARSHALLABLE;
532+
goto anyset_done;
533+
}
534+
for (Py_ssize_t i = 0; i < n; i++) {
535+
PyObject *pair = PyList_GET_ITEM(pairs, i);
536+
PyObject *value = PyTuple_GET_ITEM(pair, 1);
507537
w_object(value, p);
508538
}
539+
anyset_done:
540+
Py_XDECREF(pairs);
541+
Py_XDECREF(pair);
509542
}
510543
else if (PyCode_Check(v)) {
511544
PyCodeObject *co = (PyCodeObject *)v;

0 commit comments

Comments
 (0)