tests: Test that send overflow doesn't abort
authorManuel Stoeckl <code@mstoeckl.com>
Sun, 18 Aug 2019 16:36:14 +0000 (12:36 -0400)
committerManuel Stoeckl <code@mstoeckl.com>
Tue, 10 Sep 2019 11:59:50 +0000 (07:59 -0400)
The new display test runs a client that makes a very large number of
trivial requests. After responding to initial setup requests, the server
is paused, letting the trivial requests fill up the Unix socket buffer,
making further writes to the socket fail. The test then checks that the
client sets an appropriate error code, and does not abort or crash.

Signed-off-by: Manuel Stoeckl <code@mstoeckl.com>
tests/display-test.c
tests/test-compositor.c
tests/test-compositor.h

index a8e11f1..cf571fa 100644 (file)
@@ -1422,3 +1422,68 @@ TEST(registry_bind_interface_mismatch)
 
        display_destroy(d);
 }
+
+static void
+send_overflow_client(void *data)
+{
+       struct client *c = client_connect();
+       int i, err = 0;
+       int *pipes = data;
+       char tmp = '\0';
+
+       /* On Linux, the Unix socket default buffer size is <=256KB, and
+        * each noop request requires 8 bytes; the buffer should thus
+        * overflow within about 32K /unhandled/ iterations */
+       for (i = 0; i < 1000000; i++) {
+               noop_request(c);
+               err = wl_display_get_error(c->wl_display);
+               if (err)
+                       break;
+       }
+
+       /* Do not close the pipe file descriptors afterwards, because the leak
+        * check verifies that the initial/final FD counts are the same */
+       assert(write(pipes[1], &tmp, sizeof(tmp)) == (ssize_t)sizeof(tmp));
+
+       /* Expect an error */
+       fprintf(stderr, "Send loop failed on try %d, err = %d, %s\n", i, err, strerror(err));
+       assert(err == EAGAIN);
+
+       client_disconnect_nocheck(c);
+}
+
+TEST(send_overflow_disconnection)
+{
+       struct display *d;
+       struct client_info *c;
+       char tmp;
+       int rpipe[2];
+       int i;
+
+       assert(pipe(rpipe) != -1);
+
+       d = display_create();
+
+       c = client_create(d, send_overflow_client, &rpipe);
+
+       /* Close write end of the pipe, so that the later read() call gets
+        * interrupted if the client dies */
+       close(rpipe[1]);
+
+       /* At least 2 loops of this are needed to respond for the client to
+        * set up the test interface */
+       for (i = 0; i < 5; i++) {
+               wl_display_flush_clients(d->wl_display);
+               wl_event_loop_dispatch(wl_display_get_event_loop(d->wl_display), -1);
+       }
+
+       /* Wait until all noop requests have been sent, or until client
+        * process aborts */
+       (void)read(rpipe[0], &tmp, sizeof(tmp));
+       close(rpipe[0]);
+
+       /* For a clean shutdown */
+       display_run(d);
+
+       display_destroy(d);
+}
index 72f6351..bd2b015 100644 (file)
@@ -45,7 +45,8 @@ struct test_compositor;
 
 static const struct wl_message tc_requests[] = {
        /* this request serves as a barrier for synchronizing*/
-       { "stop_display", "u", NULL }
+       { "stop_display", "u", NULL },
+       { "noop", "", NULL },
 };
 
 static const struct wl_message tc_events[] = {
@@ -54,7 +55,7 @@ static const struct wl_message tc_events[] = {
 
 const struct wl_interface test_compositor_interface = {
        "test", 1,
-       1, tc_requests,
+       2, tc_requests,
        1, tc_events
 };
 
@@ -62,6 +63,8 @@ struct test_compositor_interface {
        void (*stop_display)(struct wl_client *client,
                             struct wl_resource *resource,
                             uint32_t num);
+       void (*noop)(struct wl_client *client,
+                            struct wl_resource *resource);
 };
 
 struct test_compositor_listener {
@@ -70,7 +73,8 @@ struct test_compositor_listener {
 };
 
 enum {
-       STOP_DISPLAY = 0
+       STOP_DISPLAY = 0,
+       TEST_NOOP = 1
 };
 
 enum {
@@ -294,8 +298,16 @@ handle_stop_display(struct wl_client *client,
                wl_display_terminate(d->wl_display);
 }
 
+static void
+handle_noop(struct wl_client *client, struct wl_resource *resource)
+{
+       (void)client;
+       (void)resource;
+}
+
 static const struct test_compositor_interface tc_implementation = {
-       handle_stop_display
+       handle_stop_display,
+       handle_noop,
 };
 
 static void
@@ -509,3 +521,9 @@ stop_display(struct client *c, int num)
 
        return n;
 }
+
+void
+noop_request(struct client *c)
+{
+       wl_proxy_marshal((struct wl_proxy *) c->tc, TEST_NOOP);
+}
index 876d0c0..90999b2 100644 (file)
@@ -70,6 +70,7 @@ struct client {
 struct client *client_connect(void);
 void client_disconnect(struct client *);
 int stop_display(struct client *, int);
+void noop_request(struct client *);
 
 /**
  * Usual workflow: