mirror of
https://github.com/python/cpython
synced 2024-09-15 21:50:10 +00:00
gh-100227: Lock Around Adding Global Audit Hooks (gh-105515)
The risk of a race with this state is relatively low, but we play it safe anyway.
This commit is contained in:
parent
34c63b86d3
commit
e822a676f1
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue