Can tcp_close() be called in INP_TIMEWAIT case
Julien Charbon
jch at freebsd.org
Mon Sep 28 09:08:56 UTC 2015
Hi John,
On 25/09/15 18:42, John Baldwin wrote:
> 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)
Thanks for your answer. And indeed tcp_detach() will be kept simpler
with the longer solution, I will introduce the new assertion in
tcp_close(), something like :
struct tcpcb *
tcp_close(struct tcpcb *tp)
{
...
/*
* tcp_close() should not called on TIME_WAIT connections.
* These connections should be either teardown using
* tcp_twclose(), or left alone waiting for TIME_WAIT timeout
* expiration.
*/
KASSERT((inpb->inp_flags & INP_TIMEWAIT) == 0,
("tcp_close cannot be called on TIME_WAIT connections"));
And I will fix all paths that could lead to calling tcp_close() on TCP
TIME_WAIT connections (that is why this solution will be longer). I
found three potential paths so far.
--
Julien
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 496 bytes
Desc: OpenPGP digital signature
URL: <http://lists.freebsd.org/pipermail/freebsd-net/attachments/20150928/d016ea6c/attachment.bin>
More information about the freebsd-net
mailing list