eldbus: Fix crash when removing the last reference of the message container inside of the message callback

If user try to remove the last reference of proxy, object, connection
or eldbus(lib) inside of message callback it was causing the
eldbus_pending_dispatch() being called 2 times, one because of the
eldbus_cancel() that is triggered when the last reference of the
message parent is removed and another after the return of the user
callback.

==6545== Invalid read of size 8
==6545==    at 0x52F784E: eldbus_cbs_free_dispatch (eldbus_core.c:266)
==6545==    by 0x53064AA: eldbus_pending_dispatch (eldbus_pending.c:227)
==6545==    by 0x5305961: cb_pending (eldbus_pending.c:74)
==6545==    by 0x6B29DB1: ??? (in /usr/lib/libdbus-1.so.3.8.9)
==6545==    by 0x6B2D280: dbus_connection_dispatch (in /usr/lib/libdbus-1.so.3.8.9)
==6545==    by 0x52F93B4: eldbus_idler (eldbus_core.c:773)
==6545==    by 0x4E4B300: _ecore_call_task_cb (ecore_private.h:305)
==6545==    by 0x4E4B78F: _ecore_idler_all_call (ecore_idler.c:143)
==6545==    by 0x4E4EA73: _ecore_main_loop_spin_core (ecore_main.c:1768)
==6545==    by 0x4E4EAF1: _ecore_main_loop_spin_timers (ecore_main.c:1802)
==6545==    by 0x4E4ED01: _ecore_main_loop_iterate_internal (ecore_main.c:1925)
==6545==    by 0x4E4D03B: ecore_main_loop_begin (ecore_main.c:983)
==6545==  Address 0x701aa78 is 104 bytes inside a block of size 128 free'd
==6545==    at 0x4C2B200: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==6545==    by 0x530655B: eldbus_pending_dispatch (eldbus_pending.c:241)
==6545==    by 0x5306763: eldbus_pending_cancel (eldbus_pending.c:259)
==6545==    by 0x52F29DB: _eldbus_proxy_clear (eldbus_proxy.c:146)
==6545==    by 0x52F3057: _eldbus_proxy_unref (eldbus_proxy.c:244)
==6545==    by 0x52F3393: eldbus_proxy_unref (eldbus_proxy.c:264)
==6545==    by 0x401039: on_get_playlists (banshee.c:53)
==6545==    by 0x5306493: eldbus_pending_dispatch (eldbus_pending.c:225)
==6545==    by 0x5305961: cb_pending (eldbus_pending.c:74)
==6545==    by 0x6B29DB1: ??? (in /usr/lib/libdbus-1.so.3.8.9)
==6545==    by 0x6B2D280: dbus_connection_dispatch (in /usr/lib/libdbus-1.so.3.8.9)
==6545==    by 0x52F93B4: eldbus_idler (eldbus_core.c:773)

Now we will remove the pending from parent pending list before
call the user callback, this way only the pending messages will
be canceled.

Also we need increase the eldbus reference before call
dbus_connection_dispatch() or user could remove the last reference of
eldbus inside of a message callback when we still are
holding one reference of the connection.

@fix
ref T1908
This commit is contained in:
José Roberto de Souza 2014-12-17 16:57:03 -02:00
parent 78d5bb17d7
commit 63abe9b00c
4 changed files with 47 additions and 18 deletions

View File

@ -769,9 +769,11 @@ eldbus_idler(void *data)
return ECORE_CALLBACK_CANCEL;
}
DBG("Connection@%p: Dispatching", conn);
eldbus_init();
eldbus_connection_ref(conn);
dbus_connection_dispatch(conn->dbus_conn);
eldbus_connection_unref(conn);
eldbus_shutdown();
return ECORE_CALLBACK_RENEW;
}

View File

