linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* ptrace group stop signal number not reset before PTRACE_INTERRUPT is delivered?
@ 2016-08-18  3:52 Keno Fischer
  2016-08-18 14:37 ` Oleg Nesterov
  0 siblings, 1 reply; 7+ messages in thread
From: Keno Fischer @ 2016-08-18  3:52 UTC (permalink / raw)
  To: roland, oleg, linux-kernel

Consider below test case (not all of it is necessary for reproducing
the behavior in question, but I wanted to cover related cases as well
to make sure they behave as expected). In this test case, the last
group-stop (after PTRACE_INTERRUPT) is delivered with a
WSTOPSIG(status) of SIGTTIN, which was the signr of the previous group
stop. From reading the man-page, I would have expected SIGTRAP. Now, I
understand that if there is another stop pending, PTRACE_INTERRUPT
will simply piggy-backs off that one, but I don't believe that is
happening in this case. Further, the current behavior seems to make it
very hard (impossible?) to reliably tell a true group-stop from a
PTRACE_INTERRUPT generated one. Is this behavior intended? If so, I
would be happy to update the man page accordingly, but would like some
guidance on what the intended semantics are.

```
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <linux/ptrace.h>

int main() {
    pid_t child, ret;
    int err;
    int status;
    if (0 == (child = fork())) {
       kill(getpid(), SIGSTOP);
       kill(getpid(), SIGSTOP);
       kill(getpid(), SIGTTIN);
       sleep(1000);
       exit(0);
    }
    ret = waitpid(child, &status, WSTOPPED);
    assert(ret == child);
    err = ptrace(PTRACE_SEIZE, child, NULL, NULL);
    assert(err == 0);
    err = ptrace(PTRACE_CONT, child, NULL, NULL);
    assert(err == 0);
    // Should now hit SIGSTOP signal-stop
    ret = waitpid(child, &status, 0);
    assert(ret == child && WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP);
    err = ptrace(PTRACE_CONT, child, NULL, (void*)SIGSTOP);
    assert(err == 0);
    // Should now hit SIGSTOP group-stop
    ret = waitpid(child, &status, 0);
    assert(ret == child && (status>>16 == PTRACE_EVENT_STOP) &&
WSTOPSIG(status) == SIGSTOP);
    err = ptrace(PTRACE_CONT, child, NULL, NULL);
    assert(err == 0);
    // Should now hit SIGTTIN signal-stop
    ret = waitpid(child, &status, 0);
    assert(ret == child && WIFSTOPPED(status) && WSTOPSIG(status) == SIGTTIN);
    err = ptrace(PTRACE_CONT, child, NULL, (void*)SIGTTIN);
    assert(err == 0);
    // Should now hit SIGTTIN group-stop
    ret = waitpid(child, &status, 0);
    assert(ret == child && (status>>16 == PTRACE_EVENT_STOP) &&
WSTOPSIG(status) == SIGTTIN);
    err = ptrace(PTRACE_CONT, child, NULL, NULL);
    assert(err == 0);
    // Now interrupt it
    err = ptrace(PTRACE_INTERRUPT, child, NULL, NULL);
    assert(err == 0);
    // Should now hit interrupt group-stop
    ret = waitpid(child, &status, 0);
    printf("Interrupt group-stop delivered with signal %d\n", WSTOPSIG(status));
    assert(ret == child && (status>>16 == PTRACE_EVENT_STOP) &&
WSTOPSIG(status) == SIGTRAP);
    exit(0);
}
```

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: ptrace group stop signal number not reset before PTRACE_INTERRUPT is delivered?
  2016-08-18  3:52 ptrace group stop signal number not reset before PTRACE_INTERRUPT is delivered? Keno Fischer
@ 2016-08-18 14:37 ` Oleg Nesterov
  2016-08-18 15:38   ` Oleg Nesterov
  0 siblings, 1 reply; 7+ messages in thread
From: Oleg Nesterov @ 2016-08-18 14:37 UTC (permalink / raw)
  To: Keno Fischer; +Cc: roland, linux-kernel, Tejun Heo

On 08/17, Keno Fischer wrote:
>
> In this test case, the last
> group-stop (after PTRACE_INTERRUPT) is delivered with a
> WSTOPSIG(status) of SIGTTIN, which was the signr of the previous group
> stop. From reading the man-page, I would have expected SIGTRAP.

Me too ;)

> Now, I
> understand that if there is another stop pending, PTRACE_INTERRUPT
> will simply piggy-backs off that one, but I don't believe that is
> happening in this case.

Yes, thanks. This is wrong. We need to remove SIGTTIN from jobctl.
The problem, I am not sure when... I'll try to think.

Thanks!

> #include <assert.h>
> #include <stdlib.h>
> #include <stdio.h>
> #include <signal.h>
> #include <unistd.h>
> #include <sys/ptrace.h>
> #include <sys/wait.h>
> #include <linux/ptrace.h>
> 
> int main() {
>     pid_t child, ret;
>     int err;
>     int status;
>     if (0 == (child = fork())) {
>        kill(getpid(), SIGSTOP);
>        kill(getpid(), SIGSTOP);
>        kill(getpid(), SIGTTIN);
>        sleep(1000);
>        exit(0);
>     }
>     ret = waitpid(child, &status, WSTOPPED);
>     assert(ret == child);
>     err = ptrace(PTRACE_SEIZE, child, NULL, NULL);
>     assert(err == 0);
>     err = ptrace(PTRACE_CONT, child, NULL, NULL);
>     assert(err == 0);
>     // Should now hit SIGSTOP signal-stop
>     ret = waitpid(child, &status, 0);
>     assert(ret == child && WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP);
>     err = ptrace(PTRACE_CONT, child, NULL, (void*)SIGSTOP);
>     assert(err == 0);
>     // Should now hit SIGSTOP group-stop
>     ret = waitpid(child, &status, 0);
>     assert(ret == child && (status>>16 == PTRACE_EVENT_STOP) &&
> WSTOPSIG(status) == SIGSTOP);
>     err = ptrace(PTRACE_CONT, child, NULL, NULL);
>     assert(err == 0);
>     // Should now hit SIGTTIN signal-stop
>     ret = waitpid(child, &status, 0);
>     assert(ret == child && WIFSTOPPED(status) && WSTOPSIG(status) == SIGTTIN);
>     err = ptrace(PTRACE_CONT, child, NULL, (void*)SIGTTIN);
>     assert(err == 0);
>     // Should now hit SIGTTIN group-stop
>     ret = waitpid(child, &status, 0);
>     assert(ret == child && (status>>16 == PTRACE_EVENT_STOP) &&
> WSTOPSIG(status) == SIGTTIN);
>     err = ptrace(PTRACE_CONT, child, NULL, NULL);
>     assert(err == 0);
>     // Now interrupt it
>     err = ptrace(PTRACE_INTERRUPT, child, NULL, NULL);
>     assert(err == 0);
>     // Should now hit interrupt group-stop
>     ret = waitpid(child, &status, 0);
>     printf("Interrupt group-stop delivered with signal %d\n", WSTOPSIG(status));
>     assert(ret == child && (status>>16 == PTRACE_EVENT_STOP) &&
> WSTOPSIG(status) == SIGTRAP);
>     exit(0);
> }
> ```

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: ptrace group stop signal number not reset before PTRACE_INTERRUPT is delivered?
  2016-08-18 14:37 ` Oleg Nesterov
@ 2016-08-18 15:38   ` Oleg Nesterov
  2016-08-18 16:23     ` Oleg Nesterov
  0 siblings, 1 reply; 7+ messages in thread
From: Oleg Nesterov @ 2016-08-18 15:38 UTC (permalink / raw)
  To: Keno Fischer; +Cc: roland, linux-kernel, Tejun Heo

On 08/18, Oleg Nesterov wrote:
>
> On 08/17, Keno Fischer wrote:
> >
> > In this test case, the last
> > group-stop (after PTRACE_INTERRUPT) is delivered with a
> > WSTOPSIG(status) of SIGTTIN, which was the signr of the previous group
> > stop. From reading the man-page, I would have expected SIGTRAP.
>
> Me too ;)

Yes, but on the second though...

> > Now, I
> > understand that if there is another stop pending, PTRACE_INTERRUPT
> > will simply piggy-backs off that one, but I don't believe that is
> > happening in this case.
>
> Yes, thanks. This is wrong. We need to remove SIGTTIN from jobctl.
> The problem, I am not sure when... I'll try to think.

Probably not.

Let me try to clarify. It reports SIGTTIN because your test-case doesn't
send SIGCONT to the tracee before PTRACE_INTERRUPT. Because the child is
stopped-but-running.

IOW, after the tracer acks SIGSTOP

	err = ptrace(PTRACE_CONT, child, NULL, (void*)SIGSTOP);

the child actually starts the group-stop and becomes "STOPPED", so that
if you do ptrace_detach() after that it will stop again in TASK_STOPPED
state even if it is no longer traced.

Then SIGTTIN changes the signal number in "jobctl & JOBCTL_STOP_SIGMASK",
the tracee is still "STOPPED" but now it looks as if it was stopped by
SIGTTIN, not by SIGSTOP. Hmm, not sure this is really good, but we can't
use signal->group_exit_code.

Finally, after PTRACE_INTERRUPT the tracee "returns" to the "STOPPED"
state and reports SIGTTIN, and I agree this looks confusing... But I'm
not sure we should/can change this, this behaviour probably makes sense
too.

Damn. I'll try to think more, but I simply can't decide what do we
actually want in this case.

Oleg.

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: ptrace group stop signal number not reset before PTRACE_INTERRUPT is delivered?
  2016-08-18 15:38   ` Oleg Nesterov
