From 04527176458b0183d440891ec31770abedece71c Mon Sep 17 00:00:00 2001 From: "Joshua C. Colp" Date: Thu, 27 Aug 2020 07:31:40 -0300 Subject: [PATCH] pbx: Fix hints deadlock between reload and ExtensionState. 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 | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/main/pbx.c b/main/pbx.c index bbc6df9d37..b520f5fc98 100644 --- a/main/pbx.c +++ b/main/pbx.c @@ -3148,10 +3148,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; @@ -3228,9 +3233,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; @@ -3766,9 +3773,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;