From: Eric Snow Date: Fri, 14 Sep 2018 21:17:20 +0000 (-0700) Subject: bpo-34651: Only allow the main interpreter to fork. (gh-9279) X-Git-Tag: v3.8.0a1~984 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5903296045b586b9cd1fce0b1e02caf896028d1d;p=thirdparty%2FPython%2Fcpython.git bpo-34651: Only allow the main interpreter to fork. (gh-9279) When os.fork() is called (on platforms that support it) all threads but the current one are destroyed in the child process. Consequently we must ensure that all but the associated interpreter are likewise destroyed. The main interpreter is critical for runtime operation, so we must ensure that fork only happens in the main interpreter. https://bugs.python.org/issue34651 --- diff --git a/Include/internal/pystate.h b/Include/internal/pystate.h index ef83af59323e..c93dda28954a 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 ac76cc198ad7..26032d6c85f2 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 000000000000..a3f0132fc1d6 --- /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 dd69b9eb1ff6..9661e38540ed 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 2fddd9587f76..0ac0042f124d 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 7d63f4febb93..7b3d3d4c9032 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) {