diff --git a/src/unix/process.c b/src/unix/process.c index 16ba6c87..3a1b5b74 100644 --- a/src/unix/process.c +++ b/src/unix/process.c @@ -671,27 +671,25 @@ static int uv__spawn_resolve_and_spawn(const uv_process_options_t* options, if (options->env != NULL) env = options->env; - /* If options->file contains a slash, posix_spawn/posix_spawnp behave - * the same, and don't involve PATH resolution at all. Otherwise, if - * options->file does not include a slash, but no custom environment is - * to be used, the environment used for path resolution as well for the - * child process is that of the parent process, so posix_spawnp is the - * way to go. */ - if (strchr(options->file, '/') != NULL || options->env == NULL) { + /* If options->file contains a slash, posix_spawn/posix_spawnp should behave + * the same, and do not involve PATH resolution at all. The libc + * `posix_spawnp` provided by Apple is buggy (since 10.15), so we now emulate it + * here, per https://github.com/libuv/libuv/pull/3583. */ + if (strchr(options->file, '/') != NULL) { do - err = posix_spawnp(pid, options->file, actions, attrs, options->args, env); + err = posix_spawn(pid, options->file, actions, attrs, options->args, env); while (err == EINTR); return err; } /* Look for the definition of PATH in the provided env */ - path = uv__spawn_find_path_in_env(options->env); + path = uv__spawn_find_path_in_env(env); /* The following resolution logic (execvpe emulation) is copied from * https://git.musl-libc.org/cgit/musl/tree/src/process/execvp.c * and adapted to work for our specific usage */ - /* If no path was provided in options->env, use the default value + /* If no path was provided in env, use the default value * to look for the executable */ if (path == NULL) path = _PATH_DEFPATH; diff --git a/test/test-list.h b/test/test-list.h index 2f023715..d75fc48e 100644 --- a/test/test-list.h +++ b/test/test-list.h @@ -321,6 +321,7 @@ TEST_DECLARE (spawn_inherit_streams) TEST_DECLARE (spawn_quoted_path) TEST_DECLARE (spawn_tcp_server) TEST_DECLARE (spawn_exercise_sigchld_issue) +TEST_DECLARE (spawn_relative_path) TEST_DECLARE (fs_poll) TEST_DECLARE (fs_poll_getpath) TEST_DECLARE (fs_poll_close_request) @@ -956,6 +957,7 @@ TASK_LIST_START TEST_ENTRY (spawn_quoted_path) TEST_ENTRY (spawn_tcp_server) TEST_ENTRY (spawn_exercise_sigchld_issue) + TEST_ENTRY (spawn_relative_path) TEST_ENTRY (fs_poll) TEST_ENTRY (fs_poll_getpath) TEST_ENTRY (fs_poll_close_request) diff --git a/test/test-spawn.c b/test/test-spawn.c index dfd5458e..de9c7100 100644 --- a/test/test-spawn.c +++ b/test/test-spawn.c @@ -1981,3 +1981,37 @@ void spawn_stdin_stdout(void) { } } #endif /* !_WIN32 */ + +TEST_IMPL(spawn_relative_path) { + char* sep; + + init_process_options("spawn_helper1", exit_cb); + + exepath_size = sizeof(exepath) - 2; + ASSERT_EQ(0, uv_exepath(exepath, &exepath_size)); + exepath[exepath_size] = '\0'; + + /* Poor man's basename(3). */ + sep = strrchr(exepath, '/'); + if (sep == NULL) + sep = strrchr(exepath, '\\'); + ASSERT_NOT_NULL(sep); + + /* Split into dirname and basename and make basename relative. */ + memmove(sep + 2, sep, 1 + strlen(sep)); + sep[0] = '\0'; + sep[1] = '.'; + sep[2] = '/'; + + options.cwd = exepath; + options.file = options.args[0] = sep + 1; + + ASSERT_EQ(0, uv_spawn(uv_default_loop(), &process, &options)); + ASSERT_EQ(0, uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + + ASSERT_EQ(1, exit_cb_called); + ASSERT_EQ(1, close_cb_called); + + MAKE_VALGRIND_HAPPY(); + return 0; +}