Skip to content

Commit 57a906e

Browse files
committed
Set TMPDIR/TMP/TEMP environment variables
When invoking rustc, rustdoc and build scripts, set `TMP` and `TEMP` (on Windows) or `TMPDIR` (everywhere else) to a project-local temporary directory.
1 parent 3861f60 commit 57a906e

File tree

6 files changed

+137
-10
lines changed

6 files changed

+137
-10
lines changed

crates/cargo-util/src/paths.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,32 @@ pub fn join_paths<T: AsRef<OsStr>>(paths: &[T], env: &str) -> Result<OsString> {
3333
})
3434
}
3535

36+
/// The environment variables that control the temporary directory returned by
37+
/// `std::env::temp_dir()`.
38+
///
39+
/// Cargo overrides this directory when invoking the compiler or when running
40+
/// build scripts. This is useful when debugging, as it allows more easily
41+
/// inspecting the state the compiler was working with when something went
42+
/// wrong (especially if using `-Csave-temps=yes`).
43+
///
44+
/// Additionally, this is useful for preventing information leakage and
45+
/// Man-in-the-middle attacks: `/tmp` is world readable and semi-writable by
46+
/// design. This is even an issue on macOS, where the temporary directory is
47+
/// scoped to the current user with `getconf DARWIN_USER_TEMP_DIR`, since you
48+
/// can still have information leakage / MITM to less privileged processes by
49+
/// the same user.
50+
pub fn tmpdir_envvars() -> &'static [&'static str] {
51+
// NOTE: On Windows, there's two environment variables that control the
52+
// temporary directory, `TMP` and `TEMP`, see `GetTempPath2`:
53+
// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-gettemppath2a
54+
// We set both of them for consistency with programs that only read one.
55+
if cfg!(windows) {
56+
&["TMP", "TEMP"]
57+
} else {
58+
&["TMPDIR"]
59+
}
60+
}
61+
3662
/// Returns the name of the environment variable used for searching for
3763
/// dynamic libraries.
3864
pub fn dylib_path_envvar() -> &'static str {

src/cargo/core/compiler/build_runner/compilation_files.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,20 @@ impl<'a, 'gctx: 'a> CompilationFiles<'a, 'gctx> {
389389
self.build_script_run_dir(unit).join("out")
390390
}
391391

392+
/// Returns the directory which build scripts should use for temporary
393+
/// files.
394+
/// `/path/to/target/{debug,release}/build/PKG-HASH/tmp`
395+
pub fn build_script_tmp_dir(&self, unit: &Unit) -> PathBuf {
396+
self.build_script_run_dir(unit).join("tmp")
397+
}
398+
399+
/// Returns the directory which `rustc` invocations should use for
400+
/// temporary files, and which `CARGO_CFG_TMPDIR` should be set to.
401+
/// `/path/to/target/tmp`
402+
pub fn rustc_tmp_dir(&self, unit: &Unit) -> &Path {
403+
self.layout(unit.kind).build_dir().tmp()
404+
}
405+
392406
/// Returns the path to the executable binary for the given bin target.
393407
///
394408
/// This should only to be used when a `Unit` is not available.

src/cargo/core/compiler/custom_build.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,7 @@ fn emit_build_output(
325325
///
326326
/// * Set environment variables for the build script run.
327327
/// * Create the output dir (`OUT_DIR`) for the build script output.
328+
/// * Create the temporary dir (`TMPDIR`/`TMP`/`TEMP`) that will be set.
328329
/// * Determine if the build script needs a re-run.
329330
/// * Run the build script and store its output.
330331
fn build_work(build_runner: &mut BuildRunner<'_, '_>, unit: &Unit) -> CargoResult<Job> {
@@ -339,6 +340,7 @@ fn build_work(build_runner: &mut BuildRunner<'_, '_>, unit: &Unit) -> CargoResul
339340
let script_dir = build_runner.files().build_script_dir(build_script_unit);
340341
let script_out_dir = build_runner.files().build_script_out_dir(unit);
341342
let script_run_dir = build_runner.files().build_script_run_dir(unit);
343+
let script_tmp_dir = build_runner.files().build_script_tmp_dir(unit);
342344

343345
if let Some(deps) = unit.pkg.manifest().metabuild() {
344346
prepare_metabuild(build_runner, build_script_unit, deps)?;
@@ -376,6 +378,12 @@ fn build_work(build_runner: &mut BuildRunner<'_, '_>, unit: &Unit) -> CargoResul
376378
.env("RUSTDOC", &*bcx.gctx.rustdoc()?)
377379
.inherit_jobserver(&build_runner.jobserver);
378380

381+
// Make build scripts output temporary files to `script_tmp_dir` instead
382+
// of the path returned by `std::env::temp_dir()`.
383+
for key in paths::tmpdir_envvars() {
384+
cmd.env(key, &script_tmp_dir);
385+
}
386+
379387
// Find all artifact dependencies and make their file and containing directory discoverable using environment variables.
380388
for (var, value) in artifact::get_env(build_runner, dependencies)? {
381389
cmd.env(&var, value);
@@ -495,6 +503,7 @@ fn build_work(build_runner: &mut BuildRunner<'_, '_>, unit: &Unit) -> CargoResul
495503

496504
paths::create_dir_all(&script_dir)?;
497505
paths::create_dir_all(&script_out_dir)?;
506+
paths::create_dir_all(&script_tmp_dir)?;
498507

499508
let nightly_features_allowed = build_runner.bcx.gctx.nightly_features_allowed;
500509
let targets: Vec<Target> = unit.pkg.targets().to_vec();

src/cargo/core/compiler/layout.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -380,9 +380,8 @@ impl BuildDirLayout {
380380
pub fn build_unit(&self, pkg_dir: &str) -> PathBuf {
381381
self.build().join(pkg_dir)
382382
}
383-
/// Create and return the tmp path.
384-
pub fn prepare_tmp(&self) -> CargoResult<&Path> {
385-
paths::create_dir_all(&self.tmp)?;
386-
Ok(&self.tmp)
383+
/// Return the tmp path.
384+
pub fn tmp(&self) -> &Path {
385+
&self.tmp
387386
}
388387
}

src/cargo/core/compiler/mod.rs

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -787,13 +787,17 @@ fn prepare_rustc(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> CargoResult
787787
}
788788
}
789789

790+
let tmpdir = build_runner.files().rustc_tmp_dir(unit);
791+
paths::create_dir_all(tmpdir)?;
792+
793+
// Make `rustc`, proc-macros and the linker output temporary files to
794+
// `tmpdir` instead of the path returned by `std::env::temp_dir()`.
795+
for key in paths::tmpdir_envvars() {
796+
base.env(key, tmpdir);
797+
}
798+
790799
if unit.target.is_test() || unit.target.is_bench() {
791-
let tmp = build_runner
792-
.files()
793-
.layout(unit.kind)
794-
.build_dir()
795-
.prepare_tmp()?;
796-
base.env("CARGO_TARGET_TMPDIR", tmp.display().to_string());
800+
base.env("CARGO_TARGET_TMPDIR", tmpdir.display().to_string());
797801
}
798802

799803
Ok(base)
@@ -923,6 +927,15 @@ fn prepare_rustdoc(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> CargoResu
923927
append_crate_version_flag(unit, &mut rustdoc);
924928
}
925929

930+
let tmpdir = build_runner.files().rustc_tmp_dir(unit);
931+
paths::create_dir_all(tmpdir)?;
932+
933+
// Make `rustdoc` and proc-macros output temporary files to `tmpdir`
934+
// instead of the path returned by `std::env::temp_dir()`.
935+
for key in paths::tmpdir_envvars() {
936+
rustdoc.env(key, tmpdir);
937+
}
938+
926939
Ok(rustdoc)
927940
}
928941

tests/testsuite/build.rs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6425,3 +6425,69 @@ fn embed_metadata_dylib_dep() {
64256425
)
64266426
.run();
64276427
}
6428+
6429+
#[cargo_test]
6430+
fn temp_dir_is_inside_build_dir() {
6431+
let p = project()
6432+
.file(
6433+
"Cargo.toml",
6434+
r#"
6435+
[package]
6436+
name = "foo"
6437+
version = "0.1.0"
6438+
edition = "2021"
6439+
[dependencies]
6440+
pm = { path = "pm" }
6441+
"#,
6442+
)
6443+
.file(
6444+
"src/lib.rs",
6445+
r#"
6446+
pm::foo!();
6447+
"#,
6448+
)
6449+
.file(
6450+
"build.rs",
6451+
r#"
6452+
fn main() {
6453+
let temp_dir = std::env::temp_dir();
6454+
assert!(temp_dir.components().any(|c| c.as_os_str() == "custom-build-dir"), "{temp_dir:?}");
6455+
}
6456+
"#,
6457+
)
6458+
.file(
6459+
".cargo/config.toml",
6460+
r#"
6461+
[build]
6462+
target-dir = "custom-target-dir"
6463+
build-dir = "custom-build-dir"
6464+
"#,
6465+
)
6466+
.file(
6467+
"pm/Cargo.toml",
6468+
r#"
6469+
[package]
6470+
name = "pm"
6471+
version = "0.1.0"
6472+
edition = "2021"
6473+
[lib]
6474+
proc-macro = true
6475+
"#,
6476+
)
6477+
.file(
6478+
"pm/src/lib.rs",
6479+
r#"
6480+
use proc_macro::TokenStream;
6481+
6482+
#[proc_macro]
6483+
pub fn foo(input: TokenStream) -> TokenStream {
6484+
let temp_dir = std::env::temp_dir();
6485+
assert!(temp_dir.components().any(|c| c.as_os_str() == "custom-build-dir"), "{temp_dir:?}");
6486+
input
6487+
}
6488+
"#,
6489+
)
6490+
.build();
6491+
6492+
p.cargo("build").run();
6493+
}

0 commit comments

Comments
 (0)