Gentoo Archives: gentoo-portage-dev

From: Zac Medico <zmedico@g.o>
To: gentoo-portage-dev@l.g.o
Cc: Zac Medico <zmedico@g.o>
Subject: [gentoo-portage-dev] [PATCH v2] pid-ns-init: fix child process signal disposition (bug 675828)
Date: Sun, 20 Jan 2019 19:37:04
Message-Id: 20190120193447.24478-1-zmedico@gentoo.org
In Reply to: [gentoo-portage-dev] [PATCH] pid-ns-init: fix child process signal disposition (bug 675828) by Zac Medico
1 Use subprocess.Popen to correctly configure the signal disposition
2 of the child process, since os.fork leaves the signal disposition
3 in a state which may be inappropriate for various signals including
4 SIGPIPE, SIGQUIT, SIGTERM, and SIGINT. For python implementations
5 other that CPython >= 3, use preexec_fn to manually configure the
6 signal disposition (I have found that this is necessary for CPython
7 2.7 and all PyPy versions tested, including PyPy3).
8
9 Bug: https://bugs.gentoo.org/675828
10 Signed-off-by: Zac Medico <zmedico@g.o>
11 ---
12 [PATCH v2] fix to use Popen pass_fds parameter when appropriate
13
14 bin/pid-ns-init | 41 +++++++++++++++++++++++++++++++++++------
15 lib/portage/process.py | 1 +
16 2 files changed, 36 insertions(+), 6 deletions(-)
17
18 diff --git a/bin/pid-ns-init b/bin/pid-ns-init
19 index 182d00a43..271a32855 100644
20 --- a/bin/pid-ns-init
21 +++ b/bin/pid-ns-init
22 @@ -2,25 +2,44 @@
23 # Copyright 2018-2019 Gentoo Authors
24 # Distributed under the terms of the GNU General Public License v2
25
26 +import errno
27 import functools
28 import os
29 +import platform
30 import signal
31 +import subprocess
32 import sys
33
34
35 +if sys.version_info.major < 3 or platform.python_implementation() != 'CPython':
36 + def signal_disposition_preexec():
37 + for signum in (
38 + signal.SIGHUP,
39 + signal.SIGINT,
40 + signal.SIGPIPE,
41 + signal.SIGQUIT,
42 + signal.SIGTERM,
43 + ):
44 + signal.signal(signum, signal.SIG_DFL)
45 +else:
46 + # CPython >= 3 subprocess.Popen handles this internally.
47 + signal_disposition_preexec = None
48 +
49 +
50 KILL_SIGNALS = (
51 signal.SIGINT,
52 signal.SIGTERM,
53 signal.SIGHUP,
54 )
55
56 +
57 def forward_kill_signal(main_child_pid, signum, frame):
58 os.kill(main_child_pid, signum)
59
60
61 def main(argv):
62 if len(argv) < 2:
63 - return 'Usage: {} <main-child-pid> or <binary> <argv0> [arg]..'.format(argv[0])
64 + return 'Usage: {} <main-child-pid> or <pass_fds> <binary> <argv0> [arg]..'.format(argv[0])
65
66 if len(argv) == 2:
67 # The child process is init (pid 1) in a child pid namespace, and
68 @@ -28,14 +47,19 @@ def main(argv):
69 # (forwarding signals to init and forwarding exit status to the parent
70 # process).
71 main_child_pid = int(argv[1])
72 + proc = None
73 else:
74 # The current process is init (pid 1) in a child pid namespace.
75 - binary = argv[1]
76 - args = argv[2:]
77 + pass_fds = [int(fd) for fd in argv[1].split(',')]
78 + binary = argv[2]
79 + args = argv[3:]
80
81 - main_child_pid = os.fork()
82 - if main_child_pid == 0:
83 - os.execv(binary, args)
84 + popen_kwargs = {}
85 + if sys.version_info.major > 2:
86 + popen_kwargs['pass_fds'] = pass_fds
87 + proc = subprocess.Popen(args, executable=binary,
88 + preexec_fn=signal_disposition_preexec, **popen_kwargs)
89 + main_child_pid = proc.pid
90
91 sig_handler = functools.partial(forward_kill_signal, main_child_pid)
92 for signum in KILL_SIGNALS:
93 @@ -50,6 +74,11 @@ def main(argv):
94 continue
95 raise
96 if pid == main_child_pid:
97 + if proc is not None:
98 + # Suppress warning messages like this:
99 + # ResourceWarning: subprocess 1234 is still running
100 + proc.returncode = 0
101 +
102 if os.WIFEXITED(status):
103 return os.WEXITSTATUS(status)
104 elif os.WIFSIGNALED(status):
105 diff --git a/lib/portage/process.py b/lib/portage/process.py
106 index 6af3ac37d..dd3d58ddc 100644
107 --- a/lib/portage/process.py
108 +++ b/lib/portage/process.py
109 @@ -571,6 +571,7 @@ def _exec(binary, mycommand, opt_name, fd_pipes,
110 portage._python_interpreter,
111 os.path.join(portage._bin_path,
112 'pid-ns-init'),
113 + _unicode_encode(','.join(str(fd) for fd in fd_pipes)),
114 binary] + myargs
115 else:
116 # Execute a supervisor process which will forward
117 --
118 2.18.1