Skip to content

Commit

Permalink
pythongh-100227: Lock Around Adding Global Audit Hooks (pythongh-105515)
Browse files Browse the repository at this point in the history
The risk of a race with this state is relatively low, but we play it safe anyway.
  • Loading branch information
ericsnowcurrently committed Jun 8, 2023
1 parent 2ad2bd8 commit 986572b
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 21 deletions.
5 changes: 4 additions & 1 deletion Include/internal/pycore_runtime.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
7 changes: 4 additions & 3 deletions Python/pystate.c
Original file line number Diff line number Diff line change
Expand Up @@ -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, \
Expand All @@ -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
Expand Down Expand Up @@ -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);

Expand All @@ -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;
Expand Down
56 changes: 39 additions & 17 deletions Python/sysmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -353,15 +356,35 @@ _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);
e = n;
}
}

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)
{
Expand Down Expand Up @@ -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;
}

Expand Down

0 comments on commit 986572b

Please sign in to comment.