diff --git a/src/wayland-client-private.h b/src/wayland-client-private.h new file mode 100644 index 00000000..5bb9b918 --- /dev/null +++ b/src/wayland-client-private.h @@ -0,0 +1,83 @@ +/* + * Copyright © 2008-2012 Kristian Høgsberg + * Copyright © 2010-2012 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* Struct definintions shared by the client code and test suite. */ + +/** \cond */ + +struct wl_event_queue { + struct wl_list event_list; + struct wl_list proxy_list; /**< struct wl_proxy::queue_link */ + struct wl_display *display; + char *name; +}; + +struct wl_proxy { + struct wl_object object; + struct wl_display *display; + struct wl_event_queue *queue; + uint32_t flags; + int refcount; + void *user_data; + wl_dispatcher_func_t dispatcher; + uint32_t version; + const char * const *tag; + struct wl_list queue_link; /**< in struct wl_event_queue::proxy_list */ +}; + +struct wl_display { + struct wl_proxy proxy; + struct wl_connection *connection; + + /* errno of the last wl_display error */ + int last_error; + + /* When display gets an error event from some object, it stores + * information about it here, so that client can get this + * information afterwards */ + struct { + /* Code of the error. It can be compared to + * the interface's errors enumeration. */ + uint32_t code; + /* interface (protocol) in which the error occurred */ + const struct wl_interface *interface; + /* id of the proxy that caused the error. There's no warranty + * that the proxy is still valid. It's up to client how it will + * use it */ + uint32_t id; + } protocol_error; + int fd; + struct wl_map objects; + struct wl_event_queue display_queue; + struct wl_event_queue default_queue; + pthread_mutex_t mutex; + + int reader_count; + uint32_t read_serial; + pthread_cond_t reader_cond; +}; + +/** \endcond */ diff --git a/src/wayland-client.c b/src/wayland-client.c index ed686b5c..9b0dc2fe 100644 --- a/src/wayland-client.c +++ b/src/wayland-client.c @@ -46,6 +46,7 @@ #include "wayland-client.h" #include "wayland-private.h" #include "timespec-util.h" +#include "wayland-client-private.h" /** \cond */ @@ -60,58 +61,6 @@ struct wl_zombie { int *fd_count; }; -struct wl_proxy { - struct wl_object object; - struct wl_display *display; - struct wl_event_queue *queue; - uint32_t flags; - int refcount; - void *user_data; - wl_dispatcher_func_t dispatcher; - uint32_t version; - const char * const *tag; - struct wl_list queue_link; /**< in struct wl_event_queue::proxy_list */ -}; - -struct wl_event_queue { - struct wl_list event_list; - struct wl_list proxy_list; /**< struct wl_proxy::queue_link */ - struct wl_display *display; - char *name; -}; - -struct wl_display { - struct wl_proxy proxy; - struct wl_connection *connection; - - /* errno of the last wl_display error */ - int last_error; - - /* When display gets an error event from some object, it stores - * information about it here, so that client can get this - * information afterwards */ - struct { - /* Code of the error. It can be compared to - * the interface's errors enumeration. */ - uint32_t code; - /* interface (protocol) in which the error occurred */ - const struct wl_interface *interface; - /* id of the proxy that caused the error. There's no warranty - * that the proxy is still valid. It's up to client how it will - * use it */ - uint32_t id; - } protocol_error; - int fd; - struct wl_map objects; - struct wl_event_queue display_queue; - struct wl_event_queue default_queue; - pthread_mutex_t mutex; - - int reader_count; - uint32_t read_serial; - pthread_cond_t reader_cond; -}; - /** \endcond */ static int debug_client = 0; diff --git a/tests/connection-test.c b/tests/connection-test.c index aed97a0a..3a9e28e2 100644 --- a/tests/connection-test.c +++ b/tests/connection-test.c @@ -807,6 +807,51 @@ leak_after_error(void) free(c); } +static void +too_long_message(void *arg) +{ +#define TOO_LONG ((size_t)(4096 - 12 + 1)) + void *buf = malloc(TOO_LONG); + assert(buf); + memset(buf, 0, TOO_LONG); + struct client *c = client_connect(); + struct wl_array arr = { TOO_LONG - 1, TOO_LONG, buf }; + set_buffer_size(c, *(uint32_t *)arg); + long_request(c, &arr); + wl_display_roundtrip(c->wl_display); + assert(wl_display_get_error(c->wl_display) == 0); + arr.size += 1; + long_request(c, &arr); + wl_display_dispatch(c->wl_display); + assert(wl_display_get_error(c->wl_display) == EINVAL); + wl_proxy_destroy((struct wl_proxy *) c->tc); + wl_display_disconnect(c->wl_display); + free(c); + free(buf); +} + +TEST(overlong_message_small_buffer) +{ + struct display *d = display_create(); + + uint32_t size = 4096; + client_create(d, too_long_message, &size); + display_run(d); + + display_destroy(d); +} + +TEST(overlong_message_long_buffer) +{ + struct display *d = display_create(); + + uint32_t size = 8192; + client_create(d, too_long_message, &size); + display_run(d); + + display_destroy(d); +} + TEST(closure_leaks_after_error) { struct display *d = display_create(); diff --git a/tests/test-compositor.c b/tests/test-compositor.c index efec31fa..2f8a08f8 100644 --- a/tests/test-compositor.c +++ b/tests/test-compositor.c @@ -34,8 +34,8 @@ #include #include #include - -#define WL_HIDE_DEPRECATED +#include "wayland-private.h" +#include "wayland-client-private.h" #include "test-runner.h" #include "test-compositor.h" @@ -49,6 +49,8 @@ static const struct wl_message tc_requests[] = { /* this request serves as a barrier for synchronizing*/ { "stop_display", "u", NULL }, { "noop", "", NULL }, + { "long_request", "a", NULL }, + { "set_buffer_size", "u", NULL }, }; static const struct wl_message tc_events[] = { @@ -57,7 +59,7 @@ static const struct wl_message tc_events[] = { const struct wl_interface test_compositor_interface = { "test", 1, - 2, tc_requests, + 4, tc_requests, 1, tc_events }; @@ -67,6 +69,12 @@ struct test_compositor_interface { uint32_t num); void (*noop)(struct wl_client *client, struct wl_resource *resource); + void (*handle_long_request)(struct wl_client *client, + struct wl_resource *resource, + struct wl_array *array); + void (*handle_set_buffer_size)(struct wl_client *client, + struct wl_resource *resource, + uint32_t buffer_size); }; struct test_compositor_listener { @@ -76,11 +84,13 @@ struct test_compositor_listener { enum { STOP_DISPLAY = 0, - TEST_NOOP = 1 + TEST_NOOP = 1, + LONG_REQUEST = 2, + SET_BUFFER_SIZE = 3, }; enum { - DISPLAY_RESUMED = 0 + DISPLAY_RESUMED = 0, }; /* Since tests can run parallelly, we need unique socket names @@ -338,9 +348,29 @@ handle_noop(struct wl_client *client, struct wl_resource *resource) (void)resource; } +static void +handle_long_request(struct wl_client *client, struct wl_resource *resource, struct wl_array *array) +{ + (void)client; + (void)resource; + (void)array; + /* This request should be rejected before the handler is called. */ + assert(array->size <= 4096 - 12 && + "overlong message not rejected sooner"); +} + +static void +handle_set_buffer_size(struct wl_client *client, struct wl_resource *resource, uint32_t buffer_size) +{ + (void)resource; + wl_client_set_max_buffer_size(client, buffer_size); +} + static const struct test_compositor_interface tc_implementation = { handle_stop_display, handle_noop, + handle_long_request, + handle_set_buffer_size, }; static void @@ -583,3 +613,32 @@ noop_request(struct client *c) { wl_proxy_marshal((struct wl_proxy *) c->tc, TEST_NOOP); } + +void +set_buffer_size(struct client *c, uint32_t size) +{ + wl_proxy_marshal((struct wl_proxy *) c->tc, SET_BUFFER_SIZE, size); +} + +void +long_request(struct client *c, struct wl_array *array) +{ + /* 12 is 8 bytes for the header and 4 bytes for the array length. */ + + /* Ensure that the array can validly be encoded in a message. */ + assert(array->size <= (UINT16_MAX & ~UINT16_C(3)) - 12 && "overlong wl_array"); + + struct wl_proxy *proxy = (struct wl_proxy *)c->tc; + if (array->size > WL_MAX_MESSAGE_SIZE - 12) { + size_t size = 12 + ((array->size + 3) & ~3); + uint32_t buf[2]; + + /* The server will post an error after getting + * the header, so don't send the request body. */ + buf[0] = wl_proxy_get_id(proxy); + buf[1] = size << 16 | LONG_REQUEST; + assert(wl_connection_write(proxy->display->connection, buf, sizeof(buf)) == 0); + } else { + wl_proxy_marshal(proxy, LONG_REQUEST, array); + } +} diff --git a/tests/test-compositor.h b/tests/test-compositor.h index 662a81c3..09f543ae 100644 --- a/tests/test-compositor.h +++ b/tests/test-compositor.h @@ -75,6 +75,8 @@ struct client *client_connect(void); void client_disconnect(struct client *); int stop_display(struct client *, int); void noop_request(struct client *); +void long_request(struct client *c, struct wl_array *a); +void set_buffer_size(struct client *c, uint32_t size); /** * Usual workflow: