Fix exec in prewarm forked process sometimes inheriting env vars from grandparent process

This commit is contained in:
Kovid Goyal 2022-08-30 19:03:32 +05:30
parent 1ba027c277
commit 89a0c04d19
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
4 changed files with 35 additions and 6 deletions

View File

@ -181,8 +181,26 @@ spawn(PyObject *self UNUSED, PyObject *args) {
return PyLong_FromLong(pid); return PyLong_FromLong(pid);
} }
#ifdef __APPLE__
#include <crt_externs.h>
#else
extern char **environ;
#endif
static PyObject*
clearenv_py(PyObject *self UNUSED, PyObject *args UNUSED) {
#ifdef __APPLE__
char **e = *_NSGetEnviron();
if (e) *e = NULL;
#else
if (environ) *environ = NULL;
#endif
Py_RETURN_NONE;
}
static PyMethodDef module_methods[] = { static PyMethodDef module_methods[] = {
METHODB(spawn, METH_VARARGS), METHODB(spawn, METH_VARARGS),
{"clearenv", clearenv_py, METH_NOARGS, ""},
{NULL, NULL, 0, NULL} /* Sentinel */ {NULL, NULL, 0, NULL} /* Sentinel */
}; };

View File

@ -1485,3 +1485,4 @@ class SingleKey:
def set_use_os_log(yes: bool) -> None: ... def set_use_os_log(yes: bool) -> None: ...
def get_docs_ref_map() -> bytes: ... def get_docs_ref_map() -> bytes: ...
def clearenv() -> None: ...

View File

@ -24,9 +24,9 @@ from typing import (
from kitty.constants import kitty_exe, running_in_kitty from kitty.constants import kitty_exe, running_in_kitty
from kitty.entry_points import main as main_entry_point from kitty.entry_points import main as main_entry_point
from kitty.fast_data_types import ( from kitty.fast_data_types import (
CLD_EXITED, CLD_KILLED, CLD_STOPPED, get_options, install_signal_handlers, CLD_EXITED, CLD_KILLED, CLD_STOPPED, clearenv, get_options,
read_signals, remove_signal_handlers, safe_pipe, set_options, install_signal_handlers, read_signals, remove_signal_handlers, safe_pipe,
set_use_os_log set_options, set_use_os_log
) )
from kitty.options.types import Options from kitty.options.types import Options
from kitty.shm import SharedMemory from kitty.shm import SharedMemory
@ -293,6 +293,13 @@ def child_main(cmd: Dict[str, Any], ready_fd: int = -1, prewarm_type: str = 'dir
env = cmd.get('env') env = cmd.get('env')
if env is not None: if env is not None:
os.environ.clear() os.environ.clear()
# os.environ.clear() does not delete all existing env vars from the
# libc environ pointer in some circumstances, I havent figured out
# which exactly. Presumably there is something that alters the
# libc environ pointer?? The environ pointer is used by os.exec and
# therefore by subprocess and friends, so we need to ensure it is
# cleared.
clearenv()
os.environ.update(env) os.environ.update(env)
argv = cmd.get('argv') argv = cmd.get('argv')
if argv: if argv:

View File

@ -28,13 +28,15 @@ class Prewarm(BaseTest):
cwd = tempfile.gettempdir() cwd = tempfile.gettempdir()
env = {'TEST_ENV_PASS': 'xyz'} env = {'TEST_ENV_PASS': 'xyz'}
cols = 117 cols = 317
stdin_data = 'from_stdin' stdin_data = 'from_stdin'
pty = self.create_pty(cols=cols) pty = self.create_pty(cols=cols)
ttyname = os.ttyname(pty.slave_fd) ttyname = os.ttyname(pty.slave_fd)
opts = get_options() opts = get_options()
opts.config_overrides = 'font_family prewarm', opts.config_overrides = 'font_family prewarm',
os.environ['SHOULD_NOT_BE_PRESENT'] = '1'
p = fork_prewarm_process(opts, use_exec=True) p = fork_prewarm_process(opts, use_exec=True)
del os.environ['SHOULD_NOT_BE_PRESENT']
if p is None: if p is None:
return return
p.take_from_worker_fd(create_file=True) p.take_from_worker_fd(create_file=True)
@ -44,7 +46,7 @@ import os, json; from kitty.utils import *; from kitty.fast_data_types import ge
'ttyname': os.ttyname(sys.stdout.fileno()), 'ttyname': os.ttyname(sys.stdout.fileno()),
'cols': read_screen_size().cols, 'cols': read_screen_size().cols,
'cwd': os.getcwd(), 'cwd': os.getcwd(),
'env': os.environ.get('TEST_ENV_PASS'), 'env': os.environ.copy(),
'pid': os.getpid(), 'pid': os.getpid(),
'font_family': get_options().font_family, 'font_family': get_options().font_family,
'stdin': sys.stdin.read(), 'stdin': sys.stdin.read(),
@ -59,7 +61,8 @@ import os, json; from kitty.utils import *; from kitty.fast_data_types import ge
self.assertTrue(data['cterm']) self.assertTrue(data['cterm'])
self.ae(data['ttyname'], ttyname) self.ae(data['ttyname'], ttyname)
self.ae(os.path.realpath(data['cwd']), os.path.realpath(cwd)) self.ae(os.path.realpath(data['cwd']), os.path.realpath(cwd))
self.ae(data['env'], env['TEST_ENV_PASS']) self.ae(data['env']['TEST_ENV_PASS'], env['TEST_ENV_PASS'])
self.assertNotIn('SHOULD_NOT_BE_PRESENT', data['env'])
self.ae(data['font_family'], 'prewarm') self.ae(data['font_family'], 'prewarm')
self.ae(int(p.from_worker.readline()), data['pid']) self.ae(int(p.from_worker.readline()), data['pid'])