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

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

$ ##### 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)

Related

gdbserver can't intrrupt "SOME" process,kill(pid,2) called by gdbserver didn't send SIGINT to process,what's happening?

Envirment is:
target:x86_64 client,runs the program which is striped
Host:x86_64 server ,has code,toolchain,striped program,symbles file for debug
run gdbserver on target:
%gdbserver --multi :1234 /pathtolog/gdb.log
run program on target:
./someprogram &
[1] PID
run gdb on host:
%gdb
(gdb)target extended-remote TARGETIP:1234
(gdb)file someprogram
(gdb)setrootfs pathtorootfs
(gdb)...//set lib path etc.
(gdb)attach PID
...//load everything as normal
...//stop somewhere
(gdb)c
^C^CThe target is not responding to interrupt requests.
Stop debugging it? (y or n)
tried to find the root cause:
on the target:
gdb attach to gdbserver(yes I can use gdb on the target right now,but the target machine shall be released without gdb,symbles,etc. for size).
(gdb) b kill
Breakpoint 1 at 0xf760afb0
(gdb) c
Continuing.
when press ctrl+c from host gdb ,gdbserver will break into the breakpoint
Breakpoint 1, 0xf760afb0 in kill () from /lib/libc.so.6
(gdb)
I'v checked register,the %esp register shows like this:
(gdb) x /32wx 0xffee8070
0xffee8070: 0xfffffe0c 0x00000002 0x00000001 0x00000000
0xfffffe0c = -PID
0x00000002 = SIGINT
some program will get the signalwhen gdbserver continue .
so,kill() is good for "SOME PROGRAM",not all.
And I'v use tcpdump monitored data between gdb/gdbserver.
If kill() worked (for "GOOD" program),gdbserver will send a packet to gdb.
I'v tried sigmonitor,found out gdbserver didn't send any sigal to "BAD program" in this case.but I can call kill(pid,2) int gdbserver debuging gdb process
(gdb) call kill(PID,2)
then dmesg shows like this
[11902.060722] ==========send_signal===========
SIG 2 to 6141[a.out], tgid=6141
...
SIG 19 to 6142[a.out], tgid=6141
[11902.111135] Task Tree of 6142 = {
...
Any ideas?
Found out a possible match bug of gdbserver.
parameter of kill() called by gdbserver is -PID,not PID.
gdbserver sends SIGINT not to the process, but to the process group (-signal_pid).
But the attached process is not always a process group leader.
If not, "kill (-signal_pid, SIGINT)" returns error and fails to interrupt the attached process.
static void linux_request_interrupt (void)
{
/* Send a SIGINT to the process group. This acts just like the user
typed a ^C on the controlling terminal. */
- kill (-signal_pid, SIGINT);
+ kill (signal_pid, SIGINT);
}
This problem remained in gdb-8.1,don't know why they don't think it's a problem.

How do I launch a background process through a wrapper that'll undo the SIGINT ignore?

I want to launch a background process from bash in such a way that I can then send SIGINT signals to it (eg: via kill or htop). How do I do that?
By default, when a process is launched in the backround, SIGINT and SIGQUIT are ignored (see this question).
If the process is execed (=isn't just a subshell but is based on binary), you can run it through a wrapper that'll undo the SIGINT/SIGQUIT ignore:
reset_sigint.c:
#include <signal.h>
#include <unistd.h>
int main(int C, char**V)
{
sigaction(SIGINT,&(struct sigaction){.sa_handler=SIG_DFL}, 0);
execvp(V[1],V+1);
return 127;
}
To prevent the SIGINT/SIGQUIT ignore more generically for any process run as a command in the shell, you can run it with set -m on
sh -c 'set -m; ( sleep 10 )& ps -p $! -o ignored'
#compare with: sh -c '( sleep 10 )& ps -p $! -o ignored'
but that'll run the command in a separate process group as a (possibly undesirable) sideffect.

Signal handling with qemu-user

On my machine I have an aarch64 binary, that is statically compiled. I run it using qemu-aarch64-static with the -g 6566 flag. In another terminal I start up gdb-multiarch and connect as target remote localhost:6566.
I expect the binary to raise a signal for which I have a handler defined in the binary. I set a breakpoint at the handler from inside gdb-multiarch after connecting to remote. However, when the signal arises, the breakpoint is not hit on gdb-multiarch. Instead, on the terminal that runs the binary, I get a message along the lines of :-
[1] + 8388 suspended (signal) qemu-aarch64-static -g 6566 ./testbinary
Why does this happen? How can I set a breakpoint on the handler and debug it? I've tried SIGCHLD and SIGFPE.
This works for me with a recent QEMU:
$ cat sig.c
#include <stdlib.h>
#include <signal.h>
#include <stdio.h>
void handler(int sig) {
printf("In signal handler, signal %d\n", sig);
return;
}
int main(void) {
printf("hello world\n");
signal(SIGUSR1, handler);
raise(SIGUSR1);
printf("done\n");
return 0;
}
$ aarch64-linux-gnu-gcc -g -Wall -o sig sig.c -static
$ qemu-aarch64 -g 6566 ./sig
and then in another window:
$ gdb-multiarch
GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.2) 7.7.1
[etc]
(gdb) set arch aarch64
The target architecture is assumed to be aarch64
(gdb) file /tmp/sigs/sig
Reading symbols from /tmp/sigs/sig...done.
(gdb) target remote :6566
Remote debugging using :6566
0x0000000000400c98 in _start ()
(gdb) break handler
Breakpoint 1 at 0x400e44: file sig.c, line 6.
(gdb) c
Continuing.
Program received signal SIGUSR1, User defined signal 1.
0x0000000000405c68 in raise ()
(gdb) c
Continuing.
Breakpoint 1, handler (sig=10) at sig.c:6
6 printf("In signal handler, signal %d\n", sig);
(gdb)
As you can see, gdb gets control both immediately the process receives the signal and then again when we hit the breakpoint for the handler function.
Incidentally, (integer) dividing by zero is not a reliable way to provoke a signal. This is undefined behaviour in C, and the implementation is free to do the most convenient thing. On x86 this typically results in a SIGFPE. On ARM you will typically find that the result is zero and execution will continue without a signal. (This is a manifestation of the different behaviour of the underlying hardware instructions for division between the two architectures.)
i was doing some R&D for your answer and find following answer
"Internally, bad memory accesses result in the Mach exception EXC_BAD_ACCESS being sent to the program. Normally, this is translated into a SIGBUS UNIX signal. However, gdb intercepts Mach exceptions directly, before the signal translation. The solution is to give gdb the command set dont-handle-bad-access 1 before running your program. Then the normal mechanism is used, and breakpoints inside your signal handler are honored."
The link is gdb: set a breakpoint for a SIGBUS handler
It perhaps help you by considering that qemu does not change the functionality of base operations

Linux effect of ptrace TRACEME call

I have the following code. It simply calls ptrace(PTRACE_TRACEME) before going into an infinite loop.
I have two issues:
After executing the binary, I can't attach with gdb even if I am root.
With ptrace(PTRACE_TRACEME), I can't terminate the process with Ctrl-C (SIGINT). it simply stops.
Can someone explain what's going on? Thank you in advance.
PS: I know that most debuggers fork a child which then calls ptrace(PTRACE_TRACEME) before execve. No need to remind me of this.
#include <sys/ptrace.h>
#include <sys/reg.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
int main(int argc, char **argv) {
printf("my pid : %d\n", getpid());
ptrace(PTRACE_TRACEME);
while(1){
printf("euid : %d\n", geteuid());
sleep(2);
}
return 0;
}
after executing this binary, I can't attach gdb even if I am root.
From man ptrace:
ERRORS
EPERM The specified process cannot be traced. This could be
because the parent has insufficient privileges (the required
capability is CAP_SYS_PTRACE); non-root processes cannot trace
processes that they cannot send signals to or those running
set-user-ID/set- group-ID programs, for obvious reasons.
Alternatively, the process may already be being traced, or be init(8) (PID 1).
with ptrace(PTRACE_TRACEME), I can't terminate the process with Ctrl-C (SIGINT). it simply stops.
From man ptrace:
DESCRIPTION
While being traced, the child will stop each time a signal is
delivered, even if the signal is being ignored. (The exception is
SIGKILL, which has its usual effect.) The parent will be notified at
its next wait(2) and may inspect and modify the child process
while it is stopped. The parent then causes the child to continue,
optionally ignoring the delivered signal (or even delivering a
different signal instead).

Can a Linux process block external signals but accept signals from its own process?

I am trying to setup a Linux process, which blocks SIGTERM that is sent from kill command (or any other process), but allows SIGTERM to be sent from within itself (through kill(2) system call).
Is it possible?
Here is an example program that I wrote, but it SIG_BLOCKS both external and internal signals, so it doesn't do what I want:
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
int main(int argc, char **argv)
{
sigset_t sigs;
sigemptyset(&sigs);
sigaddset(&sigs, SIGTERM);
sigprocmask(SIG_BLOCK, &sigs, 0);
printf("Sleeping 30 secs, try killing me! (pid: %d)\n", getpid());
sleep(30);
printf("About to call kill\n");
kill(getpid(), SIGTERM);
printf("This never happens!\n");
return 1;
}
The output is:
Sleeping 30 secs, try killing me! (pid: 29416)
About to call kill
This never happens!
But it should be:
Sleeping 30 secs, try killing me! (pid: 29416)
About to call kill
Because the process should get killed from within through kill(getpid(), SIGTERM).
Not sure if this is what you're after, but you can set up a signal handler using sigaction with the SA_SIGINFO flag, have your SIGTERM handler only call _exit if siginfo.si_pid is your PID
According to what I test if you don't block the signal. you can see the expected behaviour.
But if you blocked the signal, the kill will return with value 0, and since the program continue to execute,it'll print the line and exit. I'm using ubuntu 12.04LTS for testing.

Resources