Prepare 1.7.4 for tomorrow
[platform/upstream/dbus.git] / test / corrupt.c
1 /* Regression test for being disconnected by a corrupt message (fd.o #15578)
2  *
3  * Author: Simon McVittie <simon.mcvittie@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 }