walkerlala
walkerlala

Reputation: 1680

Why can GDB mask tracee's SIGKILL when attaching to the tracee

The signal(7) man page says that SIGKILL cannot be caught, blocked, or ignored. But I just observed that after attaching to a process with GDB, I can no longer send SIGKILL to that process (similarly, other signal cannot be delivered either). But after I detach and quit GDB, SIGKILL is delivered as usual.

It seems to me that GDB has blocked that signal (on behalf of the tracee) when attaching, and unblocked it when detaching. However, the ptrace(2) man page says:

While being traced, the tracee will stop each time a signal is delivered, even if the signal is being ignored. (An exception is SIGKILL, which has its usual effect.)

So why does it behave this way? What tricks is GDB using?

Here is an trivial example for demonstration:

1. test program

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <string.h>

/* Simple error handling functions */

#define handle_error_en(en, msg) \
    do { errno = en; perror(msg); exit(EXIT_FAILURE); } while (0)

struct sigaction act;

void sighandler(int signum, siginfo_t *info, void *ptr) {
    printf("Received signal: %d\n", signum);
    printf("signal originate from pid[%d]\n", info->si_pid);
}

int
main(int argc, char *argv[])
{
    printf("Pid of the current process: %d\n", getpid());

    memset(&act, 0, sizeof(act));

    act.sa_sigaction = sighandler;
    act.sa_flags = SA_SIGINFO;

    sigaction(SIGQUIT, &act, NULL);

    while(1) {
        ;
    }

    return 0;
}

If you try to kill this program using SIGKILL (i.e., using kill -KILL ${pid}), it will die as expected. If you try to send it SIGQUIT (i.e., using kill -QUIT ${pid}), those printf statements get executed, as expected. However, if you have attached it with GDB before sending it signal, nothing will happen:

$ ##### in shell 1 #####
$ gdb
(gdb) attach ${pid}
(gdb)

/* now that gdb has attached successfully, in another shell: */

$ #### in shell 2 ####
$ kill -QUIT ${pid}    # nothing happen
$ kill -KILL ${pid}    # again, nothing happen!

/* now gdb detached */

##### in shell 1 ####
(gdb) quit

/* the process will receive SIGKILL */

##### in shell 2 ####
$ Killed    # the tracee receive **SIGKILL** eventually...

FYI, I am using a CentOS-6u3 and uname -r result in 2.6.32_1-16-0-0. My GDB version is: GNU gdb (GDB) Red Hat Enterprise Linux (7.2-56.el6) and my GCC version is: gcc (GCC) 3.4.6 20060404 (Red Hat 3.4.6-19.el6). An old machine...

Any idea will be appreciated ;-)

Upvotes: 4

Views: 1219

Answers (1)

Employed Russian
Employed Russian

Reputation: 213375

$ ##### in shell 1 ##### $ gdb (gdb) attach ${pid} (gdb)

The issue is that once GDB has attached to ${pid}, the inferior (being debugged) process is no longer running -- it is stopped.

The kernel will not do anything to it until it is either continued (with the (gdb) continue command), or it is no longer being traced ((gdb) detach or quit).

If you issue continue (either before or after kill -QUIT), you'll see this:

(gdb) c
Continuing.

kill -QUIT $pid executed in another shell:

Program received signal SIGQUIT, Quit.
main (argc=1, argv=0x7ffdcc9c1518) at t.c:35
35      }

(gdb) c
Continuing.
Received signal: 3
signal originate from pid[123419]

kill -KILL executed in another window:

Program terminated with signal SIGKILL, Killed.
The program no longer exists.
(gdb)

Upvotes: 3

Related Questions