1 /* vim: et ts=2 sw=2 tw=80: */
3 * This file is part of the Nice GLib ICE library.
5 * (C) 2010, 2014 Collabora Ltd.
6 * Contact: Philip Withnall
8 * The contents of this file are subject to the Mozilla Public License Version
9 * 1.1 (the "License"); you may not use this file except in compliance with
10 * the License. You may obtain a copy of the License at
11 * http://www.mozilla.org/MPL/
13 * Software distributed under the License is distributed on an "AS IS" basis,
14 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
15 * for the specific language governing rights and limitations under the
18 * The Original Code is the Nice GLib ICE library.
20 * The Initial Developers of the Original Code are Collabora Ltd and Nokia
21 * Corporation. All Rights Reserved.
24 * Philip Withnall, Collabora Ltd.
25 * Youness Alaoui, Collabora Ltd.
27 * Alternatively, the contents of this file may be used under the terms of the
28 * the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
29 * case the provisions of LGPL are applicable instead of those above. If you
30 * wish to allow use of your version of this file only under the terms of the
31 * LGPL and not to allow others to use your version of this file under the
32 * MPL, indicate your decision by deleting the provisions above and replace
33 * them with the notice and other provisions required by the LGPL. If you do
34 * not delete the provisions above, a recipient may use your version of this
35 * file under either the MPL or the LGPL.
49 #include "pseudotcp.h"
53 * A fuzzing test for the pseudotcp socket. This connects two sockets in a
54 * loopback arrangement, with the packet output from one being fed to the other,
55 * and vice-versa. Fuzzing happens on the packet interface between the two,
56 * mutating the packets slightly and seeing what happens.
58 * The input data to the left-most socket is read from a file. The output data
59 * from the loopback is written to another file, although this probably isn’t
60 * very useful. If no files are provided, a small amount of dummy data is sent
61 * through the sockets instead. This almost certainly won’t catch any bugs, and
62 * is just present to allow this test to be run as part of `make check` so it
65 * A good command to generate an input file is:
66 * dd if=/dev/urandom of=rand count=10000 ibs=1024
68 * None of the data is validated, and the test results are effectively the 1-bit
69 * value of ‘did it crash?’. In particular, the output file is not validated,
70 * and the TCP packets emitted by both sockets are not checked for validity.
72 * It is suggested that this test is run under GDB and Valgrind. Any crashes or
73 * errors which are detected can be reproduced by providing the same input file
74 * and seed (using the --seed option). The seed is printed out at the beginning
79 PseudoTcpSocket *left;
80 PseudoTcpSocket *right;
81 GMainLoop *main_loop = NULL;
89 guint right_clock = 0;
90 gboolean left_closed = FALSE;
91 gboolean right_closed = FALSE;
92 gboolean reading_done = FALSE;
94 /* Number of bytes of payload each socket has received so far. */
95 guint32 left_stream_pos = 0;
96 guint32 right_stream_pos = 0;
98 /* Configuration options. */
100 guint32 fuzz_start_pos = 1; /* bytes into stream payload; after the SYN-ACKs */
101 guint n_changes_lambda = 1; /* lambda parameter for a Poisson distribution
102 * controlling the number of mutations made to each
107 adjust_clock (PseudoTcpSocket *sock);
111 write_to_sock (PseudoTcpSocket *sock)
119 len = fread (buf, 1, sizeof(buf), in);
121 g_debug ("Done reading data from file");
122 g_assert (feof (in));
124 pseudo_tcp_socket_close (sock, FALSE);
127 wlen = pseudo_tcp_socket_send (sock, buf, len);
128 g_debug ("Sending %" G_GSIZE_FORMAT " bytes : %d", len, wlen);
131 if (wlen < (gint) len) {
132 g_debug ("seeking %" G_GSIZE_FORMAT " from %lu", wlen - len, ftell (in));
133 fseek (in, wlen - len, SEEK_CUR);
134 g_assert (!feof (in));
135 g_debug ("Socket queue full after %d bytes written", total);
144 opened (PseudoTcpSocket *sock, gpointer data)
146 g_debug ("Socket %p Opened", sock);
149 write_to_sock (sock);
151 pseudo_tcp_socket_send (sock, "abcdefghijklmnopqrstuvwxyz", 26);
153 pseudo_tcp_socket_close (sock, FALSE);
159 readable (PseudoTcpSocket *sock, gpointer data)
163 g_debug ("Socket %p Readable", sock);
166 len = pseudo_tcp_socket_recv (sock, buf, sizeof(buf));
169 g_debug ("Read %d bytes", len);
171 if (fwrite (buf, len, 1, out) == 0)
172 g_debug ("Error writing to output file");
176 g_assert_cmpint (total_wrote, <=, total_read);
177 g_debug ("Written %d bytes, need %d bytes", total_wrote, total_read);
178 if (total_wrote == total_read && feof (in)) {
179 g_assert (reading_done);
180 pseudo_tcp_socket_close (sock, FALSE);
184 pseudo_tcp_socket_close (sock, FALSE);
186 } else if (len == 0) {
187 pseudo_tcp_socket_close (sock, FALSE);
192 pseudo_tcp_socket_get_error (sock) != EWOULDBLOCK) {
193 g_printerr ("Error reading from socket %p: %s.\n",
194 sock, g_strerror (pseudo_tcp_socket_get_error (sock)));
197 g_main_loop_quit (main_loop);
203 writable (PseudoTcpSocket *sock, gpointer data)
205 g_debug ("Socket %p Writable", sock);
206 if (in && sock == left)
207 write_to_sock (sock);
211 closed (PseudoTcpSocket *sock, guint32 err, gpointer data)
213 /* Don’t treat this as an error, since we’re throwing rubbish into the
214 * socket and can hardly expect it to complete successfully. */
215 g_debug ("Socket %p Closed: %s", sock, g_strerror (err));
217 g_main_loop_quit (main_loop);
221 PseudoTcpSocket *sock;
228 * random_int_poisson:
229 * @lambda: Lambda parameter for the distribution function, which must be
232 * Generate a random variable from a Poisson distribution with parameter
233 * @lambda. This consumes one %gdouble’s worth of randomness from the global
236 * This is implemented using the inverse transform of the Poisson CDF, and is
237 * guaranteed to return in time linearly proportional to @lambda.
239 * Returns: Poisson-distributed pseudo-random variable
242 random_int_poisson (guint lambda)
248 g_return_val_if_fail (lambda > 0, 0);
251 * Reference: http://www.cs.bgu.ac.il/~mps042/invtransnote.htm,
252 * §Simulating a Poisson random variable.
254 U = g_rand_double (prng); /* step 1 */
256 p = exp (0.0 - (gdouble) lambda);
259 while (U >= F) { /* step 3 */
260 p = (lambda * p) / (i + 1);
262 i += 1; /* step 4 and 5 */
269 fuzz_packet (guint8 *buf, guint32 len, guint32 stream_pos)
273 #define HEADER_LENGTH 24 /* bytes; or thereabouts (include some options) */
275 /* Do we want to fuzz this packet? */
276 if (stream_pos < fuzz_start_pos) {
280 /* Get fuzzing. Only bother fuzzing the header; fuzzing the payload is
281 * pointless. Weight the number of changes towards having only a few changes,
282 * since that makes them less likely to be summarily rejected. */
283 n_changes = random_int_poisson (n_changes_lambda);
284 g_debug ("Making %u changes for bytes at stream position %u:",
285 n_changes, stream_pos);
287 for (i = 0; i < n_changes; i++) {
288 guint32 pos = g_rand_int_range (prng, 0, MIN (len, HEADER_LENGTH));
289 g_debug (" • Changing byte %u.", stream_pos + pos);
290 buf[pos] = g_rand_int_range (prng, 0, G_MAXUINT8 + 1);
297 notify_packet (gpointer user_data)
299 struct notify_data *data = (struct notify_data*) user_data;
301 /* Fuzz the packet. */
302 data->len = fuzz_packet (data->buffer, data->len, data->stream_pos);
304 pseudo_tcp_socket_notify_packet (data->sock,
305 (gchar *) data->buffer, data->len);
306 adjust_clock (data->sock);
312 static PseudoTcpWriteResult
313 write_packet (PseudoTcpSocket *sock, const gchar *buffer, guint32 len,
316 struct notify_data *data;
317 PseudoTcpState state;
318 g_object_get (sock, "state", &state, NULL);
320 data = g_malloc (sizeof(struct notify_data) + len);
322 g_debug ("Socket %p(%d) Writing : %d bytes", sock, state, len);
324 memcpy (data->buffer, buffer, len);
328 data->stream_pos = left_stream_pos;
329 left_stream_pos += len;
332 data->stream_pos = right_stream_pos;
333 right_stream_pos += len;
337 g_idle_add (notify_packet, data);
343 static gboolean notify_clock (gpointer data)
345 PseudoTcpSocket *sock = (PseudoTcpSocket *)data;
346 //g_debug ("Socket %p: Notifying clock", sock);
347 pseudo_tcp_socket_notify_clock (sock);
352 static void adjust_clock (PseudoTcpSocket *sock)
356 if (pseudo_tcp_socket_get_next_clock (sock, &timeout)) {
357 timeout -= g_get_monotonic_time () / 1000;
358 g_debug ("Socket %p: Adjusting clock to %" G_GUINT64_FORMAT " ms", sock, timeout);
361 g_source_remove (left_clock);
362 left_clock = g_timeout_add (timeout, notify_clock, sock);
364 if (right_clock != 0)
365 g_source_remove (right_clock);
366 right_clock = g_timeout_add (timeout, notify_clock, sock);
369 g_debug ("Socket %p should be destroyed, it's done", sock);
374 if (left_closed && right_closed)
375 g_main_loop_quit (main_loop);
379 static GOptionEntry entries[] = {
380 { "seed", 's', 0, G_OPTION_ARG_INT64, &seed, "PRNG seed", "N" },
381 { "fuzz-start-position", 'p', 0, G_OPTION_ARG_INT, &fuzz_start_pos,
382 "Number of bytes into the stream to start fuzzing after", "B" },
383 { "fuzz-n-changes-lambda", 'l', 0, G_OPTION_ARG_INT, &n_changes_lambda,
384 "Lambda value for the Poisson distribution controlling the number of "
385 "changes made to each packet", "λ" },
389 int main (int argc, char *argv[])
391 PseudoTcpCallbacks cbs = {
392 NULL, opened, readable, writable, closed, write_packet
394 GOptionContext *context;
395 GError *error = NULL;
397 setlocale (LC_ALL, "");
400 context = g_option_context_new ("— fuzz-test the pseudotcp socket");
401 g_option_context_add_main_entries (context, entries, NULL);
403 if (!g_option_context_parse (context, &argc, &argv, &error)) {
404 g_printerr ("Option parsing failed: %s\n", error->message);
408 if (n_changes_lambda == 0) {
409 g_printerr ("Option parsing failed: %s\n",
410 "Lambda values must be positive.");
414 g_option_context_free (context);
416 /* Tweak the configuration. */
418 seed = g_get_real_time ();
421 /* Open the input and output files */
423 in = fopen (argv[1], "r");
424 out = fopen (argv[2], "w");
427 /* Set up the main loop and sockets. */
428 main_loop = g_main_loop_new (NULL, FALSE);
430 g_print ("Using seed: %" G_GINT64_FORMAT ", start position: %u, λ: %u\n",
431 seed, fuzz_start_pos, n_changes_lambda);
432 prng = g_rand_new_with_seed (seed);
434 pseudo_tcp_set_debug_level (PSEUDO_TCP_DEBUG_VERBOSE);
436 left = pseudo_tcp_socket_new (0, &cbs);
437 right = pseudo_tcp_socket_new (0, &cbs);
438 g_debug ("Left: %p. Right: %p", left, right);
440 pseudo_tcp_socket_notify_mtu (left, 1496);
441 pseudo_tcp_socket_notify_mtu (right, 1496);
443 pseudo_tcp_socket_connect (left);
445 adjust_clock (right);
447 /* Run the main loop. */
448 g_main_loop_run (main_loop);
449 g_main_loop_unref (main_loop);
451 g_object_unref (left);
452 g_object_unref (right);
464 g_printerr ("\n%s\n", g_option_context_get_help (context, TRUE, NULL));
465 g_option_context_free (context);