From 67cde1542039ff1de343b1e340e31817422e132d Mon Sep 17 00:00:00 2001 From: Andrew Gallant Date: Fri, 16 Feb 2024 09:44:47 -0500 Subject: [PATCH] only parse /bin/sh (not /bin/ls) (#1493) 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 --- crates/platform-host/src/linux.rs | 30 ++++++++++-------------------- 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/crates/platform-host/src/linux.rs b/crates/platform-host/src/linux.rs index cd698b232..f4d40afb3 100644 --- a/crates/platform-host/src/linux.rs +++ b/crates/platform-host/src/linux.rs @@ -127,27 +127,17 @@ fn get_musl_version(ld_path: impl AsRef) -> std::io::Result Result { - // 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)]