mirror of
https://github.com/python/cpython.git
synced 2025-09-10 10:47:34 +00:00
[3.12] gh-100227: Lock Around Adding Global Audit Hooks (gh-105515) (gh-105525)
The risk of a race with this state is relatively low, but we play it safe anyway.
(cherry picked from commit e822a676f1
)
This commit is contained in:
parent
2ad2bd8b14
commit
b08ea9a561
4 changed files with 1334 additions and 1300 deletions
File diff suppressed because it is too large
Load diff
|
@ -143,7 +143,10 @@ typedef struct pyruntimestate {
|
||||||
// is called multiple times.
|
// is called multiple times.
|
||||||
Py_OpenCodeHookFunction open_code_hook;
|
Py_OpenCodeHookFunction open_code_hook;
|
||||||
void *open_code_userdata;
|
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_object_runtime_state object_state;
|
||||||
struct _Py_float_runtime_state float_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);
|
static const _PyRuntimeState initial = _PyRuntimeState_INIT(_PyRuntime);
|
||||||
_Py_COMP_DIAG_POP
|
_Py_COMP_DIAG_POP
|
||||||
|
|
||||||
#define NUMLOCKS 6
|
#define NUMLOCKS 7
|
||||||
#define LOCKS_INIT(runtime) \
|
#define LOCKS_INIT(runtime) \
|
||||||
{ \
|
{ \
|
||||||
&(runtime)->interpreters.mutex, \
|
&(runtime)->interpreters.mutex, \
|
||||||
|
@ -389,6 +389,7 @@ _Py_COMP_DIAG_POP
|
||||||
&(runtime)->unicode_state.ids.lock, \
|
&(runtime)->unicode_state.ids.lock, \
|
||||||
&(runtime)->imports.extensions.mutex, \
|
&(runtime)->imports.extensions.mutex, \
|
||||||
&(runtime)->atexit.mutex, \
|
&(runtime)->atexit.mutex, \
|
||||||
|
&(runtime)->audit_hooks.mutex, \
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
@ -432,7 +433,7 @@ init_runtime(_PyRuntimeState *runtime,
|
||||||
|
|
||||||
runtime->open_code_hook = open_code_hook;
|
runtime->open_code_hook = open_code_hook;
|
||||||
runtime->open_code_userdata = open_code_userdata;
|
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);
|
PyPreConfig_InitPythonConfig(&runtime->preconfig);
|
||||||
|
|
||||||
|
@ -458,7 +459,7 @@ _PyRuntimeState_Init(_PyRuntimeState *runtime)
|
||||||
initialization and interpreter initialization. */
|
initialization and interpreter initialization. */
|
||||||
void *open_code_hook = runtime->open_code_hook;
|
void *open_code_hook = runtime->open_code_hook;
|
||||||
void *open_code_userdata = runtime->open_code_userdata;
|
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()
|
// bpo-42882: Preserve next_index value if Py_Initialize()/Py_Finalize()
|
||||||
// is called multiple times.
|
// is called multiple times.
|
||||||
Py_ssize_t unicode_next_index = runtime->unicode_state.ids.next_index;
|
Py_ssize_t unicode_next_index = runtime->unicode_state.ids.next_index;
|
||||||
|
|
|
@ -168,7 +168,7 @@ should_audit(PyInterpreterState *interp)
|
||||||
if (!interp) {
|
if (!interp) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return (interp->runtime->audit_hook_head
|
return (interp->runtime->audit_hooks.head
|
||||||
|| interp->audit_hooks
|
|| interp->audit_hooks
|
||||||
|| PyDTrace_AUDIT_ENABLED());
|
|| PyDTrace_AUDIT_ENABLED());
|
||||||
}
|
}
|
||||||
|
@ -224,8 +224,11 @@ sys_audit_tstate(PyThreadState *ts, const char *event,
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Call global hooks */
|
/* Call global hooks
|
||||||
_Py_AuditHookEntry *e = is->runtime->audit_hook_head;
|
*
|
||||||
|
* 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) {
|
for (; e; e = e->next) {
|
||||||
if (e->hookCFunction(event, eventArgs, e->userData) < 0) {
|
if (e->hookCFunction(event, eventArgs, e->userData) < 0) {
|
||||||
goto exit;
|
goto exit;
|
||||||
|
@ -353,8 +356,12 @@ _PySys_ClearAuditHooks(PyThreadState *ts)
|
||||||
_PySys_Audit(ts, "cpython._PySys_ClearAuditHooks", NULL);
|
_PySys_Audit(ts, "cpython._PySys_ClearAuditHooks", NULL);
|
||||||
_PyErr_Clear(ts);
|
_PyErr_Clear(ts);
|
||||||
|
|
||||||
_Py_AuditHookEntry *e = runtime->audit_hook_head, *n;
|
/* We don't worry about the very unlikely race right here,
|
||||||
runtime->audit_hook_head = NULL;
|
* 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) {
|
while (e) {
|
||||||
n = e->next;
|
n = e->next;
|
||||||
PyMem_RawFree(e);
|
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
|
int
|
||||||
PySys_AddAuditHook(Py_AuditHookFunction hook, void *userData)
|
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;
|
_Py_AuditHookEntry *e = (_Py_AuditHookEntry*)PyMem_RawMalloc(
|
||||||
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(
|
|
||||||
sizeof(_Py_AuditHookEntry));
|
sizeof(_Py_AuditHookEntry));
|
||||||
}
|
|
||||||
|
|
||||||
if (!e) {
|
if (!e) {
|
||||||
if (tstate != NULL) {
|
if (tstate != NULL) {
|
||||||
_PyErr_NoMemory(tstate);
|
_PyErr_NoMemory(tstate);
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
e->next = NULL;
|
e->next = NULL;
|
||||||
e->hookCFunction = (Py_AuditHookFunction)hook;
|
e->hookCFunction = (Py_AuditHookFunction)hook;
|
||||||
e->userData = userData;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue