tests: Test that an overlong message is rejected

If one runs the test case without the fix for
d074d52902 ("connection: Dynamically resize connection buffers"), the
server spins and never processes the message.  The test eventually times
out.  With the fix, a protocol error is delivered to the client and the
test finishes immediately.  The test covers both the default 4096-byte
buffer size and the case where the buffer size is large enough for the
message, but the message itself exceeds the 4096-byte limit.  Both are
rejected.

The test relies on the definition of struct wl_proxy, so this definition
is moved to a client-specific private header.  The definition itself is
not changed in any way.

Signed-off-by: Demi Marie Obenour <demi@invisiblethingslab.com>
This commit is contained in:
Demi Marie Obenour 2024-08-14 21:14:49 -04:00 committed by Demi Marie Obenour
parent cd0d1543c0
commit 3872a9362e
5 changed files with 195 additions and 57 deletions

View file

@ -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 */

View file

@ -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;

View file

@ -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();

View file

@ -34,8 +34,8 @@
#include <sys/socket.h>
#include <sys/wait.h>
#include <signal.h>
#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);
}
}

View file

@ -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: