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: