From viro@math.psu.edu  Fri Oct 30 00:32:34 1998
Received: from math.psu.edu (leibniz.math.psu.edu [146.186.130.2])
          by hub.freebsd.org (8.8.8/8.8.8) with ESMTP id AAA04851
          for <FreeBSD-gnats-submit@freebsd.org>; Fri, 30 Oct 1998 00:32:33 -0800 (PST)
          (envelope-from viro@math.psu.edu)
Received: from hilbert.math.psu.edu (hilbert.math.psu.edu [146.186.130.197]) by math.psu.edu (8.8.5/8.7.3) with ESMTP id DAA25694 for <FreeBSD-gnats-submit@freebsd.org>; Fri, 30 Oct 1998 03:32:31 -0500 (EST)
Received: (viro@localhost) by hilbert.math.psu.edu (8.8.8/8.6.9) id DAA28142 for FreeBSD-gnats-submit@freebsd.org; Fri, 30 Oct 1998 03:32:30 -0500 (EST)
Message-Id: <199810300832.DAA28142@hilbert.math.psu.edu>
Date: Fri, 30 Oct 1998 03:32:30 -0500 (EST)
From: viro@math.psu.edu
Reply-To: viro@math.psu.edu
To: FreeBSD-gnats-submit@freebsd.org
Subject: Race condition between unp_gc() and accept().
X-Send-Pr-Version: 3.2

>Number:         8498
>Category:       kern
>Synopsis:       Race condition between unp_gc() and accept().
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    silby
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Fri Oct 30 00:40:00 PST 1998
>Closed-Date:    
>Last-Modified:  Sun Oct 07 00:23:09 GMT 2007
>Originator:     Al Viro
>Release:        FreeBSD 2.2.7-RELEASE i386
>Organization:
-ENOENT
>Environment:

	Present both in 2.2.7-RELEASE and 3.0-RELEASE

>Description:

	Mark phase of unp_gc() doesn't scan the list of pending connections.
