macos: avoid posix_spawnp() cwd bug (#3597)

macOS 10.15 has a bug where configuring the working directory with
posix_spawn_file_actions_addchdir_np() makes posix_spawnp() fail with
ENOENT even though the executable is spawned successfully.

Co-authored-by: Ben Noordhuis <info@bnoordhuis.nl>
This commit is contained in:
Jameson Nash 2022-04-15 14:10:27 -04:00 committed by GitHub
parent 6dfcdb9878
commit 7c9b3938df
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 44 additions and 10 deletions

View File

@ -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;

View File

@ -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)

View File

@ -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;
}