Skip to content

Commit 7799c8e

Browse files
gh-100227: Lock Around Use of the Global "atexit" State (gh-105514)
The risk of a race with this state is relatively low, but we play it safe anyway.
1 parent 6a8b862 commit 7799c8e

File tree

3 files changed

+28
-25
lines changed

3 files changed

+28
-25
lines changed

Include/internal/pycore_atexit.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ extern "C" {
1515
typedef void (*atexit_callbackfunc)(void);
1616

1717
struct _atexit_runtime_state {
18+
PyThread_type_lock mutex;
1819
#define NEXITFUNCS 32
1920
atexit_callbackfunc callbacks[NEXITFUNCS];
2021
int ncallbacks;

Python/pylifecycle.c

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2973,24 +2973,35 @@ wait_for_thread_shutdown(PyThreadState *tstate)
29732973

29742974
int Py_AtExit(void (*func)(void))
29752975
{
2976-
if (_PyRuntime.atexit.ncallbacks >= NEXITFUNCS)
2976+
struct _atexit_runtime_state *state = &_PyRuntime.atexit;
2977+
PyThread_acquire_lock(state->mutex, WAIT_LOCK);
2978+
if (state->ncallbacks >= NEXITFUNCS) {
2979+
PyThread_release_lock(state->mutex);
29772980
return -1;
2978-
_PyRuntime.atexit.callbacks[_PyRuntime.atexit.ncallbacks++] = func;
2981+
}
2982+
state->callbacks[state->ncallbacks++] = func;
2983+
PyThread_release_lock(state->mutex);
29792984
return 0;
29802985
}
29812986

29822987
static void
29832988
call_ll_exitfuncs(_PyRuntimeState *runtime)
29842989
{
2990+
atexit_callbackfunc exitfunc;
29852991
struct _atexit_runtime_state *state = &runtime->atexit;
2992+
2993+
PyThread_acquire_lock(state->mutex, WAIT_LOCK);
29862994
while (state->ncallbacks > 0) {
29872995
/* pop last function from the list */
29882996
state->ncallbacks--;
2989-
atexit_callbackfunc exitfunc = state->callbacks[state->ncallbacks];
2997+
exitfunc = state->callbacks[state->ncallbacks];
29902998
state->callbacks[state->ncallbacks] = NULL;
29912999

3000+
PyThread_release_lock(state->mutex);
29923001
exitfunc();
3002+
PyThread_acquire_lock(state->mutex, WAIT_LOCK);
29933003
}
3004+
PyThread_release_lock(state->mutex);
29943005

29953006
fflush(stdout);
29963007
fflush(stderr);

Python/pystate.c

Lines changed: 13 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -380,7 +380,16 @@ _Py_COMP_DIAG_IGNORE_DEPR_DECLS
380380
static const _PyRuntimeState initial = _PyRuntimeState_INIT(_PyRuntime);
381381
_Py_COMP_DIAG_POP
382382

383-
#define NUMLOCKS 5
383+
#define NUMLOCKS 6
384+
#define LOCKS_INIT(runtime) \
385+
{ \
386+
&(runtime)->interpreters.mutex, \
387+
&(runtime)->xidregistry.mutex, \
388+
&(runtime)->getargs.mutex, \
389+
&(runtime)->unicode_state.ids.lock, \
390+
&(runtime)->imports.extensions.mutex, \
391+
&(runtime)->atexit.mutex, \
392+
}
384393

385394
static int
386395
alloc_for_runtime(PyThread_type_lock locks[NUMLOCKS])
@@ -427,13 +436,7 @@ init_runtime(_PyRuntimeState *runtime,
427436

428437
PyPreConfig_InitPythonConfig(&runtime->preconfig);
429438

430-
PyThread_type_lock *lockptrs[NUMLOCKS] = {
431-
&runtime->interpreters.mutex,
432-
&runtime->xidregistry.mutex,
433-
&runtime->getargs.mutex,
434-
&runtime->unicode_state.ids.lock,
435-
&runtime->imports.extensions.mutex,
436-
};
439+
PyThread_type_lock *lockptrs[NUMLOCKS] = LOCKS_INIT(runtime);
437440
for (int i = 0; i < NUMLOCKS; i++) {
438441
assert(locks[i] != NULL);
439442
*lockptrs[i] = locks[i];
@@ -512,13 +515,7 @@ _PyRuntimeState_Fini(_PyRuntimeState *runtime)
512515
LOCK = NULL; \
513516
}
514517

515-
PyThread_type_lock *lockptrs[NUMLOCKS] = {
516-
&runtime->interpreters.mutex,
517-
&runtime->xidregistry.mutex,
518-
&runtime->getargs.mutex,
519-
&runtime->unicode_state.ids.lock,
520-
&runtime->imports.extensions.mutex,
521-
};
518+
PyThread_type_lock *lockptrs[NUMLOCKS] = LOCKS_INIT(runtime);
522519
for (int i = 0; i < NUMLOCKS; i++) {
523520
FREE_LOCK(*lockptrs[i]);
524521
}
@@ -541,13 +538,7 @@ _PyRuntimeState_ReInitThreads(_PyRuntimeState *runtime)
541538
PyMemAllocatorEx old_alloc;
542539
_PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
543540

544-
PyThread_type_lock *lockptrs[NUMLOCKS] = {
545-
&runtime->interpreters.mutex,
546-
&runtime->xidregistry.mutex,
547-
&runtime->getargs.mutex,
548-
&runtime->unicode_state.ids.lock,
549-
&runtime->imports.extensions.mutex,
550-
};
541+
PyThread_type_lock *lockptrs[NUMLOCKS] = LOCKS_INIT(runtime);
551542
int reinit_err = 0;
552543
for (int i = 0; i < NUMLOCKS; i++) {
553544
reinit_err += _PyThread_at_fork_reinit(lockptrs[i]);

0 commit comments

Comments
 (0)