зеркало из https://github.com/microsoft/BuildXL.git
Merged PR 776479: Add support for parsing argv on exec in ptrace sandbox
- Reports the process command line with ptrace
This commit is contained in:
Родитель
d0844d605f
Коммит
a8fe896fb7
|
@ -9,6 +9,7 @@
|
||||||
#include <sys/ptrace.h>
|
#include <sys/ptrace.h>
|
||||||
#include <sys/reg.h>
|
#include <sys/reg.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
|
#include <sys/user.h>
|
||||||
|
|
||||||
#define SYSCALL_NAME_TO_NUMBER(name) __NR_##name
|
#define SYSCALL_NAME_TO_NUMBER(name) __NR_##name
|
||||||
#define SYSCALL_NAME_STRING(name) #name
|
#define SYSCALL_NAME_STRING(name) #name
|
||||||
|
@ -341,51 +342,80 @@ void *PTraceSandbox::GetArgumentAddr(int index)
|
||||||
return (void *)addr;
|
return (void *)addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned long long PTraceSandbox::ArgumentIndexToRegister(int index, struct user_regs_struct *regs)
|
||||||
|
{
|
||||||
|
// Order of the arguments on the stack: %rdi, %rsi, %rdx, %rcx, %r8, and %r9
|
||||||
|
// Reference: http://6.s081.scripts.mit.edu/sp18/x86-64-architecture-guide.html#:~:text=The%20caller%20uses%20registers%20to,off%20the%20stack%20in%20order.
|
||||||
|
switch (index)
|
||||||
|
{
|
||||||
|
case 0: // Return value
|
||||||
|
return regs->rax;
|
||||||
|
case 1:
|
||||||
|
return regs->rdi;
|
||||||
|
case 2:
|
||||||
|
return regs->rsi;
|
||||||
|
case 3:
|
||||||
|
return regs->rdx;
|
||||||
|
case 4:
|
||||||
|
return regs->r10;
|
||||||
|
case 5:
|
||||||
|
return regs->r8;
|
||||||
|
case 6:
|
||||||
|
return regs->r9;
|
||||||
|
default:
|
||||||
|
// We don't currently support reading more than the 6 arguments above with ptrace
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::string PTraceSandbox::ReadArgumentString(char *syscall, int argumentIndex, bool nullTerminated, int length)
|
std::string PTraceSandbox::ReadArgumentString(char *syscall, int argumentIndex, bool nullTerminated, int length)
|
||||||
{
|
{
|
||||||
void *addr = GetArgumentAddr(argumentIndex);
|
void *addr = GetArgumentAddr(argumentIndex);
|
||||||
// We are only interested in reading paths from the arguments so PATH_MAX (+1 for null terminator) should be safe to use here
|
|
||||||
char argument[PATH_MAX + 1];
|
|
||||||
char *addrRegValue = (char *)ptrace(PTRACE_PEEKUSER, m_traceePid, addr, 0);
|
char *addrRegValue = (char *)ptrace(PTRACE_PEEKUSER, m_traceePid, addr, 0);
|
||||||
|
|
||||||
|
return ReadArgumentStringAtAddr(syscall, addrRegValue, nullTerminated, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string PTraceSandbox::ReadArgumentStringAtAddr(char *syscall, char *addr, bool nullTerminated, int length) {
|
||||||
int currentStringLength = 0;
|
int currentStringLength = 0;
|
||||||
|
std::string argument;
|
||||||
|
|
||||||
|
argument.reserve(PATH_MAX); // We are mostly interested in reading paths from the arguments so PATH_MAX should be enough here for most cases
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
long addrMemoryLocation = ptrace(PTRACE_PEEKTEXT, m_traceePid, addrRegValue, NULL);
|
long addrMemoryLocation = ptrace(PTRACE_PEEKTEXT, m_traceePid, addr, NULL);
|
||||||
if (addrMemoryLocation == -1)
|
if (addrMemoryLocation == -1)
|
||||||
{
|
{
|
||||||
BXL_LOG_DEBUG(m_bxl, "[PTrace] Error occured while executing PTRACE_PEEKTEXT for syscall '%s' argument '%d' : '%s'", syscall, argumentIndex, strerror(errno));
|
BXL_LOG_DEBUG(m_bxl, "[PTrace] Error occured while executing PTRACE_PEEKTEXT for syscall '%s' : '%s'", syscall, strerror(errno));
|
||||||
argument[currentStringLength] = '\0';
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
addrRegValue += sizeof(long);
|
addr += sizeof(long);
|
||||||
|
|
||||||
char *currentArgReadChar = (char *)&addrMemoryLocation;
|
char *currentArgReadChar = (char *)&addrMemoryLocation;
|
||||||
bool finishedReadingArgument = false;
|
bool finishedReadingArgument = false;
|
||||||
|
|
||||||
for (int i = 0; i < sizeof(long); i++)
|
for (int i = 0; i < sizeof(long); i++)
|
||||||
{
|
{
|
||||||
argument[currentStringLength] = *currentArgReadChar;
|
|
||||||
currentStringLength++;
|
|
||||||
|
|
||||||
if ((nullTerminated && *currentArgReadChar == '\0') || (length > 0 && currentStringLength == length))
|
if ((nullTerminated && *currentArgReadChar == '\0') || (length > 0 && currentStringLength == length))
|
||||||
{
|
{
|
||||||
finishedReadingArgument = true;
|
finishedReadingArgument = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
argument.push_back(*currentArgReadChar);
|
||||||
|
currentStringLength++;
|
||||||
currentArgReadChar++;
|
currentArgReadChar++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (finishedReadingArgument)
|
if (finishedReadingArgument)
|
||||||
{
|
{
|
||||||
argument[currentStringLength] = '\0';
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::string(argument);
|
return argument;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned long PTraceSandbox::ReadArgumentLong(int argumentIndex)
|
unsigned long PTraceSandbox::ReadArgumentLong(int argumentIndex)
|
||||||
|
@ -394,6 +424,40 @@ unsigned long PTraceSandbox::ReadArgumentLong(int argumentIndex)
|
||||||
return ptrace(PTRACE_PEEKUSER, m_traceePid, addr, NULL);
|
return ptrace(PTRACE_PEEKUSER, m_traceePid, addr, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string PTraceSandbox::ReadArgumentVector(char *syscall, int argumentIndex)
|
||||||
|
{
|
||||||
|
struct user_regs_struct regs;
|
||||||
|
ptrace(PTRACE_GETREGS, m_traceePid, 0, ®s);
|
||||||
|
|
||||||
|
auto addr = ArgumentIndexToRegister(argumentIndex, ®s); // Pointer to argv
|
||||||
|
bool firstArgument = true;
|
||||||
|
std::string arguments;
|
||||||
|
arguments.reserve(PATH_MAX);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
// Pointer to each individual element in the argv array
|
||||||
|
auto argPtr = ptrace(PTRACE_PEEKTEXT, m_traceePid, addr, NULL);
|
||||||
|
if (argPtr == -1) {
|
||||||
|
BXL_LOG_DEBUG(m_bxl, "[PTrace] Error occured while parsing arguments for syscall '%s' with error %s", syscall, strerror(errno));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argPtr == 0) {
|
||||||
|
// End of the argv array
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
arguments.append((!firstArgument ? " " : "") + ReadArgumentStringAtAddr(syscall, (char *)argPtr, /* nullTerminated */ true, /* length */ 0));
|
||||||
|
addr += sizeof(unsigned long long);
|
||||||
|
|
||||||
|
if (firstArgument) {
|
||||||
|
firstArgument = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return arguments;
|
||||||
|
}
|
||||||
|
|
||||||
int PTraceSandbox::GetErrno()
|
int PTraceSandbox::GetErrno()
|
||||||
{
|
{
|
||||||
long returnValue = ReadArgumentLong(0);
|
long returnValue = ReadArgumentLong(0);
|
||||||
|
@ -563,6 +627,10 @@ HANDLER_FUNCTION(execveat)
|
||||||
UpdateTraceeTableForExec(exePath);
|
UpdateTraceeTableForExec(exePath);
|
||||||
|
|
||||||
m_bxl->report_exec(SYSCALL_NAME_STRING(execveat), basename(mutableExePath), exePath.c_str(), /* error*/ 0, /* mode */ 0, m_traceePid);
|
m_bxl->report_exec(SYSCALL_NAME_STRING(execveat), basename(mutableExePath), exePath.c_str(), /* error*/ 0, /* mode */ 0, m_traceePid);
|
||||||
|
if (m_bxl->IsReportingProcessArgs()) {
|
||||||
|
m_bxl->report_exec_args(m_traceePid, ReadArgumentVector(SYSCALL_NAME_STRING(execveat), /* argumentIndex */ 3).c_str());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
HANDLER_FUNCTION(execve)
|
HANDLER_FUNCTION(execve)
|
||||||
|
@ -575,6 +643,9 @@ HANDLER_FUNCTION(execve)
|
||||||
UpdateTraceeTableForExec(file);
|
UpdateTraceeTableForExec(file);
|
||||||
|
|
||||||
m_bxl->report_exec(SYSCALL_NAME_STRING(execve), basename(mutableFilePath), file.c_str(), /* error */ 0, /* mode */ 0, m_traceePid);
|
m_bxl->report_exec(SYSCALL_NAME_STRING(execve), basename(mutableFilePath), file.c_str(), /* error */ 0, /* mode */ 0, m_traceePid);
|
||||||
|
if (m_bxl->IsReportingProcessArgs()) {
|
||||||
|
m_bxl->report_exec_args(m_traceePid, ReadArgumentVector(SYSCALL_NAME_STRING(execve), /* argumentIndex */ 2).c_str());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
HANDLER_FUNCTION(stat)
|
HANDLER_FUNCTION(stat)
|
||||||
|
|
|
@ -61,8 +61,18 @@ private:
|
||||||
|
|
||||||
void *GetArgumentAddr(int index);
|
void *GetArgumentAddr(int index);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Given a set of registers from PTRACE_GETREGS, this function will return the value of the argument at the given index.
|
||||||
|
*/
|
||||||
|
static unsigned long long ArgumentIndexToRegister(int index, struct user_regs_struct *regs);
|
||||||
|
|
||||||
// @brief Gets the offset to read an argument at a given index starting from 1 (0 is used for the return value of the function)
|
// @brief Gets the offset to read an argument at a given index starting from 1 (0 is used for the return value of the function)
|
||||||
std::string ReadArgumentString(char *syscall, int argumentIndex, bool nullTerminated, int length = 0);
|
std::string ReadArgumentString(char *syscall, int argumentIndex, bool nullTerminated, int length = 0);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Gets a string at the provided address.
|
||||||
|
*/
|
||||||
|
std::string ReadArgumentStringAtAddr(char *syscall, char *addr, bool nullTerminated, int length);
|
||||||
/*
|
/*
|
||||||
* @brief Reads an argument string at a given address with ptrace
|
* @brief Reads an argument string at a given address with ptrace
|
||||||
* @param argumentIndex Index of the argument to read starting from 1 (or 0 for the return value)
|
* @param argumentIndex Index of the argument to read starting from 1 (or 0 for the return value)
|
||||||
|
@ -71,6 +81,12 @@ private:
|
||||||
* @return String containing the argument
|
* @return String containing the argument
|
||||||
*/
|
*/
|
||||||
unsigned long ReadArgumentLong(int argumentIndex);
|
unsigned long ReadArgumentLong(int argumentIndex);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads a set of arguments provided to an execve or an execveat call.
|
||||||
|
*/
|
||||||
|
std::string ReadArgumentVector(char *syscall, int argumentIndex);
|
||||||
|
|
||||||
void ReportOpen(std::string path, int oflag, std::string syscallName);
|
void ReportOpen(std::string path, int oflag, std::string syscallName);
|
||||||
void ReportCreate(std::string syscallName, int dirfd, const char *pathname, mode_t mode, long returnValue = 0, bool checkCache = true);
|
void ReportCreate(std::string syscallName, int dirfd, const char *pathname, mode_t mode, long returnValue = 0, bool checkCache = true);
|
||||||
int GetErrno();
|
int GetErrno();
|
||||||
|
|
|
@ -584,6 +584,13 @@ void BxlObserver::report_exec_args(pid_t pid)
|
||||||
}
|
}
|
||||||
close(fd);
|
close(fd);
|
||||||
|
|
||||||
|
report_exec_args(pid, cmdLine.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BxlObserver::report_exec_args(pid_t pid, const char *args) {
|
||||||
|
if (IsReportingProcessArgs())
|
||||||
|
{
|
||||||
AccessReport report =
|
AccessReport report =
|
||||||
{
|
{
|
||||||
.operation = kOpProcessCommandLine,
|
.operation = kOpProcessCommandLine,
|
||||||
|
@ -600,7 +607,8 @@ void BxlObserver::report_exec_args(pid_t pid)
|
||||||
.shouldReport = true,
|
.shouldReport = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
strlcpy(report.path, cmdLine.c_str(), sizeof(report.path));
|
// This may truncate the path but that's okay because it's the process command line.
|
||||||
|
strlcpy(report.path, args, sizeof(report.path));
|
||||||
SendReport(report, /* isDebugMessage */ false, /* useSecondaryPipe */ false);
|
SendReport(report, /* isDebugMessage */ false, /* useSecondaryPipe */ false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -297,7 +297,6 @@ private:
|
||||||
bool IsMonitoringChildProcesses() const { return !pip_ || CheckMonitorChildProcesses(pip_->GetFamFlags()); }
|
bool IsMonitoringChildProcesses() const { return !pip_ || CheckMonitorChildProcesses(pip_->GetFamFlags()); }
|
||||||
bool IsPTraceEnabled() const { return pip_ && (CheckEnableLinuxPTraceSandbox(pip_->GetFamExtraFlags()) || CheckUnconditionallyEnableLinuxPTraceSandbox(pip_->GetFamExtraFlags())); }
|
bool IsPTraceEnabled() const { return pip_ && (CheckEnableLinuxPTraceSandbox(pip_->GetFamExtraFlags()) || CheckUnconditionallyEnableLinuxPTraceSandbox(pip_->GetFamExtraFlags())); }
|
||||||
bool IsPTraceForced(const char *path);
|
bool IsPTraceForced(const char *path);
|
||||||
bool IsReportingProcessArgs() const { return !pip_ || CheckReportProcessArgs(pip_->GetFamFlags()); }
|
|
||||||
|
|
||||||
inline bool IsValid() const { return sandbox_ != NULL; }
|
inline bool IsValid() const { return sandbox_ != NULL; }
|
||||||
inline bool IsEnabled(pid_t pid) const
|
inline bool IsEnabled(pid_t pid) const
|
||||||
|
@ -378,8 +377,11 @@ public:
|
||||||
const char* GetSecondaryReportsPath() { return secondaryReportPath_; }
|
const char* GetSecondaryReportsPath() { return secondaryReportPath_; }
|
||||||
const char* GetDetoursLibPath() { return detoursLibFullPath_; }
|
const char* GetDetoursLibPath() { return detoursLibFullPath_; }
|
||||||
|
|
||||||
|
bool IsReportingProcessArgs() const { return !pip_ || CheckReportProcessArgs(pip_->GetFamFlags()); }
|
||||||
|
|
||||||
void report_exec(const char *syscallName, const char *procName, const char *file, int error, mode_t mode = 0, pid_t associatedPid = 0);
|
void report_exec(const char *syscallName, const char *procName, const char *file, int error, mode_t mode = 0, pid_t associatedPid = 0);
|
||||||
void report_exec_args(pid_t pid);
|
void report_exec_args(pid_t pid);
|
||||||
|
void report_exec_args(pid_t pid, const char *args);
|
||||||
void report_audit_objopen(const char *fullpath)
|
void report_audit_objopen(const char *fullpath)
|
||||||
{
|
{
|
||||||
IOEvent event(ES_EVENT_TYPE_NOTIFY_OPEN, ES_ACTION_TYPE_NOTIFY, fullpath, progFullPath_, S_IFREG);
|
IOEvent event(ES_EVENT_TYPE_NOTIFY_OPEN, ES_ACTION_TYPE_NOTIFY, fullpath, progFullPath_, S_IFREG);
|
||||||
|
|
Загрузка…
Ссылка в новой задаче