From nobody@FreeBSD.org  Wed Jan 20 19:16:06 2010
Return-Path: <nobody@FreeBSD.org>
Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34])
	by hub.freebsd.org (Postfix) with ESMTP id 42AE1106568B
	for <freebsd-gnats-submit@FreeBSD.org>; Wed, 20 Jan 2010 19:16:06 +0000 (UTC)
	(envelope-from nobody@FreeBSD.org)
Received: from www.freebsd.org (www.freebsd.org [IPv6:2001:4f8:fff6::21])
	by mx1.freebsd.org (Postfix) with ESMTP id 18D358FC18
	for <freebsd-gnats-submit@FreeBSD.org>; Wed, 20 Jan 2010 19:16:06 +0000 (UTC)
Received: from www.freebsd.org (localhost [127.0.0.1])
	by www.freebsd.org (8.14.3/8.14.3) with ESMTP id o0KJG5WK086538
	for <freebsd-gnats-submit@FreeBSD.org>; Wed, 20 Jan 2010 19:16:05 GMT
	(envelope-from nobody@www.freebsd.org)
Received: (from nobody@localhost)
	by www.freebsd.org (8.14.3/8.14.3/Submit) id o0KJG55D086537;
	Wed, 20 Jan 2010 19:16:05 GMT
	(envelope-from nobody)
Message-Id: <201001201916.o0KJG55D086537@www.freebsd.org>
Date: Wed, 20 Jan 2010 19:16:05 GMT
From: John Plevyak <jplevyak@apache.org>
To: freebsd-gnats-submit@FreeBSD.org
Subject: poll() can return too large a number
X-Send-Pr-Version: www-3.1
X-GNATS-Notify:

>Number:         143029
>Category:       kern
>Synopsis:       [libc] poll(2) can return too large a number
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    kib
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Wed Jan 20 19:20:08 UTC 2010
>Closed-Date:    Fri Sep 10 13:12:52 UTC 2010
>Last-Modified:  Fri Sep 10 13:12:52 UTC 2010
>Originator:     John Plevyak
>Release:        8.0-RELEASE
>Organization:
Apache.org
>Environment:
FreeBSD jplevyak2.hsd1.ca.comcast.net. 8.0-RELEASE FreeBSD 8.0-RELEASE #0: Sat Nov 21 15:02:08 UTC 2009     root@mason.cse.buffalo.edu:/usr/obj/usr/src/sys/GENERIC  amd64

>Description:
I am calling poll() and the result is 2 while only one
of the revent fields is non-zero.

This is inside of libev where it results in a SEGV.
I have talked to the libev maintainers who suggested
that I talk to you.

This is on FreeBSD 8.

Inspection of the code in sys_generic.c
and uipc_socket.c indicates that selrecord is being called
twice for poll sockets and that pollrescan is counting
the number of td->td_sel entries which are ready and
returning the result. I am concerned that there are 2
entries for the poll socket because of the comments
in selsetbits (sys_generic.c) which are:

* XXX Check for a duplicate set. This can occur because a
* socket calls selrecord() twice for each poll() call
* resulting in two selfds per real fd. selrescan() will
* call selsetbits twice as a result.


This check is not being done for poll().

>How-To-Repeat:
Simple tests do not show the problem since the pollscan() does not have
the problem so if it returns a result the problem is not evident.

I can reproduce in the context of the apache.org trafficserver
linked with libev.  I can provide build and setup instructions or
a login to a VM with the required setup.

>Fix:
selsetbits uses the conditional:

                /*
                 * XXX Check for a duplicate set.  This can occur because a
                 * socket calls selrecord() twice for each poll() call
                 * resulting in two selfds per real fd.  selrescan() will
                 * call selsetbits twice as a result.
                 */
                if ((obits[msk][idx] & bit) != 0)
                        continue;

however looking at the code I think perhaps changing pollrescan to:

                /*
                 * Note: backend also returns POLLHUP and
                 * POLLERR if appropriate.
                 */
                int old_revents = fd->revents;
                fd->revents = fo_poll(fp, fd->events, td->td_ucred, td);
                if (!old_revents && fd->revents != 0)
                        n++;

or something similar might fix the problem.

>Release-Note:
>Audit-Trail:

