From 8d54c4e22d3d090d22fcdae6f6d8057789ffa684 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Thu, 11 Sep 2025 14:04:20 +0200 Subject: [PATCH 1/3] Don't hard code header format Signed-off-by: Julian Orth --- src/connection.c | 54 +++++++++++++++++++++++++++++++------------ src/wayland-client.c | 11 ++++----- src/wayland-private.h | 12 ++++++++++ src/wayland-server.c | 17 +++++++------- 4 files changed, 65 insertions(+), 29 deletions(-) diff --git a/src/connection.c b/src/connection.c index 593f52f3..28d27538 100644 --- a/src/connection.c +++ b/src/connection.c @@ -619,6 +619,28 @@ wl_connection_get_fd(struct wl_connection *connection) return connection->fd; } +size_t +wl_connection_header_size(struct wl_connection *connection) +{ + // Adjust WL_HEADER_MAX_SIZE when increasing this. + return 8; +} + +void +wl_connection_parse_header(struct wl_connection *connection, + uint32_t *header, + uint32_t *sender_id, + int *size, + int *opcode) +{ + *sender_id = header[0]; + uint32_t field2 = header[1]; + uint32_t field2_hi = field2 >> 16; + uint32_t field2_lo = field2 & 0xffff; + *size = (int)field2_hi; + *opcode = (int)field2_lo; +} + static int wl_connection_put_fd(struct wl_connection *connection, int32_t fd) { @@ -894,14 +916,14 @@ wl_connection_demarshal(struct wl_connection *connection, uint32_t *p, *next, *end, length, length_in_u32, id; int fd; char *s; - int i, count, num_arrays; + int i, count, num_arrays, ssize, opcode; const char *signature; struct argument_details arg; struct wl_closure *closure; struct wl_array *array_extra; /* Space for sender_id and opcode */ - if (size < 2 * sizeof *p) { + if (size < wl_connection_header_size(connection)) { wl_log("message too short, invalid header\n"); wl_connection_consume(connection, size); errno = EINVAL; @@ -921,8 +943,9 @@ wl_connection_demarshal(struct wl_connection *connection, end = p + size / sizeof *p; wl_connection_copy(connection, p, size); - closure->sender_id = *p++; - closure->opcode = *p++ & 0x0000ffff; + wl_connection_parse_header(connection, p, &closure->sender_id, &ssize, &opcode); + closure->opcode = (uint32_t)opcode; + p += wl_connection_header_size(connection) / sizeof *p; signature = message->signature; for (i = 0; i < count; i++) { @@ -1283,7 +1306,7 @@ copy_fds_to_connection(struct wl_closure *closure, static uint32_t -buffer_size_for_closure(struct wl_closure *closure) +buffer_size_for_closure(struct wl_connection *connection, struct wl_closure *closure) { const struct wl_message *message = closure->message; int i, count; @@ -1329,23 +1352,24 @@ buffer_size_for_closure(struct wl_closure *closure) } } - return buffer_size + 2; + return buffer_size + wl_connection_header_size(connection) / sizeof(uint32_t); } static int -serialize_closure(struct wl_closure *closure, uint32_t *buffer, - size_t buffer_count) +serialize_closure(struct wl_connection *connection, struct wl_closure *closure, + uint32_t *buffer, size_t buffer_count) { const struct wl_message *message = closure->message; unsigned int i, count, size; uint32_t *p, *end; struct argument_details arg; const char *signature; + uint32_t header_words = wl_connection_header_size(connection) / sizeof(*buffer); - if (buffer_count < 2) + if (buffer_count < header_words) goto overflow; - p = buffer + 2; + p = buffer + header_words; end = buffer + buffer_count; signature = message->signature; @@ -1414,7 +1438,7 @@ serialize_closure(struct wl_closure *closure, uint32_t *buffer, size = (p - buffer) * sizeof *p; buffer[0] = closure->sender_id; - buffer[1] = size << 16 | (closure->opcode & 0x0000ffff); + buffer[1] = size << 16 | (closure->opcode & 0x0000ffff); return size; @@ -1436,7 +1460,7 @@ wl_closure_send(struct wl_closure *closure, struct wl_connection *connection) if (copy_fds_to_connection(closure, connection)) return -1; - buffer_size = buffer_size_for_closure(closure); + buffer_size = buffer_size_for_closure(connection, closure); buffer = zalloc(buffer_size * sizeof buffer[0]); if (buffer == NULL) { wl_log("wl_closure_send error: buffer allocation failure of " @@ -1446,7 +1470,7 @@ wl_closure_send(struct wl_closure *closure, struct wl_connection *connection) return -1; } - size = serialize_closure(closure, buffer, buffer_size); + size = serialize_closure(connection, closure, buffer, buffer_size); if (size < 0) { free(buffer); return -1; @@ -1469,7 +1493,7 @@ wl_closure_queue(struct wl_closure *closure, struct wl_connection *connection) if (copy_fds_to_connection(closure, connection)) return -1; - buffer_size = buffer_size_for_closure(closure); + buffer_size = buffer_size_for_closure(connection, closure); buffer = malloc(buffer_size * sizeof buffer[0]); if (buffer == NULL) { wl_log("wl_closure_queue error: buffer allocation failure of " @@ -1479,7 +1503,7 @@ wl_closure_queue(struct wl_closure *closure, struct wl_connection *connection) return -1; } - size = serialize_closure(closure, buffer, buffer_size); + size = serialize_closure(connection, closure, buffer, buffer_size); if (size < 0) { free(buffer); return -1; diff --git a/src/wayland-client.c b/src/wayland-client.c index c8633046..aa04ed70 100644 --- a/src/wayland-client.c +++ b/src/wayland-client.c @@ -1564,7 +1564,7 @@ increase_closure_args_refcount(struct wl_closure *closure) static int queue_event(struct wl_display *display, int len) { - uint32_t p[2], id; + uint32_t p[WL_MAX_HEADER_U32], id; int opcode, size; struct wl_proxy *proxy; struct wl_closure *closure; @@ -1574,10 +1574,8 @@ queue_event(struct wl_display *display, int len) unsigned int time; int num_zombie_fds; - wl_connection_copy(display->connection, p, sizeof p); - id = p[0]; - opcode = p[1] & 0xffff; - size = p[1] >> 16; + wl_connection_copy(display->connection, p, wl_connection_header_size(display->connection)); + wl_connection_parse_header(display->connection, p, &id, &size, &opcode); /* * If the message is larger than the maximum size of the @@ -1736,6 +1734,7 @@ read_events(struct wl_display *display) { int total, rem, size; uint32_t serial; + int header_size = wl_connection_header_size(display->connection); display->reader_count--; if (display->reader_count == 0) { @@ -1760,7 +1759,7 @@ read_events(struct wl_display *display) return -1; } - for (rem = total; rem >= 8; rem -= size) { + for (rem = total; rem >= header_size; rem -= size) { size = queue_event(display, rem); if (size == -1) { display_fatal_error(display, errno); diff --git a/src/wayland-private.h b/src/wayland-private.h index d7ba9dae..b2e14a83 100644 --- a/src/wayland-private.h +++ b/src/wayland-private.h @@ -49,6 +49,8 @@ #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) +#define WL_MAX_HEADER_SIZE 8 +#define WL_MAX_HEADER_U32 (WL_MAX_HEADER_SIZE / 4) #if WL_BUFFER_DEFAULT_MAX_SIZE < WL_MAX_MESSAGE_SIZE # error default buffer cannot hold maximum-sized message #endif @@ -164,6 +166,16 @@ wl_connection_queue(struct wl_connection *connection, int wl_connection_get_fd(struct wl_connection *connection); +size_t +wl_connection_header_size(struct wl_connection *connection); + +void +wl_connection_parse_header(struct wl_connection *connection, + uint32_t *header, + uint32_t *sender_id, + int *size, + int *opcode); + struct wl_closure { int count; const struct wl_message *message; diff --git a/src/wayland-server.c b/src/wayland-server.c index 482743b3..cd954f33 100644 --- a/src/wayland-server.c +++ b/src/wayland-server.c @@ -357,10 +357,12 @@ wl_client_connection_data(int fd, uint32_t mask, void *data) struct wl_object *object; struct wl_closure *closure; const struct wl_message *message; - uint32_t p[2]; + uint32_t p[WL_MAX_HEADER_U32]; uint32_t resource_flags; + uint32_t object_id; int opcode, size, since; int len; + size_t header_size = wl_connection_header_size(connection); if (mask & WL_EVENT_HANGUP) { wl_client_destroy(client); @@ -394,10 +396,9 @@ wl_client_connection_data(int fd, uint32_t mask, void *data) } } - while (len >= 0 && (size_t) len >= sizeof p) { - wl_connection_copy(connection, p, sizeof p); - opcode = p[1] & 0xffff; - size = p[1] >> 16; + while (len >= 0 && (size_t) len >= header_size) { + wl_connection_copy(connection, p, header_size); + wl_connection_parse_header(connection, p, &object_id, &size, &opcode); /* * If the message is larger than the maximum size of the @@ -424,12 +425,12 @@ wl_client_connection_data(int fd, uint32_t mask, void *data) if (len < size) break; - resource = wl_map_lookup(&client->objects, p[0]); - resource_flags = wl_map_lookup_flags(&client->objects, p[0]); + resource = wl_map_lookup(&client->objects, object_id); + resource_flags = wl_map_lookup_flags(&client->objects, object_id); if (resource == NULL) { wl_resource_post_error(client->display_resource, WL_DISPLAY_ERROR_INVALID_OBJECT, - "invalid object %u", p[0]); + "invalid object %u", object_id); break; } From 5f4add98a2d5455c1ca0f210b18b53c176717d36 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Thu, 11 Sep 2025 14:08:26 +0200 Subject: [PATCH 2/3] Add new header format version Signed-off-by: Julian Orth --- protocol/wayland.xml | 14 +++++ src/connection.c | 29 +++++++++-- src/wayland-client.c | 116 +++++++++++++++++++++++++++++++++++++++++- src/wayland-private.h | 7 ++- src/wayland-server.c | 4 +- 5 files changed, 162 insertions(+), 8 deletions(-) diff --git a/protocol/wayland.xml b/protocol/wayland.xml index bee74a10..aa0aba1f 100644 --- a/protocol/wayland.xml +++ b/protocol/wayland.xml @@ -3296,4 +3296,18 @@ + + + + + + + + + + + + + + diff --git a/src/connection.c b/src/connection.c index 28d27538..8b7b9a11 100644 --- a/src/connection.c +++ b/src/connection.c @@ -69,6 +69,7 @@ struct wl_connection { struct wl_ring_buffer fds_in, fds_out; int fd; int want_flush; + bool v2; }; static inline size_t @@ -619,6 +620,12 @@ wl_connection_get_fd(struct wl_connection *connection) return connection->fd; } +void +wl_connection_enable_v2(struct wl_connection *connection) +{ + connection->v2 = true; +} + size_t wl_connection_header_size(struct wl_connection *connection) { @@ -631,14 +638,21 @@ wl_connection_parse_header(struct wl_connection *connection, uint32_t *header, uint32_t *sender_id, int *size, - int *opcode) + int *opcode, + int *num_fds) { *sender_id = header[0]; uint32_t field2 = header[1]; uint32_t field2_hi = field2 >> 16; uint32_t field2_lo = field2 & 0xffff; *size = (int)field2_hi; - *opcode = (int)field2_lo; + if (connection->v2) { + *opcode = (int)(field2_lo >> 8); + *num_fds = (int)(field2_lo & 0xff); + } else { + *opcode = (int)field2_lo; + *num_fds = -1; + } } static int @@ -807,6 +821,7 @@ wl_closure_init(const struct wl_message *message, uint32_t size, closure->message = message; closure->count = count; + closure->num_fds = -1; /* Set these all to -1 so we can close any that have been * set to a real value during wl_closure_destroy(). @@ -826,7 +841,7 @@ wl_closure_marshal(struct wl_object *sender, uint32_t opcode, { struct wl_closure *closure; struct wl_object *object; - int i, count, fd, dup_fd; + int i, count, fd, dup_fd, num_fds = 0; const char *signature; struct argument_details arg; @@ -874,6 +889,7 @@ wl_closure_marshal(struct wl_object *sender, uint32_t opcode, return NULL; } closure->args[i].h = dup_fd; + num_fds += 1; break; default: wl_abort("unhandled format code: '%c'\n", arg.type); @@ -883,6 +899,7 @@ wl_closure_marshal(struct wl_object *sender, uint32_t opcode, closure->sender_id = sender->id; closure->opcode = opcode; + closure->num_fds = num_fds; return closure; @@ -943,7 +960,7 @@ wl_connection_demarshal(struct wl_connection *connection, end = p + size / sizeof *p; wl_connection_copy(connection, p, size); - wl_connection_parse_header(connection, p, &closure->sender_id, &ssize, &opcode); + wl_connection_parse_header(connection, p, &closure->sender_id, &ssize, &opcode, &closure->num_fds); closure->opcode = (uint32_t)opcode; p += wl_connection_header_size(connection) / sizeof *p; @@ -1438,7 +1455,11 @@ serialize_closure(struct wl_connection *connection, struct wl_closure *closure, size = (p - buffer) * sizeof *p; buffer[0] = closure->sender_id; + if (connection->v2) { + buffer[1] = size << 16 | ((closure->opcode << 8) & 0x0000ff00) | (closure->num_fds & 0x000000ff); + } else { buffer[1] = size << 16 | (closure->opcode & 0x0000ffff); + } return size; diff --git a/src/wayland-client.c b/src/wayland-client.c index aa04ed70..e7f1aebb 100644 --- a/src/wayland-client.c +++ b/src/wayland-client.c @@ -1214,6 +1214,118 @@ connect_to_socket(const char *name) return fd; } +struct upgrade_data { + struct wl_fixes **fixes; + struct wl_upgrade **upgrade; + bool *upgraded; +}; + +static void upgrade_global(void *data, + struct wl_registry *wl_registry, + uint32_t name, + const char *interface, + uint32_t version) +{ + struct upgrade_data *upgrade = data; + if (strcmp(interface, "wl_fixes") == 0) { + *upgrade->fixes = wl_registry_bind(wl_registry, name, &wl_fixes_interface, 1); + } else if (strcmp(interface, "wl_upgrade") == 0) { + *upgrade->upgrade = wl_registry_bind(wl_registry, name, &wl_upgrade_interface, 1); + } +} + +static void upgrade_global_removed(void *data, + struct wl_registry *wl_registry, + uint32_t name) +{ + // nothing +} + +static const struct wl_registry_listener upgrade_registry_listener = { + .global = upgrade_global, + .global_remove = upgrade_global_removed, +}; + +static void upgrade_upgraded(void *data, + struct wl_upgrade *wl_upgrade) +{ + struct upgrade_data *upgrade = data; + *upgrade->upgraded = true; +} + +static const struct wl_upgrade_listener upgrade_upgrade_listener = { + .upgraded = upgrade_upgraded, +}; + +static void +try_upgrade(struct wl_display *display) +{ + struct wl_event_queue *queue = NULL; + struct wl_display *display_wrapper = NULL; + struct wl_registry *registry = NULL; + struct wl_fixes *fixes = NULL; + struct wl_upgrade *upgrade = NULL; + bool upgraded = false; + struct upgrade_data data = { + .fixes = &fixes, + .upgrade = &upgrade, + .upgraded = &upgraded, + }; + + queue = wl_display_create_queue_with_name(display, "Upgrade Queue"); + if (!queue) { + goto out; + } + display_wrapper = wl_proxy_create_wrapper(display); + if (!display_wrapper) { + goto out; + } + wl_proxy_set_queue((struct wl_proxy *)display_wrapper, queue); + registry = wl_display_get_registry(display_wrapper); + if (!registry) { + goto out; + } + wl_registry_add_listener(registry, &upgrade_registry_listener, &data); + wl_display_roundtrip_queue(display, queue); + if (!upgrade) { + goto out; + } + assert(fixes); + wl_fixes_destroy_registry(fixes, registry); + wl_registry_destroy(registry); + registry = NULL; + wl_fixes_destroy(fixes); + fixes = NULL; + wl_upgrade_add_listener(upgrade, &upgrade_upgrade_listener, &data); + wl_upgrade_upgrade(upgrade); + while (!upgraded) { + if (wl_display_dispatch_queue(display, queue) == -1) { + goto out; + } + } + wl_connection_enable_v2(display->connection); + +out: + if (upgrade) { + wl_upgrade_destroy(upgrade); + } + if (registry) { + if (fixes) { + wl_fixes_destroy_registry(fixes, registry); + } + wl_registry_destroy(registry); + } + if (fixes) { + wl_fixes_destroy(fixes); + } + if (display_wrapper) { + wl_proxy_wrapper_destroy(display_wrapper); + } + if (queue) { + wl_event_queue_release(queue); + } +} + /** Connect to Wayland display on an already open fd * * \param fd The fd to use for the connection @@ -1302,6 +1414,8 @@ wl_display_connect_to_fd(int fd) if (display->connection == NULL) goto err_connection; + try_upgrade(display); + return display; err_connection: @@ -1575,7 +1689,7 @@ queue_event(struct wl_display *display, int len) int num_zombie_fds; wl_connection_copy(display->connection, p, wl_connection_header_size(display->connection)); - wl_connection_parse_header(display->connection, p, &id, &size, &opcode); + wl_connection_parse_header(display->connection, p, &id, &size, &opcode, &num_zombie_fds); /* * If the message is larger than the maximum size of the diff --git a/src/wayland-private.h b/src/wayland-private.h index b2e14a83..7fd62657 100644 --- a/src/wayland-private.h +++ b/src/wayland-private.h @@ -166,6 +166,9 @@ wl_connection_queue(struct wl_connection *connection, int wl_connection_get_fd(struct wl_connection *connection); +void +wl_connection_enable_v2(struct wl_connection *connection); + size_t wl_connection_header_size(struct wl_connection *connection); @@ -174,13 +177,15 @@ wl_connection_parse_header(struct wl_connection *connection, uint32_t *header, uint32_t *sender_id, int *size, - int *opcode); + int *opcode, + int *num_fds); struct wl_closure { int count; const struct wl_message *message; uint32_t opcode; uint32_t sender_id; + int32_t num_fds; union wl_argument args[WL_CLOSURE_MAX_ARGS]; struct wl_list link; struct wl_proxy *proxy; diff --git a/src/wayland-server.c b/src/wayland-server.c index cd954f33..a8931399 100644 --- a/src/wayland-server.c +++ b/src/wayland-server.c @@ -360,7 +360,7 @@ wl_client_connection_data(int fd, uint32_t mask, void *data) uint32_t p[WL_MAX_HEADER_U32]; uint32_t resource_flags; uint32_t object_id; - int opcode, size, since; + int opcode, size, since, num_fds; int len; size_t header_size = wl_connection_header_size(connection); @@ -398,7 +398,7 @@ wl_client_connection_data(int fd, uint32_t mask, void *data) while (len >= 0 && (size_t) len >= header_size) { wl_connection_copy(connection, p, header_size); - wl_connection_parse_header(connection, p, &object_id, &size, &opcode); + wl_connection_parse_header(connection, p, &object_id, &size, &opcode, &num_fds); /* * If the message is larger than the maximum size of the From 6d60b36f8a3c530deeccec90307f106cdac02d4b Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Thu, 11 Sep 2025 14:08:53 +0200 Subject: [PATCH 3/3] Log number of fds Signed-off-by: Julian Orth --- src/connection.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/connection.c b/src/connection.c index 8b7b9a11..014a0f61 100644 --- a/src/connection.c +++ b/src/connection.c @@ -1643,6 +1643,13 @@ wl_closure_print(struct wl_closure *closure, struct wl_object *target, } } + if (closure->num_fds > 0) { + if (closure->count) { + fprintf(f, ", "); + } + fprintf(f, "num_fds = %d", closure->num_fds); + } + fprintf(f, ")%s\n", color ? WL_DEBUG_COLOR_RESET : ""); if (fclose(f) == 0) {