static int copy_fds_to_connection(struct wl_closure *closure, struct wl_connection *connection) { const struct wl_message *message = closure->message; uint32_t i, count; struct argument_details arg; const char *signature = message->signature; int fd; count = arg_count_for_signature(signature); for (i = 0; i < count; i++) { signature = get_next_argument(signature, &arg); if (arg.type != 'h') continue; fd = closure->args[i].h; if (wl_connection_put_fd(connection, fd)) { wl_log("request could not be marshaled: " "can't send file descriptor"); return -1; } } return 0; }
static void decrease_closure_args_refcount(struct wl_closure *closure) { const char *signature; struct argument_details arg; int i, count; struct wl_proxy *proxy; signature = closure->message->signature; count = arg_count_for_signature(signature); for (i = 0; i < count; i++) { signature = get_next_argument(signature, &arg); switch (arg.type) { case 'n': case 'o': proxy = (struct wl_proxy *) closure->args[i].o; if (proxy) { if (proxy->flags & WL_PROXY_FLAG_DESTROYED) closure->args[i].o = NULL; proxy->refcount--; if (!proxy->refcount) free(proxy); } break; default: break; } } }
void wl_closure_invoke(struct wl_closure *closure, uint32_t flags, struct wl_object *target, uint32_t opcode, void *data) { int count; ffi_cif cif; ffi_type *ffi_types[WL_CLOSURE_MAX_ARGS + 2]; void * ffi_args[WL_CLOSURE_MAX_ARGS + 2]; void (* const *implementation)(void); count = arg_count_for_signature(closure->message->signature); ffi_types[0] = &ffi_type_pointer; ffi_args[0] = &data; ffi_types[1] = &ffi_type_pointer; ffi_args[1] = ⌖ convert_arguments_to_ffi(closure->message->signature, flags, closure->args, count, ffi_types + 2, ffi_args + 2); ffi_prep_cif(&cif, FFI_DEFAULT_ABI, count + 2, &ffi_type_void, ffi_types); implementation = target->implementation; if (!implementation[opcode]) { wl_abort("listener function for opcode %u of %s is NULL\n", opcode, target->interface->name); } ffi_call(&cif, implementation[opcode], NULL, ffi_args); }
static int create_proxies(struct wl_proxy *sender, struct wl_closure *closure) { struct wl_proxy *proxy; const char *signature; struct argument_details arg; uint32_t id; int i; int count; signature = closure->message->signature; count = arg_count_for_signature(signature); for (i = 0; i < count; i++) { signature = get_next_argument(signature, &arg); switch (arg.type) { case 'n': id = closure->args[i].n; if (id == 0) { closure->args[i].o = NULL; break; } proxy = wl_proxy_create_for_id(sender, id, closure->message->types[i]); if (proxy == NULL) return -1; closure->args[i].o = (struct wl_object *)proxy; break; default: break; } } return 0; }
static struct wl_proxy * create_outgoing_proxy(struct wl_proxy *proxy, const struct wl_message *message, union wl_argument *args, const struct wl_interface *interface) { int i, count; const char *signature; struct argument_details arg; struct wl_proxy *new_proxy = NULL; signature = message->signature; count = arg_count_for_signature(signature); for (i = 0; i < count; i++) { signature = get_next_argument(signature, &arg); switch (arg.type) { case 'n': new_proxy = proxy_create(proxy, interface); if (new_proxy == NULL) return NULL; args[i].o = &new_proxy->object; break; } } return new_proxy; }
static uint32_t buffer_size_for_closure(struct wl_closure *closure) { const struct wl_message *message = closure->message; int i, count; struct argument_details arg; const char *signature; uint32_t size, buffer_size = 0; signature = message->signature; count = arg_count_for_signature(signature); for (i = 0; i < count; i++) { signature = get_next_argument(signature, &arg); switch (arg.type) { case 'h': break; case 'u': case 'i': case 'f': case 'o': case 'n': buffer_size++; break; case 's': if (closure->args[i].s == NULL) { buffer_size++; break; } size = strlen(closure->args[i].s) + 1; buffer_size += 1 + DIV_ROUNDUP(size, sizeof(uint32_t)); break; case 'a': if (closure->args[i].a == NULL) { buffer_size++; break; } size = closure->args[i].a->size; buffer_size += (1 + DIV_ROUNDUP(size, sizeof(uint32_t))); break; default: break; } } return buffer_size + 2; }
int wl_closure_lookup_objects(struct wl_closure *closure, struct wl_map *objects) { struct wl_object *object; const struct wl_message *message; const char *signature; struct argument_details arg; int i, count; uint32_t id; message = closure->message; signature = message->signature; count = arg_count_for_signature(signature); for (i = 0; i < count; i++) { signature = get_next_argument(signature, &arg); switch (arg.type) { case 'o': id = closure->args[i].n; closure->args[i].o = NULL; object = wl_map_lookup(objects, id); if (object == WL_ZOMBIE_OBJECT) { /* references object we've already * destroyed client side */ object = NULL; } else if (object == NULL && id != 0) { wl_log("unknown object (%u), message %s(%s)\n", id, message->name, message->signature); object = NULL; errno = EINVAL; return -1; } if (object != NULL && message->types[i] != NULL && !wl_interface_equal((object)->interface, message->types[i])) { wl_log("invalid object (%u), type (%s), " "message %s(%s)\n", id, (object)->interface->name, message->name, message->signature); errno = EINVAL; return -1; } closure->args[i].o = object; } } return 0; }
static void increase_closure_args_refcount(struct wl_closure *closure) { const char *signature; struct argument_details arg; int i, count; struct wl_proxy *proxy; signature = closure->message->signature; count = arg_count_for_signature(signature); for (i = 0; i < count; i++) { signature = get_next_argument(signature, &arg); switch (arg.type) { case 'n': case 'o': proxy = (struct wl_proxy *) closure->args[i].o; if (proxy) proxy->refcount++; break; default: break; } } }
struct wl_closure * wl_connection_demarshal(struct wl_connection *connection, uint32_t size, struct wl_map *objects, const struct wl_message *message) { uint32_t *p, *next, *end, length, id; int fd; char *s; unsigned int i, count, num_arrays; const char *signature; struct argument_details arg; struct wl_closure *closure; struct wl_array *array, *array_extra; count = arg_count_for_signature(message->signature); if (count > WL_CLOSURE_MAX_ARGS) { wl_log("too many args (%d)\n", count); errno = EINVAL; wl_connection_consume(connection, size); return NULL; } num_arrays = wl_message_count_arrays(message); closure = malloc(sizeof *closure + size + num_arrays * sizeof *array); if (closure == NULL) { errno = ENOMEM; wl_connection_consume(connection, size); return NULL; } array_extra = closure->extra; p = (uint32_t *)(closure->extra + num_arrays); end = p + size / sizeof *p; wl_connection_copy(connection, p, size); closure->sender_id = *p++; closure->opcode = *p++ & 0x0000ffff; signature = message->signature; for (i = 0; i < count; i++) { signature = get_next_argument(signature, &arg); if (arg.type != 'h' && p + 1 > end) { wl_log("message too short, " "object (%d), message %s(%s)\n", *p, message->name, message->signature); errno = EINVAL; goto err; } switch (arg.type) { case 'u': closure->args[i].u = *p++; break; case 'i': closure->args[i].i = *p++; break; case 'f': closure->args[i].f = *p++; break; case 's': length = *p++; if (length == 0) { closure->args[i].s = NULL; break; } next = p + DIV_ROUNDUP(length, sizeof *p); if (next > end) { wl_log("message too short, " "object (%d), message %s(%s)\n", closure->sender_id, message->name, message->signature); errno = EINVAL; goto err; } s = (char *) p; if (length > 0 && s[length - 1] != '\0') { wl_log("string not nul-terminated, " "message %s(%s)\n", message->name, message->signature); errno = EINVAL; goto err; } closure->args[i].s = s; p = next; break; case 'o': id = *p++; closure->args[i].n = id; if (id == 0 && !arg.nullable) { wl_log("NULL object received on non-nullable " "type, message %s(%s)\n", message->name, message->signature); errno = EINVAL; goto err; } break; case 'n': id = *p++; closure->args[i].n = id; if (id == 0 && !arg.nullable) { wl_log("NULL new ID received on non-nullable " "type, message %s(%s)\n", message->name, message->signature); errno = EINVAL; goto err; } if (wl_map_reserve_new(objects, id) < 0) { wl_log("not a valid new object id (%u), " "message %s(%s)\n", id, message->name, message->signature); errno = EINVAL; goto err; } break; case 'a': length = *p++; next = p + DIV_ROUNDUP(length, sizeof *p); if (next > end) { wl_log("message too short, " "object (%d), message %s(%s)\n", closure->sender_id, message->name, message->signature); errno = EINVAL; goto err; } array_extra->size = length; array_extra->alloc = 0; array_extra->data = p; closure->args[i].a = array_extra++; p = next; break; case 'h': if (connection->fds_in.tail == connection->fds_in.head) { wl_log("file descriptor expected, " "object (%d), message %s(%s)\n", closure->sender_id, message->name, message->signature); errno = EINVAL; goto err; } wl_buffer_copy(&connection->fds_in, &fd, sizeof fd); connection->fds_in.tail += sizeof fd; closure->args[i].h = fd; break; default: wl_abort("unknown type\n"); break; } } closure->count = count; closure->message = message; wl_connection_consume(connection, size); return closure; err: wl_closure_destroy(closure); wl_connection_consume(connection, size); return NULL; }
struct wl_closure * wl_closure_marshal(struct wl_object *sender, uint32_t opcode, union wl_argument *args, const struct wl_message *message) { struct wl_closure *closure; struct wl_object *object; int i, count, fd, dup_fd; const char *signature; struct argument_details arg; count = arg_count_for_signature(message->signature); if (count > WL_CLOSURE_MAX_ARGS) { wl_log("too many args (%d)\n", count); errno = EINVAL; return NULL; } closure = malloc(sizeof *closure); if (closure == NULL) { errno = ENOMEM; return NULL; } memcpy(closure->args, args, count * sizeof *args); signature = message->signature; for (i = 0; i < count; i++) { signature = get_next_argument(signature, &arg); switch (arg.type) { case 'f': case 'u': case 'i': break; case 's': if (!arg.nullable && args[i].s == NULL) goto err_null; break; case 'o': if (!arg.nullable && args[i].o == NULL) goto err_null; break; case 'n': object = args[i].o; if (!arg.nullable && object == NULL) goto err_null; closure->args[i].n = object ? object->id : 0; break; case 'a': if (!arg.nullable && args[i].a == NULL) goto err_null; break; case 'h': fd = args[i].h; dup_fd = wl_os_dupfd_cloexec(fd, 0); if (dup_fd < 0) wl_abort("dup failed: %s\n", strerror(errno)); closure->args[i].h = dup_fd; break; default: wl_abort("unhandled format code: '%c'\n", arg.type); break; } } closure->sender_id = sender->id; closure->opcode = opcode; closure->message = message; closure->count = count; return closure; err_null: wl_closure_destroy(closure); wl_log("error marshalling arguments for %s (signature %s): " "null value passed for arg %i\n", message->name, message->signature, i); errno = EINVAL; return NULL; }
static int serialize_closure(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; if (buffer_count < 2) goto overflow; p = buffer + 2; end = buffer + buffer_count; signature = message->signature; count = arg_count_for_signature(signature); for (i = 0; i < count; i++) { signature = get_next_argument(signature, &arg); if (arg.type == 'h') continue; if (p + 1 > end) goto overflow; switch (arg.type) { case 'u': *p++ = closure->args[i].u; break; case 'i': *p++ = closure->args[i].i; break; case 'f': *p++ = closure->args[i].f; break; case 'o': *p++ = closure->args[i].o ? closure->args[i].o->id : 0; break; case 'n': *p++ = closure->args[i].n; break; case 's': if (closure->args[i].s == NULL) { *p++ = 0; break; } size = strlen(closure->args[i].s) + 1; *p++ = size; if (p + DIV_ROUNDUP(size, sizeof *p) > end) goto overflow; memcpy(p, closure->args[i].s, size); p += DIV_ROUNDUP(size, sizeof *p); break; case 'a': if (closure->args[i].a == NULL) { *p++ = 0; break; } size = closure->args[i].a->size; *p++ = size; if (p + DIV_ROUNDUP(size, sizeof *p) > end) goto overflow; memcpy(p, closure->args[i].a->data, size); p += DIV_ROUNDUP(size, sizeof *p); break; default: break; } } size = (p - buffer) * sizeof *p; buffer[0] = closure->sender_id; buffer[1] = size << 16 | (closure->opcode & 0x0000ffff); return size; overflow: errno = ERANGE; return -1; }
struct wl_closure * wl_connection_demarshal(struct wl_connection *connection, uint32_t size, struct wl_map *objects, const struct wl_message *message) { uint32_t *p, *next, *end, length, **id; int *fd; char *extra, **s; unsigned int i, count, extra_space; const char *signature = message->signature; struct argument_details arg; struct wl_object **object; struct wl_array **array; struct wl_closure *closure; count = arg_count_for_signature(signature) + 2; if (count > ARRAY_LENGTH(closure->types)) { printf("too many args (%d)\n", count); errno = EINVAL; wl_connection_consume(connection, size); return NULL; } extra_space = wl_message_size_extra(message); closure = malloc(sizeof *closure + 8 + size + extra_space); if (closure == NULL) return NULL; closure->message = message; closure->types[0] = &ffi_type_pointer; closure->types[1] = &ffi_type_pointer; closure->start = closure->buffer; wl_connection_copy(connection, closure->buffer, size); p = &closure->buffer[2]; end = (uint32_t *) ((char *) p + size); extra = (char *) end; for (i = 2; i < count; i++) { signature = get_next_argument(signature, &arg); if (p + 1 > end) { printf("message too short, " "object (%d), message %s(%s)\n", *p, message->name, message->signature); errno = EINVAL; goto err; } switch (arg.type) { case 'u': closure->types[i] = &ffi_type_uint32; closure->args[i] = p++; break; case 'i': closure->types[i] = &ffi_type_sint32; closure->args[i] = p++; break; case 'f': closure->types[i] = &ffi_type_sint32; closure->args[i] = p++; break; case 's': closure->types[i] = &ffi_type_pointer; length = *p++; next = p + DIV_ROUNDUP(length, sizeof *p); if (next > end) { printf("message too short, " "object (%d), message %s(%s)\n", *p, message->name, message->signature); errno = EINVAL; goto err; } s = (char **) extra; extra += sizeof *s; closure->args[i] = s; if (length == 0) { *s = NULL; } else { *s = (char *) p; } if (length > 0 && (*s)[length - 1] != '\0') { printf("string not nul-terminated, " "message %s(%s)\n", message->name, message->signature); errno = EINVAL; goto err; } p = next; break; case 'o': closure->types[i] = &ffi_type_pointer; object = (struct wl_object **) extra; extra += sizeof *object; closure->args[i] = object; if (*p == 0 && !arg.nullable) { printf("NULL new ID received on non-nullable " "type, message %s(%s)\n", message->name, message->signature); *object = NULL; errno = EINVAL; goto err; } *object = wl_map_lookup(objects, *p); if (*object == WL_ZOMBIE_OBJECT) { /* references object we've already * destroyed client side */ *object = NULL; } else if (*object == NULL && *p != 0) { printf("unknown object (%u), message %s(%s)\n", *p, message->name, message->signature); *object = NULL; errno = EINVAL; goto err; } if (*object != NULL && message->types[i-2] != NULL && (*object)->interface != message->types[i-2]) { printf("invalid object (%u), type (%s), " "message %s(%s)\n", *p, (*object)->interface->name, message->name, message->signature); errno = EINVAL; goto err; } p++; break; case 'n': closure->types[i] = &ffi_type_pointer; id = (uint32_t **) extra; extra += sizeof *id; closure->args[i] = id; *id = p; if (*id == 0 && !arg.nullable) { printf("NULL new ID received on non-nullable " "type, message %s(%s)\n", message->name, message->signature); errno = EINVAL; goto err; } if (wl_map_reserve_new(objects, *p) < 0) { printf("not a valid new object id (%d), " "message %s(%s)\n", *p, message->name, message->signature); errno = EINVAL; goto err; } p++; break; case 'a': closure->types[i] = &ffi_type_pointer; length = *p++; next = p + DIV_ROUNDUP(length, sizeof *p); if (next > end) { printf("message too short, " "object (%d), message %s(%s)\n", *p, message->name, message->signature); errno = EINVAL; goto err; } array = (struct wl_array **) extra; extra += sizeof *array; closure->args[i] = array; *array = (struct wl_array *) extra; extra += sizeof **array; (*array)->size = length; (*array)->alloc = 0; (*array)->data = p; p = next; break; case 'h': closure->types[i] = &ffi_type_sint; fd = (int *) extra; extra += sizeof *fd; closure->args[i] = fd; wl_buffer_copy(&connection->fds_in, fd, sizeof *fd); connection->fds_in.tail += sizeof *fd; break; default: printf("unknown type\n"); assert(0); break; } } closure->count = i; ffi_prep_cif(&closure->cif, FFI_DEFAULT_ABI, closure->count, &ffi_type_void, closure->types); wl_connection_consume(connection, size); return closure; err: closure->count = i; wl_closure_destroy(closure); wl_connection_consume(connection, size); return NULL; }
struct wl_closure * wl_closure_vmarshal(struct wl_object *sender, uint32_t opcode, va_list ap, const struct wl_message *message) { struct wl_closure *closure; struct wl_object **objectp, *object; uint32_t length, aligned, *p, *start, size, *end; int dup_fd; struct wl_array **arrayp, *array; const char **sp, *s; const char *signature = message->signature; struct argument_details arg; char *extra; int i, count, fd, extra_size, *fd_ptr; /* FIXME: Match old fixed allocation for now */ closure = malloc(sizeof *closure + 1024); if (closure == NULL) return NULL; extra_size = wl_message_size_extra(message); count = arg_count_for_signature(signature) + 2; extra = (char *) closure->buffer; start = &closure->buffer[DIV_ROUNDUP(extra_size, sizeof *p)]; end = &closure->buffer[256]; p = &start[2]; closure->types[0] = &ffi_type_pointer; closure->types[1] = &ffi_type_pointer; for (i = 2; i < count; i++) { signature = get_next_argument(signature, &arg); switch (arg.type) { case 'f': closure->types[i] = &ffi_type_sint32; closure->args[i] = p; if (end - p < 1) goto err; *p++ = va_arg(ap, wl_fixed_t); break; case 'u': closure->types[i] = &ffi_type_uint32; closure->args[i] = p; if (end - p < 1) goto err; *p++ = va_arg(ap, uint32_t); break; case 'i': closure->types[i] = &ffi_type_sint32; closure->args[i] = p; if (end - p < 1) goto err; *p++ = va_arg(ap, int32_t); break; case 's': closure->types[i] = &ffi_type_pointer; closure->args[i] = extra; sp = (const char **) extra; extra += sizeof *sp; s = va_arg(ap, const char *); if (!arg.nullable && s == NULL) goto err_null; length = s ? strlen(s) + 1: 0; aligned = (length + 3) & ~3; if (p + aligned / sizeof *p + 1 > end) goto err; *p++ = length; if (length > 0) *sp = (const char *) p; else *sp = NULL; memcpy(p, s, length); memset((char *) p + length, 0, aligned - length); p += aligned / sizeof *p; break; case 'o': closure->types[i] = &ffi_type_pointer; closure->args[i] = extra; objectp = (struct wl_object **) extra; extra += sizeof *objectp; object = va_arg(ap, struct wl_object *); if (!arg.nullable && object == NULL) goto err_null; *objectp = object; if (end - p < 1) goto err; *p++ = object ? object->id : 0; break; case 'n': closure->types[i] = &ffi_type_uint32; closure->args[i] = p; object = va_arg(ap, struct wl_object *); if (end - p < 1) goto err; if (!arg.nullable && object == NULL) goto err_null; *p++ = object ? object->id : 0; break; case 'a': closure->types[i] = &ffi_type_pointer; closure->args[i] = extra; arrayp = (struct wl_array **) extra; extra += sizeof *arrayp; *arrayp = (struct wl_array *) extra; extra += sizeof **arrayp; array = va_arg(ap, struct wl_array *); if (!arg.nullable && array == NULL) goto err_null; if (array == NULL || array->size == 0) { if (end - p < 1) goto err; *p++ = 0; break; } if (p + DIV_ROUNDUP(array->size, sizeof *p) + 1 > end) goto err; *p++ = array->size; memcpy(p, array->data, array->size); (*arrayp)->size = array->size; (*arrayp)->alloc = array->alloc; (*arrayp)->data = p; p += DIV_ROUNDUP(array->size, sizeof *p); break; case 'h': closure->types[i] = &ffi_type_sint; closure->args[i] = extra; fd_ptr = (int *) extra; extra += sizeof *fd_ptr; fd = va_arg(ap, int); dup_fd = wl_os_dupfd_cloexec(fd, 0); if (dup_fd < 0) { fprintf(stderr, "dup failed: %m"); abort(); } *fd_ptr = dup_fd; break; default: fprintf(stderr, "unhandled format code: '%c'\n", arg.type); assert(0); break; } } size = (p - start) * sizeof *p; start[0] = sender->id; start[1] = opcode | (size << 16); closure->start = start; closure->message = message; closure->count = count; ffi_prep_cif(&closure->cif, FFI_DEFAULT_ABI, closure->count, &ffi_type_void, closure->types); return closure; err: printf("request too big to marshal, maximum size is %zu\n", sizeof closure->buffer); errno = ENOMEM; return NULL; err_null: free(closure); wl_log("error marshalling arguments for %s:%i.%s (signature %s): " "null value passed for arg %i\n", sender->interface->name, sender->id, message->name, message->signature, i); errno = EINVAL; return NULL; }