Ptrace parent process - linux

I'm trying to monitor/redirect syscalls in my own process. LD_PRELOAD doesn't work when fwrite calls write inside libc, and got/plt hooks seem to have the same problem. I'm looking for a solution based on ptrace, but I can't fork() and run the main app as a child because the app communicates with its parent via signals.
There is a thread from 2006 that suggests the tracer can be on a thread group that's different from the tracee, but it doesn't seem to work in practice: http://yarchive.net/comp/linux/ptrace_self_attach.html
pid = fork();
if (pid == 0) {
prctl(PR_SET_PTRACER, getppid());
raise(SIGSTOP);
} else {
sleep(1);
ptrace(PTRACE_SEIZE, pid, NULL, NULL);
for (;;) {
int status;
int ret = waitpid(pid, &status, 0);
warn("wait=%d:", ret);
ret = ptrace(PTRACE_SYSCALL, pid, NULL, NULL);
warn("ptrace=%d:", ret);
}
}
The problem I'm facing is that ptrace(PTRACE_SYSCALL) expects the tracee to be in ptrace-wait state, i.e. it must have raised SIGSTOP and the tracer needs to wait() for it. Since the relation is inversed in this case (tracer is the child of the tracee) PTRACE_SYSCALL returns ESRCH.
How does strace get away with tracing an existing pid ?

I'm a bit unclear on what exactly you're asking here. It sounds like you have the attaching part resolved (which is the most difficult problem to resolve). If that is the case, then getting the process to stop is not a problem. Just send the process a signal. The process will stop and send you a TRAP so you can decide what to do with the signal. At this point you can call ptrace(PTRACE_SYSCALL, pid, 0, 0). This will both start it in SYSCALL trace mode, and prevent your signal from arriving at the debugee (thus not introducing unexpected signals into the process).

Related

How to pthread_kill synchronously?

I am starting a bunch of joinable worker threads and main() waits for them to completed with pthread_join(). However, a user may hit CTRL+C on the terminal before the worker threads have completed their task. My understanding is that any thread could get the signal so all my worker threads call pthread_sigmask() on start up and block SIGINT (the CTRL+C signal). This causes the signal to be copied to other threads and main(). This way I know that at least main() will get definitely the signal.
I have defined a signal handler function on main() so that main() gets the signal and can kill all the worker threads and free their resources from one place. The problem is that this happens asynchronously. I call pthread_kill() from main() and then try to free() resources the worker thread is using and it's still running because the signal is dispatched asynchronously.
If I call pthread_kill(SIGTERM, ...) from main() to kill the thread main() gets killed too and do_thread_cleanup(i) is never called:
int main () {
signal (SIGINT, signal_handler);
for (i = 0; i < num_thd; i++) {
pthread_create(thread_init, ...);
}
for (i = 0; i < num_thd; i++) {
pthread_join(...);
}
return 0;
}
void signal_handler(int signal) {
for (i = 0; i < num_thd; i++) {
pthread_kill(pthread_t, SIGINT);
pthread_join(pthread_t, ...);
do_thread_cleanup(i); // Calls functions like free() and close()
}
}
void thread_init() {
sigset_t sigset;
sigemptyset(&sigset);
sigaddset(&sigset, SIGINT);
pthread_sigmask(SIG_BLOCK, &sigset, NULL);
do_stuff_in_a_loop();
}
How can I send SIGKILL to a thread without main() receiving that signal and killing itself? Alternatively, how can I wait for the thread to exit?
Having read the other SO posts the talk about using pthread_cleanup_push() and pthread_cleanup_pop() but that doesn't allow me to check form one central place that all threads are killed and their resources released.
The short answer is that you can’t; but you can do something close.
Free(), malloc() and thus all paths leading to them are not signal safe; so you can’t call them from a signal handler. It is rare that these functions would notice the signal (re)entry, so unpredictable behaviour is the likely result.
A good pattern is to have the main thread notice signals have occurred, and perform the processing for them within it. You can do this, safely, by having the main thread employ a pthread_cond_t,pthread_mutex_t pair to watch a counter, and have the signal handler use the same pair to update the counter and notify the change.
Thus the main thread can treat signals as simple inputs to transition between states, such as Normal/SIGINT -> Quitting, Quitting/SIGALRM -> HardStop.
Free() is probably a bit heavy-handed, as it can cause your program to make sporadic memory references, which may be exploitable as an attack surface.

