diff --git a/Include/internal/pycore_runtime.h b/Include/internal/pycore_runtime.h index 6e06e874711bc2..8f51e2def69fc9 100644 --- a/Include/internal/pycore_runtime.h +++ b/Include/internal/pycore_runtime.h @@ -143,7 +143,10 @@ typedef struct pyruntimestate { // is called multiple times. Py_OpenCodeHookFunction open_code_hook; void *open_code_userdata; - _Py_AuditHookEntry *audit_hook_head; + struct { + PyThread_type_lock mutex; + _Py_AuditHookEntry *head; + } audit_hooks; struct _py_object_runtime_state object_state; struct _Py_float_runtime_state float_state; diff --git a/Python/pystate.c b/Python/pystate.c index a1f14f76e751df..3ee99a4106d455 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -380,7 +380,7 @@ _Py_COMP_DIAG_IGNORE_DEPR_DECLS static const _PyRuntimeState initial = _PyRuntimeState_INIT(_PyRuntime); _Py_COMP_DIAG_POP -#define NUMLOCKS 6 +#define NUMLOCKS 7 #define LOCKS_INIT(runtime) \ { \ &(runtime)->interpreters.mutex, \ @@ -389,6 +389,7 @@ _Py_COMP_DIAG_POP &(runtime)->unicode_state.ids.lock, \ &(runtime)->imports.extensions.mutex, \ &(runtime)->atexit.mutex, \ + &(runtime)->audit_hooks.mutex, \ } static int @@ -432,7 +433,7 @@ init_runtime(_PyRuntimeState *runtime, runtime->open_code_hook = open_code_hook; runtime->open_code_userdata = open_code_userdata; - runtime->audit_hook_head = audit_hook_head; + runtime->audit_hooks.head = audit_hook_head; PyPreConfig_InitPythonConfig(&runtime->preconfig); @@ -458,7 +459,7 @@ _PyRuntimeState_Init(_PyRuntimeState *runtime) initialization and interpreter initialization. */ void *open_code_hook = runtime->open_code_hook; void *open_code_userdata = runtime->open_code_userdata; - _Py_AuditHookEntry *audit_hook_head = runtime->audit_hook_head; + _Py_AuditHookEntry *audit_hook_head = runtime->audit_hooks.head; // bpo-42882: Preserve next_index value if Py_Initialize()/Py_Finalize() // is called multiple times. Py_ssize_t unicode_next_index = runtime->unicode_state.ids.next_index; diff --git a/Python/sysmodule.c b/Python/sysmodule.c index 4427e73e584e30..70274108a97cdb 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -168,7 +168,7 @@ should_audit(PyInterpreterState *interp) if (!interp) { return 0; } - return (interp->runtime->audit_hook_head + return (interp->runtime->audit_hooks.head || interp->audit_hooks || PyDTrace_AUDIT_ENABLED()); } @@ -224,8 +224,11 @@ sys_audit_tstate(PyThreadState *ts, const char *event, goto exit; } - /* Call global hooks */ - _Py_AuditHookEntry *e = is->runtime->audit_hook_head; + /* Call global hooks + * + * We don't worry about any races on hooks getting added, + * since that would not leave is in an inconsistent state. */ + _Py_AuditHookEntry *e = is->runtime->audit_hooks.head; for (; e; e = e->next) { if (e->hookCFunction(event, eventArgs, e->userData) < 0) { goto exit; @@ -353,8 +356,12 @@ _PySys_ClearAuditHooks(PyThreadState *ts) _PySys_Audit(ts, "cpython._PySys_ClearAuditHooks", NULL); _PyErr_Clear(ts); - _Py_AuditHookEntry *e = runtime->audit_hook_head, *n; - runtime->audit_hook_head = NULL; + /* We don't worry about the very unlikely race right here, + * since it's entirely benign. Nothing else removes entries + * from the list and adding an entry right now would not cause + * any trouble. */ + _Py_AuditHookEntry *e = runtime->audit_hooks.head, *n; + runtime->audit_hooks.head = NULL; while (e) { n = e->next; PyMem_RawFree(e); @@ -362,6 +369,22 @@ _PySys_ClearAuditHooks(PyThreadState *ts) } } +static void +add_audit_hook_entry_unlocked(_PyRuntimeState *runtime, + _Py_AuditHookEntry *entry) +{ + if (runtime->audit_hooks.head == NULL) { + runtime->audit_hooks.head = entry; + } + else { + _Py_AuditHookEntry *last = runtime->audit_hooks.head; + while (last->next) { + last = last->next; + } + last->next = entry; + } +} + int PySys_AddAuditHook(Py_AuditHookFunction hook, void *userData) { @@ -389,29 +412,28 @@ PySys_AddAuditHook(Py_AuditHookFunction hook, void *userData) } } - _Py_AuditHookEntry *e = runtime->audit_hook_head; - if (!e) { - e = (_Py_AuditHookEntry*)PyMem_RawMalloc(sizeof(_Py_AuditHookEntry)); - runtime->audit_hook_head = e; - } else { - while (e->next) { - e = e->next; - } - e = e->next = (_Py_AuditHookEntry*)PyMem_RawMalloc( + _Py_AuditHookEntry *e = (_Py_AuditHookEntry*)PyMem_RawMalloc( sizeof(_Py_AuditHookEntry)); - } - if (!e) { if (tstate != NULL) { _PyErr_NoMemory(tstate); } return -1; } - e->next = NULL; e->hookCFunction = (Py_AuditHookFunction)hook; e->userData = userData; + if (runtime->audit_hooks.mutex == NULL) { + /* The runtime must not be initailized yet. */ + add_audit_hook_entry_unlocked(runtime, e); + } + else { + PyThread_acquire_lock(runtime->audit_hooks.mutex, WAIT_LOCK); + add_audit_hook_entry_unlocked(runtime, e); + PyThread_release_lock(runtime->audit_hooks.mutex); + } + return 0; }