Open Source Telephony
 help / color / mirror / Atom feed
* [PATCH] gatchat: fix g_at_chat_unref in notify handler
@ 2024-01-23 15:00 Maxim Lyubimov
  2024-01-24 16:47 ` [PATCH v3] " Denis Kenzior
  2024-01-24 16:52 ` [PATCH] " Denis Kenzior
  0 siblings, 2 replies; 4+ messages in thread
From: Maxim Lyubimov @ 2024-01-23 15:00 UTC (permalink / raw)
  To: ofono; +Cc: Maxim Lyubimov

If the new_bytes handler processes a modem shutdown URC, it results in
a call to the g_at_chat_unref function, which includes a call to the
chat_cleanup function, which clears the list of URC handlers that
continue to be traversed after the current handler has completed, and
may lead to errors. To correct the situation, the chat_cleanup function
is not called in the at_chat_unref function if the in_read_handler flag
is set. In the new_bytes function, if the destroyed flag is set, the
chat_cleanup function is called before calling g_free. Additional
checks have been added to the chat_cleanup function.
---
 gatchat/gatchat.c | 102 +++++++++++++++++++++++++++-------------------
 1 file changed, 60 insertions(+), 42 deletions(-)

diff --git a/gatchat/gatchat.c b/gatchat/gatchat.c
index 9e777107..50f8ba57 100644
--- a/gatchat/gatchat.c
+++ b/gatchat/gatchat.c
@@ -319,19 +319,25 @@ static void chat_cleanup(struct at_chat *chat)
 	struct at_command *c;
 
 	/* Cleanup pending commands */
-	while ((c = g_queue_pop_head(chat->command_queue)))
-		at_command_destroy(c);
+	if (chat->command_queue) {
+		while ((c = g_queue_pop_head(chat->command_queue)))
+			at_command_destroy(c);
 
-	g_queue_free(chat->command_queue);
-	chat->command_queue = NULL;
+		g_queue_free(chat->command_queue);
+		chat->command_queue = NULL;
+	}
 
 	/* Cleanup any response lines we have pending */
-	g_slist_free_full(chat->response_lines, g_free);
-	chat->response_lines = NULL;
+	if (chat->response_lines) {
+		g_slist_free_full(chat->response_lines, g_free);
+		chat->response_lines = NULL;
+	}
 
 	/* Cleanup registered notifications */
-	g_hash_table_destroy(chat->notify_list);
-	chat->notify_list = NULL;
+	if (chat->notify_list) {
+		g_hash_table_destroy(chat->notify_list);
+		chat->notify_list = NULL;
+	}
 
 	if (chat->pdu_notify) {
 		g_free(chat->pdu_notify);
@@ -727,6 +733,48 @@ static char *extract_line(struct at_chat *p, struct ring_buffer *rbuf)
 	return line;
 }
 
+static void at_chat_suspend(struct at_chat *chat)
+{
+	chat->suspended = TRUE;
+
+	g_at_io_set_write_handler(chat->io, NULL, NULL);
+	g_at_io_set_read_handler(chat->io, NULL, NULL);
+	g_at_io_set_debug(chat->io, NULL, NULL);
+}
+
+static struct at_chat *at_chat_ref(struct at_chat *chat)
+{
+	if (chat == NULL)
+		return NULL;
+
+	g_atomic_int_inc(&chat->ref_count);
+
+	return chat;
+}
+
+static void at_chat_unref(struct at_chat *chat)
+{
+	gboolean is_zero;
+
+	is_zero = g_atomic_int_dec_and_test(&chat->ref_count);
+
+	if (is_zero == FALSE)
+		return;
+
+	if (chat->io) {
+		at_chat_suspend(chat);
+		g_at_io_unref(chat->io);
+		chat->io = NULL;
+	}
+
+	if (chat->in_read_handler)
+		chat->destroyed = TRUE;
+	else {
+		chat_cleanup(chat);
+		g_free(chat);
+	}
+}
+
 static void new_bytes(struct ring_buffer *rbuf, gpointer user_data)
 {
 	struct at_chat *p = user_data;
@@ -780,8 +828,10 @@ static void new_bytes(struct ring_buffer *rbuf, gpointer user_data)
 
 	p->in_read_handler = FALSE;
 
-	if (p->destroyed)
+	if (p->destroyed) {
+		chat_cleanup(p);
 		g_free(p);
+	}
 }
 
 static void wakeup_cb(gboolean ok, GAtResult *result, gpointer user_data)
@@ -931,15 +981,6 @@ static void chat_wakeup_writer(struct at_chat *chat)
 	g_at_io_set_write_handler(chat->io, can_write_data, chat);
 }
 
