wait() hangs when CLONE_THREAD - c++

I am tracing some processes and their children using ptrace. I am trying to print specific system call (using Seccomp filter that notifies ptrace, see this blogpost).
In most cases my code (see below) is working fine. However, when I am tracing a java program (from the default-jre package), the latter clones using the CLONE_THREAD flag. And for some reason, my tracer hangs (I believe) because I can't receive signals from the cloned process. I think the reason is that (according to this discussion) the child process in fact becomes a child of the original process' parent, instead of becoming the original process' child.
I reproduced this issue by using a simple program that simply calls clone() with flags and perform actions. When I used the when I use CLONE_THREAD | CLONE_SIGHAND | CLONE_VM flags (as clone() documentation specifies they should come together since Linux 2.6.0), at least I am able to trace everything correctly until one of the two thread finishes.
I would like to trace both thread independently. Is it possible?
More importantly, I need to trace a Java program, and I cannot change it. Here a strace of the Java program clone call:
[...]
4665 clone(child_stack=0x7fb166e95fb0, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tid=[4666], tls=0x7fb166e96700, child_tidptr=0x7fb166e969d0) = 4666
[...]
So Java seems to respect the rules. I wanted to experiment to understand: I ruled out any flags unrelated to thread (i.e., `CLONE_FS | CLONE_FILES | CLONE_SYSVSEM).
Here are the results of running my test program with different combination of flags (I know, I am really desperate):
CLONE_VM|CLONE_SIGHAND|CLONE_THREAD|CLONE_SETTLS: only gets trace from parent
CLONE_VM|CLONE_SIGHAND|CLONE_THREAD|CLONE_PARENT_SETTID: inconsistent; gets trace from both until the parent finishes
CLONE_VM|CLONE_SIGHAND|CLONE_THREAD|CLONE_CHILD_CLEARTID: inconsistent; gets trace from both until the child finishes
CLONE_VM|CLONE_SIGHAND|CLONE_THREAD|CLONE_SETTLS|CLONE_PARENT_SETTID: only gets trace from parent
CLONE_VM|CLONE_SIGHAND|CLONE_THREAD|CLONE_SETTLS|CLONE_CHILD_CLEARTID: only gets trace from parent
CLONE_VM|CLONE_SIGHAND|CLONE_THREAD|CLONE_PARENT_SETTID|CLONE_SETTLS: only gets trace from parent
CLONE_VM|CLONE_SIGHAND|CLONE_THREAD|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID: inconsistent; gets trace from both until the child finishes
CLONE_VM|CLONE_SIGHAND|CLONE_THREAD|CLONE_CHILD_CLEARTID|CLONE_SETTLS: only gets trace from parent
CLONE_VM|CLONE_SIGHAND|CLONE_THREAD|CLONE_CHILD_CLEARTID|CLONE_PARENT_SETTID: inconsistent; gets trace from both until the child finishes
CLONE_VM|CLONE_SIGHAND|CLONE_THREAD|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID:
only gets trace from parent
So at least I get the same behaviour from my program and the Java program: it does not work.
How can I make it work? For instance, how does strace successfully traces any kind of clone? I tried to dig into its code but I can't find how they are doing it.
Any help might appreciated!
Best regards,
The tracer code (compile with g++ tracer.cpp -o tracer -g -lseccomp -lexplain):
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <stddef.h>
#include <sys/ptrace.h>
#include <sys/reg.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <sys/user.h>
#include <sys/prctl.h>
#include <fcntl.h>
#include <linux/limits.h>
#include <linux/filter.h>
#include <linux/seccomp.h>
#include <linux/unistd.h>
#include <libexplain/waitpid.h>
#include <tuple>
#include <vector>
#define DEFAULT_SIZE 1000
#define MAX_SIZE 1000
int process_signals();
int inspect(pid_t);
void read_string_into_buff(const pid_t, unsigned long long, char *, unsigned int);
int main(int argc, char **argv){
pid_t pid;
int status;
if (argc < 2) {
fprintf(stderr, "Usage: %s <prog> <arg1> ... <argN>\n", argv[0]);
return 1;
}
if ((pid = fork()) == 0) {
/* If execve syscall, trace */
struct sock_filter filter[] = {
BPF_STMT(BPF_LD+BPF_W+BPF_ABS, offsetof(struct seccomp_data, nr)),
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_getpid, 0, 1),
BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_TRACE),
BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW),
};
struct sock_fprog prog = {
.len = (unsigned short) (sizeof(filter)/sizeof(filter[0])),
.filter = filter,
};
ptrace(PTRACE_TRACEME, 0, 0, 0);
if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) {
perror("prctl(PR_SET_NO_NEW_PRIVS)");
return 1;
}
if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) == -1) {
perror("when setting seccomp filter");
return 1;
}
kill(getpid(), SIGSTOP);
return execvp(argv[1], argv + 1);
} else {
waitpid(pid, &status, 0);
ptrace(PTRACE_SETOPTIONS, pid, 0, PTRACE_O_TRACESECCOMP | PTRACE_O_TRACEFORK | PTRACE_O_TRACECLONE | PTRACE_O_TRACEVFORK );
ptrace(PTRACE_CONT, pid, 0, 0);
process_signals();
return 0;
}
}
int process_signals(){
int status;
while (1){
pid_t child_pid;
// When child status changes
if ((child_pid = waitpid(-1, &status, 0)) < 0){
fprintf(stderr, "%s\n", explain_waitpid(child_pid, &status, 0));
exit(EXIT_FAILURE);
}
//printf("Sigtrap received\n");
// Checking if it is thanks to seccomp
if (status >> 8 == (SIGTRAP | (PTRACE_EVENT_SECCOMP << 8))){
// Perform argument inspection with ptrace
int syscall = inspect(child_pid);
}
// Resume no matter what
ptrace(PTRACE_CONT, child_pid, 0, 0);
}
}
int inspect(pid_t pid){
printf("From PID: %d\n", pid);
struct user_regs_struct regs;
ptrace(PTRACE_GETREGS, pid, 0, &regs);
// Get syscall number
int syscall = regs.orig_rax;
printf("------\nCaught syscall: %d\n", syscall);
if (syscall == __NR_getpid){
printf("Getpid detected\n");
}
return syscall;
}
void read_string_into_buff(const pid_t pid, unsigned long long addr, char * buff, unsigned int max_len){
/* Are we aligned on the "start" front? */
unsigned int offset=((unsigned long)addr)%sizeof(long);
addr-=offset;
unsigned int i=0;
int done=0;
int word_offset=0;
while( !done ) {
unsigned long word=ptrace( PTRACE_PEEKDATA, pid, addr+(word_offset++)*sizeof(long), 0 );
// While loop to stop at the first '\0' char indicating end of string
while( !done && offset<sizeof(long) && i<max_len ) {
buff[i]=((char *)&word)[offset]; /* Endianity neutral copy */
done=buff[i]=='\0';
++i;
++offset;
}
offset=0;
done=done || i>=max_len;
}
}
The sample program (compile with gcc sample.c -o sample):
#define _GNU_SOURCE
#include <stdio.h>
#include <sched.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
#include <signal.h>
#define FLAGS CLONE_VM|CLONE_SIGHAND|CLONE_THREAD|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID
int fn(void *arg)
{
printf("\nINFO: This code is running under child process.\n");
int i = 0;
int n = atoi(arg);
for ( i = 1 ; i <= 10 ; i++ )
printf("[%d] %d * %d = %d\n", getpid(), n, i, (n*i));
printf("\n");
return 0;
}
void main(int argc, char *argv[])
{
printf("[%d] Hello, World!\n", getpid());
void *pchild_stack = malloc(1024 * 1024);
if ( pchild_stack == NULL ) {
printf("ERROR: Unable to allocate memory.\n");
exit(EXIT_FAILURE);
}
int pid = clone(fn, pchild_stack + (1024 * 1024), FLAGS, argv[1]);
if ( pid < 0 ) {
printf("ERROR: Unable to create the child process.\n");
exit(EXIT_FAILURE);
}
fn(argv[1]);
wait(NULL);
free(pchild_stack);
printf("INFO: Child process terminated.\n");
}
You can test what you want by running ./tracer ./sample. You can also test the original test case ./tracer java and observe that both the tracer and java hangs.
ANSWER:
As pointed it out in the comment, I had issues in that example that were preventing me from handling signals from the child.
In my original code (not listed here because too complex), I was only attaching ptrace AFTER the processes started... and I was only attaching to PID listed by pstree. My mistake was that I omitted the threads (and java is one program that does create threads), explaining why I had issue tracing java only.
I modified the code to attach to all the children process and thread (ps -L -g <Main_PID> -o tid=) and everything works again.

Your sample program has a bug: it may free the second thread’s stack before that thread exits, causing a SEGV. And your tracer just doesn’t handle signals well.
If the traced program gets a signal, your tracer intercepts it, not passing it down to the program. When it continues the program, it continues from the very same operation that caused SEGV, so it gets SEGV again. Ad infinitum. Both the tracer and the tracee appear to hang but in fact, they are in an infinite loop.
Rewriting the continuation like the following seems to work:
if (status >> 8 == (SIGTRAP | (PTRACE_EVENT_SECCOMP << 8))){
// Perform argument inspection with ptrace
int syscall = inspect(child_pid);
ptrace(PTRACE_CONT, child_pid, 0, 0);
} else if (WIFSTOPPED(status)) {
ptrace(PTRACE_CONT, child_pid, 0, WSTOPSIG(status));
} else {
ptrace(PTRACE_CONT, child_pid, 0, 0);
}
Not sure of Java but it seems to get SEGVs in regular operation...

Related

How to a run command on Linux terminal from a C/C++ program [duplicate]

I want to execute another program within C code.
For example, I want to execute a command
./foo 1 2 3
foo is the program which exists in the same folder, and 1 2 3 are arguments.
foo program creates a file which will be used in my code.
How do I do this?
For a simple way, use system():
#include <stdlib.h>
...
int status = system("./foo 1 2 3");
system() will wait for foo to complete execution, then return a status variable which you can use to check e.g. exitcode (the command's exitcode gets multiplied by 256, so divide system()'s return value by that to get the actual exitcode: int exitcode = status / 256).
The manpage for wait() (in section 2, man 2 wait on your Linux system) lists the various macros you can use to examine the status, the most interesting ones would be WIFEXITED and WEXITSTATUS.
Alternatively, if you need to read foo's standard output, use popen(3), which returns a file pointer (FILE *); interacting with the command's standard input/output is then the same as reading from or writing to a file.
The system function invokes a shell to run the command. While this is convenient, it has well known security implications. If you can fully specify the path to the program or script that you want to execute, and you can afford losing the platform independence that system provides, then you can use an execve wrapper as illustrated in the exec_prog function below to more securely execute your program.
Here's how you specify the arguments in the caller:
const char *my_argv[64] = {"/foo/bar/baz" , "-foo" , "-bar" , NULL};
Then call the exec_prog function like this:
int rc = exec_prog(my_argv);
Here's the exec_prog function:
static int exec_prog(const char **argv)
{
pid_t my_pid;
int status, timeout /* unused ifdef WAIT_FOR_COMPLETION */;
if (0 == (my_pid = fork())) {
if (-1 == execve(argv[0], (char **)argv , NULL)) {
perror("child process execve failed [%m]");
return -1;
}
}
#ifdef WAIT_FOR_COMPLETION
timeout = 1000;
while (0 == waitpid(my_pid , &status , WNOHANG)) {
if ( --timeout < 0 ) {
perror("timeout");
return -1;
}
sleep(1);
}
printf("%s WEXITSTATUS %d WIFEXITED %d [status %d]\n",
argv[0], WEXITSTATUS(status), WIFEXITED(status), status);
if (1 != WIFEXITED(status) || 0 != WEXITSTATUS(status)) {
perror("%s failed, halt system");
return -1;
}
#endif
return 0;
}
Remember the includes:
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
See related SE post for situations that require communication with the executed program via file descriptors such as stdin and stdout.
You can use fork() and system() so that your program doesn't have to wait until system() returns.
#include <stdio.h>
#include <stdlib.h>
int main(int argc,char* argv[]){
int status;
// By calling fork(), a child process will be created as a exact duplicate of the calling process.
// Search for fork() (maybe "man fork" on Linux) for more information.
if(fork() == 0){
// Child process will return 0 from fork()
printf("I'm the child process.\n");
status = system("my_app");
exit(0);
}else{
// Parent process will return a non-zero value from fork()
printf("I'm the parent.\n");
}
printf("This is my main program and it will continue running and doing anything i want to...\n");
return 0;
}
system() executes a shell which is then responsible for parsing the arguments and executing the desired program. To execute the program directly, use fork() and exec() (which is what system() uses to execute the shell as well as what the shell itself uses to execute commands).
#include <unistd.h>
int main() {
if (fork() == 0) {
/*
* fork() returns 0 to the child process
* and the child's PID to the parent.
*/
execl("/path/to/foo", "foo", "arg1", "arg2", "arg3", 0);
/*
* We woundn't still be here if execl() was successful,
* so a non-zero exit value is appropriate.
*/
return 1;
}
return 0;
}
In C
#include <stdlib.h>
system("./foo 1 2 3");
In C++
#include <cstdlib>
std::system("./foo 1 2 3");
Then open and read the file as usual.
How about like this:
char* cmd = "./foo 1 2 3";
system(cmd);
Here's the way to extend to variable args when you don't have the args hard coded (although they are still technically hard coded in this example, but should be easy to figure out how to extend...):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int argcount = 3;
const char* args[] = {"1", "2", "3"};
const char* binary_name = "mybinaryname";
char myoutput_array[5000];
sprintf(myoutput_array, "%s", binary_name);
for(int i = 0; i < argcount; ++i)
{
strcat(myoutput_array, " ");
strcat(myoutput_array, args[i]);
}
system(myoutput_array);

Is there way to detach process from out-stream after some time?

I am using boost::process::child to spawn new process.
Start time of process which I am start isn't instant, so I have to wait some time until full initialization of it.
auto is_ptr = std::make_shared<bp::ipstream>();
auto child_pr = std::make_shared<bp::child>(executable, args, bp::std_out > *is_ptr);
m_childs[port] = {child_pr, is_ptr};
std::string line;
while (child_pr->running() && std::getline(*is_ptr, line)) {
std::cerr <<"SI: \t" << line << std::endl;
if( 0 == line.compare(0, string_to_find.size(), string_to_find)){
break;
}
}
...
After this cycle I don't need to have ipstream anymore. Is any way to detach it from the child process?
Since you asked to provide answer, I'll put some additional information here, although I am not sure it will completely answer your question.
Assuming the target platform is Linux, once ipstream is destroyed in the parent process, it effectively means that the file descriptor for the associated pipe between the parent and child process is closed in the parent process. Once the child process writes to the pipe after the parent process closed its read end of the pipe, SIGPIPE is generated for the child process, which will cause it to terminate in case no extra measures are taken.
To prevent this, one option is to ignore SIGPIPE in the child. This will now cause errors in the child process when writing to that pipe. It depends on the implementation of the child process what cause that will have. A solution in your case could be to ignore SIGPIPE, and take measures in the child process once it can no longer successfully write data, to prevent a lot of wasted CPU cycles.
To experiment with this on a lower level, you can use the following program. It will fork a child process that will keep on writing to some output as long as that succeeds. The parent process will close the corresponding pipe as soon as it has read some data from it.
The behavior of the program differs depending on how SIGPIPE is handled in the child process. In case it is ignored, the write() in the child process will fail, and the child process will exit with a non-zero exit code. In case the SIGPIPE is not ignored, the child process is terminated by the operating system. The parent process will tell you what happened in the child process.
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc, char** argv)
{
int pipe_fds[2];
if (pipe(pipe_fds) < 0) {
perror("pipe");
exit(1);
}
pid_t pid;
if ((pid = fork()) < 0) {
perror("fork");
exit(1);
}
if (pid == 0)
{
close(pipe_fds[0]); /* close read-end in the child */
/* Uncomment the following line, and the child will terminate as soon
as the parent closes the read end of the pipe...This is here merely
for illustrative purposes, production code should use either
sigaction() or pthreads related signal functionality in case of a
multi-threaded program. */
/* signal(SIGPIPE, SIG_IGN); */
/* Child process, start writing to the write-end of the pipe. */
const char message[] = "Hello world!\n";
while (write(pipe_fds[1], message, strlen(message)) >= 0);
exit(1);
}
close(pipe_fds[1]);
char buf[256];
ssize_t count;
while ((count = read(pipe_fds[0], buf, sizeof(buf) - 1)) == 0);
if (count < 0) {
perror("read");
exit(1);
}
buf[count] = '\0';
printf("%s", buf);
/* Close read-end in the parent, this will trigger SIGPIPE in the child
once the child writes to the pipe. */
close(pipe_fds[0]);
int stat;
if (waitpid(pid, &stat, 0) < 0) {
perror("waitpid");
exit(1);
}
if (WIFSIGNALED(stat) && WTERMSIG(stat) == SIGPIPE) {
printf("\nChild terminated by SIGPIPE\n");
}
if (WIFEXITED(stat)) {
printf("\nChild exited with exit code %d\n", WEXITSTATUS(stat));
}
exit(0);
}

Cuda: how to reset GPU after "sticky" error? [duplicate]

I have a working app which uses Cuda / C++, but sometimes, because of memory leaks, throws exception. I need to be able to reset the GPU on live, my app is a server so it has to stay available.
I tried something like this, but it doesnt seems to work:
try
{
// do process using GPU
}
catch (std::exception &e)
{
// catching exception from cuda only
cudaSetDevice(0);
CUDA_RETURN_(cudaDeviceReset());
}
My idea is to reset the device each times I get an exception from the GPU, but I cannot manage to make it working. :(
Btw, for some reasons, I cannot fix every problems of my Cuda code, I need a temporary solution. Thanks !
The only method to restore proper device functionality after a non-recoverable ("sticky") CUDA error is to terminate the host process that initiated (i.e. issued the CUDA runtime API calls that led to) the error.
Therefore, for a single-process application, the only method is to terminate the application.
It should be possible to design a multi-process application, where the initial ("parent") process makes no usage of CUDA whatsoever, and spawns a child process that uses the GPU. When the child process encounters an unrecoverable CUDA error, it must terminate.
The parent process can, optionally, monitor the child process. If it determines that the child process has terminated, it can re-spawn the process and restore CUDA functional behavior.
Sticky vs. non-sticky errors are covered elsewhere, such as here.
An example of a proper multi-process app that uses e.g. fork() to spawn a child process that uses CUDA is available in the CUDA sample code simpleIPC. Here is a rough example assembled from the simpleIPC example (for linux):
$ cat t477.cu
/*
* Copyright 1993-2015 NVIDIA Corporation. All rights reserved.
*
* Please refer to the NVIDIA end user license agreement (EULA) associated
* with this source code for terms and conditions that govern your use of
* this software. Any use, reproduction, disclosure, or distribution of
* this software and related documentation outside the terms of the EULA
* is strictly prohibited.
*
*/
// Includes
#include <stdio.h>
#include <assert.h>
// CUDA runtime includes
#include <cuda_runtime_api.h>
// CUDA utilities and system includes
#include <helper_cuda.h>
#define MAX_DEVICES 1
#define PROCESSES_PER_DEVICE 1
#define DATA_BUF_SIZE 4096
#ifdef __linux
#include <unistd.h>
#include <sched.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <linux/version.h>
typedef struct ipcDevices_st
{
int count;
int results[MAX_DEVICES];
} ipcDevices_t;
// CUDA Kernel
__global__ void simpleKernel(int *dst, int *src, int num)
{
// Dummy kernel
int idx = blockIdx.x * blockDim.x + threadIdx.x;
dst[idx] = src[idx] / num;
}
void runTest(int index, ipcDevices_t* s_devices)
{
if (s_devices->results[0] == 0){
simpleKernel<<<1,1>>>(NULL, NULL, 1); // make a fault
cudaDeviceSynchronize();
s_devices->results[0] = 1;}
else {
int *d, *s;
int n = 1;
cudaMalloc(&d, n*sizeof(int));
cudaMalloc(&s, n*sizeof(int));
simpleKernel<<<1,1>>>(d, s, n);
cudaError_t err = cudaDeviceSynchronize();
if (err != cudaSuccess)
s_devices->results[0] = 0;
else
s_devices->results[0] = 2;}
cudaDeviceReset();
}
#endif
int main(int argc, char **argv)
{
ipcDevices_t *s_devices = (ipcDevices_t *) mmap(NULL, sizeof(*s_devices),
PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, 0, 0);
assert(MAP_FAILED != s_devices);
// We can't initialize CUDA before fork() so we need to spawn a new process
s_devices->count = 1;
s_devices->results[0] = 0;
printf("\nSpawning child process\n");
int index = 0;
pid_t pid = fork();
printf("> Process %3d\n", pid);
if (pid == 0) { // child process
// launch our test
runTest(index, s_devices);
}
// Cleanup and shutdown
else { // parent process
int status;
waitpid(pid, &status, 0);
if (s_devices->results[0] < 2) {
printf("first process launch reported error: %d\n", s_devices->results[0]);
printf("respawn\n");
pid_t newpid = fork();
if (newpid == 0) { // child process
// launch our test
runTest(index, s_devices);
}
// Cleanup and shutdown
else { // parent process
int status;
waitpid(newpid, &status, 0);
if (s_devices->results[0] < 2)
printf("second process launch reported error: %d\n", s_devices->results[0]);
else
printf("second process launch successful\n");
}
}
}
printf("\nShutting down...\n");
exit(EXIT_SUCCESS);
}
$ nvcc -I/usr/local/cuda/samples/common/inc t477.cu -o t477
$ ./t477
Spawning child process
> Process 10841
> Process 0
Shutting down...
first process launch reported error: 1
respawn
Shutting down...
second process launch successful
Shutting down...
$
For windows, the only changes need should be to use a windows IPC mechanism for host interprocess communication.

How does execve prevents vulnerabilities compared to system command

I am referring to this link,
Basically, consider the input happy'; useradd 'attacker, the security advice differentiates between a compliant and non-compliant code -
Non Complaint Code
#include <string.h>
#include <stdlib.h>
enum { BUFFERSIZE = 512 };
void func(const char *input) {
char cmdbuf[BUFFERSIZE];
int len_wanted = snprintf(cmdbuf, BUFFERSIZE,
"any_cmd '%s'", input);
if (len_wanted >= BUFFERSIZE) {
/* Handle error */
} else if (len_wanted < 0) {
/* Handle error */
} else if (system(cmdbuf) == -1) {
/* Handle error */
}
}
Compliant Code
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
void func(char *input) {
pid_t pid;
int status;
pid_t ret;
char *const args[3] = {"any_exe", input, NULL};
char **env;
extern char **environ;
/* ... Sanitize arguments ... */
pid = fork();
if (pid == -1) {
/* Handle error */
} else if (pid != 0) {
while ((ret = waitpid(pid, &status, 0)) == -1) {
if (errno != EINTR) {
/* Handle error */
break;
}
}
if ((ret != -1) &&
(!WIFEXITED(status) || !WEXITSTATUS(status)) ) {
/* Report unexpected child status */
}
} else {
/* ... Initialize env as a sanitized copy of environ ... */
if (execve("/usr/bin/any_cmd", args, env) == -1) {
/* Handle error */
_Exit(127);
}
}
}
Assume we pass the same input to both the function with equal privilege, i.e run by root etc etc, How does the second solution ensure that command injection attack is repelled?
My only guess is that, execve will refresh your binary image with any_cmdand use input happy'; useradd 'attacker as args to any_cmd. So we will be have a return value equivalent to "invalid parameters". Is my understanding right? Or is there something deeper than my understanding which I am missing?
The main difference is indeed that with the system function you can launch whatever your shell can execute, so you basically can have shell injections with multiple commands. Whereas with execve first you specify a specific binary to execute, so you're pretty much sure that there is only one command executed (except if you execve a shell..). Also since you give a complete path to execve you avoid hacks based on modifying the HOME or the current working directory.
So yes, your understanding is rather right

Running an executable from a C++ program in the same process

Is that possible? I'd like an easy access to the executable's memory to edit it. Alternately, when I'm not the administrator, is it possible to edit the executable's memory from another process? I've tried the ptrace library and it fails if I'm not the administrator. I'm on Linux
I'm not entirely sure what you are asking, but this is possible with shared memory.
See here: http://www.kernel.org/doc/man-pages/online/pages/man7/shm_overview.7.html
This is what a debugger does. You could look at the code of an open source debugger, e.g. gdb, to see how it works.
The answer:
Yes - it works: you don't have to be administrator / root, but of course you need the rights to access the process' memory, i.e. same user.
No - it is not easy
The possibility to write to /proc/pid/mem was added some time ago to the Linux kernel. Therefore it depends on the kernel you are using. The small programs were checked with kernel 3.2 where this works and 2.6.32 where it fails.
The solution consists of two programs:
A 'server' which is started, allocates some memory, writes some pattern into this memory and outputs every three seconds the memory contents which is placed after the pattern is printed.
A 'client' which connects via the /proc/pid/maps and /proc/pid/mem to the server, searches for the pattern and writes some other string into the server's memory.
The implementation uses heap - but as long as the permissions allow - it is also possible to change other portions of the other process' memory.
This is implemented in C, because it is very 'low level' - but it should work in C++. It is a proof of concept - no production code - e.g. there are some error checks missing and it has some fixed size buffers.
memholder.c
/*
* Alloc memory - write in some pattern and print out the some bytes
* after the pattern.
*
* Compile: gcc -Wall -Werror memholder.c -o memholder.o
*/
#include <sys/types.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
int main() {
char * m = (char*) malloc(2048);
memset(m, '\xAA', 1024);
strcpy(m + 1024, "Some local data.");
printf("PID: %d\n", getpid());
while(1) {
printf("%s\n", m + 1024);
sleep(3);
}
return 0;
}
memwriter.c
/*
* Searches for a pattern in the given PIDs memory
* and changes some bytes after them.
*
* Compile: gcc -Wall -std=c99 -Werror memwriter.c -o memwriter
*/
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
int open_proc_file(pid_t other_pid, char const * const sn,
int flags) {
char fname[1024];
snprintf(fname, 1023, "/proc/%d/%s", other_pid, sn);
// Open file for reading and writing
int const fd = open(fname, flags );
if(fd==-1) {
perror("Open file");
exit(1);
}
return fd;
}
void get_heap(int fd_maps, size_t * heap_start, size_t * heap_end) {
char buf[65536];
ssize_t const r = read(fd_maps, buf, 65535);
if(r==-1) {
perror("Reading maps file");
exit(1);
}
buf[r] = '\0';
char * const heap = strstr(buf, "[heap]");
if(heap==NULL) {
printf("[heap] not found in maps file");
exit(1);
}
// Look backward to the latest newline
char const * hl_start;
for(hl_start = heap; hl_start > buf && *hl_start != '\n';
--hl_start) {}
// skip \n
++hl_start;
// Convert to beginnig and end address
char * lhe;
*heap_start = strtol(hl_start, &lhe, 16);
++lhe;
*heap_end = strtol(lhe, &lhe, 16);
}
int main(int argc, char *argv[]) {
if(argc!=2) {
printf("Usage: memwriter <pid>\n");
return 1;
}
pid_t const other_pid = atoi(argv[1]);
int fd_mem = open_proc_file(other_pid, "mem", O_RDWR);
int fd_maps = open_proc_file(other_pid, "maps", O_RDONLY);
size_t other_mem_start;
size_t other_mem_end;
get_heap(fd_maps, &other_mem_start, &other_mem_end);
ptrace(PTRACE_ATTACH, other_pid, NULL, NULL);
waitpid(other_pid, NULL, 0);
if( lseek(fd_mem, other_mem_start, SEEK_SET) == -1 ) {
perror("lseek");
return 1;
}
char buf[512];
do {
ssize_t const r = read(fd_mem, buf, 512);
if(r!=512) {
perror("read?");
break;
}
// Check for pattern
int pat_found = 1;
for(int i = 0; i < 512; ++i) {
if( buf[i] != '\xAA' )
pat_found = 0;
break;
}
if( ! pat_found ) continue;
// Write about one k of strings
char const * const wbuf = "REMOTE DATA - ";
for(int i = 0; i < 70; ++i) {
ssize_t const w = write(fd_mem, wbuf, strlen(wbuf));
if( w == -1) {
perror("Write");
return 1;
}
}
// Append a \0
write(fd_mem, "\0", 1);
break;
} while(1);
ptrace(PTRACE_DETACH, other_pid, NULL, NULL);
close(fd_mem);
close(fd_maps);
return 0;
}
Example output
$ ./memholder
PID: 2621
Some local data.
Some local data.
MOTE DATA - REMOTE DA...
Other interpretation
There is also another interpretation of your question (when reading the headline and not the question), that you want to replace the 'executable' from one process with another one. That can be easily handled by exec() (and friends):
From man exec:
The exec() family of functions replaces the current process image with a new process image.
In Windows, the methods used for this are named ReadProcessMemory / WriteProcessMemory, you will, however, need administrative rights for this. The same is for linux, as I've said in my comment, no sane system would allow user process to modify non-owned memory.
For linux, the only function is ptrace. You will need to be administrator.
http://cboard.cprogramming.com/cplusplus-programming/92093-readprocessmemory-writeprocessmemory-linux-equivalent.html contains more detailed discussion.
Can you imagine the consequences of allowing process to modify other process memory, without being administrator?