]> git.ipfire.org Git - thirdparty/git.git/commitdiff
Merge branch 'js/mingw-inherit-only-std-handles'
authorJunio C Hamano <gitster@pobox.com>
Tue, 10 Dec 2019 21:11:42 +0000 (13:11 -0800)
committerJunio C Hamano <gitster@pobox.com>
Tue, 10 Dec 2019 21:11:42 +0000 (13:11 -0800)
Work around a issue where a FD that is left open when spawning a
child process and is kept open in the child can interfere with the
operation in the parent process on Windows.

* js/mingw-inherit-only-std-handles:
  mingw: forbid translating ERROR_SUCCESS to an errno value
  mingw: do set `errno` correctly when trying to restrict handle inheritance
  mingw: restrict file handle inheritance only on Windows 7 and later
  mingw: spawned processes need to inherit only standard handles
  mingw: work around incorrect standard handles
  mingw: demonstrate that all file handles are inherited by child processes

1  2 
compat/mingw.c
t/helper/test-run-command.c

diff --cc compat/mingw.c
index ae72c929c34cc5c763bb15a7892f68423db5612a,827065d96ded7650fadb7a2ccbeb2cf9652aed00..c2a4835104e97f91d7e9b45d68cc5d17803b7b5d
@@@ -1444,9 -1422,21 +1460,22 @@@ static pid_t mingw_spawnve_fd(const cha
        BOOL ret;
        HANDLE cons;
        const char *(*quote_arg)(const char *arg) =
 -              is_msys2_sh(*argv) ? quote_arg_msys2 : quote_arg_msvc;
 +              is_msys2_sh(cmd ? cmd : *argv) ?
 +              quote_arg_msys2 : quote_arg_msvc;
  
+       /* Make sure to override previous errors, if any */
+       errno = 0;
+       if (restrict_handle_inheritance < 0)
+               restrict_handle_inheritance = core_restrict_inherited_handles;
+       /*
+        * The following code to restrict which handles are inherited seems
+        * to work properly only on Windows 7 and later, so let's disable it
+        * on Windows Vista and 2008.
+        */
+       if (restrict_handle_inheritance < 0)
+               restrict_handle_inheritance = GetVersion() >> 16 >= 7601;
        do_unset_environment_variables();
  
        /* Determine whether or not we are associated to a console */
index 724328975a38e1c630d019bc49e1c507ff514b1f,40ec4dbb6e3292762a2ca8ab827129c3164a79db..1646aa25d82f9e705d8d4b097e95fec768337e70
@@@ -200,134 -200,46 +200,174 @@@ static int testsuite(int argc, const ch
        return !!ret;
  }
  
 +static uint64_t my_random_next = 1234;
 +
 +static uint64_t my_random(void)
 +{
 +      uint64_t res = my_random_next;
 +      my_random_next = my_random_next * 1103515245 + 12345;
 +      return res;
 +}
 +
 +static int quote_stress_test(int argc, const char **argv)
 +{
 +      /*
 +       * We are running a quote-stress test.
 +       * spawn a subprocess that runs quote-stress with a
 +       * special option that echoes back the arguments that
 +       * were passed in.
 +       */
 +      char special[] = ".?*\\^_\"'`{}()[]<>@~&+:;$%"; // \t\r\n\a";
 +      int i, j, k, trials = 100, skip = 0, msys2 = 0;
 +      struct strbuf out = STRBUF_INIT;
 +      struct argv_array args = ARGV_ARRAY_INIT;
 +      struct option options[] = {
 +              OPT_INTEGER('n', "trials", &trials, "Number of trials"),
 +              OPT_INTEGER('s', "skip", &skip, "Skip <n> trials"),
 +              OPT_BOOL('m', "msys2", &msys2, "Test quoting for MSYS2's sh"),
 +              OPT_END()
 +      };
 +      const char * const usage[] = {
 +              "test-tool run-command quote-stress-test <options>",
 +              NULL
 +      };
 +
 +      argc = parse_options(argc, argv, NULL, options, usage, 0);
 +
 +      setenv("MSYS_NO_PATHCONV", "1", 0);
 +
 +      for (i = 0; i < trials; i++) {
 +              struct child_process cp = CHILD_PROCESS_INIT;
 +              size_t arg_count, arg_offset;
 +              int ret = 0;
 +
 +              argv_array_clear(&args);
 +              if (msys2)
 +                      argv_array_pushl(&args, "sh", "-c",
 +                                       "printf %s\\\\0 \"$@\"", "skip", NULL);
 +              else
 +                      argv_array_pushl(&args, "test-tool", "run-command",
 +                                       "quote-echo", NULL);
 +              arg_offset = args.argc;
 +
 +              if (argc > 0) {
 +                      trials = 1;
 +                      arg_count = argc;
 +                      for (j = 0; j < arg_count; j++)
 +                              argv_array_push(&args, argv[j]);
 +              } else {
 +                      arg_count = 1 + (my_random() % 5);
 +                      for (j = 0; j < arg_count; j++) {
 +                              char buf[20];
 +                              size_t min_len = 1;
 +                              size_t arg_len = min_len +
 +                                      (my_random() % (ARRAY_SIZE(buf) - min_len));
 +
 +                              for (k = 0; k < arg_len; k++)
 +                                      buf[k] = special[my_random() %
 +                                              ARRAY_SIZE(special)];
 +                              buf[arg_len] = '\0';
 +
 +                              argv_array_push(&args, buf);
 +                      }
 +              }
 +
 +              if (i < skip)
 +                      continue;
 +
 +              cp.argv = args.argv;
 +              strbuf_reset(&out);
 +              if (pipe_command(&cp, NULL, 0, &out, 0, NULL, 0) < 0)
 +                      return error("Failed to spawn child process");
 +
 +              for (j = 0, k = 0; j < arg_count; j++) {
 +                      const char *arg = args.argv[j + arg_offset];
 +
 +                      if (strcmp(arg, out.buf + k))
 +                              ret = error("incorrectly quoted arg: '%s', "
 +                                          "echoed back as '%s'",
 +                                           arg, out.buf + k);
 +                      k += strlen(out.buf + k) + 1;
 +              }
 +
 +              if (k != out.len)
 +                      ret = error("got %d bytes, but consumed only %d",
 +                                   (int)out.len, (int)k);
 +
 +              if (ret) {
 +                      fprintf(stderr, "Trial #%d failed. Arguments:\n", i);
 +                      for (j = 0; j < arg_count; j++)
 +                              fprintf(stderr, "arg #%d: '%s'\n",
 +                                      (int)j, args.argv[j + arg_offset]);
 +
 +                      strbuf_release(&out);
 +                      argv_array_clear(&args);
 +
 +                      return ret;
 +              }
 +
 +              if (i && (i % 100) == 0)
 +                      fprintf(stderr, "Trials completed: %d\n", (int)i);
 +      }
 +
 +      strbuf_release(&out);
 +      argv_array_clear(&args);
 +
 +      return 0;
 +}
 +
 +static int quote_echo(int argc, const char **argv)
 +{
 +      while (argc > 1) {
 +              fwrite(argv[1], strlen(argv[1]), 1, stdout);
 +              fputc('\0', stdout);
 +              argv++;
 +              argc--;
 +      }
 +
 +      return 0;
 +}
 +
+ static int inherit_handle(const char *argv0)
+ {
+       struct child_process cp = CHILD_PROCESS_INIT;
+       char path[PATH_MAX];
+       int tmp;
+       /* First, open an inheritable handle */
+       xsnprintf(path, sizeof(path), "out-XXXXXX");
+       tmp = xmkstemp(path);
+       argv_array_pushl(&cp.args,
+                        "test-tool", argv0, "inherited-handle-child", NULL);
+       cp.in = -1;
+       cp.no_stdout = cp.no_stderr = 1;
+       if (start_command(&cp) < 0)
+               die("Could not start child process");
+       /* Then close it, and try to delete it. */
+       close(tmp);
+       if (unlink(path))
+               die("Could not delete '%s'", path);
+       if (close(cp.in) < 0 || finish_command(&cp) < 0)
+               die("Child did not finish");
+       return 0;
+ }
+ static int inherit_handle_child(void)
+ {
+       struct strbuf buf = STRBUF_INIT;
+       if (strbuf_read(&buf, 0, 0) < 0)
+               die("Could not read stdin");
+       printf("Received %s\n", buf.buf);
+       strbuf_release(&buf);
+       return 0;
+ }
  int cmd__run_command(int argc, const char **argv)
  {
        struct child_process proc = CHILD_PROCESS_INIT;
  
        if (argc > 1 && !strcmp(argv[1], "testsuite"))
                exit(testsuite(argc - 1, argv + 1));
+       if (!strcmp(argv[1], "inherited-handle"))
+               exit(inherit_handle(argv[0]));
+       if (!strcmp(argv[1], "inherited-handle-child"))
+               exit(inherit_handle_child());
  
 +      if (argc >= 2 && !strcmp(argv[1], "quote-stress-test"))
 +              return !!quote_stress_test(argc - 1, argv + 1);
 +
 +      if (argc >= 2 && !strcmp(argv[1], "quote-echo"))
 +              return !!quote_echo(argc - 1, argv + 1);
 +
        if (argc < 3)
                return 1;
        while (!strcmp(argv[1], "env")) {