]> git.ipfire.org Git - thirdparty/dbus.git/commitdiff
DBusCounter: add a mutex to protect the refcount and notify function
authorAdrian Szyndela <adrian.s@samsung.com>
Tue, 5 May 2015 11:30:30 +0000 (12:30 +0100)
committerSimon McVittie <simon.mcvittie@collabora.co.uk>
Tue, 5 May 2015 11:30:30 +0000 (12:30 +0100)
The overall problem here is that DBusCounter is indirectly linked
to a DBusConnection, but is not actually guaranteed to be protected by
that connection's mutex; and a DBusMessage can carry a reference to the
DBusCounter, resulting in freeing that DBusMessage having an effect on
the DBusCounter.

Making the refcount atomic would not be a sufficient fix, since it would
not protect the notify function: _dbus_counter_notify() could be called
indirectly by dbus_message_unref(), in an arbitrary thread that does not
hold the DBusConnection's lock, at the same time that the holder
of the DBusConnection lock calls _dbus_transport_set_max_message_size().

[smcv: added commit message]
Bug: https://bugs.freedesktop.org/show_bug.cgi?id=89297
Reviewed-by: Simon McVittie <simon.mcvittie@collabora.co.uk>
dbus/dbus-resources.c

index 80fb55b2fa2f6a2c24182838f00400ebb221346d..0617eae23652d640f1174e157c976e2c5dc320ec 100644 (file)
@@ -69,6 +69,7 @@ struct DBusCounter
   DBusCounterNotifyFunction notify_function; /**< notify function */
   void *notify_data; /**< data for notify function */
   dbus_bool_t notify_pending : 1; /**< TRUE if the guard value has been crossed */
+  DBusRMutex *mutex;    /**< Lock on the entire DBusCounter */
 };
 
 /** @} */  /* end of resource limits internals docs */
@@ -95,6 +96,13 @@ _dbus_counter_new (void)
 
   counter->refcount = 1;
 
+  _dbus_rmutex_new_at_location (&counter->mutex);
+  if (counter->mutex == NULL)
+  {
+    dbus_free (counter);
+    counter = NULL;
+  }
+
   return counter;
 }
 
@@ -107,10 +115,14 @@ _dbus_counter_new (void)
 DBusCounter *
 _dbus_counter_ref (DBusCounter *counter)
 {
+  _dbus_rmutex_lock (counter->mutex);
+
   _dbus_assert (counter->refcount > 0);
   
   counter->refcount += 1;
 
+  _dbus_rmutex_unlock (counter->mutex);
+
   return counter;
 }
 
@@ -123,13 +135,20 @@ _dbus_counter_ref (DBusCounter *counter)
 void
 _dbus_counter_unref (DBusCounter *counter)
 {
+  dbus_bool_t last_ref = FALSE;
+
+  _dbus_rmutex_lock (counter->mutex);
+
   _dbus_assert (counter->refcount > 0);
 
   counter->refcount -= 1;
+  last_ref = (counter->refcount == 0);
+
+  _dbus_rmutex_unlock (counter->mutex);
 
-  if (counter->refcount == 0)
+  if (last_ref)
     {
-      
+      _dbus_rmutex_free_at_location (&counter->mutex);
       dbus_free (counter);
     }
 }
@@ -148,7 +167,11 @@ void
 _dbus_counter_adjust_size (DBusCounter *counter,
                            long         delta)
 {
-  long old = counter->size_value;
+  long old = 0;
+
+  _dbus_rmutex_lock (counter->mutex);
+
+  old = counter->size_value;
 
   counter->size_value += delta;
 
@@ -168,6 +191,8 @@ _dbus_counter_adjust_size (DBusCounter *counter,
        (old >= counter->notify_size_guard_value &&
         counter->size_value < counter->notify_size_guard_value)))
     counter->notify_pending = TRUE;
+
+  _dbus_rmutex_unlock (counter->mutex);
 }
 
 /**
@@ -181,11 +206,20 @@ _dbus_counter_adjust_size (DBusCounter *counter,
 void
 _dbus_counter_notify (DBusCounter *counter)
 {
+  DBusCounterNotifyFunction notify_function = NULL;
+  void *notify_data = NULL;
+
+  _dbus_rmutex_lock (counter->mutex);
   if (counter->notify_pending)
     {
       counter->notify_pending = FALSE;
-      (* counter->notify_function) (counter, counter->notify_data);
+      notify_function = counter->notify_function;
+      notify_data = counter->notify_data;
     }
+  _dbus_rmutex_unlock (counter->mutex);
+
+  if (notify_function != NULL)
+    (* notify_function) (counter, notify_data);
 }
 
 /**
@@ -202,7 +236,11 @@ void
 _dbus_counter_adjust_unix_fd (DBusCounter *counter,
                               long         delta)
 {
-  long old = counter->unix_fd_value;
+  long old = 0;
+
+  _dbus_rmutex_lock (counter->mutex);
+
+  old = counter->unix_fd_value;
   
   counter->unix_fd_value += delta;
 
@@ -222,6 +260,8 @@ _dbus_counter_adjust_unix_fd (DBusCounter *counter,
        (old >= counter->notify_unix_fd_guard_value &&
         counter->unix_fd_value < counter->notify_unix_fd_guard_value)))
     counter->notify_pending = TRUE;
+
+  _dbus_rmutex_unlock (counter->mutex);
 }
 
 /**
@@ -266,11 +306,13 @@ _dbus_counter_set_notify (DBusCounter               *counter,
                           DBusCounterNotifyFunction  function,
                           void                      *user_data)
 {
+  _dbus_rmutex_lock (counter->mutex);
   counter->notify_size_guard_value = size_guard_value;
   counter->notify_unix_fd_guard_value = unix_fd_guard_value;
   counter->notify_function = function;
   counter->notify_data = user_data;
   counter->notify_pending = FALSE;
+  _dbus_rmutex_unlock (counter->mutex);
 }
 
 #ifdef DBUS_ENABLE_STATS