Date: Wed, 23 Dec 1998 13:12:14 -0800 (PST) From: Matthew Dillon <dillon@apollo.backplane.com> To: Matthew Dillon <dillon@apollo.backplane.com> Cc: Peter Jeremy <peter.jeremy@auss2.alcatel.com.au>, freebsd-security@FreeBSD.ORG Subject: Re: cvs commit: src/etc rc.conf Message-ID: <199812232112.NAA17044@apollo.backplane.com>
next in thread | raw e-mail | index | archive | help
Ok, I've looked at it some more. It isn't as bad as I thought but it is still pretty bad. Here's the problem: process A: make unix domain connection sendmsg a couple of file descriptors close the descriptors we just sent close the connection (unc_gc() runs at this point and tries to flush/close these descriptors, but doesn't quite do it right. However, I looked all the way back to the 4.4 lite commit and it does not appear to gatuitously close the descriptors. Instead, it relies on sorflush() to do that). process B: create socket, listen on socket. wait until A has made the connection, sent the descriptors, and closed its connection. accept() the connection from A (which is now closed on A's) side. recvmsg() descriptors, which were previously closed but may now be valid again and hold references to files used by other processes if we have waited a sufficient period of time. However, from my reading of the code if these descriptors had been closed, the associated socket buffer would have been flushed so this can't happen. Ok, and this is why it *DOESN'T* actually happen: unc_gc() adds an f_count reference, then attempt to sorflush() the isolated files, then closes them (subtracting one from the ref count). Thus it is left up to sorflush() to actually close the descriptors. sorflush() will release the associated messages so we should be safe. But here's the problem: unp_gc() is confused. It is calling sorflush() on non-sockets. Line 1118 of kern/uipc_usrreq.c: /* * for each FD on our hit list, do the following two things */ for (i = nunref, fpp = extra_ref; --i >= 0; ++fpp) sorflush((struct socket *)(*fpp)->f_data); <------- for (i = nunref, fpp = extra_ref; --i >= 0; ++fpp) closef(*fpp, (struct proc *) NULL); free((caddr_t)extra_ref, M_FILE); unp_gcing = 0; It happily believes that any descriptor for which FMARK has not been set is not marked accessible and thus, somehow, is magically a socket and calls sorflush() on it. But the descriptors that were queued in those sendmsg()'s wind up not being FMARK's because their f_count == f_msgcount. unp_gc() then assumes these descriptors are unix domain sockets. But they are not necessarily unix domain sockets. Could someone take a look at that and tell me if I am right in regards to the sorflush() ? ::At the bottom of page 389: :: "If a listening socket is accessible, then any queued connections :: that it holds are also accessible; the garbage collector in 4.4BSD :: fails to take this into account." :: ::This footnote is referenced from a paragraph discussing unp_gc() - ::which can be found in kern/uipc_usrreq.c. From a quick look at the ::2.2.6 CVS logs (the latest I can quickly study), it doesn't look like ::it's ever been eradicated. :: ::Peter : : Shit. There's a bug. It took me a while to find it, but there's a : bug. A very bad bug. A Very, very, very, very bad bug. I'm going to : fix this in the FreeBSD tree first, then post the diff without : additional comment. : : -Matt Matthew Dillon Engineering, HiWay Technologies, Inc. & BEST Internet Communications & God knows what else. <dillon@backplane.com> (Please include original email in any response) To Unsubscribe: send mail to majordomo@FreeBSD.org with "unsubscribe freebsd-security" in the body of the message
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199812232112.NAA17044>