All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] gatmux: Remove finalized watches from the list
@ 2017-10-23  9:17 Slava Monich
  2017-10-23 20:27 ` Denis Kenzior
  0 siblings, 1 reply; 2+ messages in thread
From: Slava Monich @ 2017-10-23  9:17 UTC (permalink / raw)
  To: ofono

[-- Attachment #1: Type: text/plain, Size: 6507 bytes --]

Leaving them there may result in invalid reads like this:

==2312== Invalid read of size 4
==2312==    at 0xAB8C0: dispatch_sources (gatmux.c:134)
==2312==    by 0xAC5D3: channel_close (gatmux.c:479)
==2312==    by 0x4AE8885: g_io_channel_shutdown (giochannel.c:523)
==2312==    by 0x4AE8A1D: g_io_channel_unref (giochannel.c:240)
==2312==    by 0xAC423: watch_finalize (gatmux.c:426)
==2312==    by 0x4AF2CC9: g_source_unref_internal (gmain.c:2048)
==2312==    by 0x4AF44E1: g_source_destroy_internal (gmain.c:1230)
==2312==    by 0x4AF44E1: g_source_destroy (gmain.c:1256)
==2312==    by 0x4AF5257: g_source_remove (gmain.c:2282)
==2312==    by 0xAB5CB: io_shutdown (gatio.c:325)
==2312==    by 0xAB667: g_at_io_unref (gatio.c:345)
==2312==    by 0xA72C7: at_chat_unref (gatchat.c:972)
==2312==    by 0xA829B: g_at_chat_unref (gatchat.c:1446)
==2312==  Address 0x51420f0 is 56 bytes inside a block of size 60 free'd
==2312==    at 0x4840B28: free (vg_replace_malloc.c:530)
==2312==    by 0x4AF2D33: g_source_unref_internal (gmain.c:2075)
==2312==    by 0x4AF44E1: g_source_destroy_internal (gmain.c:1230)
==2312==    by 0x4AF44E1: g_source_destroy (gmain.c:1256)
==2312==    by 0x4AF5257: g_source_remove (gmain.c:2282)
==2312==    by 0xAB46B: g_at_io_set_write_handler (gatio.c:283)
==2312==    by 0xA713F: at_chat_suspend (gatchat.c:938)
==2312==    by 0xA72B7: at_chat_unref (gatchat.c:971)
==2312==    by 0xA829B: g_at_chat_unref (gatchat.c:1446)
==2312==  Block was alloc'd at
==2312==    at 0x4841BF0: calloc (vg_replace_malloc.c:711)
==2312==    by 0x4AFB117: g_malloc0 (gmem.c:124)
==2312==    by 0x4AF401F: g_source_new (gmain.c:892)
==2312==    by 0xAC6A7: channel_create_watch (gatmux.c:506)
==2312==    by 0x4AE7C4F: g_io_add_watch_full (giochannel.c:649)
==2312==    by 0xAB4EB: g_at_io_set_write_handler (gatio.c:297)
==2312==    by 0xA7103: chat_wakeup_writer (gatchat.c:931)
==2312==    by 0xA753F: at_chat_send_common (gatchat.c:1045)
==2312==    by 0xA850F: g_at_chat_send (gatchat.c:1502)

It's also necessary to add additional references to the sources
for the duration of the dispatch_sources loop because any source
can be removed when any callback is invoked (and not necessarily
the one being dispatched).
---
 gatchat/gatmux.c | 109 +++++++++++++++++++++++++++++++++++++++----------------
 1 file changed, 77 insertions(+), 32 deletions(-)

diff --git a/gatchat/gatmux.c b/gatchat/gatmux.c
index 0e275b5..909eca6 100644
--- a/gatchat/gatmux.c
+++ b/gatchat/gatmux.c
@@ -116,66 +116,109 @@ static inline void debug(GAtMux *mux, const char *format, ...)
 
 static void dispatch_sources(GAtMuxChannel *channel, GIOCondition condition)
 {
-	GAtMuxWatch *source;
 	GSList *c;
 	GSList *p;
-	GSList *t;
+	GSList *refs;
+
+	/*
+	 * Don't reference destroyed sources, they may have zero reference
+	 * count if this function is invoked from the source's finalize
+	 * callback, in which case incrementing and then decrementing
+	 * the count would result in double free (first when we decrement
+	 * the reference count and then when we return from the finalize
+	 * callback).
+	 */
 
 	p = NULL;
-	c = channel->sources;
+	refs = NULL;
 
-	while (c) {
-		gboolean destroy = FALSE;
+	for (c = channel->sources; c; c = c->next) {
+		GSource *s = c->data;
+
+		if (!g_source_is_destroyed(s)) {
+			GSList *l = g_slist_append(NULL, g_source_ref(s));
+
+			if (p)
+				p->next = l;
+			else
+				refs = l;
 
-		source = c->data;
+			p = l;
+		}
+	}
 
-		debug(channel->mux, "checking source: %p", source);
+	/*
+	 * Keep the references to all sources for the duration of the loop.
+	 * Callbacks may add and remove the sources, i.e. channel->sources
+	 * may keep changing during the loop.
+	 */
 
-		if (condition & source->condition) {
+	for (c = refs; c; c = c->next) {
+		GAtMuxWatch *w = c->data;
+		GSource *s = &w->source;
+
+		if (g_source_is_destroyed(s))
+			continue;
+
+		debug(channel->mux, "checking source: %p", s);
+
+		if (condition & w->condition) {
 			gpointer user_data = NULL;
 			GSourceFunc callback = NULL;
-			GSourceCallbackFuncs *cb_funcs;
-			gpointer cb_data;
-			gboolean (*dispatch) (GSource *, GSourceFunc, gpointer);
-
-			debug(channel->mux, "dispatching source: %p", source);
+			GSourceCallbackFuncs *cb_funcs = s->callback_funcs;
+			gpointer cb_data = s->callback_data;
+			gboolean destroy;
 
-			dispatch = source->source.source_funcs->dispatch;
-			cb_funcs = source->source.callback_funcs;
-			cb_data = source->source.callback_data;
+			debug(channel->mux, "dispatching source: %p", s);
 
-			if (cb_funcs)
+			if (cb_funcs) {
 				cb_funcs->ref(cb_data);
+				cb_funcs->get(cb_data, s, &callback,
+								&user_data);
+			}
 
-			if (cb_funcs)
-				cb_funcs->get(cb_data, (GSource *) source,
-						&callback, &user_data);
-
-			destroy = !dispatch((GSource *) source, callback,
-						user_data);
+			destroy = !s->source_funcs->dispatch(s, callback,
+								user_data);
 
 			if (cb_funcs)
 				cb_funcs->unref(cb_data);
+
+			if (destroy) {
+				debug(channel->mux, "removing source: %p", s);
+				g_source_destroy(s);
+			}
 		}
+	}
 
-		if (destroy) {
-			debug(channel->mux, "removing source: %p", source);
+	/*
+	 * Remove destroyed sources from channel->sources. During this
+	 * loop we are not invoking any callbacks, so the consistency is
+	 * guaranteed.
+	 */
 
-			g_source_destroy((GSource *) source);
+	p = NULL;
+	c = channel->sources;
 
+	while (c) {
+		GSList *n = c->next;
+		GSource *s = c->data;
+
+		if (g_source_is_destroyed(s)) {
 			if (p)
-				p->next = c->next;
+				p->next = n;
 			else
-				channel->sources = c->next;
+				channel->sources = n;
 
-			t = c;
-			c = c->next;
-			g_slist_free_1(t);
+			g_slist_free_1(c);
 		} else {
 			p = c;
-			c = c->next;
 		}
+
+		c = n;
 	}
+
+	/* Release temporary references */
+	g_slist_free_full(refs, (GDestroyNotify) g_source_unref);
 }
 
 static gboolean received_data(GIOChannel *channel, GIOCondition cond,
@@ -422,7 +465,9 @@ static gboolean watch_dispatch(GSource *source, GSourceFunc callback,
 static void watch_finalize(GSource *source)
 {
 	GAtMuxWatch *watch = (GAtMuxWatch *) source;
+	GAtMuxChannel *dlc = (GAtMuxChannel *) watch->channel;
 
+	dlc->sources = g_slist_remove(dlc->sources, watch);
 	g_io_channel_unref(watch->channel);
 }
 
-- 
1.9.1


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

* Re: [PATCH] gatmux: Remove finalized watches from the list
  2017-10-23  9:17 [PATCH] gatmux: Remove finalized watches from the list Slava Monich
@ 2017-10-23 20:27 ` Denis Kenzior
  0 siblings, 0 replies; 2+ messages in thread
