From a1422df96143f40dc1fc33f75ef69dbe1d9b3776 Mon Sep 17 00:00:00 2001 From: baldurk Date: Wed, 26 Apr 2023 14:01:54 +0100 Subject: [PATCH] Force use of getenv/setenv directly from libc to avoid bash override * Bash overrides getenv/setenv to look up its own variable set, but breaks since we need to modify the environment before main() when it initialises its variable set. Instead what happens is the first setenv initialises that variable in a blank set and so all subsequent environment variables are NULL. Then in main() the variable set gets initialised from environ, removing any changes that were made. --- renderdoc/driver/vulkan/vk_posix.cpp | 15 +++++---- renderdoc/os/posix/linux/linux_hook.cpp | 3 +- renderdoc/os/posix/linux/linux_process.cpp | 24 +++++++++++++- renderdoc/os/posix/linux/linux_stringio.cpp | 10 +++--- renderdoc/os/posix/posix_process.cpp | 35 +++++++++++++++++---- renderdoc/os/posix/posix_stringio.cpp | 20 ++++++------ 6 files changed, 75 insertions(+), 32 deletions(-) diff --git a/renderdoc/driver/vulkan/vk_posix.cpp b/renderdoc/driver/vulkan/vk_posix.cpp index 75a65f17f..32fed985e 100644 --- a/renderdoc/driver/vulkan/vk_posix.cpp +++ b/renderdoc/driver/vulkan/vk_posix.cpp @@ -373,15 +373,14 @@ rdcstr LayerRegistrationPath(LayerPath path) RENDERDOC_VULKAN_JSON_SUFFIX) ".json"; case LayerPath::home: { - const char *xdg = getenv("XDG_DATA_HOME"); - if(xdg && FileIO::exists(xdg)) - return rdcstr(xdg) + "/vulkan/implicit_layer.d/renderdoc_capture" STRINGIZE( - RENDERDOC_VULKAN_JSON_SUFFIX) ".json"; + rdcstr xdg = Process::GetEnvVariable("XDG_DATA_HOME"); + if(!xdg.empty() && FileIO::exists(xdg)) + return xdg + "/vulkan/implicit_layer.d/renderdoc_capture" STRINGIZE( + RENDERDOC_VULKAN_JSON_SUFFIX) ".json"; - const char *home_path = getenv("HOME"); - return rdcstr(home_path != NULL ? home_path : "") + - "/.local/share/vulkan/implicit_layer.d/renderdoc_capture" STRINGIZE( - RENDERDOC_VULKAN_JSON_SUFFIX) ".json"; + rdcstr home_path = Process::GetEnvVariable("HOME"); + return home_path + "/.local/share/vulkan/implicit_layer.d/renderdoc_capture" STRINGIZE( + RENDERDOC_VULKAN_JSON_SUFFIX) ".json"; } default: break; } diff --git a/renderdoc/os/posix/linux/linux_hook.cpp b/renderdoc/os/posix/linux/linux_hook.cpp index c28ea0f7d..f6d2ecebf 100644 --- a/renderdoc/os/posix/linux/linux_hook.cpp +++ b/renderdoc/os/posix/linux/linux_hook.cpp @@ -102,6 +102,7 @@ void ResetHookingEnvVars(); void StopAtMainInChild(); bool StopChildAtMain(pid_t childPid); void ResumeProcess(pid_t childPid, uint32_t delay = 0); +int direct_setenv(const char *name, const char *value, int overwrite); /////////////////////////////////////////////////////////////// // exec hooks - we have to hook each variant since if the application calls the 'real' one of a @@ -293,7 +294,7 @@ __attribute__((visibility("default"))) pid_t fork() pid_t ret = realfork(); if(ret == 0) - unsetenv(RENDERDOC_VULKAN_LAYER_VAR); + direct_setenv(RENDERDOC_VULKAN_LAYER_VAR, "", true); return ret; } diff --git a/renderdoc/os/posix/linux/linux_process.cpp b/renderdoc/os/posix/linux/linux_process.cpp index 7be046836..694275acb 100644 --- a/renderdoc/os/posix/linux/linux_process.cpp +++ b/renderdoc/os/posix/linux/linux_process.cpp @@ -22,6 +22,7 @@ * THE SOFTWARE. ******************************************************************************/ +#include #include #include #include @@ -717,9 +718,30 @@ bool OSUtility::DebuggerPresent() return debuggerPresent; } +using PFN_getenv = decltype(&getenv); + rdcstr Process::GetEnvVariable(const rdcstr &name) { - const char *val = getenv(name.c_str()); + const char *val = NULL; + // try to bypass any hooks to ensure we don't break (looking at you bash) + + static PFN_getenv dyn_getenv = NULL; + static bool checked = false; + if(!checked) + { + checked = true; + void *libc = dlopen("libc.so.6", RTLD_NOLOAD | RTLD_GLOBAL | RTLD_NOW); + if(libc) + { + dyn_getenv = (PFN_getenv)dlsym(libc, "getenv"); + } + } + + if(dyn_getenv) + val = dyn_getenv(name.c_str()); + else + val = getenv(name.c_str()); + return val ? val : rdcstr(); } diff --git a/renderdoc/os/posix/linux/linux_stringio.cpp b/renderdoc/os/posix/linux/linux_stringio.cpp index 5228a4483..2f37b4f23 100644 --- a/renderdoc/os/posix/linux/linux_stringio.cpp +++ b/renderdoc/os/posix/linux/linux_stringio.cpp @@ -596,18 +596,18 @@ rdcstr GetTempRootPath() rdcstr GetAppFolderFilename(const rdcstr &filename) { passwd *pw = getpwuid(getuid()); - const char *homedir = pw ? pw->pw_dir : NULL; + rdcstr homedir = pw ? pw->pw_dir : ""; - if(!homedir) - homedir = getenv("HOME"); + if(homedir.empty()) + homedir = Process::GetEnvVariable("HOME"); - if(!homedir) + if(homedir.empty()) { RDCERR("Can't get HOME directory, defaulting to '/' instead"); homedir = ""; } - rdcstr ret = rdcstr(homedir) + "/.renderdoc/"; + rdcstr ret = homedir + "/.renderdoc/"; mkdir(ret.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); diff --git a/renderdoc/os/posix/posix_process.cpp b/renderdoc/os/posix/posix_process.cpp index 45bcddbc7..802c2a804 100644 --- a/renderdoc/os/posix/posix_process.cpp +++ b/renderdoc/os/posix/posix_process.cpp @@ -308,7 +308,7 @@ static rdcstr shellExpand(const rdcstr &in) // if it's ~/... then replace with $HOME and return if(path[0] == '~' && path[1] == '/') - return rdcstr(getenv("HOME")) + path.substr(1); + return Process::GetEnvVariable("HOME") + path.substr(1); // if it's ~user/... then use getpwname if(path[0] == '~') @@ -341,6 +341,29 @@ static rdcstr shellExpand(const rdcstr &in) return path; } +using PFN_setenv = decltype(&setenv); + +int direct_setenv(const char *name, const char *value, int overwrite) +{ +// on linux try to bypass any hooks to ensure we don't break (looking at you bash) +#if ENABLED(RDOC_LINUX) + static PFN_setenv dyn_setenv = NULL; + static bool checked = false; + if(!checked) + { + checked = true; + void *libc = dlopen("libc.so.6", RTLD_NOLOAD | RTLD_GLOBAL | RTLD_NOW); + if(libc) + dyn_setenv = (PFN_setenv)dlsym(libc, "setenv"); + } + + if(dyn_setenv) + return dyn_setenv(name, value, overwrite); +#endif + + return setenv(name, value, overwrite); +} + void Process::RegisterEnvironmentModification(const EnvironmentModification &modif) { GetEnvModifications().push_back(modif); @@ -397,7 +420,7 @@ void ApplyEnvironmentModifications(rdcarray &modificati ApplySingleEnvMod(m, value); - setenv(m.name.c_str(), value.c_str(), true); + direct_setenv(m.name.c_str(), value.c_str(), true); } } @@ -875,10 +898,10 @@ void GetHookedEnvp(char *const *envp, rdcstr &envpStr, rdcarray &modifie void ResetHookingEnvVars() { - setenv(LIB_PATH_ENV_VAR, Process::GetEnvVariable("RENDERDOC_ORIGLIBPATH").c_str(), true); - setenv(PRELOAD_ENV_VAR, Process::GetEnvVariable("RENDERDOC_ORIGPRELOAD").c_str(), true); - unsetenv("RENDERDOC_ORIGLIBPATH"); - unsetenv("RENDERDOC_ORIGPRELOAD"); + direct_setenv(LIB_PATH_ENV_VAR, Process::GetEnvVariable("RENDERDOC_ORIGLIBPATH").c_str(), true); + direct_setenv(PRELOAD_ENV_VAR, Process::GetEnvVariable("RENDERDOC_ORIGPRELOAD").c_str(), true); + direct_setenv("RENDERDOC_ORIGLIBPATH", "", true); + direct_setenv("RENDERDOC_ORIGPRELOAD", "", true); } rdcpair Process::LaunchAndInjectIntoProcess( diff --git a/renderdoc/os/posix/posix_stringio.cpp b/renderdoc/os/posix/posix_stringio.cpp index c29b5cecf..64c7800fd 100644 --- a/renderdoc/os/posix/posix_stringio.cpp +++ b/renderdoc/os/posix/posix_stringio.cpp @@ -107,13 +107,12 @@ rdcstr FindFileInPath(const rdcstr &fileName) // Search the PATH directory list for the application (like shell which) to get the absolute path // Return "" if no exectuable found in the PATH list - char *pathEnvVar = getenv("PATH"); - if(!pathEnvVar) + rdcstr pathEnvVar = Process::GetEnvVariable("PATH"); + if(pathEnvVar.empty()) return filePath; // Make a copy of our PATH so strtok can insert NULL without actually changing env - char *localPath = new char[strlen(pathEnvVar) + 1]; - strcpy(localPath, pathEnvVar); + char *localPath = pathEnvVar.data(); const char *pathSeparator = ":"; const char *path = strtok(localPath, pathSeparator); @@ -129,7 +128,6 @@ rdcstr FindFileInPath(const rdcstr &fileName) path = strtok(NULL, pathSeparator); } - delete[] localPath; return filePath; } @@ -210,10 +208,10 @@ void GetDefaultFiles(const rdcstr &logBaseName, rdcstr &capture_filename, rdcstr strcpy(temp_folder, GetTempRootPath().c_str()); - char *temp_override = getenv("RENDERDOC_TEMP"); - if(temp_override && temp_override[0] == '/') + rdcstr temp_override = Process::GetEnvVariable("RENDERDOC_TEMP"); + if(!temp_override.empty() && temp_override[0] == '/') { - strncpy(temp_folder, temp_override, sizeof(temp_folder) - 1); + strncpy(temp_folder, temp_override.c_str(), sizeof(temp_folder) - 1); size_t len = strlen(temp_folder); while(temp_folder[len - 1] == '/') temp_folder[--len] = 0; @@ -224,9 +222,9 @@ void GetDefaultFiles(const rdcstr &logBaseName, rdcstr &capture_filename, rdcstr 1900 + now.tm_year, now.tm_mon + 1, now.tm_mday, now.tm_hour, now.tm_min); // set by UI when launching programs so all logging goes to the same file - char *logfile_override = getenv("RENDERDOC_DEBUG_LOG_FILE"); - if(logfile_override) - logging_filename = rdcstr(logfile_override); + rdcstr logfile_override = Process::GetEnvVariable("RENDERDOC_DEBUG_LOG_FILE"); + if(!logfile_override.empty()) + logging_filename = logfile_override; else logging_filename = StringFormat::Fmt( "%s/RenderDoc/%s_%04d.%02d.%02d_%02d.%02d.%02d.log", temp_folder, logBaseName.c_str(),