kdbus: handle sending messages with any number of vectors 76/159176/4 accepted/tizen/unified/20171115.061201 submit/tizen/20171113.071309
authorAdrian Szyndela <adrian.s@samsung.com>
Mon, 6 Nov 2017 13:53:11 +0000 (14:53 +0100)
committerAdrian Szyndela <adrian.s@samsung.com>
Fri, 10 Nov 2017 11:01:36 +0000 (12:01 +0100)
Bugfix. Sending function had an arbitrary limit for kdbus_msg structure size.
Any additional vector added 32 bytes to the structure. Due to the way glib
handles data, in some specific cases number of vectors could increase
significantly, eventually exhausting the limit.
The fix removes the arbitrary limit, exchanging it to standard system memory
limits, by precalculating and allocating needed space, instead of allocating
fixed amount of memory.

Change-Id: I5aa483e26f6bababababadd79422b7abe0b46b7d

gio/gkdbus.c

index 6a5aa75..a09aef0 100755 (executable)
@@ -63,7 +63,7 @@
 #include "glibintl.h"
 #include "gunixfdmessage.h"
 
-#define KDBUS_MSG_MAX_SIZE         8192
+#define KDBUS_MSG_MAX_SIZE          8192
 #define KDBUS_INFINITE_TIMEOUT_NS   0x3fffffffffffffffLLU
 
 #define RECEIVE_POOL_SIZE_DEFAULT_SIZE    (16 * 1024LU * 1024LU)