ISSUES with SIGCONT and waitpid() in LINUX

I'll try to keep it simple. I'm currently replicating a shell for LINUX. I use a linked list structure "job_list" to store all background processes. If a background process is terminated, then it is removed from the list. If a background process is suspended, its status within the list is changed from BACKGROUND to STOPPED. If the process is reawakened (through a SIGCONT signal), then the idea is that the process state inside the list should be changed back to BACKGROUND.
My problem is the following: when I send a SIGSTOP signal to a process, section //1 is executed and its change of state is successfully registered in the list. However, when I reawaken that same process using a SIGCONT signal, WIFCONTINUED(status) will return false, but WIFEXITED(status) will always return true. Consequently, section //3 is executed and the process is removed from the list.
What could be wrong? Thanks in advance.
void sigchld_handler (){
block_SIGCHLD();
job *item;
int l_size = list_size(job_list);
int i, new_pid, pid_wait, status, info;
enum status status_res;
for (i = 1; i <= l_size; i++){
item = get_item_bypos(job_list, i);
new_pid = item->pgid;
pid_wait = waitpid(new_pid, &status, WUNTRACED | WNOHANG);
if (WIFSTOPPED(status)){
//1
printf("****SUSPENDED\n");
item->state = STOPPED;
}else if (WIFCONTINUED(status)){
//2
printf("****CONTINUED\n");
item->state = BACKGROUND;
}else if (WIFEXITED(status)){
//3
printf("****EXITED\n");
l_size--;
i--;
delete_job(job_list, item);
}
}
print_job_list(job_list);
unblock_SIGCHLD();
}
You appear to be missing the WCONTINUED value in your call to waitpid.
From the waitpid specification:
pid_t waitpid(pid_t pid, int *stat_loc, int options);
The options argument is constructed from the bitwise-inclusive OR of zero or more of the following flags, defined in the header:
WCONTINUED
The waitpid() function shall report the status of any continued child process specified by pid whose status has not been reported since it continued from a job control stop.

Is it guaranteed that the kill() function will not return until the signal handler has done if a process kills itself?

