[PATCH] wayland-server: Version check requests

alexl at redhat.com alexl at redhat.com
Thu May 23 13:20:42 PDT 2013


From: Alexander Larsson <alexl at redhat.com>

If an interface has any messages and its version is larger than 1
then we emit a method counts array which lists the number of methods
for each version of the interface. This can be used in addition
to the normal method_count to reject requests that the
server doesn't support. This allows the wayland server library
to be upgraded and still safely run oler compositors that don't
implement the new requests.

Since there is no other space in the wm_interface we add the
method count array at the end of the events array. We then
add some warnings to the event sending code so that we never
accidentally trigger these events.

Then we add code in the server to reject messages that are not
supported by the server version of the object they are sent to.
---
 src/scanner.c        | 54 ++++++++++++++++++++++++++++++++++++++++++++++------
 src/wayland-server.c | 34 +++++++++++++++++++++++++++++++--
 2 files changed, 80 insertions(+), 8 deletions(-)

diff --git a/src/scanner.c b/src/scanner.c
index 9c14ad3..38e7909 100644
--- a/src/scanner.c
+++ b/src/scanner.c
@@ -1019,12 +1019,13 @@ emit_types(struct protocol *protocol, struct wl_list *message_list)
 
 static void
 emit_messages(struct wl_list *message_list,
-	      struct interface *interface, const char *suffix)
+	      struct interface *interface, const char *suffix,
+	      int emit_method_counts)
 {
 	struct message *m;
 	struct arg *a;
 
-	if (wl_list_empty(message_list))
+	if (wl_list_empty(message_list) && !emit_method_counts)
 		return;
 
 	printf("static const struct wl_message "
@@ -1070,13 +1071,52 @@ emit_messages(struct wl_list *message_list,
 		printf("\", types + %d },\n", m->type_index);
 	}
 
+	if (emit_method_counts) {
+		printf("\t{ NULL, \"\", (void *)%s_method_counts },\n", interface->name);
+	}
+
+	printf("};\n\n");
+}
+
+static int
+emit_method_counts(struct wl_list *message_list,
+		   struct interface *interface)
+{
+	struct message *m;
+	int version = 1;
+	int count;
+
+	if (wl_list_empty(message_list) || interface->version == 1)
+		return 0;
+
+	printf("static const uint32_t "
+	       "%s_method_counts[] = { ",
+	       interface->name);
+
+	count = 0;
+	wl_list_for_each(m, message_list, link) {
+		while (m->since != version) {
+			printf("%d,", count);
+			version++;
+		}
+		count++;
+	}
+
+	while (version <= interface->version) {
+		printf("%d, ", count);
+		version++;
+	}
+
 	printf("};\n\n");
+
+	return 1;
 }
 
 static void
 emit_code(struct protocol *protocol)
 {
 	struct interface *i;
+	int has_method_counts;
 
 	if (protocol->copyright)
 		format_copyright(protocol->copyright);
@@ -1101,8 +1141,10 @@ emit_code(struct protocol *protocol)
 
 	wl_list_for_each(i, &protocol->interface_list, link) {
 
-		emit_messages(&i->request_list, i, "requests");
-		emit_messages(&i->event_list, i, "events");
+
+		emit_messages(&i->request_list, i, "requests", 0);
+		has_method_counts = emit_method_counts(&i->request_list, i);
+		emit_messages(&i->event_list, i, "events", has_method_counts);
 
 		printf("WL_EXPORT const struct wl_interface "
 		       "%s_interface = {\n"
@@ -1115,9 +1157,9 @@ emit_code(struct protocol *protocol)
 		else
 			printf("\t0, NULL,\n");
 
-		if (!wl_list_empty(&i->event_list))
+		if (!wl_list_empty(&i->event_list) || has_method_counts)
 			printf("\t%d, %s_events,\n",
-			       wl_list_length(&i->event_list), i->name);
+			       wl_list_length(&i->event_list) + has_method_counts, i->name);
 		else
 			printf("\t0, NULL,\n");
 
diff --git a/src/wayland-server.c b/src/wayland-server.c
index c808f6a..e94366b 100644
--- a/src/wayland-server.c
+++ b/src/wayland-server.c
@@ -124,6 +124,12 @@ wl_resource_post_event(struct wl_resource *resource, uint32_t opcode, ...)
 	struct wl_object *object = &resource->object;
 	va_list ap;
 
+	if (opcode >= (uint32_t)object->interface->event_count ||
+	    object->interface->events[opcode].name == NULL) {
+		wl_log ("Trying to post unsupported event\n");
+		return;
+	}
+
 	va_start(ap, opcode);
 	closure = wl_closure_vmarshal(object, opcode, ap,
 				      &object->interface->events[opcode]);
@@ -150,6 +156,12 @@ wl_resource_queue_event(struct wl_resource *resource, uint32_t opcode, ...)
 	struct wl_object *object = &resource->object;
 	va_list ap;
 
+	if (opcode >= (uint32_t)object->interface->event_count ||
+	    object->interface->events[opcode].name == NULL) {
+		wl_log ("Trying to queue unsupported event\n");
+		return;
+	}
+
 	va_start(ap, opcode);
 	closure = wl_closure_vmarshal(object, opcode, ap,
 				      &object->interface->events[opcode]);
@@ -206,7 +218,8 @@ wl_client_connection_data(int fd, uint32_t mask, void *data)
 	struct wl_closure *closure;
 	const struct wl_message *message;
 	uint32_t p[2];
-	int opcode, size;
+	uint32_t opcode;
+	int size;
 	int len;
 
 	if (mask & (WL_EVENT_ERROR | WL_EVENT_HANGUP)) {
@@ -250,7 +263,7 @@ wl_client_connection_data(int fd, uint32_t mask, void *data)
 		}
 
 		object = &resource->object;
-		if (opcode >= object->interface->method_count) {
+		if (opcode >= (uint32_t)object->interface->method_count) {
 			wl_resource_post_error(client->display_resource,
 					       WL_DISPLAY_ERROR_INVALID_METHOD,
 					       "invalid method %d, object %s@%u",
@@ -260,6 +273,23 @@ wl_client_connection_data(int fd, uint32_t mask, void *data)
 			break;
 		}
 
+		if (object->interface->event_count > 0 &&
+		    object->interface->events[object->interface->event_count-1].name == NULL) {
+			uint32_t *method_counts = (uint32_t *)
+				object->interface->events[object->interface->event_count-1].types;
+
+			if (opcode >= method_counts[wl_resource_get_version (resource)]) {
+				wl_resource_post_error(client->display_resource,
+						       WL_DISPLAY_ERROR_INVALID_METHOD,
+						       "invalid method %d, object %s@%u",
+						       opcode,
+						       object->interface->name,
+						       object->id);
+				break;
+			}
+		}
+
+
 		message = &object->interface->methods[opcode];
 		closure = wl_connection_demarshal(client->connection, size,
 						  &client->objects, message);
-- 
1.8.1.4



More information about the wayland-devel mailing list