gdbusconnection: Fix a crash on arg0 matching

If a connection has two signal subscriptions active for the same signal,
one with arg0 matching and one without, a signal which doesn’t contain
an arg0 value (i.e. `g_dbus_message_get_arg0()` returns `NULL`) will
cause `NULL` to be passed to `strcmp()` when checking for a match
against the signal subscription which *has* arg0 matching, causing a
crash.

Fix that by adding the obvious `NULL` check, and add a unit test.

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

Fixes: #3342
This commit is contained in:
Philip Withnall 2024-04-30 18:33:57 +01:00
parent 7b07b0e675
commit 9c66880af0
No known key found for this signature in database
GPG Key ID: DCDF5885B1F3ED73
2 changed files with 65 additions and 1 deletions

View File

@ -3929,7 +3929,7 @@ schedule_callbacks (GDBusConnection *connection,
(arg0_path == NULL || !path_rule_matches (signal_data->arg0, arg0_path))) (arg0_path == NULL || !path_rule_matches (signal_data->arg0, arg0_path)))
continue; continue;
} }
else if (!g_str_equal (signal_data->arg0, arg0)) else if (arg0 == NULL || !g_str_equal (signal_data->arg0, arg0))
continue; continue;
} }

View File

@ -549,9 +549,13 @@ test_connection_signals (void)
guint s1b; guint s1b;
guint s2; guint s2;
guint s3; guint s3;
guint s4;
guint s5;
gint count_s1; gint count_s1;
gint count_s1b; gint count_s1b;
gint count_s2; gint count_s2;
gint count_s4;
gint count_s5;
gint count_name_owner_changed; gint count_name_owner_changed;
GError *error; GError *error;
gboolean ret; gboolean ret;
@ -618,6 +622,26 @@ test_connection_signals (void)
test_connection_signal_handler, test_connection_signal_handler,
&count_name_owner_changed, &count_name_owner_changed,
NULL); NULL);
s4 = g_dbus_connection_signal_subscribe (c1,
":1.2", /* sender */
"org.gtk.GDBus.ExampleInterface", /* interface */
"FooArg0", /* member */
"/org/gtk/GDBus/ExampleInterface", /* path */
"some-arg0",
G_DBUS_SIGNAL_FLAGS_NONE,
test_connection_signal_handler,
&count_s4,
NULL);
s5 = g_dbus_connection_signal_subscribe (c1,
":1.2", /* sender */
"org.gtk.GDBus.ExampleInterface", /* interface */
"FooArg0", /* member */
"/org/gtk/GDBus/ExampleInterface", /* path */
NULL,
G_DBUS_SIGNAL_FLAGS_NONE,
test_connection_signal_handler,
&count_s5,
NULL);
/* Note that s1b is *just like* s1 - this is to catch a bug where N /* Note that s1b is *just like* s1 - this is to catch a bug where N
* subscriptions of the same rule causes N calls to each of the N * subscriptions of the same rule causes N calls to each of the N
* subscriptions instead of just 1 call to each of the N subscriptions. * subscriptions instead of just 1 call to each of the N subscriptions.
@ -636,10 +660,14 @@ test_connection_signals (void)
g_assert_cmpuint (s1b, !=, 0); g_assert_cmpuint (s1b, !=, 0);
g_assert_cmpuint (s2, !=, 0); g_assert_cmpuint (s2, !=, 0);
g_assert_cmpuint (s3, !=, 0); g_assert_cmpuint (s3, !=, 0);
g_assert_cmpuint (s4, !=, 0);
g_assert_cmpuint (s5, !=, 0);
count_s1 = 0; count_s1 = 0;
count_s1b = 0; count_s1b = 0;
count_s2 = 0; count_s2 = 0;
count_s4 = 0;
count_s5 = 0;
count_name_owner_changed = 0; count_name_owner_changed = 0;
/* /*
@ -712,6 +740,38 @@ test_connection_signals (void)
g_assert_cmpint (count_s1, ==, 1); g_assert_cmpint (count_s1, ==, 1);
g_assert_cmpint (count_s2, ==, 2); g_assert_cmpint (count_s2, ==, 2);
/* Emit another signal on c2 with and without arg0 set, to check matching on that.
* Matching should fail on s4 when the signal is not emitted with an arg0. It
* should succeed on s5 both times, as that doesnt require an arg0 match. */
ret = g_dbus_connection_emit_signal (c2,
NULL, /* destination bus name */
"/org/gtk/GDBus/ExampleInterface",
"org.gtk.GDBus.ExampleInterface",
"FooArg0",
NULL,
&error);
g_assert_no_error (error);
g_assert_true (ret);
while (count_s5 < 1)
g_main_loop_run (loop);
g_assert_cmpint (count_s5, ==, 1);
ret = g_dbus_connection_emit_signal (c2,
NULL, /* destination bus name */
"/org/gtk/GDBus/ExampleInterface",
"org.gtk.GDBus.ExampleInterface",
"FooArg0",
g_variant_new_parsed ("('some-arg0',)"),
&error);
g_assert_no_error (error);
g_assert_true (ret);
while (count_s4 < 1)
g_main_loop_run (loop);
g_assert_cmpint (count_s4, ==, 1);
g_assert_cmpint (count_s5, ==, 2);
/* /*
* Also to check the total amount of NameOwnerChanged signals - use a 5 second ceiling * Also to check the total amount of NameOwnerChanged signals - use a 5 second ceiling
* to avoid spinning forever * to avoid spinning forever
@ -724,11 +784,15 @@ test_connection_signals (void)
g_assert_cmpint (count_s1, ==, 1); g_assert_cmpint (count_s1, ==, 1);
g_assert_cmpint (count_s2, ==, 2); g_assert_cmpint (count_s2, ==, 2);
g_assert_cmpint (count_name_owner_changed, ==, 2); g_assert_cmpint (count_name_owner_changed, ==, 2);
g_assert_cmpint (count_s4, ==, 1);
g_assert_cmpint (count_s5, ==, 2);
g_dbus_connection_signal_unsubscribe (c1, s1); g_dbus_connection_signal_unsubscribe (c1, s1);
g_dbus_connection_signal_unsubscribe (c1, s2); g_dbus_connection_signal_unsubscribe (c1, s2);
g_dbus_connection_signal_unsubscribe (c1, s3); g_dbus_connection_signal_unsubscribe (c1, s3);
g_dbus_connection_signal_unsubscribe (c1, s1b); g_dbus_connection_signal_unsubscribe (c1, s1b);
g_dbus_connection_signal_unsubscribe (c1, s4);
g_dbus_connection_signal_unsubscribe (c1, s5);
g_object_unref (c1); g_object_unref (c1);
g_object_unref (c2); g_object_unref (c2);