You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@sobolevn I think your solution is the only one that is correct.
There's a known issues with clearing during the comparison: #82769
Unfortunately, the nature of OrderedDict does not allow us to apply the same fix as for #82769 (Just incref the reference counter of values to compare before PyObject_RichCompareBool).
The root cause is in how is clear method is implemented for OrderedDict. It's decref's keys in nodes and then frees the node.
So, we cannot "incref" keys in the _odict_keys_equal.
Let's imagine we implemented this:
while (1) {
if (node_a==NULL&&node_b==NULL)
/* success: hit the end of each at the same time */return1;
elseif (node_a==NULL||node_b==NULL)
/* unequal length */return0;
else {
Py_INCREF(_odictnode_KEY(node_a));
Py_INCREF(_odictnode_KEY(node_b));
intres=PyObject_RichCompareBool(
(PyObject*)_odictnode_KEY(node_a),
(PyObject*)_odictnode_KEY(node_b),
Py_EQ);
if (res<0)
returnres;
elseif (res==0)
return0;
/* otherwise it must match, so move on to the next one */node_a=_odictnode_NEXT(node_a);
node_b=_odictnode_NEXT(node_b);
}
}
The first iteration of this loop will complete without any errors (actually clearing our OrderedDict). So, after this iteration our node_a and node_b become the next nodes (see the last lines of the cycle above).
But... some of these nodes actually freed! (in our example it's the node_a but it's not hard to clear both of our OrderedDicts :))
Now node_a points to something like 0xdddddddddddddddd and accessing its field key leads to a segfault.
Activity
graingert commentedon May 13, 2024
this seems to work correctly in 3.12.3+ and Python 3.13.0a6+ for me (ubuntu Jammy)
it also works correctly on main: Python 3.14.0a0 (heads/main:7d7eec595a, May 13 2024, 17:38:47)
sobolevn commentedon May 13, 2024
I can reproduce locally on 7d7eec5 with
--with-pydebugOutput on macos:
The problem happens here:
cpython/Objects/odictobject.c
Lines 819 to 822 in 7d7eec5
sobolevn commentedon May 13, 2024
I think we can add something like
🤔
sobolevn commentedon May 13, 2024
I came up with something like
I am not sure that this is a correct and/or optimal solution though.
It raises an error like so:
Eclips4 commentedon May 13, 2024
@sobolevn I think your solution is the only one that is correct.
There's a known issues with clearing during the comparison: #82769
Unfortunately, the nature of
OrderedDictdoes not allow us to apply the same fix as for #82769 (Just incref the reference counter of values to compare beforePyObject_RichCompareBool).The root cause is in how is
clearmethod is implemented forOrderedDict. It's decref's keys in nodes and then frees the node.So, we cannot "incref" keys in the
_odict_keys_equal.Let's imagine we implemented this:
The first iteration of this loop will complete without any errors (actually clearing our OrderedDict). So, after this iteration our
node_aandnode_bbecome the next nodes (see the last lines of the cycle above).But... some of these nodes actually freed! (in our example it's the
node_abut it's not hard to clear both of our OrderedDicts :))Now
node_apoints to something like0xddddddddddddddddand accessing its fieldkeyleads to a segfault.sobolevn commentedon May 13, 2024
@Eclips4 yeap, this was the first thing I tried.
node_a->keycaused the crash on the second loop.gh-119004: fix a crash in equality testing between `OrderedDict` (#12…
pythongh-119004: fix a crash in equality testing between `OrderedDict` (
pythongh-119004: fix a crash in equality testing between `OrderedDict` (
[3.12] gh-119004: fix a crash in equality testing between `OrderedDic…
[3.13] gh-119004: fix a crash in equality testing between `OrderedDic…
pythongh-148660: Fix use-after-free in OrderedDict.copy() on re-entra…
pythongh-148660: Fix use-after-free in OrderedDict.copy() on re-entra…
gh-148660: Fix use-after-free in OrderedDict.copy() on reentrant muta…
[3.14] gh-148660: Fix use-after-free in OrderedDict.copy() on reentra…
[3.15] gh-148660: Fix use-after-free in OrderedDict.copy() on reentra…
[3.13] gh-148660: Fix use-after-free in OrderedDict.copy() on reentra…