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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy