Skip to content

Commit 090a968

Browse files
committed
Only link res_init() on GNU/*nix
To workaround a bug in glibc <= 2.26 lookup_host() calls res_init() based on the glibc version detected at runtime. While this avoids calling res_init() on platforms where it's not required we will still end up linking against the symbol. This causes an issue on macOS where res_init() is implemented in a separate library (libresolv.9.dylib) from the main libc. While this is harmless for standalone programs it becomes a problem if Rust code is statically linked against another program. If the linked program doesn't already specify -lresolv it will cause the link to fail. This is captured in issue #46797 Fix this by hooking in to the glibc workaround in `cvt_gai` and only activating it for the "gnu" environment on Unix This should include all glibc platforms while excluding musl, windows-gnu, macOS, FreeBSD, etc. This has the side benefit of removing the #[cfg] in sys_common; only unix.rs has code related to the workaround now.
1 parent 8ff449d commit 090a968

File tree

3 files changed

+16
-32
lines changed

3 files changed

+16
-32
lines changed

src/libstd/sys/unix/l4re.rs

-4
Original file line numberDiff line numberDiff line change
@@ -437,9 +437,5 @@ pub mod net {
437437
pub fn lookup_host(_: &str) -> io::Result<LookupHost> {
438438
unimpl!();
439439
}
440-
441-
pub fn res_init_if_glibc_before_2_26() -> io::Result<()> {
442-
unimpl!();
443-
}
444440
}
445441

src/libstd/sys/unix/net.rs

+13-7
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ pub fn cvt_gai(err: c_int) -> io::Result<()> {
5151
if err == 0 {
5252
return Ok(())
5353
}
54+
55+
// We may need to trigger a glibc workaround. See on_resolver_failure() for details.
56+
on_resolver_failure();
57+
5458
if err == EAI_SYSTEM {
5559
return Err(io::Error::last_os_error())
5660
}
@@ -377,21 +381,22 @@ impl IntoInner<c_int> for Socket {
377381
// res_init unconditionally, we call it only when we detect we're linking
378382
// against glibc version < 2.26. (That is, when we both know its needed and
379383
// believe it's thread-safe).
380-
pub fn res_init_if_glibc_before_2_26() -> io::Result<()> {
384+
#[cfg(target_env = "gnu")]
385+
fn on_resolver_failure() {
381386
// If the version fails to parse, we treat it the same as "not glibc".
382387
if let Some(Ok(version_str)) = glibc_version_cstr().map(CStr::to_str) {
383388
if let Some(version) = parse_glibc_version(version_str) {
384389
if version < (2, 26) {
385-
let ret = unsafe { libc::res_init() };
386-
if ret != 0 {
387-
return Err(io::Error::last_os_error());
388-
}
390+
unsafe { libc::res_init() };
389391
}
390392
}
391393
}
392-
Ok(())
393394
}
394395

396+
#[cfg(not(target_env = "gnu"))]
397+
fn on_resolver_failure() {}
398+
399+
#[cfg(target_env = "gnu")]
395400
fn glibc_version_cstr() -> Option<&'static CStr> {
396401
weak! {
397402
fn gnu_get_libc_version() -> *const libc::c_char
@@ -405,6 +410,7 @@ fn glibc_version_cstr() -> Option<&'static CStr> {
405410

406411
// Returns Some((major, minor)) if the string is a valid "x.y" version,
407412
// ignoring any extra dot-separated parts. Otherwise return None.
413+
#[cfg(target_env = "gnu")]
408414
fn parse_glibc_version(version: &str) -> Option<(usize, usize)> {
409415
let mut parsed_ints = version.split(".").map(str::parse::<usize>).fuse();
410416
match (parsed_ints.next(), parsed_ints.next()) {
@@ -413,7 +419,7 @@ fn parse_glibc_version(version: &str) -> Option<(usize, usize)> {
413419
}
414420
}
415421

416-
#[cfg(test)]
422+
#[cfg(all(test, taget_env = "gnu"))]
417423
mod test {
418424
use super::*;
419425

src/libstd/sys_common/net.rs

+3-21
Original file line numberDiff line numberDiff line change
@@ -166,27 +166,9 @@ pub fn lookup_host(host: &str) -> io::Result<LookupHost> {
166166
hints.ai_socktype = c::SOCK_STREAM;
167167
let mut res = ptr::null_mut();
168168
unsafe {
169-
match cvt_gai(c::getaddrinfo(c_host.as_ptr(), ptr::null(), &hints, &mut res)) {
170-
Ok(_) => {
171-
Ok(LookupHost { original: res, cur: res })
172-
},
173-
#[cfg(unix)]
174-
Err(e) => {
175-
// If we're running glibc prior to version 2.26, the lookup
176-
// failure could be caused by caching a stale /etc/resolv.conf.
177-
// We need to call libc::res_init() to clear the cache. But we
178-
// shouldn't call it in on any other platform, because other
179-
// res_init implementations aren't thread-safe. See
180-
// https://github.com/rust-lang/rust/issues/41570 and
181-
// https://github.com/rust-lang/rust/issues/43592.
182-
use sys::net::res_init_if_glibc_before_2_26;
183-
let _ = res_init_if_glibc_before_2_26();
184-
Err(e)
185-
},
186-
// the cfg is needed here to avoid an "unreachable pattern" warning
187-
#[cfg(not(unix))]
188-
Err(e) => Err(e),
189-
}
169+
cvt_gai(c::getaddrinfo(c_host.as_ptr(), ptr::null(), &hints, &mut res)).map(|_| {
170+
LookupHost { original: res, cur: res }
171+
})
190172
}
191173
}
192174

0 commit comments

Comments
 (0)