Test that a message with the byte order mangled causes disconnection but no crash
[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 /* Enough bytes for it to be obvious that this connection is broken */
164 #define CORRUPT_LEN 1024
165
166 /* All-zero is not a valid D-Bus message header - for a start, this is
167  * protocol version 1, not 0 */
168 static const gchar not_a_dbus_message[CORRUPT_LEN] = { 0 };
169
170 static void
171 test_corrupt (Fixture *f,
172     gconstpointer addr)
173 {
174   GSocket *socket;
175   GError *gerror = NULL;
176   int fd;
177   gssize len, total_sent;
178   DBusMessage *incoming;
179
180   test_message (f, addr);
181
182   dbus_connection_flush (f->server_conn);
183
184   /* OK, now the connection is working, let's break it! Don't try this
185    * at home; splicing arbitrary bytes into the middle of the stream is
186    * specifically documented as not a valid thing to do. Who'd have thought? */
187   if (!dbus_connection_get_socket (f->server_conn, &fd))
188     g_error ("failed to steal fd from server connection");
189
190   socket = g_socket_new_from_fd (fd, &gerror);
191   g_assert_no_error (gerror);
192   g_assert (socket != NULL);
193
194   total_sent = 0;
195
196   while (total_sent < CORRUPT_LEN)
197     {
198       len = g_socket_send_with_blocking (socket,
199           not_a_dbus_message + total_sent, CORRUPT_LEN - total_sent,
200           TRUE, NULL, &gerror);
201       g_assert_no_error (gerror);
202       g_assert (len >= 0);
203       total_sent += len;
204     }
205
206   /* Now spin on the client connection: the server just sent it complete
207    * rubbish, so it should disconnect */
208   while (g_queue_is_empty (&f->client_messages))
209     {
210       g_print (".");
211       g_main_context_iteration (NULL, TRUE);
212     }
213
214   incoming = g_queue_pop_head (&f->client_messages);
215
216   g_assert (!dbus_message_contains_unix_fds (incoming));
217   g_assert_cmpstr (dbus_message_get_destination (incoming), ==, NULL);
218   g_assert_cmpstr (dbus_message_get_error_name (incoming), ==, NULL);
219   g_assert_cmpstr (dbus_message_get_interface (incoming), ==,
220       "org.freedesktop.DBus.Local");
221   g_assert_cmpstr (dbus_message_get_member (incoming), ==, "Disconnected");
222   g_assert_cmpstr (dbus_message_get_sender (incoming), ==, NULL);
223   g_assert_cmpstr (dbus_message_get_signature (incoming), ==, "");
224   g_assert_cmpstr (dbus_message_get_path (incoming), ==,
225       "/org/freedesktop/DBus/Local");
226
227   dbus_message_unref (incoming);
228 }
229
230 static void
231 test_byte_order (Fixture *f,
232     gconstpointer addr)
233 {
234   GSocket *socket;
235   GError *gerror = NULL;
236   int fd;
237   char *blob;
238   const gchar *arg = not_a_dbus_message;
239   const gchar * const *args = &arg;
240   int blob_len, len, total_sent;
241   DBusMessage *message;
242   dbus_bool_t mem;
243
244   test_message (f, addr);
245
246   message = dbus_message_new_signal ("/", "a.b", "c");
247   g_assert (message != NULL);
248   /* Append 0xFF bytes, so that the length of the body when byte-swapped
249    * is 0xFF000000, which is invalid */
250   mem = dbus_message_append_args (message,
251       DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &args, 0xFF,
252       DBUS_TYPE_INVALID);
253   g_assert (mem);
254   mem = dbus_message_marshal (message, &blob, &blob_len);
255   g_assert (mem);
256   g_assert_cmpuint (blob_len, >, 0xFF);
257   g_assert (blob != NULL);
258
259   dbus_message_unref (message);
260
261   /* Break the message by changing its claimed byte order, without actually
262    * byteswapping anything. We happen to know that byte order is the first
263    * byte. */
264   if (blob[0] == 'B')
265     blob[0] = 'l';
266   else
267     blob[0] = 'B';
268
269   /* OK, now the connection is working, let's break it */
270
271   dbus_connection_flush (f->server_conn);
272
273   if (!dbus_connection_get_socket (f->server_conn, &fd))
274     g_error ("failed to steal fd from server connection");
275
276   socket = g_socket_new_from_fd (fd, &gerror);
277   g_assert_no_error (gerror);
278   g_assert (socket != NULL);
279
280   total_sent = 0;
281
282   while (total_sent < blob_len)
283     {
284       len = g_socket_send_with_blocking (socket, blob + total_sent,
285           blob_len - total_sent, TRUE, NULL, &gerror);
286       g_assert_no_error (gerror);
287       g_assert (len >= 0);
288       total_sent += len;
289     }
290
291   dbus_free (blob);
292
293   /* Now spin on the client connection: the server just sent it a faulty
294    * message, so it should disconnect */
295   while (g_queue_is_empty (&f->client_messages))
296     {
297       g_print (".");
298       g_main_context_iteration (NULL, TRUE);
299     }
300
301   message = g_queue_pop_head (&f->client_messages);
302
303   g_assert (!dbus_message_contains_unix_fds (message));
304   g_assert_cmpstr (dbus_message_get_destination (message), ==, NULL);
305   g_assert_cmpstr (dbus_message_get_error_name (message), ==, NULL);
306   g_assert_cmpstr (dbus_message_get_interface (message), ==,
307       "org.freedesktop.DBus.Local");
308   g_assert_cmpstr (dbus_message_get_member (message), ==, "Disconnected");
309   g_assert_cmpstr (dbus_message_get_sender (message), ==, NULL);
310   g_assert_cmpstr (dbus_message_get_signature (message), ==, "");
311   g_assert_cmpstr (dbus_message_get_path (message), ==,
312       "/org/freedesktop/DBus/Local");
313
314   dbus_message_unref (message);
315 }
316
317 static void
318 teardown (Fixture *f,
319     gconstpointer addr G_GNUC_UNUSED)
320 {
321   if (f->client_conn != NULL)
322     {
323       dbus_connection_close (f->client_conn);
324       dbus_connection_unref (f->client_conn);
325       f->client_conn = NULL;
326     }
327
328   if (f->server_conn != NULL)
329     {
330       dbus_connection_close (f->server_conn);
331       dbus_connection_unref (f->server_conn);
332       f->server_conn = NULL;
333     }
334
335   if (f->server != NULL)
336     {
337       dbus_server_disconnect (f->server);
338       dbus_server_unref (f->server);
339       f->server = NULL;
340     }
341 }
342
343 int
344 main (int argc,
345     char **argv)
346 {
347   g_test_init (&argc, &argv, NULL);
348   g_type_init ();
349
350   g_test_add ("/corrupt/tcp", Fixture, "tcp:host=127.0.0.1", setup,
351       test_corrupt, teardown);
352
353 #ifdef DBUS_UNIX
354   g_test_add ("/corrupt/unix", Fixture, "unix:tmpdir=/tmp", setup,
355       test_corrupt, teardown);
356 #endif
357
358   g_test_add ("/corrupt/byte-order/tcp", Fixture, "tcp:host=127.0.0.1", setup,
359       test_byte_order, teardown);
360
361   return g_test_run ();
362 }