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
This commit is contained in:
Joshua C. Colp 2020-08-27 07:31:40 -03:00 committed by George Joseph
parent 5a8cacb93d
commit 6d50d152d8
1 changed files with 10 additions and 1 deletions

View File

@ -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;