-static void at_chat_suspend(struct at_chat *chat)
-{
-	chat->suspended = TRUE;
-
-	g_at_io_set_write_handler(chat->io, NULL, NULL);
-	g_at_io_set_read_handler(chat->io, NULL, NULL);
-	g_at_io_set_debug(chat->io, NULL, NULL);
-}
-
 static void at_chat_resume(struct at_chat *chat)
 {
 	chat->suspended = FALSE;
@@ -958,28 +999,6 @@ static void at_chat_resume(struct at_chat *chat)
 		chat_wakeup_writer(chat);
 }
 
-static void at_chat_unref(struct at_chat *chat)
-{
-	gboolean is_zero;
-
-	is_zero = g_atomic_int_dec_and_test(&chat->ref_count);
-
-	if (is_zero == FALSE)
-		return;
-
-	if (chat->io) {
-		at_chat_suspend(chat);
-		g_at_io_unref(chat->io);
-		chat->io = NULL;
-		chat_cleanup(chat);
-	}
-
-	if (chat->in_read_handler)
-		chat->destroyed = TRUE;
-	else
-		g_free(chat);
-}
-
 static gboolean at_chat_set_disconnect_function(struct at_chat *chat,
 						GAtDisconnectFunc disconnect,
 						gpointer user_data)
@@ -1372,10 +1391,9 @@ GAtChat *g_at_chat_clone(GAtChat *clone)
 	if (chat == NULL)
 		return NULL;
 
-	chat->parent = clone->parent;
+	chat->parent = at_chat_ref(clone->parent);
 	chat->group = chat->parent->next_gid++;
 	chat->ref_count = 1;
-	g_atomic_int_inc(&chat->parent->ref_count);
 
 	if (clone->slave != NULL)
 		chat->slave = g_at_chat_clone(clone->slave);
-- 
2.28.0


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH v3] gatchat: fix g_at_chat_unref in notify handler
  2024-01-23 15:00 [PATCH] gatchat: fix g_at_chat_unref in notify handler Maxim Lyubimov
@ 2024-01-24 16:47 ` Denis Kenzior
  2024-01-25 22:40   ` patchwork-bot+ofono
  2024-01-24 16:52 ` [PATCH] " Denis Kenzior
  1 sibling, 1 reply; 4+ messages in thread
From: Denis Kenzior @ 2024-01-24 16:47 UTC (permalink / raw)
  To: ofono; +Cc: Maxim Lyubimov

From: Maxim Lyubimov <m.lyubimov@aqsi.ru>

If the new_bytes handler processes a modem shutdown URC, it results in
a call to the g_at_chat_unref function, which includes a call to the
chat_cleanup function, which clears the list of URC handlers that
continue to be traversed after the current handler has completed, and
may lead to errors. To correct the situation, the chat_cleanup function
is not called in the at_chat_unref function if the in_read_handler flag
is set. In the new_bytes function, if the destroyed flag is set, the
chat_cleanup function is called before calling g_free. Additional
checks have been added to the chat_cleanup function.
---
 gatchat/gatchat.c | 44 +++++++++++++++++++++++++++++++-------------
 1 file changed, 31 insertions(+), 13 deletions(-)

diff --git a/gatchat/gatchat.c b/gatchat/gatchat.c
index 9e777107..dd910915 100644
--- a/gatchat/gatchat.c
+++ b/gatchat/gatchat.c
@@ -319,19 +319,25 @@ static void chat_cleanup(struct at_chat *chat)
 	struct at_command *c;
 
 	/* Cleanup pending commands */
-	while ((c = g_queue_pop_head(chat->command_queue)))
-		at_command_destroy(c);
+	if (chat->command_queue) {
+		while ((c = g_queue_pop_head(chat->command_queue)))
+			at_command_destroy(c);
 
-	g_queue_free(chat->command_queue);
-	chat->command_queue = NULL;
+		g_queue_free(chat->command_queue);
+		chat->command_queue = NULL;
+	}
 
 	/* Cleanup any response lines we have pending */
-	g_slist_free_full(chat->response_lines, g_free);
-	chat->response_lines = NULL;
+	if (chat->response_lines) {
+		g_slist_free_full(chat->response_lines, g_free);
+		chat->response_lines = NULL;
+	}
 
 	/* Cleanup registered notifications */
-	g_hash_table_destroy(chat->notify_list);
-	chat->notify_list = NULL;
+	if (chat->notify_list) {
+		g_hash_table_destroy(chat->notify_list);
+		chat->notify_list = NULL;
+	}
 
 	if (chat->pdu_notify) {
 		g_free(chat->pdu_notify);
@@ -780,8 +786,10 @@ static void new_bytes(struct ring_buffer *rbuf, gpointer user_data)
 
 	p->in_read_handler = FALSE;
 
-	if (p->destroyed)
+	if (p->destroyed) {
+		chat_cleanup(p);
 		g_free(p);
+	}
 }
 
 static void wakeup_cb(gboolean ok, GAtResult *result, gpointer user_data)
@@ -958,6 +966,16 @@ static void at_chat_resume(struct at_chat *chat)
 		chat_wakeup_writer(chat);
 }
 
+static struct at_chat *at_chat_ref(struct at_chat *chat)
+{
+	if (chat == NULL)
+		return NULL;
+
+	g_atomic_int_inc(&chat->ref_count);
+
+	return chat;
+}
+
 static void at_chat_unref(struct at_chat *chat)
 {
 	gboolean is_zero;
@@ -971,13 +989,14 @@ static void at_chat_unref(struct at_chat *chat)
 		at_chat_suspend(chat);
 		g_at_io_unref(chat->io);
 		chat->io = NULL;
-		chat_cleanup(chat);
 	}
 
 	if (chat->in_read_handler)
 		chat->destroyed = TRUE;
-	else
+	else {
+		chat_cleanup(chat);
 		g_free(chat);
+	}
 }
 
 static gboolean at_chat_set_disconnect_function(struct at_chat *chat,
@@ -1372,10 +1391,9 @@ GAtChat *g_at_chat_clone(GAtChat *clone)
 	if (chat == NULL)
 		return NULL;
 
-	chat->parent = clone->parent;
+	chat->parent = at_chat_ref(clone->parent);
 	chat->group = chat->parent->next_gid++;
 	chat->ref_count = 1;
-	g_atomic_int_inc(&chat->parent->ref_count);
 
 	if (clone->slave != NULL)
 		chat->slave = g_at_chat_clone(clone->slave);
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH] gatchat: fix g_at_chat_unref in notify handler
  2024-01-23 15:00 [PATCH] gatchat: fix g_at_chat_unref in notify handler Maxim Lyubimov
  2024-01-24 16:47 ` [PATCH v3] " Denis Kenzior
@ 2024-01-24 16:52 ` Denis Kenzior
  1 sibling, 0 replies; 4+ messages in thread
From: Denis Kenzior @ 2024-01-24 16:52 UTC (permalink / raw)
  To: Maxim Lyubimov, ofono

Hi Maxim,

On 1/23/24 09:00, Maxim Lyubimov wrote:
> If the new_bytes handler processes a modem shutdown URC, it results in
> a call to the g_at_chat_unref function, which includes a call to the
> chat_cleanup function, which clears the list of URC handlers that
> continue to be traversed after the current handler has completed, and
> may lead to errors. To correct the situation, the chat_cleanup function
> is not called in the at_chat_unref function if the in_read_handler flag
> is set. In the new_bytes function, if the destroyed flag is set, the
> chat_cleanup function is called before calling g_free. Additional
> checks have been added to the chat_cleanup function.
> ---
>   gatchat/gatchat.c | 102 +++++++++++++++++++++++++++-------------------
>   1 file changed, 60 insertions(+), 42 deletions(-)
> 

Looks good.  There's no need to move some of these functions around now though. 
I sent a (much smaller) v3 of this patch so it goes through CI.  Please take a 
look as well.

Regards,
-Denis


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH v3] gatchat: fix g_at_chat_unref in notify handler
  2024-01-24 16:47 ` [PATCH v3] " Denis Kenzior
@ 2024-01-25 22:40   ` patchwork-bot+ofono
  0 siblings, 0 replies; 4+ messages in thread
From: patchwork-bot+ofono @ 2024-01-25 22:40 UTC (permalink / raw)
  To: Denis Kenzior; +Cc: ofono, m.lyubimov

Hello:

This patch was applied to ofono.git (master)
by Denis Kenzior <denkenz@gmail.com>:

On Wed, 24 Jan 2024 10:47:07 -0600 you wrote:
> From: Maxim Lyubimov <m.lyubimov@aqsi.ru>
> 
> If the new_bytes handler processes a modem shutdown URC, it results in
> a call to the g_at_chat_unref function, which includes a call to the
> chat_cleanup function, which clears the list of URC handlers that
> continue to be traversed after the current handler has completed, and
> may lead to errors. To correct the situation, the chat_cleanup function
> is not called in the at_chat_unref function if the in_read_handler flag
> is set. In the new_bytes function, if the destroyed flag is set, the
> chat_cleanup function is called before calling g_free. Additional
> checks have been added to the chat_cleanup function.
> 
> [...]

Here is the summary with links:
  - [v3] gatchat: fix g_at_chat_unref in notify handler
    https://git.kernel.org/pub/scm/network/ofono/ofono.git/?id=c994bc96d9c5

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2024-01-25 22:40 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-01-23 15:00 [PATCH] gatchat: fix g_at_chat_unref in notify handler Maxim Lyubimov
2024-01-24 16:47 ` [PATCH v3] " Denis Kenzior
2024-01-25 22:40   ` patchwork-bot+ofono
2024-01-24 16:52 ` [PATCH] " Denis Kenzior

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox