1
0
mirror of https://gitlab.gnome.org/GNOME/glib.git synced 2025-04-25 16:46:52 +02:00

Fix use-after-free when calling g_dbus_connection_flush_sync()

When the _g_dbus_worker_flush_sync() schedules the 'data' and releases
the worker->write_lock, it is possible for the GDBus worker thread thread
to finish the D-Bus call and acquire the worker->write_lock before
the _g_dbus_worker_flush_sync() re-acquires it in the if (data != NULL) body.
When that happens, the ostream_flush_cb() increases the worker->write_num_messages_flushed
and then releases the worker->write_lock. The write lock is reacquired by
the _g_dbus_worker_flush_sync(), which sees that the while condition is satisfied,
thus it doesn't enter the loop body and immediately clears the data members and
frees the data structure itself. The ostream_flush_cb() is still ongoing, possibly
inside flush_data_list_complete(), where it accesses the FlushData, which can be
in any stage of being freed.

Instead, add an explicit boolean flag indicating when the flush is truly finished.

Closes 
This commit is contained in:
Milan Crha 2019-10-10 14:55:20 +00:00 committed by Philip Withnall
parent c97565748a
commit 822f8bae9e

@ -408,6 +408,7 @@ typedef struct
GMutex mutex;
GCond cond;
guint64 number_to_wait_for;
gboolean finished;
GError *error;
} FlushData;
@ -1158,6 +1159,7 @@ flush_data_list_complete (const GList *flushers,
f->error = error != NULL ? g_error_copy (error) : NULL;
g_mutex_lock (&f->mutex);
f->finished = TRUE;
g_cond_signal (&f->cond);
g_mutex_unlock (&f->mutex);
}
@ -1787,6 +1789,7 @@ _g_dbus_worker_flush_sync (GDBusWorker *worker,
g_mutex_init (&data->mutex);
g_cond_init (&data->cond);
data->number_to_wait_for = worker->write_num_messages_written + pending_writes;
data->finished = FALSE;
g_mutex_lock (&data->mutex);
schedule_writing_unlocked (worker, NULL, data, NULL);
@ -1796,14 +1799,10 @@ _g_dbus_worker_flush_sync (GDBusWorker *worker,
if (data != NULL)
{
/* Wait for flush operations to finish. */
g_mutex_lock (&worker->write_lock);
while (worker->write_num_messages_flushed < data->number_to_wait_for)
while (!data->finished)
{
g_mutex_unlock (&worker->write_lock);
g_cond_wait (&data->cond, &data->mutex);
g_mutex_lock (&worker->write_lock);
}
g_mutex_unlock (&worker->write_lock);
g_mutex_unlock (&data->mutex);
g_cond_clear (&data->cond);