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>
Brandon Williams committed
Apr 19, 2017 at 16:13 UTC
ae25394b4c21f072f176ccbaf7047abe7fa5f04d
1 file changed
+56
-10
run-command.c
+56
-10
@@ -267,6 +267,55 @@ static void prepare_cmd(struct argv_array *out, const struct child_process *cmd)
267
}
268
}
269
}
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
+}
319
#endif
320
321
static inline void set_cloexec(int fd)
@@ -395,12 +444,14 @@ fail_pipe:
444
#ifndef GIT_WINDOWS_NATIVE
445
{
446
int notify_pipe[2];
447
+ char **childenv;
448
struct argv_array argv = ARGV_ARRAY_INIT;
449
450
if (pipe(notify_pipe))
451
notify_pipe[0] = notify_pipe[1] = -1;
452
453
prepare_cmd(&argv, cmd);
454
+ childenv = prep_childenv(cmd->env);
455
456
cmd->pid = fork();
457
failed_errno = errno;
@@ -456,14 +507,6 @@ fail_pipe:
507
if (cmd->dir && chdir(cmd->dir))
508
die_errno("exec '%s': cd to '%s' failed", cmd->argv[0],
509
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
- }
510
511
/*
512
* Attempt to exec using the command and arguments starting at
@@ -471,9 +514,11 @@ fail_pipe:
514
* be used in the event exec failed with ENOEXEC at which point
515
* we will try to interpret the command using 'sh'.
516
*/
474
- execv(argv.argv[1], (char *const *) argv.argv + 1);
517
+ execve(argv.argv[1], (char *const *) argv.argv + 1,
518
+ (char *const *) childenv);
519
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);
522
523
if (errno == ENOENT) {
524
if (!cmd->silent_exec_failure)
@@ -509,6 +554,7 @@ fail_pipe:
554
close(notify_pipe[0]);
555
556
argv_array_clear(&argv);
557
+ free(childenv);
558
}
559
#else
560
{