Hi,

I saw a crash on an OpenBSD 6.1 based system when a kassert in
pf_state_key_unref() was triggert.

kernel diagnostic assertion "(sk->inp == NULL) || (sk->inp->inp_pf_sk == NULL)" 
 failed: file "../../../../../net/pf.c", line 7155                              

panic() at panic+0xfe                                                           
__assert() at __assert+0x25                                                     
pf_state_key_unref() at pf_state_key_unref+0xc6                                 
pf_pkt_unlink_state_key() at pf_pkt_unlink_state_key+0x15                       
m_free() at m_free+0xc0                                                         
soreceive() at soreceive+0xb5d                                                  
recvit() at recvit+0x13a                                                        
sys_recvmsg() at sys_recvmsg+0x107                                              
syscall() at syscall+0x2df                                                      

The problem is that setting the inp pointer in the statekey to NULL
is delayed until the statekey refcounter reaches 0.  So the inp
could get linked to another statekey while the mbuf in the socket
buffer was keeping the refcounter at 1.

The sk->inp should be set to NULL immediately, then the kassert can
get even stricter.

ok?

bluhm

Index: net/pf.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/net/pf.c,v
retrieving revision 1.1034
diff -u -p -r1.1034 pf.c
--- net/pf.c    5 Jun 2017 22:18:28 -0000       1.1034
+++ net/pf.c    20 Jun 2017 22:37:43 -0000
@@ -779,6 +779,7 @@ pf_state_key_detach(struct pf_state *s, 
                sk->removed = 1;
                pf_state_key_unlink_reverse(sk);
                pf_inpcb_unlink_state_key(sk->inp);
+               sk->inp = NULL;
                pf_state_key_unref(sk);
        }
 }
@@ -7147,8 +7148,7 @@ pf_state_key_unref(struct pf_state_key *
                /* state key must be unlinked from reverse key */
                KASSERT(sk->reverse == NULL);
                /* state key must be unlinked from socket */
-               KASSERT((sk->inp == NULL) || (sk->inp->inp_pf_sk == NULL));
-               sk->inp = NULL;
+               KASSERT(sk->inp == NULL);
                pool_put(&pf_state_key_pl, sk);
        }
 }

Reply via email to