diff options
Diffstat (limited to 'ipc/kdbus/handle.c')
-rw-r--r-- | ipc/kdbus/handle.c | 691 |
1 files changed, 0 insertions, 691 deletions
diff --git a/ipc/kdbus/handle.c b/ipc/kdbus/handle.c deleted file mode 100644 index 2f82c2a32..000000000 --- a/ipc/kdbus/handle.c +++ /dev/null @@ -1,691 +0,0 @@ -/* - * Copyright (C) 2013-2015 Kay Sievers - * Copyright (C) 2013-2015 Greg Kroah-Hartman <gregkh@linuxfoundation.org> - * Copyright (C) 2013-2015 Daniel Mack <daniel@zonque.org> - * Copyright (C) 2013-2015 David Herrmann <dh.herrmann@gmail.com> - * Copyright (C) 2013-2015 Linux Foundation - * Copyright (C) 2014-2015 Djalal Harouni <tixxdz@opendz.org> - * - * kdbus is free software; you can redistribute it and/or modify it under - * the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation; either version 2.1 of the License, or (at - * your option) any later version. - */ - -#include <linux/file.h> -#include <linux/fs.h> -#include <linux/idr.h> -#include <linux/init.h> -#include <linux/kdev_t.h> -#include <linux/module.h> -#include <linux/mutex.h> -#include <linux/poll.h> -#include <linux/rwsem.h> -#include <linux/sched.h> -#include <linux/sizes.h> -#include <linux/slab.h> -#include <linux/uaccess.h> -#include <linux/syscalls.h> - -#include "bus.h" -#include "connection.h" -#include "endpoint.h" -#include "fs.h" -#include "handle.h" -#include "item.h" -#include "match.h" -#include "message.h" -#include "names.h" -#include "domain.h" -#include "policy.h" - -static int kdbus_args_verify(struct kdbus_args *args) -{ - struct kdbus_item *item; - size_t i; - int ret; - - KDBUS_ITEMS_FOREACH(item, args->items, args->items_size) { - struct kdbus_arg *arg = NULL; - - if (!KDBUS_ITEM_VALID(item, args->items, args->items_size)) - return -EINVAL; - - for (i = 0; i < args->argc; ++i) - if (args->argv[i].type == item->type) - break; - if (i >= args->argc) - return -EINVAL; - - arg = &args->argv[i]; - - ret = kdbus_item_validate(item); - if (ret < 0) - return ret; - - if (arg->item && !arg->multiple) - return -EINVAL; - - arg->item = item; - } - - if (!KDBUS_ITEMS_END(item, args->items, args->items_size)) - return -EINVAL; - - return 0; -} - -static int kdbus_args_negotiate(struct kdbus_args *args) -{ - struct kdbus_item __user *user; - struct kdbus_item *negotiation; - size_t i, j, num; - - /* - * If KDBUS_FLAG_NEGOTIATE is set, we overwrite the flags field with - * the set of supported flags. Furthermore, if an KDBUS_ITEM_NEGOTIATE - * item is passed, we iterate its payload (array of u64, each set to an - * item type) and clear all unsupported item-types to 0. - * The caller might do this recursively, if other flags or objects are - * embedded in the payload itself. - */ - - if (args->cmd->flags & KDBUS_FLAG_NEGOTIATE) { - if (put_user(args->allowed_flags & ~KDBUS_FLAG_NEGOTIATE, - &args->user->flags)) - return -EFAULT; - } - - if (args->argc < 1 || args->argv[0].type != KDBUS_ITEM_NEGOTIATE || - !args->argv[0].item) - return 0; - - negotiation = args->argv[0].item; - user = (struct kdbus_item __user *) - ((u8 __user *)args->user + - ((u8 *)negotiation - (u8 *)args->cmd)); - num = KDBUS_ITEM_PAYLOAD_SIZE(negotiation) / sizeof(u64); - - for (i = 0; i < num; ++i) { - for (j = 0; j < args->argc; ++j) - if (negotiation->data64[i] == args->argv[j].type) - break; - - if (j < args->argc) - continue; - - /* this item is not supported, clear it out */ - negotiation->data64[i] = 0; - if (put_user(negotiation->data64[i], &user->data64[i])) - return -EFAULT; - } - - return 0; -} - -/** - * __kdbus_args_parse() - parse payload of kdbus command - * @args: object to parse data into - * @is_cmd: whether this is a command or msg payload - * @argp: user-space location of command payload to parse - * @type_size: overall size of command payload to parse - * @items_offset: offset of items array in command payload - * @out: output variable to store pointer to copied payload - * - * This parses the ioctl payload at user-space location @argp into @args. @args - * must be pre-initialized by the caller to reflect the supported flags and - * items of this command. This parser will then copy the command payload into - * kernel-space, verify correctness and consistency and cache pointers to parsed - * items and other data in @args. - * - * If this function succeeded, you must call kdbus_args_clear() to release - * allocated resources before destroying @args. - * - * This can also be used to import kdbus_msg objects. In that case, @is_cmd must - * be set to 'false' and the 'return_flags' field will not be touched (as it - * doesn't exist on kdbus_msg). - * - * Return: On failure a negative error code is returned. Otherwise, 1 is - * returned if negotiation was requested, 0 if not. - */ -int __kdbus_args_parse(struct kdbus_args *args, bool is_cmd, void __user *argp, - size_t type_size, size_t items_offset, void **out) -{ - u64 user_size; - int ret, i; - - ret = kdbus_copy_from_user(&user_size, argp, sizeof(user_size)); - if (ret < 0) - return ret; - - if (user_size < type_size) - return -EINVAL; - if (user_size > KDBUS_CMD_MAX_SIZE) - return -EMSGSIZE; - - if (user_size <= sizeof(args->cmd_buf)) { - if (copy_from_user(args->cmd_buf, argp, user_size)) - return -EFAULT; - args->cmd = (void*)args->cmd_buf; - } else { - args->cmd = memdup_user(argp, user_size); - if (IS_ERR(args->cmd)) - return PTR_ERR(args->cmd); - } - - if (args->cmd->size != user_size) { - ret = -EINVAL; - goto error; - } - - if (is_cmd) - args->cmd->return_flags = 0; - args->user = argp; - args->items = (void *)((u8 *)args->cmd + items_offset); - args->items_size = args->cmd->size - items_offset; - args->is_cmd = is_cmd; - - if (args->cmd->flags & ~args->allowed_flags) { - ret = -EINVAL; - goto error; - } - - ret = kdbus_args_verify(args); - if (ret < 0) - goto error; - - ret = kdbus_args_negotiate(args); - if (ret < 0) - goto error; - - /* mandatory items must be given (but not on negotiation) */ - if (!(args->cmd->flags & KDBUS_FLAG_NEGOTIATE)) { - for (i = 0; i < args->argc; ++i) - if (args->argv[i].mandatory && !args->argv[i].item) { - ret = -EINVAL; - goto error; - } - } - - *out = args->cmd; - return !!(args->cmd->flags & KDBUS_FLAG_NEGOTIATE); - -error: - return kdbus_args_clear(args, ret); -} - -/** - * kdbus_args_clear() - release allocated command resources - * @args: object to release resources of - * @ret: return value of this command - * - * This frees all allocated resources on @args and copies the command result - * flags into user-space. @ret is usually returned unchanged by this function, - * so it can be used in the final 'return' statement of the command handler. - * - * Return: -EFAULT if return values cannot be copied into user-space, otherwise - * @ret is returned unchanged. - */ -int kdbus_args_clear(struct kdbus_args *args, int ret) -{ - if (!args) - return ret; - - if (!IS_ERR_OR_NULL(args->cmd)) { - if (args->is_cmd && put_user(args->cmd->return_flags, - &args->user->return_flags)) - ret = -EFAULT; - if (args->cmd != (void*)args->cmd_buf) - kfree(args->cmd); - args->cmd = NULL; - } - - return ret; -} - -/** - * enum kdbus_handle_type - type an handle can be of - * @KDBUS_HANDLE_NONE: no type set, yet - * @KDBUS_HANDLE_BUS_OWNER: bus owner - * @KDBUS_HANDLE_EP_OWNER: endpoint owner - * @KDBUS_HANDLE_CONNECTED: endpoint connection after HELLO - */ -enum kdbus_handle_type { - KDBUS_HANDLE_NONE, - KDBUS_HANDLE_BUS_OWNER, - KDBUS_HANDLE_EP_OWNER, - KDBUS_HANDLE_CONNECTED, -}; - -/** - * struct kdbus_handle - handle to the kdbus system - * @lock: handle lock - * @type: type of this handle (KDBUS_HANDLE_*) - * @bus_owner: bus this handle owns - * @ep_owner: endpoint this handle owns - * @conn: connection this handle owns - */ -struct kdbus_handle { - struct mutex lock; - - enum kdbus_handle_type type; - union { - struct kdbus_bus *bus_owner; - struct kdbus_ep *ep_owner; - struct kdbus_conn *conn; - }; -}; - -static int kdbus_handle_open(struct inode *inode, struct file *file) -{ - struct kdbus_handle *handle; - struct kdbus_node *node; - int ret; - - node = kdbus_node_from_inode(inode); - if (!kdbus_node_acquire(node)) - return -ESHUTDOWN; - - handle = kzalloc(sizeof(*handle), GFP_KERNEL); - if (!handle) { - ret = -ENOMEM; - goto exit; - } - - mutex_init(&handle->lock); - handle->type = KDBUS_HANDLE_NONE; - - file->private_data = handle; - ret = 0; - -exit: - kdbus_node_release(node); - return ret; -} - -static int kdbus_handle_release(struct inode *inode, struct file *file) -{ - struct kdbus_handle *handle = file->private_data; - - switch (handle->type) { - case KDBUS_HANDLE_BUS_OWNER: - if (handle->bus_owner) { - kdbus_node_drain(&handle->bus_owner->node); - kdbus_bus_unref(handle->bus_owner); - } - break; - case KDBUS_HANDLE_EP_OWNER: - if (handle->ep_owner) { - kdbus_node_drain(&handle->ep_owner->node); - kdbus_ep_unref(handle->ep_owner); - } - break; - case KDBUS_HANDLE_CONNECTED: - kdbus_conn_disconnect(handle->conn, false); - kdbus_conn_unref(handle->conn); - break; - case KDBUS_HANDLE_NONE: - /* nothing to clean up */ - break; - } - - kfree(handle); - - return 0; -} - -static long kdbus_handle_ioctl_control(struct file *file, unsigned int cmd, - void __user *argp) -{ - struct kdbus_handle *handle = file->private_data; - struct kdbus_node *node = file_inode(file)->i_private; - struct kdbus_domain *domain; - int ret = 0; - - if (!kdbus_node_acquire(node)) - return -ESHUTDOWN; - - /* - * The parent of control-nodes is always a domain, make sure to pin it - * so the parent is actually valid. - */ - domain = kdbus_domain_from_node(node->parent); - if (!kdbus_node_acquire(&domain->node)) { - kdbus_node_release(node); - return -ESHUTDOWN; - } - - switch (cmd) { - case KDBUS_CMD_BUS_MAKE: { - struct kdbus_bus *bus; - - bus = kdbus_cmd_bus_make(domain, argp); - if (IS_ERR_OR_NULL(bus)) { - ret = PTR_ERR_OR_ZERO(bus); - break; - } - - handle->bus_owner = bus; - ret = KDBUS_HANDLE_BUS_OWNER; - break; - } - - default: - ret = -EBADFD; - break; - } - - kdbus_node_release(&domain->node); - kdbus_node_release(node); - return ret; -} - -static long kdbus_handle_ioctl_ep(struct file *file, unsigned int cmd, - void __user *buf) -{ - struct kdbus_handle *handle = file->private_data; - struct kdbus_node *node = file_inode(file)->i_private; - struct kdbus_ep *ep, *file_ep = kdbus_ep_from_node(node); - struct kdbus_bus *bus = file_ep->bus; - struct kdbus_conn *conn; - int ret = 0; - - if (!kdbus_node_acquire(node)) - return -ESHUTDOWN; - - switch (cmd) { - case KDBUS_CMD_ENDPOINT_MAKE: { - /* creating custom endpoints is a privileged operation */ - if (!kdbus_ep_is_owner(file_ep, file)) { - ret = -EPERM; - break; - } - - ep = kdbus_cmd_ep_make(bus, buf); - if (IS_ERR_OR_NULL(ep)) { - ret = PTR_ERR_OR_ZERO(ep); - break; - } - - handle->ep_owner = ep; - ret = KDBUS_HANDLE_EP_OWNER; - break; - } - - case KDBUS_CMD_HELLO: - conn = kdbus_cmd_hello(file_ep, file, buf); - if (IS_ERR_OR_NULL(conn)) { - ret = PTR_ERR_OR_ZERO(conn); - break; - } - - handle->conn = conn; - ret = KDBUS_HANDLE_CONNECTED; - break; - - default: - ret = -EBADFD; - break; - } - - kdbus_node_release(node); - return ret; -} - -static long kdbus_handle_ioctl_ep_owner(struct file *file, unsigned int command, - void __user *buf) -{ - struct kdbus_handle *handle = file->private_data; - struct kdbus_ep *ep = handle->ep_owner; - int ret; - - if (!kdbus_node_acquire(&ep->node)) - return -ESHUTDOWN; - - switch (command) { - case KDBUS_CMD_ENDPOINT_UPDATE: - ret = kdbus_cmd_ep_update(ep, buf); - break; - default: - ret = -EBADFD; - break; - } - - kdbus_node_release(&ep->node); - return ret; -} - -static long kdbus_handle_ioctl_connected(struct file *file, - unsigned int command, void __user *buf) -{ - struct kdbus_handle *handle = file->private_data; - struct kdbus_conn *conn = handle->conn; - struct kdbus_conn *release_conn = NULL; - int ret; - - release_conn = conn; - ret = kdbus_conn_acquire(release_conn); - if (ret < 0) - return ret; - - switch (command) { - case KDBUS_CMD_BYEBYE: - /* - * BYEBYE is special; we must not acquire a connection when - * calling into kdbus_conn_disconnect() or we will deadlock, - * because kdbus_conn_disconnect() will wait for all acquired - * references to be dropped. - */ - kdbus_conn_release(release_conn); - release_conn = NULL; - ret = kdbus_cmd_byebye_unlocked(conn, buf); - break; - case KDBUS_CMD_NAME_ACQUIRE: - ret = kdbus_cmd_name_acquire(conn, buf); - break; - case KDBUS_CMD_NAME_RELEASE: - ret = kdbus_cmd_name_release(conn, buf); - break; - case KDBUS_CMD_LIST: - ret = kdbus_cmd_list(conn, buf); - break; - case KDBUS_CMD_CONN_INFO: - ret = kdbus_cmd_conn_info(conn, buf); - break; - case KDBUS_CMD_BUS_CREATOR_INFO: - ret = kdbus_cmd_bus_creator_info(conn, buf); - break; - case KDBUS_CMD_UPDATE: - ret = kdbus_cmd_update(conn, buf); - break; - case KDBUS_CMD_MATCH_ADD: - ret = kdbus_cmd_match_add(conn, buf); - break; - case KDBUS_CMD_MATCH_REMOVE: - ret = kdbus_cmd_match_remove(conn, buf); - break; - case KDBUS_CMD_SEND: - ret = kdbus_cmd_send(conn, file, buf); - break; - case KDBUS_CMD_RECV: - ret = kdbus_cmd_recv(conn, buf); - break; - case KDBUS_CMD_FREE: - ret = kdbus_cmd_free(conn, buf); - break; - default: - ret = -EBADFD; - break; - } - - kdbus_conn_release(release_conn); - return ret; -} - -static long kdbus_handle_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) -{ - struct kdbus_handle *handle = file->private_data; - struct kdbus_node *node = kdbus_node_from_inode(file_inode(file)); - void __user *argp = (void __user *)arg; - long ret = -EBADFD; - - switch (cmd) { - case KDBUS_CMD_BUS_MAKE: - case KDBUS_CMD_ENDPOINT_MAKE: - case KDBUS_CMD_HELLO: - mutex_lock(&handle->lock); - if (handle->type == KDBUS_HANDLE_NONE) { - if (node->type == KDBUS_NODE_CONTROL) - ret = kdbus_handle_ioctl_control(file, cmd, - argp); - else if (node->type == KDBUS_NODE_ENDPOINT) - ret = kdbus_handle_ioctl_ep(file, cmd, argp); - - if (ret > 0) { - /* - * The data given via open() is not sufficient - * to setup a kdbus handle. Hence, we require - * the user to perform a setup ioctl. This setup - * can only be performed once and defines the - * type of the handle. The different setup - * ioctls are locked against each other so they - * cannot race. Once the handle type is set, - * the type-dependent ioctls are enabled. To - * improve performance, we don't lock those via - * handle->lock. Instead, we issue a - * write-barrier before performing the - * type-change, which pairs with smp_rmb() in - * all handlers that access the type field. This - * guarantees the handle is fully setup, if - * handle->type is set. If handle->type is - * unset, you must not make any assumptions - * without taking handle->lock. - * Note that handle->type is only set once. It - * will never change afterwards. - */ - smp_wmb(); - handle->type = ret; - } - } - mutex_unlock(&handle->lock); - break; - - case KDBUS_CMD_ENDPOINT_UPDATE: - case KDBUS_CMD_BYEBYE: - case KDBUS_CMD_NAME_ACQUIRE: - case KDBUS_CMD_NAME_RELEASE: - case KDBUS_CMD_LIST: - case KDBUS_CMD_CONN_INFO: - case KDBUS_CMD_BUS_CREATOR_INFO: - case KDBUS_CMD_UPDATE: - case KDBUS_CMD_MATCH_ADD: - case KDBUS_CMD_MATCH_REMOVE: - case KDBUS_CMD_SEND: - case KDBUS_CMD_RECV: - case KDBUS_CMD_FREE: { - enum kdbus_handle_type type; - - /* - * This read-barrier pairs with smp_wmb() of the handle setup. - * it guarantees the handle is fully written, in case the - * type has been set. It allows us to access the handle without - * taking handle->lock, given the guarantee that the type is - * only ever set once, and stays constant afterwards. - * Furthermore, the handle object itself is not modified in any - * way after the type is set. That is, the type-field is the - * last field that is written on any handle. If it has not been - * set, we must not access the handle here. - */ - type = handle->type; - smp_rmb(); - - if (type == KDBUS_HANDLE_EP_OWNER) - ret = kdbus_handle_ioctl_ep_owner(file, cmd, argp); - else if (type == KDBUS_HANDLE_CONNECTED) - ret = kdbus_handle_ioctl_connected(file, cmd, argp); - - break; - } - default: - ret = -ENOTTY; - break; - } - - return ret < 0 ? ret : 0; -} - -static unsigned int kdbus_handle_poll(struct file *file, - struct poll_table_struct *wait) -{ - struct kdbus_handle *handle = file->private_data; - enum kdbus_handle_type type; - unsigned int mask = POLLOUT | POLLWRNORM; - - /* - * This pairs with smp_wmb() during handle setup. It guarantees that - * _iff_ the handle type is set, handle->conn is valid. Furthermore, - * _iff_ the type is set, the handle object is constant and never - * changed again. If it's not set, we must not access the handle but - * bail out. We also must assume no setup has taken place, yet. - */ - type = handle->type; - smp_rmb(); - - /* Only a connected endpoint can read/write data */ - if (type != KDBUS_HANDLE_CONNECTED) - return POLLERR | POLLHUP; - - poll_wait(file, &handle->conn->wait, wait); - - /* - * Verify the connection hasn't been deactivated _after_ adding the - * wait-queue. This guarantees, that if the connection is deactivated - * after we checked it, the waitqueue is signaled and we're called - * again. - */ - if (!kdbus_conn_active(handle->conn)) - return POLLERR | POLLHUP; - - if (!list_empty(&handle->conn->queue.msg_list) || - atomic_read(&handle->conn->lost_count) > 0) - mask |= POLLIN | POLLRDNORM; - - return mask; -} - -static int kdbus_handle_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct kdbus_handle *handle = file->private_data; - enum kdbus_handle_type type; - int ret = -EBADFD; - - /* - * This pairs with smp_wmb() during handle setup. It guarantees that - * _iff_ the handle type is set, handle->conn is valid. Furthermore, - * _iff_ the type is set, the handle object is constant and never - * changed again. If it's not set, we must not access the handle but - * bail out. We also must assume no setup has taken place, yet. - */ - type = handle->type; - smp_rmb(); - - /* Only connected handles have a pool we can map */ - if (type == KDBUS_HANDLE_CONNECTED) - ret = kdbus_pool_mmap(handle->conn->pool, vma); - - return ret; -} - -const struct file_operations kdbus_handle_ops = { - .owner = THIS_MODULE, - .open = kdbus_handle_open, - .release = kdbus_handle_release, - .poll = kdbus_handle_poll, - .llseek = noop_llseek, - .unlocked_ioctl = kdbus_handle_ioctl, - .mmap = kdbus_handle_mmap, -#ifdef CONFIG_COMPAT - .compat_ioctl = kdbus_handle_ioctl, -#endif -}; |