zhouchengcom / google-breakpad

Automatically exported from code.google.com/p/google-breakpad
0 stars 0 forks source link

Breakpad's use of tgkill() is ineffective #377

Open GoogleCodeExporter opened 9 years ago

GoogleCodeExporter commented 9 years ago
On Linux, Breakpad's signal handler uses the tgkill() syscall to kill the 
process after 
reporting the crash.

When stracing the signal handler, I noticed that this doesn't work, and 
surprisingly, 
the process continues after the tgkill() call and only dies when it returns 
from the 
signal handler to re-run the original faulting instruction.

This means the "// not reached" comment is misleading.

The test program below demonstrates the behaviour.  tgkill() is only 
ineffective inside 
the signal handler, so I suspect this happens because SIGSEGV is blocked while 
the 
signal handler is running.

$ gcc -Wall tgkill.c -o tgkill
$ ./tgkill
survived
$ strace ./tgkill
...
rt_sigaction(SIGSEGV, {0x8048476, [SEGV], SA_RESTART}, {SIG_DFL}, 8) = 0
--- SIGSEGV (Segmentation fault) @ 0 (0) ---
rt_sigaction(SIGSEGV, {SIG_DFL}, {0x8048476, [SEGV], SA_RESTART}, 8) = 0
gettid()                                = 20946
getpid()                                = 20946
tgkill(20946, 20946, SIGSEGV)           = 0
write(2, "survived\n", 9survived
)               = 9
sigreturn()                             = ? (mask now [])
--- SIGSEGV (Segmentation fault) @ 0 (0) ---
+++ killed by SIGSEGV +++
Process 20946 detached

#include <stdio.h>

#include <signal.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>

int tgkill(pid_t tgid, pid_t tid, int sig) {
  syscall(__NR_tgkill, tgid, tid, sig);
  return 0;
}

pid_t gettid(void) {
  return syscall(__NR_gettid);
}

void handler(int sig) {
  signal(SIGSEGV, SIG_DFL);
  tgkill(getpid(), gettid(), SIGSEGV);
  fprintf(stderr, "survived\n");
}

int main() {
  signal(SIGSEGV, handler);
  *(int *) 0 = 0;
  fprintf(stderr, "not reached, of course\n");
  return 0;
}

Original issue reported on code.google.com by mseaborn@chromium.org on 25 Mar 2010 at 1:44

GoogleCodeExporter commented 9 years ago
From my experiments, raise() and kill() have the same behaviour.

I think you need to unblock the signal, e.g.

  sigset_t set;
  sigemptyset(&set);
  sigaddset(&set, SIGSEGV);
  pthread_sigmask(SIG_UNBLOCK, &set, NULL);

Original comment by mseaborn@chromium.org on 25 Mar 2010 at 3:32

GoogleCodeExporter commented 9 years ago
For comparison, see glibc's abort() implementation.  It also unblocks the 
signal and 
tries really hard to die. :-)

http://repo.or.cz/w/glibc.git/blob/HEAD:/stdlib/abort.c

Original comment by mseaborn@chromium.org on 25 Mar 2010 at 3:45