NetBSD-Bugs archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

kern/44410: policy reference count not decremented correctly on socket close



>Number:         44410
>Category:       kern
>Synopsis:       policy reference count not decremented correctly on socket 
>close
>Confidential:   no
>Severity:       serious
>Priority:       high
>Responsible:    kern-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Tue Jan 18 14:35:00 +0000 2011
>Originator:     Dr. W. Stukenbrock
>Release:        NetBSD 5.1
>Organization:
Dr. Nagler & Company GmbH
>Environment:
        
        
System: NetBSD test-s0 4.0 NetBSD 4.0 (NSW-WS) #0: Tue Aug 17 17:28:09 CEST 
2010 wgstuken@test-s0:/usr/src/sys/arch/amd64/compile/NSW-WS amd64
Architecture: x86_64
Machine: amd64
>Description:
        If the kernel is compiled with FAST_IPSEC some policy information is 
stored in the PCB of the socket.
        FAST_IPSEC seems to have an additional cache to speed up things.

        Here some things I've figgured out with debugging IPSEC with "sysctl -w 
net-key.debug=0x41":
        remark: the output is after running the system a short time, we start 
with 410 references already lost ...
                I used a small program that sends two UPD-packets to the echo 
port on an other machine with sleep(1)
                between each part.

        I've added some comments about the system calls to the output ...

-- socket(PF_INET, SOCK_DGRAM, 0) ... ipsec_init_policy()

DP key_newsp from ../../../../netipsec/ipsec.c:1176 return SP:0xffff800010207600
DP key_newsp from ../../../../netipsec/ipsec.c:1183 return SP:0xffff80001018ec00

-- sendto echo port of other host

  ... ipsec_getpolicybysock() called from ipsec4_checkpolicy() from ip_output() 
from udp_output()
DP key_allocsp from ../../../../netipsec/ipsec.c:589
DP key_allocsp return SP:0x0 (ID=0) refcnt 0
DP key_allocsp_default from ../../../../netipsec/ipsec.c:591
DP key_allocsp_default returns SP:0xffffffff806948a0 (411)
DP ipsec_getpolicybysock (priv 1 policy 3) allocates SP:0xffffffff806948a0 
(refcnt 411)
DP ipsec_fillpcbcache (dir 2) cause refcnt++:412 SP:0xffffffff806948a0
  ... inside of ipsec4_checkpolicy() - returns NULL
DP key_freesp SP:0xffffffff806948a0 (ID=0) from 
../../../../netipsec/ipsec.c:718; refcnt now 411
  ... ipsec_invalpcbcache(IPSEC_DIR_ANY) called from ipsec_pcbdisconn() from
  ...    in_pcbdisconnect() called after udp_output()
DP key_freesp SP:0xffffffff806948a0 (ID=0) from 
../../../../netipsec/ipsec.c:389; refcnt now 410

-- reply packet

  ... udp4_realinput() called from udp_input() from ip_input()
  ... in_pcblookup_bind() will return a pointer for this packet ...
  ... udp4_sendup called ...
  ... ipsec4_in_reject_so() called - macro for FAST_IPSEC to ipsec4_in_reject() 
...
  ... remark: we have a "inp" pointer -> call ipsec_getpolicybysock() ...
  ... ipsec_checkpcbcache() from ipsec_getpolicybysock() - but nothing in the 
cache
  ... so ipsec4_setspidx_inpcb() is called that calls ipsec_setspidx()
  ... now we try to get the policy - first try specific (failes) then get 
default
DP key_allocsp from ../../../../netipsec/ipsec.c:589
DP key_allocsp return SP:0x0 (ID=0) refcnt 0
DP key_allocsp_default from ../../../../netipsec/ipsec.c:591
DP key_allocsp_default returns SP:0xffffffff806948a0 (411)
  ... debug message of ipsec_getpolicybysock() followed by IN-dir cache setup 
....
DP ipsec_getpolicybysock (priv 1 policy 3) allocates SP:0xffffffff806948a0 
(refcnt 411)
DP ipsec_fillpcbcache (dir 1) cause refcnt++:412 SP:0xffffffff806948a0
  ... back in ipsec4_in_reject() ...
  ... call ipsec_in_reject() that will return OK
  ... free the reference to the policy ...
DP key_freesp SP:0xffffffff806948a0 (ID=0) from 
../../../../netipsec/ipsec.c:1732; refcnt now 411

  ... remark: now a reference to the IN-dir SP is still in the cache !!!!!!!

-- sendto echo port of other host

