Skip to content

Commit

Permalink
only parse /bin/sh (not /bin/ls) (#1493)
Browse files Browse the repository at this point in the history
It turns out that /bin/ls can sometimes be plain text file. For
example, in Rocky Linux 9:

```
$ cat /bin/ls
#!/usr/bin/coreutils --coreutils-prog-shebang=ls
```

However, `/bin/sh` is an ELF binary:

```
$ file /bin/sh
/bin/sh: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=7acbb41bf6f1b7d977f1b44675bf3ed213776835, for GNU/Linux 3.2.0, stripped
```

In a related issue (#1433), @zanieb fixed #1395 where, on NixOS,
`/bin/ls` doesn't exist but `/bin/sh` does. However, the fix attempts
`/bin/ls` first and only tries `/bin/sh` if `/bin/ls` doesn't exist. If
`/bin/ls` exists but isn't a valid ELF file, then the entire enterprise
gives up and `uv` fails to detect the version of `libc` that is
installed.

Instead of tweaking the logic to keep trying `/bin/ls` and then
`/bin/sh` after even if parsing `/bin/ls` fails, we just switch over to
reading `/bin/sh` only. It seems like a more fundamental thing to sniff
and likely less error prone.

We can adjust this heuristic as needed if it provdes to be problematic.

I tested this fix manually on Rocky Linux 9 via Docker:

```
$ cross b -r -p uv --target x86_64-unknown-linux-musl
$ cp target/x86_64-unknown-linux-musl/release/uv ~/astral/issues/uv/i1486/uv
$ docker run --rm -it --mount type=bind,src=/home/andrew/astral/issues/uv/i1486,dst=/host rockylinux:9 bash
[root@df2baa65d2f8 /]# /host/uv venv
Using Python 3.9.18 interpreter at /usr/bin/python3.9
Creating virtualenv at: .venv
[root@df2baa65d2f8 /]#
```

Fixes #1486, Ref #1433
  • Loading branch information
BurntSushi committed Feb 16, 2024
1 parent e913167 commit 67cde15
Showing 1 changed file with 10 additions and 20 deletions.
30 changes: 10 additions & 20 deletions crates/platform-host/src/linux.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,27 +127,17 @@ fn get_musl_version(ld_path: impl AsRef<Path>) -> std::io::Result<Option<(u16, u

/// Find musl libc path from executable's ELF header.
fn find_libc() -> Result<PathBuf, PlatformError> {
// We'll try to parse the first file we read successfully
for path in ["/bin/ls", "/bin/sh"] {
let Ok(buffer) = fs::read(path) else {
continue;
};
let elf = Elf::parse(&buffer).map_err(|err| {
PlatformError::OsVersionDetectionError(format!(
"Couldn't parse {path} to detect the ld version: {err}"
))
})?;
if let Some(elf_interpreter) = elf.interpreter {
return Ok(PathBuf::from(elf_interpreter));
}

return Err(PlatformError::OsVersionDetectionError(format!(
"Couldn't parse {path} to detect the ld version"
)));
let buffer = fs::read("/bin/sh")?;
let error_str = "Couldn't parse /bin/sh for detecting the ld version";
let elf = Elf::parse(&buffer)
.map_err(|err| PlatformError::OsVersionDetectionError(format!("{error_str}: {err}")))?;
if let Some(elf_interpreter) = elf.interpreter {
Ok(PathBuf::from(elf_interpreter))
} else {
Err(PlatformError::OsVersionDetectionError(
error_str.to_string(),
))
}
Err(PlatformError::OsVersionDetectionError(
"Failed to find binary at `/bin/ls` or `/bin/sh` to read ld version from".to_string(),
))
}

#[cfg(test)]
Expand Down

0 comments on commit 67cde15

Please sign in to comment.