Skip to content

Commit ae25394

Browse files
bmwillgitster
authored andcommitted
run-command: prepare child environment before forking
In order to avoid allocation between 'fork()' and 'exec()' prepare the environment to be used in the child process prior to forking. Switch to using 'execve()' so that the construct child environment can used in the exec'd process. Signed-off-by: Brandon Williams <bmwill@google.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
1 parent 3a30033 commit ae25394

1 file changed

Lines changed: 56 additions & 10 deletions

File tree

run-command.c

Lines changed: 56 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,55 @@ static void prepare_cmd(struct argv_array *out, const struct child_process *cmd)
267267
}
268268
}
269269
}
270+
271+
static char **prep_childenv(const char *const *deltaenv)
272+
{
273+
extern char **environ;
274+
char **childenv;
275+
struct string_list env = STRING_LIST_INIT_DUP;
276+
struct strbuf key = STRBUF_INIT;
277+
const char *const *p;
278+
int i;
279+
280+
/* Construct a sorted string list consisting of the current environ */
281+
for (p = (const char *const *) environ; p && *p; p++) {
282+
const char *equals = strchr(*p, '=');
283+
284+
if (equals) {
285+
strbuf_reset(&key);
286+
strbuf_add(&key, *p, equals - *p);
287+
string_list_append(&env, key.buf)->util = (void *) *p;
288+
} else {
289+
string_list_append(&env, *p)->util = (void *) *p;
290+
}
291+
}
292+
string_list_sort(&env);
293+
294+
/* Merge in 'deltaenv' with the current environ */
295+
for (p = deltaenv; p && *p; p++) {
296+
const char *equals = strchr(*p, '=');
297+
298+
if (equals) {
299+
/* ('key=value'), insert or replace entry */
300+
strbuf_reset(&key);
301+
strbuf_add(&key, *p, equals - *p);
302+
string_list_insert(&env, key.buf)->util = (void *) *p;
303+
} else {
304+
/* otherwise ('key') remove existing entry */
305+
string_list_remove(&env, *p, 0);
306+
}
307+
}
308+
309+
/* Create an array of 'char *' to be used as the childenv */
310+
childenv = xmalloc((env.nr + 1) * sizeof(char *));
311+
for (i = 0; i < env.nr; i++)
312+
childenv[i] = env.items[i].util;
313+
childenv[env.nr] = NULL;
314+
315+
string_list_clear(&env, 0);
316+
strbuf_release(&key);
317+
return childenv;
318+
}
270319
#endif
271320

272321
static inline void set_cloexec(int fd)
@@ -395,12 +444,14 @@ int start_command(struct child_process *cmd)
395444
#ifndef GIT_WINDOWS_NATIVE
396445
{
397446
int notify_pipe[2];
447+
char **childenv;
398448
struct argv_array argv = ARGV_ARRAY_INIT;
399449

400450
if (pipe(notify_pipe))
401451
notify_pipe[0] = notify_pipe[1] = -1;
402452

403453
prepare_cmd(&argv, cmd);
454+
childenv = prep_childenv(cmd->env);
404455

405456
cmd->pid = fork();
406457
failed_errno = errno;
@@ -456,24 +507,18 @@ int start_command(struct child_process *cmd)
456507
if (cmd->dir && chdir(cmd->dir))
457508
die_errno("exec '%s': cd to '%s' failed", cmd->argv[0],
458509
cmd->dir);
459-
if (cmd->env) {
460-
for (; *cmd->env; cmd->env++) {
461-
if (strchr(*cmd->env, '='))
462-
putenv((char *)*cmd->env);
463-
else
464-
unsetenv(*cmd->env);
465-
}
466-
}
467510

468511
/*
469512
* Attempt to exec using the command and arguments starting at
470513
* argv.argv[1]. argv.argv[0] contains SHELL_PATH which will
471514
* be used in the event exec failed with ENOEXEC at which point
472515
* we will try to interpret the command using 'sh'.
473516
*/
474-
execv(argv.argv[1], (char *const *) argv.argv + 1);
517+
execve(argv.argv[1], (char *const *) argv.argv + 1,
518+
(char *const *) childenv);
475519
if (errno == ENOEXEC)
476-
execv(argv.argv[0], (char *const *) argv.argv);
520+
execve(argv.argv[0], (char *const *) argv.argv,
521+
(char *const *) childenv);
477522

478523
if (errno == ENOENT) {
479524
if (!cmd->silent_exec_failure)
@@ -509,6 +554,7 @@ int start_command(struct child_process *cmd)
509554
close(notify_pipe[0]);
510555

511556
argv_array_clear(&argv);
557+
free(childenv);
512558
}
513559
#else
514560
{

0 commit comments

Comments
 (0)