gdbusinterfaceskeleton: Fix a use-after-free of a GDBusMethodInvocation

This `GDBusMethodInvocation` may be shared across threads, with no
guarantee on the strong ref in one thread outlasting any refs in other
threads — so it needs a ref in this helper struct.

This should fix a use-after-free where the `GDBusMethodInvocation` is
freed from `g_value_unset()` after `g_signal_emit()` returns in
`dispatch_in_thread_func()` in one thread; but then dereferenced again
in `g_source_destroy_internal()` from another thread.

Signed-off-by: Philip Withnall <pwithnall@endlessos.org>

Fixes: #2924
This commit is contained in:
Philip Withnall 2023-02-22 12:47:36 +00:00
parent 1f86923766
commit d5710deb9d

View File

@ -462,14 +462,17 @@ typedef struct
{ {
gint ref_count; /* (atomic) */ gint ref_count; /* (atomic) */
GDBusInterfaceMethodCallFunc method_call_func; GDBusInterfaceMethodCallFunc method_call_func;
GDBusMethodInvocation *invocation; GDBusMethodInvocation *invocation; /* (owned) */
} DispatchData; } DispatchData;
static void static void
dispatch_data_unref (DispatchData *data) dispatch_data_unref (DispatchData *data)
{ {
if (g_atomic_int_dec_and_test (&data->ref_count)) if (g_atomic_int_dec_and_test (&data->ref_count))
g_slice_free (DispatchData, data); {
g_clear_object (&data->invocation);
g_slice_free (DispatchData, data);
}
} }
static DispatchData * static DispatchData *
@ -628,7 +631,7 @@ g_dbus_interface_method_dispatch_helper (GDBusInterfaceSkeleton *interface
data = g_slice_new0 (DispatchData); data = g_slice_new0 (DispatchData);
data->method_call_func = method_call_func; data->method_call_func = method_call_func;
data->invocation = invocation; data->invocation = g_object_ref (invocation);
data->ref_count = 1; data->ref_count = 1;
task = g_task_new (interface, NULL, NULL, NULL); task = g_task_new (interface, NULL, NULL, NULL);