From 13b77f3bd3dbf13c8d677dc611c072c4caf876c3 Mon Sep 17 00:00:00 2001 From: Kai Krakow Date: Sun, 15 Mar 2026 17:46:27 +0100 Subject: [PATCH 1/3] overlay: remove dead MANGOHUD_RECURSION code when glxinfo is disabled When `glxinfo` is not called, setting up `MANGOHUD_RECURSION` is dead code: we read, set and unset the variable without any effect. Dropping this block also avoids unnecessary environment mutation in-process. `getenv()`/`setenv()` are not thread-safe in general, so avoiding them here reduces risk in multi-threaded contexts. v2: Dropping this code completely instead of only disabling it as per the discussion with @flightlessmango. Ref: https://github.com/flightlessmango/MangoHud/pull/1983#discussion_r2936990573 --- src/overlay.cpp | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/overlay.cpp b/src/overlay.cpp index fe919a93ee..daa79a3abf 100644 --- a/src/overlay.cpp +++ b/src/overlay.cpp @@ -779,16 +779,6 @@ void init_system_info(){ trim(os); cpusched = read_line("/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor"); - const char* mangohud_recursion = getenv("MANGOHUD_RECURSION"); - if (!mangohud_recursion) { - setenv("MANGOHUD_RECURSION", "1", 1); - // driver = exec("glxinfo -B | sed -n 's/^OpenGL version.*: \\(.*\\)/\\1/p' | sed 's/([^)]*)//g;s/ / /g'"); - // trim(driver); - unsetenv("MANGOHUD_RECURSION"); - } else { - driver = "MangoHud glxinfo recursion detected"; - } - // Get WINE version wineProcess = get_exe_path(); From 1f5a7a1acf1db5d76020631bc01b86492b8e117b Mon Sep 17 00:00:00 2001 From: Kai Krakow Date: Sun, 8 Mar 2026 21:43:58 +0100 Subject: [PATCH 2/3] overlay: replace shell spawns in init_system_info() with native code Spawning a shell from inside pressure-vessel is fragile and can fail due to seccomp constraints, causing noisy logs and host coredumps. Replace the shell pipeline (`sh|sed|tail`) used in `init_system_info()` with native parsing and Linux APIs where available. This avoids subprocess creation entirely for the covered paths. This does not address every shell-based code path yet (for example the disabled OpenGL version reader could be migrated similarly), but that is outside the scope of this change. The primary goal is to prevent `/usr/bin/dash` crashes from the Steam Runtime when MangoHud runs inside pressure-vessel. Ref: https://github.com/ValveSoftware/steam-runtime/issues/804 --- src/overlay.cpp | 69 +++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 58 insertions(+), 11 deletions(-) diff --git a/src/overlay.cpp b/src/overlay.cpp index daa79a3abf..2af192bcb3 100644 --- a/src/overlay.cpp +++ b/src/overlay.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -32,6 +33,8 @@ #ifdef __linux__ #include #include +#include +#include #endif namespace fs = ghc::filesystem; @@ -762,23 +765,67 @@ struct pci_bus { int func; }; +static std::string parse_value_from_file(std::ifstream& file, const std::string& key, const std::string& separator) { + std::string line; + + while (std::getline(file, line)) { + const auto position = line.find(separator); + if (std::string::npos == position) continue; + + auto current_key = line.substr(0, position); trim(current_key); + auto current_value = line.substr(position + separator.size()); trim(current_value); + + if (current_key == key && !current_value.empty()) + return current_value; + } + return std::string(); +} + +static void strip_parenthesized_blocks(std::string& s) { + std::size_t pos = 0; + while (std::string::npos != (pos = s.find('(', pos))) { + const auto end = s.find(')', pos + 1); + if (end == std::string::npos) break; + s.erase(pos, end - pos + 1); + } + + while (std::string::npos != s.find(" ")) + s.replace(s.find(" "), 2, " "); + + trim(s); +} + void init_system_info(){ #ifdef __linux__ + struct sysinfo info_buf; + if (0 == sysinfo(&info_buf)) { + auto ram_bytes = static_cast(info_buf.totalram) * info_buf.mem_unit; + ram = std::to_string(ram_bytes >> 10); + } else { + ram.clear(); + } + + struct utsname uts_buf; + if (0 == uname(&uts_buf)) { + kernel = uts_buf.release; + } else { + kernel.clear(); + } + + std::ifstream cpuinfo("/proc/cpuinfo"); + cpu = parse_value_from_file(cpuinfo, "model name", ":"); + strip_parenthesized_blocks(cpu); + + std::ifstream osrel("/etc/os-release"); + os = parse_value_from_file(osrel, "PRETTY_NAME", "="); + os.erase(std::remove(os.begin(), os.end(), '\"' ), os.end()); + + cpusched = read_line("/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor"); + const char* ld_preload = getenv("LD_PRELOAD"); if (ld_preload) unsetenv("LD_PRELOAD"); - ram = exec("sed -n 's/^MemTotal: *\\([0-9]*\\).*/\\1/p' /proc/meminfo"); - trim(ram); - cpu = exec("sed -n 's/^model name.*: \\(.*\\)/\\1/p' /proc/cpuinfo | sed 's/([^)]*)//g' | tail -n1"); - trim(cpu); - kernel = exec("uname -r"); - trim(kernel); - os = exec("sed -n 's/PRETTY_NAME=\\(.*\\)/\\1/p' /etc/os-release"); - os.erase(remove(os.begin(), os.end(), '\"' ), os.end()); - trim(os); - cpusched = read_line("/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor"); - // Get WINE version wineProcess = get_exe_path(); From 64dbedd601da4aecd417a19538e9c3e760c9ece7 Mon Sep 17 00:00:00 2001 From: Kai Krakow Date: Sun, 8 Mar 2026 21:56:55 +0100 Subject: [PATCH 3/3] overlay: narrow getenv()/setenv() scope to the remaining callout A previous commit removed subprocess spawners from `init_system_info()`. This allows us to reduce `getenv()`/`setenv()` usage to the absolute minimum and lower risk in multi-threaded contexts. The environment workaround is now only applied around the remaining subprocess call used to query the Wine version. Also add a preprocessor error in the disabled OpenGL callout block so the `LD_PRELOAD` workaround is not missed if that callout is re-enabled. --- src/overlay.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/overlay.cpp b/src/overlay.cpp index 2af192bcb3..3aca55bb79 100644 --- a/src/overlay.cpp +++ b/src/overlay.cpp @@ -822,10 +822,6 @@ void init_system_info(){ cpusched = read_line("/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor"); - const char* ld_preload = getenv("LD_PRELOAD"); - if (ld_preload) - unsetenv("LD_PRELOAD"); - // Get WINE version wineProcess = get_exe_path(); @@ -864,14 +860,20 @@ void init_system_info(){ findVersion << "\"" << dir << "/wine\" --version"; else findVersion << "\"" << dir << "/wine64\" --version"; + const char* ld_preload = getenv("LD_PRELOAD"); + if (ld_preload) + unsetenv("LD_PRELOAD"); const char *wine_env = getenv("WINELOADERNOEXEC"); if (wine_env) unsetenv("WINELOADERNOEXEC"); + wineVersion = exec(findVersion.str()); trim(wineVersion); SPDLOG_DEBUG("WINE version: {}", wineVersion); if (wine_env) setenv("WINELOADERNOEXEC", wine_env, 1); + if (ld_preload) + setenv("LD_PRELOAD", ld_preload, 1); } } else { @@ -880,9 +882,6 @@ void init_system_info(){ check_for_vkbasalt_and_gamemode(); - if (ld_preload) - setenv("LD_PRELOAD", ld_preload, 1); - SPDLOG_DEBUG("Ram:{}", ram); SPDLOG_DEBUG("Cpu:{}", cpu); SPDLOG_DEBUG("Kernel:{}", kernel);