cli: don't call setenv() after fork

setenv() cannot be called after fork, because it might allocate memory,
which can deadlock.

Instead, prepare the environment and use execvpe().

`man 2 fork` says:

  After a fork() in a multithreaded program, the child can safely call
  only async-signal-safe functions (see signal-safety(7)) until such time
  as it calls execve(2).

This means, we are quite strongly limited what can be done in the child
process, before exec. setenv() is not listed as async-signal-safe, obviously
because it allocates memory, and malloc() isn't async-signal-safe either.

See also glib's documentation of GSpawnChildSetupFunc ([1]) about what
can be done in the child process.

[1] 08cb200aec/glib/gspawn.h (L124)
This commit is contained in:
Thomas Haller 2022-10-11 08:35:59 +02:00
parent b395c6959e
commit a35d8ff769
No known key found for this signature in database
GPG key ID: 29C2366E4DFC5728

View file

@ -1447,11 +1447,12 @@ pager_fallback(void)
pid_t
nmc_terminal_spawn_pager(const NmcConfig *nmc_config)
{
const char *pager = getenv("PAGER");
pid_t pager_pid;
pid_t parent_pid;
int fd[2];
int errsv;
const char *pager = getenv("PAGER");
pid_t pager_pid;
pid_t parent_pid;
int fd[2];
int errsv;
gs_strfreev char **ev = NULL;
if (nmc_config->in_editor || nmc_config->print_output == NMC_PRINT_TERSE
|| !nmc_config->use_colors || g_strcmp0(pager, "") == 0 || getauxval(AT_SECURE))
@ -1465,6 +1466,10 @@ nmc_terminal_spawn_pager(const NmcConfig *nmc_config)
parent_pid = getpid();
ev = g_get_environ();
ev = g_environ_setenv(ev, "LESS", "FRSXMK", TRUE);
ev = g_environ_setenv(ev, "LESSCHARSET", "utf-8", TRUE);
pager_pid = fork();
if (pager_pid == -1) {
errsv = errno;
@ -1480,9 +1485,6 @@ nmc_terminal_spawn_pager(const NmcConfig *nmc_config)
nm_close(fd[0]);
nm_close(fd[1]);
setenv("LESS", "FRSXMK", 1);
setenv("LESSCHARSET", "utf-8", 1);
/* Make sure the pager goes away when the parent dies */
if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
_exit(EXIT_FAILURE);
@ -1493,8 +1495,8 @@ nmc_terminal_spawn_pager(const NmcConfig *nmc_config)
_exit(EXIT_SUCCESS);
if (pager) {
execlp(pager, pager, NULL);
execl("/bin/sh", "sh", "-c", pager, NULL);
execvpe(pager, (char **) NM_MAKE_STRV(pager), ev);
execvpe("/bin/sh", (char **) NM_MAKE_STRV("sh", "-c", pager), ev);
}
/* Debian's alternatives command for pagers is
@ -1503,10 +1505,10 @@ nmc_terminal_spawn_pager(const NmcConfig *nmc_config)
* shell script that implements a logic that
* is similar to this one anyway, but is
* Debian-specific. */
execlp("pager", "pager", NULL);
execvpe("pager", (char **) NM_MAKE_STRV("pager"), ev);
execlp("less", "less", NULL);
execlp("more", "more", NULL);
execvpe("less", (char **) NM_MAKE_STRV("less"), ev);
execvpe("more", (char **) NM_MAKE_STRV("more"), ev);
pager_fallback();
/* not reached */