summaryrefslogtreecommitdiff
path: root/gio
diff options
context:
space:
mode:
authorKarol Lewandowski <k.lewandowsk@samsung.com>2024-01-03 15:58:00 +0100
committerKarol Lewandowski <k.lewandowsk@samsung.com>2024-01-03 15:58:00 +0100
commitcbef7bb66192abf9d98aba079fe5236a8fb54e6f (patch)
treeb85871bd2e3d03a1b7b4b4c718cd90a59a544694 /gio
parent8e3545296b8994a7531f4d140a9deefd32a3a9f9 (diff)
downloadglib-cbef7bb66192abf9d98aba079fe5236a8fb54e6f.tar.gz
glib-cbef7bb66192abf9d98aba079fe5236a8fb54e6f.tar.bz2
glib-cbef7bb66192abf9d98aba079fe5236a8fb54e6f.zip
Imported Upstream version 2.70.5
Diffstat (limited to 'gio')
-rw-r--r--gio/gdbusauthmechanismsha1.c1
-rw-r--r--gio/gdbusconnection.c175
-rw-r--r--gio/gdbusmessage.c259
-rw-r--r--gio/gdelayedsettingsbackend.c3
-rw-r--r--gio/gnextstepsettingsbackend.m6
-rw-r--r--gio/gsettings.c30
-rw-r--r--gio/gtestdbus.c89
-rw-r--r--gio/gwin32appinfo.c5
-rw-r--r--gio/tests/gdbus-export.c180
-rw-r--r--gio/tests/gdbus-serialization.c149
-rw-r--r--gio/tests/gsettings.c191
-rw-r--r--gio/tests/meson.build11
12 files changed, 815 insertions, 284 deletions
diff --git a/gio/gdbusauthmechanismsha1.c b/gio/gdbusauthmechanismsha1.c
index 94fe0bce8..066ef1ab7 100644
--- a/gio/gdbusauthmechanismsha1.c
+++ b/gio/gdbusauthmechanismsha1.c
@@ -908,6 +908,7 @@ keyring_generate_entry (const gchar *cookie_context,
_("(Additionally, releasing the lock for “%s” also failed: %s) "),
path,
local_error->message);
+ g_error_free (local_error);
}
}
else
diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c
index d730111f8..73b5b309a 100644
--- a/gio/gdbusconnection.c
+++ b/gio/gdbusconnection.c
@@ -466,7 +466,8 @@ typedef struct ExportedObject ExportedObject;
static void exported_object_free (ExportedObject *eo);
typedef struct ExportedSubtree ExportedSubtree;
-static void exported_subtree_free (ExportedSubtree *es);
+static ExportedSubtree *exported_subtree_ref (ExportedSubtree *es);
+static void exported_subtree_unref (ExportedSubtree *es);
enum
{
@@ -1096,7 +1097,7 @@ g_dbus_connection_init (GDBusConnection *connection)
connection->map_object_path_to_es = g_hash_table_new_full (g_str_hash,
g_str_equal,
NULL,
- (GDestroyNotify) exported_subtree_free);
+ (GDestroyNotify) exported_subtree_unref);
connection->map_id_to_es = g_hash_table_new (g_direct_hash,
g_direct_equal);
@@ -4085,23 +4086,39 @@ typedef struct
{
ExportedObject *eo;
+ gint refcount; /* (atomic) */
+
guint id;
- gchar *interface_name;
- GDBusInterfaceVTable *vtable;
- GDBusInterfaceInfo *interface_info;
+ gchar *interface_name; /* (owned) */
+ GDBusInterfaceVTable *vtable; /* (owned) */
+ GDBusInterfaceInfo *interface_info; /* (owned) */
- GMainContext *context;
+ GMainContext *context; /* (owned) */
gpointer user_data;
GDestroyNotify user_data_free_func;
} ExportedInterface;
-/* called with lock held */
+static ExportedInterface *
+exported_interface_ref (ExportedInterface *ei)
+{
+ g_atomic_int_inc (&ei->refcount);
+
+ return ei;
+}
+
+/* May be called with lock held */
static void
-exported_interface_free (ExportedInterface *ei)
+exported_interface_unref (ExportedInterface *ei)
{
+ if (!g_atomic_int_dec_and_test (&ei->refcount))
+ return;
+
g_dbus_interface_info_cache_release (ei->interface_info);
g_dbus_interface_info_unref ((GDBusInterfaceInfo *) ei->interface_info);
+ /* All uses of ei->vtable from callbacks scheduled in idle functions must
+ * have completed by this call_destroy_notify() call, as language bindings
+ * may destroy function closures in this callback. */
call_destroy_notify (ei->context,
ei->user_data_free_func,
ei->user_data);
@@ -4113,36 +4130,95 @@ exported_interface_free (ExportedInterface *ei)
g_free (ei);
}
+struct ExportedSubtree
+{
+ gint refcount; /* (atomic) */
+
+ guint id;
+ gchar *object_path; /* (owned) */
+ GDBusConnection *connection; /* (unowned) */
+ GDBusSubtreeVTable *vtable; /* (owned) */
+ GDBusSubtreeFlags flags;
+
+ GMainContext *context; /* (owned) */
+ gpointer user_data;
+ GDestroyNotify user_data_free_func;
+};
+
+static ExportedSubtree *
+exported_subtree_ref (ExportedSubtree *es)
+{
+ g_atomic_int_inc (&es->refcount);
+
+ return es;
+}
+
+/* May be called with lock held */
+static void
+exported_subtree_unref (ExportedSubtree *es)
+{
+ if (!g_atomic_int_dec_and_test (&es->refcount))
+ return;
+
+ /* All uses of es->vtable from callbacks scheduled in idle functions must
+ * have completed by this call_destroy_notify() call, as language bindings
+ * may destroy function closures in this callback. */
+ call_destroy_notify (es->context,
+ es->user_data_free_func,
+ es->user_data);
+
+ g_main_context_unref (es->context);
+
+ _g_dbus_subtree_vtable_free (es->vtable);
+ g_free (es->object_path);
+ g_free (es);
+}
+
/* ---------------------------------------------------------------------------------------------------- */
/* Convenience function to check if @registration_id (if not zero) or
* @subtree_registration_id (if not zero) has been unregistered. If
* so, returns %TRUE.
*
+ * If not, sets @out_ei and/or @out_es to a strong reference to the relevant
+ * #ExportedInterface/#ExportedSubtree and returns %FALSE.
+ *
* May be called by any thread. Caller must *not* hold lock.
*/
static gboolean
-has_object_been_unregistered (GDBusConnection *connection,
- guint registration_id,
- guint subtree_registration_id)
+has_object_been_unregistered (GDBusConnection *connection,
+ guint registration_id,
+ ExportedInterface **out_ei,
+ guint subtree_registration_id,
+ ExportedSubtree **out_es)
{
gboolean ret;
+ ExportedInterface *ei = NULL;
+ gpointer es = NULL;
g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), FALSE);
ret = FALSE;
CONNECTION_LOCK (connection);
- if (registration_id != 0 && g_hash_table_lookup (connection->map_id_to_ei,
- GUINT_TO_POINTER (registration_id)) == NULL)
+
+ if (registration_id != 0)
{
- ret = TRUE;
+ ei = g_hash_table_lookup (connection->map_id_to_ei, GUINT_TO_POINTER (registration_id));
+ if (ei == NULL)
+ ret = TRUE;
+ else if (out_ei != NULL)
+ *out_ei = exported_interface_ref (ei);
}
- else if (subtree_registration_id != 0 && g_hash_table_lookup (connection->map_id_to_es,
- GUINT_TO_POINTER (subtree_registration_id)) == NULL)
+ if (subtree_registration_id != 0)
{
- ret = TRUE;
+ es = g_hash_table_lookup (connection->map_id_to_es, GUINT_TO_POINTER (subtree_registration_id));
+ if (es == NULL)
+ ret = TRUE;
+ else if (out_es != NULL)
+ *out_es = exported_subtree_ref (es);
}
+
CONNECTION_UNLOCK (connection);
return ret;
@@ -4179,10 +4255,14 @@ invoke_get_property_in_idle_cb (gpointer _data)
GVariant *value;
GError *error;
GDBusMessage *reply;
+ ExportedInterface *ei = NULL;
+ ExportedSubtree *es = NULL;
if (has_object_been_unregistered (data->connection,
data->registration_id,
- data->subtree_registration_id))
+ &ei,
+ data->subtree_registration_id,
+ &es))
{
reply = g_dbus_message_new_method_error (data->message,
"org.freedesktop.DBus.Error.UnknownMethod",
@@ -4229,6 +4309,9 @@ invoke_get_property_in_idle_cb (gpointer _data)
}
out:
+ g_clear_pointer (&ei, exported_interface_unref);
+ g_clear_pointer (&es, exported_subtree_unref);
+
return FALSE;
}
@@ -4526,10 +4609,14 @@ invoke_get_all_properties_in_idle_cb (gpointer _data)
GVariantBuilder builder;
GDBusMessage *reply;
guint n;
+ ExportedInterface *ei = NULL;
+ ExportedSubtree *es = NULL;
if (has_object_been_unregistered (data->connection,
data->registration_id,
- data->subtree_registration_id))
+ &ei,
+ data->subtree_registration_id,
+ &es))
{
reply = g_dbus_message_new_method_error (data->message,
"org.freedesktop.DBus.Error.UnknownMethod",
@@ -4582,6 +4669,9 @@ invoke_get_all_properties_in_idle_cb (gpointer _data)
g_object_unref (reply);
out:
+ g_clear_pointer (&ei, exported_interface_unref);
+ g_clear_pointer (&es, exported_subtree_unref);
+
return FALSE;
}
@@ -4891,13 +4981,17 @@ call_in_idle_cb (gpointer user_data)
GDBusInterfaceVTable *vtable;
guint registration_id;
guint subtree_registration_id;
+ ExportedInterface *ei = NULL;
+ ExportedSubtree *es = NULL;
registration_id = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (invocation), "g-dbus-registration-id"));
subtree_registration_id = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (invocation), "g-dbus-subtree-registration-id"));
if (has_object_been_unregistered (g_dbus_method_invocation_get_connection (invocation),
registration_id,
- subtree_registration_id))
+ &ei,
+ subtree_registration_id,
+ &es))
{
GDBusMessage *reply;
reply = g_dbus_message_new_method_error (g_dbus_method_invocation_get_message (invocation),
@@ -4923,6 +5017,9 @@ call_in_idle_cb (gpointer user_data)
g_dbus_method_invocation_get_user_data (invocation));
out:
+ g_clear_pointer (&ei, exported_interface_unref);
+ g_clear_pointer (&es, exported_subtree_unref);
+
return FALSE;
}
@@ -5224,7 +5321,7 @@ g_dbus_connection_register_object (GDBusConnection *connection,
eo->map_if_name_to_ei = g_hash_table_new_full (g_str_hash,
g_str_equal,
NULL,
- (GDestroyNotify) exported_interface_free);
+ (GDestroyNotify) exported_interface_unref);
g_hash_table_insert (connection->map_object_path_to_eo, eo->object_path, eo);
}
@@ -5241,6 +5338,7 @@ g_dbus_connection_register_object (GDBusConnection *connection,
}
ei = g_new0 (ExportedInterface, 1);
+ ei->refcount = 1;
ei->id = (guint) g_atomic_int_add (&_global_registration_id, 1); /* TODO: overflow etc. */
ei->eo = eo;
ei->user_data = user_data;
@@ -6401,33 +6499,6 @@ g_dbus_connection_call_with_unix_fd_list_sync (GDBusConnection *connection,
/* ---------------------------------------------------------------------------------------------------- */
-struct ExportedSubtree
-{
- guint id;
- gchar *object_path;
- GDBusConnection *connection;
- GDBusSubtreeVTable *vtable;
- GDBusSubtreeFlags flags;
-
- GMainContext *context;
- gpointer user_data;
- GDestroyNotify user_data_free_func;
-};
-
-static void
-exported_subtree_free (ExportedSubtree *es)
-{
- call_destroy_notify (es->context,
- es->user_data_free_func,
- es->user_data);
-
- g_main_context_unref (es->context);
-
- _g_dbus_subtree_vtable_free (es->vtable);
- g_free (es->object_path);
- g_free (es);
-}
-
/* called without lock held in the thread where the caller registered
* the subtree
*/
@@ -6753,14 +6824,15 @@ handle_subtree_method_invocation (GDBusConnection *connection,
typedef struct
{
- GDBusMessage *message;
- ExportedSubtree *es;
+ GDBusMessage *message; /* (owned) */
+ ExportedSubtree *es; /* (owned) */
} SubtreeDeferredData;
static void
subtree_deferred_data_free (SubtreeDeferredData *data)
{
g_object_unref (data->message);
+ exported_subtree_unref (data->es);
g_free (data);
}
@@ -6819,7 +6891,7 @@ subtree_message_func (GDBusConnection *connection,
data = g_new0 (SubtreeDeferredData, 1);
data->message = g_object_ref (message);
- data->es = es;
+ data->es = exported_subtree_ref (es);
/* defer this call to an idle handler in the right thread */
idle_source = g_idle_source_new ();
@@ -6924,6 +6996,7 @@ g_dbus_connection_register_subtree (GDBusConnection *connection,
}
es = g_new0 (ExportedSubtree, 1);
+ es->refcount = 1;
es->object_path = g_strdup (object_path);
es->connection = connection;
diff --git a/gio/gdbusmessage.c b/gio/gdbusmessage.c
index cdc0b83e8..3415ed613 100644
--- a/gio/gdbusmessage.c
+++ b/gio/gdbusmessage.c
@@ -83,21 +83,36 @@ g_memory_buffer_is_byteswapped (GMemoryBuffer *mbuf)
}
static guchar
-g_memory_buffer_read_byte (GMemoryBuffer *mbuf)
+g_memory_buffer_read_byte (GMemoryBuffer *mbuf,
+ GError **error)
{
+ g_return_val_if_fail (error == NULL || *error == NULL, 0);
+
if (mbuf->pos >= mbuf->valid_len)
- return 0;
+ {
+ g_set_error (error,
+ G_IO_ERROR,
+ G_IO_ERROR_INVALID_ARGUMENT,
+ "Unexpected end of message while reading byte.");
+ return 0;
+ }
return mbuf->data [mbuf->pos++];
}
static gint16
-g_memory_buffer_read_int16 (GMemoryBuffer *mbuf)
+g_memory_buffer_read_int16 (GMemoryBuffer *mbuf,
+ GError **error)
{
gint16 v;
-
+
+ g_return_val_if_fail (error == NULL || *error == NULL, -1);
+
if (mbuf->pos > mbuf->valid_len - 2)
{
- mbuf->pos = mbuf->valid_len;
+ g_set_error (error,
+ G_IO_ERROR,
+ G_IO_ERROR_INVALID_ARGUMENT,
+ "Unexpected end of message while reading int16.");
return 0;
}
@@ -111,13 +126,19 @@ g_memory_buffer_read_int16 (GMemoryBuffer *mbuf)
}
static guint16
-g_memory_buffer_read_uint16 (GMemoryBuffer *mbuf)
+g_memory_buffer_read_uint16 (GMemoryBuffer *mbuf,
+ GError **error)
{
guint16 v;
-
+
+ g_return_val_if_fail (error == NULL || *error == NULL, 0);
+
if (mbuf->pos > mbuf->valid_len - 2)
{
- mbuf->pos = mbuf->valid_len;
+ g_set_error (error,
+ G_IO_ERROR,
+ G_IO_ERROR_INVALID_ARGUMENT,
+ "Unexpected end of message while reading uint16.");
return 0;
}
@@ -131,13 +152,19 @@ g_memory_buffer_read_uint16 (GMemoryBuffer *mbuf)
}
static gint32
-g_memory_buffer_read_int32 (GMemoryBuffer *mbuf)
+g_memory_buffer_read_int32 (GMemoryBuffer *mbuf,
+ GError **error)
{
gint32 v;
-
+
+ g_return_val_if_fail (error == NULL || *error == NULL, -1);
+
if (mbuf->pos > mbuf->valid_len - 4)
{
- mbuf->pos = mbuf->valid_len;
+ g_set_error (error,
+ G_IO_ERROR,
+ G_IO_ERROR_INVALID_ARGUMENT,
+ "Unexpected end of message while reading int32.");
return 0;
}
@@ -151,13 +178,19 @@ g_memory_buffer_read_int32 (GMemoryBuffer *mbuf)
}
static guint32
-g_memory_buffer_read_uint32 (GMemoryBuffer *mbuf)
+g_memory_buffer_read_uint32 (GMemoryBuffer *mbuf,
+ GError **error)
{
guint32 v;
-
+
+ g_return_val_if_fail (error == NULL || *error == NULL, 0);
+
if (mbuf->pos > mbuf->valid_len - 4)
{
- mbuf->pos = mbuf->valid_len;
+ g_set_error (error,
+ G_IO_ERROR,
+ G_IO_ERROR_INVALID_ARGUMENT,
+ "Unexpected end of message while reading uint32.");
return 0;
}
@@ -171,13 +204,19 @@ g_memory_buffer_read_uint32 (GMemoryBuffer *mbuf)
}
static gint64
-g_memory_buffer_read_int64 (GMemoryBuffer *mbuf)
+g_memory_buffer_read_int64 (GMemoryBuffer *mbuf,
+ GError **error)
{
gint64 v;
-
+
+ g_return_val_if_fail (error == NULL || *error == NULL, -1);
+
if (mbuf->pos > mbuf->valid_len - 8)
{
- mbuf->pos = mbuf->valid_len;
+ g_set_error (error,
+ G_IO_ERROR,
+ G_IO_ERROR_INVALID_ARGUMENT,
+ "Unexpected end of message while reading int64.");
return 0;
}
@@ -191,13 +230,19 @@ g_memory_buffer_read_int64 (GMemoryBuffer *mbuf)
}
static guint64
-g_memory_buffer_read_uint64 (GMemoryBuffer *mbuf)
+g_memory_buffer_read_uint64 (GMemoryBuffer *mbuf,
+ GError **error)
{
guint64 v;
-
+
+ g_return_val_if_fail (error == NULL || *error == NULL, 0);
+
if (mbuf->pos > mbuf->valid_len - 8)
{
- mbuf->pos = mbuf->valid_len;
+ g_set_error (error,
+ G_IO_ERROR,
+ G_IO_ERROR_INVALID_ARGUMENT,
+ "Unexpected end of message while reading uint64.");
return 0;
}
@@ -1500,7 +1545,9 @@ parse_value_from_blob (GMemoryBuffer *buf,
if (!just_align)
{
gboolean v;
- v = g_memory_buffer_read_uint32 (buf);
+ v = g_memory_buffer_read_uint32 (buf, &local_error);
+ if (local_error)
+ goto fail;
ret = g_variant_new_boolean (v);
}
break;
@@ -1509,7 +1556,9 @@ parse_value_from_blob (GMemoryBuffer *buf,
if (!just_align)
{
guchar v;
- v = g_memory_buffer_read_byte (buf);
+ v = g_memory_buffer_read_byte (buf, &local_error);
+ if (local_error)
+ goto fail;
ret = g_variant_new_byte (v);
}
break;
@@ -1519,7 +1568,9 @@ parse_value_from_blob (GMemoryBuffer *buf,
if (!just_align)
{
gint16 v;
- v = g_memory_buffer_read_int16 (buf);
+ v = g_memory_buffer_read_int16 (buf, &local_error);
+ if (local_error)
+ goto fail;
ret = g_variant_new_int16 (v);
}
break;
@@ -1529,7 +1580,9 @@ parse_value_from_blob (GMemoryBuffer *buf,
if (!just_align)
{
guint16 v;
- v = g_memory_buffer_read_uint16 (buf);
+ v = g_memory_buffer_read_uint16 (buf, &local_error);
+ if (local_error)
+ goto fail;
ret = g_variant_new_uint16 (v);
}
break;
@@ -1539,7 +1592,9 @@ parse_value_from_blob (GMemoryBuffer *buf,
if (!just_align)
{
gint32 v;
- v = g_memory_buffer_read_int32 (buf);
+ v = g_memory_buffer_read_int32 (buf, &local_error);
+ if (local_error)
+ goto fail;
ret = g_variant_new_int32 (v);
}
break;
@@ -1549,7 +1604,9 @@ parse_value_from_blob (GMemoryBuffer *buf,
if (!just_align)
{
guint32 v;
- v = g_memory_buffer_read_uint32 (buf);
+ v = g_memory_buffer_read_uint32 (buf, &local_error);
+ if (local_error)
+ goto fail;
ret = g_variant_new_uint32 (v);
}
break;
@@ -1559,7 +1616,9 @@ parse_value_from_blob (GMemoryBuffer *buf,
if (!just_align)
{
gint64 v;
- v = g_memory_buffer_read_int64 (buf);
+ v = g_memory_buffer_read_int64 (buf, &local_error);
+ if (local_error)
+ goto fail;
ret = g_variant_new_int64 (v);
}
break;
@@ -1569,7 +1628,9 @@ parse_value_from_blob (GMemoryBuffer *buf,
if (!just_align)
{
guint64 v;
- v = g_memory_buffer_read_uint64 (buf);
+ v = g_memory_buffer_read_uint64 (buf, &local_error);
+ if (local_error)
+ goto fail;
ret = g_variant_new_uint64 (v);
}
break;
@@ -1583,7 +1644,9 @@ parse_value_from_blob (GMemoryBuffer *buf,
gdouble v_double;
} u;
G_STATIC_ASSERT (sizeof (gdouble) == sizeof (guint64));
- u.v_uint64 = g_memory_buffer_read_uint64 (buf);
+ u.v_uint64 = g_memory_buffer_read_uint64 (buf, &local_error);
+ if (local_error)
+ goto fail;
ret = g_variant_new_double (u.v_double);
}
break;
@@ -1594,7 +1657,9 @@ parse_value_from_blob (GMemoryBuffer *buf,
{
guint32 len;
const gchar *v;
- len = g_memory_buffer_read_uint32 (buf);
+ len = g_memory_buffer_read_uint32 (buf, &local_error);
+ if (local_error)
+ goto fail;
v = read_string (buf, (gsize) len, &local_error);
if (v == NULL)
goto fail;
@@ -1608,7 +1673,9 @@ parse_value_from_blob (GMemoryBuffer *buf,
{
guint32 len;
const gchar *v;
- len = g_memory_buffer_read_uint32 (buf);
+ len = g_memory_buffer_read_uint32 (buf, &local_error);
+ if (local_error)
+ goto fail;
v = read_string (buf, (gsize) len, &local_error);
if (v == NULL)
goto fail;
@@ -1630,7 +1697,9 @@ parse_value_from_blob (GMemoryBuffer *buf,
{
guchar len;
const gchar *v;
- len = g_memory_buffer_read_byte (buf);
+ len = g_memory_buffer_read_byte (buf, &local_error);
+ if (local_error)
+ goto fail;
v = read_string (buf, (gsize) len, &local_error);
if (v == NULL)
goto fail;
@@ -1652,7 +1721,9 @@ parse_value_from_blob (GMemoryBuffer *buf,
if (!just_align)
{
gint32 v;
- v = g_memory_buffer_read_int32 (buf);
+ v = g_memory_buffer_read_int32 (buf, &local_error);
+ if (local_error)
+ goto fail;
ret = g_variant_new_handle (v);
}
break;
@@ -1672,7 +1743,9 @@ parse_value_from_blob (GMemoryBuffer *buf,
const GVariantType *element_type;
guint fixed_size;
- array_len = g_memory_buffer_read_uint32 (buf);
+ array_len = g_memory_buffer_read_uint32 (buf, &local_error);
+ if (local_error)
+ goto fail;
#ifdef DEBUG_SERIALIZER
is_leaf = FALSE;
@@ -1776,6 +1849,16 @@ parse_value_from_blob (GMemoryBuffer *buf,
}
g_variant_builder_add_value (&builder, item);
g_variant_unref (item);
+
+ /* Array elements must not be zero-length. There are no
+ * valid zero-length serialisations of any types which
+ * can be array elements in the D-Bus wire format, so this
+ * assertion should always hold.
+ *
+ * See https://gitlab.gnome.org/GNOME/glib/-/issues/2557
+ */
+ g_assert (buf->pos > (gsize) offset);
+
offset = buf->pos;
}
}
@@ -1844,6 +1927,16 @@ parse_value_from_blob (GMemoryBuffer *buf,
g_variant_builder_init (&builder, type);
element_type = g_variant_type_first (type);
+ if (!element_type)
+ {
+ g_variant_builder_clear (&builder);
+ g_set_error_literal (&local_error,
+ G_IO_ERROR,
+ G_IO_ERROR_INVALID_ARGUMENT,
+ _("Empty structures (tuples) are not allowed in D-Bus"));
+ goto fail;
+ }
+
while (element_type != NULL)
{
GVariant *item;
@@ -1880,7 +1973,9 @@ parse_value_from_blob (GMemoryBuffer *buf,
GVariantType *variant_type;
GVariant *value;
- siglen = g_memory_buffer_read_byte (buf);
+ siglen = g_memory_buffer_read_byte (buf, &local_error);
+ if (local_error)
+ goto fail;
sig = read_string (buf, (gsize) siglen, &local_error);
if (sig == NULL)
goto fail;
@@ -2078,7 +2173,7 @@ g_dbus_message_new_from_blob (guchar *blob,
GDBusCapabilityFlags capabilities,
GError **error)
{
- gboolean ret;
+ GError *local_error = NULL;
GMemoryBuffer mbuf;
GDBusMessage *message;
guchar endianness;
@@ -2091,11 +2186,8 @@ g_dbus_message_new_from_blob (guchar *blob,
/* TODO: check against @capabilities */
- ret = FALSE;
-
g_return_val_if_fail (blob != NULL, NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
- g_return_val_if_fail (blob_len >= 12, NULL);
message = g_dbus_message_new ();
@@ -2103,7 +2195,10 @@ g_dbus_message_new_from_blob (guchar *blob,
mbuf.data = (gchar *)blob;
mbuf.len = mbuf.valid_len = blob_len;
- endianness = g_memory_buffer_read_byte (&mbuf);
+ endianness = g_memory_buffer_read_byte (&mbuf, &local_error);
+ if (local_error)
+ goto fail;
+
switch (endianness)
{
case 'l':
@@ -2115,28 +2210,38 @@ g_dbus_message_new_from_blob (guchar *blob,
message->byte_order = G_DBUS_MESSAGE_BYTE_ORDER_BIG_ENDIAN;
break;
default:
- g_set_error (error,
+ g_set_error (&local_error,
G_IO_ERROR,
G_IO_ERROR_INVALID_ARGUMENT,
_("Invalid endianness value. Expected 0x6c (“l”) or 0x42 (“B”) but found value 0x%02x"),
endianness);
- goto out;
+ goto fail;
}
- message->type = g_memory_buffer_read_byte (&mbuf);
- message->flags = g_memory_buffer_read_byte (&mbuf);
- major_protocol_version = g_memory_buffer_read_byte (&mbuf);
+ message->type = g_memory_buffer_read_byte (&mbuf, &local_error);
+ if (local_error)
+ goto fail;
+ message->flags = g_memory_buffer_read_byte (&mbuf, &local_error);
+ if (local_error)
+ goto fail;
+ major_protocol_version = g_memory_buffer_read_byte (&mbuf, &local_error);
+ if (local_error)
+ goto fail;
if (major_protocol_version != 1)
{
- g_set_error (error,
+ g_set_error (&local_error,
G_IO_ERROR,
G_IO_ERROR_INVALID_ARGUMENT,
_("Invalid major protocol version. Expected 1 but found %d"),
major_protocol_version);
- goto out;
+ goto fail;
}
- message_body_len = g_memory_buffer_read_uint32 (&mbuf);
- message->serial = g_memory_buffer_read_uint32 (&mbuf);
+ message_body_len = g_memory_buffer_read_uint32 (&mbuf, &local_error);
+ if (local_error)
+ goto fail;
+ message->serial = g_memory_buffer_read_uint32 (&mbuf, &local_error);
+ if (local_error)
+ goto fail;
#ifdef DEBUG_SERIALIZER
g_print ("Parsing blob (blob_len = 0x%04x bytes)\n", (gint) blob_len);
@@ -2156,9 +2261,9 @@ g_dbus_message_new_from_blob (guchar *blob,
G_DBUS_MAX_TYPE_DEPTH + 2 /* for the a{yv} */,
FALSE,
2,
- error);
+ &local_error);
if (headers == NULL)
- goto out;
+ goto fail;
g_variant_iter_init (&iter, headers);
while ((item = g_variant_iter_next_value (&iter)) != NULL)
{
@@ -2182,11 +2287,11 @@ g_dbus_message_new_from_blob (guchar *blob,
if (!g_variant_is_of_type (signature, G_VARIANT_TYPE_SIGNATURE))
{
- g_set_error_literal (error,
+ g_set_error_literal (&local_error,
G_IO_ERROR,
G_IO_ERROR_INVALID_ARGUMENT,
_("Signature header found but is not of type signature"));
- goto out;
+ goto fail;
}
signature_str = g_variant_get_string (signature, &signature_str_len);
@@ -2194,12 +2299,12 @@ g_dbus_message_new_from_blob (guchar *blob,
/* signature but no body */
if (message_body_len == 0 && signature_str_len > 0)
{
- g_set_error (error,
+ g_set_error (&local_error,
G_IO_ERROR,
G_IO_ERROR_INVALID_ARGUMENT,
_("Signature header with signature “%s” found but message body is empty"),
signature_str);
- goto out;
+ goto fail;
}
else if (signature_str_len > 0)
{
@@ -2209,13 +2314,13 @@ g_dbus_message_new_from_blob (guchar *blob,
if (!g_variant_is_signature (signature_str) ||
!g_variant_type_string_is_valid (tupled_signature_str))
{
- g_set_error (error,
+ g_set_error (&local_error,
G_IO_ERROR,
G_IO_ERROR_INVALID_ARGUMENT,
_("Parsed value “%s” is not a valid D-Bus signature (for body)"),
signature_str);
g_free (tupled_signature_str);
- goto out;
+ goto fail;
}
variant_type = g_variant_type_new (tupled_signature_str);
@@ -2228,10 +2333,10 @@ g_dbus_message_new_from_blob (guchar *blob,
G_DBUS_MAX_TYPE_DEPTH + 1 /* for the surrounding tuple */,
FALSE,
2,
- error);
+ &local_error);
g_variant_type_free (variant_type);
if (message->body == NULL)
- goto out;
+ goto fail;
}
}
else
@@ -2240,7 +2345,7 @@ g_dbus_message_new_from_blob (guchar *blob,
if (message_body_len != 0)
{
/* G_GUINT32_FORMAT doesn't work with gettext, just use %u */
- g_set_error (error,
+ g_set_error (&local_error,
G_IO_ERROR,
G_IO_ERROR_INVALID_ARGUMENT,
g_dngettext (GETTEXT_PACKAGE,
@@ -2248,29 +2353,22 @@ g_dbus_message_new_from_blob (guchar *blob,
"No signature header in message but the message body is %u bytes",
message_body_len),
message_body_len);
- goto out;
+ goto fail;
}
}
- if (!validate_headers (message, error))
+ if (!validate_headers (message, &local_error))
{
- g_prefix_error (error, _("Cannot deserialize message: "));
- goto out;
+ g_prefix_error (&local_error, _("Cannot deserialize message: "));
+ goto fail;
}
- ret = TRUE;
+ return message;
- out:
- if (ret)
- {
- return message;
- }
- else
- {
- if (message != NULL)
- g_object_unref (message);
- return NULL;
- }
+fail:
+ g_clear_object (&message);
+ g_propagate_error (error, local_error);
+ return NULL;
}
/* ---------------------------------------------------------------------------------------------------- */
@@ -2549,6 +2647,15 @@ append_value_to_blob (GVariant *value,
default:
if (g_variant_type_is_dict_entry (type) || g_variant_type_is_tuple (type))
{
+ if (!g_variant_type_first (type))
+ {
+ g_set_error_literal (error,
+ G_IO_ERROR,
+ G_IO_ERROR_INVALID_ARGUMENT,
+ _("Empty structures (tuples) are not allowed in D-Bus"));
+ goto fail;
+ }
+
padding_added = ensure_output_padding (mbuf, 8);
if (value != NULL)
{
diff --git a/gio/gdelayedsettingsbackend.c b/gio/gdelayedsettingsbackend.c
index 93ced25db..6fff6f71c 100644
--- a/gio/gdelayedsettingsbackend.c
+++ b/gio/gdelayedsettingsbackend.c
@@ -156,7 +156,8 @@ add_to_tree (gpointer key,
gpointer value,
gpointer user_data)
{
- g_tree_insert (user_data, g_strdup (key), g_variant_ref (value));
+ /* A value may be %NULL if its key has been reset */
+ g_tree_insert (user_data, g_strdup (key), (value != NULL) ? g_variant_ref (value) : NULL);
return FALSE;
}
diff --git a/gio/gnextstepsettingsbackend.m b/gio/gnextstepsettingsbackend.m
index 44ea845d8..c09d995b6 100644
--- a/gio/gnextstepsettingsbackend.m
+++ b/gio/gnextstepsettingsbackend.m
@@ -444,17 +444,17 @@ g_nextstep_settings_backend_get_ns_object (GVariant *variant)
{
NSMutableDictionary *dictionary;
GVariantIter iter;
- GVariant *name;
+ const gchar *name;
GVariant *value;
dictionary = [NSMutableDictionary dictionaryWithCapacity:g_variant_iter_init (&iter, variant)];
- while (g_variant_iter_loop (&iter, "{s*}", &name, &value))
+ while (g_variant_iter_loop (&iter, "{&s*}", &name, &value))
{
NSString *key;
id object;
- key = [NSString stringWithUTF8String:g_variant_get_string (name, NULL)];
+ key = [NSString stringWithUTF8String:name];
object = g_nextstep_settings_backend_get_ns_object (value);
[dictionary setObject:object forKey:key];
diff --git a/gio/gsettings.c b/gio/gsettings.c
index f7d39c77e..9130dafd5 100644
--- a/gio/gsettings.c
+++ b/gio/gsettings.c
@@ -343,8 +343,6 @@ struct _GSettingsPrivate
GSettingsBackend *backend;
GSettingsSchema *schema;
gchar *path;
-
- GDelayedSettingsBackend *delayed;
};
enum
@@ -642,7 +640,7 @@ g_settings_get_property (GObject *object,
break;
case PROP_DELAY_APPLY:
- g_value_set_boolean (value, settings->priv->delayed != NULL);
+ g_value_set_boolean (value, G_IS_DELAYED_SETTINGS_BACKEND (settings->priv->backend));
break;
default:
@@ -2256,19 +2254,20 @@ g_settings_set_strv (GSettings *settings,
void
g_settings_delay (GSettings *settings)
{
+ GDelayedSettingsBackend *delayed = NULL;
+
g_return_if_fail (G_IS_SETTINGS (settings));
- if (settings->priv->delayed)
+ if (G_IS_DELAYED_SETTINGS_BACKEND (settings->priv->backend))
return;
- settings->priv->delayed =
- g_delayed_settings_backend_new (settings->priv->backend,
- settings,
- settings->priv->main_context);
+ delayed = g_delayed_settings_backend_new (settings->priv->backend,
+ settings,
+ settings->priv->main_context);
g_settings_backend_unwatch (settings->priv->backend, G_OBJECT (settings));
g_object_unref (settings->priv->backend);
- settings->priv->backend = G_SETTINGS_BACKEND (settings->priv->delayed);
+ settings->priv->backend = G_SETTINGS_BACKEND (delayed);
g_settings_backend_watch (settings->priv->backend,
&listener_vtable, G_OBJECT (settings),
settings->priv->main_context);
@@ -2288,7 +2287,7 @@ g_settings_delay (GSettings *settings)
void
g_settings_apply (GSettings *settings)
{
- if (settings->priv->delayed)
+ if (G_IS_DELAYED_SETTINGS_BACKEND (settings->priv->backend))
{
GDelayedSettingsBackend *delayed;
@@ -2311,7 +2310,7 @@ g_settings_apply (GSettings *settings)
void
g_settings_revert (GSettings *settings)
{
- if (settings->priv->delayed)
+ if (G_IS_DELAYED_SETTINGS_BACKEND (settings->priv->backend))
{
GDelayedSettingsBackend *delayed;
@@ -2336,7 +2335,7 @@ g_settings_get_has_unapplied (GSettings *settings)
{
g_return_val_if_fail (G_IS_SETTINGS (settings), FALSE);
- return settings->priv->delayed &&
+ return G_IS_DELAYED_SETTINGS_BACKEND (settings->priv->backend) &&
g_delayed_settings_backend_get_has_unapplied (
G_DELAYED_SETTINGS_BACKEND (settings->priv->backend));
}
@@ -2424,9 +2423,12 @@ g_settings_is_writable (GSettings *settings,
* @settings.
*
* The schema for the child settings object must have been declared
- * in the schema of @settings using a <child> element.
+ * in the schema of @settings using a `<child>` element.
+ *
+ * The created child settings object will inherit the #GSettings:delay-apply
+ * mode from @settings.
*
- * Returns: (transfer full): a 'child' settings object
+ * Returns: (not nullable) (transfer full): a 'child' settings object
*
* Since: 2.26
*/
diff --git a/gio/gtestdbus.c b/gio/gtestdbus.c
index 703a0b3a5..992d29cef 100644
--- a/gio/gtestdbus.c
+++ b/gio/gtestdbus.c
@@ -32,6 +32,8 @@
#endif
#ifdef G_OS_WIN32
#include <io.h>
+#include <fcntl.h>
+#include <windows.h>
#endif
#include <glib.h>
@@ -44,8 +46,8 @@
#include "glibintl.h"
-#ifdef G_OS_WIN32
-#include <windows.h>
+#ifdef G_OS_UNIX
+#include "glib-unix.h"
#endif
/* -------------------------------------------------------------------------- */
@@ -436,7 +438,6 @@ struct _GTestDBusPrivate
GTestDBusFlags flags;
GPtrArray *service_dirs;
GPid bus_pid;
- gint bus_stdout_fd;
gchar *bus_address;
gboolean up;
};
@@ -596,58 +597,87 @@ write_config_file (GTestDBus *self)
return path;
}
+static gboolean
+make_pipe (gint pipe_fds[2],
+ GError **error)
+{
+#if defined(G_OS_UNIX)
+ return g_unix_open_pipe (pipe_fds, FD_CLOEXEC, error);
+#elif defined(G_OS_WIN32)
+ if (_pipe (pipe_fds, 4096, _O_BINARY) < 0)
+ {
+ int errsv = errno;
+
+ g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
+ _("Failed to create pipe for communicating with child process (%s)"),
+ g_strerror (errsv));
+ return FALSE;
+ }
+ return TRUE;
+#else
+ g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
+ _("Pipes are not supported in this platform"));
+ return FALSE;
+#endif
+}
+
static void
start_daemon (GTestDBus *self)
{
const gchar *argv[] = {"dbus-daemon", "--print-address", "--config-file=foo", NULL};
+ gint pipe_fds[2] = {-1, -1};
gchar *config_path;
gchar *config_arg;
+ gchar *print_address;
GIOChannel *channel;
- gint stdout_fd2;
gsize termpos;
GError *error = NULL;
if (g_getenv ("G_TEST_DBUS_DAEMON") != NULL)
argv[0] = (gchar *)g_getenv ("G_TEST_DBUS_DAEMON");
+ make_pipe (pipe_fds, &error);
+ g_assert_no_error (error);
+
+ print_address = g_strdup_printf ("--print-address=%d", pipe_fds[1]);
+ argv[1] = print_address;
+ g_assert_no_error (error);
+
/* Write config file and set its path in argv */
config_path = write_config_file (self);
config_arg = g_strdup_printf ("--config-file=%s", config_path);
argv[2] = config_arg;
/* Spawn dbus-daemon */
- g_spawn_async_with_pipes (NULL,
- (gchar **) argv,
- NULL,
- /* We Need this to get the pid returned on win32 */
- G_SPAWN_DO_NOT_REAP_CHILD |
- G_SPAWN_SEARCH_PATH |
- /* dbus-daemon will not abuse our descriptors, and
- * passing this means we can use posix_spawn() for speed */
- G_SPAWN_LEAVE_DESCRIPTORS_OPEN,
- NULL,
- NULL,
- &self->priv->bus_pid,
- NULL,
- &self->priv->bus_stdout_fd,
- NULL,
- &error);
+ g_spawn_async_with_pipes_and_fds (NULL,
+ argv,
+ NULL,
+ /* We Need this to get the pid returned on win32 */
+ G_SPAWN_DO_NOT_REAP_CHILD |
+ G_SPAWN_SEARCH_PATH |
+ /* dbus-daemon will not abuse our descriptors, and
+ * passing this means we can use posix_spawn() for speed */
+ G_SPAWN_LEAVE_DESCRIPTORS_OPEN,
+ NULL, NULL,
+ -1, -1, -1,
+ &pipe_fds[1], &pipe_fds[1], 1,
+ &self->priv->bus_pid,
+ NULL, NULL, NULL,
+ &error);
g_assert_no_error (error);
_g_test_watcher_add_pid (self->priv->bus_pid);
- /* Read bus address from daemon' stdout. We have to be careful to avoid
- * closing the FD, as it is passed to any D-Bus service activated processes,
- * and if we close it, they will get a SIGPIPE and die when they try to write
- * to their stdout. */
- stdout_fd2 = dup (self->priv->bus_stdout_fd);
- g_assert_cmpint (stdout_fd2, >=, 0);
- channel = g_io_channel_unix_new (stdout_fd2);
-
+ /* Read bus address from pipe */
+ channel = g_io_channel_unix_new (pipe_fds[0]);
+ pipe_fds[0] = -1;
+ g_io_channel_set_close_on_unref (channel, TRUE);
g_io_channel_read_line (channel, &self->priv->bus_address, NULL,
&termpos, &error);
g_assert_no_error (error);
self->priv->bus_address[termpos] = '\0';
+ close (pipe_fds[1]);
+ pipe_fds[1] = -1;
/* start dbus-monitor */
if (g_getenv ("G_DBUS_MONITOR") != NULL)
@@ -671,6 +701,7 @@ start_daemon (GTestDBus *self)
if (g_unlink (config_path) != 0)
g_assert_not_reached ();
+ g_free (print_address);
g_free (config_path);
g_free (config_arg);
}
@@ -687,8 +718,6 @@ stop_daemon (GTestDBus *self)
_g_test_watcher_remove_pid (self->priv->bus_pid);
g_spawn_close_pid (self->priv->bus_pid);
self->priv->bus_pid = 0;
- close (self->priv->bus_stdout_fd);
- self->priv->bus_stdout_fd = -1;
g_free (self->priv->bus_address);
self->priv->bus_address = NULL;
diff --git a/gio/gwin32appinfo.c b/gio/gwin32appinfo.c
index 3468dfee0..cafd053b4 100644
--- a/gio/gwin32appinfo.c
+++ b/gio/gwin32appinfo.c
@@ -983,7 +983,10 @@ get_verbs (GWin32RegistryKey *program_id_key,
name,
NULL);
- g_assert (subkey != NULL);
+ /* We may not have the required access rights to open the child key */
+ if (subkey == NULL)
+ continue;
+
/* The key we're looking at is "<some_root>/Shell/<this_key>",
* where "Shell" is verbshell_prefix.
* If it has a value named 'Subcommands' (doesn't matter what its data is),
diff --git a/gio/tests/gdbus-export.c b/gio/tests/gdbus-export.c
index aec21d7d0..4cdc24492 100644
--- a/gio/tests/gdbus-export.c
+++ b/gio/tests/gdbus-export.c
@@ -1792,6 +1792,184 @@ test_async_properties (void)
g_object_unref (c);
}
+typedef struct
+{
+ GDBusConnection *connection; /* (owned) */
+ guint registration_id;
+ guint subtree_registration_id;
+} ThreadedUnregistrationData;
+
+static gpointer
+unregister_thread_cb (gpointer user_data)
+{
+ ThreadedUnregistrationData *data = user_data;
+
+ /* Sleeping here makes the race more likely to be hit, as it balances the
+ * time taken to set up the thread and unregister, with the time taken to
+ * make and handle the D-Bus call. This will likely change with future kernel
+ * versions, but there isn’t a more deterministic synchronisation point that
+ * I can think of to use instead. */
+ usleep (330);
+
+ if (data->registration_id > 0)
+ g_assert_true (g_dbus_connection_unregister_object (data->connection, data->registration_id));
+
+ if (data->subtree_registration_id > 0)
+ g_assert_true (g_dbus_connection_unregister_subtree (data->connection, data->subtree_registration_id));
+
+ return NULL;
+}
+
+static void
+async_result_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GAsyncResult **result_out = user_data;
+
+ *result_out = g_object_ref (result);
+ g_main_context_wakeup (NULL);
+}
+
+/* Returns %TRUE if this iteration resolved the race with the unregistration
+ * first, %FALSE if the call handler was invoked first. */
+static gboolean
+test_threaded_unregistration_iteration (gboolean subtree)
+{
+ ThreadedUnregistrationData data = { NULL, 0, 0 };
+ ObjectRegistrationData object_registration_data = { 0, 0, 2 };
+ GError *local_error = NULL;
+ GThread *unregister_thread = NULL;
+ const gchar *object_path;
+ GVariant *value = NULL;
+ const gchar *value_str;
+ GAsyncResult *call_result = NULL;
+ gboolean unregistration_was_first;
+
+ data.connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &local_error);
+ g_assert_no_error (local_error);
+ g_assert_nonnull (data.connection);
+
+ /* Register an object or a subtree */
+ if (!subtree)
+ {
+ data.registration_id = g_dbus_connection_register_object (data.connection,
+ "/foo/boss",
+ (GDBusInterfaceInfo *) &foo_interface_info,
+ &foo_vtable,
+ &object_registration_data,
+ on_object_unregistered,
+ &local_error);
+ g_assert_no_error (local_error);
+ g_assert_cmpint (data.registration_id, >, 0);
+
+ object_path = "/foo/boss";
+ }
+ else
+ {
+ data.subtree_registration_id = g_dbus_connection_register_subtree (data.connection,
+ "/foo/boss/executives",
+ &subtree_vtable,
+ G_DBUS_SUBTREE_FLAGS_NONE,
+ &object_registration_data,
+ on_subtree_unregistered,
+ &local_error);
+ g_assert_no_error (local_error);
+ g_assert_cmpint (data.subtree_registration_id, >, 0);
+
+ object_path = "/foo/boss/executives/vp0";
+ }
+
+ /* Allow the registrations to go through. */
+ g_main_context_iteration (NULL, FALSE);
+
+ /* Spawn a thread to unregister the object/subtree. This will race with
+ * the call we subsequently make. */
+ unregister_thread = g_thread_new ("unregister-object",
+ unregister_thread_cb, &data);
+
+ /* Call a method on the object (or an object in the subtree). The callback
+ * will be invoked in this main context. */
+ g_dbus_connection_call (data.connection,
+ g_dbus_connection_get_unique_name (data.connection),
+ object_path,
+ "org.example.Foo",
+ "Method1",
+ g_variant_new ("(s)", "winwinwin"),
+ NULL,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ async_result_cb,
+ &call_result);
+
+ while (call_result == NULL)
+ g_main_context_iteration (NULL, TRUE);
+
+ value = g_dbus_connection_call_finish (data.connection, call_result, &local_error);
+
+ /* The result of the method could either be an error (that the object doesn’t
+ * exist) or a valid result, depending on how the thread was scheduled
+ * relative to the call. */
+ unregistration_was_first = (value == NULL);
+ if (value != NULL)
+ {
+ g_assert_no_error (local_error);
+ g_assert_true (g_variant_is_of_type (value, G_VARIANT_TYPE ("(s)")));
+ g_variant_get (value, "(&s)", &value_str);
+ g_assert_cmpstr (value_str, ==, "You passed the string 'winwinwin'. Jolly good!");
+ }
+ else
+ {
+ g_assert_null (value);
+ g_assert_error (local_error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD);
+ }
+
+ g_clear_pointer (&value, g_variant_unref);
+ g_clear_error (&local_error);
+
+ /* Tidy up. */
+ g_thread_join (g_steal_pointer (&unregister_thread));
+
+ g_clear_object (&call_result);
+ g_clear_object (&data.connection);
+
+ return unregistration_was_first;
+}
+
+static void
+test_threaded_unregistration (gconstpointer test_data)
+{
+ gboolean subtree = GPOINTER_TO_INT (test_data);
+ guint i;
+ guint n_iterations_unregistration_first = 0;
+ guint n_iterations_call_first = 0;
+
+ g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/2400");
+ g_test_summary ("Test that object/subtree unregistration from one thread "
+ "doesn’t cause problems when racing with method callbacks "
+ "in another thread for that object or subtree");
+
+ /* Run iterations of the test until it’s likely we’ve hit the race. Limit the
+ * number of iterations so the test doesn’t run forever if not. The choice of
+ * 100 is arbitrary. */
+ for (i = 0; i < 1000 && (n_iterations_unregistration_first < 100 || n_iterations_call_first < 100); i++)
+ {
+ if (test_threaded_unregistration_iteration (subtree))
+ n_iterations_unregistration_first++;
+ else
+ n_iterations_call_first++;
+ }
+
+ /* If the condition below is met, we probably failed to reproduce the race.
+ * Don’t fail the test, though, as we can’t always control whether we hit the
+ * race, and spurious test failures are annoying. */
+ if (n_iterations_unregistration_first < 100 ||
+ n_iterations_call_first < 100)
+ g_test_skip_printf ("Failed to reproduce race (%u iterations with unregistration first, %u with call first); skipping test",
+ n_iterations_unregistration_first, n_iterations_call_first);
+}
+
/* ---------------------------------------------------------------------------------------------------- */
int
@@ -1809,6 +1987,8 @@ main (int argc,
g_test_add_func ("/gdbus/object-registration-with-closures", test_object_registration_with_closures);
g_test_add_func ("/gdbus/registered-interfaces", test_registered_interfaces);
g_test_add_func ("/gdbus/async-properties", test_async_properties);
+ g_test_add_data_func ("/gdbus/threaded-unregistration/object", GINT_TO_POINTER (FALSE), test_threaded_unregistration);
+ g_test_add_data_func ("/gdbus/threaded-unregistration/subtree", GINT_TO_POINTER (TRUE), test_threaded_unregistration);
/* TODO: check that we spit out correct introspection data */
/* TODO: check that registering a whole subtree works */
diff --git a/gio/tests/gdbus-serialization.c b/gio/tests/gdbus-serialization.c
index a3b03c5e0..7cc46a4ae 100644
--- a/gio/tests/gdbus-serialization.c
+++ b/gio/tests/gdbus-serialization.c
@@ -1472,6 +1472,149 @@ test_message_parse_deep_body_nesting (void)
/* ---------------------------------------------------------------------------------------------------- */
+static void
+test_message_parse_truncated (void)
+{
+ GDBusMessage *message = NULL;
+ GDBusMessage *message2 = NULL;
+ GVariantBuilder builder;
+ guchar *blob = NULL;
+ gsize size = 0;
+ GError *error = NULL;
+
+ g_test_summary ("Test that truncated messages are properly rejected.");
+ g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/2528");
+
+ message = g_dbus_message_new ();
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("(asbynqiuxtd)"));
+ g_variant_builder_open (&builder, G_VARIANT_TYPE ("as"));
+ g_variant_builder_add (&builder, "s", "fourtytwo");
+ g_variant_builder_close (&builder);
+ g_variant_builder_add (&builder, "b", TRUE);
+ g_variant_builder_add (&builder, "y", 42);
+ g_variant_builder_add (&builder, "n", 42);
+ g_variant_builder_add (&builder, "q", 42);
+ g_variant_builder_add (&builder, "i", 42);
+ g_variant_builder_add (&builder, "u", 42);
+ g_variant_builder_add (&builder, "x", 42);
+ g_variant_builder_add (&builder, "t", 42);
+ g_variant_builder_add (&builder, "d", (gdouble) 42);
+
+ g_dbus_message_set_message_type (message, G_DBUS_MESSAGE_TYPE_METHOD_CALL);
+ g_dbus_message_set_header (message, G_DBUS_MESSAGE_HEADER_FIELD_PATH,
+ g_variant_new_object_path ("/foo/bar"));
+ g_dbus_message_set_header (message, G_DBUS_MESSAGE_HEADER_FIELD_MEMBER,
+ g_variant_new_string ("Member"));
+ g_dbus_message_set_body (message, g_variant_builder_end (&builder));
+
+ blob = g_dbus_message_to_blob (message, &size, G_DBUS_CAPABILITY_FLAGS_NONE, &error);
+ g_assert_no_error (error);
+
+ g_clear_object (&message);
+
+ /* Try parsing all possible prefixes of the full @blob. */
+ for (gsize i = 0; i < size; i++)
+ {
+ message2 = g_dbus_message_new_from_blob (blob, i, G_DBUS_CAPABILITY_FLAGS_NONE, &error);
+ g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
+ g_assert_null (message2);
+ g_clear_error (&error);
+ }
+
+ message2 = g_dbus_message_new_from_blob (blob, size, G_DBUS_CAPABILITY_FLAGS_NONE, &error);
+ g_assert_no_error (error);
+ g_assert_true (G_IS_DBUS_MESSAGE (message2));
+ g_clear_object (&message2);
+
+ g_free (blob);
+}
+
+static void
+test_message_parse_empty_structure (void)
+{
+ const guint8 data[] =
+ {
+ 'l', /* little-endian byte order */
+ 0x02, /* message type (method return) */
+ 0x00, /* message flags (none) */
+ 0x01, /* major protocol version */
+ 0x08, 0x00, 0x00, 0x00, /* body length (in bytes) */
+ 0x00, 0x00, 0x00, 0x00, /* message serial */
+ /* a{yv} of header fields */
+ 0x20, 0x00, 0x00, 0x00, /* array length (in bytes), must be a multiple of 8 */
+ 0x01, /* array key (PATH) */
+ 0x01, /* signature length */
+ 'o', /* type (OBJECT_PATH) */
+ 0x00, /* nul terminator */
+ 0x05, 0x00, 0x00, 0x00, /* length 5 */
+ '/', 'p', 'a', 't', 'h', 0x00, 0x00, 0x00, /* string '/path' and padding */
+ 0x03, /* array key (MEMBER) */
+ 0x01, /* signature length */
+ 's', /* type (STRING) */
+ 0x00, /* nul terminator */
+ 0x06, 0x00, 0x00, 0x00, /* length 6 */
+ 'M', 'e', 'm', 'b', 'e', 'r', 0x00, 0x00, /* string 'Member' and padding */
+ 0x08, /* array key (SIGNATURE) */
+ 0x01, /* signature length */
+ 'g', /* type (SIGNATURE) */
+ 0x00, /* nul terminator */
+ 0x03, /* length 3 */
+ 'a', '(', ')', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* type 'a()' and padding */
+ 0x08, 0x00, 0x00, 0x00, /* array length: 4 bytes */
+ 0x00, 0x00, 0x00, 0x00, /* padding to 8 bytes */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* array data */
+ 0x00
+ };
+ gsize size = sizeof (data);
+ GDBusMessage *message = NULL;
+ GError *local_error = NULL;
+
+ g_test_summary ("Test that empty structures are rejected when parsing.");
+ g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/2557");
+
+ message = g_dbus_message_new_from_blob ((guchar *) data, size,
+ G_DBUS_CAPABILITY_FLAGS_NONE,
+ &local_error);
+ g_assert_error (local_error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
+ g_assert_cmpstr (local_error->message, ==, "Empty structures (tuples) are not allowed in D-Bus");
+ g_assert_null (message);
+
+ g_clear_error (&local_error);
+}
+
+static void
+test_message_serialize_empty_structure (void)
+{
+ GDBusMessage *message;
+ GVariantBuilder builder;
+ gsize size = 0;
+ GError *local_error = NULL;
+
+ g_test_summary ("Test that empty structures are rejected when serializing.");
+ g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/2557");
+
+ message = g_dbus_message_new ();
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("(a())"));
+ g_variant_builder_open (&builder, G_VARIANT_TYPE ("a()"));
+ g_variant_builder_add (&builder, "()");
+ g_variant_builder_close (&builder);
+ g_dbus_message_set_message_type (message, G_DBUS_MESSAGE_TYPE_METHOD_CALL);
+ g_dbus_message_set_header (message, G_DBUS_MESSAGE_HEADER_FIELD_PATH,
+ g_variant_new_object_path ("/path"));
+ g_dbus_message_set_header (message, G_DBUS_MESSAGE_HEADER_FIELD_MEMBER,
+ g_variant_new_string ("Member"));
+ g_dbus_message_set_body (message, g_variant_builder_end (&builder));
+
+ g_dbus_message_to_blob (message, &size, G_DBUS_CAPABILITY_FLAGS_NONE, &local_error);
+ g_assert_error (local_error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
+ g_assert_cmpstr (local_error->message, ==, "Empty structures (tuples) are not allowed in D-Bus");
+
+ g_clear_error (&local_error);
+ g_clear_object (&message);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
int
main (int argc,
char *argv[])
@@ -1491,6 +1634,8 @@ main (int argc,
test_message_serialize_header_checks);
g_test_add_func ("/gdbus/message-serialize/double-array",
test_message_serialize_double_array);
+ g_test_add_func ("/gdbus/message-serialize/empty-structure",
+ test_message_serialize_empty_structure);
g_test_add_func ("/gdbus/message-parse/empty-arrays-of-arrays",
test_message_parse_empty_arrays_of_arrays);
@@ -1506,6 +1651,10 @@ main (int argc,
test_message_parse_deep_header_nesting);
g_test_add_func ("/gdbus/message-parse/deep-body-nesting",
test_message_parse_deep_body_nesting);
+ g_test_add_func ("/gdbus/message-parse/truncated",
+ test_message_parse_truncated);
+ g_test_add_func ("/gdbus/message-parse/empty-structure",
+ test_message_parse_empty_structure);
return g_test_run();
}
diff --git a/gio/tests/gsettings.c b/gio/tests/gsettings.c
index 42eeccd7a..dad1623b7 100644
--- a/gio/tests/gsettings.c
+++ b/gio/tests/gsettings.c
@@ -56,6 +56,15 @@ check_and_free (GVariant *value,
g_variant_unref (value);
}
+/* Wrapper around g_assert_cmpstr() which gets a setting from a #GSettings
+ * using g_settings_get(). */
+#define settings_assert_cmpstr(settings, key, op, expected_value) G_STMT_START { \
+ gchar *__str; \
+ g_settings_get ((settings), (key), "s", &__str); \
+ g_assert_cmpstr (__str, op, (expected_value)); \
+ g_free (__str); \
+} G_STMT_END
+
/* Just to get warmed up: Read and set a string, and
* verify that can read the changed string back
@@ -88,15 +97,10 @@ test_basic (void)
g_object_unref (b);
g_free (path);
- g_settings_get (settings, "greeting", "s", &str);
- g_assert_cmpstr (str, ==, "Hello, earthlings");
- g_free (str);
+ settings_assert_cmpstr (settings, "greeting", ==, "Hello, earthlings");
g_settings_set (settings, "greeting", "s", "goodbye world");
- g_settings_get (settings, "greeting", "s", &str);
- g_assert_cmpstr (str, ==, "goodbye world");
- g_free (str);
- str = NULL;
+ settings_assert_cmpstr (settings, "greeting", ==, "goodbye world");
if (!backend_set && g_test_undefined ())
{
@@ -110,10 +114,7 @@ test_basic (void)
g_object_unref (tmp_settings);
}
- g_settings_get (settings, "greeting", "s", &str);
- g_assert_cmpstr (str, ==, "goodbye world");
- g_free (str);
- str = NULL;
+ settings_assert_cmpstr (settings, "greeting", ==, "goodbye world");
g_settings_reset (settings, "greeting");
str = g_settings_get_string (settings, "greeting");
@@ -342,10 +343,7 @@ test_basic_types (void)
g_settings_get (settings, "test-double", "d", &d);
g_assert_cmpfloat (d, ==, G_MAXDOUBLE);
- g_settings_get (settings, "test-string", "s", &str);
- g_assert_cmpstr (str, ==, "a string, it seems");
- g_free (str);
- str = NULL;
+ settings_assert_cmpstr (settings, "test-string", ==, "a string, it seems");
g_settings_get (settings, "test-objectpath", "o", &str);
g_assert_cmpstr (str, ==, "/a/object/path");
@@ -484,7 +482,6 @@ test_delay_apply (void)
{
GSettings *settings;
GSettings *settings2;
- gchar *str;
gboolean writable;
GVariant *v;
const gchar *s;
@@ -530,20 +527,14 @@ test_delay_apply (void)
writable = g_settings_is_writable (settings, "greeting");
g_assert_true (writable);
- g_settings_get (settings, "greeting", "s", &str);
- g_assert_cmpstr (str, ==, "greetings from test_delay_apply");
- g_free (str);
- str = NULL;
+ settings_assert_cmpstr (settings, "greeting", ==, "greetings from test_delay_apply");
v = g_settings_get_user_value (settings, "greeting");
s = g_variant_get_string (v, NULL);
g_assert_cmpstr (s, ==, "greetings from test_delay_apply");
g_variant_unref (v);
- g_settings_get (settings2, "greeting", "s", &str);
- g_assert_cmpstr (str, ==, "top o' the morning");
- g_free (str);
- str = NULL;
+ settings_assert_cmpstr (settings2, "greeting", ==, "top o' the morning");
g_assert_true (g_settings_get_has_unapplied (settings));
g_assert_false (g_settings_get_has_unapplied (settings2));
@@ -556,15 +547,8 @@ test_delay_apply (void)
g_assert_false (changed_cb_called);
g_assert_true (changed_cb_called2);
- g_settings_get (settings, "greeting", "s", &str);
- g_assert_cmpstr (str, ==, "greetings from test_delay_apply");
- g_free (str);
- str = NULL;
-
- g_settings_get (settings2, "greeting", "s", &str);
- g_assert_cmpstr (str, ==, "greetings from test_delay_apply");
- g_free (str);
- str = NULL;
+ settings_assert_cmpstr (settings, "greeting", ==, "greetings from test_delay_apply");
+ settings_assert_cmpstr (settings2, "greeting", ==, "greetings from test_delay_apply");
g_assert_false (g_settings_get_has_unapplied (settings));
g_assert_false (g_settings_get_has_unapplied (settings2));
@@ -572,9 +556,7 @@ test_delay_apply (void)
g_settings_reset (settings, "greeting");
g_settings_apply (settings);
- g_settings_get (settings, "greeting", "s", &str);
- g_assert_cmpstr (str, ==, "Hello, earthlings");
- g_free (str);
+ settings_assert_cmpstr (settings, "greeting", ==, "Hello, earthlings");
g_object_unref (settings2);
g_object_unref (settings);
@@ -588,30 +570,20 @@ test_delay_revert (void)
{
GSettings *settings;
GSettings *settings2;
- gchar *str;
settings = g_settings_new ("org.gtk.test");
settings2 = g_settings_new ("org.gtk.test");
g_settings_set (settings2, "greeting", "s", "top o' the morning");
- g_settings_get (settings, "greeting", "s", &str);
- g_assert_cmpstr (str, ==, "top o' the morning");
- g_free (str);
+ settings_assert_cmpstr (settings, "greeting", ==, "top o' the morning");
g_settings_delay (settings);
g_settings_set (settings, "greeting", "s", "greetings from test_delay_revert");
- g_settings_get (settings, "greeting", "s", &str);
- g_assert_cmpstr (str, ==, "greetings from test_delay_revert");
- g_free (str);
- str = NULL;
-
- g_settings_get (settings2, "greeting", "s", &str);
- g_assert_cmpstr (str, ==, "top o' the morning");
- g_free (str);
- str = NULL;
+ settings_assert_cmpstr (settings, "greeting", ==, "greetings from test_delay_revert");
+ settings_assert_cmpstr (settings2, "greeting", ==, "top o' the morning");
g_assert_true (g_settings_get_has_unapplied (settings));
@@ -619,15 +591,8 @@ test_delay_revert (void)
g_assert_false (g_settings_get_has_unapplied (settings));
- g_settings_get (settings, "greeting", "s", &str);
- g_assert_cmpstr (str, ==, "top o' the morning");
- g_free (str);
- str = NULL;
-
- g_settings_get (settings2, "greeting", "s", &str);
- g_assert_cmpstr (str, ==, "top o' the morning");
- g_free (str);
- str = NULL;
+ settings_assert_cmpstr (settings, "greeting", ==, "top o' the morning");
+ settings_assert_cmpstr (settings2, "greeting", ==, "top o' the morning");
g_object_unref (settings2);
g_object_unref (settings);
@@ -654,7 +619,7 @@ test_delay_child (void)
g_assert_nonnull (child);
g_object_get (child, "delay-apply", &delay, NULL);
- g_assert_false (delay);
+ g_assert_true (delay);
g_settings_get (child, "test-byte", "y", &byte);
g_assert_cmpuint (byte, ==, 36);
@@ -665,18 +630,66 @@ test_delay_child (void)
g_settings_get (base, "test-byte", "y", &byte);
g_assert_cmpuint (byte, ==, 36);
+ /* apply the child and the changes should be saved */
+ g_settings_apply (child);
+ g_settings_get (base, "test-byte", "y", &byte);
+ g_assert_cmpuint (byte, ==, 42);
+
g_object_unref (child);
g_object_unref (settings);
g_object_unref (base);
}
static void
+test_delay_reset_key (void)
+{
+ GSettings *direct_settings = NULL, *delayed_settings = NULL;
+
+ g_test_summary ("Test that resetting a key on a delayed settings instance works");
+
+ delayed_settings = g_settings_new ("org.gtk.test");
+ direct_settings = g_settings_new ("org.gtk.test");
+
+ g_settings_set (direct_settings, "greeting", "s", "ey up");
+
+ settings_assert_cmpstr (delayed_settings, "greeting", ==, "ey up");
+
+ /* Set up a delayed settings backend. */
+ g_settings_delay (delayed_settings);
+
+ g_settings_set (delayed_settings, "greeting", "s", "how do");
+
+ settings_assert_cmpstr (delayed_settings, "greeting", ==, "how do");
+ settings_assert_cmpstr (direct_settings, "greeting", ==, "ey up");
+
+ g_assert_true (g_settings_get_has_unapplied (delayed_settings));
+
+ g_settings_reset (delayed_settings, "greeting");
+
+ /* There are still unapplied settings, because the reset is resetting to the
+ * value from the schema, not the value from @direct_settings. */
+ g_assert_true (g_settings_get_has_unapplied (delayed_settings));
+
+ settings_assert_cmpstr (delayed_settings, "greeting", ==, "Hello, earthlings");
+ settings_assert_cmpstr (direct_settings, "greeting", ==, "ey up");
+
+ /* Apply the settings changes (i.e. the reset). */
+ g_settings_apply (delayed_settings);
+
+ g_assert_false (g_settings_get_has_unapplied (delayed_settings));
+
+ settings_assert_cmpstr (delayed_settings, "greeting", ==, "Hello, earthlings");
+ settings_assert_cmpstr (direct_settings, "greeting", ==, "Hello, earthlings");
+
+ g_object_unref (direct_settings);
+ g_object_unref (delayed_settings);
+}
+
+static void
keys_changed_cb (GSettings *settings,
const GQuark *keys,
gint n_keys)
{
- gchar *str;
-
g_assert_cmpint (n_keys, ==, 2);
g_assert_true ((keys[0] == g_quark_from_static_string ("greeting") &&
@@ -684,15 +697,8 @@ keys_changed_cb (GSettings *settings,
(keys[1] == g_quark_from_static_string ("greeting") &&
keys[0] == g_quark_from_static_string ("farewell")));
- g_settings_get (settings, "greeting", "s", &str);
- g_assert_cmpstr (str, ==, "greetings from test_atomic");
- g_free (str);
- str = NULL;
-
- g_settings_get (settings, "farewell", "s", &str);
- g_assert_cmpstr (str, ==, "atomic bye-bye");
- g_free (str);
- str = NULL;
+ settings_assert_cmpstr (settings, "greeting", ==, "greetings from test_atomic");
+ settings_assert_cmpstr (settings, "farewell", ==, "atomic bye-bye");
}
/* Check that delay-applied changes appear atomically.
@@ -704,7 +710,6 @@ test_atomic (void)
{
GSettings *settings;
GSettings *settings2;
- gchar *str;
settings = g_settings_new ("org.gtk.test");
settings2 = g_settings_new ("org.gtk.test");
@@ -724,25 +729,10 @@ test_atomic (void)
g_settings_apply (settings);
- g_settings_get (settings, "greeting", "s", &str);
- g_assert_cmpstr (str, ==, "greetings from test_atomic");
- g_free (str);
- str = NULL;
-
- g_settings_get (settings, "farewell", "s", &str);
- g_assert_cmpstr (str, ==, "atomic bye-bye");
- g_free (str);
- str = NULL;
-
- g_settings_get (settings2, "greeting", "s", &str);
- g_assert_cmpstr (str, ==, "greetings from test_atomic");
- g_free (str);
- str = NULL;
-
- g_settings_get (settings2, "farewell", "s", &str);
- g_assert_cmpstr (str, ==, "atomic bye-bye");
- g_free (str);
- str = NULL;
+ settings_assert_cmpstr (settings, "greeting", ==, "greetings from test_atomic");
+ settings_assert_cmpstr (settings, "farewell", ==, "atomic bye-bye");
+ settings_assert_cmpstr (settings2, "greeting", ==, "greetings from test_atomic");
+ settings_assert_cmpstr (settings2, "farewell", ==, "atomic bye-bye");
g_object_unref (settings2);
g_object_unref (settings);
@@ -851,13 +841,7 @@ test_l10n_context (void)
setlocale (LC_MESSAGES, "de_DE.UTF-8");
/* Only do the test if translation is actually working... */
if (g_str_equal (dgettext ("test", "\"Unnamed\""), "\"Unbenannt\""))
- {
- g_settings_get (settings, "backspace", "s", &str);
-
- g_assert_cmpstr (str, ==, "Löschen");
- g_free (str);
- str = NULL;
- }
+ settings_assert_cmpstr (settings, "backspace", ==, "Löschen");
else
g_printerr ("warning: translation is not working... skipping test. ");
@@ -2767,14 +2751,10 @@ test_null_backend (void)
g_assert_cmpstr (str, ==, "org.gtk.test");
g_free (str);
- g_settings_get (settings, "greeting", "s", &str);
- g_assert_cmpstr (str, ==, "Hello, earthlings");
- g_free (str);
+ settings_assert_cmpstr (settings, "greeting", ==, "Hello, earthlings");
g_settings_set (settings, "greeting", "s", "goodbye world");
- g_settings_get (settings, "greeting", "s", &str);
- g_assert_cmpstr (str, ==, "Hello, earthlings");
- g_free (str);
+ settings_assert_cmpstr (settings, "greeting", ==, "Hello, earthlings");
writable = g_settings_is_writable (settings, "greeting");
g_assert_false (writable);
@@ -2784,9 +2764,7 @@ test_null_backend (void)
g_settings_delay (settings);
g_settings_set (settings, "greeting", "s", "goodbye world");
g_settings_apply (settings);
- g_settings_get (settings, "greeting", "s", &str);
- g_assert_cmpstr (str, ==, "Hello, earthlings");
- g_free (str);
+ settings_assert_cmpstr (settings, "greeting", ==, "Hello, earthlings");
g_object_unref (settings);
g_object_unref (backend);
@@ -3114,6 +3092,7 @@ main (int argc, char *argv[])
g_test_add_func ("/gsettings/delay-apply", test_delay_apply);
g_test_add_func ("/gsettings/delay-revert", test_delay_revert);
g_test_add_func ("/gsettings/delay-child", test_delay_child);
+ g_test_add_func ("/gsettings/delay-reset-key", test_delay_reset_key);
g_test_add_func ("/gsettings/atomic", test_atomic);
g_test_add_func ("/gsettings/simple-binding", test_simple_binding);
diff --git a/gio/tests/meson.build b/gio/tests/meson.build
index 5dbfb8e60..b563e8dde 100644
--- a/gio/tests/meson.build
+++ b/gio/tests/meson.build
@@ -17,7 +17,7 @@ if build_machine.system() == 'linux'
libutil_name = 'libutil'
libutil = run_command('sh', '-c',
'''ldconfig -p | grep -o "[[:space:]]@0@\.so\(\.[0-9]\+\)\?\b"'''
- .format(libutil_name)).stdout().strip().split('\n')
+ .format(libutil_name), check: false).stdout().strip().split('\n')
if libutil.length() > 0
message('Found libutil as @0@'.format(libutil[0]))
@@ -683,7 +683,14 @@ if not meson.is_cross_build() or meson.has_exe_wrapper()
objcopy_supports_add_symbol = false
objcopy = find_program('objcopy', required : false)
if objcopy.found()
- objcopy_supports_add_symbol = run_command(objcopy, '--help').stdout().contains('--add-symbol')
+ # FIXME: This should be `check: true` because we never really expect
+ # `objcopy --help` to fail, given that `objcopy` exists. However, it does
+ # fail on FreeBSD because ELF Tool Chain has
+ # [a bug](https://sourceforge.net/p/elftoolchain/code/3950/).
+ # This can be changed back to `check: true` once our CI uses a FreeBSD
+ # version which includes the fix.
+ # See https://gitlab.gnome.org/GNOME/glib/-/merge_requests/2360#note_1318608
+ objcopy_supports_add_symbol = run_command(objcopy, '--help', check: false).stdout().contains('--add-symbol')
endif
ld = find_program('ld', required : false)