From: George Joseph Date: Wed, 6 Nov 2019 11:47:17 +0000 (-0700) Subject: stasis: Don't hold app_registry and session locks unnecessarily X-Git-Tag: 13.30.0-rc1~30 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7a236ed20d884942d266f734b216238296af56a1;p=thirdparty%2Fasterisk.git stasis: Don't hold app_registry and session locks unnecessarily resource_events:app_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:app_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 --- diff --git a/res/ari/resource_events.c b/res/ari/resource_events.c index 20d9eaae0d..b5dc3cbb4e 100644 --- a/res/ari/resource_events.c +++ b/res/ari/resource_events.c @@ -124,6 +124,7 @@ static void app_handler(void *data, const char *app_name, const char *msg_application = S_OR( ast_json_string_get(ast_json_object_get(message, "application")), ""); + int app_debug_enabled; if (!session) { return; @@ -142,9 +143,12 @@ static void app_handler(void *data, const char *app_name, return; } + /* Don't lock the app_registry container with session locked */ + app_debug_enabled = stasis_app_get_debug_by_name(app_name); + ao2_lock(session); if (session->ws_session && 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", diff --git a/res/res_stasis.c b/res/res_stasis.c index 114a43fbaf..a8cc14c5ce 100644 --- a/res/res_stasis.c +++ b/res/res_stasis.c @@ -1677,7 +1677,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; @@ -1686,36 +1686,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; }