@ 2016-08-18 16:23     ` Oleg Nesterov
  2016-08-18 17:37       ` Keno Fischer
  0 siblings, 1 reply; 7+ messages in thread
From: Oleg Nesterov @ 2016-08-18 16:23 UTC (permalink / raw)
  To: Keno Fischer; +Cc: roland, linux-kernel, Tejun Heo

Damn, forgot to mention...

On 08/18, Oleg Nesterov wrote:
>
> Damn. I'll try to think more, but I simply can't decide what do we
> actually want in this case.

Yes, but at least

	> Further, the current behavior seems to make it
	> very hard (impossible?) to reliably tell a true group-stop from a
	> PTRACE_INTERRUPT generated one.

is not true or I misunderstood... PTRACE_INTERRUPT doesn't lead to
group-stop, it stops the tracee individually.

And you if you get PTRACE_EVENT_STOP and WSTOPSIG() == SIGTTIN after
PTRACE_INTERRUPT, you know that the tracee did not report the "new"
SIGTTIN.

Or I missed your point?

Oleg.

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: ptrace group stop signal number not reset before PTRACE_INTERRUPT is delivered?
  2016-08-18 16:23     ` Oleg Nesterov
@ 2016-08-18 17:37       ` Keno Fischer
  2016-08-23 15:34         ` Oleg Nesterov
  0 siblings, 1 reply; 7+ messages in thread
From: Keno Fischer @ 2016-08-18 17:37 UTC (permalink / raw)
  To: Oleg Nesterov; +Cc: Roland McGrath, linux-kernel, Tejun Heo

On Thu, Aug 18, 2016 at 12:23 PM, Oleg Nesterov <oleg@redhat.com> wrote:
>
> And you if you get PTRACE_EVENT_STOP and WSTOPSIG() == SIGTTIN after
> PTRACE_INTERRUPT, you know that the tracee did not report the "new"
> SIGTTIN.

It seems possible to remember whether or not we injected a stopping
signal and if so the next PTRACE_EVENT_STOP is a group-stop, otherwise
a PTRACE_INTERRUPT stop. Currently what I do is the other way around,
after issuing PTRACE_INTERRUPT, the first (if any) of the next two
stops that is a PTRACE_EVENT_STOP get interpreted as a
PTRACE_INTERRUPT stop. I haven't thought through this fully yet, so I
can't give you a concrete example I worried about, it just seems
fragile compared to just checking whether WSTOPSIG() == SIGTRAP.

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: ptrace group stop signal number not reset before PTRACE_INTERRUPT is delivered?
  2016-08-18 17:37       ` Keno Fischer
