Author: rrs
Date: Wed Jun  9 16:42:42 2010
New Revision: 208953
URL: http://svn.freebsd.org/changeset/base/208953

Log:
  Fix serveral bugs all having to do with freeing an
  sctp_inpcb:
  1) Make sure not to remove the flag on the PCB until
     after the close() caller is back in control with the
     lock. Otherwise a quickly freeing assoc could kill the
     inpcb and cause a panic.
  
  2) Make sure all calls to log_closing have not released
     the locks before calling the log function, we don't
     want the logging function to crash us due to a freed
     inpcb.
  
  3) Make sure that when we get to the end, we release all
     locks (after removing them from view) and as long as
     we are NOT the inp-kill timer removing the inp, call
     the callout_drain() function so a racing timer won't
     later call in and cause a racing crash.
  MFC after:    1 week

Modified:
  head/sys/netinet/sctp_pcb.c

Modified: head/sys/netinet/sctp_pcb.c
==============================================================================
--- head/sys/netinet/sctp_pcb.c Wed Jun  9 16:39:18 2010        (r208952)
+++ head/sys/netinet/sctp_pcb.c Wed Jun  9 16:42:42 2010        (r208953)
@@ -3114,12 +3114,6 @@ sctp_inpcb_free(struct sctp_inpcb *inp, 
                 * via the sockets layer.
                 */
                SCTP_ITERATOR_LOCK();
-               inp->sctp_flags &= ~SCTP_PCB_FLAGS_CLOSE_IP;
-               /* socket is gone, so no more wakeups allowed */
-               inp->sctp_flags |= SCTP_PCB_FLAGS_DONT_WAKE;
-               inp->sctp_flags &= ~SCTP_PCB_FLAGS_WAKEINPUT;
-               inp->sctp_flags &= ~SCTP_PCB_FLAGS_WAKEOUTPUT;
-
                /* mark any iterators on the list or being processed */
                sctp_iterator_inp_being_freed(inp);
                SCTP_ITERATOR_UNLOCK();
@@ -3137,6 +3131,14 @@ sctp_inpcb_free(struct sctp_inpcb *inp, 
        SCTP_INP_INFO_WLOCK();
 
        SCTP_INP_WLOCK(inp);
+       if (from == SCTP_CALLED_AFTER_CMPSET_OFCLOSE) {
+               inp->sctp_flags &= ~SCTP_PCB_FLAGS_CLOSE_IP;
+               /* socket is gone, so no more wakeups allowed */
+               inp->sctp_flags |= SCTP_PCB_FLAGS_DONT_WAKE;
+               inp->sctp_flags &= ~SCTP_PCB_FLAGS_WAKEINPUT;
+               inp->sctp_flags &= ~SCTP_PCB_FLAGS_WAKEOUTPUT;
+
+       }
        /* First time through we have the socket lock, after that no more. */
        sctp_timer_stop(SCTP_TIMER_TYPE_NEWCOOKIE, inp, NULL, NULL,
            SCTP_FROM_SCTP_PCB + SCTP_LOC_1);
@@ -3334,13 +3336,13 @@ sctp_inpcb_free(struct sctp_inpcb *inp, 
                }
                /* now is there some left in our SHUTDOWN state? */
                if (cnt_in_sd) {
-                       SCTP_INP_WUNLOCK(inp);
-                       SCTP_ASOC_CREATE_UNLOCK(inp);
-                       SCTP_INP_INFO_WUNLOCK();
 #ifdef SCTP_LOG_CLOSING
                        sctp_log_closing(inp, NULL, 2);
 #endif
                        inp->sctp_socket = NULL;
+                       SCTP_INP_WUNLOCK(inp);
+                       SCTP_ASOC_CREATE_UNLOCK(inp);
+                       SCTP_INP_INFO_WUNLOCK();
                        return;
                }
        }
@@ -3415,12 +3417,12 @@ sctp_inpcb_free(struct sctp_inpcb *inp, 
        if (cnt) {
                /* Ok we have someone out there that will kill us */
                (void)SCTP_OS_TIMER_STOP(&inp->sctp_ep.signature_change.timer);
-               SCTP_INP_WUNLOCK(inp);
-               SCTP_ASOC_CREATE_UNLOCK(inp);
-               SCTP_INP_INFO_WUNLOCK();
 #ifdef SCTP_LOG_CLOSING
                sctp_log_closing(inp, NULL, 3);
 #endif
+               SCTP_INP_WUNLOCK(inp);
+               SCTP_ASOC_CREATE_UNLOCK(inp);
+               SCTP_INP_INFO_WUNLOCK();
                return;
        }
        if (SCTP_INP_LOCK_CONTENDED(inp))
@@ -3434,26 +3436,43 @@ sctp_inpcb_free(struct sctp_inpcb *inp, 
            (being_refed) ||
            (inp->sctp_flags & SCTP_PCB_FLAGS_CLOSE_IP)) {
                (void)SCTP_OS_TIMER_STOP(&inp->sctp_ep.signature_change.timer);
+#ifdef SCTP_LOG_CLOSING
+               sctp_log_closing(inp, NULL, 4);
+#endif
                sctp_timer_start(SCTP_TIMER_TYPE_INPKILL, inp, NULL, NULL);
                SCTP_INP_WUNLOCK(inp);
                SCTP_ASOC_CREATE_UNLOCK(inp);
                SCTP_INP_INFO_WUNLOCK();
-#ifdef SCTP_LOG_CLOSING
-               sctp_log_closing(inp, NULL, 4);
-#endif
                return;
        }
-       (void)SCTP_OS_TIMER_STOP(&inp->sctp_ep.signature_change.timer);
        inp->sctp_ep.signature_change.type = 0;
        inp->sctp_flags |= SCTP_PCB_FLAGS_SOCKET_ALLGONE;
+       /*
+        * Remove it from the list .. last thing we need a lock for.
+        */
+       LIST_REMOVE(inp, sctp_list);
+       SCTP_INP_WUNLOCK(inp);
+       SCTP_ASOC_CREATE_UNLOCK(inp);
+       SCTP_INP_INFO_WUNLOCK();
+       /*
+        * Now we release all locks. Since this INP cannot be found anymore
+        * except possbily by the kill timer that might be running. We call
+        * the drain function here. It should hit the case were it sees the
+        * ACTIVE flag cleared and exit out freeing us to proceed and
+        * destroy everything.
+        */
+       if (from != SCTP_CALLED_FROM_INPKILL_TIMER) {
+               
(void)SCTP_OS_TIMER_STOP_DRAIN(&inp->sctp_ep.signature_change.timer);
+       } else {
+               /* Probably un-needed */
+               (void)SCTP_OS_TIMER_STOP(&inp->sctp_ep.signature_change.timer);
+       }
 
 #ifdef SCTP_LOG_CLOSING
        sctp_log_closing(inp, NULL, 5);
 #endif
 
-       (void)SCTP_OS_TIMER_STOP(&inp->sctp_ep.signature_change.timer);
-       inp->sctp_ep.signature_change.type = SCTP_TIMER_TYPE_NONE;
-       /* Clear the read queue */
+
        if ((inp->sctp_asocidhash) != NULL) {
                SCTP_HASH_FREE(inp->sctp_asocidhash, inp->hashasocidmark);
                inp->sctp_asocidhash = NULL;
@@ -3524,8 +3543,6 @@ sctp_inpcb_free(struct sctp_inpcb *inp, 
                shared_key = LIST_FIRST(&inp->sctp_ep.shared_keys);
        }
 
-       LIST_REMOVE(inp, sctp_list);
-
        /*
         * if we have an address list the following will free the list of
         * ifaddr's that are set into this ep. Again macro limitations here,
@@ -3558,7 +3575,6 @@ sctp_inpcb_free(struct sctp_inpcb *inp, 
        SCTP_INP_LOCK_DESTROY(inp);
        SCTP_INP_READ_DESTROY(inp);
        SCTP_ASOC_CREATE_LOCK_DESTROY(inp);
-       SCTP_INP_INFO_WUNLOCK();
        SCTP_ZONE_FREE(SCTP_BASE_INFO(ipi_zone_ep), inp);
        SCTP_DECR_EP_COUNT();
 }
_______________________________________________
svn-src-head@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to