@ -560,12 +560,15 @@ eldbus_object_path_get(const Eldbus_Object *obj)
}
static void
_on_pending_free(void *data, const void *dead_pointer)
_on_object_message_cb(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending)
{
Eldbus_Object *obj = data;
Eldbus_Pending *pending = (Eldbus_Pending*) dead_pointer;
Eldbus_Message_Cb cb = eldbus_pending_data_del(pending, "__user_cb");
Eldbus_Object *obj = eldbus_pending_data_del(pending, "__object");
ELDBUS_OBJECT_CHECK(obj);
obj->pendings = eina_inlist_remove(obj->pendings, EINA_INLIST_GET(pending));
cb(data, msg, pending);
}
EAPI Eldbus_Pending *
@ -576,11 +579,17 @@ eldbus_object_send(Eldbus_Object *obj, Eldbus_Message *msg, Eldbus_Message_Cb cb
ELDBUS_OBJECT_CHECK_RETVAL(obj, NULL);
EINA_SAFETY_ON_NULL_RETURN_VAL(msg, NULL);
pending = _eldbus_connection_send(obj->conn, msg, cb, cb_data, timeout);
if (!cb) return NULL;
if (!cb)
{
_eldbus_connection_send(obj->conn, msg, NULL, NULL, timeout);
return NULL;
}
pending = _eldbus_connection_send(obj->conn, msg, _on_object_message_cb,
cb_data, timeout);
EINA_SAFETY_ON_NULL_RETURN_VAL(pending, NULL);
eldbus_pending_free_cb_add(pending, _on_pending_free, obj);
eldbus_pending_data_set(pending, "__user_cb", cb);
eldbus_pending_data_set(pending, "__object", obj);
obj->pendings = eina_inlist_append(obj->pendings, EINA_INLIST_GET(pending));
return pending;

View File

@ -80,10 +80,14 @@ cleanup:
}
static void
_on_pending_free(void *data, const void *dead_pointer)
_on_conn_message_cb(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending)
{
Eldbus_Connection *conn = data;
eldbus_connection_pending_del(conn, (void *)dead_pointer);
Eldbus_Message_Cb cb = eldbus_pending_data_del(pending, "__user_cb");
Eldbus_Connection *conn = eldbus_pending_data_del(pending, "__connection");
EINA_SAFETY_ON_NULL_RETURN(conn);
eldbus_connection_pending_del(conn, pending);
cb(data, msg, pending);
}
EAPI Eldbus_Pending *
@ -94,12 +98,18 @@ eldbus_connection_send(Eldbus_Connection *conn, Eldbus_Message *msg, Eldbus_Mess
EINA_SAFETY_ON_NULL_RETURN_VAL(conn, NULL);
EINA_SAFETY_ON_NULL_RETURN_VAL(msg, NULL);
pending = _eldbus_connection_send(conn, msg, cb, cb_data, timeout);
if (!cb) return NULL;
if (!cb)
{
_eldbus_connection_send(conn, msg, NULL, NULL, timeout);
return NULL;
}
pending = _eldbus_connection_send(conn, msg, _on_conn_message_cb, cb_data,
timeout);
EINA_SAFETY_ON_NULL_RETURN_VAL(pending, NULL);
eldbus_pending_data_set(pending, "__user_cb", cb);
eldbus_pending_data_set(pending, "__connection", conn);
eldbus_connection_pending_add(conn, pending);
eldbus_pending_free_cb_add(pending, _on_pending_free, conn);
return pending;
}

View File

@ -521,13 +521,15 @@ eldbus_proxy_interface_get(const Eldbus_Proxy *proxy)
}
static void
_on_pending_free(void *data, const void *dead_pointer)
_on_proxy_message_cb(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending)
{
Eldbus_Proxy *proxy = data;
Eldbus_Pending *pending = (Eldbus_Pending *)dead_pointer;
Eldbus_Message_Cb cb = eldbus_pending_data_del(pending, "__user_cb");
Eldbus_Proxy *proxy = eldbus_pending_data_del(pending, "__proxy");
ELDBUS_PROXY_CHECK(proxy);
proxy->pendings = eina_inlist_remove(proxy->pendings,
EINA_INLIST_GET(pending));
cb(data, msg, pending);
}
static Eldbus_Pending *
@ -535,11 +537,17 @@ _eldbus_proxy_send(Eldbus_Proxy *proxy, Eldbus_Message *msg, Eldbus_Message_Cb c
{
Eldbus_Pending *pending;
pending = _eldbus_connection_send(proxy->obj->conn, msg, cb, cb_data, timeout);
if (!cb) return NULL;
if (!cb)
{
_eldbus_connection_send(proxy->obj->conn, msg, NULL, NULL, timeout);
return NULL;
}
pending = _eldbus_connection_send(proxy->obj->conn, msg,
_on_proxy_message_cb, cb_data, timeout);
EINA_SAFETY_ON_NULL_RETURN_VAL(pending, NULL);
eldbus_pending_free_cb_add(pending, _on_pending_free, proxy);
eldbus_pending_data_set(pending, "__user_cb", cb);
eldbus_pending_data_set(pending, "__proxy", proxy);
proxy->pendings = eina_inlist_append(proxy->pendings,
EINA_INLIST_GET(pending));