Re: socket state questions

Rick Macklem <[email protected]>
Newsgroups gmane.os.freebsd.devel.net
Message-ID <CAM5tNy5=_f8r8HNMY2CaUXE1sxS9DG2+QPihjxi3ssDEuznBwA@mail.gmail.com>
On Fri, May 1, 2026 at 7:05 AM Mark Johnston <[email protected]> wrote:
>
> On Wed, Apr 29, 2026 at 07:53:18PM -0700, Rick Macklem wrote:
> > On Mon, Apr 27, 2026 at 4:15 PM Rick Macklem <[email protected]> wrote:
> > >
> > > On Mon, Apr 27, 2026 at 2:41 PM Mark Johnston <[email protected]> wrote:
> > > >
> > > > On Sun, Apr 26, 2026 at 03:24:31PM -0700, Rick Macklem wrote:
> > > > > Hi,
> > > > >
> > > > > I'm trying to figure out what might be causing the
> > > > > crashes reported by bugzilla PR#293127.
> > > > >
> > > > > When I look in svc_vc.c I find this:
> > > > > svc_vc_create(SVCPOOL *pool, struct socket *so, size_t sendsize,
> > > > >     size_t recvsize)
> > > > > {
> > > > >    SVCXPRT *xprt;
> > > > >    int error;
> > > > >
> > > > >    SOCK_LOCK(so);
> > > > >    if (so->so_state & (SS_ISCONNECTED|SS_ISDISCONNECTED)) {
> > > > >
> > > > > This "if" is testing to see if the socket should be used for listening or
> > > > > if it one returned from "accept". For NFS, this should always be true.
> > > > >
> > > > > I'm wondering about a couple of things...
> > > > > - Should SS_ISDISCONNECTING be here, as well, or will accepted
> > > > >   TCP sockets go directly from SS_ISCONNECTED to SS_ISDISCONNECTED?
> > > >
> > > > I don't think so: it's possible for TCP sockets to go through
> > > > SS_ISDISCONNECTING, but only via system calls, and soisdisconnecting()
> > > > doesn't clear SS_ISCONNECTED.
> > > >
> > > > > - Why would SS_ISDISCONNECTED be here? Can a socket go from
> > > > >   SS_ISDISCONNECTED back to SS_ISCONNECTED?
> > > > >   If not, the only reason I can think of is so that it will get closed by
> > > > >   svc_vc_destroy_common(), but then why not just soclose() it here?
> > > >
> > > > It was added there explicitly in commit dad14216507bf:
> > > >
> > > > commit dad14216507bfff12693742399c3540722178263
> > > > Author: John Baldwin <[email protected]>
> > > > Date:   Mon Apr 8 19:03:01 2013 +0000
> > > >
> > > >     Fix a potential socket leak in the NFS server.  If a client closes its
> > > >     connection after it was accepted by the userland nfsd process but before
> > > >     it was handled off to svc_vc_create() in the kernel, then svc_vc_create()
> > > >     would see it as a new listen socket and try to listen on it leaving a
> > > >     dangling reference to the socket.  Instead, check for disconnected sockets
> > > >     and treat them like a connected socket.  The call to pru_getaddr() should
> > > >     fail and cause svc_vc_create() to fail.  Note that we need to lock the
> > > >     socket to get a consistent snapshot of so_state since there is a window
> > > >     in soisdisconnected() where both flags are clear.
> > > Interesting. First off, pru_getattr() didn't exist when he did this commit.
> > > There is pru_getpeeraddr() and pru_getsockaddr(), which for TCP
> > > are set to in_getpeeraddr() and in_getsockaddr().
> > > But neither of them ever return an error.
> > > In main, they are sopeeraddr() and sosockaddr(), but they
> > > still call in_getpeeraddr() and in_getsockaddr(), which never
> > > return an error.
> > >
> > > So, something else must have been causing the failure.
> > > Maybe the old code caused sosetopt() to fail, due to a
> > > flag like INP_DROPPED or INP_TIMEWAIT being set?
> > > The new code for tcp_ctloutput() is quite different, using
> > > stuff like t_fb which I have never heard of?
> > >
> > > I wonder if the svc_vc_create() code should just check for
> > > SS_DISCONNECTED and return an error for that case
> > > or can it trust sosetopt() to fail?
> > Taking this a little further..does this look correct?
> > (If done in svc_vc_create_conn() just before it is set up for
> > processing by the kernel nfsd threads.)
> >
> > SOCK_LOCK(so);
> > SOCK_RECVBUF_LOCK(so);
> > if (!soreadable(so) && (so->so_state & SS_ISDISCONNECTED) != 0) {
> >     SOCK_RECVBUF_UNLOCK(so);
> >     SOCK_UNLOCK(so);
> >     - Just get rid of socket. (Basically return an error to the userland nfsd
> >        so that it closes the socket fd.)
> >
> > Put another way, if the socket is not soreadable() and is SS_ISDISCONNECTED,
> > is there anything to do with the socket other than close() it?
>
> Even if you check it there, there's some chance the socket would be
> disconnected immediately after the check, so I don't know that your
> proposal would change anything.  Because the transport is marked active
> at the end of svc_vc_create(), we will try to read from the socket, and
> if the socket is disconnected that ought to result in an error from
> soreceive().
>
> How exactly is svc_vc_create() getting called?  I see a few different
> call paths but I'm not sure which one(s) to focus on.
For NFS, the call is always the one in nfs_nfsdkrpc.c with a socket
that was passed to the userspace nfsd via an accept(2) and then
passed into the kernel via the nfssvc(2) syscall.

The only place that I can spot in the svc side of the krpc that seems
in any way racy is svc_vc_create(). I am now testing a patch that
does all the last stuff from xprt_register()->xprt_active() inside
the xp_lock, SOCK_LOCK and SOCK_RECV_BUFFER locks,
to try and ensure nothing weird happens while the socket is
being registered and activated. (I cannot think of a race in the
current code that would lead to these crashes, but I'm getting
desperate;-)

Since I have both SOCK_LOCK and SOCK_RECV_BUFFER_LOCK,
I thought I might as well check for the case where the socket is
already closed down and just get rid of it now instead of activating
it, etc.
--> I agree that a disconnect can happen during this activation or
     just after it, but at least the socket is registered and activated
     when I drop the above locks.

One reason why I liked having the soreadable() call is that it
checks for the socket being spliced and returns false if it is.
(Maybe I should have a separate check for isspliced()?)
Most of the churn related to sorele()s is in the socket splice
code, which I don't understand well enough to know what is
going on or even what decides that a socket should be spliced.

Since I cannot reproduce the crashes, this is all I can think of
to try. Different reporters have patches that stop the crashes
for them, but I am not convinced they fix the underlying problem.
(Basically, if you added a bunch of soref()s to the code, the
crashes would go away and the socket structure would just
get leaked, but that doesn't seem appropriate for the FreeBSD
main kernel.)

rick
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.