dbus-marshal-validate: Check brackets in signature nest correctly
[platform/upstream/dbus.git] / dbus / dbus-message-util.c
index f972c8a..ebf00e2 100644 (file)
  * @{
  */
 
-#ifdef DBUS_BUILD_TESTS
+/**
+ * Gets the number of unix fds attached to this message.
+ *
+ * @param message the message
+ * @returns the number of file descriptors
+ */
+unsigned int
+_dbus_message_get_n_unix_fds (DBusMessage *message)
+{
+#ifdef HAVE_UNIX_FD_PASSING
+  return message->n_unix_fds;
+#else
+  return 0;
+#endif
+}
+
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
 /**
  * Reads arguments from a message iterator given a variable argument
  * list. Only arguments of basic type and arrays of fixed-length
@@ -76,11 +92,11 @@ dbus_message_iter_get_args (DBusMessageIter *iter,
 
   return retval;
 }
-#endif /* DBUS_BUILD_TESTS */
+#endif /* DBUS_ENABLE_EMBEDDED_TESTS */
 
 /** @} */
 
-#ifdef DBUS_BUILD_TESTS
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
 #include "dbus-test.h"
 #include "dbus-message-factory.h"
 #include <stdio.h>
@@ -132,23 +148,84 @@ check_memleaks (void)
 
   if (_dbus_get_malloc_blocks_outstanding () != 0)
     {
-      _dbus_warn ("%d dbus_malloc blocks were not freed in %s\n",
+      _dbus_warn ("%d dbus_malloc blocks were not freed in %s",
                   _dbus_get_malloc_blocks_outstanding (), __FILE__);
       _dbus_assert_not_reached ("memleaks");
     }
 }
 
-void
-_dbus_check_fdleaks(void)
-{
+#ifdef __linux__
+struct DBusInitialFDs {
+    fd_set set;
+};
+#endif
 
+DBusInitialFDs *
+_dbus_check_fdleaks_enter (void)
+{
 #ifdef __linux__
+  DIR *d;
+  DBusInitialFDs *fds;
+
+  /* this is plain malloc so it won't interfere with leak checking */
+  fds = malloc (sizeof (DBusInitialFDs));
+  _dbus_assert (fds != NULL);
+
+  /* This works on Linux only */
+
+  if ((d = opendir ("/proc/self/fd")))
+    {
+      struct dirent *de;
+
+      while ((de = readdir(d)))
+        {
+          long l;
+          char *e = NULL;
+          int fd;
 
+          if (de->d_name[0] == '.')
+            continue;
+
+          errno = 0;
+          l = strtol (de->d_name, &e, 10);
+          _dbus_assert (errno == 0 && e && !*e);
+
+          fd = (int) l;
+
+          if (fd < 3)
+            continue;
+
+          if (fd == dirfd (d))
+            continue;
+
+          if (fd >= FD_SETSIZE)
+            {
+              _dbus_verbose ("FD %d unexpectedly large; cannot track whether "
+                             "it is leaked\n", fd);
+              continue;
+            }
+
+          FD_SET (fd, &fds->set);
+        }
+
+      closedir (d);
+    }
+
+  return fds;
+#else
+  return NULL;
+#endif
+}
+
+void
+_dbus_check_fdleaks_leave (DBusInitialFDs *fds)
+{
+#ifdef __linux__
   DIR *d;
 
   /* This works on Linux only */
 
-  if ((d = opendir("/proc/self/fd")))
+  if ((d = opendir ("/proc/self/fd")))
     {
       struct dirent *de;
 
@@ -162,23 +239,37 @@ _dbus_check_fdleaks(void)
             continue;
 
           errno = 0;
-          l = strtol(de->d_name, &e, 10);
-          _dbus_assert(errno == 0 && e && !*e);
+          l = strtol (de->d_name, &e, 10);
+          _dbus_assert (errno == 0 && e && !*e);
 
           fd = (int) l;
 
           if (fd < 3)
             continue;
 
-          if (fd == dirfd(d))
+          if (fd == dirfd (d))
+            continue;
+
+          if (fd >= FD_SETSIZE)
+            {
+              _dbus_verbose ("FD %d unexpectedly large; cannot track whether "
+                             "it is leaked\n", fd);
+              continue;
+            }
+
+          if (FD_ISSET (fd, &fds->set))
             continue;
 
-          _dbus_warn("file descriptor %i leaked in %s.\n", fd, __FILE__);
-          _dbus_assert_not_reached("fdleaks");
+          _dbus_warn ("file descriptor %i leaked in %s.", fd, __FILE__);
+          _dbus_assert_not_reached ("fdleaks");
         }
 
-      closedir(d);
+      closedir (d);
     }
+
+  free (fds);
+#else
+  _dbus_assert (fds == NULL);
 #endif
 }
 
@@ -193,7 +284,7 @@ check_have_valid_message (DBusMessageLoader *loader)
 
   if (_dbus_message_loader_get_is_corrupted (loader))
     {
-      _dbus_warn ("loader corrupted on message that was expected to be valid; invalid reason %d\n",
+      _dbus_warn ("loader corrupted on message that was expected to be valid; invalid reason %d",
                   loader->corruption_reason);
       goto failed;
     }
@@ -201,13 +292,13 @@ check_have_valid_message (DBusMessageLoader *loader)
   message = _dbus_message_loader_pop_message (loader);
   if (message == NULL)
     {
-      _dbus_warn ("didn't load message that was expected to be valid (message not popped)\n");
+      _dbus_warn ("didn't load message that was expected to be valid (message not popped)");
       goto failed;
     }
 
   if (_dbus_string_get_length (&loader->data) > 0)
     {
-      _dbus_warn ("had leftover bytes from expected-to-be-valid single message\n");
+      _dbus_warn ("had leftover bytes from expected-to-be-valid single message");
       goto failed;
     }
 
@@ -242,7 +333,7 @@ check_invalid_message (DBusMessageLoader *loader,
 
   if (!_dbus_message_loader_get_is_corrupted (loader))
     {
-      _dbus_warn ("loader not corrupted on message that was expected to be invalid\n");
+      _dbus_warn ("loader not corrupted on message that was expected to be invalid");
       goto failed;
     }
 
@@ -251,7 +342,7 @@ check_invalid_message (DBusMessageLoader *loader,
   if (expected_validity != DBUS_INVALID_FOR_UNKNOWN_REASON &&
       loader->corruption_reason != expected_validity)
     {
-      _dbus_warn ("expected message to be corrupted for reason %d and was corrupted for %d instead\n",
+      _dbus_warn ("expected message to be corrupted for reason %d and was corrupted for %d instead",
                   expected_validity, loader->corruption_reason);
       goto failed;
     }
@@ -273,7 +364,7 @@ check_incomplete_message (DBusMessageLoader *loader)
 
   if (_dbus_message_loader_get_is_corrupted (loader))
     {
-      _dbus_warn ("loader corrupted on message that was expected to be valid (but incomplete), corruption reason %d\n",
+      _dbus_warn ("loader corrupted on message that was expected to be valid (but incomplete), corruption reason %d",
                   loader->corruption_reason);
       goto failed;
     }
@@ -281,7 +372,7 @@ check_incomplete_message (DBusMessageLoader *loader)
   message = _dbus_message_loader_pop_message (loader);
   if (message != NULL)
     {
-      _dbus_warn ("loaded message that was expected to be incomplete\n");
+      _dbus_warn ("loaded message that was expected to be incomplete");
       goto failed;
     }
 
@@ -339,7 +430,7 @@ dbus_internal_do_not_use_load_message_file (const DBusString    *filename,
   _dbus_verbose ("Loading raw %s\n", _dbus_string_get_const_data (filename));
   if (!_dbus_file_get_contents (data, filename, &error))
     {
-      _dbus_warn ("Could not load message file %s: %s\n",
+      _dbus_warn ("Could not load message file %s: %s",
                   _dbus_string_get_const_data (filename),
                   error.message);
       dbus_error_free (&error);
@@ -371,7 +462,7 @@ dbus_internal_do_not_use_try_message_file (const DBusString    *filename,
   retval = FALSE;
 
   if (!_dbus_string_init (&data))
-    _dbus_assert_not_reached ("could not allocate string\n");
+    _dbus_assert_not_reached ("could not allocate string");
 
   if (!dbus_internal_do_not_use_load_message_file (filename, &data))
     goto failed;
@@ -386,7 +477,7 @@ dbus_internal_do_not_use_try_message_file (const DBusString    *filename,
         _dbus_verbose_bytes_of_string (&data, 0,
                                        _dbus_string_get_length (&data));
 
-      _dbus_warn ("Failed message loader test on %s\n",
+      _dbus_warn ("Failed message loader test on %s",
                   _dbus_string_get_const_data (filename));
     }
 
@@ -418,21 +509,23 @@ dbus_internal_do_not_use_try_message_data (const DBusString    *data,
   /* Write the data one byte at a time */
 
   loader = _dbus_message_loader_new ();
+  if (loader == NULL)
+    goto failed;
 
   /* check some trivial loader functions */
   _dbus_message_loader_ref (loader);
   _dbus_message_loader_unref (loader);
-  _dbus_message_loader_get_max_message_size (loader);
 
   len = _dbus_string_get_length (data);
   for (i = 0; i < len; i++)
     {
       DBusString *buffer;
 
-      _dbus_message_loader_get_buffer (loader, &buffer);
-      _dbus_string_append_byte (buffer,
-                                _dbus_string_get_byte (data, i));
-      _dbus_message_loader_return_buffer (loader, buffer, 1);
+      _dbus_message_loader_get_buffer (loader, &buffer, NULL, NULL);
+      if (!_dbus_string_append_byte (buffer,
+                                     _dbus_string_get_byte (data, i)))
+        goto failed;
+      _dbus_message_loader_return_buffer (loader, buffer);
     }
 
   if (!check_loader_results (loader, expected_validity))
@@ -444,14 +537,17 @@ dbus_internal_do_not_use_try_message_data (const DBusString    *data,
   /* Write the data all at once */
 
   loader = _dbus_message_loader_new ();
+  if (loader == NULL)
+    goto failed;
 
   {
     DBusString *buffer;
 
-    _dbus_message_loader_get_buffer (loader, &buffer);
-    _dbus_string_copy (data, 0, buffer,
-                       _dbus_string_get_length (buffer));
-    _dbus_message_loader_return_buffer (loader, buffer, 1);
+    _dbus_message_loader_get_buffer (loader, &buffer, NULL, NULL);
+    if (!_dbus_string_copy (data, 0, buffer,
+                            _dbus_string_get_length (buffer)))
+        goto failed;
+    _dbus_message_loader_return_buffer (loader, buffer);
   }
 
   if (!check_loader_results (loader, expected_validity))
@@ -463,19 +559,27 @@ dbus_internal_do_not_use_try_message_data (const DBusString    *data,
   /* Write the data 2 bytes at a time */
 
   loader = _dbus_message_loader_new ();
+  if (loader == NULL)
+    goto failed;
 
   len = _dbus_string_get_length (data);
   for (i = 0; i < len; i += 2)
     {
       DBusString *buffer;
 
-      _dbus_message_loader_get_buffer (loader, &buffer);
-      _dbus_string_append_byte (buffer,
-                                _dbus_string_get_byte (data, i));
+      _dbus_message_loader_get_buffer (loader, &buffer, NULL, NULL);
+      if (!_dbus_string_append_byte (buffer,
+                                     _dbus_string_get_byte (data, i)))
+        goto failed;
+
       if ((i+1) < len)
-        _dbus_string_append_byte (buffer,
-                                  _dbus_string_get_byte (data, i+1));
-      _dbus_message_loader_return_buffer (loader, buffer, 1);
+        {
+          if (!_dbus_string_append_byte (buffer,
+                                         _dbus_string_get_byte (data, i+1)))
+            goto failed;
+        }
+
+      _dbus_message_loader_return_buffer (loader, buffer);
     }
 
   if (!check_loader_results (loader, expected_validity))
@@ -511,7 +615,7 @@ process_test_subdir (const DBusString          *test_base_dir,
   dir = NULL;
 
   if (!_dbus_string_init (&test_directory))
-    _dbus_assert_not_reached ("didn't allocate test_directory\n");
+    _dbus_assert_not_reached ("didn't allocate test_directory");
 
   _dbus_string_init_const (&filename, subdir);
 
@@ -524,12 +628,12 @@ process_test_subdir (const DBusString          *test_base_dir,
 
   _dbus_string_free (&filename);
   if (!_dbus_string_init (&filename))
-    _dbus_assert_not_reached ("didn't allocate filename string\n");
+    _dbus_assert_not_reached ("didn't allocate filename string");
 
   dir = _dbus_directory_open (&test_directory, &error);
   if (dir == NULL)
     {
-      _dbus_warn ("Could not open %s: %s\n",
+      _dbus_warn ("Could not open %s: %s",
                   _dbus_string_get_const_data (&test_directory),
                   error.message);
       dbus_error_free (&error);
@@ -558,8 +662,8 @@ process_test_subdir (const DBusString          *test_base_dir,
         {
           if (_dbus_string_ends_with_c_str (&filename, ".message"))
             {
-              _dbus_warn ("Could not load %s, message builder language no longer supported\n",
-                          _dbus_string_get_const_data (&filename));
+              printf ("SKIP: Could not load %s, message builder language no longer supported\n",
+                      _dbus_string_get_const_data (&filename));
             }
           
           _dbus_verbose ("Skipping non-.message file %s\n",
@@ -583,7 +687,7 @@ process_test_subdir (const DBusString          *test_base_dir,
 
   if (dbus_error_is_set (&error))
     {
-      _dbus_warn ("Could not get next file in %s: %s\n",
+      _dbus_warn ("Could not get next file in %s: %s",
                   _dbus_string_get_const_data (&test_directory),
                   error.message);
       dbus_error_free (&error);
@@ -693,10 +797,8 @@ message_iter_test (DBusMessage *message)
   dbus_uint16_t v_UINT16;
   dbus_int32_t v_INT32;
   dbus_uint32_t v_UINT32;
-#ifdef DBUS_HAVE_INT64
   dbus_int64_t v_INT64;
   dbus_uint64_t v_UINT64;
-#endif
   unsigned char v_BYTE;
   dbus_bool_t v_BOOLEAN;
 
@@ -769,14 +871,12 @@ verify_test_message (DBusMessage *message)
   int our_uint32_array_len;
   dbus_int32_t *our_int32_array = (void*)0xdeadbeef;
   int our_int32_array_len;
-#ifdef DBUS_HAVE_INT64
   dbus_int64_t our_int64;
   dbus_uint64_t our_uint64;
   dbus_int64_t *our_uint64_array = (void*)0xdeadbeef;
   int our_uint64_array_len;
   const dbus_int64_t *our_int64_array = (void*)0xdeadbeef;
   int our_int64_array_len;
-#endif
   const double *our_double_array = (void*)0xdeadbeef;
   int our_double_array_len;
   const unsigned char *our_byte_array = (void*)0xdeadbeef;
@@ -793,10 +893,8 @@ verify_test_message (DBusMessage *message)
                                    DBUS_TYPE_UINT16, &our_uint16,
                                   DBUS_TYPE_INT32, &our_int,
                                    DBUS_TYPE_UINT32, &our_uint,
-#ifdef DBUS_HAVE_INT64
                                    DBUS_TYPE_INT64, &our_int64,
                                    DBUS_TYPE_UINT64, &our_uint64,
-#endif
                                   DBUS_TYPE_STRING, &our_str,
                                   DBUS_TYPE_DOUBLE, &our_double,
                                   DBUS_TYPE_BOOLEAN, &our_bool,
@@ -806,12 +904,10 @@ verify_test_message (DBusMessage *message)
                                    &our_uint32_array, &our_uint32_array_len,
                                    DBUS_TYPE_ARRAY, DBUS_TYPE_INT32,
                                    &our_int32_array, &our_int32_array_len,
-#ifdef DBUS_HAVE_INT64
                                   DBUS_TYPE_ARRAY, DBUS_TYPE_UINT64,
                                    &our_uint64_array, &our_uint64_array_len,
                                    DBUS_TYPE_ARRAY, DBUS_TYPE_INT64,
                                    &our_int64_array, &our_int64_array_len,
-#endif
                                    DBUS_TYPE_ARRAY, DBUS_TYPE_DOUBLE,
                                    &our_double_array, &our_double_array_len,
                                    DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE,
@@ -822,7 +918,7 @@ verify_test_message (DBusMessage *message)
                                    &our_string_array, &our_string_array_len,
                                   0))
     {
-      _dbus_warn ("error: %s - %s\n", error.name,
+      _dbus_warn ("error: %s - %s", error.name,
                   (error.message != NULL) ? error.message : "no message");
       _dbus_assert_not_reached ("Could not get arguments");
     }
@@ -839,12 +935,10 @@ verify_test_message (DBusMessage *message)
   if (our_uint != 0x12300042)
     _dbus_assert_not_reached ("uints differ!");
 
-#ifdef DBUS_HAVE_INT64
   if (our_int64 != DBUS_INT64_CONSTANT (-0x123456789abcd))
     _dbus_assert_not_reached ("64-bit integers differ!");
   if (our_uint64 != DBUS_UINT64_CONSTANT (0x123456789abcd))
     _dbus_assert_not_reached ("64-bit unsigned integers differ!");
-#endif
 
   v_DOUBLE = 3.14159;
   if (! _DBUS_DOUBLES_BITWISE_EQUAL (our_double, v_DOUBLE))
@@ -876,7 +970,6 @@ verify_test_message (DBusMessage *message)
       our_int32_array[3] != -0x45678123)
     _dbus_assert_not_reached ("int array differs");
 
-#ifdef DBUS_HAVE_INT64
   if (our_uint64_array_len != 4 ||
       our_uint64_array[0] != 0x12345678 ||
       our_uint64_array[1] != 0x23456781 ||
@@ -890,7 +983,6 @@ verify_test_message (DBusMessage *message)
       our_int64_array[2] != 0x34567812 ||
       our_int64_array[3] != -0x45678123)
     _dbus_assert_not_reached ("int64 array differs");
-#endif /* DBUS_HAVE_INT64 */
 
   if (our_double_array_len != 3)
     _dbus_assert_not_reached ("double array had wrong length");
@@ -942,6 +1034,163 @@ verify_test_message (DBusMessage *message)
     _dbus_assert_not_reached ("Didn't reach end of arguments");
 }
 
+static void
+verify_test_message_args_ignored (DBusMessage *message)
+{
+  DBusMessageIter iter;
+  DBusError error = DBUS_ERROR_INIT;
+  dbus_uint32_t our_uint;
+  DBusInitialFDs *initial_fds;
+
+  initial_fds = _dbus_check_fdleaks_enter ();
+
+  /* parse with empty signature: "" */
+  dbus_message_iter_init (message, &iter);
+  if (!dbus_message_iter_get_args (&iter, &error,
+                                   DBUS_TYPE_INVALID))
+    {
+      _dbus_warn ("error: %s - %s", error.name,
+                     (error.message != NULL) ? error.message : "no message");
+    }
+  else
+    {
+      _dbus_assert (!dbus_error_is_set (&error));
+      _dbus_verbose ("arguments ignored.\n");
+    }
+
+  /* parse with shorter signature: "u" */
+  dbus_message_iter_init (message, &iter);
+  if (!dbus_message_iter_get_args (&iter, &error,
+                                   DBUS_TYPE_UINT32, &our_uint,
+                                   DBUS_TYPE_INVALID))
+    {
+      _dbus_warn ("error: %s - %s", error.name,
+                     (error.message != NULL) ? error.message : "no message");
+    }
+  else
+    {
+      _dbus_assert (!dbus_error_is_set (&error));
+      _dbus_verbose ("arguments ignored.\n");
+    }
+
+  _dbus_check_fdleaks_leave (initial_fds);
+}
+
+static void
+verify_test_message_memleak (DBusMessage *message)
+{
+  DBusMessageIter iter;
+  DBusError error = DBUS_ERROR_INIT;
+  dbus_uint32_t our_uint1;
+  dbus_uint32_t our_uint2;
+  dbus_uint32_t our_uint3;
+  char **our_string_array1;
+  int our_string_array_len1;
+  char **our_string_array2;
+  int our_string_array_len2;
+#ifdef HAVE_UNIX_FD_PASSING
+  int our_unix_fd1;
+  int our_unix_fd2;
+#endif
+  DBusInitialFDs *initial_fds;
+
+  initial_fds = _dbus_check_fdleaks_enter ();
+
+  /* parse with wrong signature: "uashuu" */
+  dbus_error_free (&error);
+  dbus_message_iter_init (message, &iter);
+  if (!dbus_message_iter_get_args (&iter, &error,
+                                   DBUS_TYPE_UINT32, &our_uint1,
+                                   DBUS_TYPE_ARRAY, DBUS_TYPE_STRING,
+                                   &our_string_array1, &our_string_array_len1,
+#ifdef HAVE_UNIX_FD_PASSING
+                                   DBUS_TYPE_UNIX_FD, &our_unix_fd1,
+#endif
+                                   DBUS_TYPE_UINT32, &our_uint2,
+                                   DBUS_TYPE_UINT32, &our_uint3,
+                                   DBUS_TYPE_INVALID))
+    {
+      _dbus_verbose ("expected error: %s - %s\n", error.name,
+                     (error.message != NULL) ? error.message : "no message");
+      /* ensure array of string and unix fd not leaked */
+      _dbus_assert (our_string_array1 == NULL);
+#ifdef HAVE_UNIX_FD_PASSING
+      _dbus_assert (our_unix_fd1 == -1);
+#endif
+    }
+  else
+    {
+      _dbus_warn ("error: parse with wrong signature: 'uashuu'.");
+    }
+
+  /* parse with wrong signature: "uashuashu" */
+  dbus_message_iter_init (message, &iter);
+  dbus_error_free (&error);
+  if (!dbus_message_iter_get_args (&iter, &error,
+                                   DBUS_TYPE_UINT32, &our_uint1,
+                                   DBUS_TYPE_ARRAY, DBUS_TYPE_STRING,
+                                   &our_string_array1, &our_string_array_len1,
+#ifdef HAVE_UNIX_FD_PASSING
+                                   DBUS_TYPE_UNIX_FD, &our_unix_fd1,
+#endif
+                                   DBUS_TYPE_UINT32, &our_uint2,
+                                   DBUS_TYPE_ARRAY, DBUS_TYPE_STRING,
+                                   &our_string_array2, &our_string_array_len2,
+#ifdef HAVE_UNIX_FD_PASSING
+                                   DBUS_TYPE_UNIX_FD, &our_unix_fd2,
+#endif
+                                   DBUS_TYPE_UINT32, &our_uint3,
+                                   DBUS_TYPE_INVALID))
+    {
+      _dbus_verbose ("expected error: %s - %s\n", error.name,
+                     (error.message != NULL) ? error.message : "no message");
+      /* ensure array of string and unix fd not leaked */
+      _dbus_assert (our_string_array1 == NULL);
+      _dbus_assert (our_string_array2 == NULL);
+#ifdef HAVE_UNIX_FD_PASSING
+      _dbus_assert (our_unix_fd1 == -1);
+      _dbus_assert (our_unix_fd2 == -1);
+#endif
+    }
+  else
+    {
+      _dbus_warn ("error: parse with wrong signature: 'uashuashu'.");
+    }
+
+  /* parse with correct signature: "uashuash" */
+  dbus_message_iter_init (message, &iter);
+  dbus_error_free (&error);
+  if (!dbus_message_iter_get_args (&iter, &error,
+                                   DBUS_TYPE_UINT32, &our_uint1,
+                                   DBUS_TYPE_ARRAY, DBUS_TYPE_STRING,
+                                   &our_string_array1, &our_string_array_len1,
+#ifdef HAVE_UNIX_FD_PASSING
+                                   DBUS_TYPE_UNIX_FD, &our_unix_fd1,
+#endif
+                                   DBUS_TYPE_UINT32, &our_uint2,
+                                   DBUS_TYPE_ARRAY, DBUS_TYPE_STRING,
+                                   &our_string_array2, &our_string_array_len2,
+#ifdef HAVE_UNIX_FD_PASSING
+                                   DBUS_TYPE_UNIX_FD, &our_unix_fd2,
+#endif
+                                   DBUS_TYPE_INVALID))
+    {
+      _dbus_warn ("error: %s - %s", error.name,
+                  (error.message != NULL) ? error.message : "no message");
+      _dbus_assert_not_reached ("Could not get arguments");
+    }
+  else
+    {
+      dbus_free_string_array (our_string_array1);
+      dbus_free_string_array (our_string_array2);
+#ifdef HAVE_UNIX_FD_PASSING
+      _dbus_close (our_unix_fd1, &error);
+      _dbus_close (our_unix_fd2, &error);
+#endif
+    }
+  _dbus_check_fdleaks_leave (initial_fds);
+}
+
 /**
  * @ingroup DBusMessageInternals
  * Unit test for DBusMessage.
@@ -964,16 +1213,16 @@ _dbus_message_test (const char *test_data_dir)
     { 0x12345678, -0x23456781, 0x34567812, -0x45678123 };
   const dbus_uint32_t *v_ARRAY_UINT32 = our_uint32_array;
   const dbus_int32_t *v_ARRAY_INT32 = our_int32_array;
-#ifdef DBUS_HAVE_INT64
   const dbus_uint64_t our_uint64_array[] =
     { 0x12345678, 0x23456781, 0x34567812, 0x45678123 };
   const dbus_int64_t our_int64_array[] =
     { 0x12345678, -0x23456781, 0x34567812, -0x45678123 };
   const dbus_uint64_t *v_ARRAY_UINT64 = our_uint64_array;
   const dbus_int64_t *v_ARRAY_INT64 = our_int64_array;
-#endif
   const char *our_string_array[] = { "Foo", "bar", "", "woo woo woo woo" };
+  const char *our_string_array1[] = { "foo", "Bar", "", "Woo woo Woo woo" };
   const char **v_ARRAY_STRING = our_string_array;
+  const char **v1_ARRAY_STRING = our_string_array1;
   const double our_double_array[] = { 0.1234, 9876.54321, -300.0 };
   const double *v_ARRAY_DOUBLE = our_double_array;
   const unsigned char our_byte_array[] = { 'a', 'b', 'c', 234 };
@@ -988,18 +1237,32 @@ _dbus_message_test (const char *test_data_dir)
   dbus_uint16_t v_UINT16;
   dbus_int32_t v_INT32;
   dbus_uint32_t v_UINT32;
-#ifdef DBUS_HAVE_INT64
+  dbus_uint32_t v1_UINT32;
   dbus_int64_t v_INT64;
   dbus_uint64_t v_UINT64;
-#endif
   unsigned char v_BYTE;
   unsigned char v2_BYTE;
   dbus_bool_t v_BOOLEAN;
   DBusMessageIter iter, array_iter, struct_iter;
 #ifdef HAVE_UNIX_FD_PASSING
   int v_UNIX_FD;
+  int v1_UNIX_FD;
 #endif
   char **decomposed;
+  DBusInitialFDs *initial_fds;
+  dbus_bool_t ok;
+  char basic_types[] = DBUS_TYPE_BYTE_AS_STRING \
+                       DBUS_TYPE_BOOLEAN_AS_STRING \
+                       DBUS_TYPE_INT16_AS_STRING \
+                       DBUS_TYPE_INT32_AS_STRING \
+                       DBUS_TYPE_INT64_AS_STRING \
+                       DBUS_TYPE_UINT16_AS_STRING \
+                       DBUS_TYPE_UINT32_AS_STRING \
+                       DBUS_TYPE_UINT64_AS_STRING \
+                       DBUS_TYPE_DOUBLE_AS_STRING \
+                       DBUS_TYPE_STRING_AS_STRING;
+
+  initial_fds = _dbus_check_fdleaks_enter ();
 
   message = dbus_message_new_method_call ("org.freedesktop.DBus.TestService",
                                           "/org/freedesktop/TestPath",
@@ -1137,10 +1400,8 @@ _dbus_message_test (const char *test_data_dir)
   v_UINT16 = 0x123;
   v_INT32 = -0x12345678;
   v_UINT32 = 0x12300042;
-#ifdef DBUS_HAVE_INT64
   v_INT64 = DBUS_INT64_CONSTANT (-0x123456789abcd);
   v_UINT64 = DBUS_UINT64_CONSTANT (0x123456789abcd);
-#endif
   v_STRING = "Test string";
   v_DOUBLE = 3.14159;
   v_BOOLEAN = TRUE;
@@ -1148,6 +1409,7 @@ _dbus_message_test (const char *test_data_dir)
   v2_BYTE = 24;
 #ifdef HAVE_UNIX_FD_PASSING
   v_UNIX_FD = 1;
+  v1_UNIX_FD = 2;
 #endif
 
   dbus_message_append_args (message,
@@ -1155,10 +1417,8 @@ _dbus_message_test (const char *test_data_dir)
                             DBUS_TYPE_UINT16, &v_UINT16,
                            DBUS_TYPE_INT32, &v_INT32,
                             DBUS_TYPE_UINT32, &v_UINT32,
-#ifdef DBUS_HAVE_INT64
                             DBUS_TYPE_INT64, &v_INT64,
                             DBUS_TYPE_UINT64, &v_UINT64,
-#endif
                            DBUS_TYPE_STRING, &v_STRING,
                            DBUS_TYPE_DOUBLE, &v_DOUBLE,
                            DBUS_TYPE_BOOLEAN, &v_BOOLEAN,
@@ -1168,12 +1428,10 @@ _dbus_message_test (const char *test_data_dir)
                             _DBUS_N_ELEMENTS (our_uint32_array),
                             DBUS_TYPE_ARRAY, DBUS_TYPE_INT32, &v_ARRAY_INT32,
                             _DBUS_N_ELEMENTS (our_int32_array),
-#ifdef DBUS_HAVE_INT64
                             DBUS_TYPE_ARRAY, DBUS_TYPE_UINT64, &v_ARRAY_UINT64,
                             _DBUS_N_ELEMENTS (our_uint64_array),
                             DBUS_TYPE_ARRAY, DBUS_TYPE_INT64, &v_ARRAY_INT64,
                             _DBUS_N_ELEMENTS (our_int64_array),
-#endif
                             DBUS_TYPE_ARRAY, DBUS_TYPE_DOUBLE, &v_ARRAY_DOUBLE,
                             _DBUS_N_ELEMENTS (our_double_array),
                             DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &v_ARRAY_BYTE,
@@ -1190,10 +1448,8 @@ _dbus_message_test (const char *test_data_dir)
   sig[i++] = DBUS_TYPE_UINT16;
   sig[i++] = DBUS_TYPE_INT32;
   sig[i++] = DBUS_TYPE_UINT32;
-#ifdef DBUS_HAVE_INT64
   sig[i++] = DBUS_TYPE_INT64;
   sig[i++] = DBUS_TYPE_UINT64;
-#endif
   sig[i++] = DBUS_TYPE_STRING;
   sig[i++] = DBUS_TYPE_DOUBLE;
   sig[i++] = DBUS_TYPE_BOOLEAN;
@@ -1203,12 +1459,10 @@ _dbus_message_test (const char *test_data_dir)
   sig[i++] = DBUS_TYPE_UINT32;
   sig[i++] = DBUS_TYPE_ARRAY;
   sig[i++] = DBUS_TYPE_INT32;
-#ifdef DBUS_HAVE_INT64
   sig[i++] = DBUS_TYPE_ARRAY;
   sig[i++] = DBUS_TYPE_UINT64;
   sig[i++] = DBUS_TYPE_ARRAY;
   sig[i++] = DBUS_TYPE_INT64;
-#endif
   sig[i++] = DBUS_TYPE_ARRAY;
   sig[i++] = DBUS_TYPE_DOUBLE;
   sig[i++] = DBUS_TYPE_ARRAY;
@@ -1287,9 +1541,9 @@ _dbus_message_test (const char *test_data_dir)
     {
       DBusString *buffer;
 
-      _dbus_message_loader_get_buffer (loader, &buffer);
+      _dbus_message_loader_get_buffer (loader, &buffer, NULL, NULL);
       _dbus_string_append_byte (buffer, data[i]);
-      _dbus_message_loader_return_buffer (loader, buffer, 1);
+      _dbus_message_loader_return_buffer (loader, buffer);
     }
 
   /* Write the body data one byte at a time */
@@ -1298,9 +1552,9 @@ _dbus_message_test (const char *test_data_dir)
     {
       DBusString *buffer;
 
-      _dbus_message_loader_get_buffer (loader, &buffer);
+      _dbus_message_loader_get_buffer (loader, &buffer, NULL, NULL);
       _dbus_string_append_byte (buffer, data[i]);
-      _dbus_message_loader_return_buffer (loader, buffer, 1);
+      _dbus_message_loader_return_buffer (loader, buffer);
     }
 
 #ifdef HAVE_UNIX_FD_PASSING
@@ -1394,7 +1648,58 @@ _dbus_message_test (const char *test_data_dir)
   _dbus_message_loader_unref (loader);
 
   check_memleaks ();
-  _dbus_check_fdleaks();
+  _dbus_check_fdleaks_leave (initial_fds);
+  initial_fds = _dbus_check_fdleaks_enter ();
+
+  /* Test enumeration of array elements */
+  for (i = strlen (basic_types) - 1; i > 0; i--)
+    {
+      DBusBasicValue val;
+      int some;
+      char* signature = _dbus_strdup ("?");
+
+      signature[0] = basic_types[i];
+      s = "SomeThingToSay";
+      memset (&val, '\0', sizeof (val));
+
+      message = dbus_message_new_method_call ("de.ende.test",
+        "/de/ende/test", "de.ende.Test", "ArtistName");
+      _dbus_assert (message != NULL);
+      dbus_message_iter_init_append (message, &iter);
+      dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY,
+                                        signature, &array_iter);
+      for (some = 0; some < 3; some++)
+        {
+          if (basic_types[i] == DBUS_TYPE_STRING)
+            dbus_message_iter_append_basic (&array_iter, DBUS_TYPE_STRING, &s);
+          else
+            dbus_message_iter_append_basic (&array_iter, basic_types[i], &val);
+        }
+      dbus_message_iter_close_container (&iter, &array_iter);
+      dbus_message_iter_init (message, &iter);
+      _dbus_assert (dbus_message_iter_get_element_count (&iter) == some);
+      dbus_message_unref (message);
+      dbus_free (signature);
+    }
+  /* Array of structs */
+  message = dbus_message_new_method_call ("de.ende.test",
+      "/de/ende/test", "de.ende.Test", "ArtistName");
+  _dbus_assert (message != NULL);
+  dbus_message_iter_init_append (message, &iter);
+  dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY,
+                                    DBUS_STRUCT_BEGIN_CHAR_AS_STRING
+                                    DBUS_TYPE_STRING_AS_STRING
+                                    DBUS_STRUCT_END_CHAR_AS_STRING, &array_iter);
+  dbus_message_iter_open_container (&array_iter, DBUS_TYPE_STRUCT,
+                                    NULL, &struct_iter);
+  s = "SpamAndEggs";
+  dbus_message_iter_append_basic (&struct_iter, DBUS_TYPE_STRING, &s);
+  dbus_message_iter_close_container (&array_iter, &struct_iter);
+  dbus_message_iter_close_container (&iter, &array_iter);
+  dbus_message_iter_init (message, &iter);
+  _dbus_assert (dbus_message_iter_get_element_count (&iter) == 1);
+  dbus_message_unref (message);
+  check_memleaks ();
 
   /* Check that we can abandon a container */
   message = dbus_message_new_method_call ("org.freedesktop.DBus.TestService",
@@ -1404,18 +1709,19 @@ _dbus_message_test (const char *test_data_dir)
 
   dbus_message_iter_init_append (message, &iter);
 
-  _dbus_assert (dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY,
+  ok = dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY,
                                                  (DBUS_STRUCT_BEGIN_CHAR_AS_STRING
                                                   DBUS_TYPE_STRING_AS_STRING
                                                   DBUS_TYPE_STRING_AS_STRING
                                                   DBUS_STRUCT_END_CHAR_AS_STRING),
-                                                 &array_iter));
-  _dbus_assert (dbus_message_iter_open_container (&array_iter, DBUS_TYPE_STRUCT,
-                                                 NULL, &struct_iter));
-
+                          &array_iter);
+  _dbus_assert (ok);
+  ok = dbus_message_iter_open_container (&array_iter, DBUS_TYPE_STRUCT,
+                          NULL, &struct_iter);
+  _dbus_assert (ok);
   s = "peaches";
-  _dbus_assert (dbus_message_iter_append_basic (&struct_iter, DBUS_TYPE_STRING,
-                                               &s));
+  ok = dbus_message_iter_append_basic (&struct_iter, DBUS_TYPE_STRING, &s);
+  _dbus_assert (ok);
 
   /* uh-oh, error, try and unwind */
 
@@ -1424,6 +1730,51 @@ _dbus_message_test (const char *test_data_dir)
 
   dbus_message_unref (message);
 
+  /* Check we should not leak array of string or unix fd, fd.o#21259 */
+  message = dbus_message_new_method_call ("org.freedesktop.DBus.TestService",
+                                          "/org/freedesktop/TestPath",
+                                          "Foo.TestInterface",
+                                          "Method");
+
+  /* signature "uashuash" */
+  dbus_message_append_args (message,
+                            DBUS_TYPE_UINT32, &v_UINT32,
+                            DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &v_ARRAY_STRING,
+                            _DBUS_N_ELEMENTS (our_string_array),
+#ifdef HAVE_UNIX_FD_PASSING
+                            DBUS_TYPE_UNIX_FD, &v_UNIX_FD,
+#endif
+                            DBUS_TYPE_UINT32, &v1_UINT32,
+                            DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &v1_ARRAY_STRING,
+                            _DBUS_N_ELEMENTS (our_string_array1),
+#ifdef HAVE_UNIX_FD_PASSING
+                            DBUS_TYPE_UNIX_FD, &v1_UNIX_FD,
+#endif
+
+                            DBUS_TYPE_INVALID);
+
+  i = 0;
+  sig[i++] = DBUS_TYPE_UINT32;
+  sig[i++] = DBUS_TYPE_ARRAY;
+  sig[i++] = DBUS_TYPE_STRING;
+#ifdef HAVE_UNIX_FD_PASSING
+  sig[i++] = DBUS_TYPE_UNIX_FD;
+#endif
+  sig[i++] = DBUS_TYPE_UINT32;
+  sig[i++] = DBUS_TYPE_ARRAY;
+  sig[i++] = DBUS_TYPE_STRING;
+#ifdef HAVE_UNIX_FD_PASSING
+  sig[i++] = DBUS_TYPE_UNIX_FD;
+#endif
+  sig[i++] = DBUS_TYPE_INVALID;
+
+  _dbus_assert (i < (int) _DBUS_N_ELEMENTS (sig));
+
+  verify_test_message_args_ignored (message);
+  verify_test_message_memleak (message);
+
+  dbus_message_unref (message);
+
   /* Load all the sample messages from the message factory */
   {
     DBusMessageDataIter diter;
@@ -1441,7 +1792,7 @@ _dbus_message_test (const char *test_data_dir)
         if (!dbus_internal_do_not_use_try_message_data (&mdata.data,
                                                         mdata.expected_validity))
           {
-            _dbus_warn ("expected validity %d and did not get it\n",
+            _dbus_warn ("expected validity %d and did not get it",
                         mdata.expected_validity);
             _dbus_assert_not_reached ("message data failed");
           }
@@ -1458,16 +1809,23 @@ _dbus_message_test (const char *test_data_dir)
   }
 
   check_memleaks ();
-  _dbus_check_fdleaks();
+  _dbus_check_fdleaks_leave (initial_fds);
 
   /* Now load every message in test_data_dir if we have one */
   if (test_data_dir == NULL)
     return TRUE;
 
-  return dbus_internal_do_not_use_foreach_message_file (test_data_dir,
+  initial_fds = _dbus_check_fdleaks_enter ();
+
+  if (!dbus_internal_do_not_use_foreach_message_file (test_data_dir,
                                                         (DBusForeachMessageFileFunc)
                                                         dbus_internal_do_not_use_try_message_file,
-                                                        NULL);  
+                                                        NULL))
+    _dbus_assert_not_reached ("foreach_message_file test failed");
+
+  _dbus_check_fdleaks_leave (initial_fds);
+
+  return TRUE;
 }
 
-#endif /* DBUS_BUILD_TESTS */
+#endif /* DBUS_ENABLE_EMBEDDED_TESTS */