summaryrefslogtreecommitdiffstats
path: root/sys/kern/sys_generic.c
diff options
context:
space:
mode:
authorkib <kib@FreeBSD.org>2009-10-27 10:55:34 +0000
committerkib <kib@FreeBSD.org>2009-10-27 10:55:34 +0000
commit08e50139380f182b37e3e8f5bc7c1319119a4abd (patch)
tree707b4bb2816586b23064a94f87f2fbfdd1dc7380 /sys/kern/sys_generic.c
parentce081b037e7a762f0dd090a207cafc5121f39f51 (diff)
downloadFreeBSD-src-08e50139380f182b37e3e8f5bc7c1319119a4abd.zip
FreeBSD-src-08e50139380f182b37e3e8f5bc7c1319119a4abd.tar.gz
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
Diffstat (limited to 'sys/kern/sys_generic.c')
-rw-r--r--sys/kern/sys_generic.c56
1 files changed, 53 insertions, 3 deletions
diff --git a/sys/kern/sys_generic.c b/sys/kern/sys_generic.c
index 6831fe8..b34af61 100644
--- a/sys/kern/sys_generic.c
+++ b/sys/kern/sys_generic.c
@@ -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;
OpenPOWER on IntegriCloud