Received: by 2002:ad5:474a:0:0:0:0:0 with SMTP id i10csp1811903imu; Wed, 12 Dec 2018 04:53:56 -0800 (PST) X-Google-Smtp-Source: AFSGD/XjkaYJE7k3+tBySx48dyMJgnQe0ftXJGdlDuL0ZPSkpkueRv7njCZAFr3n04fhPRy7SDax X-Received: by 2002:a63:451a:: with SMTP id s26mr18330329pga.150.1544619236095; Wed, 12 Dec 2018 04:53:56 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1544619236; cv=none; d=google.com; s=arc-20160816; b=0AD3YwejKbSpYfkmUEuJqinaHahsXB65ihytDG1OhDGPcGXpbxeKemJjvGsmVTd4Il Wt7psu3Klk7Oc8DF+NniFiq1DTcxZBAXFYYJaAG1jPhBnkbV8v1tWd7qzpcOX8w0ixKM dN8xVHUkniHPDLiNkltU3PIq5A9pbnpG21JS7g/UaaY5wVJceGVGOTC++X5U2R2hUNnj LJUNRu/11p9qXOkMPv2LBhV0e5VJsrWFpdNL7/Y5eUIWGhoSOjnLGkaQDgJIUzzoQ2hQ q5aJBq9WZm5TlQYYcqgfM6aNOvPz8K3oLgv0HLj4UVzcXF/hAS1nChlcUJJLEkOvl4Ds tbBQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:user-agent:in-reply-to :content-disposition:mime-version:references:message-id:subject:cc :to:from:date:dkim-signature; bh=0sYzmQBUkLScpvKsrmWAwQEHf2TcQFHDBFaKmHJeS/g=; b=mCjrwcjqqwHgeEkzaxCpi2RjgXRj+NNonSXMX3xvAOCXo5WCcedQnjtojzWpKzcjPs 3UeM2tFAkbNkTiyIHLolViKnoMF+Si2byZSJKZUW8GTKaE/qUGL7G76sjxXHDZglBntI 73kKKyYIoxHPYAzGXCfKyo7InobLT9s482zFdDpw3hN4DZdjGF1UciJoovnFgyBluVR+ FgbQuDcn0XRUSNPckg66eKlbZ7w8T2NBatiDpCrXze1pW9pJGgDDd377rWAfnMRy7svp lFeUh7OkqX60XFD1sKp/WZvFy2tuo7Zao4PO9T/WYKAnk+qoMOlclt1k4aqz+uQfFTIO JlWA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@brauner.io header.s=google header.b=RdO3RJ7e; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id n32si14704148pgm.439.2018.12.12.04.53.40; Wed, 12 Dec 2018 04:53:56 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@brauner.io header.s=google header.b=RdO3RJ7e; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727454AbeLLMve (ORCPT + 99 others); Wed, 12 Dec 2018 07:51:34 -0500 Received: from mail-wr1-f65.google.com ([209.85.221.65]:44046 "EHLO mail-wr1-f65.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727161AbeLLMvd (ORCPT ); Wed, 12 Dec 2018 07:51:33 -0500 Received: by mail-wr1-f65.google.com with SMTP id z5so17549041wrt.11 for ; Wed, 12 Dec 2018 04:51:30 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=brauner.io; s=google; h=date:from:to:cc:subject:message-id:references:mime-version :content-disposition:in-reply-to:user-agent; bh=0sYzmQBUkLScpvKsrmWAwQEHf2TcQFHDBFaKmHJeS/g=; b=RdO3RJ7eFV2Ny99czkZnHQ9W5gm0JQweQw7b5VkaTwsNRlj5pZyBrf0mrD24EarPLO ueHBrcuVy7G0+QC96/fdps+YW5WYLOwgoBoi1X2R3R9sMKqMWyHPcfF5U+yMsC0uO51y MoaMUX6TD7izAJL1ZFKFDUlcJoXs7NBQmURk7vMP5O2ojAe6SA4kkslsENiU5qBznuNh n5ctrfA6qXdhDAO/uUAVxSO2jcPVgqDrr5Y0hABdixewDVUw+pW+p5zfpFEPcdL4GqaL ILkxaxjL/6RsHMP/GOKn9eAEMaWqASAt24dzWrw2lvWbjP+SOByFyAPoYiEUsn/FpftS XS4g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:from:to:cc:subject:message-id:references :mime-version:content-disposition:in-reply-to:user-agent; bh=0sYzmQBUkLScpvKsrmWAwQEHf2TcQFHDBFaKmHJeS/g=; b=FSwncDvr9iSfJh8olnifyXXP7iL7Ok9mBKFBo6E0Ynv+INHBj12CEZzlKkpfvj4W7n GwzckAc39dbkiXVxM+ZQJFjFYhLgdN5VrBFLYcYLxLmLo7fYa2IV1Bm5ZGEeVNuIVo5R odnt/nqihQ/rv4O2PdHA7ENzPj8NGThmuL7r9MwLxjB/mlpiFiUqIphpX962+pEEUcNa +fvZjY3Kd/IpnU+T2f2ZnvIYHsUE9Qgn4UKrz8DxzxeHxquvgXR6PaTonx3agV9ZNTEu Ncs0V5RREiVzjy/TL9h+TpNPhjgpDI8XiWn7m9aD4AltWsPWI6H0Q0Irla6nKckALc4h Efog== X-Gm-Message-State: AA+aEWbcWn7+H8KXUGzcPPJFj0HhckAGq6JN11ndzeKKcTe4xZPNppty Ql/e1XLFaqQppqfw+QHRaYLlzA== X-Received: by 2002:a5d:4286:: with SMTP id k6mr17198704wrq.225.1544619089307; Wed, 12 Dec 2018 04:51:29 -0800 (PST) Received: from brauner.io ([134.3.72.161]) by smtp.gmail.com with ESMTPSA id o4sm2830979wmh.40.2018.12.12.04.51.28 (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Wed, 12 Dec 2018 04:51:28 -0800 (PST) Date: Wed, 12 Dec 2018 13:51:27 +0100 From: Christian Brauner To: Todd Kjos , Greg Kroah-Hartman Cc: Todd Kjos , Martijn Coenen , LKML , Arve =?utf-8?B?SGrDuG5uZXbDpWc=?= , joel@joelfernandes.org, darrick.wong@oracle.com, david@fromorbit.com, kilobyte@angband.pl, "open list:ANDROID DRIVERS" , chouryzhou@tencent.com Subject: Re: [PATCH v1] binder: implement binderfs Message-ID: <20181212125126.4lhnjvrvaz5wwpib@brauner.io> References: <20181207232509.31771-1-christian@brauner.io> MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Disposition: inline In-Reply-To: User-Agent: NeoMutt/20180716 Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org > On Fri, Dec 7, 2018 at 3:26 PM Christian Brauner wrote: > > > > As discussed at Linux Plumbers Conference 2018 in Vancouver [1] this is the > > implementation of binderfs. > > > > binderfs is a backwards-compatible filesystem for Android's binder ipc > > mechanism. Each ipc namespace will mount a new binderfs instance. Mounting > > binderfs multiple times at different locations in the same ipc namespace > > will not cause a new super block to be allocated and hence it will be the > > same filesystem instance. > > Each new binderfs mount will have its own set of binder devices only > > visible in the ipc namespace it has been mounted in. All devices in a new > > binderfs mount will follow the scheme binder%d and numbering will always > > start at 0. > > > > /* Backwards compatibility */ > > Devices requested in the Kconfig via CONFIG_ANDROID_BINDER_DEVICES for the > > initial ipc namespace will work as before. They will be registered via > > misc_register() and appear in the devtmpfs mount. Specifically, the > > standard devices binder, hwbinder, and vndbinder will all appear in their > > standard locations in /dev. Mounting or unmounting the binderfs mount in > > the initial ipc namespace will have no effect on these devices, i.e. they > > will neither show up in the binderfs mount nor will they disappear when the > > binderfs mount is gone. > > > > /* binder-control */ > > Each new binderfs instance comes with a binder-control device. No other > > devices will be present at first. The binder-control device can be used to > > dynamically allocate binder devices. All requests operate on the binderfs > > mount the binder-control device resides in: > > - BINDER_CTL_ADD > > Allocate a new binder device. > > Assuming a new instance of binderfs has been mounted at /dev/binderfs via > > mount -t binderfs binderfs /dev/binderfs. Then a request to create a new > > binder device can be made via: > > > > struct binderfs_device device = {0}; > > snprintf(device.name, BINDERFS_MAX_NAME, "%s", "my-device"); > > int fd = open("/dev/binderfs/binder-control", O_RDWR); > > ioctl(fd, BINDER_CTL_ADD, &device); > > > > binderfs will then allocate a new minor number and create the device > > "my-device". > > The struct binderfs_device will then be used to return the major and minor > > number, for the device. > > Binderfs devices can simply be removed via unlink(). > > > > /* Implementation details */ > > - When binderfs is registered as a new filesystem it will dynamically > > allocate a new major number. The allocated major number will be returned > > in struct binderfs_device when a new binder device is allocated. > > Minor numbers that have been given out are tracked in a global idr struct > > that is capped at BINDERFS_MAX_MINOR. The minor number tracker is > > protected by a global mutex. This is the only point of contention between > > binderfs mounts. > > - Each binderfs super block has its own struct binderfs_info that tracks > > specific details about a binderfs instance: the ipc namespace, the dentry > > of the binder-control device, the root uid and gid of the user namespace > > the binderfs instance was mounted in. > > - binderfs can be mounted by user namespace root in a non-initial user > > namespace. The devices will be owned by user namespace root. > > - New binder devices associated with a binderfs mount do not use the > > full misc_register() infrastructure. The misc_register() infrastructure > > can only create new devices in the host's devtmpfs mount. binderfs does > > however only make devices appear under its own mountpoint and thus > > allocates new character devices nodes from the inode of the root dentry > > of the super block. This will have the side-effect that binderfs specific > > device nodes do not appear in sysfs. This behavior is similar to devpts > > allocated pts devices and has no effect on the functionality of the ipc > > mechanism itself. > > > > /* Create a new binder device in a binderfs mount */ > > sudo mkdir /dev/binderfs > > sudo mount -t binder binder /dev/binderfs > > > > #define _GNU_SOURCE > > #include > > #include > > #include > > #include > > #include > > #include > > #include > > #include > > #include > > #include > > > > int main(int argc, char *argv[]) > > { > > int fd, ret, saved_errno; > > struct binderfs_device device = { 0 }; > > > > if (argc < 2) > > exit(EXIT_FAILURE); > > > > strncpy(device.name, argv[1], sizeof(device.name)); > > > > fd = open("/dev/binderfs/binder-control", O_RDONLY | O_CLOEXEC); > > if (fd < 0) { > > printf("%s - Failed to open binder-control device\n", > > strerror(errno)); > > exit(EXIT_FAILURE); > > } > > > > ret = ioctl(fd, BINDER_CTL_ADD, &device); > > saved_errno = errno; > > close(fd); > > errno = saved_errno; > > if (ret < 0) { > > printf("%s - Failed to allocate new binder device\n", > > strerror(errno)); > > exit(EXIT_FAILURE); > > } > > > > printf("Allocated new binder device with major %d, minor %d, and " > > "name %s\n", device.major, device.minor, > > device.name); > > > > exit(EXIT_SUCCESS); > > } > > > > /* Demo */ > > A demo of how binderfs works can be found under [2]. > > > > [1]: https://goo.gl/JL2tfX > > > > Cc: Martijn Coenen > > Cc: Todd Kjos > > Cc: Greg Kroah-Hartman > > Signed-off-by: Christian Brauner Do we plan to bring this into mergeable shape before Christmas? I'm happy to do it. :) Christian > > --- > > v1: > > - simplify init_binderfs() > > Move the creation of binder-control into binderfs_fill_super() so that we can > > cleanly and without any complex error handling deallocate the super block on > > failure. > > - switch from __u32 to __u8 in struct binderfs_device > > __u8 is the correct value to cross the kernel <-> userspace boundary. > > - introduce BINDERFS_MAX_NAME > > This determines the maximum length of a binderfs binder device name. > > - add name member struct binderfs_device > > This lets userspace specify a name for the binder device. The maximum length > > is determined by BINDERFS_MAX_NAME. > > - handle naming collisions > > Since userspace now gives us a name to use for the device we need to handle > > the case where userspace passes the same device name twice. This is done by > > using d_lookup() (takes rename lock too). If a matching dentry under the root > > dentry of the superblock is found we test whether it is still active and if > > so return -EEXIST. > > - remove per-super block idr tracking and locking > > Since userspace now determines the name remove the idr tracking since it's > > not needed anymore. > > - remove ctl_mutex from struct binders_info > > It was only needed to protect the per-super block idr which has been removed. > > If I'm not mistaken we currently do not need to lock the ioctl() itself since > > removing is handled by a simple unlink(). So remove the mutex. > > - ensure that binderfs_evict_inode() doesn't cause double-frees on iput() > > When setting up the inode fails at a step where a new inode already has been > > allocated we need to set inode->i_private to NULL to ensure that > > binderfs_evict_inode() doesn't try to free up stuff that we already freed > > while handling the error. > > --- > > drivers/android/Kconfig | 12 + > > drivers/android/Makefile | 1 + > > drivers/android/binder.c | 25 +- > > drivers/android/binder_internal.h | 49 ++ > > drivers/android/binderfs.c | 565 ++++++++++++++++++++++++ > > include/uapi/linux/android/binder_ctl.h | 35 ++ > > include/uapi/linux/magic.h | 1 + > > 7 files changed, 671 insertions(+), 17 deletions(-) > > create mode 100644 drivers/android/binder_internal.h > > create mode 100644 drivers/android/binderfs.c > > create mode 100644 include/uapi/linux/android/binder_ctl.h > > > > diff --git a/drivers/android/Kconfig b/drivers/android/Kconfig > > index 51e8250d113f..4c190f8d1f4c 100644 > > --- a/drivers/android/Kconfig > > +++ b/drivers/android/Kconfig > > @@ -20,6 +20,18 @@ config ANDROID_BINDER_IPC > > Android process, using Binder to identify, invoke and pass arguments > > between said processes. > > > > +config ANDROID_BINDERFS > > + bool "Android Binderfs filesystem" > > + depends on ANDROID_BINDER_IPC > > + default n > > + ---help--- > > + Binderfs is a pseudo-filesystem for the Android Binder IPC driver > > + which can be mounted per-ipc namespace allowing to run multiple > > + instances of Android. > > + Each binderfs mount initially only contains a binder-control device. > > + It can be used to dynamically allocate new binder IPC devices via > > + ioctls. > > + > > config ANDROID_BINDER_DEVICES > > string "Android Binder devices" > > depends on ANDROID_BINDER_IPC > > diff --git a/drivers/android/Makefile b/drivers/android/Makefile > > index a01254c43ee3..c7856e3200da 100644 > > --- a/drivers/android/Makefile > > +++ b/drivers/android/Makefile > > @@ -1,4 +1,5 @@ > > ccflags-y += -I$(src) # needed for trace events > > > > +obj-$(CONFIG_ANDROID_BINDERFS) += binderfs.o > > obj-$(CONFIG_ANDROID_BINDER_IPC) += binder.o binder_alloc.o > > obj-$(CONFIG_ANDROID_BINDER_IPC_SELFTEST) += binder_alloc_selftest.o > > diff --git a/drivers/android/binder.c b/drivers/android/binder.c > > index cb30a524d16d..3ed8bc4b7451 100644 > > --- a/drivers/android/binder.c > > +++ b/drivers/android/binder.c > > @@ -78,6 +78,7 @@ > > #include > > > > #include "binder_alloc.h" > > +#include "binder_internal.h" > > #include "binder_trace.h" > > > > static HLIST_HEAD(binder_deferred_list); > > @@ -262,20 +263,6 @@ static struct binder_transaction_log_entry *binder_transaction_log_add( > > return e; > > } > > > > -struct binder_context { > > - struct binder_node *binder_context_mgr_node; > > - struct mutex context_mgr_node_lock; > > - > > - kuid_t binder_context_mgr_uid; > > - const char *name; > > -}; > > - > > -struct binder_device { > > - struct hlist_node hlist; > > - struct miscdevice miscdev; > > - struct binder_context context; > > -}; > > - > > /** > > * struct binder_work - work enqueued on a worklist > > * @entry: node enqueued on list > > @@ -4935,8 +4922,12 @@ static int binder_open(struct inode *nodp, struct file *filp) > > proc->tsk = current->group_leader; > > INIT_LIST_HEAD(&proc->todo); > > proc->default_priority = task_nice(current); > > - binder_dev = container_of(filp->private_data, struct binder_device, > > - miscdev); > > + /* binderfs stashes devices in i_private */ > > + if (is_binderfs_device(nodp)) > > + binder_dev = nodp->i_private; > > + else > > + binder_dev = container_of(filp->private_data, > > + struct binder_device, miscdev); > > proc->context = &binder_dev->context; > > binder_alloc_init(&proc->alloc); > > > > @@ -5724,7 +5715,7 @@ static int binder_transaction_log_show(struct seq_file *m, void *unused) > > return 0; > > } > > > > -static const struct file_operations binder_fops = { > > +const struct file_operations binder_fops = { > > .owner = THIS_MODULE, > > .poll = binder_poll, > > .unlocked_ioctl = binder_ioctl, > > diff --git a/drivers/android/binder_internal.h b/drivers/android/binder_internal.h > > new file mode 100644 > > index 000000000000..7fb97f503ef2 > > --- /dev/null > > +++ b/drivers/android/binder_internal.h > > @@ -0,0 +1,49 @@ > > +/* SPDX-License-Identifier: GPL-2.0 */ > > + > > +#ifndef _LINUX_BINDER_INTERNAL_H > > +#define _LINUX_BINDER_INTERNAL_H > > + > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > + > > +struct binder_context { > > + struct binder_node *binder_context_mgr_node; > > + struct mutex context_mgr_node_lock; > > + kuid_t binder_context_mgr_uid; > > + const char *name; > > +}; > > + > > +/** > > + * struct binder_device - information about a binder device node > > + * @hlist: list of binder devices (only used for devices requested via > > + * CONFIG_ANDROID_BINDER_DEVICES) > > + * @miscdev: information about a binder character device node > > + * @context: binder context information > > + * @binderfs_inode: This is the inode of the root dentry of the super block > > + * belonging to a binderfs mount. > > + */ > > +struct binder_device { > > + struct hlist_node hlist; > > + struct miscdevice miscdev; > > + struct binder_context context; > > + struct inode *binderfs_inode; > > +}; > > + > > +extern const struct file_operations binder_fops; > > + > > +#ifdef CONFIG_ANDROID_BINDERFS > > +extern bool is_binderfs_device(const struct inode *inode); > > +#else > > +static inline bool is_binderfs_device(const struct inode *inode) > > +{ > > + return false; > > +} > > +#endif > > + > > +#endif /* _LINUX_BINDER_INTERNAL_H */ > > diff --git a/drivers/android/binderfs.c b/drivers/android/binderfs.c > > new file mode 100644 > > index 000000000000..ac435210eb53 > > --- /dev/null > > +++ b/drivers/android/binderfs.c > > @@ -0,0 +1,565 @@ > > +/* SPDX-License-Identifier: GPL-2.0 */ > > + > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > + > > +#include "binder_internal.h" > > + > > +#define FIRST_INODE 1 > > +#define SECOND_INODE 2 > > +#define INODE_OFFSET 3 > > +#define INTSTRLEN 21 > > +#define BINDERFS_MAX_MINOR (1U << MINORBITS) > > + > > +static struct vfsmount *binderfs_mnt; > > + > > +static dev_t binderfs_dev; > > +static DEFINE_MUTEX(binderfs_minors_mutex); > > +static DEFINE_IDA(binderfs_minors); > > + > > +/** > > + * binderfs_info - information about a binderfs mount > > + * @ipc_ns: The ipc namespace the binderfs mount belongs to. > > + * @control_dentry: This records the dentry of this binderfs mount > > + * binder-control device. > > + * @root_uid: uid that needs to be used when a new binder device is > > + * created. > > + * @root_gid: gid that needs to be used when a new binder device is > > + * created. > > + */ > > +struct binderfs_info { > > + struct ipc_namespace *ipc_ns; > > + struct dentry *control_dentry; > > + kuid_t root_uid; > > + kgid_t root_gid; > > + > > +}; > > + > > +static inline struct binderfs_info *BINDERFS_I(struct inode *inode) > > +{ > > + return inode->i_sb->s_fs_info; > > +} > > + > > +bool is_binderfs_device(const struct inode *inode) > > +{ > > + if (inode->i_sb->s_magic == BINDERFS_SUPER_MAGIC) > > + return true; > > + > > + return false; > > +} > > + > > +/** > > + * binderfs_new_inode - allocate inode from super block of a binderfs mount > > + * @ref_inode: inode from wich the super block will be taken > > + * @userp: buffer to copy information about new device for userspace to > > + * @device: binder device for which the new inode will be allocated > > + * @req: struct binderfs_device as copied from userspace > > + * > > + * This function will allocate a new inode from the super block of the > > + * filesystem mount and attach a dentry to that inode. > > + * Minor numbers are limited and tracked globally in binderfs_minors. > > + * The function will stash a struct binder_device for the specific binder > > + * device in i_private of the inode. > > + * > > + * Return: 0 on success, negative errno on failure > > + */ > > +static int binderfs_new_inode(struct inode *ref_inode, > > + struct binder_device *device, > > + struct binderfs_device __user *userp, > > + struct binderfs_device *req) > > +{ > > + int minor, ret; > > + struct dentry *dentry, *dup, *root; > > + size_t name_len = BINDERFS_MAX_NAME + 1; > > + char *name = NULL; > > + struct inode *inode = NULL; > > + struct super_block *sb = ref_inode->i_sb; > > + struct binderfs_info *info = sb->s_fs_info; > > + > > + /* Reserve new minor number for the new device. */ > > + mutex_lock(&binderfs_minors_mutex); > > + minor = ida_alloc_max(&binderfs_minors, BINDERFS_MAX_MINOR, GFP_KERNEL); > > + mutex_unlock(&binderfs_minors_mutex); > > + if (minor < 0) > > + return minor; > > + > > + ret = -ENOMEM; > > + inode = new_inode(sb); > > + if (!inode) > > + goto err; > > + > > + inode->i_ino = minor + INODE_OFFSET; > > + inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode); > > + init_special_inode(inode, S_IFCHR | 0600, > > + MKDEV(MAJOR(binderfs_dev), minor)); > > + inode->i_fop = &binder_fops; > > + inode->i_uid = info->root_uid; > > + inode->i_gid = info->root_gid; > > + inode->i_private = device; > > + > > + name = kmalloc(name_len, GFP_KERNEL); > > + if (!name) > > + goto err; > > + > > + ret = snprintf(name, name_len, "%s", req->name); > > + if (ret < 0 || (size_t)ret >= name_len) { > > + ret = -EINVAL; > > + goto err; > > + } > > + > > + device->binderfs_inode = inode; > > + device->context.binder_context_mgr_uid = INVALID_UID; > > + device->context.name = name; > > + device->miscdev.name = name; > > + device->miscdev.minor = minor; > > + mutex_init(&device->context.context_mgr_node_lock); > > + > > + req->major = MAJOR(binderfs_dev); > > + req->minor = minor; > > + > > + ret = copy_to_user(userp, req, sizeof(*req)); > > + if (ret) > > + goto err; > > + > > + root = sb->s_root; > > + inode_lock(d_inode(root)); > > + dentry = d_alloc_name(root, name); > > + if (!dentry) { > > + inode_unlock(d_inode(root)); > > + ret = -ENOMEM; > > + goto err; > > + } > > + > > + /* Verify that the name userspace gave us is not already in use. */ > > + dup = d_lookup(root, &dentry->d_name); > > + if (dup) { > > + if (d_really_is_positive(dup)) { > > + dput(dup); > > + dput(dentry); > > + inode_unlock(d_inode(root)); > > + /* > > + * Prevent double free since iput() calls > > + * binderfs_evict_inode(). > > + */ > > + inode->i_private = NULL; > > + ret = -EEXIST; > > + goto err; > > + } > > + dput(dup); > > + } > > + > > + d_add(dentry, inode); > > + fsnotify_create(root->d_inode, dentry); > > + inode_unlock(d_inode(root)); > > + > > + return 0; > > + > > +err: > > + kfree(name); > > + mutex_lock(&binderfs_minors_mutex); > > + ida_free(&binderfs_minors, minor); > > + mutex_unlock(&binderfs_minors_mutex); > > + iput(inode); > > + > > + return ret; > > +} > > + > > +static int binderfs_binder_device_create(struct inode *inode, > > + struct binderfs_device __user *userp, > > + struct binderfs_device *req) > > +{ > > + struct binder_device *device; > > + int ret; > > + > > + device = kzalloc(sizeof(*device), GFP_KERNEL); > > + if (!device) > > + return -ENOMEM; > > + > > + ret = binderfs_new_inode(inode, device, userp, req); > > + if (ret < 0) { > > + kfree(device); > > + return ret; > > + } > > + > > + return 0; > > +} > > + > > +/** > > + * binderfs_ctl_ioctl - handle binder device node allocation requests > > + * > > + * The request handler for the binder-control device. All requests operate on > > + * the binderfs mount the binder-control device resides in: > > + * - BINDER_CTL_ADD > > + * Allocate a new binder device. > > + * > > + * Return: 0 on success, negative errno on failure > > + */ > > +static long binder_ctl_ioctl(struct file *file, unsigned int cmd, > > + unsigned long arg) > > +{ > > + struct binderfs_info *info; > > + int ret = -EINVAL; > > + struct inode *inode = file_inode(file); > > + struct binderfs_device *device = (struct binderfs_device __user *)arg; > > + struct binderfs_device device_req; > > + > > + info = BINDERFS_I(inode); > > + switch (cmd) { > > + case BINDER_CTL_ADD: > > + ret = copy_from_user(&device_req, device, sizeof(device_req)); > > + if (ret) > > + break; > > + > > + ret = binderfs_binder_device_create(inode, device, &device_req); > > + break; > > + default: > > + break; > > + } > > + > > + return ret; > > +} > > + > > +static void binderfs_evict_inode(struct inode *inode) > > +{ > > + struct binder_device *device = inode->i_private; > > + > > + clear_inode(inode); > > + > > + if (!device) > > + return; > > + > > + mutex_lock(&binderfs_minors_mutex); > > + ida_free(&binderfs_minors, device->miscdev.minor); > > + mutex_unlock(&binderfs_minors_mutex); > > + > > + kfree(device->context.name); > > + kfree(device); > > +} > > + > > +static const struct super_operations binderfs_super_ops = { > > + .statfs = simple_statfs, > > + .evict_inode = binderfs_evict_inode, > > +}; > > + > > +static int binderfs_rename(struct inode *old_dir, struct dentry *old_dentry, > > + struct inode *new_dir, struct dentry *new_dentry, > > + unsigned int flags) > > +{ > > + struct inode *inode = d_inode(old_dentry); > > + > > + /* binderfs doesn't support directories. */ > > + if (d_is_dir(old_dentry)) > > + return -EPERM; > > + > > + if (flags & ~RENAME_NOREPLACE) > > + return -EINVAL; > > + > > + if (!simple_empty(new_dentry)) > > + return -ENOTEMPTY; > > + > > + if (d_really_is_positive(new_dentry)) > > + simple_unlink(new_dir, new_dentry); > > + > > + old_dir->i_ctime = old_dir->i_mtime = new_dir->i_ctime = > > + new_dir->i_mtime = inode->i_ctime = current_time(old_dir); > > + > > + return 0; > > +} > > + > > +static int binderfs_unlink(struct inode *dir, struct dentry *dentry) > > +{ > > + /* > > + * The control dentry is only ever touched during mount so checking it > > + * here should not require us to take lock. > > + */ > > + if (BINDERFS_I(dir)->control_dentry == dentry) > > + return -EPERM; > > + > > + return simple_unlink(dir, dentry); > > +} > > + > > +static const struct file_operations binder_ctl_fops = { > > + .owner = THIS_MODULE, > > + .open = nonseekable_open, > > + .unlocked_ioctl = binder_ctl_ioctl, > > + .compat_ioctl = binder_ctl_ioctl, > > + .llseek = noop_llseek, > > +}; > > + > > +/** > > + * binderfs_binder_ctl_create - create a new binder-control device > > + * @sb: super block of the binderfs mount > > + * > > + * This function creates a new binder-control device node in the binderfs mount > > + * referred to by @sb. > > + * > > + * Return: 0 on success, negative errno on failure > > + */ > > +static int binderfs_binder_ctl_create(struct super_block *sb) > > +{ > > + int minor; > > + struct dentry *dentry; > > + struct binder_device *device; > > + int ret = 0; > > + struct inode *inode = NULL; > > + struct dentry *root = sb->s_root; > > + struct binderfs_info *info = sb->s_fs_info; > > + > > + device = kzalloc(sizeof(*device), GFP_KERNEL); > > + if (!device) > > + return -ENOMEM; > > + > > + inode_lock(d_inode(root)); > > + > > + if (info->control_dentry) > > + goto out; > > + > > + ret = -ENOMEM; > > + inode = new_inode(sb); > > + if (!inode) > > + goto out; > > + > > + /* Reserve a new minor number for the new device. */ > > + mutex_lock(&binderfs_minors_mutex); > > + minor = ida_alloc_max(&binderfs_minors, BINDERFS_MAX_MINOR, GFP_KERNEL); > > + mutex_unlock(&binderfs_minors_mutex); > > + if (minor < 0) { > > + ret = minor; > > + goto out; > > + } > > + > > + inode->i_ino = SECOND_INODE; > > + inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode); > > + init_special_inode(inode, S_IFCHR | 0600, > > + MKDEV(MAJOR(binderfs_dev), minor)); > > + inode->i_fop = &binder_ctl_fops; > > + inode->i_uid = info->root_uid; > > + inode->i_gid = info->root_gid; > > + inode->i_private = device; > > + > > + device->binderfs_inode = inode; > > + device->miscdev.minor = minor; > > + > > + dentry = d_alloc_name(root, "binder-control"); > > + if (!dentry) > > + goto out; > > + > > + info->control_dentry = dentry; > > + d_add(dentry, inode); > > + inode_unlock(d_inode(root)); > > + > > + return 0; > > + > > +out: > > + inode_unlock(d_inode(root)); > > + kfree(device); > > + if (inode) { > > + inode->i_private = NULL; > > + iput(inode); > > + } > > + > > + return ret; > > +} > > + > > +static const struct inode_operations binderfs_dir_inode_operations = { > > + .lookup = simple_lookup, > > + .rename = binderfs_rename, > > + .unlink = binderfs_unlink, > > +}; > > + > > +static int binderfs_fill_super(struct super_block *sb, void *data, int silent) > > +{ > > + struct binderfs_info *info; > > + int ret = -ENOMEM; > > + struct inode *inode = NULL; > > + struct ipc_namespace *ipc_ns = sb->s_fs_info; > > + > > + get_ipc_ns(ipc_ns); > > + > > + sb->s_blocksize = PAGE_SIZE; > > + sb->s_blocksize_bits = PAGE_SHIFT; > > + > > + /* > > + * The binderfs filesystem can be mounted by userns root in a > > + * non-initial userns. By default such mounts have the SB_I_NODEV flag > > + * set in s_iflags to prevent security issues where userns root can > > + * just create random device nodes via mknod() since it owns the > > + * filesystem mount. But binderfs does not allow to create any files > > + * including devices nodes. The only way to create binder devices nodes > > + * is through the binder-control device which userns root is explicitly > > + * allowed to do. So removing the SB_I_NODEV flag from s_iflags is both > > + * necessary and safe. > > + */ > > + sb->s_iflags &= ~SB_I_NODEV; > > + sb->s_iflags |= SB_I_NOEXEC; > > + sb->s_magic = BINDERFS_SUPER_MAGIC; > > + sb->s_op = &binderfs_super_ops; > > + sb->s_time_gran = 1; > > + > > + info = kzalloc(sizeof(struct binderfs_info), GFP_KERNEL); > > + if (!info) > > + return ret; > > + > > + info->ipc_ns = ipc_ns; > > + info->root_gid = make_kgid(sb->s_user_ns, 0); > > + if (!gid_valid(info->root_gid)) > > + info->root_gid = GLOBAL_ROOT_GID; > > + info->root_uid = make_kuid(sb->s_user_ns, 0); > > + if (!uid_valid(info->root_uid)) > > + info->root_uid = GLOBAL_ROOT_UID; > > + > > + sb->s_fs_info = info; > > + > > + inode = new_inode(sb); > > + if (!inode) > > + goto err_without_dentry; > > + > > + inode->i_ino = FIRST_INODE; > > + inode->i_fop = &simple_dir_operations; > > + inode->i_mode = S_IFDIR | 0755; > > + inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode); > > + inode->i_op = &binderfs_dir_inode_operations; > > + set_nlink(inode, 2); > > + > > + sb->s_root = d_make_root(inode); > > + if (!sb->s_root) > > + goto err_without_dentry; > > + > > + ret = binderfs_binder_ctl_create(sb); > > + if (ret) > > + goto err_with_dentry; > > + > > + return 0; > > + > > +err_with_dentry: > > + dput(sb->s_root); > > + sb->s_root = NULL; > > + > > +err_without_dentry: > > + if (inode) > > + iput(inode); > > + kfree(info); > > + put_ipc_ns(ipc_ns); > > + > > + return ret; > > +} > > + > > +static int binderfs_test_super(struct super_block *sb, void *data) > > +{ > > + struct binderfs_info *info = sb->s_fs_info; > > + > > + if (info) > > + return info->ipc_ns == data; > > + > > + return 0; > > +} > > + > > +static int binderfs_set_super(struct super_block *sb, void *data) > > +{ > > + sb->s_fs_info = data; > > + return set_anon_super(sb, NULL); > > +} > > + > > +static struct dentry *binderfs_mount(struct file_system_type *fs_type, > > + int flags, const char *dev_name, > > + void *data) > > +{ > > + struct super_block *sb; > > + struct ipc_namespace *ipc_ns = current->nsproxy->ipc_ns; > > + > > + if (!ns_capable(ipc_ns->user_ns, CAP_SYS_ADMIN)) > > + return ERR_PTR(-EPERM); > > + > > + sb = sget_userns(fs_type, binderfs_test_super, binderfs_set_super, > > + flags, ipc_ns->user_ns, ipc_ns); > > + if (IS_ERR(sb)) > > + return ERR_CAST(sb); > > + > > + if (!sb->s_root) { > > + int ret = binderfs_fill_super(sb, data, flags & SB_SILENT ? 1 : 0); > > + if (ret) { > > + deactivate_locked_super(sb); > > + return ERR_PTR(ret); > > + } > > + > > + sb->s_flags |= SB_ACTIVE; > > + } > > + > > + return dget(sb->s_root); > > +} > > + > > +static void binderfs_kill_super(struct super_block *sb) > > +{ > > + struct binderfs_info *info = sb->s_fs_info; > > + > > + if (info && info->ipc_ns) > > + put_ipc_ns(info->ipc_ns); > > + > > + kfree(info); > > + kill_litter_super(sb); > > +} > > + > > +static struct file_system_type binder_fs_type = { > > + .name = "binder", > > + .mount = binderfs_mount, > > + .kill_sb = binderfs_kill_super, > > + .fs_flags = FS_USERNS_MOUNT, > > +}; > > + > > +static int __init init_binderfs(void) > > +{ > > + int ret; > > + > > + /* Allocate new major number for binderfs. */ > > + ret = alloc_chrdev_region(&binderfs_dev, 0, BINDERFS_MAX_MINOR, > > + "binder"); > > + if (ret < 0) > > + return ret; > > + > > + ret = register_filesystem(&binder_fs_type); > > + if (ret) { > > + unregister_chrdev_region(binderfs_dev, BINDERFS_MAX_MINOR); > > + return ret; > > + } > > + > > + binderfs_mnt = kern_mount(&binder_fs_type); > > + if (IS_ERR(binderfs_mnt)) { > > + ret = PTR_ERR(binderfs_mnt); > > + binderfs_mnt = NULL; > > + unregister_filesystem(&binder_fs_type); > > + unregister_chrdev_region(binderfs_dev, BINDERFS_MAX_MINOR); > > + } > > + > > + return ret; > > +} > > + > > +device_initcall(init_binderfs); > > diff --git a/include/uapi/linux/android/binder_ctl.h b/include/uapi/linux/android/binder_ctl.h > > new file mode 100644 > > index 000000000000..65b2efd1a0a5 > > --- /dev/null > > +++ b/include/uapi/linux/android/binder_ctl.h > > @@ -0,0 +1,35 @@ > > +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ > > +/* > > + * Copyright (C) 2018 Canonical Ltd. > > + * > > + */ > > + > > +#ifndef _UAPI_LINUX_BINDER_CTL_H > > +#define _UAPI_LINUX_BINDER_CTL_H > > + > > +#include > > +#include > > +#include > > + > > +#define BINDERFS_MAX_NAME 255 > > + > > +/** > > + * struct binderfs_device - retrieve information about a new binder device > > + * @name: the name to use for the new binderfs binder device > > + * @major: major number allocated for binderfs binder devices > > + * @minor: minor number allocated for the new binderfs binder device > > + * > > + */ > > +struct binderfs_device { > > + char name[BINDERFS_MAX_NAME + 1]; > > + __u8 major; > > + __u8 minor; > > +}; > > + > > +/** > > + * Allocate a new binder device. > > + */ > > +#define BINDER_CTL_ADD _IOWR('b', 1, struct binderfs_device) > > + > > +#endif /* _UAPI_LINUX_BINDER_CTL_H */ > > + > > diff --git a/include/uapi/linux/magic.h b/include/uapi/linux/magic.h > > index 96c24478d8ce..f8c00045d537 100644 > > --- a/include/uapi/linux/magic.h > > +++ b/include/uapi/linux/magic.h > > @@ -73,6 +73,7 @@ > > #define DAXFS_MAGIC 0x64646178 > > #define BINFMTFS_MAGIC 0x42494e4d > > #define DEVPTS_SUPER_MAGIC 0x1cd1 > > +#define BINDERFS_SUPER_MAGIC 0x6c6f6f70 > > #define FUTEXFS_SUPER_MAGIC 0xBAD1DEA > > #define PIPEFS_MAGIC 0x50495045 > > #define PROC_SUPER_MAGIC 0x9fa0 > > -- > > 2.19.1 > >