Thus if we connect() to a UNIX domain socket, pass a file descriptor via the
obtained connection (connect() doesn't block for UNIX domain) and close that
descriptor unp_gc() will consider it garbage until we accept() the connection.
Sweep phase of unp_gc() will merrily flush the queue of passed file.
	So we have a race - unp_gc() may be triggered by event completely
unrelated to sockets in question.
	I didn't check other *BSD kernels wrt this bug. Actually I've caught
it when I wrote a fix for Linux - garbage collector in Linux implementation
couldn't handle the circular dependencies. In the first variant of the patch
I've reproduced the bug in question - forgot to scan the queues of listening
sockets. Well, when I finally fixed it and submitted the patch I decided to
look into the FreeBSD kernel. Duh.

>How-To-Repeat:

	See above. I have a clear testcase, but it's 100 lines of (sparce) C.
If you need it I'll send it, indeed.
	Pseudocode (all sockets are PF_UNIX,SOCK_STREAM) follows:
	create a socket A, bind it to some address and listen().
	create a socket B, connect() to address of A.
	create a pair of sockets (C and D) by socketpair().
	write something to C.
	send a descriptor of D from A.
	close D.
***	trigger unp_gc().
	accept a connection (A).
	receive a message from resulting socket.
	read from the received descriptor.

Triggering unp_gc() may be done by creating another socketpair, passing
an arbitrary descriptor (0 ;-) and closing another end.

	Results: if we didn't trigger unp_gc() final read() returns the data
we had written to C. If we did trigger unp_gc() read() will return 0.

>Fix:
	
	The following is the patch against 2.2.7. It applies to 3.0-RELEASE
too.

*** uipc_usrreq.c	Thu Oct 29 21:27:35 1998
--- uipc_usrreq.c.new	Thu Oct 29 22:03:30 1998
***************
*** 736,742 ****
  unp_gc()
  {
  	register struct file *fp, *nextfp;
! 	register struct socket *so;
  	struct file **extra_ref, **fpp;
  	int nunref, i;
  
--- 736,742 ----
  unp_gc()
  {
  	register struct file *fp, *nextfp;
! 	register struct socket *so, *so1;
  	struct file **extra_ref, **fpp;
  	int nunref, i;
  
***************
*** 793,800 ****
  			if (fp->f_type != DTYPE_SOCKET ||
  			    (so = (struct socket *)fp->f_data) == 0)
  				continue;
! 			if (so->so_proto->pr_domain != &localdomain ||
! 			    (so->so_proto->pr_flags&PR_RIGHTS) == 0)
  				continue;
  #ifdef notdef
  			if (so->so_rcv.sb_flags & SB_LOCK) {
--- 793,819 ----
  			if (fp->f_type != DTYPE_SOCKET ||
  			    (so = (struct socket *)fp->f_data) == 0)
  				continue;
! 			if (so->so_proto->pr_domain != &localdomain)
! 				continue;
! 			if (so->so_options & SO_ACCEPTCONN) {
! 				/*
! 				 * Duh. There is something in theory that
! 				 * bugs are more natural than standards.
! 				 * 1) Linux implementation reproduced the bug
! 				 * in dealing with circular dependencies
! 				 * almost one-to-one. 
! 				 * 2) In the first version of my patch I
! 				 * forgot to scan queues of listening sockets.
! 				 * That is, reproduced another bug of _this_
! 				 * implementation ;-/
! 				 *   29/10/98, viro@math.psu.edu
! 				 */
! 				TAILQ_FOREACH(so1,&so->so_comp,so_list) {
! 					 unp_scan(so1->so_rcv.sb_mb, unp_mark);
! 				}
! 				continue;
! 			}
! 			if ((so->so_proto->pr_flags&PR_RIGHTS) == 0)
  				continue;
  #ifdef notdef
  			if (so->so_rcv.sb_flags & SB_LOCK) {

>Release-Note:
>Audit-Trail:
State-Changed-From-To: open->feedback 
State-Changed-By: iedowse 
State-Changed-When: Sat Jan 19 14:47:11 PST 2002 
State-Changed-Why:  

Does this problem still exist? 

http://www.FreeBSD.org/cgi/query-pr.cgi?pr=8498 

From: Ian Dowse <iedowse@maths.tcd.ie>
To: freebsd-gnats-submit@freebsd.org
Cc:  
Subject: Re: kern/8498: Race condition between unp_gc() and accept(). 
Date: Sat, 19 Jan 2002 23:17:46 +0000

 Adding to the audit trail:
 
 In message <Pine.GSO.4.21.0201191801310.5397-100000@weyl.math.psu.edu>, Alexand
 er Viro writes:
 >
 >
 >On Sat, 19 Jan 2002 iedowse@FreeBSD.org wrote:
 >
 >> Synopsis: Race condition between unp_gc() and accept().
 >> 
 >> State-Changed-From-To: open->feedback
 >> State-Changed-By: iedowse
 >> State-Changed-When: Sat Jan 19 14:47:11 PST 2002
 >> State-Changed-Why: 
 >> 
 >> Does this problem still exist?
 >
 >As far as I can see it's still there in HEAD - analysis from the original
 >bug report still applies.
 >
 >BTW, there is another problem: uipc_userreq.c:1378 has
 >        extra_ref = malloc(nfiles * sizeof(struct file *), M_FILE, M_WAITOK);
 >which can block.  During that time we might get new files opened and sent
 >in SCM_RIGHTS cookies.  Notice that
 >	a) we will have them _not_ marked, so the code after that will try
 >to kill them.
 >	b) nfiles might have grown!
 >
 >The former means that legitimate stuff gets killed.  The latter is a buffer
 >overrun in kernel space waiting to happen.
 >
 >Fix: invert the logics with "marked".  I.e. start with marking everything,
 >then unmark those you want to stay around.  Then by the end of the first
 >phase you have marked exactly the stuff that needs to die.  New struct
 >file won't be marked, so the loop populating extra_ref will skip it...
 >
Responsible-Changed-From-To: freebsd-bugs->dwmalone 
Responsible-Changed-By: dwmalone 
Responsible-Changed-When: Sun Jan 20 05:39:51 PST 2002 
Responsible-Changed-Why:  
I'll try to look at this as I have a few discriptor passing PRs to my 
name at the moment. If anyone else wants to look at this (Matt might, 
as he has worked with Al Viro on file discriptor problems before) please 
be my guest. 

http://www.FreeBSD.org/cgi/query-pr.cgi?pr=8498 
State-Changed-From-To: feedback->open 
State-Changed-By: gavin 
State-Changed-When: Mon Jul 30 10:32:35 UTC 2007 
State-Changed-Why:  
Feedback was received 

http://www.freebsd.org/cgi/query-pr.cgi?pr=8498 
Responsible-Changed-From-To: dwmalone->silby 
Responsible-Changed-By: silby 
Responsible-Changed-When: Sun Oct 7 00:22:55 UTC 2007 
Responsible-Changed-Why:  
I'll take this. 

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