NetBSD-Bugs archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

kern/51596: ptrace(2): raising SIGCONT in a child results with WIFCONTINUED and WIFSTOPPED true in the parent



>Number:         51596
>Category:       kern
>Synopsis:       ptrace(2): raising SIGCONT in a child results with WIFCONTINUED and WIFSTOPPED true in the parent
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    kern-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Thu Nov 03 17:55:00 +0000 2016
>Originator:     Kamil Rytarowski
>Release:        NetBSD 7.99.39 amd64
>Organization:
TNF
>Environment:
NetBSD chieftec 7.99.39 NetBSD 7.99.39 (GENERIC) #0: Tue Oct  4 15:59:50 CEST 2016  kamil@chieftec:/tmp/netbsd-tmp/sys/arch/amd64/compile/GENERIC amd64
>Description:
In the ptrace(2) context raising SIGCONT in a child results status with WIFCONTINUED and WIFSTOPPED true returned from waitpid(2) in the parent.

Linux and FreeBSD return there status with WIFSTOPPED true and WIFCONTINUED false.

The ptrace(2) interface is not a part of POSIX and it is implementation specific behavior, however WIFCONTINUED and WIFSTOPPED both set to true is suspicious.

This use-case is tested as ptraceme4 in the ATF test called t_ptrace.

http://mail-index.netbsd.org/source-changes/2016/11/03/msg078847.html

Discussion:

http://mail-index.netbsd.org/source-changes-d/2016/11/03/msg008823.html

As proposed by Robert Elz, I'm assuming the Linux behavior as correct.
>How-To-Repeat:
This code triggers the issue:

#include <sys/param.h>
#include <sys/types.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <assert.h>
#include <err.h>
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

#define ATF_REQUIRE(a) assert(a)

#if defined(linux)
#define WALLSIG __WALL
#define sys_signame sys_siglist
#endif

int main(int argc, char **argv)
{
        int status;
        const int exitval = 5;
        const int sigval = SIGSTOP, sigsent = SIGCONT;
        pid_t child, wpid;

        printf("1: Before forking process PID=%d\n", getpid());
        ATF_REQUIRE((child = fork()) != -1);
        if (child == 0) {
                /* printf(3) messages from a child aren't intercepted by ATF */
                /* "2: Child process PID=%d\n", getpid() */

                /* "2: Before calling ptrace(PT_TRACE_ME, ...)\n" */
                if (ptrace(PT_TRACE_ME, 0, NULL, 0) == -1) {
                        /* XXX: Is it safe to use ATF functions in a child? */
                        err(EXIT_FAILURE, "2: ptrace(2) call failed with "
                            "status %s", sys_errlist[errno]);
                }

                /* "2: Before raising SIGSTOP\n" */
                raise(sigval);

                /* "2: Before raising SIGCONT\n" */
                raise(sigsent);

                /* "2: Before calling _exit(%d)\n", exitval */
                _exit(exitval);
        } else {
                printf("1: Parent process PID=%d, child's PID=%d\n", getpid(),
                    child);

                printf("1: Before calling waitpid() for the child\n");
                wpid = waitpid(child, &status, 0);

                printf("1: Validating child's PID (expected %d, got %d)\n",
                    child, wpid);
                ATF_REQUIRE(child == wpid);

                printf("1: Ensuring that the child has not been exited\n");
                ATF_REQUIRE(!WIFEXITED(status));

                printf("1: Ensuring that the child has not been continued\n");
                ATF_REQUIRE(!WIFCONTINUED(status));

                printf("1: Ensuring that the child has not been terminated "
                    "with a signal\n");
                ATF_REQUIRE(!WIFSIGNALED(status));

                printf("1: Ensuring that the child has been stopped\n");
                ATF_REQUIRE(WIFSTOPPED(status));
                printf("1: Verifying that he child has been stopped with the"
                    " %s signal (received %s)\n", sys_signame[sigval],
                    sys_signame[WSTOPSIG(status)]);
                ATF_REQUIRE(WSTOPSIG(status) == sigval);

                printf("1: Before resuming the child process where it left "
                    "off and without signal to be sent\n");
                ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0)
                    != -1);

                printf("1: Before calling waitpid() for the child\n");
                wpid = waitpid(child, &status, WALLSIG);

                printf("1: Validating that child's PID is still there\n");
                ATF_REQUIRE(wpid == child);

                printf("1: Ensuring that the child has not been exited\n");
                ATF_REQUIRE(!WIFEXITED(status));

                printf("1: Ensuring that the child has been continued\n");
                ATF_REQUIRE(WIFCONTINUED(status));

                printf("1: Ensuring that the child has not been terminated "
                    "with a signal\n");
                ATF_REQUIRE(!WIFSIGNALED(status));

                printf("1: Ensuring that the child has not been stopped\n");
                ATF_REQUIRE(WIFSTOPPED(status));

                printf("1: Before resuming the child process where it left "
                    "off and without signal to be sent\n");
                ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
                printf("1: Before calling waitpid() for the child\n");
                wpid = waitpid(child, &status, 0);

                printf("1: Validating that child's PID is still there\n");
                ATF_REQUIRE(wpid == child);

                printf("1: Ensuring that the child has been exited\n");
                ATF_REQUIRE(WIFEXITED(status));

                printf("1: Ensuring that the child has not been continued\n");
                ATF_REQUIRE(!WIFCONTINUED(status));

                printf("1: Ensuring that the child has not been terminated "
                    "with a signal\n");
                ATF_REQUIRE(!WIFSIGNALED(status));

                printf("1: Ensuring that the child has not been stopped\n");
                ATF_REQUIRE(!WIFSTOPPED(status));

                printf("1: Verifying that he child has exited with the "
                    "%d status (received %d)\n", exitval, WEXITSTATUS(status));
                ATF_REQUIRE(WEXITSTATUS(status) == exitval);

                printf("1: Before calling waitpid() for the exited child\n");
                wpid = waitpid(child, &status, 0);

                printf("1: Validating that child's PID no longer exists\n");
                ATF_REQUIRE(wpid == -1);

                printf("1: Validating that errno is set to %s (got %s)\n",
                    sys_errlist[ECHILD], sys_errlist[errno]);
                ATF_REQUIRE(errno == ECHILD);
	}
}
>Fix:
N/A



Home | Main Index | Thread Index | Old Index