Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 30 Oct 1998 03:32:30 -0500 (EST)
From:      viro@math.psu.edu
To:        FreeBSD-gnats-submit@FreeBSD.ORG
Subject:   kern/8498: Race condition between unp_gc() and accept().
Message-ID:  <199810300832.DAA28142@hilbert.math.psu.edu>

next in thread | raw e-mail | index | archive | help

>Number:         8498
>Category:       kern
>Synopsis:       Race condition between unp_gc() and accept().
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    freebsd-bugs
>State:          open
>Quarter:
>Keywords:
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Fri Oct 30 00:40:00 PST 1998
>Last-Modified:
>Originator:     Al Viro
>Organization:
-ENOENT
>Release:        FreeBSD 2.2.7-RELEASE i386
>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) {

>Audit-Trail:
>Unformatted:

To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-bugs" in the body of the message



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199810300832.DAA28142>