@@ -3102,9 +3102,6 @@ g_kdbus_msg_append_item (struct kdbus_msg  *msg,
 
   item_size = size + G_STRUCT_OFFSET(struct kdbus_item, data);
 
-  if (msg->size + item_size > KDBUS_MSG_MAX_SIZE)
-    return FALSE;
-
   msg->size += (-msg->size) & 7;
   item = (struct kdbus_item *) ((guchar *) msg + msg->size);
   item->type = type;
@@ -3159,8 +3156,6 @@ g_kdbus_msg_append_bloom (struct kdbus_msg  *msg,
   bloom_item_size = G_STRUCT_OFFSET (struct kdbus_item, bloom_filter) +
                     G_STRUCT_OFFSET (struct kdbus_bloom_filter, data) +
                     size;
-  if (msg->size + bloom_item_size > KDBUS_MSG_MAX_SIZE)
-    return NULL;
 
   msg->size += (-msg->size) & 7;
   bloom_item = (struct kdbus_item *) ((guchar *) msg + msg->size);
@@ -3172,49 +3167,174 @@ g_kdbus_msg_append_bloom (struct kdbus_msg  *msg,
   return &bloom_item->bloom_filter;
 }
 
-
 /*
- * _g_kdbus_send
+ * Returns header size.
  */
-static gboolean
-_g_kdbus_send (GKDBusWorker  *worker,
-               GDBusMessage  *message,
-               GDBusMessage **out_reply,
-               gint           timeout_msec,
-               GCancellable  *cancellable,
-               GError       **error)
+static gsize
+prepare_body_vectors (GKDBusWorker    *worker,
+                      GDBusMessage    *message,
+                      GVariantVectors *body_vectors,
+                      gboolean        *lg_h_field_exist)
 {
-  struct kdbus_msg *msg;
-  GVariantVectors body_vectors;
-  struct kdbus_cmd_send *send;
-  gsize send_size;
-  const gchar *dst_name;
-  gboolean result;
-  gboolean lg_h_field_exist = FALSE;
+  struct dbus_fixed_header fh;
+  GHashTableIter header_iter;
+  GVariantBuilder builder;
+  gpointer key, value;
+  GVariant *parts[3];
+  GVariant *body;
+  gsize header_size;
 
-  gint memfd_fd;
-  gint cancel_fd;
+  *lg_h_field_exist = FALSE;
 
-  gsize header_size;
+  fh.endian = (G_BYTE_ORDER == G_LITTLE_ENDIAN) ? 'l': 'B';
+  fh.type = g_dbus_message_get_message_type (message);
+  fh.flags = g_dbus_message_get_flags (message);
+  fh.version = 2;
+  fh.reserved = 0;
+  fh.serial = g_dbus_message_get_serial (message);
+  parts[0] = g_variant_new_from_data (DBUS_FIXED_HEADER_TYPE, &fh, sizeof fh, TRUE, NULL, NULL);
+  g_assert_nonnull (parts[0]);
 
-  g_return_val_if_fail (G_IS_KDBUS_WORKER (worker), FALSE);
+  g_dbus_message_init_header_iter (message, &header_iter);
+  g_variant_builder_init (&builder, DBUS_EXTENDED_HEADER_TYPE);
 
-  send = NULL;
-  send_size = sizeof(*send);
+  /* We set the sender field to the correct value for ourselves */
+  g_variant_builder_add (&builder, "{tv}",
+                         (guint64) G_DBUS_MESSAGE_HEADER_FIELD_SENDER,
+                         g_variant_new_printf (":1.%"G_GUINT64_FORMAT, worker->unique_id));
 
-  msg = alloca (KDBUS_MSG_MAX_SIZE);
-  result = TRUE;
+  while (g_hash_table_iter_next (&header_iter, &key, &value))
+    {
+      guint64 key_int = (gsize) key;
 
-  memfd_fd = -1;
-  cancel_fd = -1;
+      switch (key_int)
+        {
+          /* These are the normal header fields that get passed
+           * straight through.
+           */
+          case G_DBUS_MESSAGE_HEADER_FIELD_REPLY_SERIAL:
+            g_variant_builder_add (&builder, "{tv}", key_int, g_variant_new_uint64 (g_dbus_message_get_reply_serial(message)));
+            continue;
 
-  /* fill in as we go... */
-  memset (msg, 0, sizeof (struct kdbus_msg));
-  msg->size = sizeof (struct kdbus_msg);
-  msg->payload_type = KDBUS_PAYLOAD_DBUS;
-  msg->cookie = g_dbus_message_get_serial(message);
 
-  /* Message destination */
+          case G_DBUS_MESSAGE_HEADER_FIELD_PATH:
+          case G_DBUS_MESSAGE_HEADER_FIELD_INTERFACE:
+          case G_DBUS_MESSAGE_HEADER_FIELD_MEMBER:
+          case G_DBUS_MESSAGE_HEADER_FIELD_ERROR_NAME:
+          case G_DBUS_MESSAGE_HEADER_FIELD_DESTINATION:
+          case G_DBUS_MESSAGE_HEADER_FIELD_NUM_UNIX_FDS:
+            g_variant_builder_add (&builder, "{tv}", key_int, value);
+            /* This is a little bit gross.
+             *
+             * We must send the header part of the message in a single
+             * vector as per kdbus rules, but the GVariant serialiser
+             * code will split any item >= 128 bytes into its own
+             * vector to save the copy.
+             *
+             * Anyway, kdbus does not check it, and libraries handle it...
+             */
+
+            if (g_variant_get_size (value) >= 128)
+              *lg_h_field_exist = TRUE;
+
+            continue;
+
+          /* We send this one unconditionally, but set it ourselves */
+          case G_DBUS_MESSAGE_HEADER_FIELD_SENDER:
+            continue;
+
+          /* We don't send these at all in GVariant format */
+          case G_DBUS_MESSAGE_HEADER_FIELD_SIGNATURE:
+            continue;
+
+          default:
+            g_assert_not_reached ();
+        }
+    }
+  parts[1] = g_variant_builder_end (&builder);
+  g_assert_nonnull (parts[1]);
+
+  body = g_dbus_message_get_body (message);
+  if (!body)
+    body = g_variant_new ("()");
+  parts[2] = g_variant_new_variant (body);
+  g_assert_nonnull (parts[2]);
+
+  header_size = g_variant_get_size (parts[0]) + g_variant_get_size (parts[1]);
+
+  body = g_variant_ref_sink (g_variant_new_tuple (parts, G_N_ELEMENTS (parts)));
+  GLIB_PRIVATE_CALL(g_variant_to_vectors) (body, body_vectors);
+
+  /* Sanity check to make sure the header is really contiguous:
+   *
+   *  - we must have at least one vector in the output
+   *  - the first vector must completely contain at least the header; this
+   *    condition must be assured later, during composing kdbus vectors.
+   */
+  g_assert_cmpint (body_vectors->vectors->len, >, 0);
+
+  g_variant_unref (body);
+
+  return header_size;
+}
+
+static void
+make_single_header_vector (GVariantVectors *body_vectors,
+                           gsize            header_size,
+                           gboolean         lg_h_field_exist)
+{
+  guint i = 0;
+  gsize added_vectors_size = 0;
+  GVariantVector *first_vector;
+
+  /* Merge all header vectors into single vector */
+  first_vector = &g_array_index (body_vectors->vectors, GVariantVector, 0);
+  if (first_vector->size < header_size)
+    {
+      /* There are multiple header vectors, we have to join them together into a single one */
+      GByteArray *header = g_byte_array_sized_new (header_size);
+      gsize dummy_size;
+
+      g_assert_nonnull (header);
+      g_assert_true (lg_h_field_exist);
+
+      while (added_vectors_size < header_size && i < body_vectors->vectors->len)
+        {
+          GVariantVector *vector = &g_array_index (body_vectors->vectors, GVariantVector, i);
+
+          if (vector->gbytes)
+            {
+              g_byte_array_append (header, vector->data.pointer, vector->size);
+              g_bytes_unref (vector->gbytes);
+            }
+          else
+            {
+              g_byte_array_append (header, body_vectors->extra_bytes->data + vector->data.offset, vector->size);
+            }
+
+          added_vectors_size += vector->size;
+          i++;
+        }
+
+      /* Sanity check if the first vector contains at least the complete header */
+      g_assert_cmpint (added_vectors_size, >=, header_size);
+
+      first_vector->gbytes = g_byte_array_free_to_bytes (header);
+      first_vector->data.pointer = g_bytes_get_data (first_vector->gbytes, &dummy_size);
+      first_vector->size = added_vectors_size;
+
+      g_array_remove_range (body_vectors->vectors, 1, i-1);
+    }
+  /* else: If there is only a single header vector, then just go */
+}
+
+static gboolean
+add_message_destination_item (struct kdbus_msg *msg,
+                              GDBusMessage     *message,
+                              GError          **error)
+{
+  const gchar *dst_name;
+
   dst_name = g_dbus_message_get_destination (message);
   if (dst_name != NULL)
     {
@@ -3241,238 +3361,247 @@ _g_kdbus_send (GKDBusWorker  *worker,
   else
     msg->dst_id = KDBUS_DST_ID_BROADCAST;
 
-  /* File descriptors */
-  {
-    GUnixFDList *fd_list;
+  return TRUE;
+}
 
-    fd_list = g_dbus_message_get_unix_fd_list (message);
+static void
+add_file_descriptors_item (struct kdbus_msg *msg,
+                           GDBusMessage     *message)
+{
+  GUnixFDList *fd_list;
 
-    if (fd_list != NULL)
-      {
-        const gint *fds;
-        gint n_fds;
+  fd_list = g_dbus_message_get_unix_fd_list (message);
+  if (fd_list != NULL)
+    {
+      const gint *fds;
+      gint n_fds;
 
-        fds = g_unix_fd_list_peek_fds (fd_list, &n_fds);
+      fds = g_unix_fd_list_peek_fds (fd_list, &n_fds);
 
-        if (n_fds)
-          g_kdbus_msg_append_item (msg, KDBUS_ITEM_FDS, fds, sizeof (gint) * n_fds);
-      }
-  }
-
-  /* Message body */
-  {
-    struct dbus_fixed_header fh;
-    GHashTableIter header_iter;
-    GVariantBuilder builder;
-    gpointer key, value;
-    GVariant *parts[3];
-    GVariant *body;
-
-    fh.endian = (G_BYTE_ORDER == G_LITTLE_ENDIAN) ? 'l': 'B';
-    fh.type = g_dbus_message_get_message_type (message);
-    fh.flags = g_dbus_message_get_flags (message);
-    fh.version = 2;
-    fh.reserved = 0;
-    fh.serial = g_dbus_message_get_serial (message);
-    parts[0] = g_variant_new_from_data (DBUS_FIXED_HEADER_TYPE, &fh, sizeof fh, TRUE, NULL, NULL);
-
-    g_dbus_message_init_header_iter (message, &header_iter);
-    g_variant_builder_init (&builder, DBUS_EXTENDED_HEADER_TYPE);
-
-    /* We set the sender field to the correct value for ourselves */
-    g_variant_builder_add (&builder, "{tv}",
-                           (guint64) G_DBUS_MESSAGE_HEADER_FIELD_SENDER,
-                           g_variant_new_printf (":1.%"G_GUINT64_FORMAT, worker->unique_id));
-
-    while (g_hash_table_iter_next (&header_iter, &key, &value))
-      {
-        guint64 key_int = (gsize) key;
+      if (n_fds)
+        g_kdbus_msg_append_item (msg, KDBUS_ITEM_FDS, fds, sizeof (gint) * n_fds);
+    }
+}
 
-        switch (key_int)
-          {
-            /* These are the normal header fields that get passed
-             * straight through.
-             */
-            case G_DBUS_MESSAGE_HEADER_FIELD_REPLY_SERIAL:
-              g_variant_builder_add (&builder, "{tv}", key_int, g_variant_new_uint64 (g_dbus_message_get_reply_serial(message)));
-              continue;
+static gboolean
+add_body_vectors (struct kdbus_msg *msg,
+                  GVariantVectors  *body_vectors,
+                  gint             *memfd_fd)
+{
+  guint i = 0;
 
+  /* Add all the vectors to the kdbus message */
+  for (; i < body_vectors->vectors->len; i++)
+    {
+      GVariantVector vector = g_array_index (body_vectors->vectors, GVariantVector, i);
 
-            case G_DBUS_MESSAGE_HEADER_FIELD_PATH:
-            case G_DBUS_MESSAGE_HEADER_FIELD_INTERFACE:
-            case G_DBUS_MESSAGE_HEADER_FIELD_MEMBER:
-            case G_DBUS_MESSAGE_HEADER_FIELD_ERROR_NAME:
-            case G_DBUS_MESSAGE_HEADER_FIELD_DESTINATION:
-            case G_DBUS_MESSAGE_HEADER_FIELD_NUM_UNIX_FDS:
-              g_variant_builder_add (&builder, "{tv}", key_int, value);
-              /* This is a little bit gross.
-               *
-               * We must send the header part of the message in a single
-               * vector as per kdbus rules, but the GVariant serialiser
-               * code will split any item >= 128 bytes into its own
-               * vector to save the copy.
-               *
-               * Anyway, kdbus does not check it, and libraries handle it...
-               */
-
-              if (g_variant_get_size (value) >= 128)
-                lg_h_field_exist = TRUE;
+      if (vector.gbytes)
+        {
+          const guchar *bytes_data;
+          gboolean use_memfd;
+          gsize bytes_size;
 
-              continue;
+          use_memfd = FALSE;
+          bytes_data = g_bytes_get_data (vector.gbytes, &bytes_size);
 
-            /* We send this one unconditionally, but set it ourselves */
-            case G_DBUS_MESSAGE_HEADER_FIELD_SENDER:
-              continue;
+          /* check whether we can and should use memfd */
+          if ((msg->dst_id != KDBUS_DST_ID_BROADCAST) && (bytes_size > KDBUS_MEMFD_THRESHOLD))
+            use_memfd = TRUE;
+
+          if (use_memfd)
+            {
+              const guchar *bytes_data_wr;
+              gint64 wr;
+
+              /* create memfd object */
+              *memfd_fd = glib_linux_memfd_create ("glib-kdbus-memfd", MFD_CLOEXEC | MFD_ALLOW_SEALING);
+              if (*memfd_fd == -1)
+                {
+                  g_warning ("kdbus: missing kernel memfd support");
+                  use_memfd = FALSE; /* send as PAYLOAD_VEC item */
+                }
+              else
+                {
+                  /* write data to memfd */
+                  bytes_data_wr = bytes_data;
+                  while (bytes_size)
+                    {
+                      wr = write (*memfd_fd, bytes_data_wr, bytes_size);
+                      if (wr < 0)
+                        g_warning ("kdbus: writing to memfd failed: (%d) %m", errno);
+
+                      bytes_size -= wr;
+                      bytes_data_wr += wr;
+                    }
+
+                  /* seal memfd */
+                  if (!g_unix_fd_ensure_zero_copy_safe (*memfd_fd))
+                    {
+                      g_warning ("kdbus: memfd sealing failed");
+                      use_memfd = FALSE; /* send as PAYLOAD_VEC item */
+                    }
+                  else
+                    {
+                      /* attach memfd item */
+                      if (!g_kdbus_msg_append_payload_memfd (msg, *memfd_fd, vector.data.pointer - bytes_data, vector.size))
+                        return FALSE;
+                    }
+                } /* *memfd_fd == -1 */
+            } /* use_memfd */
+
+          if (!use_memfd)
+            if (!g_kdbus_msg_append_payload_vec (msg, vector.data.pointer, vector.size))
+              return FALSE;
+        }
+      else
+        if (!g_kdbus_msg_append_payload_vec (msg, body_vectors->extra_bytes->data + vector.data.offset, vector.size))
+          return FALSE;
+    }
+  return TRUE;
+}
+
+static gboolean
+add_bloom_item (struct kdbus_msg *msg,
+                GDBusMessage     *message,
+                GKDBusWorker     *worker)
+{
+  struct kdbus_bloom_filter *bloom_filter;
+
+  if (g_dbus_message_get_message_type (message) == G_DBUS_MESSAGE_TYPE_SIGNAL)
+    {
+      msg->flags |= KDBUS_MSG_SIGNAL;
+      bloom_filter = g_kdbus_msg_append_bloom (msg, worker->bloom_size);
+      if (bloom_filter == NULL)
+        return FALSE;
+      g_kdbus_setup_bloom (worker, message, bloom_filter);
+    }
+  return TRUE;
+}
+
+static gsize
+compute_msg_size (GDBusMessage    *message,
+                  GKDBusWorker    *worker,
+                  GVariantVectors *body_vectors)
+{
+  gsize items_size = 0;
+  GUnixFDList *fd_list;
+  const gchar *dst_name;
+  guint i;
+  GDBusMessageType msg_type = g_dbus_message_get_message_type (message);
+
+  /* payload - check which vectors will become memfds */
+  for (i = 0; i < body_vectors->vectors->len; i++)
+    {
+      GVariantVector vector = g_array_index (body_vectors->vectors, GVariantVector, i);
+      gsize payload_size;
+      gsize kdbus_num_vectors;
 
-            /* We don't send these at all in GVariant format */
-            case G_DBUS_MESSAGE_HEADER_FIELD_SIGNATURE:
+      if (vector.gbytes)
+        {
+          payload_size = g_bytes_get_size (vector.gbytes);
+          if (msg_type != G_DBUS_MESSAGE_TYPE_SIGNAL && payload_size > KDBUS_MEMFD_THRESHOLD)
+            {
+              /* We can do this because we know that struct kdbus_memfd is larger or equal than
+                 struct kdbus_vec. struct kdbus_vec is fallback option if memfd does not work */
+              items_size += KDBUS_ITEM_SIZE (sizeof (struct kdbus_memfd));
               continue;
+            }
+        }
+      else
+        {
+          payload_size = vector.size;
+        }
 
-            default:
-              g_assert_not_reached ();
-          }
-      }
-    parts[1] = g_variant_builder_end (&builder);
-    if (parts[1] == NULL)
-      g_warning ("failed to make builder");
-
-    body = g_dbus_message_get_body (message);
-    if (!body)
-      body = g_variant_new ("()");
-    parts[2] = g_variant_new_variant (body);
-
-    header_size = g_variant_get_size (parts[0]) + g_variant_get_size (parts[1]);
-
-    body = g_variant_ref_sink (g_variant_new_tuple (parts, G_N_ELEMENTS (parts)));
-    GLIB_PRIVATE_CALL(g_variant_to_vectors) (body, &body_vectors);
-
-    /* Sanity check to make sure the header is really contiguous:
-     *
-     *  - we must have at least one vector in the output
-     *  - the first vector must completely contain at least the header; this
-     *    condition is assured below.
-     */
-    g_assert_cmpint (body_vectors.vectors->len, >, 0);
-
-    g_variant_unref (body);
-  }
-
-  {
-    guint i = 0;
-    gsize added_vectors_size = 0;
-    GVariantVector *first_vector;
-
-    /* First: add all header vectors to single kdbus vector item */
-    first_vector = &g_array_index (body_vectors.vectors, GVariantVector, 0);
-    if (first_vector->size < header_size)
-      {
-        /* There are multiple header vectors, we have to join them together into a single one */
-        GByteArray *header = g_byte_array_sized_new (header_size);
-        gsize dummy_size;
+      kdbus_num_vectors = (payload_size + KDBUS_MSG_MAX_PAYLOAD_VEC_SIZE - 1) / KDBUS_MSG_MAX_PAYLOAD_VEC_SIZE;
+      items_size += KDBUS_ITEM_SIZE (sizeof (struct kdbus_vec)) * kdbus_num_vectors;
+    }
 
-        g_assert_nonnull (header);
-        g_assert_true (lg_h_field_exist);
+  /* file descriptors */
+  fd_list = g_dbus_message_get_unix_fd_list (message);
+  if (fd_list != NULL)
+    items_size += KDBUS_ITEM_SIZE (g_unix_fd_list_get_length (fd_list) * sizeof(gint));
 
-        while (added_vectors_size < header_size && i < body_vectors.vectors->len)
-          {
-            GVariantVector *vector = &g_array_index (body_vectors.vectors, GVariantVector, i);
+  /* destination */
+  dst_name = g_dbus_message_get_destination (message);
+  if (dst_name != NULL)
+    items_size += KDBUS_ITEM_SIZE (strlen (dst_name) + 1);
 
-            if (vector->gbytes)
-              {
-                g_byte_array_append (header, vector->data.pointer, vector->size);
-                g_bytes_unref (vector->gbytes);
-              }
-            else
-              {
-                g_byte_array_append (header, body_vectors.extra_bytes->data + vector->data.offset, vector->size);
-              }
+  /* bloom filters */
+  if (msg_type == G_DBUS_MESSAGE_TYPE_SIGNAL)
+    items_size += KDBUS_ITEM_SIZE (sizeof (struct kdbus_bloom_filter) + worker->bloom_size);
 
-            added_vectors_size += vector->size;
-            i++;
-          }
+  return sizeof (struct kdbus_msg) + items_size;
+}
 
-        /* Sanity check if the first vector contains at least the complete header */
-        g_assert_cmpint (added_vectors_size, >=, header_size);
+/*
+ * _g_kdbus_send
+ */
+static gboolean
+_g_kdbus_send (GKDBusWorker  *worker,
+               GDBusMessage  *message,
+               GDBusMessage **out_reply,
+               gint           timeout_msec,
+               GCancellable  *cancellable,
+               GError       **error)
+{
+  struct kdbus_msg *msg;
+  GVariantVectors body_vectors;
+  struct kdbus_cmd_send *send;
+  gsize send_size;
+  gboolean result = FALSE;
+  gboolean lg_h_field_exist = FALSE;
 
-        first_vector->gbytes = g_byte_array_free_to_bytes (header);
-        first_vector->data.pointer = g_bytes_get_data (first_vector->gbytes, &dummy_size);
-        first_vector->size = added_vectors_size;
+  gint memfd_fd;
+  gint cancel_fd;
 
-        g_array_remove_range (body_vectors.vectors, 1, i-1);
+  gsize header_size;
+  gsize msg_size;
 
-        /* Now, the vectors are prepared correctly, add them to the message */
-        i = 0;
-      }
-    /* else: If there is only a single header vector, then just go */
+  g_return_val_if_fail (G_IS_KDBUS_WORKER (worker), FALSE);
 
-    /* Now, add all the remaining body vectors */
-    for (; i < body_vectors.vectors->len; i++)
-      {
-        GVariantVector vector = g_array_index (body_vectors.vectors, GVariantVector, i);
+  send = NULL;
+  send_size = sizeof(*send);
 
-        if (vector.gbytes)
-          {
-            const guchar *bytes_data;
-            gboolean use_memfd;
-            gsize bytes_size;
+  /* Prepare message body - it is needed for kdbus msg size computation */
+  header_size = prepare_body_vectors (worker, message, &body_vectors, &lg_h_field_exist);
+  make_single_header_vector (&body_vectors, header_size, lg_h_field_exist);
 
-            use_memfd = FALSE;
-            bytes_data = g_bytes_get_data (vector.gbytes, &bytes_size);
+  /* We precompute needed size for the message to allocate exact space instead
+     of some arbitrary amount */
+  msg_size = compute_msg_size (message, worker, &body_vectors);
+  if (msg_size <= KDBUS_MSG_MAX_SIZE)
+    {
+      msg = g_alloca (msg_size);
+      memset (msg, 0, msg_size);
+    }
+  else
+    {
+      msg = g_malloc0 (msg_size);
+    }
+  g_assert_nonnull (msg);
 
-            /* check whether we can and should use memfd */
-            if ((msg->dst_id != KDBUS_DST_ID_BROADCAST) && (bytes_size > KDBUS_MEMFD_THRESHOLD))
-              use_memfd = TRUE;
+  memfd_fd = -1;
+  cancel_fd = -1;
 
-            if (use_memfd)
-              {
-                const guchar *bytes_data_wr;
-                gint64 wr;
+  /* fill in as we go... */
+  msg->size = sizeof (struct kdbus_msg);
+  msg->payload_type = KDBUS_PAYLOAD_DBUS;
+  msg->cookie = g_dbus_message_get_serial(message);
 
-                /* create memfd object */
-                memfd_fd = glib_linux_memfd_create ("glib-kdbus-memfd", MFD_CLOEXEC | MFD_ALLOW_SEALING);
-                if (memfd_fd == -1)
-                  {
-                    g_warning ("kdbus: missing kernel memfd support");
-                    use_memfd = FALSE; /* send as PAYLOAD_VEC item */
-                  }
-                else
-                  {
-                    /* write data to memfd */
-                    bytes_data_wr = bytes_data;
-                    while (bytes_size)
-                      {
-                        wr = write (memfd_fd, bytes_data_wr, bytes_size);
-                        if (wr < 0)
-                          g_warning ("kdbus: writing to memfd failed: (%d) %m", errno);
-
-                        bytes_size -= wr;
-                        bytes_data_wr += wr;
-                      }
-
-                    /* seal memfd */
-                    if (!g_unix_fd_ensure_zero_copy_safe (memfd_fd))
-                      {
-                        g_warning ("kdbus: memfd sealing failed");
-                        use_memfd = FALSE; /* send as PAYLOAD_VEC item */
-                      }
-                    else
-                      {
-                        /* attach memfd item */
-                        if (!g_kdbus_msg_append_payload_memfd (msg, memfd_fd, vector.data.pointer - bytes_data, vector.size))
-                          goto need_compact;
-                      }
-                  } /* memfd_fd == -1 */
-              } /* use_memfd */
-
-            if (!use_memfd)
-              if (!g_kdbus_msg_append_payload_vec (msg, vector.data.pointer, vector.size))
-                goto need_compact;
-          }
-        else
-          if (!g_kdbus_msg_append_payload_vec (msg, body_vectors.extra_bytes->data + vector.data.offset, vector.size))
-            goto need_compact;
-      }
-  }
+  /* Message destination */
+  if (!add_message_destination_item (msg, message, error))
+    goto out;
+
+  /* File descriptors */
+  add_file_descriptors_item (msg, message);
+
+  if (!add_body_vectors (msg, &body_vectors, &memfd_fd))
+    {
+      g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
+                   "message serialisation error: body vectors");
+      g_warning ("kdbus: message serialisation error: body vectors");
+      goto out;
+    }
 
   /*
    * set message flags
@@ -3494,15 +3623,12 @@ _g_kdbus_send (GKDBusWorker  *worker,
   /*
    * append bloom filter item for broadcast signals
    */
-  if (g_dbus_message_get_message_type (message) == G_DBUS_MESSAGE_TYPE_SIGNAL)
+  if (!add_bloom_item (msg, message, worker))
     {
-      struct kdbus_bloom_filter *bloom_filter;
-
-      msg->flags |= KDBUS_MSG_SIGNAL;
-      bloom_filter = g_kdbus_msg_append_bloom (msg, worker->bloom_size);
-      if (bloom_filter == NULL)
-        goto need_compact;
-      g_kdbus_setup_bloom (worker, message, bloom_filter);
+      g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
+                   "message serialisation error: bloom filters");
+      g_warning ("kdbus: message serialisation error: bloom filters");
+      goto out;
     }
 
   if (out_reply != NULL && cancellable)
@@ -3512,6 +3638,8 @@ _g_kdbus_send (GKDBusWorker  *worker,
         send_size += KDBUS_ITEM_SIZE (sizeof(cancel_fd));
     }
 
+  g_assert_cmpuint (msg->size, <=, msg_size);
+
   send = g_alloca0 (send_size);
   send->size = send_size;
   send->msg_address = (gsize) msg;
@@ -3585,10 +3713,10 @@ _g_kdbus_send (GKDBusWorker  *worker,
                   case DBUSPOLICY_RESULT_DEST_NOT_AVAILABLE:
                     if (g_dbus_message_get_flags (message) & G_DBUS_MESSAGE_FLAGS_NO_AUTO_START)
                       g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_NAME_HAS_NO_OWNER,
-                                   "Name \"%s\" does not exist", dst_name);
+                                   "Name \"%s\" does not exist", g_dbus_message_get_destination (message));
                     else
                       g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN,
-                                   "Cannot send message - destination '%s' not known", dst_name);
+                                   "Cannot send message - destination '%s' not known", g_dbus_message_get_destination (message));
                   break;
 
                   case DBUSPOLICY_RESULT_KDBUS_ERROR:
