From owner-svn-src-head@FreeBSD.ORG Tue Oct 27 10:55:34 2009 Return-Path: Delivered-To: svn-src-head@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id BA41F106568D; Tue, 27 Oct 2009 10:55:34 +0000 (UTC) (envelope-from kib@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id A79D68FC0A; Tue, 27 Oct 2009 10:55:34 +0000 (UTC) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.3/8.14.3) with ESMTP id n9RAtYQk029115; Tue, 27 Oct 2009 10:55:34 GMT (envelope-from kib@svn.freebsd.org) Received: (from kib@localhost) by svn.freebsd.org (8.14.3/8.14.3/Submit) id n9RAtYM5029106; Tue, 27 Oct 2009 10:55:34 GMT (envelope-from kib@svn.freebsd.org) Message-Id: <200910271055.n9RAtYM5029106@svn.freebsd.org> From: Konstantin Belousov Date: Tue, 27 Oct 2009 10:55:34 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r198508 - in head: lib/libc/gen lib/libc/sys lib/libthr/thread sys/compat/freebsd32 sys/kern sys/sys X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 27 Oct 2009 10:55:34 -0000 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);