summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Snow <ericsnowcurrently@gmail.com>2018-09-14 14:17:20 -0700
committerGitHub <noreply@github.com>2018-09-14 14:17:20 -0700
commit5903296045b586b9cd1fce0b1e02caf896028d1d (patch)
tree99e8900633096060e931980d52e063c044870d3c
parent3faaa8857a42a36383bb18425444e597fc876797 (diff)
downloadcpython-git-5903296045b586b9cd1fce0b1e02caf896028d1d.tar.gz
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 ef83af5932..c93dda2895 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 ac76cc198a..26032d6c85 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 0000000000..a3f0132fc1
--- /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 dd69b9eb1f..9661e38540 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 2fddd9587f..0ac0042f12 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 7d63f4febb..7b3d3d4c90 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)
{