This patch fixes a couple problems with ptrace's interaction with
stopped processes on Linux 2.4.
The most significant bug is that gdb cannot attach to a stopped
process. Specifically, the wait that follows the PTRACE_ATTACH will
block indefinitely.
Another bug is that it is not possible to use PTRACE_DETACH to leave a
process stopped, because ptrace ignores SIGSTOPs sent by the tracing
process.
This patch is against 2.4.16 on x86. I have tested gdb and strace.
After this patch is reviewed, I would be happy to submit an analogous
patch for the other platforms, although I cannot test it.
Vic Zandy
--- linux-2.4.16/arch/i386/kernel/signal.c Fri Sep 14 16:15:40 2001
+++ linux-2.4.16.1/arch/i386/kernel/signal.c Fri Dec 21 11:05:45 2001
@@ -620,9 +620,9 @@
continue;
current->exit_code = 0;
- /* The debugger continued. Ignore SIGSTOP. */
- if (signr == SIGSTOP)
- continue;
+ /* The debugger continued. */
+ if (signr == SIGSTOP && current->ptrace & PT_PTRACED)
+ continue; /* ignore SIGSTOP */
/* Update the siginfo structure. Is this good? */
if (signr != info.si_signo) {
--- linux-2.4.16/kernel/ptrace.c Wed Nov 21 16:43:01 2001
+++ linux-2.4.16.1/kernel/ptrace.c Fri Dec 21 10:42:44 2001
@@ -89,8 +89,10 @@
SET_LINKS(task);
}
write_unlock_irq(&tasklist_lock);
-
- send_sig(SIGSTOP, task, 1);
+ if (task->state != TASK_STOPPED)
+ send_sig(SIGSTOP, task, 1);
+ else
+ task->exit_code = SIGSTOP;
return 0;
bad:
[email protected] said:
> This patch is against 2.4.16 on x86. I have tested gdb and strace.
Also make sure UML still runs.
Jeff
vic <[email protected]> writes:
> --- linux-2.4.16/kernel/ptrace.c Wed Nov 21 16:43:01 2001
> +++ linux-2.4.16.1/kernel/ptrace.c Fri Dec 21 10:42:44 2001
> @@ -89,8 +89,10 @@
> SET_LINKS(task);
> }
> write_unlock_irq(&tasklist_lock);
> -
> - send_sig(SIGSTOP, task, 1);
> + if (task->state != TASK_STOPPED)
> + send_sig(SIGSTOP, task, 1);
> + else
> + task->exit_code = SIGSTOP;
> return 0;
>
> bad:
It seems that trace is started in the place different from
usual. Then, I think PTRACE_KILL doesn't work.
If it need, I think it should wake up a task.
--
OGAWA Hirofumi <[email protected]>
vic <[email protected]> writes:
> The most significant bug is that gdb cannot attach to a stopped
> process. Specifically, the wait that follows the PTRACE_ATTACH will
> block indefinitely.
Are you sure this is a bug in ptrace? Maybe gdb and other such programs could
notice that a wait is inappropriate in this case and not do it.
(It looks to me like the wait actually does complete when the process is
continued.)
Also, is this something that used to work? Or would this be a change in the
semantics of ptrace?
> Another bug is that it is not possible to use PTRACE_DETACH to leave a
> process stopped, because ptrace ignores SIGSTOPs sent by the tracing
> process.
Unless I'm missing something (frequently the case), there are two cases here:
(1) the tracer wants to leave the tracee stopped, and (2) the tracer wants the
process to continue running in as natural a way as possible, meaning without
sending it a SIGCONT (which can cause the SIGCONT signal handler to execute).
As things currently stand, we have behavior (2), and (1) is not possible.
With your change, we'd have behavior (1), and (2) would not be possible.
To me, (2) seems very important, so I wouldn't want to give it up. Is there
some way we can get both?
> This patch is against 2.4.16 on x86. I have tested gdb and strace.
Although SUBTERFUGUE isn't widely used, I'd be curious to know how it reacts
to this patch. (If you're running Debian, there's a package.)
Mike
This is to respond to feedback for the ptrace patch I sent toward the
end of december. The original message is below.
From: Alan Cox <[email protected]>
> > + if (signr == SIGSTOP && current->ptrace & PT_PTRACED)
> This does not I suspect do what you think - surely you want brackets ?
I agree the second term should be wrapped in parens (it is now in the
patch below); but isn't that logically equivalent to what I had?
From: Mike Coleman <[email protected]>:
> Also, is this something that used to work? Or would this be a change in the
> semantics of ptrace?
This is a change of semantics at least going back to 2.2.
>> Another bug is that it is not possible to use PTRACE_DETACH to leave a
>> process stopped, because ptrace ignores SIGSTOPs sent by the tracing
>> process.
>
> Unless I'm missing something (frequently the case), there are two cases here:
> (1) the tracer wants to leave the tracee stopped, and (2) the tracer wants the
> process to continue running in as natural a way as possible, meaning without
> sending it a SIGCONT (which can cause the SIGCONT signal handler to execute).
> As things currently stand, we have behavior (2), and (1) is not possible.
> With your change, we'd have behavior (1), and (2) would not be possible.
I agree that the ability to do (2) should be preserved, but I don't
see how this patch breaks it; do you have an example?
From: OGAWA Hirofumi <[email protected]>:
>> --- linux-2.4.16/kernel/ptrace.c Wed Nov 21 16:43:01 2001
>> +++ linux-2.4.16.1/kernel/ptrace.c Fri Dec 21 10:42:44 2001
>> @@ -89,8 +89,10 @@
>> SET_LINKS(task);
>> }
>> write_unlock_irq(&tasklist_lock);
>> -
>> - send_sig(SIGSTOP, task, 1);
>> + if (task->state != TASK_STOPPED)
>> + send_sig(SIGSTOP, task, 1);
>> + else
>> + task->exit_code = SIGSTOP;
>> return 0;
>>
>> bad:
>
> It seems that trace is started in the place different from
> usual. Then, I think PTRACE_KILL doesn't work.
I don't agree, it seems to work for me.
I'd still want to check uml and subterfuge, which I'll do after these
points are cleared up.
Thanks,
Vic
From: vic <[email protected]>
Subject: [PATCH] ptrace on stopped processes (2.4)
To: [email protected]
Cc: [email protected], [email protected],
[email protected]
Date: Fri, 21 Dec 2001 13:53:32 -0600
This patch fixes a couple problems with ptrace's interaction with
stopped processes on Linux 2.4.
The most significant bug is that gdb cannot attach to a stopped
process. Specifically, the wait that follows the PTRACE_ATTACH will
block indefinitely.
Another bug is that it is not possible to use PTRACE_DETACH to leave a
process stopped, because ptrace ignores SIGSTOPs sent by the tracing
process.
This patch is against 2.4.16 on x86. I have tested gdb and strace.
After this patch is reviewed, I would be happy to submit an analogous
patch for the other platforms, although I cannot test it.
Vic Zandy
--- linux-2.4.16/arch/i386/kernel/signal.c Fri Sep 14 16:15:40 2001
+++ linux-2.4.16.1/arch/i386/kernel/signal.c Wed Jan 16 22:19:16 2002
@@ -620,9 +620,9 @@
continue;
current->exit_code = 0;
- /* The debugger continued. Ignore SIGSTOP. */
- if (signr == SIGSTOP)
- continue;
+ /* The debugger continued. */
+ if (signr == SIGSTOP && (current->ptrace & PT_PTRACED))
+ continue; /* ignore SIGSTOP */
/* Update the siginfo structure. Is this good? */
if (signr != info.si_signo) {
--- linux-2.4.16/kernel/ptrace.c Wed Nov 21 16:43:01 2001
+++ linux-2.4.16.1/kernel/ptrace.c Fri Dec 21 10:42:44 2001
@@ -89,8 +89,10 @@
SET_LINKS(task);
}
write_unlock_irq(&tasklist_lock);
-
- send_sig(SIGSTOP, task, 1);
+ if (task->state != TASK_STOPPED)
+ send_sig(SIGSTOP, task, 1);
+ else
+ task->exit_code = SIGSTOP;
return 0;
bad:
vic <[email protected]> writes:
> From: OGAWA Hirofumi <[email protected]>:
> >> --- linux-2.4.16/kernel/ptrace.c Wed Nov 21 16:43:01 2001
> >> +++ linux-2.4.16.1/kernel/ptrace.c Fri Dec 21 10:42:44 2001
> >> @@ -89,8 +89,10 @@
> >> SET_LINKS(task);
> >> }
> >> write_unlock_irq(&tasklist_lock);
> >> -
> >> - send_sig(SIGSTOP, task, 1);
> >> + if (task->state != TASK_STOPPED)
> >> + send_sig(SIGSTOP, task, 1);
> >> + else
> >> + task->exit_code = SIGSTOP;
> >> return 0;
> >>
> >> bad:
> >
> > It seems that trace is started in the place different from
> > usual. Then, I think PTRACE_KILL doesn't work.
>
> I don't agree, it seems to work for me.
I tested the following on linux-2.4.16 + your_patch:
#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/ptrace.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
pid_t pid;
int ret, status;
pid = fork();
if (pid == -1)
exit(1);
if (pid == 0) {
raise(SIGSTOP);
while (1)
;
_exit(1);
}
ret = waitpid(pid, &status, WUNTRACED);
if (ret == -1) {
perror("waitpid (1)");
exit(1);
}
ret = ptrace(PTRACE_ATTACH, pid, NULL, NULL);
if (ret == -1) {
perror("PTRACE_ATTACH");
exit(1);
}
ret = waitpid(pid, &status, 0);
if (ret == -1) {
perror("waitpid (2)");
exit(1);
}
ret = ptrace(PTRACE_KILL, pid, NULL, NULL);
if (ret == -1) {
perror("PTRACE_KILL");
exit(1);
}
return 0;
}
Test result:
hirofumi@devron (ptrace)[1111]$ ps ax|grep ptrace
688 tty1 S 0:00 grep ptrace
hirofumi@devron (ptrace)[1112]$ ls
ptrace ptrace.c
hirofumi@devron (ptrace)[1113]$ ./ptrace
hirofumi@devron (ptrace)[1114]$ ps ax|grep ptrace
691 tty1 R 0:04 ./ptrace
693 tty1 S 0:00 grep ptrace
hirofumi@devron (ptrace)[1115]$
Do I misunderstand something?
--
OGAWA Hirofumi <[email protected]>
vic <[email protected]> writes:
> From: Mike Coleman <[email protected]>:
> > Also, is this something that used to work? Or would this be a change in the
> > semantics of ptrace?
>
> This is a change of semantics at least going back to 2.2.
Okay. Is it at least backward compatible? Or are some tools expected to
break?
> > Unless I'm missing something (frequently the case), there are two cases here:
> > (1) the tracer wants to leave the tracee stopped, and (2) the tracer wants the
> > process to continue running in as natural a way as possible, meaning without
> > sending it a SIGCONT (which can cause the SIGCONT signal handler to execute).
> > As things currently stand, we have behavior (2), and (1) is not possible.
> > With your change, we'd have behavior (1), and (2) would not be possible.
>
> I agree that the ability to do (2) should be preserved, but I don't
> see how this patch breaks it; do you have an example?
No, I was just going by reading the kernel code. Can you describe how each of
(1) and (2) are accomplished by the ptracing program (with your patch)?
Mike
Now I see the problem with PTRACE_KILL. Thanks for the example.
I'm looking into it. I need to justify the quoted portion of the
patch or find a better way to get its effect.
In the meantime, the problem could be fixed by changing the
PTRACE_KILL implementation to call send_sig instead of setting
exit_code. How does that strike people?
More generally, could someone explain to me why there are two
mechanisms for interprocess signaling within the kernel, exit_code and
send_sig?
Thanks,
Vic
> vic <[email protected]> writes:
>
>> From: OGAWA Hirofumi <[email protected]>:
>> >> --- linux-2.4.16/kernel/ptrace.c Wed Nov 21 16:43:01 2001
>> >> +++ linux-2.4.16.1/kernel/ptrace.c Fri Dec 21 10:42:44 2001
>> >> @@ -89,8 +89,10 @@
>> >> SET_LINKS(task);
>> >> }
>> >> write_unlock_irq(&tasklist_lock);
>> >> -
>> >> - send_sig(SIGSTOP, task, 1);
>> >> + if (task->state != TASK_STOPPED)
>> >> + send_sig(SIGSTOP, task, 1);
>> >> + else
>> >> + task->exit_code = SIGSTOP;
>> >> return 0;
>> >>
>> >> bad:
>> >
>> > It seems that trace is started in the place different from
>> > usual. Then, I think PTRACE_KILL doesn't work.
>>
>> I don't agree, it seems to work for me.
>
> I tested the following on linux-2.4.16 + your_patch:
>
> #include <stdio.h>
> #include <signal.h>
> #include <sys/types.h>
> #include <sys/wait.h>
> #include <sys/ptrace.h>
> #include <unistd.h>
>
> int main(int argc, char *argv[])
> {
> pid_t pid;
> int ret, status;
>
> pid = fork();
> if (pid == -1)
> exit(1);
> if (pid == 0) {
> raise(SIGSTOP);
> while (1)
> ;
> _exit(1);
> }
>
> ret = waitpid(pid, &status, WUNTRACED);
> if (ret == -1) {
> perror("waitpid (1)");
> exit(1);
> }
>
> ret = ptrace(PTRACE_ATTACH, pid, NULL, NULL);
> if (ret == -1) {
> perror("PTRACE_ATTACH");
> exit(1);
> }
> ret = waitpid(pid, &status, 0);
> if (ret == -1) {
> perror("waitpid (2)");
> exit(1);
> }
> ret = ptrace(PTRACE_KILL, pid, NULL, NULL);
> if (ret == -1) {
> perror("PTRACE_KILL");
> exit(1);
> }
>
> return 0;
> }
>
> Test result:
>
> hirofumi@devron (ptrace)[1111]$ ps ax|grep ptrace
> 688 tty1 S 0:00 grep ptrace
> hirofumi@devron (ptrace)[1112]$ ls
> ptrace ptrace.c
> hirofumi@devron (ptrace)[1113]$ ./ptrace
> hirofumi@devron (ptrace)[1114]$ ps ax|grep ptrace
> 691 tty1 R 0:04 ./ptrace
> 693 tty1 S 0:00 grep ptrace
> hirofumi@devron (ptrace)[1115]$
>
> Do I misunderstand something?
> --
> OGAWA Hirofumi <[email protected]>
vic <[email protected]> writes:
> Now I see the problem with PTRACE_KILL. Thanks for the example.
>
> I'm looking into it. I need to justify the quoted portion of the
> patch or find a better way to get its effect.
>
> In the meantime, the problem could be fixed by changing the
> PTRACE_KILL implementation to call send_sig instead of setting
> exit_code. How does that strike people?
PTRACE_SYSCALL, PTRACE_CONT, and PTRACE_SINGLESTEP can't send a signal
by the same reason. Please read the do_signal().
--
OGAWA Hirofumi <[email protected]>
> PTRACE_SYSCALL, PTRACE_CONT, and PTRACE_SINGLESTEP can't send a signal
> by the same reason. Please read the do_signal().
I've read that function, but I don't see why it would not get along
with my suggestion to send SIGKILL rather than set exit_code to
implement PTRACE_KILL.
No doubt I can be rather thick; in this case, induction doesn't help me.
vic <[email protected]> writes:
> > PTRACE_SYSCALL, PTRACE_CONT, and PTRACE_SINGLESTEP can't send a signal
> > by the same reason. Please read the do_signal().
>
> I've read that function, but I don't see why it would not get along
> with my suggestion to send SIGKILL rather than set exit_code to
> implement PTRACE_KILL.
>
> No doubt I can be rather thick; in this case, induction doesn't help me.
kill(pid, SIGKILL) != ptrace(PTRACE_KILL, pid, NULL, NULL).
Whether the same effect as kill() is required for PTRACE_KILL is the
problem which is unrelated to this problem. If so, please argue on
another thread.
And If PTRACE_SYSCALL, PTRACE_CONT, and PTRACE_SINGLESTEP can send the
signal, PTRACE_KILL also work.
BTW, did you read my first email? What do you think of my suggestion?
In an example,
ptrace_attach(),
if (task->p_pptr != current) {
REMOVE_LINKS(task);
task->p_pptr = current;
SET_LINKS(task);
}
write_unlock_irq(&tasklist_lock);
stopped = (task->state == TASK_STOPPED);
send_sig(SIGSTOP, task, 1);
if (stopped)
wake_up_process(task);
return 0;
Note, this code isn't investigating at all.
--
OGAWA Hirofumi <[email protected]>
I'm sorry for this long message and my delayed replies to everyone's
feedback.
"Mike Coleman" <[email protected]> writes:
> Okay. Is it at least backward compatible? Or are some tools expected to
> break?
Some tools may break: those that (1) attach to a stopped
process or (2) detach and leave the process stopped. However,
I expect the maintainers of such tools will appreciate this
patch, as it would simplify the way they do these things now.
First, a tool that wants to attach to a stopped process needs
some tricks. For example, if you SIGSTOP any process and then
try to attach gdb to it, gdb will hang. The problem is that
when the process is already stopped, the call to wait after
PTRACE_ATTACH can block indefinitely because the process
exit_code is left at 0 (so wait blocks) but the process is not
awake. Currently tools need to detect this condition and
manually wind the process past the pending SIGSTOP. gdb
doesn't do that.
Second, our tool Paradyn (http://www.paradyn.org) sometimes wants to
detach and leave a process stopped; I am not aware of any other
tool that needs to do this, but it's not a strange request.
The natural way you would do this with ptrace (PTRACE_DEATCH
with SIGSTOP) does not work. We found that we had to read the
kernel to figure out how to do it reliably on Linux (i.e., to
understand the race conditions). The algorithm that we
eventually settled on is non-intuitive and involves calling
sleep to avoid a race we saw no other way around.
Again, tools that work around either of these problem, like
ours, will be affected by this patch: they will need to retire
their kludges and instead do what they probably wanted to do in
the first place.
Offline I corresponded with OGAWA Hirofumi and he gave me an
approach to the PTRACE_KILL issue that I have been slow to
understand. Now when attaching, I always send the SIGSTOP, but
wake the process if it was already stopped. I leave the
exit_code the way it was.
Mike Coleman is concerned about SIGCONT and whether this patch
prevents processes from being continued naturally, without
handling a SIGCONT. Nothing has changed in this aspect. If
the tracing process wants to continue a process, it just either
does PTRACE_CONT or PTRACE_DETACH as usual.
As promised, I will check UML and Subterfuge; I haven't had the
time. If anyone can alert me to potentially problematic
operations in these programs, that would very helpful.
The updated patch is below.
Thanks,
Vic
--- /home/vic/src/linux-2.4.16/kernel/ptrace.c Wed Nov 21 16:43:01 2001
+++ /home/vic/src/linux-2.4.16.1/kernel/ptrace.c Mon Jan 28 12:12:26 2002
@@ -54,6 +54,8 @@
int ptrace_attach(struct task_struct *task)
{
+ int stopped;
+
task_lock(task);
if (task->pid <= 1)
goto bad;
@@ -90,7 +92,13 @@
}
write_unlock_irq(&tasklist_lock);
+ stopped = (task->state == TASK_STOPPED);
send_sig(SIGSTOP, task, 1);
+ /* If it was stopped when we got here,
+ clear the pending SIGSTOP. */
+ if (stopped)
+ wake_up_process(task);
+
return 0;
bad:
--- /home/vic/src/linux-2.4.16/arch/i386/kernel/signal.c Fri Sep 14 16:15:40 2001
+++ /home/vic/src/linux-2.4.16.1/arch/i386/kernel/signal.c Wed Jan 16 22:19:16 2002
@@ -620,9 +620,9 @@
continue;
current->exit_code = 0;
- /* The debugger continued. Ignore SIGSTOP. */
- if (signr == SIGSTOP)
- continue;
+ /* The debugger continued. */
+ if (signr == SIGSTOP && (current->ptrace & PT_PTRACED))
+ continue; /* ignore SIGSTOP */
/* Update the siginfo structure. Is this good? */
if (signr != info.si_signo) {
This is a repost of the ptrace patch to 2.4 kernels
we've discussed in recent months.
Since the last post, I have updated it to linux 2.4.18
(no changes) and tested it with subterfuge and uml.
Subterfuge seems to be unaffected.
UML needs minor modifications; I've discussed them with
Jeff Dike and (I believe) he is happy.
I believe I have addressed everyone's concerns.
The patch fixes these two bugs:
1. gdb and other tools cannot attach to a stopped
process. The wait that follows the PTRACE_ATTACH
will block indefinitely.
2. It is not possible to use PTRACE_DETACH to leave
a process stopped, because ptrace ignores SIGSTOPs
sent by the tracing process.
Vic
--- /home/vic/p/linux-2.4.18.orig/kernel/ptrace.c Wed Mar 13 13:14:54 2002
+++ /home/vic/p/linux-2.4.18/kernel/ptrace.c Mon Mar 18 21:58:11 2002
@@ -54,6 +54,7 @@
int ptrace_attach(struct task_struct *task)
{
+ int stopped;
task_lock(task);
if (task->pid <= 1)
goto bad;
@@ -90,7 +91,13 @@
}
write_unlock_irq(&tasklist_lock);
+ stopped = (task->state == TASK_STOPPED);
send_sig(SIGSTOP, task, 1);
+ /* If it was stopped when we got here,
+ clear the pending SIGSTOP. */
+ if (stopped)
+ wake_up_process(task);
+
return 0;
bad:
--- /home/vic/p/linux-2.4.18.orig/arch/i386/kernel/signal.c Wed Mar 13 13:16:44 2002
+++ /home/vic/p/linux-2.4.18/arch/i386/kernel/signal.c Wed Mar 13 16:31:38 2002
@@ -620,9 +620,9 @@
continue;
current->exit_code = 0;
- /* The debugger continued. Ignore SIGSTOP. */
- if (signr == SIGSTOP)
- continue;
+ /* The debugger continued. */
+ if (signr == SIGSTOP && current->ptrace & PT_PTRACED)
+ continue; /* ignore SIGSTOP */
/* Update the siginfo structure. Is this good? */
if (signr != info.si_signo) {
Hi All,
I'm working on adding a function
void debugbreak(void);
to glibc. I got the inspiration for this from Windows. Windows has a
DebugBreak() function, which when invoked from a process, spawns a
debugger, which then attaches to the process. I think this would be
invaluable to linux developers in situations where they currently have to
put an infinite loop and attach to a running process (say, to stop in a
library that has been LD_PRELOADed).
To this end, I've been experimenting and found out that gdb can't attach
to a process that has been stopped. I'd like to send a SIGSTOP as soon as
debugbreak() is invoked, so that all threads are stopped in a state close
to the one they were in, when the debugbreak() was invoked.
I spoke to Vic Zandy about this and he informed me that he had submitted a patch
that would allow ptrace to attach to stopped processes also (the thread of
discussion is pasted below). I believe the patch was not accepted at that time.
I was wondering what the official line on this is? If there are no serious
objections, will the community consider accepting the patch? It would go a long
way in helping me accomplish my goal.
Thank you,
Raj
PS - I am not subscribed to the kernel mailing list. Would appreciate your
cc-ing your reply to me.
-------------------------------------------------------------------------------
Vic Zandy wrote --
This is to respond to feedback for the ptrace patch I sent toward the
end of december. The original message is below.
From: Alan Cox <[email protected]>
> > + if (signr == SIGSTOP && current->ptrace & PT_PTRACED)
> This does not I suspect do what you think - surely you want brackets ?
I agree the second term should be wrapped in parens (it is now in the
patch below); but isn't that logically equivalent to what I had?
From: Mike Coleman <[email protected]>:
> Also, is this something that used to work? Or would this be a change in the
> semantics of ptrace?
This is a change of semantics at least going back to 2.2.
>> Another bug is that it is not possible to use PTRACE_DETACH to leave a
>> process stopped, because ptrace ignores SIGSTOPs sent by the tracing
>> process.
>
> Unless I'm missing something (frequently the case), there are two cases here:
> (1) the tracer wants to leave the tracee stopped, and (2) the tracer wants the
> process to continue running in as natural a way as possible, meaning without
> sending it a SIGCONT (which can cause the SIGCONT signal handler to execute).
> As things currently stand, we have behavior (2), and (1) is not possible.
> With your change, we'd have behavior (1), and (2) would not be possible.
I agree that the ability to do (2) should be preserved, but I don't
see how this patch breaks it; do you have an example?
From: OGAWA Hirofumi <[email protected]>:
>> --- linux-2.4.16/kernel/ptrace.c Wed Nov 21 16:43:01 2001
>> +++ linux-2.4.16.1/kernel/ptrace.c Fri Dec 21 10:42:44 2001
>> @@ -89,8 +89,10 @@
>> SET_LINKS(task);
>> }
>> write_unlock_irq(&tasklist_lock);
>> -
>> - send_sig(SIGSTOP, task, 1);
>> + if (task->state != TASK_STOPPED)
>> + send_sig(SIGSTOP, task, 1);
>> + else
>> + task->exit_code = SIGSTOP;
>> return 0;
>>
>> bad:
>
> It seems that trace is started in the place different from
> usual. Then, I think PTRACE_KILL doesn't work.
I don't agree, it seems to work for me.
I'd still want to check uml and subterfuge, which I'll do after these
points are cleared up.
Thanks,
Vic
From: vic <[email protected]>
Subject: [PATCH] ptrace on stopped processes (2.4)
To: [email protected]
Cc: [email protected], [email protected],
[email protected]
Date: Fri, 21 Dec 2001 13:53:32 -0600
This patch fixes a couple problems with ptrace's interaction with
stopped processes on Linux 2.4.
The most significant bug is that gdb cannot attach to a stopped
process. Specifically, the wait that follows the PTRACE_ATTACH will
block indefinitely.
Another bug is that it is not possible to use PTRACE_DETACH to leave a
process stopped, because ptrace ignores SIGSTOPs sent by the tracing
process.
This patch is against 2.4.16 on x86. I have tested gdb and strace.
After this patch is reviewed, I would be happy to submit an analogous
patch for the other platforms, although I cannot test it.
Vic Zandy
--- linux-2.4.16/arch/i386/kernel/signal.c Fri Sep 14 16:15:40 2001
+++ linux-2.4.16.1/arch/i386/kernel/signal.c Wed Jan 16 22:19:16 2002
@@ -620,9 +620,9 @@
continue;
current->exit_code = 0;
- /* The debugger continued. Ignore SIGSTOP. */
- if (signr == SIGSTOP)
- continue;
+ /* The debugger continued. */
+ if (signr == SIGSTOP && (current->ptrace & PT_PTRACED))
+ continue; /* ignore SIGSTOP */
/* Update the siginfo structure. Is this good? */
if (signr != info.si_signo) {
--- linux-2.4.16/kernel/ptrace.c Wed Nov 21 16:43:01 2001
+++ linux-2.4.16.1/kernel/ptrace.c Fri Dec 21 10:42:44 2001
@@ -89,8 +89,10 @@
SET_LINKS(task);
}
write_unlock_irq(&tasklist_lock);
-
- send_sig(SIGSTOP, task, 1);
+ if (task->state != TASK_STOPPED)
+ send_sig(SIGSTOP, task, 1);
+ else
+ task->exit_code = SIGSTOP;
return 0;
bad:
-
On Mon, Mar 17, 2003 at 03:24:55PM -0600, Rajesh Rajamani wrote:
> Hi All,
> I'm working on adding a function
>
> void debugbreak(void);
>
> to glibc. I got the inspiration for this from Windows. Windows has a
> DebugBreak() function, which when invoked from a process, spawns a
> debugger, which then attaches to the process. I think this would be
> invaluable to linux developers in situations where they currently have to
> put an infinite loop and attach to a running process (say, to stop in a
> library that has been LD_PRELOADed).
>
> To this end, I've been experimenting and found out that gdb can't attach
> to a process that has been stopped. I'd like to send a SIGSTOP as soon as
> debugbreak() is invoked, so that all threads are stopped in a state close
> to the one they were in, when the debugbreak() was invoked.
>
> I spoke to Vic Zandy about this and he informed me that he had submitted a patch
> that would allow ptrace to attach to stopped processes also (the thread of
> discussion is pasted below). I believe the patch was not accepted at that time.
> I was wondering what the official line on this is? If there are no serious
> objections, will the community consider accepting the patch? It would go a long
> way in helping me accomplish my goal.
The question is, what _should_ happen when yu attach to a stopped
process? If the tracer receives the same one SIGSTOP that it normally
would, then it will just resume the program as if it weren't stopped.
Does that make sense or not?
As for Vic's patch, the SIGSTOP leaving processes stopped bit is
already fixed in 2.5, I believe. We need to decide what should happen
when attaching to a stopped process before that half can be considered.
(And the patch itself is wrong; send_sig is too broad a hammer for
this, it should probably be something more like force_sig_specific; but
I'm not sure that's the right approach. I'll think about it.)
--
Daniel Jacobowitz
MontaVista Software Debian GNU/Linux Developer
Daniel Jacobowitz wrote:
>On Mon, Mar 17, 2003 at 03:24:55PM -0600, Rajesh Rajamani wrote:
>
>
>>Hi All,
>>I'm working on adding a function
>>
>>void debugbreak(void);
>>
>>to glibc. I got the inspiration for this from Windows. Windows has a
>>DebugBreak() function, which when invoked from a process, spawns a
>>debugger, which then attaches to the process. I think this would be
>>invaluable to linux developers in situations where they currently have to
>>put an infinite loop and attach to a running process (say, to stop in a
>>library that has been LD_PRELOADed).
>>
>>To this end, I've been experimenting and found out that gdb can't attach
>>to a process that has been stopped. I'd like to send a SIGSTOP as soon as
>>debugbreak() is invoked, so that all threads are stopped in a state close
>>to the one they were in, when the debugbreak() was invoked.
>>
>>I spoke to Vic Zandy about this and he informed me that he had submitted a patch
>> that would allow ptrace to attach to stopped processes also (the thread of
>>discussion is pasted below). I believe the patch was not accepted at that time.
>> I was wondering what the official line on this is? If there are no serious
>>objections, will the community consider accepting the patch? It would go a long
>>way in helping me accomplish my goal.
>>
>>
>
>The question is, what _should_ happen when yu attach to a stopped
>process? If the tracer receives the same one SIGSTOP that it normally
>would, then it will just resume the program as if it weren't stopped.
>Does that make sense or not?
>
Ideally, a stopped process should not be resumed, until it receives a
SIGCONT. I ran two tests to understand the effect of this patch.
1. Attaching to a stopped process using gdb. This did not cause the
tracee to continue.
2. stracing a stopped process. This caused the stopped process to
continue. On further investigation, I found out that strace calls
ptrace(PTRACE_SYSCALL), which restarts the child, but arranges for the
child to be stopped at the next entry to or exit from a system call.
IMHO, the new ptrace semantics adds a feature to strace. If someone
tries to run strace a stopped process without the patch, it will just
hang, whereas with the patch, it'll let the process continue. Even
without the patch, if a process that sends itself a SIGCONT is straced,
the process will not stop. I do not think this is an issue, considering
that ppl will not get any info by running strace on a stopped process,
until they send a SIGCONT.
In conclusion, the action taken by the tracer depends on the mechanism
it uses for monitoring the system calls. That said, it'll definitely be
useful if the tracer could find out the state of the tracee at the time
of attachment and take appropriate action. Given below, is a port of
Vic's patch to 2.5.xx series -
--------------------------------------------------------------------------------
-- kernel/ptrace.c 2003-03-23 22:15:33.000000000 -0800
+++ kernel/ptrace.c 2003-03-23 22:19:27.000000000 -0800
@@ -115,7 +115,10 @@
__ptrace_link(task, current);
write_unlock_irq(&tasklist_lock);
- force_sig_specific(SIGSTOP, task);
+ if (task->state != TASK_STOPPED)
+ force_sig_specific(SIGSTOP, task);
+ else
+ task->exit_code = SIGSTOP;
return 0;
bad:
--------------------------------------------------------------------
Thanks,
Raj
On Sun, Mar 23, 2003 at 10:24:50PM -0800, raj wrote:
> Daniel Jacobowitz wrote:
>
> >On Mon, Mar 17, 2003 at 03:24:55PM -0600, Rajesh Rajamani wrote:
> >
> >
> >>Hi All,
> >>I'm working on adding a function
> >>
> >>void debugbreak(void);
> >>
> >>to glibc. I got the inspiration for this from Windows. Windows has a
> >>DebugBreak() function, which when invoked from a process, spawns a
> >>debugger, which then attaches to the process. I think this would be
> >>invaluable to linux developers in situations where they currently have to
> >>put an infinite loop and attach to a running process (say, to stop in a
> >>library that has been LD_PRELOADed).
> >>
> >>To this end, I've been experimenting and found out that gdb can't attach
> >>to a process that has been stopped. I'd like to send a SIGSTOP as soon as
> >>debugbreak() is invoked, so that all threads are stopped in a state close
> >>to the one they were in, when the debugbreak() was invoked.
> >>
> >>I spoke to Vic Zandy about this and he informed me that he had submitted
> >>a patch
> >>that would allow ptrace to attach to stopped processes also (the thread of
> >>discussion is pasted below). I believe the patch was not accepted at
> >>that time.
> >> I was wondering what the official line on this is? If there are no
> >> serious
> >>objections, will the community consider accepting the patch? It would go
> >>a long
> >>way in helping me accomplish my goal.
> >>
> >>
> >
> >The question is, what _should_ happen when yu attach to a stopped
> >process? If the tracer receives the same one SIGSTOP that it normally
> >would, then it will just resume the program as if it weren't stopped.
> >Does that make sense or not?
> >
> Ideally, a stopped process should not be resumed, until it receives a
> SIGCONT. I ran two tests to understand the effect of this patch.
>
> 1. Attaching to a stopped process using gdb. This did not cause the
> tracee to continue.
No, that's not what I meant. When you attach using GDB, there is no
way for GDB to determine if the process was previously stopped or
running.
> 2. stracing a stopped process. This caused the stopped process to
> continue. On further investigation, I found out that strace calls
> ptrace(PTRACE_SYSCALL), which restarts the child, but arranges for the
> child to be stopped at the next entry to or exit from a system call.
>
> IMHO, the new ptrace semantics adds a feature to strace. If someone
> tries to run strace a stopped process without the patch, it will just
> hang, whereas with the patch, it'll let the process continue. Even
> without the patch, if a process that sends itself a SIGCONT is straced,
> the process will not stop. I do not think this is an issue, considering
> that ppl will not get any info by running strace on a stopped process,
> until they send a SIGCONT.
>
> In conclusion, the action taken by the tracer depends on the mechanism
> it uses for monitoring the system calls. That said, it'll definitely be
> useful if the tracer could find out the state of the tracee at the time
> of attachment and take appropriate action. Given below, is a port of
> Vic's patch to 2.5.xx series -
> --------------------------------------------------------------------------------
> -- kernel/ptrace.c 2003-03-23 22:15:33.000000000 -0800
> +++ kernel/ptrace.c 2003-03-23 22:19:27.000000000 -0800
> @@ -115,7 +115,10 @@
> __ptrace_link(task, current);
> write_unlock_irq(&tasklist_lock);
>
> - force_sig_specific(SIGSTOP, task);
> + if (task->state != TASK_STOPPED)
> + force_sig_specific(SIGSTOP, task);
> + else
> + task->exit_code = SIGSTOP;
> return 0;
>
> bad:
I'll test this here, give me a day or two.
--
Daniel Jacobowitz
MontaVista Software Debian GNU/Linux Developer
Daniel Jacobowitz wrote:
> No, that's not what I meant. When you attach using GDB, there is no
> way for GDB to determine if the process was previously stopped or
> running.
Likewise, there's a race condition with any other concurrent use
of SIGSTOP.
Perhaps one could introduce a PTRACE_ATTACH2 that uses "addr" to
indicate the signal that should be used to sychronize attaching.
That way, programs that use STOP/CONT for their own purposes could
be attached to with ptrace(PTRACE_ATTACH2,pid,SIGTRAP,0), or such.
If the process is already stopped, the debugger would be notified
with WSTOPSIG set to that signal instead of SIGTRAP.
- Werner
--
_________________________________________________________________________
/ Werner Almesberger, Buenos Aires, Argentina [email protected] /
/_http://www.almesberger.net/____________________________________________/
On Tue, Mar 25, 2003 at 10:48:42AM -0300, Werner Almesberger wrote:
> Daniel Jacobowitz wrote:
> > No, that's not what I meant. When you attach using GDB, there is no
> > way for GDB to determine if the process was previously stopped or
> > running.
>
> Likewise, there's a race condition with any other concurrent use
> of SIGSTOP.
>
> Perhaps one could introduce a PTRACE_ATTACH2 that uses "addr" to
> indicate the signal that should be used to sychronize attaching.
> That way, programs that use STOP/CONT for their own purposes could
> be attached to with ptrace(PTRACE_ATTACH2,pid,SIGTRAP,0), or such.
>
> If the process is already stopped, the debugger would be notified
> with WSTOPSIG set to that signal instead of SIGTRAP.
Have you got an example that needs this? I'm not terribly concerned;
GDB's handling of SIGSTOP has always been pretty bad. Strace is a bit
better.
--
Daniel Jacobowitz
MontaVista Software Debian GNU/Linux Developer
Daniel Jacobowitz wrote:
> Have you got an example that needs this?
No, I was just suggesting an approach that may work, in case
somebody wants to fix this. SIGSTOP/CONT doesn't seem like a
very popular communication mechanism anyway.
I did notice that two processes trying to ptrace each other
end up being unkillably deadlocked on 2.4.18, which a fix
for the SIGSTOP problem may resolve, but I didn't check if
this still happens in more recent kernels. ("Don't do it" is
a satisfactory work-around.)
I'll worry about such subtleties once I've made heads and
tails of how UML's ptrace-relay interacts with process
termination :-)
- Werner
--
_________________________________________________________________________
/ Werner Almesberger, Buenos Aires, Argentina [email protected] /
/_http://www.almesberger.net/____________________________________________/