Unverified Kaydet (Commit) 59032960 authored tarafından Eric Snow's avatar Eric Snow Kaydeden (comit) GitHub

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
üst 3faaa885
...@@ -218,6 +218,7 @@ PyAPI_FUNC(_PyInitError) _PyRuntime_Initialize(void); ...@@ -218,6 +218,7 @@ PyAPI_FUNC(_PyInitError) _PyRuntime_Initialize(void);
/* Other */ /* Other */
PyAPI_FUNC(_PyInitError) _PyInterpreterState_Enable(_PyRuntimeState *); PyAPI_FUNC(_PyInitError) _PyInterpreterState_Enable(_PyRuntimeState *);
PyAPI_FUNC(void) _PyInterpreterState_DeleteExceptMain(void);
#ifdef __cplusplus #ifdef __cplusplus
} }
......
...@@ -824,23 +824,12 @@ class RunStringTests(TestBase): ...@@ -824,23 +824,12 @@ class RunStringTests(TestBase):
expected = 'spam spam spam spam spam' expected = 'spam spam spam spam spam'
script = dedent(f""" script = dedent(f"""
# (inspired by Lib/test/test_fork.py)
import os import os
pid = os.fork() try:
if pid == 0: # child os.fork()
except RuntimeError:
with open('{file.name}', 'w') as out: with open('{file.name}', 'w') as out:
out.write('{expected}') 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) interpreters.run_string(self.id, script)
......
Only allow the main interpreter to fork. The avoids the possibility of
affecting the main interprerter, which is critical to operation of the
runtime.
...@@ -576,6 +576,11 @@ subprocess_fork_exec(PyObject* self, PyObject *args) ...@@ -576,6 +576,11 @@ subprocess_fork_exec(PyObject* self, PyObject *args)
&restore_signals, &call_setsid, &preexec_fn)) &restore_signals, &call_setsid, &preexec_fn))
return NULL; 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 */ if (close_fds && errpipe_write < 3) { /* precondition */
PyErr_SetString(PyExc_ValueError, "errpipe_write must be >= 3"); PyErr_SetString(PyExc_ValueError, "errpipe_write must be >= 3");
return NULL; return NULL;
......
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
#else #else
#include "winreparse.h" #include "winreparse.h"
#endif #endif
#include "internal/pystate.h"
/* On android API level 21, 'AT_EACCESS' is not declared although /* On android API level 21, 'AT_EACCESS' is not declared although
* HAVE_FACCESSAT is defined. */ * HAVE_FACCESSAT is defined. */
...@@ -420,6 +421,7 @@ void ...@@ -420,6 +421,7 @@ void
PyOS_AfterFork_Child(void) PyOS_AfterFork_Child(void)
{ {
_PyGILState_Reinit(); _PyGILState_Reinit();
_PyInterpreterState_DeleteExceptMain();
PyEval_ReInitThreads(); PyEval_ReInitThreads();
_PyImport_ReInitLock(); _PyImport_ReInitLock();
_PySignal_AfterFork(); _PySignal_AfterFork();
...@@ -5790,6 +5792,10 @@ os_fork1_impl(PyObject *module) ...@@ -5790,6 +5792,10 @@ os_fork1_impl(PyObject *module)
{ {
pid_t pid; pid_t pid;
if (_PyInterpreterState_Get() != PyInterpreterState_Main()) {
PyErr_SetString(PyExc_RuntimeError, "fork not supported for subinterpreters");
return NULL;
}
PyOS_BeforeFork(); PyOS_BeforeFork();
pid = fork1(); pid = fork1();
if (pid == 0) { if (pid == 0) {
...@@ -5821,6 +5827,10 @@ os_fork_impl(PyObject *module) ...@@ -5821,6 +5827,10 @@ os_fork_impl(PyObject *module)
{ {
pid_t pid; pid_t pid;
if (_PyInterpreterState_Get() != PyInterpreterState_Main()) {
PyErr_SetString(PyExc_RuntimeError, "fork not supported for subinterpreters");
return NULL;
}
PyOS_BeforeFork(); PyOS_BeforeFork();
pid = fork(); pid = fork();
if (pid == 0) { if (pid == 0) {
...@@ -6416,6 +6426,10 @@ os_forkpty_impl(PyObject *module) ...@@ -6416,6 +6426,10 @@ os_forkpty_impl(PyObject *module)
int master_fd = -1; int master_fd = -1;
pid_t pid; pid_t pid;
if (_PyInterpreterState_Get() != PyInterpreterState_Main()) {
PyErr_SetString(PyExc_RuntimeError, "fork not supported for subinterpreters");
return NULL;
}
PyOS_BeforeFork(); PyOS_BeforeFork();
pid = forkpty(&master_fd, NULL, NULL, NULL); pid = forkpty(&master_fd, NULL, NULL, NULL);
if (pid == 0) { if (pid == 0) {
......
...@@ -268,6 +268,44 @@ PyInterpreterState_Delete(PyInterpreterState *interp) ...@@ -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 *
_PyInterpreterState_Get(void) _PyInterpreterState_Get(void)
{ {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment