-
-
Notifications
You must be signed in to change notification settings - Fork 34.2k
bpo-30604: Move co_extra_freefuncs to interpreter state to avoid crashes in threads #2015
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
9ce5553
b976f1b
642e583
93a68dd
a33106b
f2c604f
913f313
1a05a6a
6f0bedb
b41319a
2e23477
9473adc
5485fb1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -411,11 +411,11 @@ static void | |
| code_dealloc(PyCodeObject *co) | ||
| { | ||
| if (co->co_extra != NULL) { | ||
| PyThreadState *tstate = PyThreadState_Get(); | ||
| __PyCodeExtraState *state = __PyCodeExtraState_Get(); | ||
| _PyCodeObjectExtra *co_extra = co->co_extra; | ||
|
|
||
| for (Py_ssize_t i = 0; i < co_extra->ce_size; i++) { | ||
| freefunc free_extra = tstate->co_extra_freefuncs[i]; | ||
| freefunc free_extra = state->co_extra_freefuncs[i]; | ||
|
|
||
| if (free_extra != NULL) { | ||
| free_extra(co_extra->ce_extras[i]); | ||
|
|
@@ -825,8 +825,6 @@ _PyCode_CheckLineNumber(PyCodeObject* co, int lasti, PyAddrPair *bounds) | |
| int | ||
| _PyCode_GetExtra(PyObject *code, Py_ssize_t index, void **extra) | ||
| { | ||
| assert(*extra == NULL); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is this change?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It really just makes the API harder to use. extra is logically an out parameter here so it's input state is irrelevant. It means that you can't just re-use the same variable w/o zero initializing it to get the extra field (which is what I'm doing in the unit tests I've added). I just also updated it so it'll be zero'd out in the case where there is no value actually as otherwise you might think it succeeds and get garbage out.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Alright, makes sense. |
||
|
|
||
| if (!PyCode_Check(code)) { | ||
| PyErr_BadInternalCall(); | ||
| return -1; | ||
|
|
@@ -837,6 +835,7 @@ _PyCode_GetExtra(PyObject *code, Py_ssize_t index, void **extra) | |
|
|
||
|
|
||
| if (co_extra == NULL || co_extra->ce_size <= index) { | ||
| *extra = NULL; | ||
| return 0; | ||
| } | ||
|
|
||
|
|
@@ -848,10 +847,10 @@ _PyCode_GetExtra(PyObject *code, Py_ssize_t index, void **extra) | |
| int | ||
| _PyCode_SetExtra(PyObject *code, Py_ssize_t index, void *extra) | ||
| { | ||
| PyThreadState *tstate = PyThreadState_Get(); | ||
| __PyCodeExtraState *state = __PyCodeExtraState_Get(); | ||
|
|
||
| if (!PyCode_Check(code) || index < 0 || | ||
| index >= tstate->co_extra_user_count) { | ||
| index >= state->co_extra_user_count) { | ||
| PyErr_BadInternalCall(); | ||
| return -1; | ||
| } | ||
|
|
@@ -866,13 +865,13 @@ _PyCode_SetExtra(PyObject *code, Py_ssize_t index, void *extra) | |
| } | ||
|
|
||
| co_extra->ce_extras = PyMem_Malloc( | ||
| tstate->co_extra_user_count * sizeof(void*)); | ||
| state->co_extra_user_count * sizeof(void*)); | ||
| if (co_extra->ce_extras == NULL) { | ||
| PyMem_Free(co_extra); | ||
| return -1; | ||
| } | ||
|
|
||
| co_extra->ce_size = tstate->co_extra_user_count; | ||
| co_extra->ce_size = state->co_extra_user_count; | ||
|
|
||
| for (Py_ssize_t i = 0; i < co_extra->ce_size; i++) { | ||
| co_extra->ce_extras[i] = NULL; | ||
|
|
@@ -882,20 +881,27 @@ _PyCode_SetExtra(PyObject *code, Py_ssize_t index, void *extra) | |
| } | ||
| else if (co_extra->ce_size <= index) { | ||
| void** ce_extras = PyMem_Realloc( | ||
| co_extra->ce_extras, tstate->co_extra_user_count * sizeof(void*)); | ||
| co_extra->ce_extras, state->co_extra_user_count * sizeof(void*)); | ||
|
|
||
| if (ce_extras == NULL) { | ||
| return -1; | ||
| } | ||
|
|
||
| for (Py_ssize_t i = co_extra->ce_size; | ||
| i < tstate->co_extra_user_count; | ||
| i < state->co_extra_user_count; | ||
| i++) { | ||
| ce_extras[i] = NULL; | ||
| } | ||
|
|
||
| co_extra->ce_extras = ce_extras; | ||
| co_extra->ce_size = tstate->co_extra_user_count; | ||
| co_extra->ce_size = state->co_extra_user_count; | ||
| } | ||
|
|
||
| if (co_extra->ce_extras[index] != NULL) { | ||
| freefunc free = state->co_extra_freefuncs[index]; | ||
| if (free != NULL) { | ||
| free(co_extra->ce_extras[index]); | ||
| } | ||
| } | ||
|
|
||
| co_extra->ce_extras[index] = extra; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PyThreadStateis public, and you're changing the size of the struct, so this is a problematic ABI break. Keeping these fields the same size, but changing their names to_preserve_36_ABI_1and_preserve_36_ABI_2should address most of that concern.