Imagine the following program written in C:
void handler(int signo) {
write(STDOUT_FILENO, "handler\n", 8);
}
int main() {
signal(SIGUSR1, handler);
kill(getpid(), SIGUSR1);
write(STDOUT_FILENO, "after kill\n", 11);
}
If I run this program in Linux, is it possible the output is as follow:
after kill
handler
I tried many times, and the above result didn't appear.
If the SIGINT signal is delivered to the process after the puts("looping"); statement, then it will print the string "handler1" in the handle1 function. When the kill function returns, the output depends on when the SIGUSR1 signal will be delivered to the process. I think you can use the sigsuspend function to make sure it's what you want. By the way, before kill function returns, at least an unblocked signal is delivered to the process.
The following code outputs what you want and you need to block the SIGUSR1 signal first.
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
void handler(int signo) {
write(STDOUT_FILENO, "handler\n", 8);
}
int main() {
sigset_t newmask, oldmask;
sigemptyset( &newmask );
sigaddset( &newmask, SIGUSR1 );
signal(SIGUSR1, handler);
// block the SIGUSR1 signal
sigprocmask ( SIG_BLOCK, &newmask, &oldmask );
kill(getpid(), SIGUSR1);
write(STDOUT_FILENO, "after kill\n", 11);
// reset the signal mask
sigprocmask ( SIG_SETMASK, &oldmask, NULL );
}
puts is not good inside signals handlers. Read signal(7), puts is not async-signal-safe. You should use write(2) inside a signal handler (not puts).
You have edited your question to use write instead of  puts
And if you insist in wrongly using puts you should at least call fflush. But both are wrong inside signal handlers.
(Don't forget that stdoutis buffered)
BTW, it might happen (notably in multi-threaded application, where the signal handler is not running in the same thread that kill(2)-s the signal) that the signal handler is invoked after returning of kill
I'm not sure that you are guaranteed that the signal handler of a single-threaded process is returning before kill, even if I believe that would happen on most Linux kernels. What you should be sure is that the signal handler would eventually be called (but you cannot be sure on when exactly). Also, a kernel is permitted to lose some signals (e.g. if an external thing or process is sending a lot of same signals). So called POSIX real-time signals are the exception, not the norm (see also this)
If you have an event loop (e.g. around poll(2)) and accept to have Linux-specific code
you could consider using signalfd(2) and polling it inside your event loop.

ptrace and exec: can child process respond to ptrace BEFORE SIGTRAP?

I've recently run into this code:
if ((pid = fork()) == 0) {
ptrace(PT_TRACE_ME, 0, 0, 0); // trace
execl([originPath UTF8String], "", (char *) 0); // import binary memory into executable space
exit(2); // exit with err code 2 in case we could not import (this should not happen
You probably know this as it is some pretty basic code. The problem I have with the OS is that the child process is now "hostage" not being able to react in any way to it being "traced" as the execl() sends a SIGTRAP signal blocking the child. Can I prevent in any way an attacker from reading my process memory and dumping it to disk in this situation?
iOS and OS X now have a ptrace request: PT_DENY_ATTACH which is used by the traced process to prevent debuggers from controlling a child process.

SIGINT signal re-install in linux

I am writing a program dealing with Linux signals. To be more specific, I want to re-install signal SIGINT in child process, only to find that it doesn't work.
Here is a simpler version of my code:
void handler(int sig){
//do something
exit(0);
}
void handler2(int sig){
//do something
exit(0);
}
int main(){
signal(SIGINT, handler);
if ((pid = fork()) == 0) {
signal(SIGINT, handler2); // re-install signal SIGINT
// do something that takes some time
printf("In child process:\n");
execve("foo", argv, environ); // foo is a executable in local dir
exit(0);
}else{
int status;
waitpid(pid, &status, 0); // block itself waiting for child procee to exit
}
return 0;
}
When shell is printing "In child process:", I press ctrl+c. I find that function handler is executed without problem, but handler2 is never executed.
Could you help me with this bug in my code?
Update:
I want the child process to receive SIGINT signal during foo running process, is that possible?
It is not a bug - calling execve has replaced the running binary image. The function handler2() (and any other function of your binary) is no longer mapped in the program memory having been replaced by the image of "foo" and therefore all signal settings are replaced to a default.
If you wish the signal handler to be active during "foo" run, you have to:
make sure the handler function is mapped into the memory of foo
a signal handler is registered after "foo" starts.
One way to do this is to create a shared library that contains the signal handler and an init function that is defined as a constructor that registers said signal handler and force it into the "foo" memory by manipulating the environment under which you execve foo (the environ variable) to include
LD_PRELOAD=/path/to/shared_library.so
#gby's anwser has given comprehensive background knowlegde. I am here to give another solution without shared library.
Every time child process stops or terminates, parent process will receive SIGCHLD. You can handler this SIGCHLD signal to know if child process was terminated by SIGINT. In your handler:
pid_t pid = waitpid(pid_t pid,int * status,int options)
You can get status of child process through waitpid function.
if(WIFSIGNALED(status) && (pid == child_pid)){
if(WTERMSIG(status) == SIGINT){
// now you know your foo has received SIGINT.
// do whatever you like.
}
}

Resources