diff --git a/cursor/wayland-cursor.c b/cursor/wayland-cursor.c index 636f5166..89ecc9a1 100644 --- a/cursor/wayland-cursor.c +++ b/cursor/wayland-cursor.c @@ -27,6 +27,7 @@ #include "xcursor.h" #include "wayland-cursor.h" #include "wayland-client.h" +#include #include #include #include @@ -284,7 +285,8 @@ wl_cursor_create_from_xcursor_images(struct xcursor_images *images, { struct cursor *cursor; struct cursor_image *image; - int i, size; + size_t size; + int i; cursor = malloc(sizeof *cursor); if (!cursor) @@ -314,7 +316,12 @@ wl_cursor_create_from_xcursor_images(struct xcursor_images *images, image->image.hotspot_y = images->images[i]->yhot; image->image.delay = images->images[i]->delay; - size = image->image.width * image->image.height * 4; + size = (size_t) image->image.width * image->image.height * 4; + if (size > INT_MAX) { + free(image); + break; + } + image->offset = shm_pool_allocate(theme->pool, size); if (image->offset < 0) { free(image); @@ -344,6 +351,8 @@ load_callback(struct xcursor_images *images, void *data) { struct wl_cursor_theme *theme = data; struct wl_cursor *cursor; + struct wl_cursor **p; + size_t s; if (wl_cursor_theme_get_cursor(theme, images->name)) { xcursor_images_destroy(images); @@ -353,15 +362,14 @@ load_callback(struct xcursor_images *images, void *data) cursor = wl_cursor_create_from_xcursor_images(images, theme); if (cursor) { - theme->cursor_count++; - theme->cursors = - realloc(theme->cursors, - theme->cursor_count * sizeof theme->cursors[0]); + s = theme->cursor_count + 1; + p = realloc(theme->cursors, s * sizeof theme->cursors[0]); - if (theme->cursors == NULL) { - theme->cursor_count--; + if (p == NULL) { free(cursor); } else { + theme->cursor_count = s; + theme->cursors = p; theme->cursors[theme->cursor_count - 1] = cursor; } } @@ -389,6 +397,9 @@ wl_cursor_theme_load(const char *name, int size, struct wl_shm *shm) if (!theme) return NULL; + if (size < 0 || (size > 0 && INT_MAX / size / 4 < size)) + return NULL; + if (!name) name = "default"; diff --git a/cursor/xcursor.c b/cursor/xcursor.c index 2b6c47d5..6e54cdbd 100644 --- a/cursor/xcursor.c +++ b/cursor/xcursor.c @@ -259,6 +259,8 @@ xcursor_read_file_header(FILE *file) return NULL; if (!xcursor_read_uint(file, &head.ntoc)) return NULL; + if (head.header < XCURSOR_FILE_HEADER_LEN) + return NULL; skip = head.header - XCURSOR_FILE_HEADER_LEN; if (skip) if (fseek(file, skip, SEEK_CUR) == EOF) @@ -571,7 +573,7 @@ xcursor_build_theme_dir(const char *dir, const char *theme) * add space for any needed directory separators, one per component, * and one for the trailing null */ - full_size = 1 + homelen + 1 + dirlen + 1 + themelen + 1; + full_size = (size_t) 1 + homelen + 1 + dirlen + 1 + themelen + 1; full = malloc(full_size); if (!full) return NULL; @@ -686,11 +688,15 @@ load_all_cursors_from_dir(const char *path, int size, void *user_data) { FILE *f; - DIR *dir = opendir(path); + DIR *dir; struct dirent *ent; char *full; struct xcursor_images *images; + if (!path) + return; + + dir = opendir(path); if (!dir) return; diff --git a/egl/meson.build b/egl/meson.build index 5363e808..b72c7a46 100644 --- a/egl/meson.build +++ b/egl/meson.build @@ -22,6 +22,7 @@ if get_option('tests') test( 'wayland-egl symbols check', find_program('wayland-egl-symbols-check'), + depends: wayland_egl, env: [ 'WAYLAND_EGL_LIB=@0@'.format(wayland_egl_shared.full_path()), 'NM=@0@'.format(nm_path) diff --git a/meson.build b/meson.build index a3126b63..ef34ba4a 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ project( 'wayland', 'c', - version: '1.23.91', + version: '1.24.0', license: 'MIT', meson_version: '>= 0.57.0', default_options: [ diff --git a/src/connection.c b/src/connection.c index 34495211..3ef8688d 100644 --- a/src/connection.c +++ b/src/connection.c @@ -1229,6 +1229,11 @@ wl_closure_invoke(struct wl_closure *closure, uint32_t flags, count + 2, &ffi_type_void, ffi_types); implementation = target->implementation; + if (!implementation) { + wl_abort("Implementation of resource %d of %s is NULL\n", + target->id, target->interface->name); + } + if (!implementation[opcode]) { wl_abort("listener function for opcode %u of %s is NULL\n", opcode, target->interface->name); diff --git a/src/wayland-client.c b/src/wayland-client.c index 25aa1cd4..2464e7b7 100644 --- a/src/wayland-client.c +++ b/src/wayland-client.c @@ -1564,6 +1564,28 @@ queue_event(struct wl_display *display, int len) id = p[0]; opcode = p[1] & 0xffff; size = p[1] >> 16; + + /* + * If the message is larger than the maximum size of the + * connection buffer, the connection buffer will fill to + * its max size and stay there, with no message ever + * successfully being processed. If the user of + * libwayland-client uses a level-triggered event loop, + * this will cause the client to enter a loop that + * consumes CPU. To avoid this, immediately drop the + * connection. Since the maximum size of a message should + * not depend on the max buffer size chosen by the client, + * always compare the message size against the + * limit enforced by libwayland 1.22 and below (4096), + * rather than the actual value the client chose. + */ + if (size > WL_MAX_MESSAGE_SIZE) { + wl_log("Message length %u exceeds limit %d\n", + size, WL_MAX_MESSAGE_SIZE); + errno = E2BIG; + return -1; + } + if (len < size) return 0; @@ -1579,7 +1601,7 @@ queue_event(struct wl_display *display, int len) clock_gettime(CLOCK_REALTIME, &tp); time = (tp.tv_sec * 1000000L) + (tp.tv_nsec / 1000); - fprintf(stderr, "[%7u.%03u] discarded [%s]#%d.[event %d]" + fprintf(stderr, "[%7u.%03u] discarded [%s]#%u.[event %d]" "(%d fd, %d byte)\n", time / 1000, time % 1000, zombie ? "zombie" : "unknown", diff --git a/src/wayland-private.h b/src/wayland-private.h index fe9120af..b84010d0 100644 --- a/src/wayland-private.h +++ b/src/wayland-private.h @@ -49,6 +49,9 @@ #define WL_CLOSURE_MAX_ARGS 20 #define WL_BUFFER_DEFAULT_SIZE_POT 12 #define WL_BUFFER_DEFAULT_MAX_SIZE (1 << WL_BUFFER_DEFAULT_SIZE_POT) +#if WL_BUFFER_DEFAULT_MAX_SIZE < WL_MAX_MESSAGE_SIZE +# error default buffer cannot hold maximum-sized message +#endif /** * Argument types used in signatures. diff --git a/src/wayland-server.c b/src/wayland-server.c index a538519e..72a12018 100644 --- a/src/wayland-server.c +++ b/src/wayland-server.c @@ -397,6 +397,29 @@ wl_client_connection_data(int fd, uint32_t mask, void *data) wl_connection_copy(connection, p, sizeof p); opcode = p[1] & 0xffff; size = p[1] >> 16; + + /* + * If the message is larger than the maximum size of the + * connection buffer, the connection buffer will fill to + * its max size and stay there, with no message ever + * successfully being processed. Since libwayland-server + * uses level-triggered epoll, it will cause the server to + * enter a loop that consumes CPU. To avoid this, + * immediately disconnect the client with a protocol + * error. Since the maximum size of a message should not + * depend on the buffer size chosen by the compositor, + * always compare the message size against the + * limit enforced by libwayland 1.22 and below (4096), + * rather than the actual value the compositor chose. + */ + if (size > WL_MAX_MESSAGE_SIZE) { + wl_resource_post_error(client->display_resource, + WL_DISPLAY_ERROR_INVALID_METHOD, + "message length %u exceeds %d", + size, WL_MAX_MESSAGE_SIZE); + break; + } + if (len < size) break; diff --git a/src/wayland-util.h b/src/wayland-util.h index 4540f040..98c72fde 100644 --- a/src/wayland-util.h +++ b/src/wayland-util.h @@ -90,6 +90,14 @@ extern "C" { */ struct wl_object; +/** + * The maximum size of a protocol message. + * + * If a message size exceeds this value, the connection will be dropped. + * Servers will send an invalid_method error before disconnecting. + */ +#define WL_MAX_MESSAGE_SIZE 4096 + /** * Protocol message signature * diff --git a/tests/meson.build b/tests/meson.build index 2c22b82a..6ada5202 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -54,7 +54,7 @@ tests_protocol_c = custom_target( output: 'tests-protocol.c' ) -executable( +exec_fd_leak_checker = executable( 'exec-fd-leak-checker', 'exec-fd-leak-checker.c', dependencies: test_runner_dep @@ -96,78 +96,116 @@ if get_option('scanner') endif tests = { - 'array-test': [], - 'client-test': [ wayland_server_protocol_h ], - 'display-test': [ - wayland_client_protocol_h, - wayland_server_protocol_h, - tests_server_protocol_h, - tests_client_protocol_c, - tests_protocol_c, - ], - 'connection-test': [ - wayland_client_protocol_h, - wayland_server_protocol_h, - ], - 'event-loop-test': [ wayland_server_protocol_h ], - 'fixed-test': [], - 'interface-test': [ wayland_client_protocol_h ], - 'list-test': [], - 'map-test': [], - 'sanity-test' : [ - wayland_client_protocol_h, - wayland_server_protocol_h, - ], - 'socket-test': [ - wayland_client_protocol_h, - wayland_server_protocol_h, - ], - 'queue-test': [ - wayland_client_protocol_h, - wayland_server_protocol_h, - ], - 'signal-test': [ wayland_server_protocol_h ], - 'newsignal-test': [ - # wayland-server.c is needed here to access wl_priv_* functions - files('../src/wayland-server.c'), - wayland_server_protocol_h, - ], - 'resources-test': [ wayland_server_protocol_h ], - 'message-test': [ - wayland_client_protocol_h, - wayland_server_protocol_h, - ], - 'compositor-introspection-test': [ - wayland_client_protocol_h, - wayland_server_protocol_h, - ], - 'protocol-logger-test': [ - wayland_client_protocol_h, - wayland_server_protocol_h, - ], - 'headers-test': [ - wayland_client_protocol_h, - wayland_server_protocol_h, - 'headers-protocol-test.c', - wayland_client_protocol_core_h, - wayland_server_protocol_core_h, - 'headers-protocol-core-test.c', - ], - 'os-wrappers-test': [], - 'proxy-test': [ - wayland_client_protocol_h, - wayland_server_protocol_h, - ], - 'enum-validator-test': [], + 'array-test': {}, + 'client-test': { + 'extra_sources': [ wayland_server_protocol_h ], + }, + 'display-test': { + 'extra_sources': [ + wayland_client_protocol_h, + wayland_server_protocol_h, + tests_server_protocol_h, + tests_client_protocol_c, + tests_protocol_c, + ], + }, + 'connection-test': { + 'extra_sources': [ + wayland_client_protocol_h, + wayland_server_protocol_h, + ], + }, + 'event-loop-test': { + 'extra_sources': [ wayland_server_protocol_h ], + }, + 'fixed-test': {}, + 'interface-test': { + 'extra_sources': [ wayland_client_protocol_h ], + }, + 'list-test': {}, + 'map-test': {}, + 'sanity-test' : { + 'extra_sources': [ + wayland_client_protocol_h, + wayland_server_protocol_h, + ], + 'runtime_deps': [ exec_fd_leak_checker ], + }, + 'socket-test': { + 'extra_sources': [ + wayland_client_protocol_h, + wayland_server_protocol_h, + ], + }, + 'queue-test': { + 'extra_sources': [ + wayland_client_protocol_h, + wayland_server_protocol_h, + ], + }, + 'signal-test': { + 'extra_sources': [ wayland_server_protocol_h ], + }, + 'newsignal-test': { + 'extra_sources': [ + # wayland-server.c is needed here to access wl_priv_* functions + files('../src/wayland-server.c'), + wayland_server_protocol_h, + ], + }, + 'resources-test': { + 'extra_sources': [ wayland_server_protocol_h ], + }, + 'message-test': { + 'extra_sources': [ + wayland_client_protocol_h, + wayland_server_protocol_h, + ], + }, + 'compositor-introspection-test': { + 'extra_sources': [ + wayland_client_protocol_h, + wayland_server_protocol_h, + ], + }, + 'protocol-logger-test': { + 'extra_sources': [ + wayland_client_protocol_h, + wayland_server_protocol_h, + ], + }, + 'headers-test': { + 'extra_sources': [ + wayland_client_protocol_h, + wayland_server_protocol_h, + 'headers-protocol-test.c', + wayland_client_protocol_core_h, + wayland_server_protocol_core_h, + 'headers-protocol-core-test.c', + ], + }, + 'os-wrappers-test': { + 'runtime_deps': [ exec_fd_leak_checker ], + }, + 'proxy-test': { + 'extra_sources': [ + wayland_client_protocol_h, + wayland_server_protocol_h, + ], + }, + 'enum-validator-test': {}, } -foreach test_name, test_extra_sources: tests +foreach test_name, test_extras : tests + test_extra_sources = test_extras.get('extra_sources', []) + test_runtime_deps = test_extras.get('runtime_deps', []) test_sources = [ test_name + '.c' ] + test_extra_sources test_deps = [test_runner_dep, epoll_dep] bin = executable(test_name, test_sources, dependencies: test_deps) test( test_name, bin, + depends: test_runtime_deps, env: [ 'TEST_SRC_DIR=@0@'.format(meson.current_source_dir()), 'TEST_BUILD_DIR=@0@'.format(meson.current_build_dir()),