1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
|
From: Simon McVittie <smcv@collabora.com>
Date: Tue, 23 Apr 2024 21:39:43 +0100
Subject: tests: Ensure that unsubscribing with GetNameOwner in-flight doesn't
crash
This was a bug that existed during development of this branch; make sure
it doesn't come back.
This test fails with a use-after-free and crash if we comment out the
part of name_watcher_unref_watched_name() that removes the name watcher
from `map_method_serial_to_name_watcher`.
It would also fail with an assertion failure if we asserted in
name_watcher_unref_watched_name() that get_name_owner_serial == 0
(i.e. that GetNameOwner is not in-flight at destruction).
Signed-off-by: Simon McVittie <smcv@collabora.com>
Origin: upstream, https://gitlab.gnome.org/GNOME/glib/-/issues/3268
---
gio/tests/gdbus-subscribe.c | 52 ++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 51 insertions(+), 1 deletion(-)
diff --git a/gio/tests/gdbus-subscribe.c b/gio/tests/gdbus-subscribe.c
index 5406ba7..4cba4f5 100644
--- a/gio/tests/gdbus-subscribe.c
+++ b/gio/tests/gdbus-subscribe.c
@@ -116,6 +116,7 @@ typedef struct
const char *member;
const char *arg0;
GDBusSignalFlags flags;
+ gboolean unsubscribe_immediately;
} TestSubscribe;
typedef struct
@@ -141,6 +142,7 @@ typedef struct
TestEmitSignal signal;
TestSubscribe subscribe;
TestOwnName own_name;
+ guint unsubscribe_undo_step;
} u;
} TestStep;
@@ -505,6 +507,43 @@ static const TestPlan plan_limit_by_well_known_name =
},
};
+static const TestPlan plan_unsubscribe_immediately =
+{
+ .description = "Unsubscribing before GetNameOwner can return doesn't result in a crash",
+ .steps = {
+ {
+ /* Service already owns one name */
+ .action = TEST_ACTION_OWN_NAME,
+ .u.own_name = {
+ .name = ALREADY_OWNED_NAME,
+ .owner = TEST_CONN_SERVICE
+ },
+ },
+ {
+ .action = TEST_ACTION_SUBSCRIBE,
+ .u.subscribe = {
+ .string_sender = ALREADY_OWNED_NAME,
+ .path = EXAMPLE_PATH,
+ .iface = EXAMPLE_INTERFACE,
+ .unsubscribe_immediately = TRUE
+ },
+ },
+ {
+ .action = TEST_ACTION_EMIT_SIGNAL,
+ .u.signal = {
+ .sender = TEST_CONN_SERVICE,
+ .path = EXAMPLE_PATH,
+ .iface = EXAMPLE_INTERFACE,
+ .member = FOO_SIGNAL,
+ .received_by_conn = 0,
+ /* The proxy can't unsubscribe, except by destroying the proxy
+ * completely, which we don't currently implement in this test */
+ .received_by_proxy = 1
+ },
+ },
+ },
+};
+
static const TestPlan plan_limit_to_message_bus =
{
.description = "A subscription to the message bus only accepts messages "
@@ -855,8 +894,18 @@ fixture_subscribe (Fixture *f,
subscribe->flags,
subscribed_signal_cb,
f, NULL);
+
g_assert_cmpuint (id, !=, 0);
- f->subscriptions[step_number] = id;
+
+ if (subscribe->unsubscribe_immediately)
+ {
+ g_test_message ("\tImmediately unsubscribing");
+ g_dbus_connection_signal_unsubscribe (subscriber, id);
+ }
+ else
+ {
+ f->subscriptions[step_number] = id;
+ }
}
if (f->mode != SUBSCRIPTION_MODE_CONN)
@@ -1287,6 +1336,7 @@ main (int argc,
ADD_SUBSCRIBE_TEST (nonexistent_unique_name);
ADD_SUBSCRIBE_TEST (limit_by_well_known_name);
ADD_SUBSCRIBE_TEST (limit_to_message_bus);
+ ADD_SUBSCRIBE_TEST (unsubscribe_immediately);
return g_test_run();
}
|