Home | History | Annotate | Download | only in test
      1 /* Regression test for being disconnected by a corrupt message (fd.o #15578)
      2  *
      3  * Author: Simon McVittie <simon.mcvittie (at) collabora.co.uk>
      4  * Copyright  2010-2011 Nokia Corporation
      5  *
      6  * Permission is hereby granted, free of charge, to any person
      7  * obtaining a copy of this software and associated documentation files
      8  * (the "Software"), to deal in the Software without restriction,
      9  * including without limitation the rights to use, copy, modify, merge,
     10  * publish, distribute, sublicense, and/or sell copies of the Software,
     11  * and to permit persons to whom the Software is furnished to do so,
     12  * subject to the following conditions:
     13  *
     14  * The above copyright notice and this permission notice shall be
     15  * included in all copies or substantial portions of the Software.
     16  *
     17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     18  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
     20  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
     21  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
     22  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
     23  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
     24  * SOFTWARE.
     25  */
     26 
     27 #include <config.h>
     28 
     29 #include <glib.h>
     30 #include <gio/gio.h>
     31 
     32 #include <dbus/dbus.h>
     33 #include <dbus/dbus-glib-lowlevel.h>
     34 
     35 typedef struct {
     36     DBusError e;
     37 
     38     DBusServer *server;
     39     DBusConnection *server_conn;
     40     /* queue of DBusMessage */
     41     GQueue client_messages;
     42 
     43     DBusConnection *client_conn;
     44 } Fixture;
     45 
     46 static void
     47 assert_no_error (const DBusError *e)
     48 {
     49   if (G_UNLIKELY (dbus_error_is_set (e)))
     50     g_error ("expected success but got error: %s: %s", e->name, e->message);
     51 }
     52 
     53 static DBusHandlerResult
     54 client_message_cb (DBusConnection *client_conn,
     55     DBusMessage *message,
     56     void *data)
     57 {
     58   Fixture *f = data;
     59 
     60   g_assert (client_conn == f->client_conn);
     61   g_queue_push_tail (&f->client_messages, dbus_message_ref (message));
     62 
     63   return DBUS_HANDLER_RESULT_HANDLED;
     64 }
     65 
     66 static void
     67 new_conn_cb (DBusServer *server,
     68     DBusConnection *server_conn,
     69     void *data)
     70 {
     71   Fixture *f = data;
     72 
     73   g_assert (f->server_conn == NULL);
     74   f->server_conn = dbus_connection_ref (server_conn);
     75   dbus_connection_setup_with_g_main (server_conn, NULL);
     76 }
     77 
     78 static void
     79 setup (Fixture *f,
     80     gconstpointer addr)
     81 {
     82   dbus_error_init (&f->e);
     83   g_queue_init (&f->client_messages);
     84 
     85   f->server = dbus_server_listen (addr, &f->e);
     86   assert_no_error (&f->e);
     87   g_assert (f->server != NULL);
     88 
     89   dbus_server_set_new_connection_function (f->server,
     90       new_conn_cb, f, NULL);
     91   dbus_server_setup_with_g_main (f->server, NULL);
     92 }
     93 
     94 static void
     95 test_connect (Fixture *f,
     96     gconstpointer addr G_GNUC_UNUSED)
     97 {
     98   dbus_bool_t have_mem;
     99 
    100   g_assert (f->server_conn == NULL);
    101 
    102   f->client_conn = dbus_connection_open_private (
    103       dbus_server_get_address (f->server), &f->e);
    104   assert_no_error (&f->e);
    105   g_assert (f->client_conn != NULL);
    106   dbus_connection_setup_with_g_main (f->client_conn, NULL);
    107 
    108   while (f->server_conn == NULL)
    109     {
    110       g_print (".");
    111       g_main_context_iteration (NULL, TRUE);
    112     }
    113 
    114   have_mem = dbus_connection_add_filter (f->client_conn,
    115       client_message_cb, f, NULL);
    116   g_assert (have_mem);
    117 }
    118 
    119 static void
    120 test_message (Fixture *f,
    121     gconstpointer addr)
    122 {
    123   dbus_bool_t have_mem;
    124   dbus_uint32_t serial;
    125   DBusMessage *outgoing, *incoming;
    126 
    127   test_connect (f, addr);
    128 
    129   outgoing = dbus_message_new_signal ("/com/example/Hello",
    130       "com.example.Hello", "Greeting");
    131   g_assert (outgoing != NULL);
    132 
    133   have_mem = dbus_connection_send (f->server_conn, outgoing, &serial);
    134   g_assert (have_mem);
    135   g_assert (serial != 0);
    136 
    137   while (g_queue_is_empty (&f->client_messages))
    138     {
    139       g_print (".");
    140       g_main_context_iteration (NULL, TRUE);
    141     }
    142 
    143   g_assert_cmpuint (g_queue_get_length (&f->client_messages), ==, 1);
    144 
    145   incoming = g_queue_pop_head (&f->client_messages);
    146 
    147   g_assert (!dbus_message_contains_unix_fds (incoming));
    148   g_assert_cmpstr (dbus_message_get_destination (incoming), ==, NULL);
    149   g_assert_cmpstr (dbus_message_get_error_name (incoming), ==, NULL);
    150   g_assert_cmpstr (dbus_message_get_interface (incoming), ==,
    151       "com.example.Hello");
    152   g_assert_cmpstr (dbus_message_get_member (incoming), ==, "Greeting");
    153   g_assert_cmpstr (dbus_message_get_sender (incoming), ==, NULL);
    154   g_assert_cmpstr (dbus_message_get_signature (incoming), ==, "");
    155   g_assert_cmpstr (dbus_message_get_path (incoming), ==, "/com/example/Hello");
    156   g_assert_cmpuint (dbus_message_get_serial (incoming), ==, serial);
    157 
    158   dbus_message_unref (incoming);
    159 
    160   dbus_message_unref (outgoing);
    161 }
    162 
    163 static void
    164 send_n_bytes (GSocket *socket,
    165               const gchar *blob,
    166               gssize blob_len)
    167 {
    168   gssize len, total_sent;
    169   GError *gerror = NULL;
    170 
    171   total_sent = 0;
    172 
    173   while (total_sent < blob_len)
    174     {
    175       len = g_socket_send (socket,
    176                            blob + total_sent,
    177                            blob_len - total_sent,
    178                            NULL, &gerror);
    179 
    180       /* this is NULL-safe: a NULL error does not match */
    181       if (g_error_matches (gerror, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
    182         {
    183           /* we could wait for G_IO_OUT, but life's too short; just sleep */
    184           g_clear_error (&gerror);
    185           g_usleep (G_USEC_PER_SEC / 10);
    186           continue;
    187         }
    188 
    189       g_assert_no_error (gerror);
    190       g_assert (len >= 0);
    191       total_sent += len;
    192     }
    193 }
    194 
    195 /* Enough bytes for it to be obvious that this connection is broken */
    196 #define CORRUPT_LEN 1024
    197 
    198 /* All-zero is not a valid D-Bus message header - for a start, this is
    199  * protocol version 1, not 0 */
    200 static const gchar not_a_dbus_message[CORRUPT_LEN] = { 0 };
    201 
    202 static void
    203 test_corrupt (Fixture *f,
    204     gconstpointer addr)
    205 {
    206   GSocket *socket;
    207   GError *gerror = NULL;
    208   int fd;
    209   DBusMessage *incoming;
    210 
    211   test_message (f, addr);
    212 
    213   dbus_connection_flush (f->server_conn);
    214 
    215   /* OK, now the connection is working, let's break it! Don't try this
    216    * at home; splicing arbitrary bytes into the middle of the stream is
    217    * specifically documented as not a valid thing to do. Who'd have thought? */
    218   if (!dbus_connection_get_socket (f->server_conn, &fd))
    219     g_error ("failed to steal fd from server connection");
    220 
    221   socket = g_socket_new_from_fd (fd, &gerror);
    222   g_assert_no_error (gerror);
    223   g_assert (socket != NULL);
    224 
    225   send_n_bytes (socket, not_a_dbus_message, CORRUPT_LEN);
    226 
    227   /* Now spin on the client connection: the server just sent it complete
    228    * rubbish, so it should disconnect */
    229   while (g_queue_is_empty (&f->client_messages))
    230     {
    231       g_print (".");
    232       g_main_context_iteration (NULL, TRUE);
    233     }
    234 
    235   incoming = g_queue_pop_head (&f->client_messages);
    236 
    237   g_assert (!dbus_message_contains_unix_fds (incoming));
    238   g_assert_cmpstr (dbus_message_get_destination (incoming), ==, NULL);
    239   g_assert_cmpstr (dbus_message_get_error_name (incoming), ==, NULL);
    240   g_assert_cmpstr (dbus_message_get_interface (incoming), ==,
    241       "org.freedesktop.DBus.Local");
    242   g_assert_cmpstr (dbus_message_get_member (incoming), ==, "Disconnected");
    243   g_assert_cmpstr (dbus_message_get_sender (incoming), ==, NULL);
    244   g_assert_cmpstr (dbus_message_get_signature (incoming), ==, "");
    245   g_assert_cmpstr (dbus_message_get_path (incoming), ==,
    246       "/org/freedesktop/DBus/Local");
    247 
    248   dbus_message_unref (incoming);
    249   g_object_unref (socket);
    250 }
    251 
    252 static void
    253 test_byte_order (Fixture *f,
    254     gconstpointer addr)
    255 {
    256   GSocket *socket;
    257   GError *gerror = NULL;
    258   int fd;
    259   char *blob;
    260   const gchar *arg = not_a_dbus_message;
    261   const gchar * const *args = &arg;
    262   int blob_len;
    263   DBusMessage *message;
    264   dbus_bool_t mem;
    265 
    266   test_message (f, addr);
    267 
    268   message = dbus_message_new_signal ("/", "a.b", "c");
    269   g_assert (message != NULL);
    270   /* Append 0xFF bytes, so that the length of the body when byte-swapped
    271    * is 0xFF000000, which is invalid */
    272   mem = dbus_message_append_args (message,
    273       DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &args, 0xFF,
    274       DBUS_TYPE_INVALID);
    275   g_assert (mem);
    276   mem = dbus_message_marshal (message, &blob, &blob_len);
    277   g_assert (mem);
    278   g_assert_cmpuint (blob_len, >, 0xFF);
    279   g_assert (blob != NULL);
    280 
    281   dbus_message_unref (message);
    282 
    283   /* Break the message by changing its claimed byte order, without actually
    284    * byteswapping anything. We happen to know that byte order is the first
    285    * byte. */
    286   if (blob[0] == 'B')
    287     blob[0] = 'l';
    288   else
    289     blob[0] = 'B';
    290 
    291   /* OK, now the connection is working, let's break it */
    292 
    293   dbus_connection_flush (f->server_conn);
    294 
    295   if (!dbus_connection_get_socket (f->server_conn, &fd))
    296     g_error ("failed to steal fd from server connection");
    297 
    298   socket = g_socket_new_from_fd (fd, &gerror);
    299   g_assert_no_error (gerror);
    300   g_assert (socket != NULL);
    301 
    302   send_n_bytes (socket, blob, blob_len);
    303 
    304   dbus_free (blob);
    305 
    306   /* Now spin on the client connection: the server just sent it a faulty
    307    * message, so it should disconnect */
    308   while (g_queue_is_empty (&f->client_messages))
    309     {
    310       g_print (".");
    311       g_main_context_iteration (NULL, TRUE);
    312     }
    313 
    314   message = g_queue_pop_head (&f->client_messages);
    315 
    316   g_assert (!dbus_message_contains_unix_fds (message));
    317   g_assert_cmpstr (dbus_message_get_destination (message), ==, NULL);
    318   g_assert_cmpstr (dbus_message_get_error_name (message), ==, NULL);
    319   g_assert_cmpstr (dbus_message_get_interface (message), ==,
    320       "org.freedesktop.DBus.Local");
    321   g_assert_cmpstr (dbus_message_get_member (message), ==, "Disconnected");
    322   g_assert_cmpstr (dbus_message_get_sender (message), ==, NULL);
    323   g_assert_cmpstr (dbus_message_get_signature (message), ==, "");
    324   g_assert_cmpstr (dbus_message_get_path (message), ==,
    325       "/org/freedesktop/DBus/Local");
    326 
    327   dbus_message_unref (message);
    328   g_object_unref (socket);
    329 }
    330 
    331 static void
    332 teardown (Fixture *f,
    333     gconstpointer addr G_GNUC_UNUSED)
    334 {
    335   if (f->client_conn != NULL)
    336     {
    337       dbus_connection_close (f->client_conn);
    338       dbus_connection_unref (f->client_conn);
    339       f->client_conn = NULL;
    340     }
    341 
    342   if (f->server_conn != NULL)
    343     {
    344       dbus_connection_close (f->server_conn);
    345       dbus_connection_unref (f->server_conn);
    346       f->server_conn = NULL;
    347     }
    348 
    349   if (f->server != NULL)
    350     {
    351       dbus_server_disconnect (f->server);
    352       dbus_server_unref (f->server);
    353       f->server = NULL;
    354     }
    355 }
    356 
    357 int
    358 main (int argc,
    359     char **argv)
    360 {
    361   g_test_init (&argc, &argv, NULL);
    362   g_type_init ();
    363 
    364   g_test_add ("/corrupt/tcp", Fixture, "tcp:host=127.0.0.1", setup,
    365       test_corrupt, teardown);
    366 
    367 #ifdef DBUS_UNIX
    368   g_test_add ("/corrupt/unix", Fixture, "unix:tmpdir=/tmp", setup,
    369       test_corrupt, teardown);
    370 #endif
    371 
    372   g_test_add ("/corrupt/byte-order/tcp", Fixture, "tcp:host=127.0.0.1", setup,
    373       test_byte_order, teardown);
    374 
    375   return g_test_run ();
    376 }
    377