]> granicus.if.org Git - python/commitdiff
bpo-34651: Only allow the main interpreter to fork. (gh-9279)
authorEric Snow <ericsnowcurrently@gmail.com>
Fri, 14 Sep 2018 21:17:20 +0000 (14:17 -0700)
committerGitHub <noreply@github.com>
Fri, 14 Sep 2018 21:17:20 +0000 (14:17 -0700)
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

Include/internal/pystate.h
Lib/test/test__xxsubinterpreters.py
Misc/NEWS.d/next/Core and Builtins/2018-09-13-12-21-08.bpo-34651.v-bUeV.rst [new file with mode: 0644]
Modules/_posixsubprocess.c
Modules/posixmodule.c
Python/pystate.c

index ef83af59323e1ee735dac52b1c72a1cbb7754429..c93dda28954ae87bea4aa3261cf9873d90b19a17 100644 (file)
@@ -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
 }
index ac76cc198ad70d605baf5fd1e5f04fb022a572b6..26032d6c85f2a51e16986bee0087dd1d2917bfcc 100644 (file)
@@ -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 (file)
index 0000000..a3f0132
--- /dev/null
@@ -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.
index dd69b9eb1ff6c991373452ebb4e0678fcc069f68..9661e38540eda41b28532805367c767a77615627 100644 (file)
@@ -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;
index 2fddd9587f76b985bfff84285c2bccc86d618ef1..0ac0042f124dd8b1b6d3c67a858782fb167b80c9 100644 (file)
@@ -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) {
index 7d63f4febb932bb28f944b890586c91b6a53ccc8..7b3d3d4c9032590869638a8d3371b23422407bd4 100644 (file)
@@ -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)
 {