Module Name:    src
Committed By:   dsl
Date:           Sat Dec 12 17:47:05 UTC 2009

Modified Files:
        src/sys/kern: sys_select.c

Log Message:
Bounding the 'nfds' arg to poll() at the current process limit for actual
open files is rather gross - the poll map isn't required to be dense.
Instead limit to a much larger value (1000 + dt_nfiles) so that user
programs cannot allocate indefinite sized blocks of kvm.
If the limit is exceeded, then return EINVAL instead of silently truncating
the list.
(The silent truncation in select isn't quite as bad - although even there
any high bits that are set ought to generate an EBADF response.)
Move the code that converts ERESTART and EWOULDBLOCK into common code.
Effectively fixes PR/17507 since the new limit is unlikely to be detected.


To generate a diff of this commit:
cvs rdiff -u -r1.19 -r1.20 src/sys/kern/sys_select.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/kern/sys_select.c
diff -u src/sys/kern/sys_select.c:1.19 src/sys/kern/sys_select.c:1.20
--- src/sys/kern/sys_select.c:1.19	Wed Nov 11 09:48:51 2009
+++ src/sys/kern/sys_select.c	Sat Dec 12 17:47:05 2009
@@ -1,4 +1,4 @@
-/*	$NetBSD: sys_select.c,v 1.19 2009/11/11 09:48:51 rmind Exp $	*/
+/*	$NetBSD: sys_select.c,v 1.20 2009/12/12 17:47:05 dsl Exp $	*/
 
 /*-
  * Copyright (c) 2007, 2008, 2009 The NetBSD Foundation, Inc.
@@ -70,7 +70,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: sys_select.c,v 1.19 2009/11/11 09:48:51 rmind Exp $");
+__KERNEL_RCSID(0, "$NetBSD: sys_select.c,v 1.20 2009/12/12 17:47:05 dsl Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -261,6 +261,12 @@
 		l->l_sigmask = oldmask;
 		mutex_exit(p->p_lock);
 	}
+
+	/* select and poll are not restarted after signals... */
+	if (error == ERESTART)
+		return EINTR;
+	if (error == EWOULDBLOCK)
+		return 0;
 	return error;
 }
 
@@ -293,7 +299,7 @@
 	if (u_ ## name) {						\
 		error = copyin(u_ ## name, bits + ni * x, ni);		\
 		if (error)						\
-			goto done;					\
+			goto fail;					\
 	} else								\
 		memset(bits + ni * x, 0, ni);
 	getbits(in, 0);
@@ -302,18 +308,13 @@
 #undef	getbits
 
 	error = sel_do_scan(bits, nd, ts, mask, retval, 1);
- done:
-	/* select is not restarted after signals... */
-	if (error == ERESTART)
-		error = EINTR;
-	if (error == EWOULDBLOCK)
-		error = 0;
 	if (error == 0 && u_in != NULL)
 		error = copyout(bits + ni * 3, u_in, ni);
 	if (error == 0 && u_ou != NULL)
 		error = copyout(bits + ni * 4, u_ou, ni);
 	if (error == 0 && u_ex != NULL)
 		error = copyout(bits + ni * 5, u_ex, ni);
+ fail:
 	if (bits != smallbits)
 		kmem_free(bits, ni * 6);
 	return (error);
@@ -418,12 +419,19 @@
 	struct pollfd	smallfds[32];
 	struct pollfd	*fds;
 	int		error;
-	size_t		ni, nf;
+	size_t		ni;
 
-	nf = curlwp->l_fd->fd_dt->dt_nfiles;
-	if (nfds > nf) {
-		/* forgiving; slightly wrong */
-		nfds = nf;
+	if (nfds > 1000 + curlwp->l_fd->fd_dt->dt_nfiles) {
+		/*
+		 * Either the user passed in a very sparse 'fds' or junk!
+		 * The kmem_alloc() call below would be bad news.
+		 * We could process the 'fds' array in chunks, but that
+		 * is a lot of code that isn't normally useful.
+		 * (Or just move the copyin/out into pollscan().)
+		 * Historically the code silently truncated 'fds' to
+		 * dt_nfiles entries - but that does cause issues.
+		 */
+		return EINVAL;
 	}
 	ni = nfds * sizeof(struct pollfd);
 	if (ni > sizeof(smallfds)) {
@@ -435,17 +443,12 @@
 
 	error = copyin(u_fds, fds, ni);
 	if (error)
-		goto done;
+		goto fail;
 
 	error = sel_do_scan(fds, nfds, ts, mask, retval, 0);
- done:
-	/* poll is not restarted after signals... */
-	if (error == ERESTART)
-		error = EINTR;
-	if (error == EWOULDBLOCK)
-		error = 0;
 	if (error == 0)
 		error = copyout(fds, u_fds, ni);
+ fail:
 	if (fds != smallfds)
 		kmem_free(fds, ni);
 	return (error);

Reply via email to