]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
stasis: Don't hold app_registry and session locks unnecessarily
authorGeorge Joseph <gjoseph@digium.com>
Wed, 6 Nov 2019 11:47:17 +0000 (04:47 -0700)
committerGeorge Joseph <gjoseph@digium.com>
Mon, 11 Nov 2019 00:42:16 +0000 (17:42 -0700)
resource_events:stasis_app_message_handler() was locking the session,
then attempting to determine if the app had debug enabled which
locked the app_registry container.  res_stasis:__stasis_app_register
was locking the app_registry container then calling app_update
which caused app_handler (which locks the session) to run.
The result was a deadlock.

* Updated resource_events:stasis_app_message_handler() to determine
  if debug was set (which locks the app_registry) before obtaining the
  session lock.

* Updated res_stasis:__stasis_app_register to release the app_registry
  container lock before calling app_update (which locks the sesison).

ASTERISK-28423
Reported by Ross Beer

Change-Id: I58c69d08cb372852a63933608e4d6c3e456247b4

res/ari/resource_events.c
res/res_stasis.c

index d935da15f530ef611a97138f89d4db89d89feb46..f70de430a8b435d85b202337018def6909ee75aa 100644 (file)
@@ -81,9 +81,16 @@ static void stasis_app_message_handler(
 {
        struct event_session *session = data;
        const char *msg_type, *msg_application;
+       int app_debug_enabled;
 
        ast_assert(session != NULL);
 
+       /*
+        * We need to get the debug flag before lockinf session
+        * to help prevent a deadlock with the apps_registry container.
+        */
+       app_debug_enabled = stasis_app_get_debug_by_name(app_name);
+
        ao2_lock(session);
 
        msg_type = S_OR(ast_json_string_get(ast_json_object_get(message, "type")), "");
@@ -115,7 +122,7 @@ static void stasis_app_message_handler(
                                msg_type,
                                msg_application);
        } else if (stasis_app_event_allowed(app_name, message)) {
-               if (stasis_app_get_debug_by_name(app_name)) {
+               if (app_debug_enabled) {
                        char *str = ast_json_dump_string_format(message, ast_ari_json_format());
 
                        ast_verbose("<--- Sending ARI event to %s --->\n%s\n",
@@ -310,7 +317,6 @@ static void event_session_dtor(void *obj)
 {
 #ifdef AST_DEVMODE /* Avoid unused variable warning */
        struct event_session *session = obj;
-
 #endif
 
        /* event_session_shutdown should have been called before now */
index 5cc758c48991f988673b5a4ec5f5c4f258d74cf3..04b10ef5e3e537c78efe2efc2fe1549c5fdf5c07 100644 (file)
@@ -1704,7 +1704,7 @@ struct ao2_container *stasis_app_get_all(void)
 
 static int __stasis_app_register(const char *app_name, stasis_app_cb handler, void *data, int all_events)
 {
-       struct stasis_app *app;
+       RAII_VAR(struct stasis_app *, app, NULL, ao2_cleanup);
 
        if (!apps_registry) {
                return -1;
@@ -1713,36 +1713,43 @@ static int __stasis_app_register(const char *app_name, stasis_app_cb handler, vo
        ao2_lock(apps_registry);
        app = ao2_find(apps_registry, app_name, OBJ_SEARCH_KEY | OBJ_NOLOCK);
        if (app) {
+               /*
+                * We need to unlock the apps_registry before calling app_update to
+                * prevent the possibility of a deadlock with the session.  We'll still
+                * run the lazy cleanup first though.
+                */
+               cleanup();
+               ao2_unlock(apps_registry);
                app_update(app, handler, data);
-       } else {
-               app = app_create(app_name, handler, data, all_events ? STASIS_APP_SUBSCRIBE_ALL : STASIS_APP_SUBSCRIBE_MANUAL);
-               if (!app) {
-                       ao2_unlock(apps_registry);
-                       return -1;
-               }
+               return 0;
+       }
 
-               if (all_events) {
-                       struct stasis_app_event_source *source;
+       app = app_create(app_name, handler, data, all_events ? STASIS_APP_SUBSCRIBE_ALL : STASIS_APP_SUBSCRIBE_MANUAL);
+       if (!app) {
+               ao2_unlock(apps_registry);
+               return -1;
+       }
 
-                       AST_RWLIST_RDLOCK(&event_sources);
-                       AST_LIST_TRAVERSE(&event_sources, source, next) {
-                               if (!source->subscribe) {
-                                       continue;
-                               }
+       if (all_events) {
+               struct stasis_app_event_source *source;
 
-                               source->subscribe(app, NULL);
+               AST_RWLIST_RDLOCK(&event_sources);
+               AST_LIST_TRAVERSE(&event_sources, source, next) {
+                       if (!source->subscribe) {
+                               continue;
                        }
-                       AST_RWLIST_UNLOCK(&event_sources);
+
+                       source->subscribe(app, NULL);
                }
-               ao2_link_flags(apps_registry, app, OBJ_NOLOCK);
+               AST_RWLIST_UNLOCK(&event_sources);
        }
+       ao2_link_flags(apps_registry, app, OBJ_NOLOCK);
 
        /* We lazily clean up the apps_registry, because it's good enough to
         * prevent memory leaks, and we're lazy.
         */
        cleanup();
        ao2_unlock(apps_registry);
-       ao2_ref(app, -1);
        return 0;
 }