DP key_allocsp from ../../../../netipsec/ipsec.c:589
DP key_allocsp return SP:0x0 (ID=0) refcnt 0
DP key_allocsp_default from ../../../../netipsec/ipsec.c:591
DP key_allocsp_default returns SP:0xffffffff806948a0 (412)
DP ipsec_getpolicybysock (priv 1 policy 3) allocates SP:0xffffffff806948a0 
(refcnt 412)
DP ipsec_fillpcbcache (dir 2) cause refcnt++:413 SP:0xffffffff806948a0
DP key_freesp SP:0xffffffff806948a0 (ID=0) from 
../../../../netipsec/ipsec.c:718; refcnt now 412
  ... ipsec_invalpcbcache(IPSEC_DIR_ANY) called from ipsec_pcbdisconn() from
  ...    in_pcbdisconnect() called after udp_output()
DP key_freesp SP:0xffffffff806948a0 (ID=0) from 
../../../../netipsec/ipsec.c:389; refcnt now 411
DP key_freesp SP:0xffffffff806948a0 (ID=0) from 
../../../../netipsec/ipsec.c:389; refcnt now 410
  ... remark: this has freed the IN-bound cache entry too (why? not realy 
usefull for the next packet ...)

-- reply packet

  ... udp4_realinput() called from udp_input() from ip_input()
  ... in_pcblookup_bind() will return a pointer for this packet ...
  ... udp4_sendup called ...
  ... ipsec4_in_reject_so() called - macro for FAST_IPSEC to ipsec4_in_reject() 
...
  ... remark: we have a "inp" pointer -> call ipsec_getpolicybysock() ...
  ... ipsec_checkpcbcache() from ipsec_getpolicybysock() - but nothing in the 
cache
  ... so ipsec4_setspidx_inpcb() is called that calls ipsec_setspidx()
  ... now we try to get the policy - first try specific (failes) then get 
default
DP key_allocsp from ../../../../netipsec/ipsec.c:589
DP key_allocsp from ../../../../netipsec/ipsec.c:589
DP key_allocsp return SP:0x0 (ID=0) refcnt 0
DP key_allocsp_default from ../../../../netipsec/ipsec.c:591
DP key_allocsp_default returns SP:0xffffffff806948a0 (411)
  ... debug message of ipsec_getpolicybysock() followed by IN-dir cache setup 
....
DP ipsec_getpolicybysock (priv 1 policy 3) allocates SP:0xffffffff806948a0 
(refcnt 411)
DP ipsec_fillpcbcache (dir 1) cause refcnt++:412 SP:0xffffffff806948a0
  ... back in ipsec4_in_reject() ...
  ... call ipsec_in_reject() that will return OK
  ... free the reference to the policy ...
DP key_freesp SP:0xffffffff806948a0 (ID=0) from 
../../../../netipsec/ipsec.c:1732; refcnt now 411

  ... remark: now a reference to the IN-dir SP is still in the cache !!!!!!!

-- close(socket) ... ipsec4_delete_pcbpolicy()

DP key_freesp SP:0xffff800010207600 (ID=0) from 
../../../../netipsec/ipsec.c:1429; refcnt now 0
DP key_freesp SP:0xffff80001018ec00 (ID=0) from 
../../../../netipsec/ipsec.c:1432; refcnt now 0

  ... XXXXXX reference stored in cache not freed again XXXXXXX

-- exiting 


        Now what has happend ...
        The first packet is send and the answer has been recieved.
        After a recieve operation there is a reference in the cache to the SP 
...
        The second packet will kill the hole cache after sending - including the
        recieve side - not good for performace - the cache will only hit, if 
there is more than
        packet inbound before another packet is send. Hmmm - but that is not 
the point here ...

        After all trafic is done, there is again a reference in the cache, but 
that is
        not freed on socket shutdown ...
        I'm not shure (simply not tested) if this also happens for connection 
oriented protocols.

        During shutdown ipsec4_delete_pcbpolicy() calls ipsec_delpcbpolicy().
        That one simply frees the memory.
        The following patch adds code to this routine to clear the cache prior 
freeing the memeory.
        It seems to solve the problem.

>How-To-Repeat:
        Just enable debugging with "sysctl -w net.key.debu=0x41" on a system 
build with FAST_IPSEC
        and have a look at the reference counter for the policy. No IPSEC rule 
needs to be entered.
>Fix:
        The following fix seems to solve the problem. It clears the cache prior 
freeing the memory
        of the ancor structure in /usr/src/sys/netipsec/ipsec.c

--- ipsec.c.old 2011-01-18 15:31:16.000000000 +0100
+++ ipsec.c     2011-01-18 15:22:36.000000000 +0100
@@ -1160,6 +1160,9 @@
 static void
 ipsec_delpcbpolicy(struct inpcbpolicy *p)
 {
+#ifdef __NetBSD__
+       ipsec_invalpcbcache(p, IPSEC_DIR_ANY);
+#endif
        free(p, M_SECA);
 }
 

>Unformatted:
        
        


Home | Main Index | Thread Index | Old Index