]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
Avoid segfault and race condition when socket is destroyed while listener is in use.
authorDarren Schreiber <d@d-man.org>
Thu, 5 Jul 2012 08:47:07 +0000 (01:47 -0700)
committerDarren Schreiber <d@d-man.org>
Thu, 5 Jul 2012 08:47:07 +0000 (01:47 -0700)
src/mod/event_handlers/mod_erlang_event/mod_erlang_event.c

index b8e13b2a5c6d0eb858de13d1ece3efd91b2f563e..8d7b239282795f539790a08654d51fa3ed22998c 100644 (file)
@@ -411,14 +411,20 @@ static switch_xml_t erlang_fetch(const char *sectionstr, const char *tag_name, c
 
        switch_thread_rwlock_rdlock(globals.bindings_rwlock);
 
+       /* Keep the listener from getting pulled out from under us */
+       switch_thread_rwlock_rdlock(globals.listener_rwlock);
+
        for (ptr = bindings.head; ptr; ptr = ptr->next) {
-               if (ptr->section != section)
+               /* If we got listener_rwlock while a listner thread was dying after removing the listener
+                  from listener_list but before locking for the bindings removal (now pending our lock) check
+                  if it already closed the socket.  Our listener pointer should still be good (pointed at an orphan
+                  listener) until it is removed from the binding...*/
+               if (!ptr->listener) {
                        continue;
+               }
 
-               if (!ptr->listener) {
-                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "NULL pointer binding!\n");
-                       switch_thread_rwlock_unlock(globals.bindings_rwlock);
-                       goto cleanup; /* our pointer is trash */
+               if (ptr->section != section) {
+                       continue;
                }
 
                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "binding for %s in section %s with key %s and value %s requested from node %s\n", tag_name, sectionstr, key_name, key_value, ptr->process.pid.node);
@@ -445,11 +451,14 @@ static switch_xml_t erlang_fetch(const char *sectionstr, const char *tag_name, c
                   on our condition before the action starts. */
 
                switch_mutex_lock(ptr->listener->sock_mutex);
-               ei_sendto(ptr->listener->ec, ptr->listener->sockfd, &ptr->process, &buf);
+               if (ptr->listener->sockfd) {
+                       ei_sendto(ptr->listener->ec, ptr->listener->sockfd, &ptr->process, &buf);
+               }
                switch_mutex_unlock(ptr->listener->sock_mutex);
        }
 
        switch_thread_rwlock_unlock(globals.bindings_rwlock);
+       switch_thread_rwlock_unlock(globals.listener_rwlock);
 
        ei_x_free(&buf);
 
@@ -1243,9 +1252,11 @@ void destroy_listener(listener_t * listener)
        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Session complete, waiting for children\n");
        switch_thread_rwlock_wrlock(listener->rwlock);
 
+       switch_mutex_lock(listener->sock_mutex);
        if (listener->sockfd) {
                close_socket(&listener->sockfd);
        }
+       switch_mutex_unlock(listener->sock_mutex);
 
        switch_core_hash_destroy(&listener->event_hash);