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.
This commit is contained in:
baldurk
2023-04-26 14:01:54 +01:00
parent 9998714b50
commit a1422df961
6 changed files with 75 additions and 32 deletions
+7 -8
View File
@@ -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;
}
+2 -1
View File
@@ -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;
}
+23 -1
View File
@@ -22,6 +22,7 @@
* THE SOFTWARE.
******************************************************************************/
#include <dlfcn.h>
#include <elf.h>
#include <stdlib.h>
#include <sys/ptrace.h>
@@ -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();
}
+5 -5
View File
@@ -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);
+29 -6
View File
@@ -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<EnvironmentModification> &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<char *> &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<RDResult, uint32_t> Process::LaunchAndInjectIntoProcess(
+9 -11
View File
@@ -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(),