8000 gh-100227: Lock Around Adding Global Audit Hooks (gh-105515) · python/cpython@e822a67 · GitHub
[go: up one dir, main page]

Skip to content

Commit e822a67

Browse files
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.
1 parent 34c63b8 commit e822a67

File tree

3 files changed

+47
-21
lines changed

3 files changed

+47
-21
lines changed

Include/internal/pycore_runtime.h

Lines changed: 4 additions & 1 deletion
< 8000 tr class="diff-line-row">
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,10 @@ typedef struct pyruntimestate {
143143
// is called multiple times.
144144
Py_OpenCodeHookFunction open_code_hook;
145145
void *open_code_userdata;
146-
_Py_AuditHookEntry *audit_hook_head;
146+
struct {
147+
PyThread_type_lock mutex;
148+
_Py_AuditHookEntry *head;
149+
} audit_hooks;
147150

148151
struct _py_object_runtime_state object_state;
149152
struct _Py_float_runtime_state float_state;

Python/pystate.c

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

383-
#define NUMLOCKS 6
383+
#define NUMLOCKS 7
384384
#define LOCKS_INIT(runtime) \
385385
{ \
386386
&(runtime)->interpreters.mutex, \
@@ -389,6 +389,7 @@ _Py_COMP_DIAG_POP
389389
&(runtime)->unicode_state.ids.lock, \
390390
&(runtime)->imports.extensions.mutex, \
391391
&(runtime)->atexit.mutex, \
392+
&(runtime)->audit_hooks.mutex, \
392393
}
393394

394395
static int
@@ -432,7 +433,7 @@ init_runtime(_PyRuntimeState *runtime,
432433

433434
runtime->open_code_hook = open_code_hook;
434435
runtime->open_code_userdata = open_code_userdata;
435-
runtime->audit_hook_head = audit_hook_head;
436+
runtime->audit_hooks.head = audit_hook_head;
436437

437438
PyPreConfig_InitPythonConfig(&runtime->preconfig);
438439

@@ -458,7 +459,7 @@ _PyRuntimeState_Init(_PyRuntimeState *runtime)
458459
initialization and interpreter initialization. */
459460
void *open_code_hook = runtime->open_code_hook;
460461
void *open_code_userdata = runtime->open_code_userdata;
461-
_Py_AuditHookEntry *audit_hook_head = runtime->audit_hook_head;
462+
_Py_AuditHookEntry *audit_hook_head = runtime->audit_hooks.head;
462463
// bpo-42882: Preserve next_index value if Py_Initialize()/Py_Finalize()
463464
// is called multiple times.
464465
Py_ssize_t unicode_next_index = runtime->unicode_state.ids.next_index;

Python/sysmodule.c

Lines changed: 39 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ should_audit(PyInterpreterState *interp)
168168
if (!interp) {
169169
return 0;
170170
}
171-
return (interp->runtime->audit_hook_head
171+
return (interp->runtime->audit_hooks.head
172172
|| interp->audit_hooks
173173
|| PyDTrace_AUDIT_ENABLED());
174174
}
@@ -224,8 +224,11 @@ sys_audit_tstate(PyThreadState *ts, const char *event,
224224
goto exit;
225225
}
226226

227-
/* Call global hooks */
228-
_Py_AuditHookEntry *e = is->runtime->audit_hook_head;
227+
/* Call global hooks
228+
*
229+
* We don't worry about any races on hooks getting added,
230+
* since that would not leave is in an inconsistent state. */
231+
_Py_AuditHookEntry *e = is->runtime->audit_hooks.head;
229232
for (; e; e = e->next) {
230233
if (e->hookCFunction(event, eventArgs, e->userData) < 0) {
231234
goto exit;
@@ -353,15 +356,35 @@ _PySys_ClearAuditHooks(PyThreadState *ts)
353356
_PySys_Audit(ts, "cpython._PySys_ClearAuditHooks", NULL);
354357
_PyErr_Clear(ts);
355358

356-
_Py_AuditHookEntry *e = runtime->audit_hook_head, *n;
357-
runtime->audit_hook_head = NULL;
359+
/* We don't worry about the very unlikely race right here,
360+
* since it's entirely benign. Nothing else removes entries
361+
* from the list and adding an entry right now would not cause
362+
* any trouble. */
363+
_Py_AuditHookEntry *e = runtime->audit_hooks.head, *n;
364+
runtime->audit_hooks.head = NULL;
358365
while (e) {
359366
n = e->next;
360367
PyMem_RawFree(e);
361368
e = n;
362369
}
363370
}
364371

372+
static void
373+
add_audit_hook_entry_unlocked(_PyRuntimeState *runtime,
374+
_Py_AuditHookEntry *entry)
375+
{
376+
if (runtime->audit_hooks.head == NULL) {
377+
runtime->audit_hooks.head = entry;
378+
}
379+
else {
380+
_Py_AuditHookEntry *last = runtime->audit_hooks.head;
381+
while (last->next) {
382+
last = last->next;
383+
}
384+
last->next = entry;
385+
}
386+
}
387+
365388
int
366389
PySys_AddAuditHook(Py_AuditHookFunction hook, void *userData)
367390
{
@@ -389,29 +412,28 @@ PySys_AddAuditHook(Py_AuditHookFunction hook, void *userData)
389412
}
390413
}
391414

392-
_Py_AuditHookEntry *e = runtime->audit_hook_head;
393-
if (!e) {
394-
e = (_Py_AuditHookEntry*)PyMem_RawMalloc(sizeof(_Py_AuditHookEntry));
395-
runtime->audit_hook_head = e;
396-
} else {
397-
while (e->next) {
398-
e = e->next;
399-
}
400-
e = e->next = (_Py_AuditHookEntry*)PyMem_RawMalloc(
415+
_Py_AuditHookEntry *e = (_Py_AuditHookEntry*)PyMem_RawMalloc(
401416
sizeof(_Py_AuditHookEntry));
402-
}
403-
404417
if (!e) {
405418
if (tstate != NULL) {
406419
_PyErr_NoMemory(tstate);
407420
}
408421
return -1;
409422
}
410-
411423
e->next = NULL;
412424
e->hookCFunction = (Py_AuditHookFunction)hook;
413425
e->userData = userData;
414426

427+
if (runtime->audit_hooks.mutex == NULL) {
428+
/* The runtime must not be initailized yet. */
429+
add_audit_hook_entry_unlocked(runtime, e);
430+
}
431+
else {
432+
PyThread_acquire_lock(runtime->audit_hooks.mutex, WAIT_LOCK);
433+
add_audit_hook_entry_unlocked(runtime, e);
434+
PyThread_release_lock(runtime->audit_hooks.mutex);
435+
}
436+
415437
return 0;
416438
}
417439

0 commit comments

Comments
 (0)
0