Ensure we hook all exec* variants not just 'terminal' ones. Closes #2301

This commit is contained in:
baldurk
2021-07-13 16:43:24 +01:00
parent 9bffeb3c37
commit e889e4aa52
2 changed files with 123 additions and 37 deletions
+118 -37
View File
@@ -30,6 +30,7 @@
#include <map>
#include "common/threading.h"
#include "core/core.h"
#include "core/settings.h"
#include "hooks/hooks.h"
#include "os/os_specific.h"
#include "plthook/plthook.h"
@@ -37,6 +38,8 @@
Threading::CriticalSection libLock;
RDOC_EXTERN_CONFIG(bool, Linux_Debug_PtraceLogging);
static std::map<rdcstr, rdcarray<FunctionLoadCallback>> libraryCallbacks;
static rdcarray<rdcstr> libraryHooks;
static rdcarray<FunctionHook> functionHooks;
@@ -97,47 +100,95 @@ void StopAtMainInChild();
bool StopChildAtMain(pid_t childPid);
void ResumeProcess(pid_t childPid, uint32_t delay = 0);
__attribute__((visibility("default"))) int execle(const char *pathname, const char *arg, ...)
{
va_list args;
va_start(args, arg);
rdcarray<char *> argList;
argList.push_back((char *)arg);
while(true)
{
char *nextArg = va_arg(args, char *);
argList.push_back(nextArg);
// list is terminated with a NULL
if(!nextArg)
break;
}
char **envp = va_arg(args, char **);
///////////////////////////////////////////////////////////////
// exec hooks - we have to hook each variant since if the application calls the 'real' one of a
// variant, even if it ultimately goes to execve it will be resolved to the real libc one as libc
// doesn't get LD_PRELOAD hooked.
//
// There are two 'real' implementations we have, execve and execvpe that forward to the real
// function after patching the environment. That's to allow the real function to handle the path
// handling in the vpe case, otherwise they're identical.
//
// The other variants all forward to one of those - the 'l' cases unroll the va_args first before
// calling onwards
#define GET_EXECL_PARAMS(has_e) \
va_list args; \
va_start(args, arg); \
\
rdcarray<char *> arglist; \
arglist.push_back((char *)arg); \
\
while(true) \
{ \
char *nextArg = va_arg(args, char *); \
arglist.push_back(nextArg); \
\
/* list is terminated with a NULL */ \
if(!nextArg) \
break; \
} \
\
char **envp = NULL; \
if(has_e) \
envp = va_arg(args, char **); \
\
va_end(args);
if(!realexecve)
{
EXECVEPROC passthru = (EXECVEPROC)dlsym(RTLD_NEXT, "execve");
return passthru(pathname, argList.data(), envp);
}
__attribute__((visibility("default"))) int execl(const char *pathname, const char *arg, ...)
{
GET_EXECL_PARAMS(false);
rdcarray<char *> modifiedEnv;
rdcstr envpStr;
if(Linux_Debug_PtraceLogging())
RDCLOG("execl(%s)", pathname);
// if we're not hooking children just call to the real one, but ensure we remove any hooking env
// vars that were kept around after initialisation
if(!RenderDoc::Inst().GetCaptureOptions().hookIntoChildren)
{
GetUnhookedEnvp(envp, envpStr, modifiedEnv);
return realexecve(pathname, argList.data(), modifiedEnv.data());
}
return execve(pathname, arglist.data(), environ);
}
GetHookedEnvp(envp, envpStr, modifiedEnv);
return realexecve(pathname, argList.data(), modifiedEnv.data());
__attribute__((visibility("default"))) int execlp(const char *pathname, const char *arg, ...)
{
GET_EXECL_PARAMS(false);
if(Linux_Debug_PtraceLogging())
RDCLOG("execlp(%s)", pathname);
return execvpe(pathname, arglist.data(), environ);
}
__attribute__((visibility("default"))) int execle(const char *pathname, const char *arg, ...)
{
GET_EXECL_PARAMS(true);
if(Linux_Debug_PtraceLogging())
RDCLOG("execle(%s)", pathname);
return execve(pathname, arglist.data(), envp);
}
__attribute__((visibility("default"))) int execlpe(const char *pathname, const char *arg, ...)
{
GET_EXECL_PARAMS(true);
if(Linux_Debug_PtraceLogging())
RDCLOG("execlpe(%s)", pathname);
return execvpe(pathname, arglist.data(), envp);
}
__attribute__((visibility("default"))) int execv(const char *pathname, char *const argv[])
{
if(Linux_Debug_PtraceLogging())
RDCLOG("execv(%s)", pathname);
return execve(pathname, argv, environ);
}
__attribute__((visibility("default"))) int execvp(const char *pathname, char *const argv[])
{
if(Linux_Debug_PtraceLogging())
RDCLOG("execvp(%s)", pathname);
return execvpe(pathname, argv, environ);
}
__attribute__((visibility("default"))) int execve(const char *pathname, char *const argv[],
@@ -145,6 +196,9 @@ __attribute__((visibility("default"))) int execve(const char *pathname, char *co
{
if(!realexecve)
{
if(Linux_Debug_PtraceLogging())
RDCLOG("unhooked early execve(%s)", pathname);
EXECVEPROC passthru = (EXECVEPROC)dlsym(RTLD_NEXT, "execve");
return passthru(pathname, argv, envp);
}
@@ -156,10 +210,16 @@ __attribute__((visibility("default"))) int execve(const char *pathname, char *co
// vars that were kept around after initialisation
if(!RenderDoc::Inst().GetCaptureOptions().hookIntoChildren)
{
if(Linux_Debug_PtraceLogging())
RDCLOG("unhooked execve(%s)", pathname);
GetUnhookedEnvp(envp, envpStr, modifiedEnv);
return realexecve(pathname, argv, envp);
return realexecve(pathname, argv, modifiedEnv.data());
}
if(Linux_Debug_PtraceLogging())
RDCLOG("hooked execve(%s)", pathname);
GetHookedEnvp(envp, envpStr, modifiedEnv);
return realexecve(pathname, argv, modifiedEnv.data());
}
@@ -169,6 +229,9 @@ __attribute__((visibility("default"))) int execvpe(const char *pathname, char *c
{
if(!realexecvpe)
{
if(Linux_Debug_PtraceLogging())
RDCLOG("unhooked early execvpe(%s)", pathname);
EXECVPEPROC passthru = (EXECVPEPROC)dlsym(RTLD_NEXT, "execvpe");
return passthru(pathname, argv, envp);
}
@@ -180,10 +243,16 @@ __attribute__((visibility("default"))) int execvpe(const char *pathname, char *c
// vars that were kept around after initialisation
if(!RenderDoc::Inst().GetCaptureOptions().hookIntoChildren)
{
if(Linux_Debug_PtraceLogging())
RDCLOG("unhooked execvpe(%s)", pathname);
GetUnhookedEnvp(envp, envpStr, modifiedEnv);
return realexecvpe(pathname, argv, envp);
return realexecvpe(pathname, argv, modifiedEnv.data());
}
if(Linux_Debug_PtraceLogging())
RDCLOG("hooked execvpe(%s)", pathname);
GetHookedEnvp(envp, envpStr, modifiedEnv);
return realexecvpe(pathname, argv, modifiedEnv.data());
}
@@ -207,6 +276,9 @@ __attribute__((visibility("default"))) pid_t fork()
// envp because it was probably fetched before the fork - see the exec hooks for where we patch
// that part.
if(Linux_Debug_PtraceLogging())
RDCLOG("non-hooked fork()");
pid_t ret = realfork();
if(ret == 0)
unsetenv(RENDERDOC_VULKAN_LAYER_VAR);
@@ -214,6 +286,9 @@ __attribute__((visibility("default"))) pid_t fork()
return ret;
}
if(Linux_Debug_PtraceLogging())
RDCLOG("hooked fork()");
// fork in a captured application. Need to get the child ident and register it
// set up environment variables for hooking now
@@ -223,6 +298,9 @@ __attribute__((visibility("default"))) pid_t fork()
if(ret == 0)
{
if(Linux_Debug_PtraceLogging())
RDCLOG("hooked fork() in child %d", getpid());
StopAtMainInChild();
}
else if(ret > 0)
@@ -230,6 +308,9 @@ __attribute__((visibility("default"))) pid_t fork()
// restore environment variables
ResetHookingEnvVars();
if(Linux_Debug_PtraceLogging())
RDCLOG("hooked fork() in parent, child is %d", ret);
bool stopped = StopChildAtMain(ret);
if(stopped)
+5
View File
@@ -8,7 +8,12 @@
dlopen;
dlsym;
fork;
execl;
execlp;
execle;
execlpe;
execv;
execvp;
execve;
execvpe;
_exit;