diff options
author | Kay Sievers <kay@vrfy.org> | 2013-05-28 10:02:01 +0200 |
---|---|---|
committer | Kay Sievers <kay@vrfy.org> | 2013-05-30 05:26:31 +0200 |
commit | f971c4d6c0f148f3ab1a0acbc89c964796fc41c5 (patch) | |
tree | 3ff544a76be131e0da8995fc0e31328284fb5483 | |
parent | 789cd310f26cfb8fb62673e49058f0c93df8ea42 (diff) | |
download | kdbus-bus-f971c4d6c0f148f3ab1a0acbc89c964796fc41c5.tar.gz kdbus-bus-f971c4d6c0f148f3ab1a0acbc89c964796fc41c5.tar.bz2 kdbus-bus-f971c4d6c0f148f3ab1a0acbc89c964796fc41c5.zip |
pool: use shmem file as backing store
-rw-r--r-- | TODO | 8 | ||||
-rw-r--r-- | bus.c | 2 | ||||
-rw-r--r-- | connection.c | 361 | ||||
-rw-r--r-- | connection.h | 9 | ||||
-rw-r--r-- | internal.h | 3 | ||||
-rw-r--r-- | kdbus.h | 20 | ||||
-rw-r--r-- | kdbus.txt | 2 | ||||
-rw-r--r-- | memfd.c | 2 | ||||
-rw-r--r-- | message.c | 35 | ||||
-rw-r--r-- | message.h | 9 | ||||
-rw-r--r-- | notify.c | 16 | ||||
-rw-r--r-- | pool.c | 315 | ||||
-rw-r--r-- | pool.h | 69 | ||||
-rw-r--r-- | test/kdbus-enum.c | 1 | ||||
-rw-r--r-- | test/kdbus-util.c | 96 | ||||
-rw-r--r-- | test/kdbus-util.h | 4 | ||||
-rw-r--r-- | test/test-kdbus.c | 1 |
17 files changed, 474 insertions, 479 deletions
@@ -1,17 +1,13 @@ External API: - _IOWR() vs. _IOR()/_IOW()? + - rules for: which unknown items to ignore in userspace? Internal: - - if we expect larger amounts of memory to copy around from the sender - to the receiver's pool, we should chunk get_user_pages(), replace vmap() - update/rethink kdbus_conn_accounting_sub_size() use, we don't allocate kernel memory anymore - - BUG: the sender needs to ref the receiver when looking it up to protect - against the receiver going away while its ressources are accessed - memfd's file pos is shared, which can be confusing, document pread/pwrite - prefix all functions with kdbus_ - - "signals" miss all the process metadata, we attach only creds - - set size in shmem file for receive buffer + - conn->ep->bus ==> conn->bus Kernel Core: - remove our own task_cgroup_path_from_hierarchy() as soon as it's available: @@ -41,7 +41,7 @@ bool kdbus_bus_uid_is_privileged(const struct kdbus_bus *bus) } /** - * kdbus_bus_unref() - increase the reference counter of a kdbus_bus + * kdbus_bus_ref() - increase the reference counter of a kdbus_bus * @bus: The bus to unref * * Every user of a bus, except for its creator, must add a reference to the diff --git a/connection.c b/connection.c index 4ed74da2bb7..f585e0ea1ea 100644 --- a/connection.c +++ b/connection.c @@ -29,6 +29,7 @@ #include <linux/mm.h> #include <linux/highmem.h> #include <linux/syscalls.h> +#include <linux/uio.h> #include <uapi/linux/major.h> #include "connection.h" @@ -42,19 +43,22 @@ #include "names.h" #include "policy.h" +struct kdbus_conn *kdbus_conn_ref(struct kdbus_conn *conn); +void kdbus_conn_unref(struct kdbus_conn *conn); + struct kdbus_conn_queue { struct list_head entry; - /* pointer to message placed in the receiver's buffer */ - struct __user kdbus_msg *msg; + /* offset to the message placed in the receiver's buffer */ + size_t off; /* passed KDBUS_MSG_PAYLOAD_MEMFD */ - int __user **memfds; + size_t *memfds; struct file **memfds_fp; unsigned int memfds_count; /* passed KDBUS_MSG_FDS */ - int __user *fds; + size_t fds; struct file **fds_fp; unsigned int fds_count; @@ -169,10 +173,10 @@ exit_unref: return ret; } -static int kdbus_conn_payload_add(struct kdbus_conn_queue *queue, +static int kdbus_conn_payload_add(struct kdbus_conn *conn, + struct kdbus_conn_queue *queue, const struct kdbus_kmsg *kmsg, - struct kdbus_slice *slice, - size_t items, size_t vec_data) + size_t off, size_t items, size_t vec_data) { const struct kdbus_item *item; int ret; @@ -180,7 +184,7 @@ static int kdbus_conn_payload_add(struct kdbus_conn_queue *queue, if (kmsg->memfds_count > 0) { size_t size; - size = kmsg->memfds_count * sizeof(int *); + size = kmsg->memfds_count * sizeof(size_t); queue->memfds = kmalloc(size, GFP_KERNEL); if (!queue->memfds) return -ENOMEM; @@ -194,50 +198,54 @@ static int kdbus_conn_payload_add(struct kdbus_conn_queue *queue, KDBUS_PART_FOREACH(item, &kmsg->msg, items) { switch (item->type) { case KDBUS_MSG_PAYLOAD_VEC: { - size_t size = KDBUS_PART_HEADER_SIZE + - sizeof(struct kdbus_vec); + const size_t size = KDBUS_PART_HEADER_SIZE + + sizeof(struct kdbus_vec); char tmp[size]; struct kdbus_item *it = (struct kdbus_item *)tmp; - void *addr = NULL; /* add item */ - it->type = KDBUS_MSG_PAYLOAD_VEC; + it->type = KDBUS_MSG_PAYLOAD_OFF; it->size = size; - /* A NULL address is a "padding vec" for alignement */ + /* a NULL address is a "padding vec" for alignement */ if (KDBUS_PTR(item->vec.address)) - addr = slice->buf + vec_data; - it->vec.address = KDBUS_ADDR(addr); + it->vec.offset = off + vec_data; + else + it->vec.offset = ~0ULL; it->vec.size = item->vec.size; - ret = kdbus_pool_slice_copy(slice, items, it, size); + ret = kdbus_pool_write(conn->pool, off + items, it, size); if (ret < 0) return ret; - /* copy kdbus_vec data directly from sender */ - ret = kdbus_pool_slice_copy_user(slice, vec_data, + items += KDBUS_ALIGN8((it)->size); + + if (!KDBUS_PTR(item->vec.address)) + break; + + /* copy kdbus_vec data from sender to receiver */ + ret = kdbus_pool_write_user(conn->pool, off + vec_data, KDBUS_PTR(item->vec.address), item->vec.size); if (ret < 0) return ret; - items += KDBUS_ALIGN8((it)->size); vec_data += item->vec.size; break; } case KDBUS_MSG_PAYLOAD_MEMFD: { - size_t size = KDBUS_PART_HEADER_SIZE + - sizeof(struct kdbus_memfd); + const size_t size = KDBUS_PART_HEADER_SIZE + + sizeof(struct kdbus_memfd); char tmp[size]; struct kdbus_item *it = (struct kdbus_item *)tmp; struct file *fp; - int __user *memfd; + size_t memfd; /* add item */ it->type = KDBUS_MSG_PAYLOAD_MEMFD; it->size = size; it->memfd.size = item->memfd.size; it->memfd.fd = -1; - ret = kdbus_pool_slice_copy(slice, items, it, size); + ret = kdbus_pool_write(conn->pool, off + items, it, size); if (ret < 0) return ret; @@ -248,8 +256,7 @@ static int kdbus_conn_payload_add(struct kdbus_conn_queue *queue, /* remember the file and the location of the fd number * which will be updated at RECV time */ - memfd = slice->buf + items + - offsetof(struct kdbus_item, memfd.fd); + memfd = items + offsetof(struct kdbus_item, memfd.fd); queue->memfds[queue->memfds_count] = memfd; queue->memfds_fp[queue->memfds_count] = fp; queue->memfds_count++; @@ -273,19 +280,19 @@ void kdbus_conn_queue_cleanup(struct kdbus_conn_queue *queue) kfree(queue); } -/* enqueue a message into the receiver's connection */ +/* enqueue a message into the receiver's pool */ int kdbus_conn_queue_insert(struct kdbus_conn *conn, struct kdbus_kmsg *kmsg, u64 deadline_ns) { struct kdbus_conn_queue *queue; - void __user *buf; - struct kdbus_slice *slice = NULL; u64 msg_size; + size_t size; size_t payloads = 0; size_t fds = 0; size_t meta = 0; size_t vec_data; - size_t size; + size_t want, have; + size_t off; int ret = 0; if (!conn->type == KDBUS_CONN_EP_CONNECTED) @@ -307,11 +314,17 @@ int kdbus_conn_queue_insert(struct kdbus_conn *conn, struct kdbus_kmsg *kmsg, if (kmsg->msg.flags & KDBUS_MSG_FLAGS_EXPECT_REPLY) queue->expect_reply = true; - /* space for message header */ - msg_size = KDBUS_MSG_HEADER_SIZE; + /* we accept items from kernel-created messages */ + if (kmsg->msg.src_id == KDBUS_SRC_ID_KERNEL) + size = kmsg->msg.size; + else + size = KDBUS_MSG_HEADER_SIZE; + + /* the header */ + msg_size = size; /* space for PAYLOAD items */ - if (kmsg->vecs_count + kmsg->memfds_count > 0) { + if ((kmsg->vecs_count + kmsg->memfds_count) > 0) { payloads = msg_size; msg_size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_vec)) * kmsg->vecs_count; @@ -336,56 +349,52 @@ int kdbus_conn_queue_insert(struct kdbus_conn *conn, struct kdbus_kmsg *kmsg, /* allocate the needed space in the pool of the receiver */ mutex_lock(&conn->lock); - if (conn->msg_count > KDBUS_CONN_MAX_MSGS) { + if (!capable(CAP_IPC_OWNER) && + conn->msg_count > KDBUS_CONN_MAX_MSGS) { ret = -ENOBUFS; goto exit_unlock; } /* do not give out more than half of the remaining space */ - size = vec_data + kmsg->vecs_size; - if (size > (conn->pool.size - conn->pool.busy) / 2) { + want = vec_data + kmsg->vecs_size; + have = kdbus_pool_remain(conn->pool); + if (want < have && want > have / 2) { ret = -EXFULL; goto exit_unlock; } - buf = kdbus_pool_alloc(&conn->pool, size, &slice); - if (!buf) { - ret = -ENOBUFS; + ret = kdbus_pool_alloc(conn->pool, want, &off); + if (ret < 0) goto exit_unlock; - } mutex_unlock(&conn->lock); - ret = kdbus_pool_slice_map(slice, conn->task); - if (ret < 0) - goto exit; - - /* update and copy the message header */ - ret = kdbus_pool_slice_copy(slice, 0, &kmsg->msg, KDBUS_MSG_HEADER_SIZE); + /* copy the message header */ + ret = kdbus_pool_write(conn->pool, off, &kmsg->msg, size); if (ret < 0) goto exit; /* update the size */ - ret = kdbus_pool_slice_copy(slice, 0, &msg_size, sizeof(kmsg->msg.size)); + ret = kdbus_pool_write(conn->pool, off, &msg_size, sizeof(kmsg->msg.size)); if (ret < 0) goto exit; /* add PAYLOAD items */ if (kmsg->vecs_count + kmsg->memfds_count > 0) { - ret = kdbus_conn_payload_add(queue, kmsg, - slice, payloads, vec_data); + ret = kdbus_conn_payload_add(conn, queue, kmsg, + off, payloads, vec_data); if (ret < 0) goto exit; } /* add a FDS item; the array content will be updated at RECV time */ if (kmsg->fds_count > 0) { - size_t size = KDBUS_PART_HEADER_SIZE; + const size_t size = KDBUS_PART_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)); - ret = kdbus_pool_slice_copy(slice, fds, it, size); + ret = kdbus_pool_write(conn->pool, off + fds, it, size); if (ret < 0) goto exit; @@ -394,21 +403,20 @@ int kdbus_conn_queue_insert(struct kdbus_conn *conn, struct kdbus_kmsg *kmsg, goto exit; /* remember the array to update at RECV */ - queue->fds = buf + fds + offsetof(struct kdbus_item, fds); + queue->fds = fds + offsetof(struct kdbus_item, fds); queue->fds_count = kmsg->fds_count; } /* append message metadata/credential items */ if (kmsg->meta_size > 0) { - ret = kdbus_pool_slice_copy(slice, meta, kmsg->meta, kmsg->meta_size); + ret = kdbus_pool_write(conn->pool, off + meta, + kmsg->meta, kmsg->meta_size); if (ret < 0) goto exit; } - kdbus_pool_slice_unmap(slice); - - /* remember the pointer to the message */ - queue->msg = buf; + /* remember the offset to the message */ + queue->off = off; /* link the message into the receiver's queue */ mutex_lock(&conn->lock); @@ -424,8 +432,7 @@ exit_unlock: mutex_unlock(&conn->lock); exit: kdbus_conn_queue_cleanup(queue); - kdbus_pool_slice_unmap(slice); - kdbus_pool_free(&conn->pool, buf); + kdbus_pool_free(conn->pool, off); return ret; } @@ -448,7 +455,7 @@ static void kdbus_conn_scan_timeout(struct kdbus_conn *conn) if (queue->expect_reply) kdbus_notify_reply_timeout(conn->ep, queue->src_id, queue->cookie); - kdbus_pool_free(&conn->pool, queue->msg); + kdbus_pool_free(conn->pool, queue->off); list_del(&queue->entry); kdbus_conn_queue_cleanup(queue); } else if (queue->deadline_ns < deadline) { @@ -481,13 +488,56 @@ static void kdbus_conn_timer_func(unsigned long val) kdbus_conn_timeout_schedule_scan(conn); } +/* find and pin destination connection */ +static int kdbus_conn_get_conn_dst(struct kdbus_bus *bus, + const struct kdbus_kmsg *kmsg, + struct kdbus_conn **conn) +{ + const struct kdbus_msg *msg = &kmsg->msg; + struct kdbus_conn *c; + int ret = 0; + + mutex_lock(&bus->lock); + + if (msg->dst_id == KDBUS_DST_ID_WELL_KNOWN_NAME) { + const struct kdbus_name_entry *name_entry; + + name_entry = kdbus_name_lookup(bus->name_registry, + kmsg->dst_name); + if (!name_entry) { + ret = -ESRCH; + goto exit_unlock; + } + + c = name_entry->conn; + if ((msg->flags & KDBUS_MSG_FLAGS_NO_AUTO_START) && + (c->flags & KDBUS_HELLO_STARTER)) { + ret = -EADDRNOTAVAIL; + goto exit_unlock; + } + } else { + c = kdbus_bus_find_conn_by_id(bus, msg->dst_id); + if (!c) { + ret = -ENXIO; + goto exit_unlock; + } + } + + kdbus_conn_ref(c); + *conn = c; + +exit_unlock: + mutex_unlock(&bus->lock); + return ret; +} + int kdbus_conn_kmsg_send(struct kdbus_ep *ep, struct kdbus_conn *conn_src, struct kdbus_kmsg *kmsg) { + const struct kdbus_msg *msg = &kmsg->msg; struct kdbus_conn *conn_dst = NULL; struct kdbus_conn *conn; - const struct kdbus_msg *msg; u64 now_ns = 0; u64 deadline_ns = 0; int ret; @@ -507,8 +557,6 @@ int kdbus_conn_kmsg_send(struct kdbus_ep *ep, return ret; } - msg = &kmsg->msg; - /* broadcast message */ if (msg->dst_id == KDBUS_DST_ID_BROADCAST) { unsigned int i; @@ -526,7 +574,11 @@ int kdbus_conn_kmsg_send(struct kdbus_ep *ep, kmsg)) continue; - //FIXME: call kdbus_kmsg_append_for_dst()? + /* The first receiver which requests additional + * metadata causes the message to carry it; all + * receivers after that will see all of the added + * data, even when they did not ask for it. */ + kdbus_kmsg_append_meta(kmsg, conn_src, conn_dst); kdbus_conn_queue_insert(conn_dst, kmsg, 0); } @@ -536,24 +588,9 @@ int kdbus_conn_kmsg_send(struct kdbus_ep *ep, } /* direct message */ - if (msg->dst_id == KDBUS_DST_ID_WELL_KNOWN_NAME) { - const struct kdbus_name_entry *name_entry; - - name_entry = kdbus_name_lookup(ep->bus->name_registry, - kmsg->dst_name); - 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 { - conn_dst = kdbus_bus_find_conn_by_id(ep->bus, msg->dst_id); - if (!conn_dst) - return -ENXIO; - } + ret = kdbus_conn_get_conn_dst(ep->bus, kmsg, &conn_dst); + if (ret < 0) + return ret; if (msg->timeout_ns) deadline_ns = now_ns + msg->timeout_ns; @@ -564,12 +601,12 @@ int kdbus_conn_kmsg_send(struct kdbus_ep *ep, conn_dst, deadline_ns); if (ret < 0) - return ret; + goto exit; } - ret = kdbus_kmsg_append_for_dst(kmsg, conn_src, conn_dst); + ret = kdbus_kmsg_append_meta(kmsg, conn_src, conn_dst); if (ret < 0) - return ret; + goto exit; /* monitor connections get all messages */ mutex_lock(&ep->bus->lock); @@ -584,19 +621,24 @@ int kdbus_conn_kmsg_send(struct kdbus_ep *ep, mutex_unlock(&ep->bus->lock); ret = kdbus_conn_queue_insert(conn_dst, kmsg, deadline_ns); + if (ret < 0) + goto exit; if (msg->timeout_ns) kdbus_conn_timeout_schedule_scan(conn_dst); +exit: + kdbus_conn_unref(conn_dst); return ret; } -static int kdbus_conn_fds_install(struct kdbus_conn_queue *queue) +static int kdbus_conn_fds_install(struct kdbus_conn *conn, + struct kdbus_conn_queue *queue) { size_t size; unsigned int i; int *fds; - int ret = 0; + int ret; /* get array of file descriptors */ size = queue->fds_count * sizeof(int); @@ -614,10 +656,9 @@ static int kdbus_conn_fds_install(struct kdbus_conn_queue *queue) } /* copy the array into the message item */ - if (copy_to_user(queue->fds, fds, size)) { - ret = -EFAULT; + ret = kdbus_pool_write(conn->pool, queue->off + queue->fds, fds, size); + if (ret < 0) goto remove_unused; - } /* install files in the receiver's process */ for (i = 0; i < queue->fds_count; i++) @@ -638,7 +679,9 @@ remove_unused: return ret; } -static int kdbus_conn_memfds_install(struct kdbus_conn_queue *queue, int **memfds) +static int kdbus_conn_memfds_install(struct kdbus_conn *conn, + struct kdbus_conn_queue *queue, + int **memfds) { size_t size; int *fds; @@ -662,10 +705,11 @@ static int kdbus_conn_memfds_install(struct kdbus_conn_queue *queue, int **memfd /* Update the file descriptor number in the items. We remembered * the locations of the values in the buffer. */ for (i = 0; i < queue->memfds_count; i++) { - if (put_user(fds[i], queue->memfds[i])) { - ret = -EFAULT; + ret = kdbus_pool_write(conn->pool, + queue->off + queue->memfds[i], + &fds[i], sizeof(int)); + if (ret < 0) goto remove_unused; - } } /* install files in the receiver's process */ @@ -689,17 +733,14 @@ remove_unused: } static int -kdbus_conn_recv_msg(struct kdbus_conn *conn, __u64 __user *address) +kdbus_conn_recv_msg(struct kdbus_conn *conn, __u64 __user *buf) { struct kdbus_conn_queue *queue; - u64 addr; + u64 off; int *memfds = NULL; unsigned int i; int ret; - if (!KDBUS_IS_ALIGNED8((unsigned long)address)) - return -EFAULT; - mutex_lock(&conn->lock); if (conn->msg_count == 0) { ret = -EAGAIN; @@ -709,23 +750,23 @@ kdbus_conn_recv_msg(struct kdbus_conn *conn, __u64 __user *address) /* return the address of the next message in the pool */ queue = list_first_entry(&conn->msg_list, struct kdbus_conn_queue, entry); - addr = KDBUS_ADDR(queue->msg); - if (copy_to_user(address, &addr, sizeof(__u64))) { + off = queue->off; + if (copy_to_user(buf, &off, sizeof(__u64))) { ret = -EFAULT; goto exit_unlock; } /* Install KDBUS_MSG_PAYLOAD_MEMFDs file descriptors, we return * the list of file descriptors to be able to cleanup on error. */ - if (queue->memfds_count) { - ret = kdbus_conn_memfds_install(queue, &memfds); + if (queue->memfds_count > 0) { + ret = kdbus_conn_memfds_install(conn, queue, &memfds); if (ret < 0) goto exit_unlock; } /* install KDBUS_MSG_FDS file descriptors */ - if (queue->fds_count) { - ret = kdbus_conn_fds_install(queue); + if (queue->fds_count > 0) { + ret = kdbus_conn_fds_install(conn, queue); if (ret < 0) goto exit_rewind; } @@ -818,6 +859,8 @@ static int kdbus_conn_open(struct inode *inode, struct file *file) if (!conn) return -ENOMEM; + kref_init(&conn->kref); + /* find and reference namespace */ ns = kdbus_ns_find_by_major(MAJOR(inode->i_rdev)); if (!ns) { @@ -866,6 +909,7 @@ static void kdbus_conn_cleanup(struct kdbus_conn *conn) mutex_lock(&conn->ep->bus->lock); hash_del(&conn->hentry); list_del(&conn->monitor_entry); + conn->type = KDBUS_CONN_EP_DISCONNECTED; mutex_unlock(&conn->ep->bus->lock); /* clean up any messages still left on this endpoint */ @@ -879,7 +923,7 @@ static void kdbus_conn_cleanup(struct kdbus_conn *conn) if (queue->src_id != conn->id && queue->expect_reply) { list_add_tail(&queue->entry, &list); } else { - kdbus_pool_free(&conn->pool, queue->msg); + kdbus_pool_free(conn->pool, queue->off); kdbus_conn_queue_cleanup(queue); } } @@ -889,7 +933,7 @@ static void kdbus_conn_cleanup(struct kdbus_conn *conn) kdbus_notify_reply_dead(conn->ep, queue->src_id, queue->cookie); mutex_lock(&conn->lock); - kdbus_pool_free(&conn->pool, queue->msg); + kdbus_pool_free(conn->pool, queue->off); mutex_unlock(&conn->lock); kdbus_conn_queue_cleanup(queue); } @@ -905,8 +949,25 @@ static void kdbus_conn_cleanup(struct kdbus_conn *conn) kdbus_match_db_unref(conn->match_db); kdbus_ep_unref(conn->ep); - kdbus_pool_cleanup(&conn->pool); - put_task_struct(current); + kdbus_pool_cleanup(conn->pool); +} + +static void __kdbus_conn_free(struct kref *kref) +{ + struct kdbus_conn *conn = container_of(kref, struct kdbus_conn, kref); + + kfree(conn); +} + +struct kdbus_conn *kdbus_conn_ref(struct kdbus_conn *conn) +{ + kref_get(&conn->kref); + return conn; +} + +void kdbus_conn_unref(struct kdbus_conn *conn) +{ + kref_put(&conn->kref, __kdbus_conn_free); } static int kdbus_conn_release(struct inode *inode, struct file *file) @@ -927,21 +988,18 @@ static int kdbus_conn_release(struct inode *inode, struct file *file) //FIXME: break; - case KDBUS_CONN_EP_CONNECTED: { - kdbus_conn_cleanup(conn); - - /* notify about the closed connection */ + case KDBUS_CONN_EP_CONNECTED: kdbus_notify_id_change(conn->ep, KDBUS_MSG_ID_REMOVE, conn->id, conn->flags); + kdbus_conn_cleanup(conn); break; - } default: break; } kdbus_ns_unref(conn->ns); - kfree(conn); + kdbus_conn_unref(conn); return 0; } @@ -1096,7 +1154,6 @@ static long kdbus_conn_ioctl_ep(struct file *file, unsigned int cmd, case KDBUS_CMD_HELLO: { /* turn this fd into a connection. */ - const struct kdbus_item *item; size_t size; void *v; @@ -1110,7 +1167,8 @@ static long kdbus_conn_ioctl_ep(struct file *file, unsigned int cmd, break; } - if (size < sizeof(struct kdbus_cmd_hello) || size > KDBUS_HELLO_MAX_SIZE) { + if (size < sizeof(struct kdbus_cmd_hello) || + size > KDBUS_HELLO_MAX_SIZE) { ret = -EMSGSIZE; break; } @@ -1127,48 +1185,15 @@ static long kdbus_conn_ioctl_ep(struct file *file, unsigned int cmd, break; } - KDBUS_PART_FOREACH(item, hello, items) { - if (!KDBUS_PART_VALID(item, hello)) { - ret = -EINVAL; - break; - } - - switch (item->type) { - case KDBUS_HELLO_POOL: { - void *p; - - if (!KDBUS_PTR(item->vec.address) || - item->vec.size == 0) { - ret = -EINVAL; - break; - } - - /* enforce page alignment */ - if (!IS_ALIGNED(item->vec.address, PAGE_SIZE) || - !IS_ALIGNED(item->vec.size, PAGE_SIZE)) { - ret = -EFAULT; - break; - } - - p = KDBUS_PTR(item->vec.address); - if (!kdbus_pool_is_anon_map(current->mm, p, - item->vec.size)) { - ret = -EFAULT; - break; - } - - ret = kdbus_pool_init(&conn->pool, - p, item->vec.size); - break; - } - - default: - ret = -ENOTSUPP; - } + if (hello->pool_size == 0 || + !IS_ALIGNED(hello->pool_size, PAGE_SIZE)) { + ret = -EFAULT; + break; } - if (!KDBUS_PART_END(item, hello)) - return -EINVAL; + ret = kdbus_pool_init(&conn->pool, hello->pool_size); + if (ret < 0) + break; mutex_init(&conn->lock); mutex_init(&conn->names_lock); @@ -1199,10 +1224,6 @@ static long kdbus_conn_ioctl_ep(struct file *file, unsigned int cmd, kdbus_conn_set_audit(conn); kdbus_conn_set_seclabel(conn); - /* pin and store the task, so a sender can copy to the receiver */ - get_task_struct(current); - conn->task = current; - /* link into bus; get new id for this connection */ mutex_lock(&conn->ep->bus->lock); conn->id = conn->ep->bus->conn_id_next++; @@ -1387,7 +1408,7 @@ static long kdbus_conn_ioctl_ep_connected(struct file *file, unsigned int cmd, break; } - case KDBUS_CMD_MSG_RECV: + case KDBUS_CMD_MSG_RECV: { /* receive a pointer to a queued message */ if (!KDBUS_IS_ALIGNED8((uintptr_t)buf)) { ret = -EFAULT; @@ -1396,16 +1417,24 @@ static long kdbus_conn_ioctl_ep_connected(struct file *file, unsigned int cmd, ret = kdbus_conn_recv_msg(conn, buf); break; + } case KDBUS_CMD_MSG_RELEASE: { + u64 off; + /* free the memory used in the receiver's pool */ if (!KDBUS_IS_ALIGNED8((uintptr_t)buf)) { ret = -EFAULT; break; } + if (copy_from_user(&off, buf, sizeof(__u64))) { + ret = -EFAULT; + break; + } + mutex_lock(&conn->lock); - kdbus_pool_free(&conn->pool, buf); + ret = kdbus_pool_free(conn->pool, off); mutex_unlock(&conn->lock); break; } @@ -1472,6 +1501,13 @@ static unsigned int kdbus_conn_poll(struct file *file, return mask; } +static int kdbus_conn_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct kdbus_conn *conn = file->private_data; + + return kdbus_pool_mmap(conn->pool, vma); +} + const struct file_operations kdbus_device_ops = { .owner = THIS_MODULE, .open = kdbus_conn_open, @@ -1479,6 +1515,7 @@ const struct file_operations kdbus_device_ops = { .poll = kdbus_conn_poll, .llseek = noop_llseek, .unlocked_ioctl = kdbus_conn_ioctl, + .mmap = kdbus_conn_mmap, #ifdef CONFIG_COMPAT .compat_ioctl = kdbus_conn_ioctl, #endif diff --git a/connection.h b/connection.h index dbc86bac5f6..98651ebf199 100644 --- a/connection.h +++ b/connection.h @@ -27,10 +27,12 @@ enum kdbus_conn_type { KDBUS_CONN_CONTROL_BUS_OWNER, /* fd to hold a bus */ KDBUS_CONN_EP, /* new fd of a bus node */ KDBUS_CONN_EP_CONNECTED, /* connection after HELLO */ + KDBUS_CONN_EP_DISCONNECTED, /* closed connection */ KDBUS_CONN_EP_OWNER, /* fd to hold an endpoint */ }; struct kdbus_conn { + struct kref kref; enum kdbus_conn_type type; struct kdbus_ns *ns; union { @@ -66,15 +68,12 @@ 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_pool pool; + /* buffer to fill with message data */ + struct kdbus_pool *pool; }; struct kdbus_kmsg; diff --git a/internal.h b/internal.h index 32313e8e619..e46df08703e 100644 --- a/internal.h +++ b/internal.h @@ -18,7 +18,7 @@ #define KDBUS_MSG_MAX_SIZE SZ_8K /* maximum size of message header and items */ #define KDBUS_MSG_MAX_ITEMS 128 /* maximum number of message items */ #define KDBUS_MSG_MAX_FDS 256 /* maximum number of passed file descriptors */ -#define KDBUS_MSG_MAX_PAYLOAD_VEC_SIZE SZ_2M /* maximum message payload size */ +#define KDBUS_MSG_MAX_PAYLOAD_VEC_SIZE SZ_8M /* maximum message payload size */ #define KDBUS_NAME_MAX_LEN 255 /* maximum length of well-known bus name */ @@ -36,7 +36,6 @@ /* exported addresses are 64bit */ #define KDBUS_PTR(addr) ((void *)(uintptr_t)(addr)) -#define KDBUS_ADDR(ptr) ((u64)(ptr)) /* exported sizes are 64bit and data aligned to 64 bit */ #define KDBUS_ALIGN8(s) ALIGN((s), 8) @@ -73,8 +73,11 @@ struct kdbus_timestamp { }; struct kdbus_vec { - __u64 address; __u64 size; + union { + __u64 address; + __u64 offset; + }; }; struct kdbus_memfd { @@ -88,6 +91,7 @@ enum { /* Filled in by userspace */ KDBUS_MSG_PAYLOAD_VEC, /* .data_vec, reference to memory area */ + KDBUS_MSG_PAYLOAD_OFF, /* .data_vec, reference to memory area */ KDBUS_MSG_PAYLOAD_MEMFD, /* file descriptor of a special data file */ KDBUS_MSG_FDS, /* .data_fds of file descriptors */ KDBUS_MSG_BLOOM, /* for broadcasts, carries bloom filter blob in .data */ @@ -243,13 +247,6 @@ enum { KDBUS_HELLO_ATTACH_AUDIT = 1 << 16, }; -/* Items to append to struct kdbus_cmd_hello */ -enum { - _KDBUS_HELLO_NULL, - KDBUS_HELLO_POOL, /* kdbus_vec, userspace supplied pool to - * place received messages */ -}; - struct kdbus_cmd_hello { __u64 size; @@ -270,6 +267,7 @@ struct kdbus_cmd_hello { __u64 id; /* id assigned to this connection */ __u64 bloom_size; /* The bloom filter size chosen by the * bus owner */ + __u64 pool_size; /* maximum size of pool buffer */ struct kdbus_item items[0]; }; @@ -285,7 +283,8 @@ enum { _KDBUS_MAKE_NULL, KDBUS_MAKE_NAME, KDBUS_MAKE_CGROUP, /* the cgroup hierarchy ID for which to attach - * cgroup membership paths * to messages. */ + * cgroup membership paths to messages. + * FIXME: remove, use *the* hierarchy */ KDBUS_MAKE_CRED, /* allow translator services which connect * to the bus on behalf of somebody else, * allow specifiying the credentials of the @@ -305,7 +304,6 @@ struct kdbus_cmd_bus_make { * KDBUS_CMD_HELLO, later */ __u64 bloom_size; /* size of the bloom filter for this bus */ struct kdbus_item items[0]; - }; struct kdbus_cmd_ep_make { @@ -415,7 +413,7 @@ enum { /* 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, __u64 *), - KDBUS_CMD_MSG_RELEASE = _IOWR(KDBUS_IOC_MAGIC, 0x42, struct kdbus_msg), + KDBUS_CMD_MSG_RELEASE = _IOWR(KDBUS_IOC_MAGIC, 0x42, __u64 *), 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), diff --git a/kdbus.txt b/kdbus.txt index 016e371b8cd..9b80cc0508a 100644 --- a/kdbus.txt +++ b/kdbus.txt @@ -352,7 +352,7 @@ Ioctl API Send a message and pass data from userspace to the kernel. KDBUS_CMD_MSG_RECV - Receive a message from the kernel which is placed in the receiver-supplied + Receive a message from the kernel which is placed in the receiver's pool. KDBUS_CMD_MSG_RELEASE @@ -80,7 +80,7 @@ int kdbus_memfd_new(int *fd) mutex_init(&mf->lock); /* allocate a new unlinked shmem file */ - shmemfp = shmem_file_setup("kdbus", 0, 0); + shmemfp = shmem_file_setup("kdbus-memfd", 0, 0); if (IS_ERR(shmemfp)) { ret = PTR_ERR(shmemfp); goto exit; diff --git a/message.c b/message.c index e09c8860e42..dcbcf9d52ed 100644 --- a/message.c +++ b/message.c @@ -81,9 +81,10 @@ void kdbus_kmsg_free(struct kdbus_kmsg *kmsg) int kdbus_kmsg_new(size_t extra_size, struct kdbus_kmsg **m) { - size_t size = sizeof(struct kdbus_kmsg) + KDBUS_ITEM_SIZE(extra_size); + size_t size; struct kdbus_kmsg *kmsg; + size = sizeof(struct kdbus_kmsg) + KDBUS_ITEM_SIZE(extra_size); kmsg = kzalloc(size, GFP_KERNEL); if (!kmsg) return -ENOMEM; @@ -122,7 +123,8 @@ static int kdbus_msg_scan_items(struct kdbus_conn *conn, struct kdbus_kmsg *kmsg return -EINVAL; kmsg->vecs_size += item->vec.size; - if (kmsg->vecs_size > KDBUS_MSG_MAX_PAYLOAD_VEC_SIZE) + if (!capable(CAP_IPC_OWNER) && + kmsg->vecs_size > KDBUS_MSG_MAX_PAYLOAD_VEC_SIZE) return -EMSGSIZE; kmsg->vecs_count++; @@ -370,7 +372,7 @@ static int kdbus_kmsg_append_data(struct kdbus_kmsg *kmsg, u64 type, item->type = type; item->size = KDBUS_PART_HEADER_SIZE + len; - memcpy(item->str, buf, len); + memcpy(item->data, buf, len); return 0; } @@ -478,13 +480,20 @@ int task_cgroup_path_from_hierarchy(struct task_struct *task, int hierarchy_id, return ret; } -int kdbus_kmsg_append_for_dst(struct kdbus_kmsg *kmsg, - struct kdbus_conn *conn_src, - struct kdbus_conn *conn_dst) +int kdbus_kmsg_append_meta(struct kdbus_kmsg *kmsg, + struct kdbus_conn *conn_src, + struct kdbus_conn *conn_dst) { struct kdbus_bus *bus = conn_dst->ep->bus; int ret = 0; + if (!conn_src) + return 0; + + /* all metadata already added */ + if ((conn_dst->flags & kmsg->meta_attached) == conn_dst->flags) + return 0; + if (conn_dst->flags & KDBUS_HELLO_ATTACH_COMM) { char comm[TASK_COMM_LEN]; @@ -497,6 +506,8 @@ int kdbus_kmsg_append_for_dst(struct kdbus_kmsg *kmsg, ret = kdbus_kmsg_append_str(kmsg, KDBUS_MSG_SRC_PID_COMM, comm); if (ret < 0) return ret; + + kmsg->meta_attached |= KDBUS_HELLO_ATTACH_COMM; } if (conn_dst->flags & KDBUS_HELLO_ATTACH_EXE) { @@ -537,6 +548,8 @@ int kdbus_kmsg_append_for_dst(struct kdbus_kmsg *kmsg, if (ret < 0) return ret; } + + kmsg->meta_attached |= KDBUS_HELLO_ATTACH_EXE; } if (conn_dst->flags & KDBUS_HELLO_ATTACH_CMDLINE) { @@ -563,6 +576,8 @@ int kdbus_kmsg_append_for_dst(struct kdbus_kmsg *kmsg, if (ret < 0) return ret; + + kmsg->meta_attached |= KDBUS_HELLO_ATTACH_CMDLINE; } /* we always return a 4 elements, the element size is 1/4 */ @@ -592,6 +607,8 @@ int kdbus_kmsg_append_for_dst(struct kdbus_kmsg *kmsg, cap, sizeof(cap)); if (ret < 0) return ret; + + kmsg->meta_attached |= KDBUS_HELLO_ATTACH_CAPS; } #ifdef CONFIG_CGROUPS @@ -611,6 +628,8 @@ int kdbus_kmsg_append_for_dst(struct kdbus_kmsg *kmsg, if (ret < 0) return ret; + + kmsg->meta_attached |= KDBUS_HELLO_ATTACH_CGROUP; } #endif @@ -621,6 +640,8 @@ int kdbus_kmsg_append_for_dst(struct kdbus_kmsg *kmsg, sizeof(conn_src->audit_ids)); if (ret < 0) return ret; + + kmsg->meta_attached |= KDBUS_HELLO_ATTACH_AUDIT; } #endif @@ -634,6 +655,8 @@ int kdbus_kmsg_append_for_dst(struct kdbus_kmsg *kmsg, if (ret < 0) return ret; } + + kmsg->meta_attached |= KDBUS_HELLO_ATTACH_SECLABEL; } #endif diff --git a/message.h b/message.h index 96a1355ae84..8f1ab7589c3 100644 --- a/message.h +++ b/message.h @@ -35,6 +35,9 @@ struct kdbus_kmsg { unsigned int vecs_count; unsigned int memfds_count; + /* added metadata flags KDBUS_HELLO_ATTACH_* */ + u64 meta_attached; + struct kdbus_msg msg; }; @@ -50,7 +53,7 @@ int kdbus_kmsg_append_src_names(struct kdbus_kmsg *kmsg, struct kdbus_conn *conn); int kdbus_kmsg_append_cred(struct kdbus_kmsg *kmsg, const struct kdbus_creds *creds); -int kdbus_kmsg_append_for_dst(struct kdbus_kmsg *kmsg, - struct kdbus_conn *conn_src, - struct kdbus_conn *conn_dst); +int kdbus_kmsg_append_meta(struct kdbus_kmsg *kmsg, + struct kdbus_conn *conn_src, + struct kdbus_conn *conn_dst); #endif @@ -81,24 +81,25 @@ int kdbus_notify_name_change(struct kdbus_ep *ep, u64 type, { struct kdbus_manager_msg_name_change *name_change; struct kdbus_kmsg *kmsg = NULL; - struct kdbus_item *data; + struct kdbus_item *item; struct kdbus_msg *msg; - u64 extra_size = sizeof(*name_change) + strlen(name); + size_t extra_size; int ret; + extra_size = sizeof(*name_change) + strlen(name); ret = kdbus_kmsg_new(extra_size, &kmsg); if (ret < 0) return ret; msg = &kmsg->msg; - data = msg->items; - name_change = (struct kdbus_manager_msg_name_change *) data->data; + item = msg->items; + name_change = (struct kdbus_manager_msg_name_change *)item->data; /* FIXME */ msg->dst_id = KDBUS_DST_ID_BROADCAST; msg->src_id = KDBUS_SRC_ID_KERNEL; - data->type = type; + item->type = type; name_change->old_id = old_id; name_change->new_id = new_id; @@ -118,16 +119,15 @@ int kdbus_notify_id_change(struct kdbus_ep *ep, u64 type, struct kdbus_kmsg *kmsg = NULL; struct kdbus_item *item; struct kdbus_msg *msg; - u64 extra_size = sizeof(*id_change); int ret; - ret = kdbus_kmsg_new(extra_size, &kmsg); + ret = kdbus_kmsg_new(sizeof(*id_change), &kmsg); if (ret < 0) return ret; msg = &kmsg->msg; item = msg->items; - id_change = (struct kdbus_manager_msg_id_change *) item->data; + id_change = (struct kdbus_manager_msg_id_change *)item->data; msg->dst_id = KDBUS_DST_ID_BROADCAST; msg->src_id = KDBUS_SRC_ID_KERNEL; @@ -21,26 +21,70 @@ #include <linux/mm.h> #include <linux/highmem.h> #include <linux/rbtree.h> +#include <linux/file.h> +#include <linux/shmem_fs.h> +#include <linux/aio.h> #include "pool.h" #include "message.h" +/* + * Messages sent with KDBUS_CMD_MSG_SEND are copied direcly by the + * sending process into the receiver's pool. + * + * Messages received with KDBUS_CMD_MSG_RECV just return the offset + * to the data placed in the pool. + * + * The internally allocated memory needs to be returned by the receiver + * with KDBUS_CMD_MSG_RELEASE. + */ + +/* The receiver's buffer, managed as a pool of allocated and free + * slices containing the queued messages. */ +struct kdbus_pool { + struct file *f; /* shmem file */ + size_t size; /* size of file */ + size_t busy; /* currently allocated size */ + + struct list_head slices; /* all slices sorted by address */ + struct rb_root slices_busy; /* tree of allocated slices */ + struct rb_root slices_free; /* tree of free slices */ +}; + +/* The pool has one or more slices, always spanning the entire size of the + * pool. + * + * Every slice is an element in a list sorted by the buffer address, to + * provide access to the next neighbor slice. + * + * Every slice is member in either the busy or the free tree. The free + * tree is organized by slice size, the busy tree organized by buffer + * offset. */ +struct kdbus_slice { + size_t off; /* offset of slice */ + size_t size; /* size of slice */ + + struct list_head entry; + struct rb_node rb_node; + bool free; +}; + static void __maybe_unused kdbus_pool_slices_dump(struct kdbus_pool *pool, const char *str) { struct kdbus_slice *s; - pr_info("=== dump start '%s' pool=%p buf=%p size=%zu ===\n", - str, pool, pool->buf, pool->size); + pr_info("=== dump start '%s' pool=%p size=%zu ===\n", + str, pool, pool->size); list_for_each_entry(s, &pool->slices, entry) - pr_info(" slice=%p free=%u, buf=%p size=%zu\n", - s, s->free, s->buf, s->size); + pr_info(" slice=%p free=%u, off=%zu size=%zu\n", + s, s->free, s->off, s->size); pr_info("=== dump end '%s' pool=%p ===\n", str, pool); } -static struct kdbus_slice *kdbus_pool_slice_new(void *__user *buf, size_t size) +static struct kdbus_slice *kdbus_pool_slice_new(size_t off, size_t size) { struct kdbus_slice *slice; @@ -48,7 +92,7 @@ static struct kdbus_slice *kdbus_pool_slice_new(void *__user *buf, size_t size) if (!slice) return NULL; - slice->buf = buf; + slice->off = off; slice->size = size; slice->free = true; return slice; @@ -90,9 +134,9 @@ static void kdbus_pool_add_busy_slice(struct kdbus_pool *pool, pn = *n; pslice = rb_entry(pn, struct kdbus_slice, rb_node); - if (slice->buf < pslice->buf) + if (slice->off < pslice->off) n = &pn->rb_left; - else if (slice->buf > pslice->buf) + else if (slice->off > pslice->off) n = &pn->rb_right; } @@ -100,9 +144,9 @@ static void kdbus_pool_add_busy_slice(struct kdbus_pool *pool, rb_insert_color(&slice->rb_node, &pool->slices_busy); } -/* find a slice by its pool buffer address */ +/* find a slice by its pool offset */ static struct kdbus_slice *kdbus_pool_find_slice(struct kdbus_pool *pool, - void __user *buf) + size_t off) { struct rb_node *n; @@ -111,9 +155,9 @@ static struct kdbus_slice *kdbus_pool_find_slice(struct kdbus_pool *pool, struct kdbus_slice *s; s = rb_entry(n, struct kdbus_slice, rb_node); - if (buf < s->buf) + if (off < s->off) n = n->rb_left; - else if (buf > s->buf) + else if (off > s->off) n = n->rb_right; else return s; @@ -123,22 +167,22 @@ static struct kdbus_slice *kdbus_pool_find_slice(struct kdbus_pool *pool, } /* allocate a slice from the pool with the given size */ -static struct kdbus_slice *kdbus_pool_alloc_slice(struct kdbus_pool *pool, - size_t size) +static int kdbus_pool_alloc_slice(struct kdbus_pool *pool, + size_t size, struct kdbus_slice **slice) { size_t slice_size = KDBUS_ALIGN8(size); struct rb_node *n; - struct kdbus_slice *slice; + struct kdbus_slice *s; struct rb_node *found = NULL; /* search a free slice with the closest matching size */ n = pool->slices_free.rb_node; while (n) { - slice = rb_entry(n, struct kdbus_slice, rb_node); - if (slice_size < slice->size) { + s = rb_entry(n, struct kdbus_slice, rb_node); + if (slice_size < s->size) { found = n; n = n->rb_left; - } else if (slice_size > slice->size) + } else if (slice_size > s->size) n = n->rb_right; else { found = n; @@ -148,36 +192,37 @@ static struct kdbus_slice *kdbus_pool_alloc_slice(struct kdbus_pool *pool, /* no slice with the minimum size found in the pool */ if (!found) - return NULL; + return -ENOBUFS; /* no exact match, use the closest one */ if (!n) - slice = rb_entry(found, struct kdbus_slice, rb_node); + s = rb_entry(found, struct kdbus_slice, rb_node); /* move slice from free to the busy tree */ rb_erase(found, &pool->slices_free); - kdbus_pool_add_busy_slice(pool, slice); + kdbus_pool_add_busy_slice(pool, s); /* we got a slice larger than what we asked for? */ - if (slice->size > slice_size) { - struct kdbus_slice *s; + if (s->size > slice_size) { + struct kdbus_slice *s_new; /* split-off the remainder of the size to its own slice */ - s = kdbus_pool_slice_new(slice->buf + slice_size, - slice->size - slice_size); - if (!s) - return NULL; + s_new = kdbus_pool_slice_new(s->off + slice_size, + s->size - slice_size); + if (!s_new) + return -ENOMEM; - list_add(&s->entry, &slice->entry); - kdbus_pool_add_free_slice(pool, s); + list_add(&s_new->entry, &s->entry); + kdbus_pool_add_free_slice(pool, s_new); /* adjust our size now that we split-off another slice */ - slice->size = slice_size; + s->size = slice_size; } - slice->free = false; - pool->busy += slice->size; - return slice; + s->free = false; + pool->busy += s->size; + *slice = s; + return 0; } /* return an allocated slice back to the pool */ @@ -218,25 +263,38 @@ static void kdbus_pool_free_slice(struct kdbus_pool *pool, kdbus_pool_add_free_slice(pool, slice); } -int kdbus_pool_init(struct kdbus_pool *pool, void __user *buf, size_t size) +int kdbus_pool_init(struct kdbus_pool **pool, size_t size) { + struct kdbus_pool *p; + struct file *f; struct kdbus_slice *s; - pool->buf = buf; - pool->size = size; - pool->busy = 0; - pool->slices_free = RB_ROOT; - pool->slices_busy = RB_ROOT; + p = kzalloc(sizeof(struct kdbus_pool), GFP_KERNEL); + if (!p) + return -ENOMEM; + + f = shmem_file_setup("kdbus-pool", size, 0); + if (IS_ERR(f)) + return PTR_ERR(f); /* allocate first slice spanning the entire pool */ - s = kdbus_pool_slice_new(buf, size); - if (!s) + s = kdbus_pool_slice_new(0, size); + if (!s) { + fput(f); return -ENOMEM; + } + + p->f = f; + p->size = size; + p->busy = 0; + p->slices_free = RB_ROOT; + p->slices_busy = RB_ROOT; - INIT_LIST_HEAD(&pool->slices); - list_add(&s->entry, &pool->slices); + INIT_LIST_HEAD(&p->slices); + list_add(&s->entry, &p->slices); - kdbus_pool_add_free_slice(pool, s); + kdbus_pool_add_free_slice(p, s); + *pool = p; return 0; } @@ -244,38 +302,49 @@ void kdbus_pool_cleanup(struct kdbus_pool *pool) { struct kdbus_slice *s, *tmp; - if (!pool->buf) + if (!pool) return; list_for_each_entry_safe(s, tmp, &pool->slices, entry) { list_del(&s->entry); kfree(s); } + + fput(pool->f); + kfree(pool); +} + +size_t kdbus_pool_remain(const struct kdbus_pool *pool) +{ + return pool->size - pool->busy; } /* allocate a message of the given size in the receiver's pool */ -void __user *kdbus_pool_alloc(struct kdbus_pool *pool, size_t size, - struct kdbus_slice **slice) +int kdbus_pool_alloc(struct kdbus_pool *pool, size_t size, size_t *off) { struct kdbus_slice *s; + int ret; - s = kdbus_pool_alloc_slice(pool, size); - if (!s) - return NULL; + ret = kdbus_pool_alloc_slice(pool, size, &s); + if (ret < 0) + return ret; - *slice = s; - return s->buf; + *off = s->off; + return 0; } /* free the allocated message */ -int kdbus_pool_free(struct kdbus_pool *pool, void __user *buf) +int kdbus_pool_free(struct kdbus_pool *pool, size_t off) { struct kdbus_slice *slice; - if (!buf) + if (!pool) return 0; - slice = kdbus_pool_find_slice(pool, buf); + if (off >= pool->size) + return -EINVAL; + + slice = kdbus_pool_find_slice(pool, off); if (!slice) return -ENXIO; @@ -283,126 +352,48 @@ int kdbus_pool_free(struct kdbus_pool *pool, void __user *buf) return 0; } -/* unpin the receiver's pages */ -void kdbus_pool_slice_unmap(struct kdbus_slice *slice) +/* write to the receiver's shmem file */ +ssize_t kdbus_pool_write_user(const struct kdbus_pool *pool, size_t off, + void *data, size_t len) { - unsigned int i; - - if (!slice) - return; - - vunmap(slice->pg_buf); - slice->pg_buf = NULL; + loff_t o = off; - for (i = 0; i < slice->pg_n; i++) - put_page(slice->pg[i]); - kfree(slice->pg); - - slice->pg_n = 0; - slice->pg = NULL; + return pool->f->f_op->write(pool->f, data, len, &o); } -bool kdbus_pool_is_anon_map(struct mm_struct *mm, - void __user *buf, size_t size) +ssize_t kdbus_pool_write(const struct kdbus_pool *pool, size_t off, + void *data, size_t len) { - unsigned long addr = (unsigned long) buf; - struct vm_area_struct *vma; - - vma = find_vma(mm, addr); - if (!vma) - return false; + loff_t o = off; + mm_segment_t old_fs; + void __user *p; + ssize_t ret; - if (vma->vm_file) - return false; + old_fs = get_fs(); + set_fs(get_ds()); - if (addr + size > vma->vm_end) - return false; + p = (void __force __user *)data; + ret = pool->f->f_op->write(pool->f, p, len, &o); - return true; + set_fs(old_fs); + return ret; } -/* pin the receiver's memory range/pages */ -int kdbus_pool_slice_map(struct kdbus_slice *slice, struct task_struct *task) +/* map the shmem file for the receiver */ +int kdbus_pool_mmap(const struct kdbus_pool *pool, struct vm_area_struct *vma) { - unsigned int n; - int have; - unsigned long base; - unsigned long addr; - struct mm_struct *mm; - - /* calculate the number of pages involved in the range */ - addr = (unsigned long)slice->buf; - n = (addr + slice->size - 1) / PAGE_SIZE - addr / PAGE_SIZE + 1; - - slice->pg = kmalloc(n * sizeof(struct page *), GFP_KERNEL); - if (!slice->pg) - return -ENOMEM; - - /* start address in our first page */ - base = addr & PAGE_MASK; - slice->pg_off = addr - base; - - /* pin the receiver's pool page(s) we will write to; the task - * is pinned as long as the connection is open */ - mm = get_task_mm(task); - if (!mm) { - kdbus_pool_slice_unmap(slice); - return -ESHUTDOWN; - } - - down_read(&mm->mmap_sem); - if (kdbus_pool_is_anon_map(mm, slice->buf, slice->size)) - have = get_user_pages(task, mm, base, n, true, false, - slice->pg, NULL); - else - have = -EFAULT; - up_read(&mm->mmap_sem); - - mmput(mm); - - if (have < 0) { - kdbus_pool_slice_unmap(slice); - return have; - } - - slice->pg_n = have; - - /* fewer pages than requested */ - if (slice->pg_n < n) { - kdbus_pool_slice_unmap(slice); - return -EFAULT; - } + /* deny write access to the pool */ + if (vma->vm_flags & VM_WRITE) + return -EPERM; - /* map the slice so we can access it */ - slice->pg_buf = vmap(slice->pg, slice->pg_n, 0, PAGE_KERNEL); - if (!slice->pg_buf) { - kdbus_pool_slice_unmap(slice); + /* do not allow to map more than the size of the file */ + if ((vma->vm_end - vma->vm_start) > pool->size) return -EFAULT; - } - - return 0; -} - -/* copy a memory range to a slice in the receiver's pool */ -int kdbus_pool_slice_copy(struct kdbus_slice *slice, size_t off, - void *buf, size_t size) -{ - memcpy(slice->pg_buf + slice->pg_off + off, buf, size); - return 0; -} - -/* copy a user memory range to a slice in the receiver's pool */ -int kdbus_pool_slice_copy_user(struct kdbus_slice *slice, size_t off, - void __user *buf, size_t size) -{ - /* a NULL from address just adds padding bytes for alignement */ - if (!buf) { - memset(slice->pg_buf + slice->pg_off + off, 0, size); - return 0; - } - if (copy_from_user(slice->pg_buf + slice->pg_off + off, buf, size)) - return -EFAULT; + /* replace the connection file with our shmem file */ + if (vma->vm_file) + fput(vma->vm_file); + vma->vm_file = get_file(pool->f); - return 0; + return pool->f->f_op->mmap(pool->f, vma); } @@ -13,67 +13,18 @@ #ifndef __KDBUS_POOL_H #define __KDBUS_POOL_H -/* - * Messages sent with KDBUS_CMD_MSG_SEND are copied direcly by the - * sending process into the receiver's pool. The receiver has provided - * the memory and registered it with KDBUS_HELLO_POOL. - * - * Messages received with KDBUS_CMD_MSG_RECV just return a pointer - * into the pool. - * - * The internally allocated memory needs to be returned by the receiver - * with * KDBUS_CMD_MSG_RELEASE. - */ - -/* The receiver-provided buffer managed as a pool of allocated and free - * slices containing the queued messages. */ -struct kdbus_pool { - void __user *buf; /* receiver-supplied buffer */ - size_t size; /* size of buffer */ - size_t busy; /* allocated size */ - - struct list_head slices; /* all slices sorted by address */ - struct rb_root slices_busy; /* tree of allocated slices */ - struct rb_root slices_free; /* tree of free slices */ -}; +struct kdbus_pool; -/* The pool has one or more slices, always spanning the entire size of the - * pool. - * - * Every slice is an element in a list sorted by the buffer address, to - * provide access to the next neighbor slice. - * - * Every slice is member in either the busy or the free tree. The free - * tree is organized by slice size, the busy tree organized by buffer - * address. */ -struct kdbus_slice { - void __user *buf; /* address of slice */ - size_t size; /* size of slice */ - - struct list_head entry; - struct rb_node rb_node; - bool free; - - struct page **pg; /* pages mapped by the slice */ - unsigned int pg_n; /* number of pages */ - size_t pg_off; /* offset into the first page */ - void *pg_buf; /* kernel address of mapped pages */ -}; - -int kdbus_pool_init(struct kdbus_pool *pool, void __user *buf, size_t size); +int kdbus_pool_init(struct kdbus_pool **pool, size_t size); void kdbus_pool_cleanup(struct kdbus_pool *pool); -bool kdbus_pool_is_anon_map(struct mm_struct *mm, - void __user *buf, size_t size); - -void __user *kdbus_pool_alloc(struct kdbus_pool *pool, size_t size, - struct kdbus_slice **slice); -int kdbus_pool_free(struct kdbus_pool *pool, void __user *buf); -int kdbus_pool_slice_map(struct kdbus_slice *slice, struct task_struct *task); -void kdbus_pool_slice_unmap(struct kdbus_slice *slice); +int kdbus_pool_alloc(struct kdbus_pool *pool, size_t size, size_t *off); +int kdbus_pool_free(struct kdbus_pool *pool, size_t off); +size_t kdbus_pool_remain(const struct kdbus_pool *pool); -int kdbus_pool_slice_copy(struct kdbus_slice *slice, size_t off, - void *buf, size_t size); -int kdbus_pool_slice_copy_user(struct kdbus_slice *slice, size_t off, - void __user *buf, size_t size); +ssize_t kdbus_pool_write(const struct kdbus_pool *pool, size_t off, + void *data, size_t len); +ssize_t kdbus_pool_write_user(const struct kdbus_pool *pool, size_t off, + void __user *data, size_t len); +int kdbus_pool_mmap(const struct kdbus_pool *pool, struct vm_area_struct *vma); #endif diff --git a/test/kdbus-enum.c b/test/kdbus-enum.c index 4439787e469..0a20c81469e 100644 --- a/test/kdbus-enum.c +++ b/test/kdbus-enum.c @@ -56,6 +56,7 @@ LOOKUP(CMD); TABLE(MSG) = { ENUM(_KDBUS_MSG_NULL), ENUM(KDBUS_MSG_PAYLOAD_VEC), + ENUM(KDBUS_MSG_PAYLOAD_OFF), ENUM(KDBUS_MSG_PAYLOAD_MEMFD), ENUM(KDBUS_MSG_FDS), ENUM(KDBUS_MSG_BLOOM), diff --git a/test/kdbus-util.c b/test/kdbus-util.c index 3cc69cf21ee..7440f941ecd 100644 --- a/test/kdbus-util.c +++ b/test/kdbus-util.c @@ -30,19 +30,14 @@ #include "kdbus-util.h" #include "kdbus-enum.h" +#define POOL_SIZE (16 * 1014LU * 1024LU) struct conn *connect_to_bus(const char *path) { int fd, ret; - void *buf; - struct { - struct kdbus_cmd_hello hello; - uint64_t v_size; - uint64_t v_type; - struct kdbus_vec vec; - } h; + struct kdbus_cmd_hello hello; struct conn *conn; - memset(&h, 0, sizeof(h)); + memset(&hello, 0, sizeof(hello)); printf("-- opening bus connection %s\n", path); fd = open(path, O_RDWR|O_CLOEXEC); @@ -51,34 +46,23 @@ struct conn *connect_to_bus(const char *path) return NULL; } - buf = mmap(NULL, 16 * 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_POOL; - h.v_size = KDBUS_PART_HEADER_SIZE + sizeof(struct kdbus_vec); - h.vec.address = (uint64_t)buf; - h.vec.size = 16 * 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_PART_HEADER_SIZE + sizeof(struct kdbus_vec); - - ret = ioctl(fd, KDBUS_CMD_HELLO, &h.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; + hello.size = sizeof(struct kdbus_cmd_hello); + hello.pool_size = POOL_SIZE; + + ret = ioctl(fd, KDBUS_CMD_HELLO, &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)h.hello.id); + printf("-- Our peer ID for %s: %llu\n", path, (unsigned long long)hello.id); conn = malloc(sizeof(*conn)); if (!conn) { @@ -86,8 +70,15 @@ struct conn *connect_to_bus(const char *path) return NULL; } + conn->buf = mmap(NULL, POOL_SIZE, PROT_READ, MAP_SHARED, fd, 0); + if (conn->buf == MAP_FAILED) { + free(conn); + fprintf(stderr, "--- error mmap (%m)\n"); + return NULL; + } + conn->fd = fd; - conn->id = h.hello.id; + conn->id = hello.id; return conn; } @@ -101,7 +92,7 @@ int msg_send(const struct conn *conn, const char ref2[] = "0123456789_1"; struct kdbus_item *item; uint64_t size; - int memfd; + int memfd = -1; int ret; size = sizeof(struct kdbus_msg); @@ -179,14 +170,13 @@ int msg_send(const struct conn *conn, if (dst_id == KDBUS_DST_ID_BROADCAST) { item->type = KDBUS_MSG_BLOOM; item->size = KDBUS_PART_HEADER_SIZE + 64; - item = KDBUS_PART_NEXT(item); } else { item->type = KDBUS_MSG_PAYLOAD_MEMFD; item->size = KDBUS_PART_HEADER_SIZE + sizeof(struct kdbus_memfd); item->memfd.size = 16; item->memfd.fd = memfd; - item = KDBUS_PART_NEXT(item); } + item = KDBUS_PART_NEXT(item); ret = ioctl(conn->fd, KDBUS_CMD_MSG_SEND, msg); if (ret) { @@ -194,7 +184,8 @@ int msg_send(const struct conn *conn, return EXIT_FAILURE; } - close(memfd); + if (memfd >= 0) + close(memfd); free(msg); return 0; @@ -210,9 +201,9 @@ char *msg_id(uint64_t id, char *buf) return buf; } -void msg_dump(struct kdbus_msg *msg) +void msg_dump(const struct conn *conn, const struct kdbus_msg *msg) { - struct kdbus_item *item = msg->items; + const struct kdbus_item *item = msg->items; char buf[32]; printf("MESSAGE: %s (%llu bytes) flags=0x%llx, %s → %s, cookie=%llu, timeout=%llu\n", @@ -228,14 +219,17 @@ void msg_dump(struct kdbus_msg *msg) } switch (item->type) { - case KDBUS_MSG_PAYLOAD_VEC: { - char *s = (char *)KDBUS_PTR(item->vec.address); + case KDBUS_MSG_PAYLOAD_OFF: { + char *s; - if (!s) + if (item->vec.offset == ~0ULL) s = "[padding bytes]"; + else + s = (char *)conn->buf + item->vec.offset; - printf(" +%s (%llu bytes) addr=%p size=%llu '%s'\n", - enum_MSG(item->type), item->size, KDBUS_PTR(item->vec.address), + printf(" +%s (%llu bytes) off=%llu size=%llu '%s'\n", + enum_MSG(item->type), item->size, + (unsigned long long)item->vec.offset, (unsigned long long)item->vec.size, s); break; } @@ -282,7 +276,7 @@ void msg_dump(struct kdbus_msg *msg) case KDBUS_MSG_SRC_CMDLINE: case KDBUS_MSG_SRC_NAMES: { size_t size = item->size - KDBUS_PART_HEADER_SIZE; - char *str = item->str; + const char *str = item->str; int count = 0; printf(" +%s (%llu bytes) ", enum_MSG(item->type), item->size); @@ -306,7 +300,7 @@ void msg_dump(struct kdbus_msg *msg) case KDBUS_MSG_SRC_CAPS: { int n; - uint32_t *cap; + const uint32_t *cap; int i; printf(" +%s (%llu bytes) len=%llu bytes)\n", @@ -378,20 +372,20 @@ void msg_dump(struct kdbus_msg *msg) int msg_recv(struct conn *conn) { - uint64_t addr; + uint64_t off; struct kdbus_msg *msg; int ret; - ret = ioctl(conn->fd, KDBUS_CMD_MSG_RECV, &addr); + ret = ioctl(conn->fd, KDBUS_CMD_MSG_RECV, &off); if (ret < 0) { fprintf(stderr, "error receiving message: %d (%m)\n", ret); return EXIT_FAILURE; } - msg = KDBUS_PTR(addr); - msg_dump(msg); + msg = (struct kdbus_msg *)(conn->buf + off); + msg_dump(conn, msg); - ret = ioctl(conn->fd, KDBUS_CMD_MSG_RELEASE, msg); + ret = ioctl(conn->fd, KDBUS_CMD_MSG_RELEASE, &off); if (ret < 0) { fprintf(stderr, "error free message: %d (%m)\n", ret); return EXIT_FAILURE; diff --git a/test/kdbus-util.h b/test/kdbus-util.h index 3f65154b5c9..ead5b2895d6 100644 --- a/test/kdbus-util.h +++ b/test/kdbus-util.h @@ -30,13 +30,15 @@ struct conn { int fd; uint64_t id; + void *buf; + size_t size; }; int name_list(struct conn *conn); int name_release(struct conn *conn, const char *name); int name_acquire(struct conn *conn, const char *name, uint64_t flags); int msg_recv(struct conn *conn); -void msg_dump(struct kdbus_msg *msg); +void msg_dump(const struct conn *conn, const struct kdbus_msg *msg); char *msg_id(uint64_t id, char *buf); int msg_send(const struct conn *conn, const char *name, uint64_t cookie, uint64_t dst_id); struct conn *connect_to_bus(const char *path); diff --git a/test/test-kdbus.c b/test/test-kdbus.c index e1055a5f1d8..7485d4cec02 100644 --- a/test/test-kdbus.c +++ b/test/test-kdbus.c @@ -126,6 +126,7 @@ int main(int argc, char *argv[]) msg_recv(conn_a); msg_send(conn_a, NULL, 0xc0000000 | cookie++, conn_b->id); } + if (fds[1].revents & POLLIN) { msg_recv(conn_b); msg_send(conn_b, NULL, 0xc0000000 | cookie++, conn_a->id); |