diff options
author | Kay Sievers <kay@vrfy.org> | 2013-05-02 21:48:04 +0200 |
---|---|---|
committer | Kay Sievers <kay@vrfy.org> | 2013-05-08 00:00:17 +0200 |
commit | 111fb9ed7d88e76942fe0f5c146333b1b1e6cbe2 (patch) | |
tree | b879202f0c4110991d26c1cd0e83b9ab7bb83043 | |
parent | 1c1a20cd7f3f610762934dbbc602b0e8edf47da7 (diff) | |
download | kdbus-bus-111fb9ed7d88e76942fe0f5c146333b1b1e6cbe2.tar.gz kdbus-bus-111fb9ed7d88e76942fe0f5c146333b1b1e6cbe2.tar.bz2 kdbus-bus-111fb9ed7d88e76942fe0f5c146333b1b1e6cbe2.zip |
copy messages directly from sender into a receiver-supplied buffer
Require the receiver to pre-allocate a buffer and register it with
HELLO. The sender will copy its data directly into the revceiver's
buffer.
The RECV call will now only return a pointer to the next message
in the buffer. The buffer needs to be free()d with the FREE ioctl.
FIXME: currently missing is a proper allocator to manage the
receiver's buffer, in the current state the allocator will only clear
the buffer if all messages are free()d. This is obviously only useful
for testing.
-rw-r--r-- | Makefile | 1 | ||||
-rw-r--r-- | TODO | 13 | ||||
-rw-r--r-- | buffer.c | 182 | ||||
-rw-r--r-- | buffer.h | 52 | ||||
-rw-r--r-- | connection.c | 493 | ||||
-rw-r--r-- | connection.h | 40 | ||||
-rw-r--r-- | internal.h | 19 | ||||
-rw-r--r-- | kdbus.h | 19 | ||||
-rw-r--r-- | match.c | 22 | ||||
-rw-r--r-- | message.c | 594 | ||||
-rw-r--r-- | message.h | 34 | ||||
-rw-r--r-- | notify.c | 28 | ||||
-rw-r--r-- | notify.h | 6 | ||||
-rw-r--r-- | test/kdbus-enum.c | 3 | ||||
-rw-r--r-- | test/kdbus-util.c | 100 |
15 files changed, 905 insertions, 701 deletions
@@ -1,6 +1,7 @@ kdbus-y := \ bus.o \ connection.o \ + buffer.o \ ep.o \ main.o \ match.o \ @@ -1,7 +1,14 @@ External API: - - check that enum values of 0 make sense, otherwise insert "*_NONE," - - always zero-out, put \0 in the padding bytes? - - merge dst_id and src_ id into a union? + +Internal: + - chunk get_user_pages() while writing to the internal buffer api + - update/rethink kdbus_conn_accounting_sub_size() use, we don't allocate + kernel memory anymore + - add real allocator to kdbus_conn_buffer_alloc() + - BUG: connections are currently listed in the endpoint not the bus, so + broadcasting will never see the other endpoints on the same bus + - BUG: the sender needs to ref the receiver when looking it up to protect + against the receiver going away while its ressources are accessed Kernel Core: - remove our own task_cgroup_path_from_hierarchy() as soon as it's available: diff --git a/buffer.c b/buffer.c new file mode 100644 index 00000000000..e97fb77ae72 --- /dev/null +++ b/buffer.c @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2013 Kay Sievers + * Copyright (C) 2013 Greg Kroah-Hartman <gregkh@linuxfoundation.org> + * Copyright (C) 2013 Linux Foundation + * Copyright (C) 2013 Daniel Mack <daniel@zonque.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. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/module.h> +#include <linux/fs.h> +#include <linux/slab.h> +#include <linux/sizes.h> +#include <linux/sched.h> +#include <linux/mutex.h> +#include <linux/init.h> +#include <linux/mm.h> +#include <linux/highmem.h> + +#include "message.h" + +/* + * At KDBUS_CMD_MSG_SEND, messages are placed direcly into the buffer the + * receiver has registered with KDBUS_HELLO_BUFFER. + * + * To receive a message, KDBUS_CMD_MSG_RECV is called, which returns a pointer + * into the buffer. + * + * The internally allocated memory needs to be freed by the receiver with + * KDBUS_CMD_MSG_FREE. + */ +struct kdbus_buffer { + void __user *buf; /* receiver-supplied buffer */ + size_t size; /* size of buffer */ + size_t pos; /* current wrire position */ + unsigned int users; +}; + +/* allocate a slot on the given size in the receiver's buffer */ +struct kdbus_msg __user * +kdbus_buffer_alloc(struct kdbus_buffer *buf, size_t len) +{ + size_t pos; + + pos = KDBUS_ALIGN8(buf->pos); + if (pos + len > buf->size) + return NULL; + + buf->pos = pos + len; + buf->users++; + + return buf->buf + pos; +} + +/* free the allocated slot */ +void kdbus_buffer_free(struct kdbus_buffer *buf, struct kdbus_msg __user *msg) +{ + if (!msg) + return; + + BUG_ON(buf->users == 0); + + /* FIXME: dumbest possible version of an allocator: just reset the buffer + * when it is empty; replace with rbtree/slice/list allocator */ + if (--buf->users == 0) + buf->pos = 0; +} + +/* + * Temporarily map a range of the receiver's buffer to write chunks of data from + * the sender into it. + */ +struct kdbus_buffer_map { + struct page **pages; /* array of pages representign the buffer */ + unsigned int n; /* number pf pages in the array */ + unsigned long cur; /* current page we write to */ + unsigned long pos; /* position in current page we write to*/ +}; + +/* unpin the receiver's pages */ +void kdbus_buffer_map_close(struct kdbus_buffer_map *buf) +{ + unsigned int i; + + for (i = 0; i < buf->n; i++) + put_page(buf->pages[i]); + kfree(buf->pages); +} + +/* pin the receiver's memory range/pages */ +int kdbus_buffer_map_open(struct kdbus_buffer_map *map, + struct task_struct *task, + void __user *to, size_t len) +{ + unsigned int n; + int have; + unsigned long base; + unsigned long addr; + struct mm_struct *mm; + + memset(map, 0, sizeof(struct kdbus_buffer)); + + /* calculate the number of pages involved in the range */ + addr = (unsigned long)to; + n = (addr + len - 1) / PAGE_SIZE - addr / PAGE_SIZE + 1; + + map->pages = kmalloc(n * sizeof(struct page *), GFP_KERNEL); + if (!map->pages) + return -ENOMEM; + + /* start address in our first page */ + base = addr & PAGE_MASK; + map->pos = addr - base; + + /* pin the receiver's buffer page(s); the task + * is pinned as long as the connection is open */ + mm = get_task_mm(task); + if (!mm) { + kdbus_buffer_map_close(map); + return -ESHUTDOWN; + } + down_read(&mm->mmap_sem); + have = get_user_pages(task, mm, base, n, + true, false, map->pages, NULL); + up_read(&mm->mmap_sem); + mmput(mm); + + if (have < 0) { + kdbus_buffer_map_close(map); + return have; + } + + map->n = have; + + /* fewer pages than requested */ + if (map->n < n) { + kdbus_buffer_map_close(map); + return -EFAULT; + } + + return 0; +} + +/* copy a memory range from the current user process page by + * page into the pinned receiver's buffer */ +int kdbus_buffer_map_write(struct kdbus_buffer_map *map, + void __user *from, size_t len) +{ + int ret = 0; + + while (len > 0) { + void *addr; + size_t bytes; + + /* bytes copy to remaining space of current page */ + bytes = min(PAGE_SIZE - map->pos, len); + + /* map, fill, unmap current page */ + addr = kmap(map->pages[map->cur]) + map->pos; + if (copy_from_user(addr, from, bytes)) + ret = -EFAULT; + kunmap(map->pages[map->cur]); + if (ret < 0) + break; + + /* add to pos, or move to next page */ + map->pos += bytes; + if (map->pos == PAGE_SIZE) { + map->pos = 0; + map->cur++; + } + + len -= bytes; + } + + return ret; +} diff --git a/buffer.h b/buffer.h new file mode 100644 index 00000000000..0cdb065d870 --- /dev/null +++ b/buffer.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2013 Kay Sievers + * Copyright (C) 2013 Greg Kroah-Hartman <gregkh@linuxfoundation.org> + * Copyright (C) 2013 Linux Foundation + * Copyright (C) 2013 Daniel Mack <daniel@zonque.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. + */ + +#ifndef __KDBUS_BUFFER_H +#define __KDBUS_BUFFER_H + +/* + * At KDBUS_CMD_MSG_SEND, messages are placed direcly into the buffer the + * receiver has registered with KDBUS_HELLO_BUFFER. + * + * To receive a message, KDBUS_CMD_MSG_RECV is called, which returns a pointer + * into the buffer. + * + * The internally allocated memory needs to be freed by the receiver with + * KDBUS_CMD_MSG_FREE. + */ +struct kdbus_buffer { + void __user *buf; /* receiver-supplied buffer */ + size_t size; /* size of buffer */ + size_t pos; /* current write position */ + unsigned int users; +}; + +/* + * Structure to keep the state of a mapped range on the buffer while + * writing chunks of data to it from the sender. + */ +struct kdbus_buffer_map { + struct page **pages; /* array of pages representign the buffer */ + unsigned int n; /* number pf pages in the array */ + unsigned long cur; /* current page we write to */ + unsigned long pos; /* position in current page we write to */ +}; + +struct kdbus_msg __user *kdbus_buffer_alloc(struct kdbus_buffer *buf, size_t len); +void kdbus_buffer_free(struct kdbus_buffer *buf, struct kdbus_msg __user *msg); +void kdbus_buffer_map_close(struct kdbus_buffer *buf); +int kdbus_buffer_map_open(struct kdbus_buffer *buf, + struct task_struct *task, + void __user *to, size_t len); +int kdbus_buffer_map_write(struct kdbus_buffer *buf, + void __user *from, size_t len); +#endif diff --git a/connection.c b/connection.c index bd9b6cde914..c5f528e3f05 100644 --- a/connection.c +++ b/connection.c @@ -22,12 +22,16 @@ #include <linux/mutex.h> #include <linux/init.h> #include <linux/poll.h> +#include <linux/file.h> #include <linux/hashtable.h> #include <linux/audit.h> #include <linux/security.h> +#include <linux/mm.h> +#include <linux/highmem.h> #include <uapi/linux/major.h> #include "connection.h" +#include "buffer.h" #include "message.h" #include "notify.h" #include "ns.h" @@ -37,7 +41,309 @@ #include "names.h" #include "policy.h" -int kdbus_conn_add_size_allocation(struct kdbus_conn *conn, size_t size) +static void kdbus_conn_fds_unref(struct kdbus_conn_queue *queue) +{ + unsigned int i; + + if (!queue->fds_fp) + return; + + for (i = 0; i < queue->fds_count; i++) { + if (!queue->fds_fp[i]) + break; + + fput(queue->fds_fp[i]); + } + + kfree(queue->fds_fp); + queue->fds_fp = NULL; +} + +/* grab references of passed-in FDS for in-flight messages */ +static int kdbus_conn_fds_ref(struct kdbus_conn_queue *queue, + const int *fds, unsigned int fds_count) +{ + unsigned int i; + + queue->fds_fp = kmalloc(fds_count * sizeof(struct file *), GFP_KERNEL); + if (!queue->fds_fp) + return -ENOMEM; + + for (i = 0; i < fds_count; i++) { + queue->fds_fp[i] = fget(fds[i]); + if (!queue->fds_fp[i]) { + kdbus_conn_fds_unref(queue); + return -EBADF; + } + } + + return 0; +} + +void kdbus_conn_queue_cleanup(struct kdbus_conn_queue *queue) +{ + kdbus_conn_fds_unref(queue); + kfree(queue); +} + +/* enqueue a message into the receiver's connection */ +int kdbus_conn_queue_insert(struct kdbus_conn *conn, + struct kdbus_kmsg *kmsg, + u64 deadline_ns) +{ + struct kdbus_conn_queue *queue; + const struct kdbus_item *item; + void __user *buf; + u64 msg_size; + size_t vec = 0; + size_t fds = 0; + size_t meta = 0; + size_t vec_data; + struct kdbus_buffer buffer; + int ret = 0; + + if (!conn->active) + return -ENOTCONN; + + if (kmsg->fds && !(conn->flags & KDBUS_HELLO_ACCEPT_FD)) + return -ECOMM; + + queue = kzalloc(sizeof(struct kdbus_conn_queue), GFP_KERNEL); + if (!queue) + return -ENOMEM; + + INIT_LIST_HEAD(&queue->entry); + + /* copy message properies we need for the queue management */ + queue->deadline_ns = deadline_ns; + queue->src_id = kmsg->msg.src_id; + queue->cookie = kmsg->msg.cookie; + if (kmsg->msg.flags & KDBUS_MSG_FLAGS_EXPECT_REPLY) + queue->expect_reply = true; + + /* space for message header */ + msg_size = KDBUS_MSG_HEADER_SIZE; + + /* space for PAYLOAD_VEC item */ + if (kmsg->vecs_size > 0) { + vec = msg_size; + msg_size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_vec)); + } + + /* space for FDS item */ + if (kmsg->fds_count > 0) { + fds = msg_size; + msg_size += KDBUS_ITEM_SIZE(kmsg->fds_count * sizeof(int)); + } + + /* space for metadata/credential items */ + if (kmsg->meta_size > 0) { + meta = msg_size; + msg_size += kmsg->meta_size; + } + + /* data starts after the message */ + vec_data = KDBUS_ALIGN8(msg_size); + + /* allocate the buffer from the receiver */ + mutex_lock(&conn->lock); + if (conn->msg_count > KDBUS_CONN_MAX_MSGS) { + ret = -EXFULL; + goto exit_unlock; + } + + buf = kdbus_buffer_alloc(&conn->buffer, vec_data + kmsg->vecs_size); + if (!buf) { + ret = -EXFULL; + goto exit_unlock; + } + mutex_unlock(&conn->lock); + + /* update and copy the message header */ + if (copy_to_user(buf, &kmsg->msg, KDBUS_MSG_HEADER_SIZE)) { + ret = -EFAULT; + goto exit; + } + + /* update the size */ + if (kdbus_size_set_user(msg_size, buf, struct kdbus_msg)) { + ret = -EFAULT; + goto exit; + } + + /* add a PAYLOAD_VEC item */ + if (kmsg->vecs_size > 0) { + size_t size = KDBUS_ITEM_HEADER_SIZE + sizeof(struct kdbus_vec); + char tmp[size]; + struct kdbus_item *it = (struct kdbus_item *)tmp; + + it->type = KDBUS_MSG_PAYLOAD_VEC; + it->size = size; + it->vec.address = (u64)buf + vec_data; + it->vec.size = kmsg->vecs_size; + if (copy_to_user(buf + vec, it, size)) { + ret = -EFAULT; + goto exit; + } + } + + /* add a FDS item; the array content will be updated at RECV time */ + if (kmsg->fds_count > 0) { + size_t size = KDBUS_ITEM_HEADER_SIZE; + char tmp[size]; + struct kdbus_item *it = (struct kdbus_item *)tmp; + + it->type = KDBUS_MSG_FDS; + it->size = size + (kmsg->fds_count * sizeof(int)); + if (copy_to_user(buf + fds, it, size)) { + ret = -EFAULT; + goto exit; + } + + queue->fds = it->fds; + + ret = kdbus_conn_fds_ref(queue, kmsg->fds, kmsg->fds_count); + if (ret < 0) + goto exit; + } + + /* append message metadata/credential items */ + if (kmsg->meta_size > 0) { + if (copy_to_user(buf + meta, kmsg->meta, kmsg->meta_size)) { + ret = -EFAULT; + goto exit; + } + } + + /* copy all payload data */ + if (kmsg->vecs_size > 0) { + kdbus_buffer_map_open(&buffer, conn->task, + buf + vec_data, kmsg->vecs_size); + KDBUS_ITEM_FOREACH(item, &kmsg->msg) { + if (item->type != KDBUS_MSG_PAYLOAD_VEC) + continue; + + ret = kdbus_buffer_map_write(&buffer, + (void *)(uintptr_t)item->vec.address, + item->vec.size); + if (ret < 0) + break; + + vec_data += item->vec.size; + } + kdbus_buffer_map_close(&buffer); + if (ret < 0) + goto exit; + } + + /* remember the pointer to the message */ + queue->msg = buf; + + /* link the message into the receiver's queue */ + mutex_lock(&conn->lock); + list_add_tail(&queue->entry, &conn->msg_list); + conn->msg_count++; + mutex_unlock(&conn->lock); + + /* wake up poll() */ + wake_up_interruptible(&conn->ep->wait); + return 0; + +exit_unlock: + mutex_unlock(&conn->lock); +exit: + kdbus_conn_queue_cleanup(queue); + return ret; +} + +static int kdbus_conn_fds_install(struct kdbus_conn_queue *queue) +{ + unsigned int i; + int *fds; + size_t size; + int ret = 0; + + /* get array of file descriptors */ + size = queue->fds_count * sizeof(int); + fds = kmalloc(size, GFP_KERNEL); + if (!fds) + return -ENOMEM; + + /* allocate new file descriptors in the receiver's process */ + for (i = 0; i < queue->fds_count; i++) { + fds[i] = get_unused_fd(); + if (fds[i] < 0) { + ret = fds[i]; + goto remove_unused; + } + } + + /* copy the array into the message item */ + if (copy_to_user(queue->fds, fds, size)) { + ret = -EFAULT; + goto remove_unused; + } + + /* install files in the receiver's process */ + for (i = 0; i < queue->fds_count; i++) + fd_install(fds[i], get_file(queue->fds_fp[i])); + + kfree(fds); + return 0; + +remove_unused: + for (i = 0; i < queue->fds_count; i++) { + if (fds[i] < 0) + break; + put_unused_fd(fds[i]); + } + + kfree(fds); + return ret; +} + +static int +kdbus_conn_recv_msg(struct kdbus_conn *conn, struct kdbus_msg __user **msg_ptr) +{ + struct kdbus_conn_queue *queue; + int ret; + + if (!KDBUS_IS_ALIGNED8((unsigned long)msg_ptr)) + return -EFAULT; + + mutex_lock(&conn->lock); + if (conn->msg_count == 0) { + ret = -EAGAIN; + goto exit_unlock; + } + + /* return the address of the next message in the buffer */ + queue = list_first_entry(&conn->msg_list, + struct kdbus_conn_queue, entry); + if (put_user(queue->msg, msg_ptr)) { + ret = -EFAULT; + goto exit_unlock; + } + + if (queue->fds_count) { + ret = kdbus_conn_fds_install(queue); + if (ret < 0) + goto exit_unlock; + } + + conn->msg_count--; + list_del(&queue->entry); + mutex_unlock(&conn->lock); + + kdbus_conn_queue_cleanup(queue); + return 0; + +exit_unlock: + mutex_unlock(&conn->lock); + return ret; +} + +int kdbus_conn_accounting_add_size(struct kdbus_conn *conn, size_t size) { int ret = 0; @@ -54,7 +360,7 @@ int kdbus_conn_add_size_allocation(struct kdbus_conn *conn, size_t size) return ret; } -void kdbus_conn_sub_size_allocation(struct kdbus_conn *conn, size_t size) +void kdbus_conn_accounting_sub_size(struct kdbus_conn *conn, size_t size) { if (!conn) return; @@ -66,7 +372,7 @@ void kdbus_conn_sub_size_allocation(struct kdbus_conn *conn, size_t size) static void kdbus_conn_scan_timeout(struct kdbus_conn *conn) { - struct kdbus_msg_list_entry *entry, *tmp; + struct kdbus_conn_queue *queue, *tmp; u64 deadline = -1; struct timespec ts; uint64_t now; @@ -74,24 +380,23 @@ static void kdbus_conn_scan_timeout(struct kdbus_conn *conn) ktime_get_ts(&ts); now = timespec_to_ns(&ts); - mutex_lock(&conn->msg_lock); - list_for_each_entry_safe(entry, tmp, &conn->msg_list, entry) { - struct kdbus_kmsg *kmsg = entry->kmsg; - - if (kmsg->deadline_ns == 0) + mutex_lock(&conn->lock); + list_for_each_entry_safe(queue, tmp, &conn->msg_list, entry) { + if (queue->deadline_ns == 0) continue; - if (kmsg->deadline_ns <= now) { - if (kmsg->msg.flags & KDBUS_MSG_FLAGS_EXPECT_REPLY) - kdbus_notify_reply_timeout(conn->ep, &kmsg->msg); - kdbus_kmsg_unref(entry->kmsg); - list_del(&entry->entry); - kfree(entry); - } else if (kmsg->deadline_ns < deadline) { - deadline = kmsg->deadline_ns; + if (queue->deadline_ns <= now) { + if (queue->expect_reply) + kdbus_notify_reply_timeout(conn->ep, + queue->src_id, queue->cookie); + kdbus_buffer_free(&conn->buffer, queue->msg); + list_del(&queue->entry); + kdbus_conn_queue_cleanup(queue); + } else if (queue->deadline_ns < deadline) { + deadline = queue->deadline_ns; } } - mutex_unlock(&conn->msg_lock); + mutex_unlock(&conn->lock); if (deadline != -1) { u64 usecs = deadline - now; @@ -106,7 +411,7 @@ static void kdbus_conn_work(struct work_struct *work) kdbus_conn_scan_timeout(conn); } -void kdbus_conn_schedule_timeout_scan(struct kdbus_conn *conn) +void kdbus_conn_timeout_schedule_scan(struct kdbus_conn *conn) { schedule_work(&conn->work); } @@ -114,7 +419,7 @@ void kdbus_conn_schedule_timeout_scan(struct kdbus_conn *conn) static void kdbus_conn_timer_func(unsigned long val) { struct kdbus_conn *conn = (struct kdbus_conn *) val; - kdbus_conn_schedule_timeout_scan(conn); + kdbus_conn_timeout_schedule_scan(conn); } #ifdef CONFIG_AUDITSYSCALL @@ -171,7 +476,6 @@ static int kdbus_conn_open(struct inode *inode, struct file *file) /* control device node */ if (MINOR(inode->i_rdev) == 0) { conn->type = KDBUS_CONN_CONTROL; - file->private_data = conn; pr_debug("opened control device '%s/control'\n", conn->ns->devpath); return 0; @@ -195,7 +499,7 @@ static int kdbus_conn_open(struct inode *inode, struct file *file) /* add this connection to hash table */ hash_add(conn->ep->bus->conn_hash, &conn->hentry, conn->id); - mutex_init(&conn->msg_lock); + mutex_init(&conn->lock); mutex_init(&conn->names_lock); mutex_init(&conn->accounting_lock); INIT_LIST_HEAD(&conn->msg_list); @@ -231,10 +535,14 @@ static int kdbus_conn_open(struct inode *inode, struct file *file) (unsigned long long)conn->id, conn->ns->devpath, conn->ep->bus->name); + //FIXME: cleanup here! ret = kdbus_notify_id_change(conn->ep, KDBUS_MSG_ID_ADD, conn->id, conn->flags); if (ret < 0) return ret; + /* pin and store the task, so a sender can copy to the receiver */ + get_task_struct(current); + conn->task = current; return 0; exit_unlock: @@ -258,43 +566,39 @@ static int kdbus_conn_release(struct inode *inode, struct file *file) break; case KDBUS_CONN_EP: { - struct kdbus_msg_list_entry *entry, *tmp; + struct kdbus_conn_queue *queue, *tmp; struct list_head list; INIT_LIST_HEAD(&list); hash_del(&conn->hentry); list_del(&conn->connection_entry); + /* clean up any messages still left on this endpoint */ - mutex_lock(&conn->msg_lock); - list_for_each_entry_safe(entry, tmp, &conn->msg_list, entry) { - struct kdbus_kmsg *kmsg = entry->kmsg; - struct kdbus_msg *msg = &kmsg->msg; - - list_del(&entry->entry); - - /* - * calling kdbus_notify_reply_dead() with msg_lock held - * causes a lockdep warning, so let's re-link those - * messages into a temporary list and handle it later. - */ - if (msg->src_id != conn->id && - msg->flags & KDBUS_MSG_FLAGS_EXPECT_REPLY) { - list_add_tail(&entry->entry, &list); + mutex_lock(&conn->lock); + list_for_each_entry_safe(queue, tmp, &conn->msg_list, entry) { + list_del(&queue->entry); + + /* we cannot hold "lock" and enqueue new messages with + * kdbus_notify_reply_dead(); move these messages + * into a temporary list and handle them below */ + if (queue->src_id != conn->id && queue->expect_reply) { + list_add_tail(&queue->entry, &list); } else { - kdbus_kmsg_unref(kmsg); - kfree(entry); + kdbus_buffer_free(&conn->buffer, queue->msg); + kdbus_conn_queue_cleanup(queue); } } - mutex_unlock(&conn->msg_lock); - - list_for_each_entry_safe(entry, tmp, &list, entry) { - struct kdbus_kmsg *kmsg = entry->kmsg; - struct kdbus_msg *msg = &kmsg->msg; - - kdbus_notify_reply_dead(conn->ep, msg); - kdbus_kmsg_unref(kmsg); - kfree(entry); + mutex_unlock(&conn->lock); + + list_for_each_entry_safe(queue, tmp, &list, entry) { + kdbus_notify_reply_dead(conn->ep, queue->src_id, + queue->cookie); + mutex_lock(&conn->lock); + kdbus_buffer_free(&conn->buffer, queue->msg); + mutex_unlock(&conn->lock); + kdbus_conn_queue_cleanup(queue); + list_del(&queue->entry); } del_timer(&conn->timer); @@ -311,6 +615,7 @@ static int kdbus_conn_release(struct inode *inode, struct file *file) kdbus_match_db_unref(conn->match_db); kdbus_ep_unref(conn->ep); + put_task_struct(current); break; } @@ -410,8 +715,8 @@ static long kdbus_conn_ioctl_ep(struct file *file, unsigned int cmd, { struct kdbus_conn *conn = file->private_data; struct kdbus_cmd_ep_kmake *kmake = NULL; + struct kdbus_cmd_hello *hello = NULL; struct kdbus_bus *bus = NULL; - struct kdbus_kmsg *kmsg; long ret = 0; if (conn && conn->ep) @@ -444,33 +749,72 @@ static long kdbus_conn_ioctl_ep(struct file *file, unsigned int cmd, case KDBUS_CMD_HELLO: { /* turn this fd into a connection. */ - struct kdbus_cmd_hello hello; + const struct kdbus_item *item; + size_t size; + void *v; if (conn->active) { ret = -EISCONN; break; } - if (copy_from_user(&hello, buf, sizeof(hello))) { + if (kdbus_size_get_user(size, buf, struct kdbus_cmd_hello)) { ret = -EFAULT; break; } - if (!check_flags(hello.conn_flags)) { + if (size < sizeof(struct kdbus_cmd_hello) || size > KDBUS_HELLO_MAX_SIZE) { + ret = -EMSGSIZE; + break; + } + + v = memdup_user(buf, size); + if (IS_ERR(v)) { + ret = PTR_ERR(v); + break; + } + hello = v; + + if (!check_flags(hello->conn_flags)) { ret = -ENOTSUPP; break; } - conn->flags = hello.conn_flags; - hello.bus_flags = bus->bus_flags; - hello.bloom_size = bus->bloom_size; - hello.id = conn->id; + KDBUS_ITEM_FOREACH_VALIDATE(item, hello) { + /* empty data records are invalid */ + if (item->size <= KDBUS_ITEM_HEADER_SIZE) { + ret = -EINVAL; + break; + } - if (copy_to_user(buf, &hello, sizeof(hello))) { + switch (item->type) { + case KDBUS_HELLO_BUFFER: + /* enforce page alignment and page granularity */ + if (!KDBUS_IS_ALIGNED_PAGE(item->vec.address) || + !KDBUS_IS_ALIGNED_PAGE(item->vec.size)) { + ret = -EFAULT; + break; + } + + conn->buffer.buf = (void *)(uintptr_t)item->vec.address; + conn->buffer.size = item->vec.size; + break; + + default: + ret = -ENOTSUPP; + } + } + + /* return properties of this connection to the caller */ + hello->bus_flags = bus->bus_flags; + hello->bloom_size = bus->bloom_size; + hello->id = conn->id; + if (copy_to_user(buf, hello, sizeof(struct kdbus_cmd_hello))) { ret = -EFAULT; break; } + conn->flags = hello->conn_flags; conn->active = true; break; @@ -484,43 +828,36 @@ static long kdbus_conn_ioctl_ep(struct file *file, unsigned int cmd, return -ENOMEM; ret = kdbus_cmd_policy_set_from_user(conn->ep->policy_db, buf); - break; case KDBUS_CMD_NAME_ACQUIRE: /* acquire a well-known name */ ret = kdbus_cmd_name_acquire(bus->name_registry, conn, buf); - break; case KDBUS_CMD_NAME_RELEASE: /* release a well-known name */ ret = kdbus_cmd_name_release(bus->name_registry, conn, buf); - break; case KDBUS_CMD_NAME_LIST: /* return all current well-known names */ ret = kdbus_cmd_name_list(bus->name_registry, conn, buf); - break; case KDBUS_CMD_NAME_QUERY: /* return details about a specific well-known name */ ret = kdbus_cmd_name_query(bus->name_registry, conn, buf); - break; case KDBUS_CMD_MATCH_ADD: /* subscribe to/filter for broadcast messages */ ret = kdbus_cmd_match_db_add(conn, buf); - break; case KDBUS_CMD_MATCH_REMOVE: /* unsubscribe from broadcast messages */ ret = kdbus_cmd_match_db_remove(conn->match_db, buf); - break; case KDBUS_CMD_MONITOR: { @@ -530,34 +867,42 @@ static long kdbus_conn_ioctl_ep(struct file *file, unsigned int cmd, return -EFAULT; conn->monitor = !!cmd_monitor.enabled; - break; } - case KDBUS_CMD_MSG_SEND: - /* send a message */ + case KDBUS_CMD_MSG_SEND: { + struct kdbus_kmsg *kmsg; + + /* submit a message which will be queued in the receiver */ ret = kdbus_kmsg_new_from_user(conn, buf, &kmsg); if (ret < 0) break; ret = kdbus_kmsg_send(conn->ep, conn, kmsg); - kdbus_kmsg_unref(kmsg); - + kdbus_kmsg_free(kmsg); break; + } case KDBUS_CMD_MSG_RECV: - /* receive a message */ - ret = kdbus_kmsg_recv(conn, buf); + /* receive a pointer to a queued message */ + ret = kdbus_conn_recv_msg(conn, buf); + break; + case KDBUS_CMD_MSG_FREE: { + /* cleanup the memory used in the receiver's buffer */ + mutex_lock(&conn->lock); + kdbus_buffer_free(&conn->buffer, buf); + mutex_unlock(&conn->lock); break; + } default: ret = -ENOTTY; - break; } kfree(kmake); + kfree(hello); return ret; } @@ -592,10 +937,10 @@ static unsigned int kdbus_conn_poll(struct file *file, poll_wait(file, &conn->ep->wait, wait); - mutex_lock(&conn->msg_lock); + mutex_lock(&conn->lock); if (!list_empty(&conn->msg_list)) mask |= POLLIN | POLLRDNORM; - mutex_unlock(&conn->msg_lock); + mutex_unlock(&conn->lock); return mask; } diff --git a/connection.h b/connection.h index d2a28287ce3..0df78a145c3 100644 --- a/connection.h +++ b/connection.h @@ -14,6 +14,7 @@ #define __KDBUS_CONNECTION_H #include "internal.h" +#include "buffer.h" /* * kdbus connection @@ -41,7 +42,7 @@ struct kdbus_conn { bool active; /* did the connection say hello yet? */ bool monitor; - struct mutex msg_lock; + struct mutex lock; struct mutex names_lock; struct mutex accounting_lock; struct list_head msg_list; @@ -67,12 +68,43 @@ struct kdbus_conn { u32 sec_label_len; #endif + /* reference to the taks owning the connection */ + struct task_struct *task; + + /* connection accounting */ unsigned int msg_count; size_t allocated_size; + + /* userspace-supplied buffer to fill with message data */ + struct kdbus_buffer buffer; }; -void kdbus_conn_schedule_timeout_scan(struct kdbus_conn *conn); -int kdbus_conn_add_size_allocation(struct kdbus_conn *conn, size_t size); -void kdbus_conn_sub_size_allocation(struct kdbus_conn *conn, size_t size); +struct kdbus_conn_queue { + struct list_head entry; + + /* pointer to message placed in the receiver's buffer */ + struct __user kdbus_msg *msg; + + /* passed file descriptors */ + int __user *fds; + struct file **fds_fp; + unsigned int fds_count; + + /* timeout in the queue */ + u64 deadline_ns; + u64 src_id; + u64 cookie; + bool expect_reply; +}; + +struct kdbus_kmsg; + +void kdbus_conn_timeout_schedule_scan(struct kdbus_conn *conn); + +int kdbus_conn_accounting_add_size(struct kdbus_conn *conn, size_t size); +void kdbus_conn_accounting_sub_size(struct kdbus_conn *conn, size_t size); +void kdbus_conn_queue_cleanup(struct kdbus_conn_queue *queue); +int kdbus_conn_queue_insert(struct kdbus_conn *conn, struct kdbus_kmsg *kmsg, + u64 deadline_ns); #endif diff --git a/internal.h b/internal.h index 61a56885666..4cc3ab22b99 100644 --- a/internal.h +++ b/internal.h @@ -20,15 +20,14 @@ #define KDBUS_MSG_MAX_FDS 256 /* maximum number of passed file descriptors */ #define KDBUS_MSG_MAX_PAYLOAD_VECS 64 /* maximum number of payload references */ #define KDBUS_MSG_MAX_PAYLOAD_SIZE SZ_128M /* maximum message payload size */ -#define KDBUS_MSG_MAX_INLINE_SIZE 2 * PAGE_SIZE /* threshold when to use the external buffer */ #define KDBUS_NAME_MAX_LEN 255 /* maximum length of well-known bus name */ #define KDBUS_MAKE_MAX_LEN 63 /* maximum length of bus, ns, ep name */ #define KDBUS_MAKE_MAX_SIZE SZ_32K /* maximum size of make buffer */ +#define KDBUS_HELLO_MAX_SIZE SZ_32K /* maximum size of hello buffer */ #define KDBUS_MATCH_MAX_SIZE SZ_32K /* maximum size of match buffer */ - #define KDBUS_POLICY_MAX_SIZE SZ_32K /* maximum size of policy buffer */ #define KDBUS_CONN_MAX_MSGS 64 /* maximum number of queued messages on the bus */ @@ -36,45 +35,45 @@ #define KDBUS_CHAR_MAJOR 222 /* FIXME: move to uapi/linux/major.h */ +#define KDBUS_ALIGN8(s) ALIGN((s), 8) #define KDBUS_IS_ALIGNED8(s) (IS_ALIGNED(s, 8)) #define KDBUS_IS_ALIGNED_PAGE(s) (IS_ALIGNED(s, PAGE_SIZE)) -#define KDBUS_ALIGN8(s) ALIGN((s), 8) -#define KDBUS_ALIGN_PAGE(s) ALIGN((s), PAGE_SIZE) +#define KDBUS_ITEM_HEADER_SIZE offsetof(struct kdbus_item, data) +#define KDBUS_ITEM_SIZE(s) KDBUS_ALIGN8((s) + KDBUS_ITEM_HEADER_SIZE) #define KDBUS_ITEM_NEXT(item) \ (typeof(item))(((u8 *)item) + KDBUS_ALIGN8((item)->size)) #define KDBUS_ITEM_FOREACH(item, head) \ for (item = (head)->items; \ (u8 *)(item) < (u8 *)(head) + (head)->size; \ item = KDBUS_ITEM_NEXT(item)) - /* same iterator with more consistency checks, to be used with incoming data */ -#define KDBUS_ITEM_HEADER_SIZE offsetof(struct kdbus_item, data) -#define KDBUS_ITEM_SIZE(s) KDBUS_ALIGN8((s) + KDBUS_ITEM_HEADER_SIZE) #define KDBUS_ITEM_FOREACH_VALIDATE(item, head) \ for (item = (head)->items; \ (u8 *)(item) + KDBUS_ITEM_HEADER_SIZE <= (u8 *)(head) + (head)->size && \ (u8 *)(item) + (item)->size <= (u8 *)(head) + (head)->size; \ item = KDBUS_ITEM_NEXT(item)) +#define KDBUS_MSG_HEADER_SIZE offsetof(struct kdbus_msg, items) + /* some architectures miss get_user_8(); copy only the lower 32bit */ #ifdef CONFIG_64BIT #define kdbus_size_get_user(_s, _b, _t) \ ({ \ - u64 __user *_sz = _b + offsetof(typeof(_t), size); \ + u64 __user *_sz = (void *)(_b) + offsetof(typeof(_t), size); \ get_user(_s, _sz); \ }) #else #ifdef __LITTLE_ENDIAN__ #define kdbus_size_get_user(_s, _b, _t) \ ({ \ - u32 __user *_sz = _b + offsetof(typeof(_t), size); \ + u32 __user *_sz = (void *)(_b) + offsetof(typeof(_t), size); \ get_user(_s, _sz); \ }) #else #define kdbus_size_get_user(_s, _b, _t) \ ({ \ - u32 __user *_sz = _b + sizeof(u32) + offsetof(typeof(_t), size); \ + u32 __user *_sz = (void *)(_b) + sizeof(u32) + offsetof(typeof(_t), size); \ get_user(_s, _sz); \ }) #endif @@ -70,14 +70,15 @@ enum { KDBUS_MSG_NULL, /* Filled in by userspace */ - KDBUS_MSG_PAYLOAD, /* .data, inline memory */ KDBUS_MSG_PAYLOAD_VEC, /* .data_vec, reference to memory area */ - KDBUS_MSG_UNIX_FDS, /* .data_fds of file descriptors */ + KDBUS_MSG_PAYLOAD_FD, /* .data_fd, reference to a data file descriptor */ + KDBUS_MSG_FDS, /* .data_fds of file descriptors */ KDBUS_MSG_BLOOM, /* for broadcasts, carries bloom filter blob in .data */ KDBUS_MSG_DST_NAME, /* destination's well-known name, in .str */ + KDBUS_MSG_PRIORITY, /* queue priority for message */ /* Filled in by kernelspace */ - KDBUS_MSG_SRC_NAMES = 0x200,/* NUL separated string list with well-known names of source */ + KDBUS_MSG_SRC_NAMES = 0x400,/* NUL separated string list with well-known names of source */ KDBUS_MSG_TIMESTAMP, /* .timestamp */ KDBUS_MSG_SRC_CREDS, /* .creds */ KDBUS_MSG_SRC_PID_COMM, /* optional, in .str */ @@ -90,7 +91,7 @@ enum { KDBUS_MSG_SRC_AUDIT, /* .audit */ /* Special messages from kernel, consisting of one and only one of these data blocks */ - KDBUS_MSG_NAME_ADD = 0x400,/* .name_change */ + KDBUS_MSG_NAME_ADD = 0x800,/* .name_change */ KDBUS_MSG_NAME_REMOVE, /* .name_change */ KDBUS_MSG_NAME_CHANGE, /* .name_change */ KDBUS_MSG_ID_ADD, /* .id_change */ @@ -99,14 +100,9 @@ enum { KDBUS_MSG_REPLY_DEAD, /* dito */ }; -enum { - KDBUS_VEC_ALIGNED = 1 << 0, -}; - struct kdbus_vec { __u64 address; __u64 size; - __u64 flags; }; /** @@ -236,6 +232,8 @@ enum { /* Items to append to struct kdbus_cmd_hello */ enum { KDBUS_HELLO_NULL, + KDBUS_HELLO_BUFFER, /* kdbus_vec, userspace supplied buffer to + * place received messages */ }; struct kdbus_cmd_hello { @@ -401,7 +399,8 @@ enum kdbus_cmd { /* kdbus ep node commands: require connected state */ KDBUS_CMD_MSG_SEND = _IOWR(KDBUS_IOC_MAGIC, 0x40, struct kdbus_msg), - KDBUS_CMD_MSG_RECV = _IOWR(KDBUS_IOC_MAGIC, 0x41, struct kdbus_msg), + KDBUS_CMD_MSG_RECV = _IOWR(KDBUS_IOC_MAGIC, 0x41, struct kdbus_msg *), + KDBUS_CMD_MSG_FREE = _IOWR(KDBUS_IOC_MAGIC, 0x42, struct kdbus_msg), KDBUS_CMD_NAME_ACQUIRE = _IOWR(KDBUS_IOC_MAGIC, 0x50, struct kdbus_cmd_name), KDBUS_CMD_NAME_RELEASE = _IOWR(KDBUS_IOC_MAGIC, 0x51, struct kdbus_cmd_name), @@ -149,12 +149,7 @@ bool kdbus_match_db_match_with_src(struct kdbus_match_db *db, struct kdbus_conn *conn_src, struct kdbus_kmsg *kmsg) { - const struct kdbus_item *bloom = - kdbus_msg_get_item(&kmsg->msg, KDBUS_MSG_BLOOM, 0); - const struct kdbus_item *src_names = - kdbus_msg_get_item(&kmsg->msg, KDBUS_MSG_SRC_NAMES, 0); struct kdbus_match_db_entry *e; - size_t bloom_size = conn_src->ep->bus->bloom_size / sizeof(u64); bool matched = false; mutex_lock(&db->entries_lock); @@ -168,19 +163,22 @@ bool kdbus_match_db_match_with_src(struct kdbus_match_db *db, matched = true; list_for_each_entry(ei, &e->items_list, list_entry) { - if (bloom && ei->type == KDBUS_MATCH_BLOOM) + if (kmsg->bloom) { + size_t bloom_size = + conn_src->ep->bus->bloom_size / sizeof(u64); + if (!kdbus_match_db_test_bloom(ei->bloom, - bloom->data64, + kmsg->bloom, bloom_size)) { matched = false; break; } + } - if (src_names && ei->type == KDBUS_MATCH_SRC_NAME) { - size_t nlen = src_names->size - KDBUS_ITEM_HEADER_SIZE; - - if (!kdbus_match_db_test_src_names(src_names->str, - nlen, ei->name)) { + if (kmsg->src_names) { + if (!kdbus_match_db_test_src_names(kmsg->src_names, + kmsg->src_names_len, + ei->name)) { matched = false; break; } diff --git a/message.c b/message.c index e87362a0b3c..616146bedbe 100644 --- a/message.c +++ b/message.c @@ -15,13 +15,12 @@ #include <linux/module.h> #include <linux/device.h> #include <linux/idr.h> -#include <linux/fs.h> #include <linux/slab.h> +#include <linux/file.h> #include <linux/sched.h> #include <linux/mutex.h> #include <linux/init.h> #include <linux/poll.h> -#include <linux/file.h> #include <linux/cgroup.h> #include <linux/cred.h> #include <linux/capability.h> @@ -35,61 +34,32 @@ #include "names.h" #include "match.h" -#define KDBUS_MSG_HEADER_SIZE offsetof(struct kdbus_msg, items) #define KDBUS_KMSG_HEADER_SIZE offsetof(struct kdbus_kmsg, msg) -static void kdbus_msg_dump(const struct kdbus_msg *msg); - -static void kdbus_kmsg_free(struct kdbus_kmsg *kmsg) -{ - size_t size = 0; - - if (kmsg->fds_fp) { - unsigned int i; - - for (i = 0; i < kmsg->fds_count; i++) - fput(kmsg->fds_fp[i]); - size += kmsg->fds_count * sizeof(struct file *); - kfree(kmsg->fds_fp); - } - - if (kmsg->fds) { - size += KDBUS_ITEM_HEADER_SIZE + (kmsg->fds_count * sizeof(int)); - kfree(kmsg->fds); - } - - if (kmsg->meta) { - size += kmsg->meta_allocated_size; - kfree(kmsg->meta); - } - - if (kmsg->vecs) { - size += kmsg->vecs_size; - kfree(kmsg->vecs); - } - - size += kmsg->msg.size + KDBUS_KMSG_HEADER_SIZE; - kdbus_conn_sub_size_allocation(kmsg->conn_src, size); - - kfree(kmsg); -} - -static void __kdbus_kmsg_free(struct kref *kref) +static void __maybe_unused kdbus_msg_dump(const struct kdbus_msg *msg) { - struct kdbus_kmsg *kmsg = container_of(kref, struct kdbus_kmsg, kref); + const struct kdbus_item *item; - return kdbus_kmsg_free(kmsg); -} + pr_debug("msg size=%llu, flags=0x%llx, dst_id=%llu, src_id=%llu, " + "cookie=0x%llx payload_type=0x%llx, timeout=%llu\n", + (unsigned long long) msg->size, + (unsigned long long) msg->flags, + (unsigned long long) msg->dst_id, + (unsigned long long) msg->src_id, + (unsigned long long) msg->cookie, + (unsigned long long) msg->payload_type, + (unsigned long long) msg->timeout_ns); -void kdbus_kmsg_unref(struct kdbus_kmsg *kmsg) -{ - kref_put(&kmsg->kref, __kdbus_kmsg_free); + KDBUS_ITEM_FOREACH(item, msg) + pr_debug("`- msg_item size=%llu, type=0x%llx\n", + item->size, item->type); } -static struct kdbus_kmsg *kdbus_kmsg_ref(struct kdbus_kmsg *kmsg) +void kdbus_kmsg_free(struct kdbus_kmsg *kmsg) { - kref_get(&kmsg->kref); - return kmsg; + if (kmsg->meta) + kfree(kmsg->meta); + kfree(kmsg); } int kdbus_kmsg_new(size_t extra_size, struct kdbus_kmsg **m) @@ -116,9 +86,8 @@ static int kdbus_msg_scan_items(struct kdbus_conn *conn, struct kdbus_kmsg *kmsg const struct kdbus_item *item; unsigned int num_items = 0; unsigned int num_vecs = 0; - unsigned int num_fds = 0; + unsigned int fds_count = 0; size_t vecs_size = 0; - bool needs_vec = false; bool has_fds = false; bool has_name = false; bool has_bloom = false; @@ -132,9 +101,6 @@ static int kdbus_msg_scan_items(struct kdbus_conn *conn, struct kdbus_kmsg *kmsg return -E2BIG; switch (item->type) { - case KDBUS_MSG_PAYLOAD: - break; - case KDBUS_MSG_PAYLOAD_VEC: if (item->size != KDBUS_ITEM_HEADER_SIZE + sizeof(struct kdbus_vec)) return -EINVAL; @@ -142,22 +108,12 @@ static int kdbus_msg_scan_items(struct kdbus_conn *conn, struct kdbus_kmsg *kmsg if (++num_vecs > KDBUS_MSG_MAX_PAYLOAD_VECS) return -E2BIG; - if (item->vec.flags & KDBUS_VEC_ALIGNED) { - /* enforce page alignment and page granularity */ - if (!KDBUS_IS_ALIGNED_PAGE(item->vec.address) || - !KDBUS_IS_ALIGNED_PAGE(item->vec.size)) - return -EFAULT; - - /* we always deliver aligned data as PAYLOAD_VEC */ - needs_vec = true; - } - - vecs_size += KDBUS_ALIGN8(item->vec.size); + vecs_size += item->vec.size; if (vecs_size > KDBUS_MSG_MAX_PAYLOAD_SIZE) return -EMSGSIZE; break; - case KDBUS_MSG_UNIX_FDS: + case KDBUS_MSG_FDS: /* do not allow multiple fd arrays */ if (has_fds) return -EEXIST; @@ -167,9 +123,12 @@ static int kdbus_msg_scan_items(struct kdbus_conn *conn, struct kdbus_kmsg *kmsg if (msg->dst_id == KDBUS_DST_ID_BROADCAST) return -ENOTUNIQ; - num_fds = (item->size - KDBUS_ITEM_HEADER_SIZE) / sizeof(int); - if (num_fds > KDBUS_MSG_MAX_FDS) + fds_count = (item->size - KDBUS_ITEM_HEADER_SIZE) / sizeof(int); + if (fds_count > KDBUS_MSG_MAX_FDS) return -EMFILE; + + kmsg->fds = item->fds; + kmsg->fds_count = fds_count; break; case KDBUS_MSG_BLOOM: @@ -189,6 +148,8 @@ static int kdbus_msg_scan_items(struct kdbus_conn *conn, struct kdbus_kmsg *kmsg /* do not allow mismatching bloom filter sizes */ if (item->size - KDBUS_ITEM_HEADER_SIZE != conn->ep->bus->bloom_size) return -EDOM; + + kmsg->bloom = item->data64; break; case KDBUS_MSG_DST_NAME: @@ -203,6 +164,8 @@ static int kdbus_msg_scan_items(struct kdbus_conn *conn, struct kdbus_kmsg *kmsg if (!kdbus_name_is_valid(item->str)) return -EINVAL; + + kmsg->dst_name = item->str; break; default: @@ -214,7 +177,7 @@ static int kdbus_msg_scan_items(struct kdbus_conn *conn, struct kdbus_kmsg *kmsg if ((char *)item - ((char *)msg + msg->size) >= 8) return -EINVAL; - /* name is needed for broadcast */ + /* name is needed if no ID is given */ if (msg->dst_id == KDBUS_DST_ID_WELL_KNOWN_NAME && !has_name) return -EDESTADDRREQ; @@ -223,128 +186,36 @@ static int kdbus_msg_scan_items(struct kdbus_conn *conn, struct kdbus_kmsg *kmsg msg->dst_id < KDBUS_DST_ID_BROADCAST && has_name) return -EBADMSG; - /* broadcast messages require a bloom filter */ - if (msg->dst_id == KDBUS_DST_ID_BROADCAST && !has_bloom) - return -EBADMSG; + if (msg->dst_id == KDBUS_DST_ID_BROADCAST) { + /* broadcast messages require a bloom filter */ + if (!has_bloom) + return -EBADMSG; + + /* timeouts are not allowed for broadcasts */ + if (msg->timeout_ns) + return -ENOTUNIQ; + } /* bloom filters are for undirected messages only */ if (has_name && has_bloom) return -EBADMSG; - /* allocate array for file descriptors */ - if (has_fds) { - size_t size; - unsigned int i; - int ret; - - size = num_fds * sizeof(struct file *); - ret = kdbus_conn_add_size_allocation(conn, size); - if (ret < 0) - return ret; - - kmsg->fds_fp = kzalloc(size, GFP_KERNEL); - if (!kmsg->fds_fp) - return -ENOMEM; - - size = KDBUS_ITEM_HEADER_SIZE + (num_fds * sizeof(int)); - ret = kdbus_conn_add_size_allocation(conn, size); - if (ret < 0) - return ret; - - kmsg->fds = kmalloc(size, GFP_KERNEL); - if (!kmsg->fds) - return -ENOMEM; - for (i = 0; i < num_fds; i++) - kmsg->fds->fds[i] = -1; - } - - /* if we have only very small PAYLOAD_VECs, they get inlined */ - if (num_vecs > 0 && !needs_vec && - msg->size + vecs_size < KDBUS_MSG_MAX_INLINE_SIZE) { - size_t size; - int ret; - - size = (num_vecs * KDBUS_ITEM_HEADER_SIZE) + vecs_size; - ret = kdbus_conn_add_size_allocation(conn, size); - if (ret < 0) - return ret; - - kmsg->vecs = kzalloc(size, GFP_KERNEL); - if (!kmsg->vecs) - return -ENOMEM; - kmsg->vecs_size = size; - } - - return 0; -} - -static int kdbus_inline_user_vec(struct kdbus_kmsg *kmsg, - struct kdbus_item *next, - const struct kdbus_item *item) -{ - void __user *user_addr; - - user_addr = (void __user *)(uintptr_t)item->vec.address; - if (copy_from_user(next->data, user_addr, item->vec.size)) - return -EFAULT; - - next->type = KDBUS_MSG_PAYLOAD; - next->size = KDBUS_ITEM_HEADER_SIZE + item->vec.size; - + kmsg->vecs_size = vecs_size; return 0; } -/* - * Grab and keep references to passed files descriptors, to install - * them in the receiving process at message delivery. - */ -static int kdbus_copy_user_fds(struct kdbus_kmsg *kmsg, - const struct kdbus_item *item) -{ - unsigned int i; - unsigned int count; - - count = (item->size - KDBUS_ITEM_HEADER_SIZE) / sizeof(int); - for (i = 0; i < count; i++) { - struct file *fp; - - fp = fget(item->fds[i]); - if (!fp) - goto unwind; - - kmsg->fds_fp[kmsg->fds_count++] = fp; - } - - return 0; - -unwind: - while (i >= 0) { - fput(kmsg->fds_fp[i]); - kmsg->fds_fp[i] = NULL; - i--; - } - - kmsg->fds_count = 0; - return -EBADF; -} - -/* - * Check the validity of a message. The general layout of the received message - * is not altered before it is delivered. - */ -int kdbus_kmsg_new_from_user(struct kdbus_conn *conn, void __user *buf, +int kdbus_kmsg_new_from_user(struct kdbus_conn *conn, + struct kdbus_msg __user *msg, struct kdbus_kmsg **m) { struct kdbus_kmsg *kmsg; - const struct kdbus_item *item; - struct kdbus_item *vecs_next; u64 size, alloc_size; int ret; - if (!KDBUS_IS_ALIGNED8((unsigned long)buf)) + if (!KDBUS_IS_ALIGNED8((unsigned long)msg)) return -EFAULT; - if (kdbus_size_get_user(size, buf, struct kdbus_msg)) + if (kdbus_size_get_user(size, msg, struct kdbus_msg)) return -EFAULT; if (size < sizeof(struct kdbus_msg) || size > KDBUS_MSG_MAX_SIZE) @@ -357,61 +228,20 @@ int kdbus_kmsg_new_from_user(struct kdbus_conn *conn, void __user *buf, return -ENOMEM; memset(kmsg, 0, KDBUS_KMSG_HEADER_SIZE); - ret = kdbus_conn_add_size_allocation(conn, alloc_size); - if (ret < 0) { - kfree(kmsg); - return ret; - } - - if (copy_from_user(&kmsg->msg, buf, size)) { + if (copy_from_user(&kmsg->msg, msg, size)) { ret = -EFAULT; goto exit_free; } - /* check validity and prepare handling of data records */ + /* check validity and gather some values for processing */ ret = kdbus_msg_scan_items(conn, kmsg); if (ret < 0) goto exit_free; - /* fill in sender ID */ + /* patch-in the source of this message */ kmsg->msg.src_id = conn->id; - /* keep a reference to the source connection, for accounting */ - kmsg->conn_src = conn; - - /* Iterate over the items, resolve external references to data - * we need to pass to the receiver; ignore all items used by - * the sender only. */ - vecs_next = kmsg->vecs; - KDBUS_ITEM_FOREACH(item, &kmsg->msg) { - switch (item->type) { - case KDBUS_MSG_PAYLOAD_VEC: - if (!kmsg->vecs) { - ret = -ENOSYS; - goto exit_free; - } - - /* convert PAYLOAD_VEC to PAYLOAD */ - ret = kdbus_inline_user_vec(kmsg, vecs_next, item); - if (ret < 0) - goto exit_free; - vecs_next = KDBUS_ITEM_NEXT(vecs_next); - break; - - case KDBUS_MSG_UNIX_FDS: - ret = kdbus_copy_user_fds(kmsg, item); - if (ret < 0) - goto exit_free; - break; - - case KDBUS_MSG_BLOOM: - //FIXME: store in kmsg - break; - } - } - kref_init(&kmsg->kref); - *m = kmsg; return 0; @@ -420,51 +250,15 @@ exit_free: return ret; } -const struct kdbus_item * -kdbus_msg_get_item(const struct kdbus_msg *msg, u64 type, unsigned int index) -{ - const struct kdbus_item *item; - - KDBUS_ITEM_FOREACH(item, msg) - if (item->type == type && index-- == 0) - return item; - - return NULL; -} - -static void __maybe_unused kdbus_msg_dump(const struct kdbus_msg *msg) -{ - const struct kdbus_item *item; - - pr_debug("msg size=%llu, flags=0x%llx, dst_id=%llu, src_id=%llu, " - "cookie=0x%llx payload_type=0x%llx, timeout=%llu\n", - (unsigned long long) msg->size, - (unsigned long long) msg->flags, - (unsigned long long) msg->dst_id, - (unsigned long long) msg->src_id, - (unsigned long long) msg->cookie, - (unsigned long long) msg->payload_type, - (unsigned long long) msg->timeout_ns); - - KDBUS_ITEM_FOREACH(item, msg) - pr_debug("`- msg_item size=%llu, type=0x%llx\n", - item->size, item->type); -} - static struct kdbus_item * kdbus_kmsg_append(struct kdbus_kmsg *kmsg, size_t extra_size) { struct kdbus_item *item; size_t size; - int ret; /* get new metadata buffer, pre-allocate at least 512 bytes */ if (!kmsg->meta) { size = roundup_pow_of_two(256 + KDBUS_ALIGN8(extra_size)); - ret = kdbus_conn_add_size_allocation(kmsg->conn_src, size); - if (ret < 0) - return ERR_PTR(ret); - kmsg->meta = kzalloc(size, GFP_KERNEL); if (!kmsg->meta) return ERR_PTR(-ENOMEM); @@ -480,11 +274,6 @@ kdbus_kmsg_append(struct kdbus_kmsg *kmsg, size_t extra_size) size = roundup_pow_of_two(size); size_diff = size - kmsg->meta_allocated_size; - - ret = kdbus_conn_add_size_allocation(kmsg->conn_src, size_diff); - if (ret < 0) - return ERR_PTR(ret); - pr_debug("%s: grow to size=%zu\n", __func__, size); meta = kmalloc(size, GFP_KERNEL); if (!meta) @@ -589,6 +378,9 @@ static int kdbus_kmsg_append_src_names(struct kdbus_kmsg *kmsg, pos += strlen(name_entry->name) + 1; } + kmsg->src_names = item->data; + kmsg->src_names_len = pos; + exit_unlock: mutex_unlock(&conn->names_lock); @@ -612,40 +404,6 @@ static int kdbus_kmsg_append_cred(struct kdbus_kmsg *kmsg, return 0; } -static int kdbus_conn_enqueue_kmsg(struct kdbus_conn *conn, - struct kdbus_kmsg *kmsg) -{ - struct kdbus_msg_list_entry *entry; - int ret = 0; - - if (!conn->active) - return -ENOTCONN; - - if (kmsg->fds && !(conn->flags & KDBUS_HELLO_ACCEPT_FD)) - return -ECOMM; - - entry = kzalloc(sizeof(*entry), GFP_KERNEL); - if (!entry) - return -ENOMEM; - - entry->kmsg = kdbus_kmsg_ref(kmsg); - INIT_LIST_HEAD(&entry->entry); - - mutex_lock(&conn->msg_lock); - if (conn->msg_count > KDBUS_CONN_MAX_MSGS) { - ret = -EXFULL; - } else { - list_add_tail(&entry->entry, &conn->msg_list); - conn->msg_count++; - } - mutex_unlock(&conn->msg_lock); - - if (ret == 0) - wake_up_interruptible(&conn->ep->wait); - - return ret; -} - /* * FIXME: dirty and unsafe version of: * http://git.kernel.org/cgit/linux/kernel/git/tj/cgroup.git/commit/?h=review-task_cgroup_path_from_hierarchy @@ -686,7 +444,7 @@ int task_cgroup_path_from_hierarchy(struct task_struct *task, int hierarchy_id, return ret; } -static int kdbus_msg_append_for_dst(struct kdbus_kmsg *kmsg, +static int kdbus_kmsg_append_for_dst(struct kdbus_kmsg *kmsg, struct kdbus_conn *conn_src, struct kdbus_conn *conn_dst) { @@ -855,6 +613,7 @@ int kdbus_kmsg_send(struct kdbus_ep *ep, struct kdbus_conn *conn_dst = NULL; const struct kdbus_msg *msg; u64 now_ns = 0; + u64 deadline_ns = 0; int ret; /* augment incoming message */ @@ -873,70 +632,9 @@ int kdbus_kmsg_send(struct kdbus_ep *ep, } msg = &kmsg->msg; -// kdbus_msg_dump(msg); - - if (msg->dst_id == KDBUS_DST_ID_WELL_KNOWN_NAME) { - const struct kdbus_item *name_item; - const struct kdbus_name_entry *name_entry; - - name_item = kdbus_msg_get_item(msg, KDBUS_MSG_DST_NAME, 0); - if (!name_item) - return -EDESTADDRREQ; - - /* lookup and determine conn_dst ... */ - name_entry = kdbus_name_lookup(ep->bus->name_registry, - name_item->data); - if (!name_entry) - return -ESRCH; - - conn_dst = name_entry->conn; - - if ((msg->flags & KDBUS_MSG_FLAGS_NO_AUTO_START) && - (conn_dst->flags & KDBUS_HELLO_STARTER)) - return -EADDRNOTAVAIL; - - } else if (msg->dst_id != KDBUS_DST_ID_BROADCAST) { - /* direct message */ - conn_dst = kdbus_bus_find_conn_by_id(ep->bus, msg->dst_id); - if (!conn_dst) - return -ENXIO; - } - - if (conn_dst) { - /* direct message */ - - if (msg->timeout_ns) - kmsg->deadline_ns = now_ns + msg->timeout_ns; - - /* check policy */ - if (ep->policy_db && conn_src) { - ret = kdbus_policy_db_check_send_access(ep->policy_db, - conn_src, - conn_dst, - kmsg->deadline_ns); - if (ret < 0) - return ret; - } - - /* direct message */ - if (conn_src) { - ret = kdbus_msg_append_for_dst(kmsg, conn_src, conn_dst); - if (ret < 0) - return ret; - } - - ret = kdbus_conn_enqueue_kmsg(conn_dst, kmsg); - - if (msg->timeout_ns) - kdbus_conn_schedule_timeout_scan(conn_dst); - } else { - /* broadcast */ - /* timeouts are not allowed for broadcasts */ - if (msg->timeout_ns) - return -ENOTUNIQ; - - ret = 0; + /* broadcast message */ + if (msg->dst_id == KDBUS_DST_ID_BROADCAST) { list_for_each_entry(conn_dst, &ep->connection_list, connection_entry) { if (conn_dst->type != KDBUS_CONN_EP) @@ -954,170 +652,56 @@ int kdbus_kmsg_send(struct kdbus_ep *ep, kmsg)) continue; - ret = kdbus_conn_enqueue_kmsg(conn_dst, kmsg); + ret = kdbus_conn_queue_insert(conn_dst, kmsg, 0); if (ret < 0) break; } - } - - return ret; -} - -int kdbus_kmsg_recv(struct kdbus_conn *conn, void __user *buf) -{ - struct kdbus_msg_list_entry *entry; - const struct kdbus_kmsg *kmsg = NULL; - const struct kdbus_msg *msg; - const struct kdbus_item *item; - const struct kdbus_item *vecs_next; - u64 size, pos, max_size; - int ret; - if (!KDBUS_IS_ALIGNED8((unsigned long)buf)) - return -EFAULT; - - if (kdbus_size_get_user(size, buf, struct kdbus_msg)) - return -EFAULT; - - mutex_lock(&conn->msg_lock); - if (conn->msg_count == 0) { - ret = -EAGAIN; - goto exit_unlock; - } - - entry = list_first_entry(&conn->msg_list, struct kdbus_msg_list_entry, entry); - kmsg = entry->kmsg; - msg = &kmsg->msg; - - max_size = msg->size; - - if (kmsg->meta) - max_size += kmsg->meta->size; - - if (kmsg->vecs) - max_size += kmsg->vecs_size; - - /* reuturn needed buffer size to the receiver */ - if (size < max_size) { - kdbus_size_set_user(max_size, buf, struct kdbus_msg); - ret = -ENOBUFS; - goto exit_unlock; - } - - /* copy the message header */ - if (copy_to_user(buf, msg, KDBUS_MSG_HEADER_SIZE)) { - ret = -EFAULT; - goto exit_unlock; + return ret; } - pos = KDBUS_MSG_HEADER_SIZE; - - /* The order and sequence of PAYLOAD and PAYLOAD_VEC is always - * preserved, it might have meaning in the sender/receiver contract; - * one type is freely concerted to the other though, depending - * on the actual copying strategy */ - vecs_next = kmsg->vecs; - KDBUS_ITEM_FOREACH(item, msg) { - switch (item->type) { - case KDBUS_MSG_PAYLOAD: - if (copy_to_user(buf + pos, item, item->size)) { - ret = -EFAULT; - goto exit_unlock; - } - - pos += KDBUS_ALIGN8(item->size); - break; + /* direct message */ + if (msg->dst_id == KDBUS_DST_ID_WELL_KNOWN_NAME) { + const struct kdbus_name_entry *name_entry; - case KDBUS_MSG_PAYLOAD_VEC: - if (!kmsg->vecs) { - /* copy PAYLOAD_VEC from the sender to the receiver */ - ret = -ENOSYS; - goto exit_unlock; - break; - } + name_entry = kdbus_name_lookup(ep->bus->name_registry, + kmsg->dst_name); + if (!name_entry) + return -ESRCH; + conn_dst = name_entry->conn; - /* copy PAYLOAD_VEC we converted to PAYLOAD */ - if (copy_to_user(buf + pos, vecs_next, vecs_next->size)) { - ret = -EFAULT; - goto exit_unlock; - } + if ((msg->flags & KDBUS_MSG_FLAGS_NO_AUTO_START) && + (conn_dst->flags & KDBUS_HELLO_STARTER)) + return -EADDRNOTAVAIL; - pos += KDBUS_ALIGN8(vecs_next->size); - vecs_next = KDBUS_ITEM_NEXT(vecs_next); - break; - } + } else { + conn_dst = kdbus_bus_find_conn_by_id(ep->bus, msg->dst_id); + if (!conn_dst) + return -ENXIO; } - /* install file descriptors */ - if (kmsg->fds) { - unsigned int i; - size_t size; - - for (i = 0; i < kmsg->fds_count; i++) { - int fd; - - fd = get_unused_fd(); - if (fd < 0) { - ret = fd; - goto exit_unlock_fds; - } - - fd_install(fd, get_file(kmsg->fds_fp[i])); - kmsg->fds->fds[i] = fd; - } + if (msg->timeout_ns) + deadline_ns = now_ns + msg->timeout_ns; - size = KDBUS_ITEM_HEADER_SIZE + (sizeof(int) * kmsg->fds_count); - kmsg->fds->size = size; - kmsg->fds->type = KDBUS_MSG_UNIX_FDS; - - if (copy_to_user(buf + pos, kmsg->fds, size)) { - ret = -EFAULT; - goto exit_unlock_fds; - } - - pos += KDBUS_ALIGN8(size); + if (ep->policy_db && conn_src) { + ret = kdbus_policy_db_check_send_access(ep->policy_db, + conn_src, + conn_dst, + deadline_ns); + if (ret < 0) + return ret; } - /* append metadata records */ - if (kmsg->meta) { - if (copy_to_user(buf + pos, kmsg->meta, kmsg->meta_size)) { - ret = -EFAULT; - goto exit_unlock_fds; - } - - pos += KDBUS_ALIGN8(kmsg->meta_size); + if (conn_src) { + ret = kdbus_kmsg_append_for_dst(kmsg, conn_src, conn_dst); + if (ret < 0) + return ret; } - /* update the returned data size in the message header */ - ret = kdbus_size_set_user(pos, buf, struct kdbus_msg); - if (ret) - goto exit_unlock_fds; - - conn->msg_count--; - list_del(&entry->entry); - kdbus_kmsg_unref(entry->kmsg); - kfree(entry); - mutex_unlock(&conn->msg_lock); - - return 0; + ret = kdbus_conn_queue_insert(conn_dst, kmsg, deadline_ns); -exit_unlock_fds: - /* cleanup installed file descriptors */ - if (kmsg->fds) { - unsigned int i; - - for (i = 0; i < kmsg->fds_count; i++) { - if (kmsg->fds->fds[i] < 0) - continue; - - fput(kmsg->fds_fp[i]); - put_unused_fd(kmsg->fds->fds[i]); - kmsg->fds->fds[i] = -1; - } - } - -exit_unlock: - mutex_unlock(&conn->msg_lock); + if (msg->timeout_ns) + kdbus_conn_timeout_schedule_scan(conn_dst); return ret; } diff --git a/message.h b/message.h index 196c130b9d7..57d4c166af3 100644 --- a/message.h +++ b/message.h @@ -16,43 +16,33 @@ struct kdbus_kmsg { struct kref kref; - u64 deadline_ns; - /* short-hand for faster match db lookup. */ + /* short-cuts for faster lookup */ u64 notification_type; + const char *dst_name; + const char *src_names; + size_t src_names_len; + const u64 *bloom; + unsigned int bloom_size; + const int *fds; + unsigned int fds_count; /* appended SCM-like metadata */ struct kdbus_item *meta; size_t meta_size; size_t meta_allocated_size; - /* inlined PAYLOAD_VECs */ - struct kdbus_item *vecs; + /* size of PAYLOAD_VECs data */ size_t vecs_size; - /* passed file descriptors */ - struct kdbus_item *fds; - struct file **fds_fp; - unsigned int fds_count; - - struct kdbus_conn *conn_src; struct kdbus_msg msg; }; -struct kdbus_msg_list_entry { - struct kdbus_kmsg *kmsg; - struct list_head entry; -}; - struct kdbus_ep; struct kdbus_conn; int kdbus_kmsg_new(size_t extra_size, struct kdbus_kmsg **m); -int kdbus_kmsg_new_from_user(struct kdbus_conn *conn, void __user *argp, struct kdbus_kmsg **m); -const struct kdbus_item *kdbus_msg_get_item(const struct kdbus_msg *msg, u64 type, unsigned int index); -void kdbus_kmsg_unref(struct kdbus_kmsg *kmsg); -int kdbus_kmsg_send(struct kdbus_ep *ep, - struct kdbus_conn *conn_src, - struct kdbus_kmsg *kmsg); -int kdbus_kmsg_recv(struct kdbus_conn *conn, void __user *buf); +int kdbus_kmsg_new_from_user(struct kdbus_conn *conn, struct kdbus_msg __user *msg, struct kdbus_kmsg **m); +int kdbus_kmsg_send(struct kdbus_ep *ep, struct kdbus_conn *conn_src, struct kdbus_kmsg *kmsg); +void kdbus_kmsg_free(struct kdbus_kmsg *kmsg); #endif @@ -27,17 +27,15 @@ #include "ep.h" #include "message.h" -static int kdbus_notify_reply(struct kdbus_ep *ep, - const struct kdbus_msg *orig_msg, - u64 msg_type) +static int kdbus_notify_reply(struct kdbus_ep *ep, u64 src_id, + u64 cookie, u64 msg_type) { struct kdbus_conn *dst_conn; struct kdbus_kmsg *kmsg; struct kdbus_item *item; - u64 dst_id = orig_msg->src_id; int ret; - dst_conn = kdbus_bus_find_conn_by_id(ep->bus, dst_id); + dst_conn = kdbus_bus_find_conn_by_id(ep->bus, src_id); if (!dst_conn) return -ENXIO; @@ -52,30 +50,28 @@ static int kdbus_notify_reply(struct kdbus_ep *ep, */ kmsg->notification_type = msg_type; - kmsg->msg.dst_id = dst_id; + kmsg->msg.dst_id = src_id; kmsg->msg.src_id = KDBUS_SRC_ID_KERNEL; kmsg->msg.payload_type = KDBUS_PAYLOAD_NULL; - kmsg->msg.cookie_reply = orig_msg->cookie; + kmsg->msg.cookie_reply = cookie; item = kmsg->msg.items; item->type = msg_type; ret = kdbus_kmsg_send(ep, NULL, kmsg); - kdbus_kmsg_unref(kmsg); + kdbus_kmsg_free(kmsg); return ret; } -int kdbus_notify_reply_timeout(struct kdbus_ep *ep, - const struct kdbus_msg *orig_msg) +int kdbus_notify_reply_timeout(struct kdbus_ep *ep, u64 src_id, u64 cookie) { - return kdbus_notify_reply(ep, orig_msg, KDBUS_MSG_REPLY_TIMEOUT); + return kdbus_notify_reply(ep, src_id, cookie, KDBUS_MSG_REPLY_TIMEOUT); } -int kdbus_notify_reply_dead(struct kdbus_ep *ep, - const struct kdbus_msg *orig_msg) +int kdbus_notify_reply_dead(struct kdbus_ep *ep, u64 src_id, u64 cookie) { - return kdbus_notify_reply(ep, orig_msg, KDBUS_MSG_REPLY_DEAD); + return kdbus_notify_reply(ep, src_id, cookie, KDBUS_MSG_REPLY_DEAD); } int kdbus_notify_name_change(struct kdbus_ep *ep, u64 type, @@ -109,7 +105,7 @@ int kdbus_notify_name_change(struct kdbus_ep *ep, u64 type, strcpy(name_change->name, name); ret = kdbus_kmsg_send(ep, NULL, kmsg); - kdbus_kmsg_unref(kmsg); + kdbus_kmsg_free(kmsg); return ret; } @@ -141,7 +137,7 @@ int kdbus_notify_id_change(struct kdbus_ep *ep, u64 type, id_change->flags = flags; ret = kdbus_kmsg_send(ep, NULL, kmsg); - kdbus_kmsg_unref(kmsg); + kdbus_kmsg_free(kmsg); return ret; } @@ -21,8 +21,6 @@ int kdbus_notify_name_change(struct kdbus_ep *ep, u64 type, const char *name); int kdbus_notify_id_change(struct kdbus_ep *ep, u64 type, u64 id, u64 flags); -int kdbus_notify_reply_timeout(struct kdbus_ep *ep, - const struct kdbus_msg *orig_msg); -int kdbus_notify_reply_dead(struct kdbus_ep *ep, - const struct kdbus_msg *orig_msg); +int kdbus_notify_reply_timeout(struct kdbus_ep *ep, u64 src_id, u64 cookie); +int kdbus_notify_reply_dead(struct kdbus_ep *ep, u64 src_id, u64 cookie); #endif diff --git a/test/kdbus-enum.c b/test/kdbus-enum.c index 9dcd8fd27f7..92060d59936 100644 --- a/test/kdbus-enum.c +++ b/test/kdbus-enum.c @@ -55,9 +55,8 @@ LOOKUP(CMD); TABLE(MSG) = { ENUM(KDBUS_MSG_NULL), - ENUM(KDBUS_MSG_PAYLOAD), ENUM(KDBUS_MSG_PAYLOAD_VEC), - ENUM(KDBUS_MSG_UNIX_FDS), + ENUM(KDBUS_MSG_FDS), ENUM(KDBUS_MSG_BLOOM), ENUM(KDBUS_MSG_DST_NAME), ENUM(KDBUS_MSG_SRC_CREDS), diff --git a/test/kdbus-util.c b/test/kdbus-util.c index 8c328a1487e..0cdf73a91e4 100644 --- a/test/kdbus-util.c +++ b/test/kdbus-util.c @@ -21,6 +21,7 @@ #include <assert.h> #include <poll.h> #include <sys/ioctl.h> +#include <sys/mman.h> //#include "include/uapi/kdbus/kdbus.h" #include "../kdbus.h" @@ -31,9 +32,17 @@ struct conn *connect_to_bus(const char *path) { int fd, ret; - struct kdbus_cmd_hello hello; + void *buf; + struct { + struct kdbus_cmd_hello hello; + uint64_t v_size; + uint64_t v_type; + struct kdbus_vec vec; + } h; struct conn *conn; + memset(&h, 0, sizeof(h)); + printf("-- opening bus connection %s\n", path); fd = open(path, O_RDWR|O_CLOEXEC); if (fd < 0) { @@ -41,23 +50,34 @@ struct conn *connect_to_bus(const char *path) return NULL; } - memset(&hello, 0, sizeof(hello)); - - hello.conn_flags = KDBUS_HELLO_ACCEPT_FD | - KDBUS_HELLO_ATTACH_COMM | - KDBUS_HELLO_ATTACH_EXE | - KDBUS_HELLO_ATTACH_CMDLINE | - KDBUS_HELLO_ATTACH_CAPS | - KDBUS_HELLO_ATTACH_CGROUP | - KDBUS_HELLO_ATTACH_SECLABEL | - KDBUS_HELLO_ATTACH_AUDIT; - - ret = ioctl(fd, KDBUS_CMD_HELLO, &hello); + buf = mmap(NULL, 128 * 1024 * 1024, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); + if (buf == MAP_FAILED) { + fprintf(stderr, "--- error mmap (%m)\n"); + return NULL; + } + h.v_type = KDBUS_HELLO_BUFFER; + h.v_size = KDBUS_ITEM_HEADER_SIZE + sizeof(struct kdbus_vec); + h.vec.address = (uint64_t)buf; + h.vec.size = 128 * 1024 * 1024; + + h.hello.conn_flags = KDBUS_HELLO_ACCEPT_FD | + KDBUS_HELLO_ATTACH_COMM | + KDBUS_HELLO_ATTACH_EXE | + KDBUS_HELLO_ATTACH_CMDLINE | + KDBUS_HELLO_ATTACH_CAPS | + KDBUS_HELLO_ATTACH_CGROUP | + KDBUS_HELLO_ATTACH_SECLABEL | + KDBUS_HELLO_ATTACH_AUDIT; + + h.hello.size = sizeof(struct kdbus_cmd_hello) + + KDBUS_ITEM_HEADER_SIZE + sizeof(struct kdbus_vec); + + ret = ioctl(fd, KDBUS_CMD_HELLO, &h.hello); if (ret) { fprintf(stderr, "--- error when saying hello: %d (%m)\n", ret); return NULL; } - printf("-- Our peer ID for %s: %llu\n", path, (unsigned long long)hello.id); + printf("-- Our peer ID for %s: %llu\n", path, (unsigned long long)h.hello.id); conn = malloc(sizeof(*conn)); if (!conn) { @@ -66,7 +86,7 @@ struct conn *connect_to_bus(const char *path) } conn->fd = fd; - conn->id = hello.id; + conn->id = h.hello.id; return conn; } @@ -76,18 +96,19 @@ int msg_send(const struct conn *conn, uint64_t dst_id) { struct kdbus_msg *msg; - const char ref[2048] = "REFERENCED"; + const char ref1[1024 * 1024] = "0123456789_0"; + const char ref2[] = "0123456789_1"; struct kdbus_item *item; uint64_t size; int ret; - size = sizeof(*msg); + size = sizeof(struct kdbus_msg); size += KDBUS_ITEM_HEADER_SIZE + sizeof(struct kdbus_vec); - size += KDBUS_ITEM_HEADER_SIZE + sizeof("INLINE1"); - + size += KDBUS_ITEM_HEADER_SIZE + sizeof(struct kdbus_vec); + if (dst_id == KDBUS_DST_ID_BROADCAST) size += KDBUS_ITEM_HEADER_SIZE + 64; - + if (name) size += KDBUS_ITEM_HEADER_SIZE + strlen(name) + 1; @@ -115,13 +136,14 @@ int msg_send(const struct conn *conn, item->type = KDBUS_MSG_PAYLOAD_VEC; item->size = KDBUS_ITEM_HEADER_SIZE + sizeof(struct kdbus_vec); - item->vec.address = (uint64_t)&ref; - item->vec.size = sizeof(ref); + item->vec.address = (uint64_t)&ref1; + item->vec.size = sizeof(ref1); item = KDBUS_ITEM_NEXT(item); - item->type = KDBUS_MSG_PAYLOAD; - item->size = KDBUS_ITEM_HEADER_SIZE + sizeof("INLINE1"); - memcpy(item->data, "INLINE1", sizeof("INLINE1")); + item->type = KDBUS_MSG_PAYLOAD_VEC; + item->size = KDBUS_ITEM_HEADER_SIZE + sizeof(struct kdbus_vec); + item->vec.address = (uint64_t)&ref2; + item->vec.size = sizeof(ref2); item = KDBUS_ITEM_NEXT(item); if (dst_id == KDBUS_DST_ID_BROADCAST) { @@ -169,16 +191,13 @@ void msg_dump(struct kdbus_msg *msg) } switch (item->type) { - case KDBUS_MSG_PAYLOAD: - printf(" +%s (%llu bytes) '%s'\n", - enum_MSG(item->type), item->size, item->data); - break; - case KDBUS_MSG_PAYLOAD_VEC: - printf(" +%s (%llu bytes) addr=%p size=%llu flags=0x%llx '%s'\n", + printf(" +%s (%llu bytes) addr=%p size=%llu\n", enum_MSG(item->type), item->size, (void *)(uintptr_t)item->vec.address, - (unsigned long long)item->vec.size, (unsigned long long)item->vec.flags, - (char *)(uintptr_t)item->vec.address); + (unsigned long long)item->vec.size); + printf(" '%s' ... '%s'\n", + (char *)(uintptr_t)item->vec.address, + (char *)(uintptr_t)item->vec.address + item->vec.size - 12); break; case KDBUS_MSG_SRC_CREDS: @@ -298,20 +317,23 @@ void msg_dump(struct kdbus_msg *msg) int msg_recv(struct conn *conn) { - char tmp[0xffff]; - struct kdbus_msg *msg = (struct kdbus_msg *) tmp; + struct kdbus_msg *msg; int ret; - memset(tmp, 0, sizeof(tmp)); - msg->size = sizeof(tmp); - ret = ioctl(conn->fd, KDBUS_CMD_MSG_RECV, msg); - if (ret) { + ret = ioctl(conn->fd, KDBUS_CMD_MSG_RECV, &msg); + if (ret < 0) { fprintf(stderr, "error receiving message: %d (%m)\n", ret); return EXIT_FAILURE; } msg_dump(msg); + ret = ioctl(conn->fd, KDBUS_CMD_MSG_FREE, msg); + if (ret < 0) { + fprintf(stderr, "error free message: %d (%m)\n", ret); + return EXIT_FAILURE; + } + return 0; } |