@ 2016-08-23 15:34         ` Oleg Nesterov
  2016-09-13 21:57           ` Keno Fischer
  0 siblings, 1 reply; 7+ messages in thread
From: Oleg Nesterov @ 2016-08-23 15:34 UTC (permalink / raw)
  To: Keno Fischer; +Cc: Roland McGrath, linux-kernel, Tejun Heo

On 08/18, Keno Fischer wrote:
>
> On Thu, Aug 18, 2016 at 12:23 PM, Oleg Nesterov <oleg@redhat.com> wrote:
> >
> > And you if you get PTRACE_EVENT_STOP and WSTOPSIG() == SIGTTIN after
> > PTRACE_INTERRUPT, you know that the tracee did not report the "new"
> > SIGTTIN.
>
> It seems possible to remember whether or not we injected a stopping
> signal and if so the next PTRACE_EVENT_STOP is a group-stop, otherwise
> a PTRACE_INTERRUPT stop. Currently what I do is the other way around,
> after issuing PTRACE_INTERRUPT, the first (if any) of the next two
> stops that is a PTRACE_EVENT_STOP get interpreted as a
> PTRACE_INTERRUPT stop. I haven't thought through this fully yet, so I
> can't give you a concrete example I worried about, it just seems
> fragile compared to just checking whether WSTOPSIG() == SIGTRAP.

