From eb1e9cdcdee58b370621e15d5866535d3bf3c238 Mon Sep 17 00:00:00 2001 From: Norm Brandinger Date: Sat, 7 Mar 2026 18:33:39 +0000 Subject: [PATCH] janus: fix pkg memory leaks in cJSON_Print/cJSON_Parse paths The janus module uses cJSON_InitHooks() to route all cJSON allocations through OpenSIPS pkg_malloc. Three call sites had missing cleanup: - janus_ipc_send_request(): cJSON_Print() result was copied to shm via shm_nt_str_dup() but the pkg-allocated original was never freed. Also added a NULL check -- under pkg exhaustion cJSON_Print returns NULL and the subsequent strlen(NULL) causes a crash. - w_janus_send_request(): the cJSON tree from cJSON_Parse() was passed to janus_ipc_send_request() (which serializes it to shm) but cJSON_Delete() was never called afterward. Also added cleanup on the get_janus_connection_by_id() failure path. - janus_raise_event() and handle_janus_json_request(): added NULL checks after cJSON_Print(). Fixed missing pkg_free(full_json) on the shm_strdup() failure path in handle_janus_json_request(). Together these leak ~350 bytes of pkg memory per janus_send_request() call, leading to SIP worker pkg exhaustion and crash under sustained load. Fixes #3712 --- modules/janus/janus_common.c | 9 +++++++++ modules/janus/janus_mod.c | 2 ++ modules/janus/janus_proc.c | 9 +++++++++ 3 files changed, 20 insertions(+) diff --git a/modules/janus/janus_common.c b/modules/janus/janus_common.c index f0c45ed9d5d..13432a562e1 100644 --- a/modules/janus/janus_common.c +++ b/modules/janus/janus_common.c @@ -112,6 +112,10 @@ int janus_raise_event(janus_connection *conn, cJSON *request) } full_json = cJSON_Print(request); + if (!full_json) { + LM_ERR("cJSON_Print failed\n"); + goto err_free_params; + } cJSON_Minify(full_json); full_json_s.s = full_json; full_json_s.len = strlen(full_json); @@ -191,10 +195,15 @@ int handle_janus_json_request(janus_connection *conn, cJSON *request) } full_json = cJSON_Print(request); + if (!full_json) { + LM_ERR("cJSON_Print failed\n"); + return 1; + } cJSON_Minify(full_json); reply->text.s = shm_strdup(full_json); if (reply->text.s == NULL) { + pkg_free(full_json); /* we're out of mem, let the requestor timeout, don't disconnect janus */ return 1; } diff --git a/modules/janus/janus_mod.c b/modules/janus/janus_mod.c index c0e1cd83077..cc856fa7ac3 100644 --- a/modules/janus/janus_mod.c +++ b/modules/janus/janus_mod.c @@ -208,12 +208,14 @@ static int w_janus_send_request(struct sip_msg *msg, str *janus_id,str *request, if ((conn = get_janus_connection_by_id(janus_id)) == NULL) { LM_ERR("Unknown JANUS ID %.*s\n",janus_id->len,janus_id->s); + cJSON_Delete(j_request); return -1; } LM_DBG("Found our conn, prep to send out %.*s !! \n",request->len,request->s); reply_id = janus_ipc_send_request(conn,j_request); + cJSON_Delete(j_request); /* tree was serialized to shm; free pkg copy */ if (reply_id == 0) { LM_ERR("Failed to queue request %.*s towards %.*s\n", request->len,request->s, diff --git a/modules/janus/janus_proc.c b/modules/janus/janus_proc.c index 8f30bf7e620..299492ae7c2 100644 --- a/modules/janus/janus_proc.c +++ b/modules/janus/janus_proc.c @@ -282,14 +282,23 @@ uint64_t janus_ipc_send_request(janus_connection *sock, cJSON *janus_cmd) lock_stop_write(sock->lists_lk); full_cmd.s = cJSON_Print(janus_cmd); + if (!full_cmd.s) { + shm_free(cmd); + LM_ERR("cJSON_Print failed (pkg OOM)\n"); + return 0; + } full_cmd.len = strlen(full_cmd.s); if (shm_nt_str_dup(&cmd->janus_cmd, &full_cmd) != 0) { + pkg_free(full_cmd.s); shm_free(cmd); LM_ERR("oom\n"); return 0; } + /* cJSON_Print() allocates from pkg via module hooks; free after shm copy */ + pkg_free(full_cmd.s); + janus_transaction_id = cmd->janus_transaction_id; if (ipc_send_job(*janus_mgr_process_no, ipc_hdl_run_janus, cmd) != 0) {