From: Denis Kenzior @ 2017-10-23 20:27 UTC (permalink / raw)
  To: ofono

[-- Attachment #1: Type: text/plain, Size: 2605 bytes --]

Hi Slava,

On 10/23/2017 04:17 AM, Slava Monich wrote:
> Leaving them there may result in invalid reads like this:
> 
> ==2312== Invalid read of size 4
> ==2312==    at 0xAB8C0: dispatch_sources (gatmux.c:134)
> ==2312==    by 0xAC5D3: channel_close (gatmux.c:479)
> ==2312==    by 0x4AE8885: g_io_channel_shutdown (giochannel.c:523)
> ==2312==    by 0x4AE8A1D: g_io_channel_unref (giochannel.c:240)
> ==2312==    by 0xAC423: watch_finalize (gatmux.c:426)
> ==2312==    by 0x4AF2CC9: g_source_unref_internal (gmain.c:2048)
> ==2312==    by 0x4AF44E1: g_source_destroy_internal (gmain.c:1230)
> ==2312==    by 0x4AF44E1: g_source_destroy (gmain.c:1256)
> ==2312==    by 0x4AF5257: g_source_remove (gmain.c:2282)
> ==2312==    by 0xAB5CB: io_shutdown (gatio.c:325)
> ==2312==    by 0xAB667: g_at_io_unref (gatio.c:345)
> ==2312==    by 0xA72C7: at_chat_unref (gatchat.c:972)
> ==2312==    by 0xA829B: g_at_chat_unref (gatchat.c:1446)
> ==2312==  Address 0x51420f0 is 56 bytes inside a block of size 60 free'd
> ==2312==    at 0x4840B28: free (vg_replace_malloc.c:530)
> ==2312==    by 0x4AF2D33: g_source_unref_internal (gmain.c:2075)
> ==2312==    by 0x4AF44E1: g_source_destroy_internal (gmain.c:1230)
> ==2312==    by 0x4AF44E1: g_source_destroy (gmain.c:1256)
> ==2312==    by 0x4AF5257: g_source_remove (gmain.c:2282)
> ==2312==    by 0xAB46B: g_at_io_set_write_handler (gatio.c:283)
> ==2312==    by 0xA713F: at_chat_suspend (gatchat.c:938)
> ==2312==    by 0xA72B7: at_chat_unref (gatchat.c:971)
> ==2312==    by 0xA829B: g_at_chat_unref (gatchat.c:1446)
> ==2312==  Block was alloc'd at
> ==2312==    at 0x4841BF0: calloc (vg_replace_malloc.c:711)
> ==2312==    by 0x4AFB117: g_malloc0 (gmem.c:124)
> ==2312==    by 0x4AF401F: g_source_new (gmain.c:892)
> ==2312==    by 0xAC6A7: channel_create_watch (gatmux.c:506)
> ==2312==    by 0x4AE7C4F: g_io_add_watch_full (giochannel.c:649)
> ==2312==    by 0xAB4EB: g_at_io_set_write_handler (gatio.c:297)
> ==2312==    by 0xA7103: chat_wakeup_writer (gatchat.c:931)
> ==2312==    by 0xA753F: at_chat_send_common (gatchat.c:1045)
> ==2312==    by 0xA850F: g_at_chat_send (gatchat.c:1502)
> 
> It's also necessary to add additional references to the sources
> for the duration of the dispatch_sources loop because any source
> can be removed when any callback is invoked (and not necessarily
> the one being dispatched).
> ---
>   gatchat/gatmux.c | 109 +++++++++++++++++++++++++++++++++++++++----------------
>   1 file changed, 77 insertions(+), 32 deletions(-)
> 

Applied, thanks.

Regards,
-Denis


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

end of thread, other threads:[~2017-10-23 20:27 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-10-23  9:17 [PATCH] gatmux: Remove finalized watches from the list Slava Monich
2017-10-23 20:27 ` Denis Kenzior

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.