diff options
author | Bénédikt Tran <10796600+picnixz@users.noreply.github.com> | 2024-09-23 23:44:36 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-09-23 23:44:36 (GMT) |
commit | 38a887dc3ec52c4a7222279bf4b3ca2431b86de9 (patch) | |
tree | 54d37cb1aa9d648e9a6dbfb7330894b5c5fd66b1 /Objects | |
parent | e80dd3035fb805716bc49f9e7e9cab5f83614661 (diff) | |
download | cpython-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.c | 33 |
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); |