From 40ce4345122ee98c23176e3d5f6134870980ec50 Mon Sep 17 00:00:00 2001 From: Mike Bradeen Date: Thu, 13 Oct 2022 10:19:17 -0600 Subject: [PATCH] res_pjsip: prevent crash on websocket disconnect When a websocket (or potentially any stateful connection) is quickly created then destroyed, it is possible that the qualify thread will destroy the transaction before the initialzing thread is finished with it. Depending on the timing, this can cause an assertion within pjsip. To prevent this, ast_send_stateful_response will now create the group lock and add a reference to it before creating the transaction. While this should resolve the crash, there is still the potential that the contact will not be cleaned up properly, see:ASTERISK~29286. As a result, the contact has to 'time out' before it will be removed. ASTERISK-28689 Change-Id: Id050fded2247a04d8f0fc5b8a2cf3e5482cb8cee --- res/res_pjsip.c | 47 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 39 insertions(+), 8 deletions(-) diff --git a/res/res_pjsip.c b/res/res_pjsip.c index 0ad5ec23b5..208a277f5a 100644 --- a/res/res_pjsip.c +++ b/res/res_pjsip.c @@ -2258,31 +2258,62 @@ int ast_sip_send_response(pjsip_response_addr *res_addr, pjsip_tx_data *tdata, s return status == PJ_SUCCESS ? 0 : -1; } +static void pool_destroy_callback(void *arg) +{ + pj_pool_t *pool = (pj_pool_t *)arg; + pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool); +} + +static void clean_contact_from_tdata(pjsip_tx_data *tdata) +{ + struct ast_sip_contact *contact; + contact = ast_sip_mod_data_get(tdata->mod_data, supplement_module.id, MOD_DATA_CONTACT); + ao2_cleanup(contact); + ast_sip_mod_data_set(tdata->pool, tdata->mod_data, supplement_module.id, MOD_DATA_CONTACT, NULL); + pjsip_tx_data_dec_ref(tdata); +} + int ast_sip_send_stateful_response(pjsip_rx_data *rdata, pjsip_tx_data *tdata, struct ast_sip_endpoint *sip_endpoint) { pjsip_transaction *tsx; + pj_grp_lock_t *tsx_glock; + pj_pool_t *pool; - if (pjsip_tsx_create_uas(NULL, rdata, &tsx) != PJ_SUCCESS) { - struct ast_sip_contact *contact; - + /* Create and initialize global lock pool */ + pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(), "stateful response", PJSIP_POOL_TSX_LEN, PJSIP_POOL_TSX_INC); + if (!pool){ /* ast_sip_create_response bumps the refcount of the contact and adds it to the tdata. * We'll leak that reference if we don't get rid of it here. */ - contact = ast_sip_mod_data_get(tdata->mod_data, supplement_module.id, MOD_DATA_CONTACT); - ao2_cleanup(contact); - ast_sip_mod_data_set(tdata->pool, tdata->mod_data, supplement_module.id, MOD_DATA_CONTACT, NULL); - pjsip_tx_data_dec_ref(tdata); + clean_contact_from_tdata(tdata); return -1; } - pjsip_tsx_recv_msg(tsx, rdata); + /* Create with handler so that we can release the pool once the glock derefs out */ + if(pj_grp_lock_create_w_handler(pool, NULL, pool, &pool_destroy_callback, &tsx_glock) != PJ_SUCCESS) { + clean_contact_from_tdata(tdata); + pool_destroy_callback((void *) pool); + return -1; + } + /* We need an additional reference as the qualify thread may destroy this out + * from under us. Add it now before it gets added to the tsx. */ + pj_grp_lock_add_ref(tsx_glock); + if (pjsip_tsx_create_uas2(NULL, rdata, tsx_glock, &tsx) != PJ_SUCCESS) { + clean_contact_from_tdata(tdata); + pj_grp_lock_dec_ref(tsx_glock); + return -1; + } + + pjsip_tsx_recv_msg(tsx, rdata); supplement_outgoing_response(tdata, sip_endpoint); if (pjsip_tsx_send_msg(tsx, tdata) != PJ_SUCCESS) { + pj_grp_lock_dec_ref(tsx_glock); pjsip_tx_data_dec_ref(tdata); return -1; } + pj_grp_lock_dec_ref(tsx_glock); return 0; }