From 1b849565df0e21d0123692f58bb700187122c6cb Mon Sep 17 00:00:00 2001 From: Aviram Hassan Date: Tue, 30 Apr 2024 14:37:04 +0300 Subject: [PATCH] Fix IntelliJ Rider newest version stuck on macOS (#2409) * Fix IntelliJ Rider newest version stuck on macOS * Update mirrord/layer/src/lib.rs Co-authored-by: t4lz * lint * lint * tests --------- Co-authored-by: t4lz --- changelog.d/2408.fixed.md | 1 + mirrord/intproxy/protocol/src/lib.rs | 15 ++++++- mirrord/intproxy/src/layer_initializer.rs | 6 ++- mirrord/layer/src/lib.rs | 55 ++++++++++++----------- mirrord/layer/src/load.rs | 50 ++++++++++++++++++--- 5 files changed, 92 insertions(+), 35 deletions(-) create mode 100644 changelog.d/2408.fixed.md diff --git a/changelog.d/2408.fixed.md b/changelog.d/2408.fixed.md new file mode 100644 index 00000000000..71ba14e1085 --- /dev/null +++ b/changelog.d/2408.fixed.md @@ -0,0 +1 @@ +Fix IntelliJ Rider newest version stuck on macOS \ No newline at end of file diff --git a/mirrord/intproxy/protocol/src/lib.rs b/mirrord/intproxy/protocol/src/lib.rs index ead2ac44761..87570596c4b 100644 --- a/mirrord/intproxy/protocol/src/lib.rs +++ b/mirrord/intproxy/protocol/src/lib.rs @@ -60,6 +60,19 @@ pub enum LayerToProxyMessage { GetEnv(GetEnvVarsRequest), } +/// Layer process information +#[derive(Encode, Decode, Debug)] +pub struct ProcessInfo { + /// Process ID. + pub pid: u32, + /// Process name. + pub name: String, + /// Command line + pub cmdline: Vec, + /// Is layer loaded? + pub loaded: bool, +} + /// Unique `layer <-> proxy` session identifier. /// New connection is established when the layer initializes or forks. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Encode, Decode)] @@ -78,7 +91,7 @@ pub struct LayerId(pub u64); #[derive(Encode, Decode, Debug)] pub enum NewSessionRequest { /// Layer initialized from its constructor, has a fresh state. - New, + New(ProcessInfo), /// Layer re-initialized from a [`fork`](https://man7.org/linux/man-pages/man2/fork.2.html) detour. /// It inherits state from its parent. Forked(LayerId), diff --git a/mirrord/intproxy/src/layer_initializer.rs b/mirrord/intproxy/src/layer_initializer.rs index 7dd5764dea3..1d3e36be2ee 100644 --- a/mirrord/intproxy/src/layer_initializer.rs +++ b/mirrord/intproxy/src/layer_initializer.rs @@ -6,6 +6,7 @@ use mirrord_intproxy_protocol::{ }; use thiserror::Error; use tokio::net::{TcpListener, TcpStream}; +use tracing::info; use crate::{ background_tasks::{BackgroundTask, MessageBus}, @@ -58,7 +59,10 @@ impl LayerInitializer { self.next_layer_id.0 += 1; let parent_id = match msg.inner { - LayerToProxyMessage::NewSession(NewSessionRequest::New) => None, + LayerToProxyMessage::NewSession(NewSessionRequest::New(process_info)) => { + info!(?process_info, "new session"); + None + } LayerToProxyMessage::NewSession(NewSessionRequest::Forked(parent)) => Some(parent), other => return Err(LayerInitializerError::UnexpectedMessage(other)), }; diff --git a/mirrord/layer/src/lib.rs b/mirrord/layer/src/lib.rs index bcc8e94e9b3..06ddb7407d6 100644 --- a/mirrord/layer/src/lib.rs +++ b/mirrord/layer/src/lib.rs @@ -68,7 +68,6 @@ extern crate core; use std::{ cmp::Ordering, collections::{HashMap, HashSet}, - ffi::OsString, net::SocketAddr, panic, sync::OnceLock, @@ -80,7 +79,7 @@ use error::{LayerError, Result}; use file::OPEN_FILES; use hooks::HookManager; use libc::{c_int, pid_t}; -use load::ExecutableName; +use load::ExecuteArgs; #[cfg(target_os = "macos")] use mirrord_config::feature::fs::FsConfig; use mirrord_config::{ @@ -151,27 +150,23 @@ fn setup() -> &'static LayerSetup { // The following statics are to avoid using CoreFoundation or high level macOS APIs // that aren't safe to use after fork. -/// Executable we're loaded to -static EXECUTABLE_NAME: OnceLock = OnceLock::new(); +/// Executable information (name, args) +static EXECUTABLE_ARGS: OnceLock = OnceLock::new(); /// Executable path we're loaded to static EXECUTABLE_PATH: OnceLock = OnceLock::new(); -/// Program arguments -static EXECUTABLE_ARGS: OnceLock> = OnceLock::new(); - /// Proxy Connection timeout /// Set to 10 seconds as most agent operations timeout after 5 seconds const PROXY_CONNECTION_TIMEOUT: Duration = Duration::from_secs(10); /// Loads mirrord configuration and does some patching (SIP, dotnet, etc) fn layer_pre_initialization() -> Result<(), LayerError> { - let given_process = EXECUTABLE_NAME.get_or_try_init(ExecutableName::from_env)?; + let given_process = EXECUTABLE_ARGS.get_or_try_init(ExecuteArgs::from_env)?; EXECUTABLE_PATH.get_or_try_init(|| { std::env::current_exe().map(|arg| arg.to_string_lossy().into_owned()) })?; - EXECUTABLE_ARGS.get_or_init(|| std::env::args_os().collect()); let config = LayerConfig::from_env()?; #[cfg(target_os = "macos")] @@ -193,7 +188,9 @@ fn layer_pre_initialization() -> Result<(), LayerError> { binary, EXECUTABLE_ARGS .get() - .expect("EXECUTABLE_ARGS needs to be set!"), + .expect("EXECUTABLE_ARGS needs to be set!") + .args + .clone(), ); tracing::error!("Couldn't execute {:?}", err); return Err(LayerError::ExecFailed(err)); @@ -229,9 +226,17 @@ fn load_only_layer_start(config: &LayerConfig) { .parse() .expect("failed to parse internal proxy address"); - let new_connection = - ProxyConnection::new(address, NewSessionRequest::New, PROXY_CONNECTION_TIMEOUT) - .expect("failed to initialize proxy connection"); + let new_connection = ProxyConnection::new( + address, + NewSessionRequest::New( + EXECUTABLE_ARGS + .get() + .expect("EXECUTABLE_ARGS MUST BE SET") + .to_process_info(config), + ), + PROXY_CONNECTION_TIMEOUT, + ) + .expect("failed to initialize proxy connection"); unsafe { // SAFETY @@ -328,6 +333,10 @@ fn layer_start(mut config: LayerConfig) { let debugger_ports = DebuggerPorts::from_env(); let local_hostname = trace_only || !config.feature.hostname; + let process_info = EXECUTABLE_ARGS + .get() + .expect("EXECUTABLE_ARGS MUST BE SET") + .to_process_info(&config); let state = LayerSetup::new(config, debugger_ports, local_hostname); SETUP.set(state).unwrap(); @@ -340,16 +349,7 @@ fn layer_start(mut config: LayerConfig) { let _detour_guard = DetourGuard::new(); tracing::info!("Initializing mirrord-layer!"); - tracing::trace!( - "Loaded into executable: {}, on pid {}, with args: {:?}", - EXECUTABLE_PATH - .get() - .expect("EXECUTABLE_PATH should be set!"), - std::process::id(), - EXECUTABLE_ARGS - .get() - .expect("EXECUTABLE_ARGS needs to be set!") - ); + tracing::trace!(executable = ?EXECUTABLE_PATH.get(), args = ?EXECUTABLE_ARGS.get(), pid = std::process::id(), "Loaded into executable"); if trace_only { tracing::debug!("Skipping new intproxy connection (trace only)"); @@ -358,9 +358,12 @@ fn layer_start(mut config: LayerConfig) { unsafe { let address = setup().proxy_address(); - let new_connection = - ProxyConnection::new(address, NewSessionRequest::New, PROXY_CONNECTION_TIMEOUT) - .expect("failed to initialize proxy connection"); + let new_connection = ProxyConnection::new( + address, + NewSessionRequest::New(process_info), + PROXY_CONNECTION_TIMEOUT, + ) + .expect("failed to initialize proxy connection"); PROXY_CONNECTION .set(new_connection) .expect("setting PROXY_CONNECTION singleton") diff --git a/mirrord/layer/src/load.rs b/mirrord/layer/src/load.rs index ca81d830b4d..ead7911865a 100644 --- a/mirrord/layer/src/load.rs +++ b/mirrord/layer/src/load.rs @@ -1,10 +1,12 @@ use std::{ collections::HashSet, + ffi::OsString, fmt::{self, Display}, sync::LazyLock, }; use mirrord_config::{util::VecOrSingle, LayerConfig}; +use mirrord_intproxy_protocol::ProcessInfo; use tracing::trace; use crate::error::LayerError; @@ -32,20 +34,22 @@ static BUILD_TOOL_PROCESSES: LazyLock> = LazyLock::new(|| { "cargo-watch", "debugserver", "jspawnhelper", - "JetBrains.Debugger.Worker", ]) }); /// Credentials of the process the layer is injected into. -pub struct ExecutableName { +#[derive(Debug)] +pub struct ExecuteArgs { /// Executable file name, for example `x86_64-linux-gnu-ld.bfd`. exec_name: String, /// Last part of the process name as seen in the arguments, for example `ld` extracted from /// `/usr/bin/ld`. invoked_as: String, + /// Executable arguments + pub(crate) args: Vec, } -impl ExecutableName { +impl ExecuteArgs { /// Creates a new instance of this struct using [`std::env::current_exe`] and /// [`std::env::args`]. pub(crate) fn from_env() -> Result { @@ -58,6 +62,7 @@ impl ExecutableName { }) .ok_or(LayerError::NoProcessFound)?; + let args = std::env::args_os().collect(); let invoked_as = std::env::args() .next() .as_ref() @@ -68,6 +73,7 @@ impl ExecutableName { Ok(Self { exec_name, invoked_as, + args, }) } @@ -92,6 +98,21 @@ impl ExecutableName { return false; } + // ignore intellij debugger https://github.com/metalbear-co/mirrord/issues/2408 + // don't put it in build tools since we don't want to SIP load on macOS. (leads to above + // issue) + if self + .exec_name + .as_str() + .ends_with("JetBrains.Debugger.Worker") + || self + .invoked_as + .as_str() + .ends_with("JetBrains.Debugger.Worker") + { + return false; + } + !skip_processes .iter() .any(|name| name.as_ref() == self.exec_name || name.as_ref() == self.invoked_as) @@ -119,9 +140,22 @@ impl ExecutableName { LoadType::Skip } } + + pub(crate) fn to_process_info(&self, config: &LayerConfig) -> ProcessInfo { + ProcessInfo { + pid: std::process::id(), + name: self.exec_name.clone(), + cmdline: self + .args + .iter() + .map(|arg| arg.to_string_lossy().to_string()) + .collect(), + loaded: matches!(self.load_type(config), LoadType::Full), + } + } } -impl Display for ExecutableName { +impl Display for ExecuteArgs { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{} invoked as {}", self.exec_name, self.invoked_as) } @@ -137,7 +171,7 @@ mod sip { static SIP_ONLY_PROCESSES: LazyLock> = LazyLock::new(|| HashSet::from(["sh", "bash", "env", "go", "dlv"])); - pub fn is_sip_only(given_process: &ExecutableName) -> bool { + pub fn is_sip_only(given_process: &ExecuteArgs) -> bool { given_process.is_build_tool() || SIP_ONLY_PROCESSES.contains(given_process.exec_name.as_str()) || SIP_ONLY_PROCESSES.contains(given_process.invoked_as.as_str()) @@ -177,9 +211,10 @@ mod tests { #[case] skip_processes: &[&str], #[case] skip_build_tools: bool, ) { - let executable_name = ExecutableName { + let executable_name = ExecuteArgs { exec_name: exec_name.to_string(), invoked_as: invoked_as.to_string(), + args: Vec::new(), }; assert!(executable_name.should_load(skip_processes, skip_build_tools)); @@ -198,9 +233,10 @@ mod tests { #[case] skip_processes: &[&str], #[case] skip_build_tools: bool, ) { - let executable_name = ExecutableName { + let executable_name = ExecuteArgs { exec_name: exec_name.to_string(), invoked_as: invoked_as.to_string(), + args: Vec::new(), }; assert!(!executable_name.should_load(skip_processes, skip_build_tools));