From 79034bd291c13b92d68561ba957dc898c6ad3ae7 Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Wed, 12 Mar 2025 20:58:56 -0400 Subject: [PATCH 1/3] fix(linker): prevent overflow when estimating CLI arg list length This also updates the estimate on Windows of the length argument list to `saturating_add` to avoid overflow. --- compiler/rustc_codegen_ssa/src/back/command.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_codegen_ssa/src/back/command.rs b/compiler/rustc_codegen_ssa/src/back/command.rs index 383d0579e528a..8425238604151 100644 --- a/compiler/rustc_codegen_ssa/src/back/command.rs +++ b/compiler/rustc_codegen_ssa/src/back/command.rs @@ -166,7 +166,8 @@ 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::(); + let estimated_command_line_len = + self.args.iter().fold(0usize, |acc, a| acc.saturating_add(a.as_encoded_bytes().len())); estimated_command_line_len > 1024 * 6 } } From a672448f0d223bcfa6907d17c321015ac83606d7 Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Fri, 14 Mar 2025 08:55:53 -0400 Subject: [PATCH 2/3] fix(linker): use arg list estimate on only Windows Though I doubt anyone running rustc outside Unix/Windows --- compiler/rustc_codegen_ssa/src/back/command.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_codegen_ssa/src/back/command.rs b/compiler/rustc_codegen_ssa/src/back/command.rs index 8425238604151..863d5e7cdd4bd 100644 --- a/compiler/rustc_codegen_ssa/src/back/command.rs +++ b/compiler/rustc_codegen_ssa/src/back/command.rs @@ -139,7 +139,7 @@ impl Command { 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) { + if cfg!(not(windows)) { return false; } From 08166b5b239beb794a48bec52adc6e5261b166d1 Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Fri, 14 Mar 2025 09:43:30 -0400 Subject: [PATCH 3/3] feat(linker): 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. --- .../rustc_codegen_ssa/src/back/command.rs | 48 ++++++++++++++++--- 1 file changed, 41 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/command.rs b/compiler/rustc_codegen_ssa/src/back/command.rs index 863d5e7cdd4bd..05351bd6ca379 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!(not(windows)) { + #[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::(); + // 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,10 +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().fold(0usize, |acc, a| acc.saturating_add(a.as_encoded_bytes().len())); - 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; + } } }