[at-spi2-core] Fix handling of event listener removals during an event callback



commit 4f53ce08cca0306829d46602a52fc7cfe11cf635
Author: Mike Gorse <mgorse suse com>
Date:   Wed Sep 2 17:11:15 2020 -0500

    Fix handling of event listener removals during an event callback
    
    We should not modify event_listeners while we are iterating through it.
    Otherwise, we may crash. If an event listener is removed from within an event
    callback, then defer the removal until the callback has ended.

 atspi/atspi-event-listener.c | 31 +++++++++++++++++++++++++++++--
 1 file changed, 29 insertions(+), 2 deletions(-)
---
diff --git a/atspi/atspi-event-listener.c b/atspi/atspi-event-listener.c
index 464177f1..82e39b6d 100644
--- a/atspi/atspi-event-listener.c
+++ b/atspi/atspi-event-listener.c
@@ -166,6 +166,9 @@ atspi_event_listener_new_simple (AtspiEventListenerSimpleCB callback,
 }
 
 static GList *event_listeners = NULL;
+static GList *pending_removals = NULL;
+static int in_send = 0;
+
 
 static gchar *
 convert_name_from_dbus (const char *name, gboolean path_hack)
@@ -788,7 +791,10 @@ atspi_event_listener_deregister_from_callback (AtspiEventListenerCB callback,
     {
       DBusMessage *message, *reply;
       l = g_list_next (l);
-      event_listeners = g_list_remove (event_listeners, e);
+      if (in_send)
+      pending_removals = g_list_append (pending_removals, e);
+      else
+        event_listeners = g_list_remove (event_listeners, e);
       for (i = 0; i < matchrule_array->len; i++)
       {
        char *matchrule = g_ptr_array_index (matchrule_array, i);
@@ -805,7 +811,8 @@ atspi_event_listener_deregister_from_callback (AtspiEventListenerCB callback,
       if (reply)
         dbus_message_unref (reply);
 
-      listener_entry_free (e);
+      if (!in_send)
+        listener_entry_free (e);
     }
     else
       l = g_list_next (l);
@@ -880,6 +887,13 @@ detail_matches_listener (const char *event_detail, const char *listener_detail)
                : strcmp (listener_detail, event_detail));
 }
 
+static void
+resolve_pending_removal (gpointer data)
+{
+  event_listeners = g_list_remove (event_listeners, data);
+  listener_entry_free (data);
+}
+
 void
 _atspi_send_event (AtspiEvent *e)
 {
@@ -900,6 +914,7 @@ _atspi_send_event (AtspiEvent *e)
     g_warning ("AT-SPI: Couldn't parse event: %s\n", e->type);
     return;
   }
+  in_send++;
   for (l = event_listeners; l; l = g_list_next (l))
   {
     EventListenerEntry *entry = l->data;
@@ -915,16 +930,28 @@ _atspi_send_event (AtspiEvent *e)
           break;
       }
       if (!l2)
+      {
+        for (l2 = pending_removals; l2; l2 = l2->next)
+        {
+        if (l2->data == entry)
+          break;
+        }
+      }
+      if (!l2)
       {
         entry->callback (atspi_event_copy (e), entry->user_data);
         called_listeners = g_list_prepend (called_listeners, entry);
       }
     }
   }
+  in_send--;
   if (detail) g_free (detail);
   g_free (name);
   g_free (category);
   g_list_free (called_listeners);
+
+  g_list_free_full (pending_removals, resolve_pending_removal);
+  pending_removals = NULL;
 }
 
 DBusHandlerResult


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]