Skip to content

Commit 8249826

Browse files
committed
warn on invalid jobserver file descriptors
1 parent 82f8c3c commit 8249826

2 files changed

Lines changed: 46 additions & 22 deletions

File tree

src/cargo/util/context/mod.rs

Lines changed: 44 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ use std::io::prelude::*;
7272
use std::mem;
7373
use std::path::{Path, PathBuf};
7474
use std::str::FromStr;
75-
use std::sync::{Arc, Mutex, MutexGuard, Once, OnceLock};
75+
use std::sync::{Arc, LazyLock, Mutex, MutexGuard, OnceLock};
7676
use std::time::Instant;
7777

7878
use self::ConfigValue as CV;
@@ -239,7 +239,7 @@ pub struct GlobalContext {
239239
/// continue operating if possible.
240240
offline: bool,
241241
/// A global static IPC control mechanism (used for managing parallel builds)
242-
jobserver: Option<jobserver::Client>,
242+
jobserver: Option<&'static jobserver::Client>,
243243
/// Cli flags of the form "-Z something" merged with config file values
244244
unstable_flags: CliUnstable,
245245
/// Cli flags of the form "-Z something"
@@ -308,17 +308,47 @@ impl GlobalContext {
308308
///
309309
/// This does only minimal initialization. In particular, it does not load
310310
/// any config files from disk. Those will be loaded lazily as-needed.
311-
pub fn new(shell: Shell, cwd: PathBuf, homedir: PathBuf) -> GlobalContext {
312-
static mut GLOBAL_JOBSERVER: *mut jobserver::Client = 0 as *mut _;
313-
static INIT: Once = Once::new();
314-
315-
// This should be called early on in the process, so in theory the
316-
// unsafety is ok here. (taken ownership of random fds)
317-
INIT.call_once(|| unsafe {
318-
if let Some(client) = jobserver::Client::from_env() {
319-
GLOBAL_JOBSERVER = Box::into_raw(Box::new(client));
311+
pub fn new(mut shell: Shell, cwd: PathBuf, homedir: PathBuf) -> GlobalContext {
312+
static GLOBAL_JOBSERVER: LazyLock<CargoResult<Option<jobserver::Client>>> = LazyLock::new(
313+
|| {
314+
use jobserver::FromEnvErrorKind;
315+
// Note that this is unsafe because it may misinterpret file descriptors
316+
// on Unix as jobserver file descriptors. We hopefully execute this near
317+
// the beginning of the process though to ensure we don't get false
318+
// positives, or in other words we try to execute this before we open
319+
// any file descriptors ourselves.
320+
let jobserver::FromEnv { client, var } =
321+
unsafe { jobserver::Client::from_env_ext(true) };
322+
323+
match client {
324+
Ok(client) => return Ok(Some(client)),
325+
Err(e)
326+
if matches!(
327+
e.kind(),
328+
FromEnvErrorKind::NoEnvVar
329+
| FromEnvErrorKind::NoJobserver
330+
| FromEnvErrorKind::NegativeFd
331+
| FromEnvErrorKind::Unsupported
332+
) =>
333+
{
334+
Ok(None)
335+
}
336+
Err(e) => {
337+
let (name, value) = var.unwrap();
338+
Err(anyhow::anyhow!(
339+
"failed to connect to jobserver from environment variable `{name}={value:?}`: {e}"
340+
))
341+
}
342+
}
343+
},
344+
);
345+
let jobserver = match &*GLOBAL_JOBSERVER {
346+
Ok(jobserver) => jobserver.as_ref(),
347+
Err(e) => {
348+
let _ = shell.warn(e);
349+
None
320350
}
321-
});
351+
};
322352

323353
let env = Env::new();
324354

@@ -342,13 +372,7 @@ impl GlobalContext {
342372
frozen: false,
343373
locked: false,
344374
offline: false,
345-
jobserver: unsafe {
346-
if GLOBAL_JOBSERVER.is_null() {
347-
None
348-
} else {
349-
Some((*GLOBAL_JOBSERVER).clone())
350-
}
351-
},
375+
jobserver,
352376
unstable_flags: CliUnstable::default(),
353377
unstable_flags_cli: None,
354378
easy: Default::default(),
@@ -1880,7 +1904,7 @@ impl GlobalContext {
18801904
}
18811905

18821906
pub fn jobserver_from_env(&self) -> Option<&jobserver::Client> {
1883-
self.jobserver.as_ref()
1907+
self.jobserver
18841908
}
18851909

18861910
pub fn http(&self) -> CargoResult<&Mutex<Easy>> {

tests/testsuite/jobserver.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -411,7 +411,6 @@ all:
411411
.run();
412412
}
413413

414-
415414
#[cargo_test]
416415
fn jobserver_invalid_fd_warning() {
417416
let p = project()
@@ -423,9 +422,10 @@ fn jobserver_invalid_fd_warning() {
423422
p.cargo("check")
424423
.env("MAKEFLAGS", "--jobserver-auth=200001,200002")
425424
.with_stderr_data(str![[r#"
425+
[WARNING] failed to connect to jobserver from environment variable `MAKEFLAGS="--jobserver-auth=200001,200002"`: [..]
426426
[COMPILING] foo v0.0.1 ([ROOT]/foo)
427427
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
428428
429429
"#]])
430430
.run();
431-
}
431+
}

0 commit comments

Comments
 (0)