Skip to content

Commit be2e1f0

Browse files
committed
gh-144503: Send initialization data for preload over pipe instead of argv
The allowed size for argv is limited, while the amount of data that can be sent over a pipe is virtually unlimited.
1 parent 0507aa0 commit be2e1f0

File tree

1 file changed

+36
-36
lines changed

1 file changed

+36
-36
lines changed

Lib/multiprocessing/forkserver.py

Lines changed: 36 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import atexit
22
import errno
3+
import json
34
import os
45
import selectors
56
import signal
@@ -163,17 +164,19 @@ def ensure_running(self):
163164
self._forkserver_pid = None
164165

165166
cmd = ('from multiprocessing.forkserver import main; ' +
166-
'main(%d, %d, %r, **%r)')
167+
'main(listener_fd=%d, alive_r=%d, init_r=%d)')
167168

168-
main_kws = {}
169169
if self._preload_modules:
170170
data = spawn.get_preparation_data('ignore')
171-
if 'sys_path' in data:
172-
main_kws['sys_path'] = data['sys_path']
173-
if 'init_main_from_path' in data:
174-
main_kws['main_path'] = data['init_main_from_path']
175-
if self._preload_on_error != 'ignore':
176-
main_kws['on_error'] = self._preload_on_error
171+
preload_kwargs = {
172+
"preload": self._preload_modules,
173+
"sys_path": data["sys_path"],
174+
"main_path": data.get("init_main_from_path", None),
175+
"sys_argv": data["sys_argv"],
176+
"on_error": self._preload_on_error,
177+
}
178+
else:
179+
preload_kwargs = None
177180

178181
with socket.socket(socket.AF_UNIX) as listener:
179182
address = connection.arbitrary_address('AF_UNIX')
@@ -186,32 +189,31 @@ def ensure_running(self):
186189
# when they all terminate the read end becomes ready.
187190
alive_r, alive_w = os.pipe()
188191
# A short lived pipe to initialize the forkserver authkey.
189-
authkey_r, authkey_w = os.pipe()
192+
init_r, init_w = os.pipe()
190193
try:
191-
fds_to_pass = [listener.fileno(), alive_r, authkey_r]
192-
main_kws['authkey_r'] = authkey_r
193-
cmd %= (listener.fileno(), alive_r, self._preload_modules,
194-
main_kws)
194+
fds_to_pass = [listener.fileno(), alive_r, init_r]
195+
cmd %= (listener.fileno(), alive_r, init_r)
195196
exe = spawn.get_executable()
196197
args = [exe] + util._args_from_interpreter_flags()
197198
args += ['-c', cmd]
198-
if self._preload_modules:
199-
args += data["sys_argv"]
200199
pid = util.spawnv_passfds(exe, args, fds_to_pass)
201200
except:
202201
os.close(alive_w)
203-
os.close(authkey_w)
202+
os.close(init_w)
204203
raise
205204
finally:
206205
os.close(alive_r)
207-
os.close(authkey_r)
206+
os.close(init_r)
208207
# Authenticate our control socket to prevent access from
209208
# processes we have not shared this key with.
210209
try:
211210
self._forkserver_authkey = os.urandom(_AUTHKEY_LEN)
212-
os.write(authkey_w, self._forkserver_authkey)
211+
os.write(init_w, self._forkserver_authkey)
212+
preload_data = json.dumps(preload_kwargs).encode()
213+
os.write(init_w, struct.pack("Q", len(preload_data)))
214+
os.write(init_w, preload_data)
213215
finally:
214-
os.close(authkey_w)
216+
os.close(init_w)
215217
self._forkserver_address = address
216218
self._forkserver_alive_fd = alive_w
217219
self._forkserver_pid = pid
@@ -281,24 +283,22 @@ def _handle_preload(preload, main_path=None, sys_path=None, sys_argv=None,
281283
util._flush_std_streams()
282284

283285

284-
def main(listener_fd, alive_r, preload, main_path=None, sys_path=None,
285-
*, authkey_r=None, on_error='ignore'):
286+
def main(listener_fd, alive_r, init_r):
286287
"""Run forkserver."""
287-
if authkey_r is not None:
288-
try:
289-
authkey = os.read(authkey_r, _AUTHKEY_LEN)
290-
assert len(authkey) == _AUTHKEY_LEN, f'{len(authkey)} < {_AUTHKEY_LEN}'
291-
finally:
292-
os.close(authkey_r)
293-
else:
294-
authkey = b''
295-
296-
if preload:
297-
sys_argv = sys.argv[1:]
298-
else:
299-
sys_argv = None
300-
301-
_handle_preload(preload, main_path, sys_path, sys_argv, on_error)
288+
try:
289+
authkey = os.read(init_r, _AUTHKEY_LEN)
290+
assert len(authkey) == _AUTHKEY_LEN, f'{len(authkey)} < {_AUTHKEY_LEN}'
291+
292+
preload_data_len, = struct.unpack("Q", os.read(init_r, struct.calcsize("Q")))
293+
preload_data = b""
294+
while len(preload_data) < preload_data_len:
295+
preload_data += os.read(init_r, preload_data_len - len(preload_data))
296+
preload_kwargs = json.loads(preload_data.decode())
297+
finally:
298+
os.close(init_r)
299+
300+
if preload_kwargs:
301+
_handle_preload(**preload_kwargs)
302302

303303
util._close_stdin()
304304

0 commit comments

Comments
 (0)