4

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 ;-)

walkerlala
  • 1,599
  • 1
  • 19
  • 32
  • By "eventually", do you mean there is some delay between gdb detaching and the shell reporting that the process was killed? – Mark Plotnick Nov 26 '17 at 11:15
  • I am quite certain GDB isn't using any tricks. You might have a broken kernel. I doubt your results will reproduce on a different system. – Employed Russian Nov 26 '17 at 16:14
  • @EmployedRussian I am using ptrace(2) to implement similar functionality as GDB, but signal handling is becoming a mess because every time the tracee get that signal, it will get interrupt and the tracer will get notified spuriously...So I think GDB *is* using some tricks here. Why are you so sure? Can you clarify? – walkerlala Nov 27 '17 at 00:48
  • @MarkPlotnick no delay. There is no delay between gdb detaching and the shell reporting "killed". But I have not measured that pragmatically so it is hard to say whether there is any delay or not... – walkerlala Nov 27 '17 at 01:05
  • @EmployedRussian I try this with a 4.4.0 kernel (Ubuntu 16.04 LTS) and it is the same, gdb is still masking those signals... – walkerlala Nov 27 '17 at 01:07
  • Oh, I see. Answer coming ... – Employed Russian Nov 27 '17 at 01:17

1 Answers1

3

$ ##### 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)
Employed Russian
  • 199,314
  • 34
  • 295
  • 362
  • Do you know which signal does GDB use to implement control transfer? By control transfer, I mean, after the tracee executing a function and return, which signal is generated so that GDB can wait*() on it and seize control again? It is not SIGTRAP though many people claim that ... – walkerlala Nov 27 '17 at 01:54
  • @walkerlala This is probably best asked as a separate question. I *do* know, but this comment field is too short ;-) – Employed Russian Nov 27 '17 at 01:57
  • Here you go: https://stackoverflow.com/questions/47503036/what-signal-does-gdb-use-to-implement-control-transfer-between-tracee-and-tracer – walkerlala Nov 27 '17 at 02:10