NetBSD-Bugs archive

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

kern/45330: ptrace: signals can alter syscall return values



>Number:         45330
>Category:       kern
>Synopsis:       ptrace: signals can alter syscall return values
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    kern-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Mon Sep 05 14:25:00 +0000 2011
>Originator:     Jared McNeill
>Release:        5.99.55
>Organization:
>Environment:
>Description:
A signal may alter the return value of a system call in traced applications.

The included application forks and traces itself, and the child sets up a 
SIGALRM handler, calls setitimer, then getpid() in a loop. The getpid() call 
returns 20 (SYS_getpid) instead of the child pid. I've seen this behaviour with 
mmap (where SYS_mmap is returned instead of the address) and clock_gettime 
(where SYS_clock_gettime is returned instead of 0).


$ ./alrm
go! pid = 5333
sendsig 17
pid = 5333
sendsig 14
sighandler: sig=14, code=-2
ack! getpid() returned 20 (expected 5333)
assertion "npid == pid" failed: file "alrm.c", line 108, function "main"
sendsig 6
PT_SYSCALL: No such process
waitpid: No child processes

The application behaves as expected on Linux.
>How-To-Repeat:
#include <sys/types.h>
#include <sys/time.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <assert.h>
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


static void
sighandler(int sig, siginfo_t *info, void *ctx)
{
        printf("%s: sig=%d, code=%d\n", __func__, sig, info->si_code);
}

pid_t
child_wait(pid_t child_pid, int *pstatus)
{
    pid_t pid = waitpid(child_pid, pstatus, 0);
    if (pid < 0) {
        perror("waitpid");
        exit(EXIT_FAILURE);
    }

    if (WIFEXITED(*pstatus)) {
        printf("child exited: status = 0x%x\n", *pstatus);
        exit(EXIT_SUCCESS);
    }

    return pid;
}

static int
traceit(pid_t pid)
{
        int insyscall = 0;
        int status;

        printf("go! pid = %d\n", pid);
        child_wait(pid, &status);
        for (;;) {
                int sig;

                errno = 0;
                sig = WSTOPSIG(status) == SIGTRAP ? 0 : WSTOPSIG(status);
                if (sig)
                        printf("sendsig %d\n", sig);
                ptrace(PT_SYSCALL, pid, (void *)1, sig);
                if (errno)
                        perror("PT_SYSCALL");

                child_wait(pid, &status);
                if (WSTOPSIG(status) == SIGTRAP) {
                        insyscall = !insyscall;
                }
        }
}

int
main(void)
{
        struct itimerval it;
        struct sigaction sa;
        sigset_t set;
        int error, status;
        pid_t pid;

        pid = fork();
        switch (pid) {
        case -1:
                perror("fork");
                return 1;
        case 0:
                errno = 0;
                ptrace(PT_TRACE_ME, 0, NULL, 0);
                if (errno)
                        perror("PT_TRACE_ME");
                raise(SIGSTOP);
                break;
        default:
                return traceit(pid);
        }

        pid = getpid();
        printf("pid = %d\n", pid);

        memset(&sa, 0, sizeof(sa));
        sa.sa_sigaction = sighandler;
        sigemptyset(&sa.sa_mask);
        sa.sa_flags = SA_SIGINFO | SA_RESTART;
        sigaction(SIGALRM, &sa, NULL);

        memset(&it, 0, sizeof(it));
        it.it_interval.tv_sec = 0;
        it.it_interval.tv_usec = 1;
        it.it_value = it.it_interval;
        error = setitimer(ITIMER_REAL, &it, NULL);
        assert(error == 0);

        for (;;) {
                pid_t npid = getpid();
                if (npid != pid) {
                        printf("ack! getpid() returned %d (expected %d)\n",
                            npid, pid);
                        assert(npid == pid);
                }
        }

        return 0;
}

>Fix:



Home | Main Index | Thread Index | Old Index