Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 25 Sep 2015 09:42:51 -0700
From:      John Baldwin <jhb@freebsd.org>
To:        Julien Charbon <jch@freebsd.org>
Cc:        Palle Girgensohn <girgen@freebsd.org>, George Neville-Neil <gnn@freebsd.org>, Konstantin Belousov <kostikbel@gmail.com>, freebsd-net@freebsd.org
Subject:   Re: Can tcp_close() be called in INP_TIMEWAIT case
Message-ID:  <2216936.QIvWsOndvU@ralph.baldwin.cx>
In-Reply-To: <5603E8E4.5030406@freebsd.org>
References:  <26B0FF93-8AE3-4514-BDA1-B966230AAB65@FreeBSD.org> <5602BB7A.9010504@freebsd.org> <5603E8E4.5030406@freebsd.org>

next in thread | previous in thread | raw e-mail | index | archive | help
On Thursday, September 24, 2015 02:13:24 PM Julien Charbon wrote:
>  So the issue is:
> 
>  - tcp_close() is called for some reasons with an inp in INP_TIMEWAIT
> state and sets the INP_DROPPED flag,
>  - tcp_detach() is called when the last reference on socket is dropped
> 
>  then now in_pcbfree() can be called twice instead of once:
> 
>  1. First in tcp_detach():
> 
> static void
> tcp_detach(struct socket *so, struct inpcb *inp)
> {
>         struct tcpcb *tp;
>         tp = intotcpcb(inp);
> 
>         if (inp->inp_flags & INP_TIMEWAIT) {
>                 if (inp->inp_flags & INP_DROPPED) {
>                         in_pcbdetach(inp);
>                         in_pcbfree(inp); <--
>                 }
> 
>  2. Second when tcptw expires here:
> 
> void
> tcp_twclose(struct tcptw *tw, int reuse)
> {
>         struct socket *so;
>         struct inpcb *inp;
> 
>         inp = tw->tw_inpcb;
> 
>         tcp_tw_2msl_stop(tw, reuse);
>         inp->inp_ppcb = NULL;
>         in_pcbdrop(inp);
> 
>         so = inp->inp_socket;
>         if (so != NULL) {
>                 ...
>         } else {
>                 in_pcbfree(inp); <--
>         }
> 
>  This behavior is backed by Palle kernel panic backstraces and coredumps.
> 
>  o Solutions:
> 
>  Long:  Forbid to call tcp_close() when inp is in INP_TIMEWAIT state,
> the TCP stack rule being:
> 
>  - if !INP_TIMEWAIT: Call tcp_close()
>  - if INP_TIMEWAIT: Call tcp_twclose() (or call nothing, the tcptw will
> expire/be recycled anyway)
> 
>  Short:
>   if INP_TIMEWAIT & INP_DROPPED:
>     Do not call in_pcbfree(inp) in tcp_detach() unless tcptw is already
> discarded.
> 
>  The long solution seems cleaner, backed by tcp_detach() old comments
> and most of current tcp_close() calls but I won't take that longer path
> without -net approval first.

I prefer the longer solution if it keeps tcp_detach() simpler by avoiding
an extra condition.  Please just document it via assertions in tcp_close()
(or is this the assertion that fired and triggered the reported panic?  If so,
then you obviously don't need to add it. :-P)

-- 
John Baldwin



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