diff options
-rw-r--r-- | Include/internal/pystate.h | 1 | ||||
-rw-r--r-- | Lib/test/test__xxsubinterpreters.py | 17 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Core and Builtins/2018-09-13-12-21-08.bpo-34651.v-bUeV.rst | 3 | ||||
-rw-r--r-- | Modules/_posixsubprocess.c | 5 | ||||
-rw-r--r-- | Modules/posixmodule.c | 14 | ||||
-rw-r--r-- | Python/pystate.c | 38 |
6 files changed, 64 insertions, 14 deletions
diff --git a/Include/internal/pystate.h b/Include/internal/pystate.h index ef83af5..c93dda2 100644 --- a/Include/internal/pystate.h +++ b/Include/internal/pystate.h @@ -218,6 +218,7 @@ PyAPI_FUNC(_PyInitError) _PyRuntime_Initialize(void); /* Other */ PyAPI_FUNC(_PyInitError) _PyInterpreterState_Enable(_PyRuntimeState *); +PyAPI_FUNC(void) _PyInterpreterState_DeleteExceptMain(void); #ifdef __cplusplus } diff --git a/Lib/test/test__xxsubinterpreters.py b/Lib/test/test__xxsubinterpreters.py index ac76cc1..26032d6 100644 --- a/Lib/test/test__xxsubinterpreters.py +++ b/Lib/test/test__xxsubinterpreters.py @@ -824,23 +824,12 @@ class RunStringTests(TestBase): expected = 'spam spam spam spam spam' script = dedent(f""" - # (inspired by Lib/test/test_fork.py) import os - pid = os.fork() - if pid == 0: # child + try: + os.fork() + except RuntimeError: with open('{file.name}', 'w') as out: out.write('{expected}') - # Kill the unittest runner in the child process. - os._exit(1) - else: - SHORT_SLEEP = 0.1 - import time - for _ in range(10): - spid, status = os.waitpid(pid, os.WNOHANG) - if spid == pid: - break - time.sleep(SHORT_SLEEP) - assert(spid == pid) """) interpreters.run_string(self.id, script) diff --git a/Misc/NEWS.d/next/Core and Builtins/2018-09-13-12-21-08.bpo-34651.v-bUeV.rst b/Misc/NEWS.d/next/Core and Builtins/2018-09-13-12-21-08.bpo-34651.v-bUeV.rst new file mode 100644 index 0000000..a3f0132 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2018-09-13-12-21-08.bpo-34651.v-bUeV.rst @@ -0,0 +1,3 @@ +Only allow the main interpreter to fork. The avoids the possibility of +affecting the main interprerter, which is critical to operation of the +runtime. diff --git a/Modules/_posixsubprocess.c b/Modules/_posixsubprocess.c index dd69b9e..9661e38 100644 --- a/Modules/_posixsubprocess.c +++ b/Modules/_posixsubprocess.c @@ -576,6 +576,11 @@ subprocess_fork_exec(PyObject* self, PyObject *args) &restore_signals, &call_setsid, &preexec_fn)) return NULL; + if (_PyInterpreterState_Get() != PyInterpreterState_Main()) { + PyErr_SetString(PyExc_RuntimeError, "fork not supported for subinterpreters"); + return NULL; + } + if (close_fds && errpipe_write < 3) { /* precondition */ PyErr_SetString(PyExc_ValueError, "errpipe_write must be >= 3"); return NULL; diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 2fddd95..0ac0042 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -32,6 +32,7 @@ #else #include "winreparse.h" #endif +#include "internal/pystate.h" /* On android API level 21, 'AT_EACCESS' is not declared although * HAVE_FACCESSAT is defined. */ @@ -420,6 +421,7 @@ void PyOS_AfterFork_Child(void) { _PyGILState_Reinit(); + _PyInterpreterState_DeleteExceptMain(); PyEval_ReInitThreads(); _PyImport_ReInitLock(); _PySignal_AfterFork(); @@ -5790,6 +5792,10 @@ os_fork1_impl(PyObject *module) { pid_t pid; + if (_PyInterpreterState_Get() != PyInterpreterState_Main()) { + PyErr_SetString(PyExc_RuntimeError, "fork not supported for subinterpreters"); + return NULL; + } PyOS_BeforeFork(); pid = fork1(); if (pid == 0) { @@ -5821,6 +5827,10 @@ os_fork_impl(PyObject *module) { pid_t pid; + if (_PyInterpreterState_Get() != PyInterpreterState_Main()) { + PyErr_SetString(PyExc_RuntimeError, "fork not supported for subinterpreters"); + return NULL; + } PyOS_BeforeFork(); pid = fork(); if (pid == 0) { @@ -6416,6 +6426,10 @@ os_forkpty_impl(PyObject *module) int master_fd = -1; pid_t pid; + if (_PyInterpreterState_Get() != PyInterpreterState_Main()) { + PyErr_SetString(PyExc_RuntimeError, "fork not supported for subinterpreters"); + return NULL; + } PyOS_BeforeFork(); pid = forkpty(&master_fd, NULL, NULL, NULL); if (pid == 0) { diff --git a/Python/pystate.c b/Python/pystate.c index 7d63f4f..7b3d3d4 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -268,6 +268,44 @@ PyInterpreterState_Delete(PyInterpreterState *interp) } +/* + * Delete all interpreter states except the main interpreter. If there + * is a current interpreter state, it *must* be the main interpreter. + */ +void +_PyInterpreterState_DeleteExceptMain() +{ + PyThreadState *tstate = PyThreadState_Swap(NULL); + if (tstate != NULL && tstate->interp != _PyRuntime.interpreters.main) { + Py_FatalError("PyInterpreterState_DeleteExceptMain: not main interpreter"); + } + + HEAD_LOCK(); + PyInterpreterState *interp = _PyRuntime.interpreters.head; + _PyRuntime.interpreters.head = NULL; + for (; interp != NULL; interp = interp->next) { + if (interp == _PyRuntime.interpreters.main) { + _PyRuntime.interpreters.main->next = NULL; + _PyRuntime.interpreters.head = interp; + continue; + } + + PyInterpreterState_Clear(interp); // XXX must activate? + zapthreads(interp); + if (interp->id_mutex != NULL) { + PyThread_free_lock(interp->id_mutex); + } + PyMem_RawFree(interp); + } + HEAD_UNLOCK(); + + if (_PyRuntime.interpreters.head == NULL) { + Py_FatalError("PyInterpreterState_DeleteExceptMain: missing main"); + } + PyThreadState_Swap(tstate); +} + + PyInterpreterState * _PyInterpreterState_Get(void) { |