Im new to systemcall. Im using execve inside a child so the parent should run as normal and not be ovewritten by execve. The problem is that the child executes and stops the whole thing after execve. My goal here is to count the number of clone executed in the bash command(argument) executed by execve.
Ive read the man, Im still a bit confused. I can only use ptrace, fork, wait/waitpid.
argument:
/bin/bash -c "echo 'first test' | wc -c"
int main(int argc, char *argv[]) {
pid_t child_pid = fork();
int status;
int counter = 0;
wait(&status);
if (child_pid == -1) {
exit(1);
} else {
while(status != child_pid){
if (child_pid == 0) {
ptrace(PTRACE_TRACEME, child_pid, 0, 0);
raise(SIGSTOP);
execve(argv[1], &argv[1], NULL);
ptrace(PTRACE_SETOPTIONS, child_pid, 0, PTRACE_O_TRACECLONE);
ptrace(PTRACE_CONT, child_pid, 0L, 0L);
if(status>>8 == (SIGTRAP | (PTRACE_EVENT_CLONE<<8)))
counter++;
}
}
}
printf("# of clone executions: %d", counter);
return 0;
}
execve
will overwrite the child process, so any instruction after execve
will not be executed, unless the call to execve
fails. You should instead run the ptrace
tracing that appears after the execve
in the parent process
Edit: here is a commented solution to count the number of clone syscalls:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/ptrace.h>
#include <sys/syscall.h>
#include <sys/reg.h> // ORIG_RAX
int main(int argc, char *argv[]) {
pid_t child_pid = fork();
int counter = 0;
int entering_syscall = 1;
if(child_pid == -1) {
perror("fork failed");
exit(1);
} else if(child_pid != 0) {
// parent
u_int64_t rax;
int status;
while(1) {
// wait for the next signal by the child
if(wait(&status) != child_pid)
continue;
if(WIFEXITED(status)) {
puts("child exited");
break;
}
// read the USER area, defined in sys/user.h, which contains the registers information
// in linux, rax contains the syscall number
rax = ptrace(PTRACE_PEEKUSER, child_pid, 8 * ORIG_RAX, NULL);
if(rax == SYS_clone /* 56 */) {
// PTRACE_SYSCALL generates a signal both when entering and exiting a syscall
// only count the syscall during enter
if(entering_syscall)
counter++;
entering_syscall = !entering_syscall;
}
//printf("syscall %ld\n", rax);
// continue the child process until the next syscall enter/exit
ptrace(PTRACE_SYSCALL, child_pid, NULL, NULL);
}
} else {
// child
// turns the calling thread into a tracee
ptrace(PTRACE_TRACEME, child_pid, 0, 0);
// signal and wait for the parent. This ensures that PTRACE_SYSCALL
// will not miss any child syscall
raise(SIGSTOP);
execve(argv[1], &argv[1], NULL);
// should be never reached
perror("execve failed");
exit(1);
}
printf("Num clone: %d\n", counter);
}
You can cross-check it against strace strace 2>&1 your_command | grep clone | wc -l