From: Daniel Mack Date: Mon, 12 May 2014 12:39:59 +0000 (+0200) Subject: switch to upstream kernel memfd implementation (ABI+API break) X-Git-Tag: upstream/0.20140911.160207utc~36 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=169709feb17217c20ecf9e411c4a00ac1884c535;p=platform%2Fcore%2Fsystem%2Fkdbus-bus.git switch to upstream kernel memfd implementation (ABI+API break) Now that a generic memfd solution is merged upstream, drop our own implementation and switch over. --- diff --git a/Makefile b/Makefile index 178257b..b334cee 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,6 @@ kdbus$(EXT)-y := \ connection.o \ endpoint.o \ handle.o \ - memfd.o \ main.o \ match.o \ message.o \ diff --git a/connection.c b/connection.c index 9b96647..99ac2a1 100644 --- a/connection.c +++ b/connection.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -30,7 +31,6 @@ #include "connection.h" #include "endpoint.h" #include "match.h" -#include "memfd.h" #include "message.h" #include "metadata.h" #include "names.h" @@ -218,6 +218,7 @@ static int kdbus_conn_memfd_ref(const struct kdbus_item *item, struct file **file) { struct file *fp; + int seals, mask; int ret; fp = fget(item->memfd.fd); @@ -225,23 +226,22 @@ static int kdbus_conn_memfd_ref(const struct kdbus_item *item, return -EBADF; /* - * We only accept kdbus_memfd files as payload, other files need to - * be passed with KDBUS_MSG_FDS. + * We only accept a sealed memfd file whose content cannot be altered + * by the sender or anybody else while it is shared or in-flight. + * Other files need to be passed with KDBUS_MSG_FDS. */ - if (!kdbus_is_memfd(fp)) { - ret = -EMEDIUMTYPE; - goto exit_unref; - } + seals = shmem_get_seals(fp); + if (seals < 0) + return -EMEDIUMTYPE; - /* We only accept a sealed memfd file whose content cannot be altered - * by the sender or anybody else while it is shared or in-flight. */ - if (!kdbus_is_memfd_sealed(fp)) { + mask = F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE; + if ((seals & mask) != mask) { ret = -ETXTBSY; goto exit_unref; } /* The specified size in the item cannot be larger than the file. */ - if (item->memfd.size > kdbus_memfd_size(fp)) { + if (item->memfd.size > i_size_read(file_inode(fp))) { ret = -EBADF; goto exit_unref; } diff --git a/handle.c b/handle.c index fc15d28..d9a2e1a 100644 --- a/handle.c +++ b/handle.c @@ -29,7 +29,6 @@ #include "endpoint.h" #include "handle.h" #include "match.h" -#include "memfd.h" #include "message.h" #include "metadata.h" #include "names.h" @@ -238,66 +237,6 @@ static int kdbus_memdup_user(void __user *user_ptr, return 0; } -static int kdbus_handle_memfd(void __user *buf) -{ - struct kdbus_cmd_memfd_make *m = NULL; - const struct kdbus_item *item; - const char *n = NULL; - int __user *addr; - int fd, ret; - - ret = kdbus_memdup_user(buf, (void **)&m, NULL, - sizeof(struct kdbus_cmd_memfd_make), - sizeof(struct kdbus_cmd_memfd_make) + - KDBUS_MAKE_MAX_SIZE); - if (ret < 0) - return ret; - - KDBUS_ITEMS_FOREACH(item, m->items, KDBUS_ITEMS_SIZE(m, items)) { - if (!KDBUS_ITEM_VALID(item, &m->items, - KDBUS_ITEMS_SIZE(m, items))) { - ret = -EINVAL; - goto exit; - } - - switch (item->type) { - case KDBUS_ITEM_MEMFD_NAME: - if (n) { - ret = -EEXIST; - goto exit; - } - - ret = kdbus_item_validate_name(item); - if (ret < 0) - goto exit; - - n = item->str; - break; - } - } - - if (!KDBUS_ITEMS_END(item, m->items, KDBUS_ITEMS_SIZE(m, items))) { - ret = -EINVAL; - goto exit; - } - - ret = kdbus_memfd_new(n, m->file_size, &fd); - if (ret < 0) - goto exit; - - /* return fd number to caller */ - addr = buf + offsetof(struct kdbus_cmd_memfd_make, fd); - if (put_user(fd, addr)) { - sys_close(fd); - ret = -EFAULT; - goto exit; - } - -exit: - kfree(m); - return ret; -} - /* kdbus control device commands */ static long kdbus_handle_ioctl_control(struct file *file, unsigned int cmd, void __user *buf) @@ -387,10 +326,6 @@ static long kdbus_handle_ioctl_control(struct file *file, unsigned int cmd, break; } - case KDBUS_CMD_MEMFD_NEW: - ret = kdbus_handle_memfd(buf); - break; - default: ret = -ENOTTY; break; @@ -793,10 +728,6 @@ static long kdbus_handle_ioctl_ep_connected(struct file *file, unsigned int cmd, break; } - case KDBUS_CMD_MEMFD_NEW: - ret = kdbus_handle_memfd(buf); - break; - default: ret = -ENOTTY; break; diff --git a/kdbus.h b/kdbus.h index b060330..3751f9c 100644 --- a/kdbus.h +++ b/kdbus.h @@ -733,24 +733,6 @@ struct kdbus_cmd_match { struct kdbus_item items[0]; } __attribute__((aligned(8))); -/** - * struct kdbus_cmd_memfd_make - create a kdbus memfd - * @size: The total size of the struct - * @file_size: The initial file size - * @fd: The returned file descriptor number - * @__pad: Padding to ensure proper alignement - * @items: A list of items for additional information - * - * This structure is used with the KDBUS_CMD_MEMFD_NEW ioctl. - */ -struct kdbus_cmd_memfd_make { - __u64 size; - __u64 file_size; - int fd; - __u32 __pad; - struct kdbus_item items[0]; -} __attribute__((aligned(8))); - /** * enum kdbus_ioctl_type - Ioctl API * @KDBUS_CMD_BUS_MAKE: After opening the "control" device node, this @@ -801,32 +783,6 @@ struct kdbus_cmd_memfd_make { * @KDBUS_CMD_MATCH_ADD: Install a match which broadcast messages should * be delivered to the connection. * @KDBUS_CMD_MATCH_REMOVE: Remove a current match for broadcast messages. - * @KDBUS_CMD_MEMFD_NEW: Return a new file descriptor which provides an - * anonymous shared memory file and which can be - * used to pass around larger chunks of data. - * Kdbus memfd files can be sealed, which allows - * the receiver to trust the data it has received. - * Kdbus memfd files expose only very limited - * operations, they can be mmap()ed, seek()ed, - * (p)read(v)() and (p)write(v)(); most other - * common file operations are not implemented. - * Special caution needs to be taken with - * read(v)()/write(v)() on a shared file; the - * underlying file position is always shared - * between all users of the file and race against - * each other, pread(v)()/pwrite(v)() avoid these - * issues. - * @KDBUS_CMD_MEMFD_SIZE_GET: Return the size of the underlying file, which - * changes with write(). - * @KDBUS_CMD_MEMFD_SIZE_SET: Truncate the underlying file to the specified - * size. - * @KDBUS_CMD_MEMFD_SEAL_GET: Return the state of the file sealing. - * @KDBUS_CMD_MEMFD_SEAL_SET: Seal or break a seal of the file. Only files - * which are not shared with other processes and - * which are currently not mapped can be sealed. - * The current process needs to be the one and - * single owner of the file, the sealing cannot - * be changed as long as the file is shared. */ enum kdbus_ioctl_type { KDBUS_CMD_BUS_MAKE = _IOW(KDBUS_IOCTL_MAGIC, 0x00, @@ -866,13 +822,6 @@ enum kdbus_ioctl_type { struct kdbus_cmd_match), KDBUS_CMD_MATCH_REMOVE = _IOW(KDBUS_IOCTL_MAGIC, 0x81, struct kdbus_cmd_match), - - KDBUS_CMD_MEMFD_NEW = _IOWR(KDBUS_IOCTL_MAGIC, 0xc0, - struct kdbus_cmd_memfd_make), - KDBUS_CMD_MEMFD_SIZE_GET = _IOR(KDBUS_IOCTL_MAGIC, 0xc1, __u64 *), - KDBUS_CMD_MEMFD_SIZE_SET = _IOW(KDBUS_IOCTL_MAGIC, 0xc2, __u64 *), - KDBUS_CMD_MEMFD_SEAL_GET = _IOR(KDBUS_IOCTL_MAGIC, 0xc3, int *), - KDBUS_CMD_MEMFD_SEAL_SET = _IO(KDBUS_IOCTL_MAGIC, 0xc4), }; /* diff --git a/kdbus.txt b/kdbus.txt index 1d68eab..cec4b20 100644 --- a/kdbus.txt +++ b/kdbus.txt @@ -350,19 +350,16 @@ that way two peers can exchange data by effectively doing a single-copy from one process to another, the kernel will not buffer the data anywhere else. KDBUS_MSG_PAYLOAD_MEMFD: -Messages can reference kdbus_memfd special files which contain the data. -Kdbus_memfd files have special semantics, which allow the sealing of the -content of the file, sealing prevents all writable access to the file content. -Only sealed kdbus_memfd files are accepted as payload data, which enforces +Messages can reference memfd files which contain the data. +memfd files are tmpfs-backed files that allow sealing of the content of the +file, which prevents all writable access to the file content. +Only sealed memfd files are accepted as payload data, which enforces reliable passing of data; the receiver can assume that the sender and nobody else can alter the content after the message is sent. -Apart from the sender filling-in the content into the kdbus_memfd file, the -data will be passed as zero-copy from one process to another, read-only, shared -between the peers. - -The sealing of a kdbus_memfd can be removed again by the sender or the -receiver, as soon as the kdbus_memfd is not shared anymore. +Apart from the sender filling-in the content into memfd files, the data will +be passed as zero-copy from one process to another, read-only, shared between +the peers. =============================================================================== Broadcast Message Matching diff --git a/memfd.c b/memfd.c deleted file mode 100644 index cc27124..0000000 --- a/memfd.c +++ /dev/null @@ -1,396 +0,0 @@ -/* - * Copyright (C) 2013 Kay Sievers - * Copyright (C) 2013 Greg Kroah-Hartman - * Copyright (C) 2013 Daniel Mack - * Copyright (C) 2013 Linux Foundation - * - * kdbus is free software; you can redistribute it and/or modify it under - * the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation; either version 2.1 of the License, or (at - * your option) any later version. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "memfd.h" -#include "util.h" - -static const struct file_operations kdbus_memfd_fops; - -/** - * struct kdbus_memfile - protectable shared memory file - * @name: Name of the (deleted) file which shows up in - * /proc, used for debugging - * @lock: Locking - * @fp: Shared memory backing file - * @sealed: Flag if the content is writable - */ -struct kdbus_memfile { - const char *name; - struct mutex lock; - struct file *fp; - bool sealed; -}; - -/** - * kdbus_is_memfd() - check if a file is one of our memfds - * @fp: File to check - * - * Return: true if the file is a memfd - */ -bool kdbus_is_memfd(const struct file *fp) -{ - return fp->f_op == &kdbus_memfd_fops; -} - -/** - * kdbus_is_memfd_sealed() - check if a memfd is protected - * @fp: Memfd file to check - * - * Return: true if the memfd is protected - */ -bool kdbus_is_memfd_sealed(const struct file *fp) -{ - struct kdbus_memfile *mf = fp->private_data; - - return mf->sealed; -} - -/** - * kdbus_memfd_size() - return the actual size of a memfd - * @fp: Memfd file to check - * - * Return: the actual size of the file in bytes - */ -u64 kdbus_memfd_size(const struct file *fp) -{ - struct kdbus_memfile *mf = fp->private_data; - - return i_size_read(file_inode(mf->fp)); -} - -/** - * kdbus_memfd_new() - create and install a memfd and file descriptor - * @name: Name of the (deleted) file which shows up in - * /proc, used for debugging - * @size: Initial size of the file - * @fd: Installed file descriptor - * - * Return: 0 on success, negative errno on failure. - */ -int kdbus_memfd_new(const char *name, size_t size, int *fd) -{ - const char *shmem_name = NULL; - const char *anon_name = NULL; - struct kdbus_memfile *mf; - struct file *shmemfp; - struct file *fp; - int f, ret; - - mf = kzalloc(sizeof(*mf), GFP_KERNEL); - if (!mf) - return -ENOMEM; - - mutex_init(&mf->lock); - - if (name) { - mf->name = kstrdup(name, GFP_KERNEL); - shmem_name = kasprintf(GFP_KERNEL, - KBUILD_MODNAME "-memfd:%s", name); - anon_name = kasprintf(GFP_KERNEL, - "[" KBUILD_MODNAME "-memfd:%s]", name); - if (!mf->name || !shmem_name || !anon_name) { - ret = -ENOMEM; - goto exit; - } - } - - /* allocate a new unlinked shmem file */ - shmemfp = shmem_file_setup(name ? shmem_name : KBUILD_MODNAME "-memfd", - size, 0); - if (IS_ERR(shmemfp)) { - ret = PTR_ERR(shmemfp); - goto exit; - } - mf->fp = shmemfp; - - f = get_unused_fd_flags(O_CLOEXEC); - if (f < 0) { - ret = f; - goto exit_shmem; - } - - /* - * The anonymous exported inode ops cannot reach the otherwise - * invisible shmem inode. We rely on the fact that nothing else - * can create a new file for the shmem inode, like by opening the - * fd in /proc/$PID/fd/ - */ - fp = anon_inode_getfile(name ? anon_name : "[" KBUILD_MODNAME "-memfd]", - &kdbus_memfd_fops, mf, O_RDWR); - if (IS_ERR(fp)) { - ret = PTR_ERR(fp); - goto exit_fd; - } - - fp->f_mode |= FMODE_LSEEK|FMODE_PREAD|FMODE_PWRITE; - fp->f_mapping = shmemfp->f_mapping; - fd_install(f, fp); - - kfree(anon_name); - kfree(shmem_name); - *fd = f; - return 0; - -exit_fd: - put_unused_fd(f); -exit_shmem: - fput(shmemfp); -exit: - kfree(anon_name); - kfree(shmem_name); - kfree(mf->name); - kfree(mf); - return ret; -} - -static int kdbus_memfd_release(struct inode *ignored, struct file *file) -{ - struct kdbus_memfile *mf = file->private_data; - - fput(mf->fp); - kfree(mf->name); - kfree(mf); - return 0; -} - -static loff_t kdbus_memfd_llseek(struct file *file, loff_t offset, int whence) -{ - struct kdbus_memfile *mf = file->private_data; - loff_t ret; - - mutex_lock(&mf->lock); - ret = mf->fp->f_op->llseek(mf->fp, offset, whence); - if (ret < 0) - goto exit; - - /* update the anonymous file */ - file->f_pos = mf->fp->f_pos; - -exit: - mutex_unlock(&mf->lock); - return ret; -} - -static ssize_t kdbus_memfd_read_iter(struct kiocb *iocb, struct iov_iter *iter) -{ - struct kdbus_memfile *mf = iocb->ki_filp->private_data; - ssize_t ret; - - mutex_lock(&mf->lock); - iocb->ki_filp = mf->fp; - ret = mf->fp->f_op->read_iter(iocb, iter); - if (ret < 0) - goto exit; - - /* update the shmem file */ - mf->fp->f_pos = iocb->ki_pos; - -exit: - mutex_unlock(&mf->lock); - return ret; -} - -static ssize_t kdbus_memfd_write_iter(struct kiocb *iocb, struct iov_iter *iter) -{ - struct kdbus_memfile *mf = iocb->ki_filp->private_data; - ssize_t ret; - - mutex_lock(&mf->lock); - - /* deny write access to a sealed file */ - if (mf->sealed) { - ret = -EPERM; - goto exit; - } - - iocb->ki_filp = mf->fp; - ret = mf->fp->f_op->write_iter(iocb, iter); - if (ret < 0) - goto exit; - - /* update the shmem file */ - mf->fp->f_pos = iocb->ki_pos; - -exit: - mutex_unlock(&mf->lock); - return ret; -} - -static int kdbus_memfd_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct kdbus_memfile *mf = file->private_data; - int ret = 0; - - if (vma->vm_flags & VM_WRITE) { - size_t size; - struct inode *inode; - - /* - * Deny a writable mapping to a sealed file. - * - * Avoid a deadlock and do not take mf->lock here, the call to - * mmap() already holds mm->mmap_sem. - * To protect KDBUS_CMD_MEMFD_SEAL_SET racing against us, - * mf->sealed is changed only with mm->mmap_sem held. - */ - if (mf->sealed) { - ret = -EPERM; - goto exit; - } - - /* - * Extend the size of the shmem file to the - * size of the mapping - */ - size = (vma->vm_end - vma->vm_start) + - (vma->vm_pgoff << PAGE_SHIFT); - inode = file_inode(mf->fp); - if (size > PAGE_ALIGN(i_size_read(inode))) - i_size_write(inode, size); - } - - /* replace the anoymous inode file with our shmem file */ - if (vma->vm_file) - fput(vma->vm_file); - vma->vm_file = get_file(mf->fp); - ret = mf->fp->f_op->mmap(file, vma); - -exit: - return ret; -} - -static long -kdbus_memfd_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -{ - void __user *argp = (void __user *)arg; - struct kdbus_memfile *mf = file->private_data; - long ret = 0; - - mutex_lock(&mf->lock); - switch (cmd) { - case KDBUS_CMD_MEMFD_SIZE_GET: { - u64 size = i_size_read(file_inode(mf->fp)); - - if (!KDBUS_IS_ALIGNED8(arg)) { - ret = -EFAULT; - goto exit; - } - - if (copy_to_user(argp, &size, sizeof(__u64))) { - ret = -EFAULT; - goto exit; - } - break; - } - - case KDBUS_CMD_MEMFD_SIZE_SET: { - u64 size; - - if (!KDBUS_IS_ALIGNED8(arg)) { - ret = -EFAULT; - goto exit; - } - - if (copy_from_user(&size, argp, sizeof(__u64))) { - ret = -EFAULT; - goto exit; - } - - /* deny a writable access to a sealed file */ - if (mf->sealed) { - if (size == i_size_read(file_inode(mf->fp))) - ret = -EALREADY; - else - ret = -EPERM; - goto exit; - } - - if (size != i_size_read(file_inode(mf->fp))) - ret = vfs_truncate(&mf->fp->f_path, size); - break; - } - - case KDBUS_CMD_MEMFD_SEAL_GET: { - int __user *addr = argp; - - if (put_user(mf->sealed, addr)) { - ret = -EFAULT; - goto exit; - } - break; - } - - case KDBUS_CMD_MEMFD_SEAL_SET: { - struct mm_struct *mm = current->mm; - - /* - * Make sure we have only one single user of the file - * before we seal, we rely on the fact there is no - * any other possibly writable references to the file. - * - * Protect mmap() racing against us, take mm->mmap_sem - * when accessing mf->sealed. - */ - down_read(&mm->mmap_sem); - /* FIXME: It is insufficient to check the back-fd, we - * only track mmap() here, the front fd has a refcount - * of 2 as soon as the task has more than one thread. - */ - if (file_count(mf->fp) != 1) { - if (mf->sealed == !!argp) - ret = -EALREADY; - else - ret = -ETXTBSY; - } - - if (ret == 0) - mf->sealed = !!argp; - up_read(&mm->mmap_sem); - break; - } - - default: - ret = -ENOTTY; - break; - } - -exit: - mutex_unlock(&mf->lock); - return ret; -} - -static const struct file_operations kdbus_memfd_fops = { - .owner = THIS_MODULE, - .release = kdbus_memfd_release, - .read_iter = kdbus_memfd_read_iter, - .write_iter = kdbus_memfd_write_iter, - .llseek = kdbus_memfd_llseek, - .mmap = kdbus_memfd_mmap, - .unlocked_ioctl = kdbus_memfd_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = kdbus_memfd_ioctl, -#endif -}; diff --git a/memfd.h b/memfd.h deleted file mode 100644 index acfc70a..0000000 --- a/memfd.h +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright (C) 2013 Kay Sievers - * Copyright (C) 2013 Greg Kroah-Hartman - * Copyright (C) 2013 Daniel Mack - * Copyright (C) 2013 Linux Foundation - * - * 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_MEMFD_H -#define __KDBUS_MEMFD_H - -bool kdbus_is_memfd(const struct file *fp); -bool kdbus_is_memfd_sealed(const struct file *fp); -u64 kdbus_memfd_size(const struct file *fp); -int kdbus_memfd_new(const char *name, size_t size, int *fd); -#endif