@@ -3607,7 +3735,6 @@ _g_kdbus_send (GKDBusWorker  *worker,
                   break;
                 }
 
-              result = FALSE;
               goto out;
             }
         }
@@ -3642,15 +3769,15 @@ _g_kdbus_send (GKDBusWorker  *worker,
             {
               if (g_dbus_message_get_flags (message) & G_DBUS_MESSAGE_FLAGS_NO_AUTO_START)
                 g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_NAME_HAS_NO_OWNER,
-                             "Name \"%s\" does not exist, %s", dst_name, info);
+                             "Name \"%s\" does not exist, %s", g_dbus_message_get_destination (message), info);
               else
                 g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN,
-                             "Destination '%s' not known, %s", dst_name, info);
+                             "Destination '%s' not known, %s", g_dbus_message_get_destination (message), info);
             }
           else if (errno == EADDRNOTAVAIL)
             {
               g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN,
-                           "No support for activation for name: %s, %s", dst_name, info);
+                           "No support for activation for name: %s, %s", g_dbus_message_get_destination (message), info);
             }
           else if (errno == EXFULL)
             {
@@ -3694,45 +3821,49 @@ _g_kdbus_send (GKDBusWorker  *worker,
             }
           free(info);
         }
-      result = FALSE;
     }
-  else if (out_reply != NULL)
+  else
     {
-      GKDBusMessage *kmsg;
-      struct kdbus_msg *kdbus_msg;
+      result = TRUE;
+      if (out_reply != NULL)
+        {
+          GKDBusMessage *kmsg;
+          struct kdbus_msg *kdbus_msg;
 
-      kdbus_msg = (struct kdbus_msg *)((guint8 *)worker->kdbus_buffer + send->reply.offset);
+          kdbus_msg = (struct kdbus_msg *)((guint8 *)worker->kdbus_buffer + send->reply.offset);
 
-      kmsg = g_kdbus_decode_dbus_msg (worker, kdbus_msg);
-      g_kdbus_close_msg (worker, kdbus_msg);
+          kmsg = g_kdbus_decode_dbus_msg (worker, kdbus_msg);
+          g_kdbus_close_msg (worker, kdbus_msg);
 
-      *out_reply = kmsg->message;
+          *out_reply = kmsg->message;
 
-      if (kmsg->sender_seclabel != NULL)
-        g_free (kmsg->sender_seclabel);
+          if (kmsg->sender_seclabel != NULL)
+            g_free (kmsg->sender_seclabel);
 
-      if (kmsg->sender_names != NULL)
-        g_free (kmsg->sender_names);
+          if (kmsg->sender_names != NULL)
+            g_free (kmsg->sender_names);
 
-      g_free (kmsg);
+          g_free (kmsg);
 
-      if (G_UNLIKELY (_g_dbus_debug_message ()))
-        {
-          gchar *s;
-          _g_dbus_debug_print_lock ();
-          g_print ("========================================================================\n"
-                   "GDBus-debug:Message:\n"
-                   "  <<<< RECEIVED D-Bus message\n");
-          s = g_dbus_message_print (*out_reply, 2);
-          g_print ("%s", s);
-          g_free (s);
-          _g_dbus_debug_print_unlock ();
+          if (G_UNLIKELY (_g_dbus_debug_message ()))
+            {
+              gchar *s;
+              _g_dbus_debug_print_lock ();
+              g_print ("========================================================================\n"
+                       "GDBus-debug:Message:\n"
+                       "  <<<< RECEIVED D-Bus message\n");
+              s = g_dbus_message_print (*out_reply, 2);
+              g_print ("%s", s);
+              g_free (s);
+              _g_dbus_debug_print_unlock ();
+            }
         }
     }
 
-#ifdef LIBDBUSPOLICY
 out:
-#endif
+  if (msg_size > KDBUS_MSG_MAX_SIZE)
+    g_free (msg);
+
   if (cancel_fd != -1)
     g_cancellable_release_fd (cancellable);
 
@@ -3742,22 +3873,6 @@ out:
   GLIB_PRIVATE_CALL(g_variant_vectors_deinit) (&body_vectors);
 
   return result;
-
-need_compact:
-  /* We end up here if:
-   *  - too many kdbus_items
-   *  - too large kdbus_msg size
-   *  - too much vector data
-   */
-  g_warning ("kdbus: message serialisation error");
-  g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
-               "message serialisation error");
-
-  if (memfd_fd != -1)
-    close (memfd_fd);
-
-  GLIB_PRIVATE_CALL(g_variant_vectors_deinit) (&body_vectors);
-  return FALSE;
 }
 
 /* ---------------------------------------------------------------------------------------------------- */