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>