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