what happens after a process calls ptrace(PTRACE_TRACEME, ...)? - c

After a process calls ptrace(PTRACE_TRACEME, ...), where the tracee stoped?
Is the tracee stoped in exec() system call? (seems not)
Is the tracee stoped in dynamic linker text?
...
If I compile a executable without any dynamic link libraries and glibc c-runtime, and specify the entry-point,
the tracee would stop at the entry-point.
But when I compile a executable with glibc(gcc hello-world.c), it would stoped at /lib/ld-2.20.so offset + 0xfb0. (cat /proc/[pid]/maps)
Hope more details about this.
man ptrace seems no help.

The tracee is usually stopped when a call to execve() is done which will cause it to be sent a SIGTRAP.
Some uses raise() to ensure a signal is sent, like this :
ptrace(PTRACE_TRACEME);
kill(getpid(), SIGSTOP);
return execvp(args[0], args);

kill(getpid(), SIGSTOP);
return execvp(args[0], args);
thx. so, when the tracee is stopped, the CPU EIP register points to which instruction?
The specification of the kill(pid_t pid, int sig) function answers this.
If the value of pid causes sig to be generated for the sending
process, and if sig is not blocked for the calling thread and if no
other thread has sig unblocked or is waiting in a sigwait() function
for sig, either sig or at least one pending unblocked signal shall be
delivered to the sending thread before kill() returns.
So, due to the above kill(getpid(), SIGSTOP) the process is stopped before the library function kill() returns, typically at the instruction just after the system call.

Related

How to block tracee catch SIGTRAP with ptrace

When I want to inspect tracee syscall I use PTRACE_ATTACH , then PTRACE_SYSCALL in loop , and finally PTRACE_DETACH .
The problem is that if the tracee registered to SIGTRAP or SIGCONT it can change his behaviour while I use PTRACE_SYSCALL or PTRACE_DETACH and I don't want to do it.
When I attach to tracee with PTRACE_ATTACH tracee got SIGSTOP but it can't register/reaction to this signal, so that it fine.
What is the solution that the tracee could not catch SIGTRAP when I use PTRACE_SYSCALL or SIGCONT when I used PTRACE_CONT
The tracer always gets first dibs at signal handling, and it can choose to suppress the signal so that the tracee's handler doesn't run. The only thing you have to worry about is if the tracee blocks the signal with something like sigprocmask or a blocking call to pselect and uses something like sigpending or signalfd to look for it, which you could fix by modifying or emulating the relevant syscalls.
To suppress a signal, just pass 0 for sig when resuming after signal-delivery-stop. From man 2 ptrace:
Signal injection and suppression
After signal-delivery-stop is observed by the tracer, the tracer should restart the tracee with the call
ptrace(PTRACE_restart, pid, 0, sig)
where PTRACE_restart is one of the restarting ptrace requests. If sig is 0, then a signal is not delivered. Otherwise, the signal sig is delivered. This operation is called signal injection in this manual page, to distinguish it from signal-delivery-stop.

Does kill() work instantly in C? Or is there a chance the next line runs?

Come to think of it, I guess the signal is sent to the OS and the program will proceed, even if the signal is still pending on the OS side. Can someone confirm?
kill(this_process, signum);
printf("will line this run in any reality of the multi-verse?");
printf("or should I go to sleep?");
sleep(10);
From the POSIX specification of kill()
If the value of pid causes sig to be generated for the sending process, and if sig is not blocked for the calling thread and if no other thread has sig unblocked or is waiting in a sigwait() function for sig, either sig or at least one pending unblocked signal shall be delivered to the sending thread before kill() returns.
So if there are no other signals pending for the process when you call kill(), the signal you send has to be delivered immediately. But if there are other signals pending, this signal could be queued and an earlier signal delivered immediately.

What does "wait and waitpid are always interrupted when a signal is caught" mean?

