svn commit: r198508 - in head: lib/libc/gen lib/libc/sys lib/libthr/thread sys/compat/freebsd32 sys/kern sys/sys

Konstantin Belousov kib at FreeBSD.org
Tue Oct 27 10:55:34 UTC 2009


Author: kib
Date: Tue Oct 27 10:55:34 2009
New Revision: 198508
URL: http://svn.freebsd.org/changeset/base/198508

Log:
  Current pselect(3) is implemented in usermode and thus vulnerable to
  well-known race condition, which elimination was the reason for the
  function appearance in first place. If sigmask supplied as argument to
  pselect() enables a signal, the signal might be delivered before thread
  called select(2), causing lost wakeup. Reimplement pselect() in kernel,
  making change of sigmask and sleep atomic.
  
  Since signal shall be delivered to the usermode, but sigmask restored,
  set TDP_OLDMASK and save old mask in td_oldsigmask. The TDP_OLDMASK
  should be cleared by ast() in case signal was not gelivered during
  syscall execution.
  
  Reviewed by:	davidxu
  Tested by:	pho
  MFC after:	1 month

Deleted:
  head/lib/libc/gen/pselect.c
Modified:
  head/lib/libc/sys/Symbol.map
  head/lib/libthr/thread/thr_syscalls.c
  head/sys/compat/freebsd32/freebsd32_misc.c
  head/sys/compat/freebsd32/syscalls.master
  head/sys/kern/subr_trap.c
  head/sys/kern/sys_generic.c
  head/sys/kern/syscalls.master
  head/sys/sys/syscallsubr.h

Modified: head/lib/libc/sys/Symbol.map
==============================================================================
--- head/lib/libc/sys/Symbol.map	Tue Oct 27 10:47:58 2009	(r198507)
+++ head/lib/libc/sys/Symbol.map	Tue Oct 27 10:55:34 2009	(r198508)
@@ -211,6 +211,7 @@ FBSD_1.0 {
 	posix_openpt;
 	preadv;
 	profil;
+	pselect;
 	ptrace;
 	pwritev;
 	quotactl;
@@ -781,6 +782,8 @@ FBSDprivate_1.0 {
 	__sys_preadv;
 	_profil;
 	__sys_profil;
+	_pselect;
+	__sys_pselect;
 	_ptrace;
 	__sys_ptrace;
 	_pwritev;

Modified: head/lib/libthr/thread/thr_syscalls.c
==============================================================================
--- head/lib/libthr/thread/thr_syscalls.c	Tue Oct 27 10:47:58 2009	(r198507)
+++ head/lib/libthr/thread/thr_syscalls.c	Tue Oct 27 10:55:34 2009	(r198508)
@@ -104,6 +104,8 @@ extern int	__sys_accept(int, struct sock
 extern int	__sys_connect(int, const struct sockaddr *, socklen_t);
 extern int	__sys_fsync(int);
 extern int	__sys_msync(void *, size_t, int);
+extern int	__sys_pselect(int, fd_set *, fd_set *, fd_set *,
+			const struct timespec *, const sigset_t *);
 extern int	__sys_poll(struct pollfd *, unsigned, int);
 extern ssize_t	__sys_recv(int, void *, size_t, int);
 extern ssize_t	__sys_recvfrom(int, void *, size_t, int, struct sockaddr *, socklen_t *);
@@ -394,7 +396,7 @@ ___pselect(int count, fd_set *rfds, fd_s
 	int ret;
 
 	_thr_cancel_enter(curthread);
-	ret = __pselect(count, rfds, wfds, efds, timo, mask);
+	ret = __sys_pselect(count, rfds, wfds, efds, timo, mask);
 	_thr_cancel_leave(curthread);
 
 	return (ret);

Modified: head/sys/compat/freebsd32/freebsd32_misc.c
==============================================================================
--- head/sys/compat/freebsd32/freebsd32_misc.c	Tue Oct 27 10:47:58 2009	(r198507)
+++ head/sys/compat/freebsd32/freebsd32_misc.c	Tue Oct 27 10:55:34 2009	(r198508)
@@ -593,6 +593,41 @@ freebsd32_select(struct thread *td, stru
 	    sizeof(int32_t) * 8));
 }
 
+int
+freebsd32_pselect(struct thread *td, struct freebsd32_pselect_args *uap)
+{
+	struct timespec32 ts32;
+	struct timespec ts;
+	struct timeval tv, *tvp;
+	sigset_t set, *uset;
+	int error;
+
+	if (uap->ts != NULL) {
+		error = copyin(uap->ts, &ts32, sizeof(ts32));
+		if (error != 0)
+			return (error);
+		CP(ts32, ts, tv_sec);
+		CP(ts32, ts, tv_nsec);
+		TIMESPEC_TO_TIMEVAL(&tv, &ts);
+		tvp = &tv;
+	} else
+		tvp = NULL;
+	if (uap->sm != NULL) {
+		error = copyin(uap->sm, &set, sizeof(set));
+		if (error != 0)
+			return (error);
+		uset = &set;
+	} else
+		uset = NULL;
+	/*
+	 * XXX big-endian needs to convert the fd_sets too.
+	 * XXX Do pointers need PTRIN()?
+	 */
+	error = kern_pselect(td, uap->nd, uap->in, uap->ou, uap->ex, tvp,
+	    uset, sizeof(int32_t) * 8);
+	return (error);
+}
+
 /*
  * Copy 'count' items into the destination list pointed to by uap->eventlist.
  */

Modified: head/sys/compat/freebsd32/syscalls.master
==============================================================================
--- head/sys/compat/freebsd32/syscalls.master	Tue Oct 27 10:47:58 2009	(r198507)
+++ head/sys/compat/freebsd32/syscalls.master	Tue Oct 27 10:55:34 2009	(r198508)
@@ -909,3 +909,7 @@
 519	AUE_PDKILL	UNIMPL	pdkill
 520	AUE_PDGETPID	UNIMPL	pdgetpid
 521	AUE_PDWAIT	UNIMPL	pdwait
+522	AUE_SELECT	STD	{ int freebsd32_pselect(int nd, fd_set *in, \
+				    fd_set *ou, fd_set *ex, \
+				    const struct timespec32 *ts, \
+				    const sigset_t *sm); }

Modified: head/sys/kern/subr_trap.c
==============================================================================
--- head/sys/kern/subr_trap.c	Tue Oct 27 10:47:58 2009	(r198507)
+++ head/sys/kern/subr_trap.c	Tue Oct 27 10:55:34 2009	(r198508)
@@ -245,6 +245,11 @@ ast(struct trapframe *framep)
 		PROC_UNLOCK(p);
 	}
 
+	if (td->td_pflags & TDP_OLDMASK) {
+		td->td_pflags &= ~TDP_OLDMASK;
+		kern_sigprocmask(td, SIG_SETMASK, &td->td_oldsigmask, NULL, 0);
+	}
+
 	userret(td, framep);
 	mtx_assert(&Giant, MA_NOTOWNED);
 }