From: Kostik Belousov <kostikbel@gmail.com>
To: bug-followup@FreeBSD.org, jplevyak@apache.org
Cc:  
Subject: Re: kern/143029: [libc] poll(2) can return too large a number
Date: Thu, 26 Aug 2010 23:16:33 +0300

 I believe the issue happens because for some file descriptors, select
 code registers two selfd structures. E.g., for socket, when specified
 POLLIN|POLLOUT in events, you would have one selfd registered for
 receiving socket buffer, and one for sending. Now, if both events
 are not ready to fire at the time of the initial scan, but are
 simultaneously ready after the sleep, pollrescan() would iterate
 over the pollfd struct twice. It is not harmful by itself, but
 pollfd gets accounted twice.
 
 The crusial part is the simultaneous firing of read and write ready
 events after the sleep, due to
 		/* If the selinfo wasn't cleared the event didn't fire. */
 		if (si != NULL)
 			continue;
 code in the pollrescan() loop. In the simple case when only one event
 fired, no double-accounting happen.
 
 Patch below should fix it. Could you, please, confirm ?
 
 diff --git a/sys/kern/sys_generic.c b/sys/kern/sys_generic.c
 index a1a7086..2b05faf 100644
 --- a/sys/kern/sys_generic.c
 +++ b/sys/kern/sys_generic.c
 @@ -76,7 +76,8 @@ static MALLOC_DEFINE(M_IOCTLOPS, "ioctlops", "ioctl data buffer");
  static MALLOC_DEFINE(M_SELECT, "select", "select() buffer");
  MALLOC_DEFINE(M_IOV, "iov", "large iov's");
  
 -static int	pollout(struct pollfd *, struct pollfd *, u_int);
 +static int	pollout(struct thread *, struct pollfd *, struct pollfd *,
 +		    u_int);
  static int	pollscan(struct thread *, struct pollfd *, u_int);
  static int	pollrescan(struct thread *);
  static int	selscan(struct thread *, fd_mask **, fd_mask **, int);
 @@ -1207,7 +1208,7 @@ done:
  	if (error == EWOULDBLOCK)
  		error = 0;
  	if (error == 0) {
 -		error = pollout(bits, uap->fds, nfds);
 +		error = pollout(td, bits, uap->fds, nfds);
  		if (error)
  			goto out;
  	}
 @@ -1262,22 +1263,27 @@ pollrescan(struct thread *td)
  
  
  static int
 -pollout(fds, ufds, nfd)
 +pollout(td, fds, ufds, nfd)
 +	struct thread *td; 
  	struct pollfd *fds;
  	struct pollfd *ufds;
  	u_int nfd;
  {
  	int error = 0;
  	u_int i = 0;
 +	u_int n = 0;
  
  	for (i = 0; i < nfd; i++) {
  		error = copyout(&fds->revents, &ufds->revents,
  		    sizeof(ufds->revents));
  		if (error)
  			return (error);
 +		if (fds->revents != 0)
 +			n++;
  		fds++;
  		ufds++;
  	}
 +	td->td_retval[0] = n;
  	return (0);
  }
  

