summaryrefslogtreecommitdiffstats
path: root/Objects
diff options
context:
space:
mode:
authorBénédikt Tran <10796600+picnixz@users.noreply.github.com>2024-09-23 23:44:36 (GMT)
committerGitHub <noreply@github.com>2024-09-23 23:44:36 (GMT)
commit38a887dc3ec52c4a7222279bf4b3ca2431b86de9 (patch)
tree54d37cb1aa9d648e9a6dbfb7330894b5c5fd66b1 /Objects
parente80dd3035fb805716bc49f9e7e9cab5f83614661 (diff)
downloadcpython-38a887dc3ec52c4a7222279bf4b3ca2431b86de9.zip
cpython-38a887dc3ec52c4a7222279bf4b3ca2431b86de9.tar.gz
cpython-38a887dc3ec52c4a7222279bf4b3ca2431b86de9.tar.bz2
gh-119004: fix a crash in equality testing between `OrderedDict` (#121329)
Diffstat (limited to 'Objects')
-rw-r--r--Objects/odictobject.c33
1 files changed, 25 insertions, 8 deletions
diff --git a/Objects/odictobject.c b/Objects/odictobject.c
index a9b801e..e151023 100644
--- a/Objects/odictobject.c
+++ b/Objects/odictobject.c
@@ -796,6 +796,7 @@ _odict_clear_nodes(PyODictObject *od)
_odictnode_DEALLOC(node);
node = next;
}
+ od->od_state++;
}
/* There isn't any memory management of nodes past this point. */
@@ -806,24 +807,40 @@ _odict_keys_equal(PyODictObject *a, PyODictObject *b)
{
_ODictNode *node_a, *node_b;
+ // keep operands' state to detect undesired mutations
+ const size_t state_a = a->od_state;
+ const size_t state_b = b->od_state;
+
node_a = _odict_FIRST(a);
node_b = _odict_FIRST(b);
while (1) {
- if (node_a == NULL && node_b == NULL)
+ if (node_a == NULL && node_b == NULL) {
/* success: hit the end of each at the same time */
return 1;
- else if (node_a == NULL || node_b == NULL)
+ }
+ else if (node_a == NULL || node_b == NULL) {
/* unequal length */
return 0;
+ }
else {
- int res = PyObject_RichCompareBool(
- (PyObject *)_odictnode_KEY(node_a),
- (PyObject *)_odictnode_KEY(node_b),
- Py_EQ);
- if (res < 0)
+ PyObject *key_a = Py_NewRef(_odictnode_KEY(node_a));
+ PyObject *key_b = Py_NewRef(_odictnode_KEY(node_b));
+ int res = PyObject_RichCompareBool(key_a, key_b, Py_EQ);
+ Py_DECREF(key_a);
+ Py_DECREF(key_b);
+ if (res < 0) {
return res;
- else if (res == 0)
+ }
+ else if (a->od_state != state_a || b->od_state != state_b) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "OrderedDict mutated during iteration");
+ return -1;
+ }
+ else if (res == 0) {
+ // This check comes after the check on the state
+ // in order for the exception to be set correctly.
return 0;
+ }
/* otherwise it must match, so move on to the next one */
node_a = _odictnode_NEXT(node_a);