Modified: head/sys/kern/sys_generic.c
==============================================================================
--- head/sys/kern/sys_generic.c	Tue Oct 27 10:47:58 2009	(r198507)
+++ head/sys/kern/sys_generic.c	Tue Oct 27 10:55:34 2009	(r198508)
@@ -751,6 +751,58 @@ poll_no_poll(int events)
 	return (events & (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM));
 }
 
+int
+pselect(struct thread *td, struct pselect_args *uap)
+{
+	struct timespec ts;
+	struct timeval tv, *tvp;
+	sigset_t set, *uset;
+	int error;
+
+	if (uap->ts != NULL) {
+		error = copyin(uap->ts, &ts, sizeof(ts));
+		if (error != 0)
+		    return (error);
+		TIMESPEC_TO_TIMEVAL(&tv, &ts);
+		tvp = &tv;
+	} else
+		tvp = NULL;
+	if (uap->sm != NULL) {
+		error = copyin(uap->sm, &set, sizeof(set));
+		if (error != 0)
+			return (error);
+		uset = &set;
+	} else
+		uset = NULL;
+	return (kern_pselect(td, uap->nd, uap->in, uap->ou, uap->ex, tvp,
+	    uset, NFDBITS));
+}
+
+int
+kern_pselect(struct thread *td, int nd, fd_set *in, fd_set *ou, fd_set *ex,
+    struct timeval *tvp, sigset_t *uset, int abi_nfdbits)
+{
+	int error;
+
+	if (uset != NULL) {
+		error = kern_sigprocmask(td, SIG_SETMASK, uset,
+		    &td->td_oldsigmask, 0);
+		if (error != 0)
+			return (error);
+		td->td_pflags |= TDP_OLDMASK;
+		/*
+		 * Make sure that ast() is called on return to
+		 * usermode and TDP_OLDMASK is cleared, restoring old
+		 * sigmask.
+		 */
+		thread_lock(td);
+		td->td_flags |= TDF_ASTPENDING;
+		thread_unlock(td);
+	}
+	error = kern_select(td, nd, in, ou, ex, tvp, abi_nfdbits);
+	return (error);
+}
+
 #ifndef _SYS_SYSPROTO_H_
 struct select_args {
 	int	nd;
@@ -759,9 +811,7 @@ struct select_args {
 };
 #endif
 int
-select(td, uap)
-	register struct thread *td;
-	register struct select_args *uap;
+select(struct thread *td, struct select_args *uap)
 {
 	struct timeval tv, *tvp;
 	int error;

Modified: head/sys/kern/syscalls.master
==============================================================================
--- head/sys/kern/syscalls.master	Tue Oct 27 10:47:58 2009	(r198507)
+++ head/sys/kern/syscalls.master	Tue Oct 27 10:55:34 2009	(r198508)
@@ -919,5 +919,9 @@
 519	AUE_PDKILL	UNIMPL	pdkill
 520	AUE_PDGETPID	UNIMPL	pdgetpid
 521	AUE_PDWAIT	UNIMPL	pdwait
+522	AUE_SELECT	STD	{ int pselect(int nd, fd_set *in, \
+				    fd_set *ou, fd_set *ex, \
+				    const struct timespec *ts, \
+				    const sigset_t *sm); }
 ; Please copy any additions and changes to the following compatability tables:
 ; sys/compat/freebsd32/syscalls.master

Modified: head/sys/sys/syscallsubr.h
==============================================================================
--- head/sys/sys/syscallsubr.h	Tue Oct 27 10:47:58 2009	(r198507)
+++ head/sys/sys/syscallsubr.h	Tue Oct 27 10:55:34 2009	(r198508)
@@ -148,6 +148,8 @@ int	kern_pathconf(struct thread *td, cha
 	    int name, u_long flags);
 int	kern_pipe(struct thread *td, int fildes[2]);
 int	kern_preadv(struct thread *td, int fd, struct uio *auio, off_t offset);
+int	kern_pselect(struct thread *td, int nd, fd_set *in, fd_set *ou,
+	    fd_set *ex, struct timeval *tvp, sigset_t *uset, int abi_nfdbits);
 int	kern_ptrace(struct thread *td, int req, pid_t pid, void *addr,
 	    int data);
 int	kern_pwritev(struct thread *td, int fd, struct uio *auio, off_t offset);


More information about the svn-src-all mailing list