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
{{ message }}
Repository navigation
nogil segmentation fault on ordered dict operations #125996
We're a research group focused on testing concurrent runtimes. Our work-in-progress prototype found a segmentation fault on the current nogil build when using concurrent operations on the same ordered dict. The program below shows the crash.
We ran this on our build from source configured with ./configure --disable-gil.
Here's some details about the OS, compiler, binutils: Linux version 5.15.0-124-generic (buildd@lcy02-amd64-118) (gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0, GNU ld (GNU Binutils for Ubuntu) 2.38) #134-Ubuntu SMP Fri Sep 27 20:20:17 UTC 2024
@flypoodles and @overlorde are part of the team, adding them so they get notified about further discussion.
We're happy to provide more details about the crash.
CPython versions tested on:
3.13, 3.14, CPython main branch
Operating systems tested on:
Linux
Output from running 'python -VV' on the command line:
gdb uses ptrace to attach to the process, our experience is that ptrace changes timings and thread schedulings. Can you confirm it crashes outside of gdb?
We'll try to get a stacktrace and more info on this, we'll also try your version.
I successfully triggered the error, and it turns out that the underlying operation of move_to_end is not atomic. If this is not considered from the beginning of the design, and the author does not intend to take over this task, I request to fix the issue.
Edit:
Actually, you only need to make the following changes, for example
After the check, other threads mutate the dict such that it becomes empty (e.g., calling clear, removing the last element from a dict with a single element, N threads concurrently removing N elements from a dict with N elements, etc)
This line assumes that node will never be NULL due to an empty dict, but now that assumption fails:
To increase the odds of finding this behavior, you can add calls to yield (maybe it's _Py_yield()) after the NULL check I mentioned above in (1).
Apologies if I misunderstood your proposed fix, but I'm curious about the case where the dict changes in a meaningful way (e.g., non empty to empty) while the method is active.
I don't think anyone has looked at making ordereddict thread-safe yet, so I'm not surprised to see a crash here.
It's slightly more embarrassing than that. Dino started to look at OrderedDict back in January, and I suggested we defer it at the time and then forgot to revisit it.
Given that 3.13.0 is already released, my preference would be:
Activity
rruuaanng commentedon Oct 26, 2024
luisggpina commentedon Oct 26, 2024
gdb uses ptrace to attach to the process, our experience is that ptrace changes timings and thread schedulings. Can you confirm it crashes outside of gdb?
We'll try to get a stacktrace and more info on this, we'll also try your version.
rruuaanng commentedon Oct 26, 2024
mpage commentedon Oct 26, 2024
I can reproduce this if I run the script provided by @luisggpina in a loop. Here is a sample stacktrace: https://gist.github.com/mpage/60b274f14c26cca1429fdfe98cc74ef9.
Built with
./configure --disable-gil --with-pydebug:I don't think anyone has looked at making ordereddict thread-safe yet, so I'm not surprised to see a crash here.
rruuaanng commentedon Oct 26, 2024
cpython/Objects/odictobject.c
Lines 1300 to 1322 in c5b99f5
I successfully triggered the error, and it turns out that the underlying operation of move_to_end is not atomic. If this is not considered from the beginning of the design, and the author does not intend to take over this task, I request to fix the issue.
Edit:
Actually, you only need to make the following changes, for example
It will be consistent with the append operation of the list.
luisggpina commentedon Oct 26, 2024
Thanks for the proposed fix!
Here's a case that might be problematic:
cpython/Objects/odictobject.c
Lines 1296 to 1299 in c5b99f5
clear, removing the last element from a dict with a single element, N threads concurrently removing N elements from a dict with N elements, etc)nodewill never beNULLdue to an empty dict, but now that assumption fails:cpython/Objects/odictobject.c
Line 1300 in c5b99f5
cpython/Objects/odictobject.c
Line 1301 in c5b99f5
To increase the odds of finding this behavior, you can add calls to
yield(maybe it's_Py_yield()) after the NULL check I mentioned above in (1).Apologies if I misunderstood your proposed fix, but I'm curious about the case where the dict changes in a meaningful way (e.g., non empty to empty) while the method is active.
colesbury commentedon Oct 28, 2024
Thanks for the bug report.
It's slightly more embarrassing than that. Dino started to look at OrderedDict back in January, and I suggested we defer it at the time and then forgot to revisit it.
Given that 3.13.0 is already released, my preference would be:
gh-125996: fix thread safety of `collections.OrderedDict` (#133734)
[3.14] pythongh-125996: fix thread safety of `collections.OrderedDict` (
[3.14] gh-125996: fix thread safety of `collections.OrderedDict` (GH-…