]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
pbx: Fix hints deadlock between reload and ExtensionState.
authorJoshua C. Colp <jcolp@sangoma.com>
Thu, 27 Aug 2020 10:31:40 +0000 (07:31 -0300)
committerFriendly Automation <jenkins2@gerrit.asterisk.org>
Fri, 28 Aug 2020 17:11:55 +0000 (12:11 -0500)
When the ExtensionState AMI action is executed on a pattern matched
hint it can end up adding a new hint if one does not already exist.
This results in a locking order of contexts -> hints -> contexts.

If at the same time a reload is occurring and adding its own hint
it will have a locking order of hints -> contexts.

This results in a deadlock as one thread wants a lock on contexts
that the other has, and the other thread wants a lock on hints
that the other has.

This change enforces a hints -> contexts locking order by explicitly
locking hints in the places where a hint is added when queried for.
This matches the order seen through normal adding of hints.

ASTERISK-29046

Change-Id: I49f027f4aab5d2d50855ae937bcf5e2fd8bfc504

main/pbx.c

index a22d30763c4d952da07a33fa186bbedb19638543..8728f2c86b47a5f2ff27a0d0872888f145ade7aa 100644 (file)
@@ -3108,10 +3108,15 @@ static int internal_extension_state_extended(struct ast_channel *c, const char *
        }
 
        if (e->exten[0] == '_') {
-               /* Create this hint on-the-fly */
+               /* Create this hint on-the-fly, we explicitly lock hints here to ensure the
+                * same locking order as if this were done through configuration file - that is
+                * hints is locked first and then (if needed) contexts is locked
+                */
+               ao2_lock(hints);
                ast_add_extension(e->parent->name, 0, exten, e->priority, e->label,
                        e->matchcid ? e->cidmatch : NULL, e->app, ast_strdup(e->data), ast_free_ptr,
                        e->registrar);
+               ao2_unlock(hints);
                if (!(e = ast_hint_extension(c, context, exten))) {
                        /* Improbable, but not impossible */
                        return -1;
@@ -3188,9 +3193,11 @@ int ast_hint_presence_state(struct ast_channel *c, const char *context, const ch
 
        if (e->exten[0] == '_') {
                /* Create this hint on-the-fly */
+               ao2_lock(hints);
                ast_add_extension(e->parent->name, 0, exten, e->priority, e->label,
                        e->matchcid ? e->cidmatch : NULL, e->app, ast_strdup(e->data), ast_free_ptr,
                        e->registrar);
+               ao2_unlock(hints);
                if (!(e = ast_hint_extension(c, context, exten))) {
                        /* Improbable, but not impossible */
                        return -1;
@@ -3720,9 +3727,11 @@ static int extension_state_add_destroy(const char *context, const char *exten,
         * individual extension, because the pattern will no longer match first.
         */
        if (e->exten[0] == '_') {
+               ao2_lock(hints);
                ast_add_extension(e->parent->name, 0, exten, e->priority, e->label,
                        e->matchcid ? e->cidmatch : NULL, e->app, ast_strdup(e->data), ast_free_ptr,
                        e->registrar);
+               ao2_unlock(hints);
                e = ast_hint_extension(NULL, context, exten);
                if (!e || e->exten[0] == '_') {
                        return -1;