corrupt test: close connection before releasing GSocket
[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
250   /* Free the DBusConnection before the GSocket, because GSocket is
251    * going to close our fd. GSocket tolerates closing an already-closed
252    * fd, whereas DBusLoop + DBusSocketSetEpoll doesn't. On Unix
253    * we could use dup() but that isn't portable to Windows :-(
254    */
255   dbus_connection_close (f->server_conn);
256   dbus_connection_unref (f->server_conn);
257   f->server_conn = NULL;
258
259   g_object_unref (socket);
260 }
261
262 static void
263 test_byte_order (Fixture *f,
264     gconstpointer addr)
265 {
266   GSocket *socket;
267   GError *gerror = NULL;
268   int fd;
269   char *blob;
270   const gchar *arg = not_a_dbus_message;
271   const gchar * const *args = &arg;
272   int blob_len;
273   DBusMessage *message;
274   dbus_bool_t mem;
275
276   test_message (f, addr);
277
278   message = dbus_message_new_signal ("/", "a.b", "c");
279   g_assert (message != NULL);
280   /* Append 0xFF bytes, so that the length of the body when byte-swapped
281    * is 0xFF000000, which is invalid */
282   mem = dbus_message_append_args (message,
283       DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &args, 0xFF,
284       DBUS_TYPE_INVALID);
285   g_assert (mem);
286   mem = dbus_message_marshal (message, &blob, &blob_len);
287   g_assert (mem);
288   g_assert_cmpuint (blob_len, >, 0xFF);
289   g_assert (blob != NULL);
290
291   dbus_message_unref (message);
292
293   /* Break the message by changing its claimed byte order, without actually
294    * byteswapping anything. We happen to know that byte order is the first
295    * byte. */
296   if (blob[0] == 'B')
297     blob[0] = 'l';
298   else
299     blob[0] = 'B';
300
301   /* OK, now the connection is working, let's break it */
302
303   dbus_connection_flush (f->server_conn);
304
305   if (!dbus_connection_get_socket (f->server_conn, &fd))
306     g_error ("failed to steal fd from server connection");
307
308   socket = g_socket_new_from_fd (fd, &gerror);
309   g_assert_no_error (gerror);
310   g_assert (socket != NULL);
311
312   send_n_bytes (socket, blob, blob_len);
313
314   dbus_free (blob);
315
316   /* Now spin on the client connection: the server just sent it a faulty
317    * message, so it should disconnect */
318   while (g_queue_is_empty (&f->client_messages))
319     {
320       g_print (".");
321       g_main_context_iteration (NULL, TRUE);
322     }
323
324   message = g_queue_pop_head (&f->client_messages);
325
326   g_assert (!dbus_message_contains_unix_fds (message));
327   g_assert_cmpstr (dbus_message_get_destination (message), ==, NULL);
328   g_assert_cmpstr (dbus_message_get_error_name (message), ==, NULL);
329   g_assert_cmpstr (dbus_message_get_interface (message), ==,
330       "org.freedesktop.DBus.Local");
331   g_assert_cmpstr (dbus_message_get_member (message), ==, "Disconnected");
332   g_assert_cmpstr (dbus_message_get_sender (message), ==, NULL);
333   g_assert_cmpstr (dbus_message_get_signature (message), ==, "");
334   g_assert_cmpstr (dbus_message_get_path (message), ==,
335       "/org/freedesktop/DBus/Local");
336
337   dbus_message_unref (message);
338
339   /* Free the DBusConnection before the GSocket, as above. */
340   dbus_connection_close (f->server_conn);
341   dbus_connection_unref (f->server_conn);
342   f->server_conn = NULL;
343
344   g_object_unref (socket);
345 }
346
347 static void
348 teardown (Fixture *f,
349     gconstpointer addr G_GNUC_UNUSED)
350 {
351   if (f->client_conn != NULL)
352     {
353       dbus_connection_close (f->client_conn);
354       dbus_connection_unref (f->client_conn);
355       f->client_conn = NULL;
356     }
357
358   if (f->server_conn != NULL)
359     {
360       dbus_connection_close (f->server_conn);
361       dbus_connection_unref (f->server_conn);
362       f->server_conn = NULL;
363     }
364
365   if (f->server != NULL)
366     {
367       dbus_server_disconnect (f->server);
368       dbus_server_unref (f->server);
369       f->server = NULL;
370     }
371 }
372
373 int
374 main (int argc,
375     char **argv)
376 {
377   g_test_init (&argc, &argv, NULL);
378   g_type_init ();
379
380   g_test_add ("/corrupt/tcp", Fixture, "tcp:host=127.0.0.1", setup,
381       test_corrupt, teardown);
382
383 #ifdef DBUS_UNIX
384   g_test_add ("/corrupt/unix", Fixture, "unix:tmpdir=/tmp", setup,
385       test_corrupt, teardown);
386 #endif
387
388   g_test_add ("/corrupt/byte-order/tcp", Fixture, "tcp:host=127.0.0.1", setup,
389       test_byte_order, teardown);
390
391   return g_test_run ();
392 }