Subject: bin/29607: systrace doesn't handle interrupted syscalls properly
To: None <gnats-admin@netbsd.org, netbsd-bugs@netbsd.org>
From: Christian Biere <christianbiere@gmx.de>
List: netbsd-bugs
Date: 03/06/2005 02:52:01
>Number: 29607
>Category: bin
>Synopsis: systrace doesn't handle interrupted syscalls properly
>Confidential: no
>Severity: serious
>Priority: medium
>Responsible: bin-bug-people
>State: open
>Class: sw-bug
>Submitter-Id: net
>Arrival-Date: Sun Mar 06 02:52:01 +0000 2005
>Originator: Christian Biere
>Release: NetBSD 2.99.11
>Environment:
System: NetBSD cyclonus 2.99.11 NetBSD 2.99.11 (STARSCREAM) #4: Fri Jan 7 14:02:19 CET 2005 bin@cyclonus:/usr/obj/sys/arch/i386/compile/STARSCREAM i386
Architecture: i386
Machine: i386
>Description:
systrace lets syscalls fail with ERESTART when they're interrupted by
interrupts even when the signal handler was installed with the SA_RESTART
flag. Userland processes should never see ERESTART - EINTR maybe. However,
even EINTR would probably break a lot of applications as you usually don't
loop arbitrary syscalls against EINTR.
>How-To-Repeat:
The following program prints this:
socket(): Unknown error: 4294967293
You might have to adjust the usleep() delay. I've only tested this
on a single-CPU machine. SIGCHLD is just an example, if the child
process sends any other (catched) signal to its parent, the same
problem occurs.
$ cc -o erestart erestart.c
$ systrace -ad /path/to/policy_dir ./erestart
erestart.c:
#include <stdlib.h>
#include <errno.h>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
void
sigchld_handler(int sig)
{
int saved_errno = errno;
(void) sig;
while (waitpid(-1, NULL, WNOHANG) > 0)
;
errno = saved_errno;
}
int
main(void)
{
static struct sigaction action;
action.sa_handler = sigchld_handler;
sigemptyset(&action.sa_mask);
action.sa_flags = SA_RESTART;
sigaction(SIGCHLD, &action, NULL);
switch (fork()) {
case (pid_t) -1:
perror("fork()");
exit(EXIT_FAILURE);
break;
case 0:
usleep(100);
_exit(0);
break;
}
usleep(1);
if (socket(PF_INET, SOCK_STREAM, 0) < 0) {
perror("socket()");
exit(EXIT_FAILURE);
}
return 0;
}
Policy: /path/to/erestart, Emulation: netbsd
netbsd-__fstat13: permit
netbsd-__sigaction_sigtramp: permit
netbsd-__sysctl: permit
netbsd-break: permit
netbsd-compat_16___sigreturn14: permit
netbsd-close: permit
netbsd-exit: permit
netbsd-fork: permit
netbsd-issetugid: permit
netbsd-nanosleep: permit
netbsd-madvise: permit
netbsd-mmap: permit
netbsd-munmap: permit
netbsd-read: permit
netbsd-wait4: permit
netbsd-write: permit
#
# Internet
#
netbsd-socket: sockdom eq "AF_INET" and socktype eq "SOCK_STREAM" then permit
#
# Read-access
#
netbsd-fsread: filename sub "/etc/malloc.conf" then permit
netbsd-fsread: filename sub "/usr/share" then permit
>Fix: