summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric Snow <ericsnowcurrently@gmail.com>2018-09-14 21:17:20 (GMT)
committerGitHub <noreply@github.com>2018-09-14 21:17:20 (GMT)
commit5903296045b586b9cd1fce0b1e02caf896028d1d (patch)
tree99e8900633096060e931980d52e063c044870d3c
parent3faaa8857a42a36383bb18425444e597fc876797 (diff)
downloadcpython-5903296045b586b9cd1fce0b1e02caf896028d1d.zip
cpython-5903296045b586b9cd1fce0b1e02caf896028d1d.tar.gz
cpython-5903296045b586b9cd1fce0b1e02caf896028d1d.tar.bz2
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
-rw-r--r--Include/internal/pystate.h1
-rw-r--r--Lib/test/test__xxsubinterpreters.py17
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2018-09-13-12-21-08.bpo-34651.v-bUeV.rst3
-rw-r--r--Modules/_posixsubprocess.c5
-rw-r--r--Modules/posixmodule.c14
-rw-r--r--Python/pystate.c38
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)
{