summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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)
{