mirror of
https://github.com/python/cpython.git
synced 2025-08-04 00:48:58 +00:00
gh-98610: Adjust the Optional Restrictions on Subinterpreters (GH-98618)
Previously, the optional restrictions on subinterpreters were: disallow fork, subprocess, and threads. By default, we were disallowing all three for "isolated" interpreters. We always allowed all three for the main interpreter and those created through the legacy `Py_NewInterpreter()` API. Those settings were a bit conservative, so here we've adjusted the optional restrictions to: fork, exec, threads, and daemon threads. The default for "isolated" interpreters disables fork, exec, and daemon threads. Regular threads are allowed by default. We continue always allowing everything For the main interpreter and the legacy API. In the code, we add `_PyInterpreterConfig.allow_exec` and `_PyInterpreterConfig.allow_daemon_threads`. We also add `Py_RTFLAGS_DAEMON_THREADS` and `Py_RTFLAGS_EXEC`.
This commit is contained in:
parent
3b86538661
commit
4702552885
15 changed files with 220 additions and 47 deletions
|
@ -825,8 +825,8 @@ subprocess_fork_exec(PyObject *module, PyObject *args)
|
|||
&preexec_fn, &allow_vfork))
|
||||
return NULL;
|
||||
|
||||
if ((preexec_fn != Py_None) &&
|
||||
(PyInterpreterState_Get() != PyInterpreterState_Main())) {
|
||||
PyInterpreterState *interp = PyInterpreterState_Get();
|
||||
if ((preexec_fn != Py_None) && (interp != PyInterpreterState_Main())) {
|
||||
PyErr_SetString(PyExc_RuntimeError,
|
||||
"preexec_fn not supported within subinterpreters");
|
||||
return NULL;
|
||||
|
@ -841,13 +841,6 @@ subprocess_fork_exec(PyObject *module, PyObject *args)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
PyInterpreterState *interp = PyInterpreterState_Get();
|
||||
if (!_PyInterpreterState_HasFeature(interp, Py_RTFLAGS_SUBPROCESS)) {
|
||||
PyErr_SetString(PyExc_RuntimeError,
|
||||
"subprocess not supported for isolated subinterpreters");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* We need to call gc.disable() when we'll be calling preexec_fn */
|
||||
if (preexec_fn != Py_None) {
|
||||
need_to_reenable_gc = PyGC_Disable();
|
||||
|
|
|
@ -3231,33 +3231,42 @@ run_in_subinterp_with_config(PyObject *self, PyObject *args, PyObject *kwargs)
|
|||
{
|
||||
const char *code;
|
||||
int allow_fork = -1;
|
||||
int allow_subprocess = -1;
|
||||
int allow_exec = -1;
|
||||
int allow_threads = -1;
|
||||
int allow_daemon_threads = -1;
|
||||
int r;
|
||||
PyThreadState *substate, *mainstate;
|
||||
/* only initialise 'cflags.cf_flags' to test backwards compatibility */
|
||||
PyCompilerFlags cflags = {0};
|
||||
|
||||
static char *kwlist[] = {"code",
|
||||
"allow_fork", "allow_subprocess", "allow_threads",
|
||||
"allow_fork",
|
||||
"allow_exec",
|
||||
"allow_threads",
|
||||
"allow_daemon_threads",
|
||||
NULL};
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwargs,
|
||||
"s$ppp:run_in_subinterp_with_config", kwlist,
|
||||
&code, &allow_fork, &allow_subprocess, &allow_threads)) {
|
||||
"s$pppp:run_in_subinterp_with_config", kwlist,
|
||||
&code, &allow_fork, &allow_exec,
|
||||
&allow_threads, &allow_daemon_threads)) {
|
||||
return NULL;
|
||||
}
|
||||
if (allow_fork < 0) {
|
||||
PyErr_SetString(PyExc_ValueError, "missing allow_fork");
|
||||
return NULL;
|
||||
}
|
||||
if (allow_subprocess < 0) {
|
||||
PyErr_SetString(PyExc_ValueError, "missing allow_subprocess");
|
||||
if (allow_exec < 0) {
|
||||
PyErr_SetString(PyExc_ValueError, "missing allow_exec");
|
||||
return NULL;
|
||||
}
|
||||
if (allow_threads < 0) {
|
||||
PyErr_SetString(PyExc_ValueError, "missing allow_threads");
|
||||
return NULL;
|
||||
}
|
||||
if (allow_daemon_threads < 0) {
|
||||
PyErr_SetString(PyExc_ValueError, "missing allow_daemon_threads");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mainstate = PyThreadState_Get();
|
||||
|
||||
|
@ -3265,8 +3274,9 @@ run_in_subinterp_with_config(PyObject *self, PyObject *args, PyObject *kwargs)
|
|||
|
||||
const _PyInterpreterConfig config = {
|
||||
.allow_fork = allow_fork,
|
||||
.allow_subprocess = allow_subprocess,
|
||||
.allow_exec = allow_exec,
|
||||
.allow_threads = allow_threads,
|
||||
.allow_daemon_threads = allow_daemon_threads,
|
||||
};
|
||||
substate = _Py_NewInterpreterFromConfig(&config);
|
||||
if (substate == NULL) {
|
||||
|
|
|
@ -1102,6 +1102,24 @@ thread_run(void *boot_raw)
|
|||
// to open the libgcc_s.so library (ex: EMFILE error).
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
thread_daemon_threads_allowed(PyObject *module, PyObject *Py_UNUSED(ignored))
|
||||
{
|
||||
PyInterpreterState *interp = _PyInterpreterState_Get();
|
||||
if (interp->feature_flags & Py_RTFLAGS_DAEMON_THREADS) {
|
||||
Py_RETURN_TRUE;
|
||||
}
|
||||
else {
|
||||
Py_RETURN_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(daemon_threads_allowed_doc,
|
||||
"daemon_threads_allowed()\n\
|
||||
\n\
|
||||
Return True if daemon threads are allowed in the current interpreter,\n\
|
||||
and False otherwise.\n");
|
||||
|
||||
static PyObject *
|
||||
thread_PyThread_start_new_thread(PyObject *self, PyObject *fargs)
|
||||
{
|
||||
|
@ -1543,6 +1561,8 @@ static PyMethodDef thread_methods[] = {
|
|||
METH_VARARGS, start_new_doc},
|
||||
{"start_new", (PyCFunction)thread_PyThread_start_new_thread,
|
||||
METH_VARARGS, start_new_doc},
|
||||
{"daemon_threads_allowed", (PyCFunction)thread_daemon_threads_allowed,
|
||||
METH_NOARGS, daemon_threads_allowed_doc},
|
||||
{"allocate_lock", thread_PyThread_allocate_lock,
|
||||
METH_NOARGS, allocate_doc},
|
||||
{"allocate", thread_PyThread_allocate_lock,
|
||||
|
|
|
@ -1089,13 +1089,6 @@ _winapi_CreateProcess_impl(PyObject *module,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
PyInterpreterState *interp = PyInterpreterState_Get();
|
||||
if (!_PyInterpreterState_HasFeature(interp, Py_RTFLAGS_SUBPROCESS)) {
|
||||
PyErr_SetString(PyExc_RuntimeError,
|
||||
"subprocess not supported for isolated subinterpreters");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ZeroMemory(&si, sizeof(si));
|
||||
si.StartupInfo.cb = sizeof(si);
|
||||
|
||||
|
|
|
@ -2003,11 +2003,9 @@ interp_create(PyObject *self, PyObject *args, PyObject *kwds)
|
|||
|
||||
// Create and initialize the new interpreter.
|
||||
PyThreadState *save_tstate = _PyThreadState_GET();
|
||||
const _PyInterpreterConfig config = {
|
||||
.allow_fork = !isolated,
|
||||
.allow_subprocess = !isolated,
|
||||
.allow_threads = !isolated,
|
||||
};
|
||||
const _PyInterpreterConfig config = isolated
|
||||
? (_PyInterpreterConfig)_PyInterpreterConfig_INIT
|
||||
: (_PyInterpreterConfig)_PyInterpreterConfig_LEGACY_INIT;
|
||||
// XXX Possible GILState issues?
|
||||
PyThreadState *tstate = _Py_NewInterpreterFromConfig(&config);
|
||||
PyThreadState_Swap(save_tstate);
|
||||
|
|
|
@ -5773,6 +5773,13 @@ os_execv_impl(PyObject *module, path_t *path, PyObject *argv)
|
|||
EXECV_CHAR **argvlist;
|
||||
Py_ssize_t argc;
|
||||
|
||||
PyInterpreterState *interp = _PyInterpreterState_GET();
|
||||
if (!_PyInterpreterState_HasFeature(interp, Py_RTFLAGS_EXEC)) {
|
||||
PyErr_SetString(PyExc_RuntimeError,
|
||||
"exec not supported for isolated subinterpreters");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* execv has two arguments: (path, argv), where
|
||||
argv is a list or tuple of strings. */
|
||||
|
||||
|
@ -5839,6 +5846,13 @@ os_execve_impl(PyObject *module, path_t *path, PyObject *argv, PyObject *env)
|
|||
EXECV_CHAR **envlist;
|
||||
Py_ssize_t argc, envc;
|
||||
|
||||
PyInterpreterState *interp = _PyInterpreterState_GET();
|
||||
if (!_PyInterpreterState_HasFeature(interp, Py_RTFLAGS_EXEC)) {
|
||||
PyErr_SetString(PyExc_RuntimeError,
|
||||
"exec not supported for isolated subinterpreters");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* execve has three arguments: (path, argv, env), where
|
||||
argv is a list or tuple of strings and env is a dictionary
|
||||
like posix.environ. */
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue