about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMatthias Krüger <476013+matthiaskrgr@users.noreply.github.com>2025-03-15 11:29:26 +0100
committerGitHub <noreply@github.com>2025-03-15 11:29:26 +0100
commit06b135f6bc4bf6df4f0fd9d7a625d168597b05df (patch)
treed7ad5dfd63334076cf08ca82e7e2a6e78a8baab5
parenta3840390538f2112ef0d3b7f4c5baee15d19c2da (diff)
parent08166b5b239beb794a48bec52adc6e5261b166d1 (diff)
downloadrust-06b135f6bc4bf6df4f0fd9d7a625d168597b05df.tar.gz
rust-06b135f6bc4bf6df4f0fd9d7a625d168597b05df.zip
Rollup merge of #138439 - weihanglo:argmax, r=jieyouxu
feat: check ARG_MAX on Unix platforms

On Unix the limits can be gargantuan anyway so we're pretty unlikely to hit them, but might still exceed it.
We consult ARG_MAX here to get an estimate.

Fixes #138421

r? `@jieyouxu`
-rw-r--r--compiler/rustc_codegen_ssa/src/back/command.rs47
1 files changed, 41 insertions, 6 deletions
diff --git a/compiler/rustc_codegen_ssa/src/back/command.rs b/compiler/rustc_codegen_ssa/src/back/command.rs
index 383d0579e52..05351bd6ca3 100644
--- a/compiler/rustc_codegen_ssa/src/back/command.rs
+++ b/compiler/rustc_codegen_ssa/src/back/command.rs
@@ -137,12 +137,42 @@ impl Command {
     /// Returns a `true` if we're pretty sure that this'll blow OS spawn limits,
     /// or `false` if we should attempt to spawn and see what the OS says.
     pub(crate) fn very_likely_to_exceed_some_spawn_limit(&self) -> bool {
-        // We mostly only care about Windows in this method, on Unix the limits
-        // can be gargantuan anyway so we're pretty unlikely to hit them
-        if cfg!(unix) {
+        #[cfg(not(any(windows, unix)))]
+        {
             return false;
         }
 
+        // On Unix the limits can be gargantuan anyway so we're pretty
+        // unlikely to hit them, but might still exceed it.
+        // We consult ARG_MAX here to get an estimate.
+        #[cfg(unix)]
+        {
+            let ptr_size = mem::size_of::<usize>();
+            // arg + \0 + pointer
+            let args_size = self.args.iter().fold(0usize, |acc, a| {
+                let arg = a.as_encoded_bytes().len();
+                let nul = 1;
+                acc.saturating_add(arg).saturating_add(nul).saturating_add(ptr_size)
+            });
+            // key + `=` + value + \0 + pointer
+            let envs_size = self.env.iter().fold(0usize, |acc, (k, v)| {
+                let k = k.as_encoded_bytes().len();
+                let eq = 1;
+                let v = v.as_encoded_bytes().len();
+                let nul = 1;
+                acc.saturating_add(k)
+                    .saturating_add(eq)
+                    .saturating_add(v)
+                    .saturating_add(nul)
+                    .saturating_add(ptr_size)
+            });
+            let arg_max = match unsafe { libc::sysconf(libc::_SC_ARG_MAX) } {
+                -1 => return false, // Go to OS anyway.
+                max => max as usize,
+            };
+            return args_size.saturating_add(envs_size) > arg_max;
+        }
+
         // Ok so on Windows to spawn a process is 32,768 characters in its
         // command line [1]. Unfortunately we don't actually have access to that
         // as it's calculated just before spawning. Instead we perform a
@@ -165,9 +195,14 @@ impl Command {
         //
         // [1]: https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessa
         // [2]: https://devblogs.microsoft.com/oldnewthing/?p=41553
-
-        let estimated_command_line_len = self.args.iter().map(|a| a.len()).sum::<usize>();
-        estimated_command_line_len > 1024 * 6
+        #[cfg(windows)]
+        {
+            let estimated_command_line_len = self
+                .args
+                .iter()
+                .fold(0usize, |acc, a| acc.saturating_add(a.as_encoded_bytes().len()));
+            return estimated_command_line_len > 1024 * 6;
+        }
     }
 }