]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
pbx: Hold channel lock for exception datastore access
authorTinet-mucw <mucw@ti-net.com.cn>
Fri, 20 Mar 2026 10:17:31 +0000 (03:17 -0700)
committergithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Mon, 23 Mar 2026 19:00:00 +0000 (19:00 +0000)
ast_channel_datastore_find() and ast_channel_datastore_add() must only be
called while the channel is locked (see channel.h). raise_exception() and the
EXCEPTION dialplan function read path accessed the exception datastore without
holding ast_channel_lock, which could corrupt the per-channel datastore list
under concurrency and lead to crashes during teardown (e.g. double free in
ast_datastore_free).

Resolves: #1831

main/pbx.c

index 7f97e1821abc83ec225e90fa06883afb7113ae57..f7a5ceb024f50d63f5fa41db2844d33855440366 100644 (file)
@@ -2820,15 +2820,20 @@ static const struct ast_datastore_info exception_store_info = {
  */
 int raise_exception(struct ast_channel *chan, const char *reason, int priority)
 {
-       struct ast_datastore *ds = ast_channel_datastore_find(chan, &exception_store_info, NULL);
-       struct pbx_exception *exception = NULL;
+       struct ast_datastore *ds;
+       struct pbx_exception *exception;
 
+       ast_channel_lock(chan);
+       ds = ast_channel_datastore_find(chan, &exception_store_info, NULL);
        if (!ds) {
                ds = ast_datastore_alloc(&exception_store_info, NULL);
-               if (!ds)
+               if (!ds) {
+                       ast_channel_unlock(chan);
                        return -1;
+               }
                if (!(exception = ast_calloc_with_stringfields(1, struct pbx_exception, 128))) {
                        ast_datastore_free(ds);
+                       ast_channel_unlock(chan);
                        return -1;
                }
                ds->data = exception;
@@ -2840,16 +2845,24 @@ int raise_exception(struct ast_channel *chan, const char *reason, int priority)
        ast_string_field_set(exception, context, ast_channel_context(chan));
        ast_string_field_set(exception, exten, ast_channel_exten(chan));
        exception->priority = ast_channel_priority(chan);
+       ast_channel_unlock(chan);
+
        set_ext_pri(chan, "e", priority);
        return 0;
 }
 
 static int acf_exception_read(struct ast_channel *chan, const char *name, char *data, char *buf, size_t buflen)
 {
-       struct ast_datastore *ds = ast_channel_datastore_find(chan, &exception_store_info, NULL);
-       struct pbx_exception *exception = NULL;
-       if (!ds || !ds->data)
+       struct ast_datastore *ds;
+       struct pbx_exception *exception;
+       int res = 0;
+
+       ast_channel_lock(chan);
+       ds = ast_channel_datastore_find(chan, &exception_store_info, NULL);
+       if (!ds || !ds->data) {
+               ast_channel_unlock(chan);
                return -1;
+       }
        exception = ds->data;
        if (!strcasecmp(data, "REASON"))
                ast_copy_string(buf, exception->reason, buflen);
@@ -2860,8 +2873,10 @@ static int acf_exception_read(struct ast_channel *chan, const char *name, char *
        else if (!strcasecmp(data, "PRIORITY"))
                snprintf(buf, buflen, "%d", exception->priority);
        else
-               return -1;
-       return 0;
+               res = -1;
+
+       ast_channel_unlock(chan);
+       return res;
 }
 
 static struct ast_custom_function exception_function = {