1
1
mirror of https://github.com/wez/wezterm.git synced 2024-11-23 06:54:45 +03:00

pty: don't try to spawn a directory from the cwd

When the cwd was set to a directory that contained
an executable directory whose name matched the argv0
of the command to be spawned, we'd incorrectly consider
that to be a valid candidate.

When later trying to spawn this, we'll fail with an EACCESS,
and in the context of wezterm, rust's internal error handling
machinery for this error would fail to write to a descriptor
because our close_random_fds() function has closed that descriptor
during pre_exec.  That in turn would cause rust to print a panic
message to the output stream of this semi-spawned process,
which would show briefly an error message with no useful context.

This commit resolves this issue by explicitly avoiding executable
directories in this case.

refs: https://github.com/wez/wezterm/issues/4920
This commit is contained in:
Wez Furlong 2024-02-01 16:57:29 -07:00
parent aa94a98314
commit 17dadbeb1e
No known key found for this signature in database
GPG Key ID: 7A7F66A31EC9B387
2 changed files with 29 additions and 19 deletions

View File

@ -32,6 +32,9 @@ As features stabilize some brief notes about them will accumulate here.
attributes. #4808 attributes. #4808
* Changing the palette via escape sequences didn't invalidate caches * Changing the palette via escape sequences didn't invalidate caches
correctly, so those escapes sequences wouldn't take effect. #4932 #2635 correctly, so those escapes sequences wouldn't take effect. #4932 #2635
* Unix: spawning a command using a relative path, with the cwd set to a
directory that contains a directory with the same name as the relative
path to the command would fail with an obscure error message. #4920
### 20240128-202157-1e552d76 ### 20240128-202157-1e552d76

View File

@ -422,38 +422,45 @@ impl CommandBuilder {
let mut errors = vec![]; let mut errors = vec![];
if access(&abs_path, AccessFlags::X_OK).is_ok() { if abs_path.is_dir() {
errors.push(format!("{} exists but is a directory", abs_path.display()));
} else if access(&abs_path, AccessFlags::X_OK).is_ok() {
return Ok(abs_path.into_os_string()); return Ok(abs_path.into_os_string());
} } else if access(&abs_path, AccessFlags::F_OK).is_ok() {
if access(&abs_path, AccessFlags::F_OK).is_ok() {
errors.push(format!( errors.push(format!(
"{} exists but is not executable", "{} exists but is not executable",
abs_path.display() abs_path.display()
)); ));
} else { }
if let Some(path) = self.resolve_path() { if let Some(path) = self.resolve_path() {
for path in std::env::split_paths(&path) { for path in std::env::split_paths(&path) {
let candidate = path.join(&exe); let candidate = path.join(&exe);
if access(&candidate, AccessFlags::X_OK).is_ok() {
return Ok(candidate.into_os_string()); if candidate.is_dir() {
} errors.push(format!("{} exists but is a directory", candidate.display()));
if access(&candidate, AccessFlags::F_OK).is_ok() { } else if access(&candidate, AccessFlags::X_OK).is_ok() {
errors.push(format!( return Ok(candidate.into_os_string());
"{} exists but is not executable", } else if access(&candidate, AccessFlags::F_OK).is_ok() {
candidate.display() errors.push(format!(
)); "{} exists but is not executable",
} candidate.display()
));
} }
errors.push(format!("No viable candidates found in PATH {path:?}"));
} else {
errors.push("Unable to resolve the PATH".to_string());
} }
errors.push(format!("No viable candidates found in PATH {path:?}"));
} else {
errors.push("Unable to resolve the PATH".to_string());
} }
anyhow::bail!( anyhow::bail!(
"Unable to spawn {} because:\n{}", "Unable to spawn {} because:\n{}",
exe_path.display(), exe_path.display(),
errors.join(".\n") errors.join(".\n")
); );
} else if exe_path.is_dir() {
anyhow::bail!(
"Unable to spawn {} because it is a directory",
exe_path.display()
);
} else { } else {
if let Err(err) = access(exe_path, AccessFlags::X_OK) { if let Err(err) = access(exe_path, AccessFlags::X_OK) {
if access(exe_path, AccessFlags::F_OK).is_ok() { if access(exe_path, AccessFlags::F_OK).is_ok() {