From: dfilter@FreeBSD.ORG (dfilter service)
To: bug-followup@FreeBSD.org
Cc:  
Subject: Re: kern/143029: commit references a PR
Date: Sat, 28 Aug 2010 17:42:22 +0000 (UTC)

 Author: kib
 Date: Sat Aug 28 17:42:08 2010
 New Revision: 211941
 URL: http://svn.freebsd.org/changeset/base/211941
 
 Log:
   For some file types, select code registers two selfd structures. E.g.,
   for socket, when specified POLLIN|POLLOUT in events, you would have one
   selfd registered for receiving socket buffer, and one for sending. Now,
   if both events are not ready to fire at the time of the initial scan,
   but are simultaneously ready after the sleep, pollrescan() would iterate
   over the pollfd struct twice. Since both times revents is not zero,
   returned value would be off by one.
   
   Fix this by recalculating the return value in pollout().
   
   PR:	kern/143029
   MFC after:	2 weeks
 
 Modified:
   head/sys/kern/sys_generic.c
 
 Modified: head/sys/kern/sys_generic.c
 ==============================================================================
 --- head/sys/kern/sys_generic.c	Sat Aug 28 17:38:40 2010	(r211940)
 +++ head/sys/kern/sys_generic.c	Sat Aug 28 17:42:08 2010	(r211941)
 @@ -76,7 +76,8 @@ static MALLOC_DEFINE(M_IOCTLOPS, "ioctlo
  static MALLOC_DEFINE(M_SELECT, "select", "select() buffer");
  MALLOC_DEFINE(M_IOV, "iov", "large iov's");
  
 -static int	pollout(struct pollfd *, struct pollfd *, u_int);
 +static int	pollout(struct thread *, struct pollfd *, struct pollfd *,
 +		    u_int);
  static int	pollscan(struct thread *, struct pollfd *, u_int);
  static int	pollrescan(struct thread *);
  static int	selscan(struct thread *, fd_mask **, fd_mask **, int);
 @@ -1207,7 +1208,7 @@ done:
  	if (error == EWOULDBLOCK)
  		error = 0;
  	if (error == 0) {
 -		error = pollout(bits, uap->fds, nfds);
 +		error = pollout(td, bits, uap->fds, nfds);
  		if (error)
  			goto out;
  	}
 @@ -1262,22 +1263,27 @@ pollrescan(struct thread *td)
  
  
  static int
 -pollout(fds, ufds, nfd)
 +pollout(td, fds, ufds, nfd)
 +	struct thread *td;
  	struct pollfd *fds;
  	struct pollfd *ufds;
  	u_int nfd;
  {
  	int error = 0;
  	u_int i = 0;
 +	u_int n = 0;
  
  	for (i = 0; i < nfd; i++) {
  		error = copyout(&fds->revents, &ufds->revents,
  		    sizeof(ufds->revents));
  		if (error)
  			return (error);
 +		if (fds->revents != 0)
 +			n++;
  		fds++;
  		ufds++;
  	}
 +	td->td_retval[0] = n;
  	return (0);
  }
  
 _______________________________________________
 svn-src-all@freebsd.org mailing list
 http://lists.freebsd.org/mailman/listinfo/svn-src-all
 To unsubscribe, send any mail to "svn-src-all-unsubscribe@freebsd.org"
 
Responsible-Changed-From-To: freebsd-bugs->kib 
Responsible-Changed-By: vwe 
Responsible-Changed-When: Sun Aug 29 12:06:38 UTC 2010 
Responsible-Changed-Why:  
over kib for MFC reminder 

http://www.freebsd.org/cgi/query-pr.cgi?pr=143029 

From: dfilter@FreeBSD.ORG (dfilter service)
To: bug-followup@FreeBSD.org
Cc:  
Subject: Re: kern/143029: commit references a PR
Date: Fri, 10 Sep 2010 12:55:45 +0000 (UTC)

 Author: kib
 Date: Fri Sep 10 12:55:36 2010
 New Revision: 212415
 URL: http://svn.freebsd.org/changeset/base/212415
 
 Log:
   MFC r211941:
   Recalcule the return value in pollout().
   
   PR:	kern/143029
   Tested by:	John Plevyak <jplevyak apache org>
 
 Modified:
   stable/8/sys/kern/sys_generic.c
 Directory Properties:
   stable/8/sys/   (props changed)
   stable/8/sys/amd64/include/xen/   (props changed)
   stable/8/sys/cddl/contrib/opensolaris/   (props changed)
   stable/8/sys/contrib/dev/acpica/   (props changed)
   stable/8/sys/contrib/pf/   (props changed)
   stable/8/sys/dev/xen/xenpci/   (props changed)
 
 Modified: stable/8/sys/kern/sys_generic.c
 ==============================================================================
 --- stable/8/sys/kern/sys_generic.c	Fri Sep 10 12:16:24 2010	(r212414)
 +++ stable/8/sys/kern/sys_generic.c	Fri Sep 10 12:55:36 2010	(r212415)
 @@ -76,7 +76,8 @@ static MALLOC_DEFINE(M_IOCTLOPS, "ioctlo
  static MALLOC_DEFINE(M_SELECT, "select", "select() buffer");
  MALLOC_DEFINE(M_IOV, "iov", "large iov's");
  
 -static int	pollout(struct pollfd *, struct pollfd *, u_int);
 +static int	pollout(struct thread *, struct pollfd *, struct pollfd *,
 +		    u_int);
  static int	pollscan(struct thread *, struct pollfd *, u_int);
  static int	pollrescan(struct thread *);
  static int	selscan(struct thread *, fd_mask **, fd_mask **, int);
 @@ -1207,7 +1208,7 @@ done:
  	if (error == EWOULDBLOCK)
  		error = 0;
  	if (error == 0) {
 -		error = pollout(bits, uap->fds, nfds);
 +		error = pollout(td, bits, uap->fds, nfds);
  		if (error)
  			goto out;
  	}
 @@ -1262,22 +1263,27 @@ pollrescan(struct thread *td)
  
  
  static int
 -pollout(fds, ufds, nfd)
 +pollout(td, fds, ufds, nfd)
 +	struct thread *td;
  	struct pollfd *fds;
  	struct pollfd *ufds;
  	u_int nfd;
  {
  	int error = 0;
  	u_int i = 0;
 +	u_int n = 0;
  
  	for (i = 0; i < nfd; i++) {
  		error = copyout(&fds->revents, &ufds->revents,
  		    sizeof(ufds->revents));
  		if (error)
  			return (error);
 +		if (fds->revents != 0)
 +			n++;
  		fds++;
  		ufds++;
  	}
 +	td->td_retval[0] = n;
  	return (0);
  }
  
 _______________________________________________
 svn-src-all@freebsd.org mailing list
 http://lists.freebsd.org/mailman/listinfo/svn-src-all
 To unsubscribe, send any mail to "svn-src-all-unsubscribe@freebsd.org"
 
State-Changed-From-To: open->closed 
State-Changed-By: kib 
State-Changed-When: Fri Sep 10 13:12:35 UTC 2010 
State-Changed-Why:  
Fix is merged to RELENG_8. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=143029 
>Unformatted:
