Commit 1f77cdc7c3 for asterisk.org

commit 1f77cdc7c30c5f125ac23a955e10afb0e11f8074
Author: George Joseph <gjoseph@sangoma.com>
Date:   Wed Dec 10 10:45:24 2025 -0700

    chan_websocket: Add locking in send_event and check for NULL websocket handle.

    On an outbound websocket connection, when the triggering caller hangs up,
    webchan_hangup() closes the outbound websocket session and sets the websocket
    session handle to NULL.  If the hangup happened in the tiny window between
    opening the outbound websocket connection and before read_thread_handler()
    was able to send the MEDIA_START message, it could segfault because the
    websocket session handle was NULL.  If it didn't actually segfault, there was
    also the possibility that the websocket instance wouldn't get cleaned up which
    could also cause the channel snapshot to not get cleaned up.  That could
    cause memory leaks and `core show channels` to list phantom WebSocket
    channels.

    To prevent the race, the send_event() macro now locks the websocket_pvt
    instance and checks the websocket session handle before attempting to send
    the MEDIA_START message.

    Resolves: #1643
    Resolves: #1645

diff --git a/channels/chan_websocket.c b/channels/chan_websocket.c
index 9b1db22298..4d86b86e53 100644
--- a/channels/chan_websocket.c
+++ b/channels/chan_websocket.c
@@ -416,7 +416,8 @@ static __attribute__ ((format (gnu_printf, 2, 3))) char *_create_event_ERROR(
 ({ \
 	int _res = -1; \
 	char *_payload = _create_event_ ## _event(_instance, ##__VA_ARGS__); \
-	if (_payload) { \
+	ao2_lock(instance); \
+	if (_payload && _instance->websocket) { \
 		_res = ast_websocket_write_string(_instance->websocket, _payload); \
 		if (_res != 0) { \
 			ast_log(LOG_ERROR, "%s: Unable to send event %s\n", \
@@ -427,6 +428,7 @@ static __attribute__ ((format (gnu_printf, 2, 3))) char *_create_event_ERROR(
 		}\
 		ast_free(_payload); \
 	} \
+	ao2_unlock(instance); \
 	(_res); \
 })