Skip to content

Commit 5903296

Browse files
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
1 parent 3faaa88 commit 5903296

File tree

6 files changed

+64
-14
lines changed

6 files changed

+64
-14
lines changed

Include/internal/pystate.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,7 @@ PyAPI_FUNC(_PyInitError) _PyRuntime_Initialize(void);
218218
/* Other */
219219

220220
PyAPI_FUNC(_PyInitError) _PyInterpreterState_Enable(_PyRuntimeState *);
221+
PyAPI_FUNC(void) _PyInterpreterState_DeleteExceptMain(void);
221222

222223
#ifdef __cplusplus
223224
}

Lib/test/test__xxsubinterpreters.py

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -824,23 +824,12 @@ def test_fork(self):
824824

825825
expected = 'spam spam spam spam spam'
826826
script = dedent(f"""
827-
# (inspired by Lib/test/test_fork.py)
828827
import os
829-
pid = os.fork()
830-
if pid == 0: # child
828+
try:
829+
os.fork()
830+
except RuntimeError:
831831
with open('{file.name}', 'w') as out:
832832
out.write('{expected}')
833-
# Kill the unittest runner in the child process.
834-
os._exit(1)
835-
else:
836-
SHORT_SLEEP = 0.1
837-
import time
838-
for _ in range(10):
839-
spid, status = os.waitpid(pid, os.WNOHANG)
840-
if spid == pid:
841-
break
842-
time.sleep(SHORT_SLEEP)
843-
assert(spid == pid)
844833
""")
845834
interpreters.run_string(self.id, script)
846835

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Only allow the main interpreter to fork. The avoids the possibility of
2+
affecting the main interprerter, which is critical to operation of the
3+
runtime.

Modules/_posixsubprocess.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -576,6 +576,11 @@ subprocess_fork_exec(PyObject* self, PyObject *args)
576576
&restore_signals, &call_setsid, &preexec_fn))
577577
return NULL;
578578

579+
if (_PyInterpreterState_Get() != PyInterpreterState_Main()) {
580+
PyErr_SetString(PyExc_RuntimeError, "fork not supported for subinterpreters");
581+
return NULL;
582+
}
583+
579584
if (close_fds && errpipe_write < 3) { /* precondition */
580585
PyErr_SetString(PyExc_ValueError, "errpipe_write must be >= 3");
581586
return NULL;

Modules/posixmodule.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#else
3333
#include "winreparse.h"
3434
#endif
35+
#include "internal/pystate.h"
3536

3637
/* On android API level 21, 'AT_EACCESS' is not declared although
3738
* HAVE_FACCESSAT is defined. */
@@ -420,6 +421,7 @@ void
420421
PyOS_AfterFork_Child(void)
421422
{
422423
_PyGILState_Reinit();
424+
_PyInterpreterState_DeleteExceptMain();
423425
PyEval_ReInitThreads();
424426
_PyImport_ReInitLock();
425427
_PySignal_AfterFork();
@@ -5790,6 +5792,10 @@ os_fork1_impl(PyObject *module)
57905792
{
57915793
pid_t pid;
57925794

5795+
if (_PyInterpreterState_Get() != PyInterpreterState_Main()) {
5796+
PyErr_SetString(PyExc_RuntimeError, "fork not supported for subinterpreters");
5797+
return NULL;
5798+
}
57935799
PyOS_BeforeFork();
57945800
pid = fork1();
57955801
if (pid == 0) {
@@ -5821,6 +5827,10 @@ os_fork_impl(PyObject *module)
58215827
{
58225828
pid_t pid;
58235829

5830+
if (_PyInterpreterState_Get() != PyInterpreterState_Main()) {
5831+
PyErr_SetString(PyExc_RuntimeError, "fork not supported for subinterpreters");
5832+
return NULL;
5833+
}
58245834
PyOS_BeforeFork();
58255835
pid = fork();
58265836
if (pid == 0) {
@@ -6416,6 +6426,10 @@ os_forkpty_impl(PyObject *module)
64166426
int master_fd = -1;
64176427
pid_t pid;
64186428

6429+
if (_PyInterpreterState_Get() != PyInterpreterState_Main()) {
6430+
PyErr_SetString(PyExc_RuntimeError, "fork not supported for subinterpreters");
6431+
return NULL;
6432+
}
64196433
PyOS_BeforeFork();
64206434
pid = forkpty(&master_fd, NULL, NULL, NULL);
64216435
if (pid == 0) {

Python/pystate.c

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,44 @@ PyInterpreterState_Delete(PyInterpreterState *interp)
268268
}
269269

270270

271+
/*
272+
* Delete all interpreter states except the main interpreter. If there
273+
* is a current interpreter state, it *must* be the main interpreter.
274+
*/
275+
void
276+
_PyInterpreterState_DeleteExceptMain()
277+
{
278+
PyThreadState *tstate = PyThreadState_Swap(NULL);
279+
if (tstate != NULL && tstate->interp != _PyRuntime.interpreters.main) {
280+
Py_FatalError("PyInterpreterState_DeleteExceptMain: not main interpreter");
281+
}
282+
283+
HEAD_LOCK();
284+
PyInterpreterState *interp = _PyRuntime.interpreters.head;
285+
_PyRuntime.interpreters.head = NULL;
286+
for (; interp != NULL; interp = interp->next) {
287+
if (interp == _PyRuntime.interpreters.main) {
288+
_PyRuntime.interpreters.main->next = NULL;
289+
_PyRuntime.interpreters.head = interp;
290+
continue;
291+
}
292+
293+
PyInterpreterState_Clear(interp); // XXX must activate?
294+
zapthreads(interp);
295+
if (interp->id_mutex != NULL) {
296+
PyThread_free_lock(interp->id_mutex);
297+
}
298+
PyMem_RawFree(interp);
299+
}
300+
HEAD_UNLOCK();
301+
302+
if (_PyRuntime.interpreters.head == NULL) {
303+
Py_FatalError("PyInterpreterState_DeleteExceptMain: missing main");
304+
}
305+
PyThreadState_Swap(tstate);
306+
}
307+
308+
271309
PyInterpreterState *
272310
_PyInterpreterState_Get(void)
273311
{

0 commit comments

Comments
 (0)