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. Fix: The following is the patch against 2.2.7. It applies to 3.0-RELEASE too. 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.
State Changed From-To: open->feedback Does this problem still exist?
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 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.
State Changed From-To: feedback->open Feedback was received
Responsible Changed From-To: dwmalone->silby I'll take this.
Comment on attachment 2952 [details] file.diff Does this still occur?
Reassign to the wild with permission of assignee.
For bugs matching the following conditions: - Status == In Progress - Assignee == "bugs@FreeBSD.org" - Last Modified Year <= 2017 Do - Set Status to "Open"
No one confirmed that the problem exists in many years.