From a2cb58d1066dd6042c7dc5655f85987b7bb4c6a5 Mon Sep 17 00:00:00 2001
From: Yalu Zhang <yalu.zhang@iopsys.eu>
Date: Thu, 30 Nov 2023 14:43:12 +0100
Subject: [PATCH] Fix a memory corruption issue related to ubus_free

In libubus, there is global variable defined as below. ubus_free() calls ubus_shutdown() which frees b->buf.
which can cause memory corrupt when there is more than thread calls ubus_free() at the same time. In order
to avoid this, ubus_free() shall be called only once when the program is about to exit. In other cases,
ubus_free_context() shall be called to only free context specific resources but keep global buffer b->buf
untouched.
---
 src/channels/chan_voicemngr.c | 33 +++++++++++++++++++++++++++++----
 1 file changed, 29 insertions(+), 4 deletions(-)

diff --git a/src/channels/chan_voicemngr.c b/src/channels/chan_voicemngr.c
index 9fa24f3..11c7b1b 100644
--- a/src/channels/chan_voicemngr.c
+++ b/src/channels/chan_voicemngr.c
@@ -251,6 +251,25 @@ typedef struct shared_context {
 } share_context_t;
 static struct shared_context ctxs[MAX_CONTEXTS];
 
+/*
+ * In libubus, there is global variable defined as below. ubus_free() calls ubus_shutdown() which frees b->buf.
+ * which can cause memory corrupt when there is more than thread calls ubus_free() at the same time. In order
+ * to avoid this, ubus_free() shall be called only once when the program is about to exit. In other cases,
+ * ubus_free_context() shall be called to only free context specific resources but keep global buffer
+ * b->buf untouched.
+ *
+ * struct blob_buf b __hidden = {};
+ */
+static void ubus_free_context(struct ubus_context *ctx)
+{
+	if (!ctx)
+		return;
+	close(ctx->sock.fd);
+	uloop_timeout_cancel(&ctx->pending_timer);
+	free(ctx->msgbuf.data);
+	free(ctx);
+}
+
 static struct ubus_context *get_shared_context(const char *func) {
 
 	int tid = ast_get_tid();
@@ -268,7 +287,7 @@ static struct ubus_context *get_shared_context(const char *func) {
 	for(int i=0; i < MAX_CONTEXTS; i++) {
 		if (ctxs[i].tid != 0 && ctxs[i].last_used < time(NULL) - 300) {
 			ast_log(LOG_DEBUG, "tid=%d, expiring context %d", tid, i);
-			ubus_free(ctxs[i].ctx);
+			ubus_free_context(ctxs[i].ctx);
 			ctxs[i].tid = 0;
 			ctxs[i].last_used = 0;
 			ctxs[i].ctx = NULL;
@@ -673,7 +692,7 @@ static int chan_voicemngr_send_ubus_event(char *ev_name, int line)
 
 	memset(&blob, 0, sizeof(blob));
 	if(blob_buf_init(&blob, 0)) {
-		ubus_free(ubusctx);
+		ubus_free_context(ubusctx);
 		return -1;
 	}
 
@@ -685,7 +704,7 @@ static int chan_voicemngr_send_ubus_event(char *ev_name, int line)
 		res = -1;
 	}
 
-	ubus_free(ubusctx);
+	ubus_free_context(ubusctx);
 	blob_buf_free(&blob);
 
 	return res;
@@ -4363,6 +4382,12 @@ static int unload_module(void)
 	feature_access_code_clear();
 	ast_sched_context_destroy(sched);
 
+	/* Free the context and b->buf finally */
+	if (ctx) {
+		ubus_free(ctx);
+		ctx = NULL;
+	}
+
 	return 0;
 }
 
@@ -4903,7 +4928,7 @@ static int endpt_get_rtp_stats(int line) {
 	ret = ubus_invoke(local_ctx, endpt_id, "rtp_stats", bb.head, ubus_call_answer_rtp_stats, NULL, 500);
 
 	blob_buf_free(&bb);
-	ubus_free(local_ctx);
+	ubus_free_context(local_ctx);
 
 	if (ret != UBUS_STATUS_OK) {
 		ast_log(LOG_DEBUG, "ubus_invoke for rtp_stats failed with return value %d\n", ret);
-- 
GitLab