Yes, I see your point. And to remind, I was confused too.

Perhaps we can add another THIS_SIGNAL_WAS_ALREADY_REPORTED bit, but
you know, I'd prefer to avoid another subtle change in behaviour. You
can never know if it is "safe" or not when it comes to ptrace, perhaps
some application already relies on this WSTOPSIG().

Oleg.

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: ptrace group stop signal number not reset before PTRACE_INTERRUPT is delivered?
  2016-08-23 15:34         ` Oleg Nesterov
@ 2016-09-13 21:57           ` Keno Fischer
  0 siblings, 0 replies; 7+ messages in thread
From: Keno Fischer @ 2016-09-13 21:57 UTC (permalink / raw)
  To: Oleg Nesterov; +Cc: Roland McGrath, linux-kernel, Tejun Heo

Hi Oleg,

I have another obscure ptrace question which seems somewhat related,
so let me ask it here.
Consider this:

```
static int sigchld_counter = 0;
void sigchld_handler(int sig) {
  (void)sig;
  sigchld_counter++;
}

int main(void) {
  signal(SIGCHLD, sigchld_handler);

  pid_t child;
  if (0 == (child = fork())) {
    raise(SIGSTOP);
    assert(0); // Should never be continued
  }

  // Wait until stopped
  int status;
  pid_t wret = waitpid(child, &status, __WALL | WSTOPPED);
  assert(wret == child);
  assert(WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP);
  assert(sigchld_counter == 1);

  // Now PTRACE_SEIZE the child
  long err = ptrace(PTRACE_SEIZE, child, NULL, (void*)PTRACE_O_TRACESYSGOOD);
  assert(err == 0);

  // Make sure that didn't cause a notification
  wret = waitpid(child, &status, __WALL | WSTOPPED | WNOHANG);
  assert(wret == 0);
  assert(sigchld_counter == 1);
}
```

I wouldn't have expected the PTRACE_SEIZE to generate another
SIGCHLD/be waitable again,
(the last two assertions fail).
Is that supposed to happen? If so, I'd like to update the man page to
mention this behavior, but
I wanted to check with you first.

Thanks,
Keno


On Tue, Aug 23, 2016 at 11:34 AM, Oleg Nesterov <oleg@redhat.com> wrote:
> On 08/18, Keno Fischer wrote:
>>
>> On Thu, Aug 18, 2016 at 12:23 PM, Oleg Nesterov <oleg@redhat.com> wrote:
>> >
>> > And you if you get PTRACE_EVENT_STOP and WSTOPSIG() == SIGTTIN after
>> > PTRACE_INTERRUPT, you know that the tracee did not report the "new"
>> > SIGTTIN.
>>
>> It seems possible to remember whether or not we injected a stopping
>> signal and if so the next PTRACE_EVENT_STOP is a group-stop, otherwise
>> a PTRACE_INTERRUPT stop. Currently what I do is the other way around,
>> after issuing PTRACE_INTERRUPT, the first (if any) of the next two
>> stops that is a PTRACE_EVENT_STOP get interpreted as a
>> PTRACE_INTERRUPT stop. I haven't thought through this fully yet, so I
>> can't give you a concrete example I worried about, it just seems
>> fragile compared to just checking whether WSTOPSIG() == SIGTRAP.
>
> Yes, I see your point. And to remind, I was confused too.
>
> Perhaps we can add another THIS_SIGNAL_WAS_ALREADY_REPORTED bit, but
> you know, I'd prefer to avoid another subtle change in behaviour. You
> can never know if it is "safe" or not when it comes to ptrace, perhaps
> some application already relies on this WSTOPSIG().
>
> Oleg.
>

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2016-09-13 21:58 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-08-18  3:52 ptrace group stop signal number not reset before PTRACE_INTERRUPT is delivered? Keno Fischer
2016-08-18 14:37 ` Oleg Nesterov
2016-08-18 15:38   ` Oleg Nesterov
2016-08-18 16:23     ` Oleg Nesterov
2016-08-18 17:37       ` Keno Fischer
2016-08-23 15:34         ` Oleg Nesterov
2016-09-13 21:57           ` Keno Fischer

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).