From d760c527fa5e85a4c38e78bafd0042bc68ec5d4d Mon Sep 17 00:00:00 2001 From: Tinet-mucw Date: Mon, 17 Aug 2026 20:54:23 -0700 Subject: [PATCH] func_speex: Hold channel lock while updating speex state. speex_write() previously unlocked the channel immediately after looking up the speex datastore, then continued to create, modify, or destroy SpeexPreprocessState and related direction data without the lock. speex_callback() runs from the media path with the channel already locked, so concurrent Set(DENOISE)/Set(AGC) could free or mutate that state while preprocess was running and crash inside speex_preprocess_run(). Keep the channel locked for the full configuration update, unlock before ast_audiohook_attach()/detach(), and hold the lock across speex_read() while copying values out of the datastore. Fixes: #2091 --- funcs/func_speex.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/funcs/func_speex.c b/funcs/func_speex.c index 1be5cc990f..65d5ab30cb 100644 --- a/funcs/func_speex.c +++ b/funcs/func_speex.c @@ -220,13 +220,14 @@ static int speex_write(struct ast_channel *chan, const char *cmd, char *data, co ast_channel_lock(chan); if (!(datastore = ast_channel_datastore_find(chan, &speex_datastore, NULL))) { - ast_channel_unlock(chan); if (!(datastore = ast_datastore_alloc(&speex_datastore, NULL))) { + ast_channel_unlock(chan); return 0; } if (!(si = ast_calloc(1, sizeof(*si)))) { + ast_channel_unlock(chan); ast_datastore_free(datastore); return 0; } @@ -235,8 +236,8 @@ static int speex_write(struct ast_channel *chan, const char *cmd, char *data, co si->audiohook.manipulate_callback = speex_callback; si->lastrate = 8000; is_new = 1; + } else { - ast_channel_unlock(chan); si = datastore->data; } @@ -248,6 +249,13 @@ static int speex_write(struct ast_channel *chan, const char *cmd, char *data, co if (!*sdi) { if (!(*sdi = ast_calloc(1, sizeof(**sdi)))) { + if (is_new) { + datastore->data = si; + ast_channel_unlock(chan); + ast_datastore_free(datastore); + } else { + ast_channel_unlock(chan); + } return 0; } /* Right now, the audiohooks API will _only_ provide us 8 kHz slinear @@ -292,24 +300,26 @@ static int speex_write(struct ast_channel *chan, const char *cmd, char *data, co if (!si->rx && !si->tx) { if (is_new) { + datastore->data = si; is_new = 0; + ast_channel_unlock(chan); } else { - ast_channel_lock(chan); ast_channel_datastore_remove(chan, datastore); - ast_channel_unlock(chan); ast_audiohook_remove(chan, &si->audiohook); + ast_channel_unlock(chan); ast_audiohook_detach(&si->audiohook); } - ast_datastore_free(datastore); + return 0; } if (is_new) { datastore->data = si; - ast_channel_lock(chan); ast_channel_datastore_add(chan, datastore); ast_channel_unlock(chan); ast_audiohook_attach(chan, &si->audiohook); + } else { + ast_channel_unlock(chan); } return 0; @@ -331,7 +341,6 @@ static int speex_read(struct ast_channel *chan, const char *cmd, char *data, cha ast_channel_unlock(chan); return -1; } - ast_channel_unlock(chan); si = datastore->data; @@ -340,6 +349,7 @@ static int speex_read(struct ast_channel *chan, const char *cmd, char *data, cha else if (!strcasecmp(data, "rx")) sdi = si->rx; else { + ast_channel_unlock(chan); ast_log(LOG_ERROR, "%s(%s) must either \"tx\" or \"rx\"\n", cmd, data); return -1; } @@ -349,6 +359,7 @@ static int speex_read(struct ast_channel *chan, const char *cmd, char *data, cha else snprintf(buf, len, "%d", sdi ? sdi->denoise : 0); + ast_channel_unlock(chan); return 0; }