From c614355753b9c2ab60bc55b7deefc09c6d0def20 Mon Sep 17 00:00:00 2001 From: arookieofc Date: Sat, 7 Nov 2026 19:11:15 +0800 Subject: [PATCH] fix(appimage): drop env entries of a previous update's mount (#1969) An in-app update restart inherits the old mount's entries alongside the new one's: the restarted IDE's LD_LIBRARY_PATH holds both mounts, and children (host curl downloading a language server) still load the old image's bundled libraries. appimage_env_fixes now drops ':'-separated entries under any /tmp/.mount_* path, not just the current $APPDIR's: an entry under a different mount is another image's, never the host's. Fixes #1966 --- crates/ide-core/src/process.rs | 38 +++++++++++++++++++++++++++++----- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/crates/ide-core/src/process.rs b/crates/ide-core/src/process.rs index 1778d0c0f..0eb1bae87 100644 --- a/crates/ide-core/src/process.rs +++ b/crates/ide-core/src/process.rs @@ -299,9 +299,11 @@ fn appimage_fixes() -> Vec<(OsString, Option)> { const APPIMAGE_LAUNCH_VARS: [&str; 4] = ["APPDIR", "APPIMAGE", "ARGV0", "OWD"]; /// What to change in `vars` (an environment) so a child sees the host's environment rather -/// than the AppImage's: every `:`-separated entry inside `$APPDIR` is dropped (a saved -/// `_ORIG` wins when present), variables left empty are removed, and so are the -/// runtime's own launch variables. Nothing changes when `APPDIR` is not set. +/// than the AppImage's: every `:`-separated entry inside `$APPDIR` — or inside *any* +/// AppImage mount, since an in-app update restart inherits the previous mount's entries +/// alongside the new one — is dropped (a saved `_ORIG` wins when present), +/// variables left empty are removed, and so are the runtime's own launch variables. +/// Nothing changes when `APPDIR` is not set. fn appimage_env_fixes(vars: &[(OsString, OsString)]) -> Vec<(OsString, Option)> { let get = |name: &str| vars.iter().find(|(k, _)| k == name).map(|(_, v)| v); let Some(appdir) = @@ -309,7 +311,12 @@ fn appimage_env_fixes(vars: &[(OsString, OsString)]) -> Vec<(OsString, Option`; an entry under another mount is + // another image's (e.g. the pre-update one a restart inherited), never the host's. + own || entry.starts_with("/tmp/.mount_") + }; let mut fixes = Vec::new(); for (key, value) in vars { let (Some(name), Some(value)) = (key.to_str(), value.to_str()) else { continue }; @@ -715,7 +722,7 @@ mod tests { ("PATH", "/tmp/.mount_OpenIDabc/usr/bin/:/tmp/.mount_OpenIDabc/usr/sbin/:/usr/local/bin:/usr/bin"), ("XDG_DATA_DIRS", "/tmp/.mount_OpenIDabc/usr/share:/usr/share:/usr/local/share:/usr/share"), ("HOME", "/home/me"), - ("JAVA_HOME", "/tmp/.mount_OpenIDabcdef/jdk"), + ("JAVA_HOME", "/opt/jdk"), ]); assert_eq!( got, @@ -728,6 +735,27 @@ mod tests { ); } + #[test] + fn entries_of_a_previous_updates_mount_are_dropped() { + // An in-app update restart inherits the old mount's entries: a child's curl + // must not load the old image's libraries (they can still be mounted). + let got = fixes(&[ + ("APPDIR", "/tmp/.mount_OpenIDjIlFFA"), + ("LD_LIBRARY_PATH", "/tmp/.mount_OpenIDjIlFFA/usr/lib/:/tmp/.mount_OpenIDdaNGfM/usr/lib/:/usr/local/lib"), + ("PATH", "/tmp/.mount_OpenIDjIlFFA/usr/bin/:/tmp/.mount_OpenIDdaNGfM/usr/bin/:/usr/bin"), + ("GIO_MODULE_DIR", "/tmp/.mount_OpenIDdaNGfM/usr/lib/gio/modules"), + ]); + assert_eq!( + got, + [ + fix("APPDIR", None), + fix("LD_LIBRARY_PATH", Some("/usr/local/lib")), + fix("PATH", Some("/usr/bin")), + fix("GIO_MODULE_DIR", None), + ] + ); + } + #[test] fn variables_left_empty_are_removed() { // AppRun prepends to an unset variable, leaving a trailing `:`; hooks set single paths.