From APUE:
To prevent applications from having to handle interrupted system
calls, 4.2BSD introduced the automatic restarting of certain
interrupted system calls. The system calls that were automatically
restarted are ioctl, read, readv, write, writev, wait, and waitpid. As
we’ve mentioned, the first five of these functions are interrupted by
a signal only if they are operating on a slow device; wait and waitpid
are always interrupted when a signal is caught. Since this caused a
problem for some applications that didn’t want the operation restarted
if it was interrupted, 4.3BSD allowed the process to disable this
feature on a per-signal basis.
Does it mean that before automatic restarting was introduced, if a process catch a signal, wait and waitpid would immediately stop waiting and execute the subsequent code?
For example:
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
void handler(int sig){}
void handler2(int sig){}
int main(){
pid_t pid;
int status;
signal(SIGUSR1, handler);
signal(SIGUSR2, handler2);
if((pid == fork()) < 0){
printf("fork error\n");
}else{
if(pid){
//child
//do something, needs several hours.
}else{
//parent
waitpid(pid, &status, 0);
printf("Hello world\n");
}
}
return 0;
}
If not providing automatic restarting, when I run this program in the background gcc test.c && ./a.out &, then I send a signal kill -SIGUSR1 pid or kill -SIGUSR2 pid, waitpid would return and the code after waitpid(pid, &status, 0); would execute.
And if providing automatic restarting, waitpid would execute again and the parent will remain waiting.
Is my understanding correct?
The original behaviour of signal() (System-V semantics) was, to interrupt any system call if the process currently slept, execute the signal handler and the system call returns with -EINTR. Then, BSD4.3 invented the restart mechanism, which would restart any system call automatically after it was interrupted. This avoids having to write a loop for each syscall if signal handlers are involved.
Linux did not change the semantics of the signal() syscall. However, the signal() glibc wrapper function nowadays calls the syscall sigaction() with SA_RESTART flag by default. So, if you do not need the restart behaviour, you have to call sigaction() and omit that flag.
So, your code indeed makes use of the restart mechanism on both BSD and linux
wait and waitpid like any other blocking function can be interrupted with errno set to EINTR - this is exactly because a signal handler can do very little - mostly set some flags. Now if a blocking function would not return with EINTR, how could you react to an signal in any way?!
But this also means that you need to have a complicated loop over every function - you might have some signals for which you know you don't want the system calls be interrupted, so you can set this signal to have automatic restart.

SIGCHLD and fork + waitpid() in a library

I am writing a library that uses fork() and exec() to run external programs.
The code in my library runs as a separate thread.
The library thread needs to waitpid() on the forked process to know the exit code of the child process.
Unfortunately if the application using my library register a signal handler for SIGCHLD, the waitpid() calls returns with an error ECHILD.
How do I deal with this as a library with the application having minimal impact?
I essentially want the child to remain a zombie and have control over when it's reaped.
Can I insulate myself from what the application decides to do?
Can I hijack the signal handler in some way and put it back after my waitpid is done?
Freeing resources allocated by a library is an application bug. It's the same in principle as if the appliction did something like free(yourlib_create_context(...)), although of course the consequences are less severe. There's no reason to try to support this kind of nonsensical application behavior; just document that it's invalid.
If you want to "shield" against this sort of programming error, mask signals an then call abort if waitpid fails with ECHLD. This will unconditionally terminate the process without the applicating having any chance to catch it.
You can do just as system does:
system() executes a command specified in command by calling /bin/sh -c command, and returns after the command has been completed. During execution of the command, SIGCHLD will be blocked, and SIGINT and SIGQUIT will be ignored.
An outline could be
pid_t pid;
sigset_t block, backup;
sigemptyset(&block);
sigaddset(&block, SIGCHLD);
sigprocmask(SIG_BLOCK, &block, &backup);
if ((pid = fork()) < 0) {
// error handle
} else if (pid == 0) { // child
sigprocmask(SIG_SETMASK, &backup, NULL);
// ...
} else {
// waitpid
sigprocmask(SIG_SETMASK, &backup, NULL);
}
// ...

Which child process send SIGCHLD

I'm trying to understand the signal handling and process. I have a parent process that created several child processes. Now in the parent process I have a list of all child processes. when a child is terminated I want to delete it from the list. I know that when a child is terminated he's sending SIGCHLD to the parent. OK, now it's the tricky part, how can I find out if that child terminated or just suspended or something else?
As you said
when a child is terminated he's sending SIGCHLD to the parent
Make the parent call wait().
Either
by a blocking call to wait() or
on a continous base or
by seting up a signal handler serving SIGCHLD which in turn calls wait().
If having called waitpid() with a pid of -1 using the option WUNTRACED and then applying the macro WIFSTOPPED to the value returend by waitpid() it tells you whether the process was stopped or ended.
For Linux since kernel version 2.6.10 the same as for WIFSTOPPED applies to WCONTINUED.
There is a system call in signal.h - sigaction() , similar to siganl( ) but more useful.Visit http://man7.org/linux/man-pages/man2/sigaction.2.html
The signal handler function prototype for sigaction() looks like this:
void sa_handler(int signo, siginfo_t *si, void *ucontext);
It has an argument of type siginfo_t which contains all the information about the signal including the pid of the process which sent it.
Though using conventional signal handling mechanism, it can be done using waitid() as mentioned in previous answer(s) but waitid() needs pid as one of its argument.
Using the options argument of the wait() family of functions (waitpid(),waitid()).
http://linux.die.net/man/2/waitid

Resources