[3.12] gh-100227: Lock Around Use of the Global "atexit" State (gh-105514) (gh-105517)

The risk of a race with this state is relatively low, but we play it safe anyway.
(cherry picked from commit 7799c8e678)

Co-authored-by: Eric Snow <ericsnowcurrently@gmail.com>
This commit is contained in:
Miss Islington (bot) 2023-06-08 12:27:44 -07:00 committed by GitHub
parent 8698fa83f6
commit 2ad2bd8b14
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 488 additions and 457 deletions

File diff suppressed because it is too large Load diff

View file

@ -15,6 +15,7 @@ extern "C" {
typedef void (*atexit_callbackfunc)(void); typedef void (*atexit_callbackfunc)(void);
struct _atexit_runtime_state { struct _atexit_runtime_state {
PyThread_type_lock mutex;
#define NEXITFUNCS 32 #define NEXITFUNCS 32
atexit_callbackfunc callbacks[NEXITFUNCS]; atexit_callbackfunc callbacks[NEXITFUNCS];
int ncallbacks; int ncallbacks;

View file

@ -2974,24 +2974,35 @@ wait_for_thread_shutdown(PyThreadState *tstate)
int Py_AtExit(void (*func)(void)) int Py_AtExit(void (*func)(void))
{ {
if (_PyRuntime.atexit.ncallbacks >= NEXITFUNCS) struct _atexit_runtime_state *state = &_PyRuntime.atexit;
PyThread_acquire_lock(state->mutex, WAIT_LOCK);
if (state->ncallbacks >= NEXITFUNCS) {
PyThread_release_lock(state->mutex);
return -1; return -1;
_PyRuntime.atexit.callbacks[_PyRuntime.atexit.ncallbacks++] = func; }
state->callbacks[state->ncallbacks++] = func;
PyThread_release_lock(state->mutex);
return 0; return 0;
} }
static void static void
call_ll_exitfuncs(_PyRuntimeState *runtime) call_ll_exitfuncs(_PyRuntimeState *runtime)
{ {
atexit_callbackfunc exitfunc;
struct _atexit_runtime_state *state = &runtime->atexit; struct _atexit_runtime_state *state = &runtime->atexit;
PyThread_acquire_lock(state->mutex, WAIT_LOCK);
while (state->ncallbacks > 0) { while (state->ncallbacks > 0) {
/* pop last function from the list */ /* pop last function from the list */
state->ncallbacks--; state->ncallbacks--;
atexit_callbackfunc exitfunc = state->callbacks[state->ncallbacks]; exitfunc = state->callbacks[state->ncallbacks];
state->callbacks[state->ncallbacks] = NULL; state->callbacks[state->ncallbacks] = NULL;
PyThread_release_lock(state->mutex);
exitfunc(); exitfunc();
PyThread_acquire_lock(state->mutex, WAIT_LOCK);
} }
PyThread_release_lock(state->mutex);
fflush(stdout); fflush(stdout);
fflush(stderr); fflush(stderr);

View file

@ -380,7 +380,16 @@ _Py_COMP_DIAG_IGNORE_DEPR_DECLS
static const _PyRuntimeState initial = _PyRuntimeState_INIT(_PyRuntime); static const _PyRuntimeState initial = _PyRuntimeState_INIT(_PyRuntime);
_Py_COMP_DIAG_POP _Py_COMP_DIAG_POP
#define NUMLOCKS 5 #define NUMLOCKS 6
#define LOCKS_INIT(runtime) \
{ \
&(runtime)->interpreters.mutex, \
&(runtime)->xidregistry.mutex, \
&(runtime)->getargs.mutex, \
&(runtime)->unicode_state.ids.lock, \
&(runtime)->imports.extensions.mutex, \
&(runtime)->atexit.mutex, \
}
static int static int
alloc_for_runtime(PyThread_type_lock locks[NUMLOCKS]) alloc_for_runtime(PyThread_type_lock locks[NUMLOCKS])
@ -427,13 +436,7 @@ init_runtime(_PyRuntimeState *runtime,
PyPreConfig_InitPythonConfig(&runtime->preconfig); PyPreConfig_InitPythonConfig(&runtime->preconfig);
PyThread_type_lock *lockptrs[NUMLOCKS] = { PyThread_type_lock *lockptrs[NUMLOCKS] = LOCKS_INIT(runtime);
&runtime->interpreters.mutex,
&runtime->xidregistry.mutex,
&runtime->getargs.mutex,
&runtime->unicode_state.ids.lock,
&runtime->imports.extensions.mutex,
};
for (int i = 0; i < NUMLOCKS; i++) { for (int i = 0; i < NUMLOCKS; i++) {
assert(locks[i] != NULL); assert(locks[i] != NULL);
*lockptrs[i] = locks[i]; *lockptrs[i] = locks[i];
@ -512,13 +515,7 @@ _PyRuntimeState_Fini(_PyRuntimeState *runtime)
LOCK = NULL; \ LOCK = NULL; \
} }
PyThread_type_lock *lockptrs[NUMLOCKS] = { PyThread_type_lock *lockptrs[NUMLOCKS] = LOCKS_INIT(runtime);
&runtime->interpreters.mutex,
&runtime->xidregistry.mutex,
&runtime->getargs.mutex,
&runtime->unicode_state.ids.lock,
&runtime->imports.extensions.mutex,
};
for (int i = 0; i < NUMLOCKS; i++) { for (int i = 0; i < NUMLOCKS; i++) {
FREE_LOCK(*lockptrs[i]); FREE_LOCK(*lockptrs[i]);
} }
@ -541,13 +538,7 @@ _PyRuntimeState_ReInitThreads(_PyRuntimeState *runtime)
PyMemAllocatorEx old_alloc; PyMemAllocatorEx old_alloc;
_PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc); _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
PyThread_type_lock *lockptrs[NUMLOCKS] = { PyThread_type_lock *lockptrs[NUMLOCKS] = LOCKS_INIT(runtime);
&runtime->interpreters.mutex,
&runtime->xidregistry.mutex,
&runtime->getargs.mutex,
&runtime->unicode_state.ids.lock,
&runtime->imports.extensions.mutex,
};
int reinit_err = 0; int reinit_err = 0;
for (int i = 0; i < NUMLOCKS; i++) { for (int i = 0; i < NUMLOCKS; i++) {
reinit_err += _PyThread_at_fork_reinit(lockptrs[i]); reinit_err += _PyThread_at_fork_reinit(lockptrs[i]);