lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:	Thu, 11 Feb 2010 08:32:14 -0800
From:	Salman Qazi <sqazi@...gle.com>
To:	Oleg Nesterov <oleg@...hat.com>
Cc:	taviso@...gle.com, Roland Dreier <rolandd@...co.com>,
	Andrew Morton <akpm@...ux-foundation.org>,
	Roland McGrath <roland@...hat.com>,
	linux-kernel@...r.kernel.org
Subject: Re: Race in ptrace.

On Thu, Feb 11, 2010 at 4:56 AM, Oleg Nesterov <oleg@...hat.com> wrote:
> On 02/10, Salman Qazi wrote:
>>
>> I have
>> made a simpler version of tavis's test case:
>
> Thanks, now I see what you mean.
>
> But this all is correct, you can't expect PTRACE_SYSCALL can succeed
> is the tracee is running, it must be stopped or traced.
>
> The tracee is running because it was TASK_STOPPED and antagonist()
> sends SIGCONT.
>
> The tracee was TASK_STOPPED because the tracer passes sig = SIGSTOP
> via ptrace(PTRACE_SYSCALL, WSTOPSIG(status).
>
> Where do you see the bug?

Shouldn't ptrace(PTRACE_SYSCALL, WSTOPSIG(status)...), cause any
future signals to the child be intercepted by the parent?

>
> OK, let me simplify the test-case even more:
>
>        int main(void)
>        {
>                int stat, ret;
>                int pid = fork();
>
>                if (!pid) {
>                        ptrace(PTRACE_TRACEME, 0, NULL, NULL);
>                        for (;;)
>                                ;
>                }
>
>                sleep(1);       // wait for PTRACE_TRACEME
>                kill(pid, SIGSTOP);
>
>                // the child reports SIGSTOP, it is TASK_TRACED
>                assert(pid == wait(&stat) && WIFSTOPPED(stat));
>
>                // the tracee should stop, we pass sig = SIGSTOP
>                assert(ptrace(PTRACE_SYSCALL, pid, 0, WSTOPSIG(stat)) == 0);
>
>                // the child reports the group stop, it is TASK_STOPPED
>                assert(pid == wait(&stat) && WIFSTOPPED(stat));
>
>                // the tracee is STOPPED as requested, not TRACED,
>                // SIGCONT wakes it up
>                kill(pid, SIGCONT);

                   According to the man page, any signals to the
process are supposed to be intercepted by the parent and that is how
one is supposed to be able to control which signals make it to the
child.  I am not sure if it makes any difference if the signal
originates at the parent.  But in our test case, it doesn't.   So, why
doesn't the parent get a notification first?

>
>                // now the tracee is _running_, and PTRACE_SYSCALL must fail
>                ret = ptrace(PTRACE_SYSCALL, pid, 0, WSTOPSIG(stat));
>                printf("should fail: ret=%d %m\n", ret);
>
>                return 0;
>        }
>
> PTRACE_SYSCALL fails, and this is absolutely correct.
>
> Now, let's look at your test-case
>
>> int main(int argc, char **argv)
>> {
>>         int status;
>>         assert((child_pid = do_fork(child)) > 0);
>>         assert((ant_pid = do_fork(antagonist)) > 0);
>>         waitpid(child_pid, &status, 0);
>>         ptrace(PTRACE_SYSCALL, child_pid, NULL, NULL);
>>         while(1) {
>>                 if (waitpid(child_pid, &status, 0) <= 0) {
>>                         printf("Errno %d\n", errno);
>>                         exit(EXIT_FAILURE);
>>                 }
>>                 if (WIFSTOPPED(status)) {
>
> WSTOPSIG() should be either SIGCONT or SIGSTOP
>
>>                         printf("stopped: %d\n", WSTOPSIG(status));
>>
>>                         /* This should work, but sometimes it doesn't */
>>                         if (ptrace(PTRACE_SYSCALL, child_pid,
>>                                                 NULL, WSTOPSIG(status)) < 0) {
>
> This should not work if the tracee reported the group stop (not the
> fact it dequeued SIGSTOP) and antagonist() sends SIGCONT in between.
>
>>                                 /* Oddly it works the second time! */
>>                                 assert (ptrace(PTRACE_SYSCALL,
>>                                         child_pid, NULL, WSTOPSIG(status)) < 0);
>
> Of couse, it _can_ work the second time, antagonist() sends a signal
> (SIGCONT or SIGSTOP), the tracee dequeues the signal, and stops to
> report this signal.
>
> See?
>
> Oleg.
>
>
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