From 3fdd94acd50d607cf6a971455307e711fd8ee16e Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Sun, 6 Jan 2019 15:05:40 +0100 Subject: binderfs: remove wrong kern_mount() call The binderfs filesystem never needs to be mounted by the kernel itself. This is conceptually wrong and should never have been done in the first place. Fixes: 3ad20fe393b ("binder: implement binderfs") Signed-off-by: Christian Brauner Signed-off-by: Greg Kroah-Hartman --- drivers/android/binderfs.c | 10 ---------- 1 file changed, 10 deletions(-) (limited to 'drivers/android') diff --git a/drivers/android/binderfs.c b/drivers/android/binderfs.c index 7496b10532aa..6f68d6217eb3 100644 --- a/drivers/android/binderfs.c +++ b/drivers/android/binderfs.c @@ -40,8 +40,6 @@ #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); @@ -530,14 +528,6 @@ static int __init init_binderfs(void) 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; } -- cgit From b6c770d7c9dc7185b17d53a9d5ca1278c182d6fa Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Sun, 6 Jan 2019 15:05:41 +0100 Subject: binderfs: make each binderfs mount a new instance When currently mounting binderfs in the same ipc namespace twice: mount -t binder binder /A mount -t binder binder /B then the binderfs instances mounted on /A and /B will be the same, i.e. they will have the same superblock. This was the first approach that seemed reasonable. However, this leads to some problems and inconsistencies: /* private binderfs instance in same ipc namespace */ There is no way for a user to request a private binderfs instance in the same ipc namespace. This request has been made in a private mail to me by two independent people. /* bind-mounts */ If users want the same binderfs instance to appear in multiple places they can use bind mounts. So there is no value in having a request for a new binderfs mount giving them the same instance. /* unexpected behavior */ It's surprising that request to mount binderfs is not giving the user a new instance like tmpfs, devpts, ramfs, and others do. /* past mistakes */ Other pseudo-filesystems once made the same mistakes of giving back the same superblock when actually requesting a new mount (cf. devpts's deprecated "newinstance" option). We should not make the same mistake. Once we've committed to always giving back the same superblock in the same IPC namespace with the next kernel release we will not be able to make that change so better to do it now. /* kdbusfs */ It was pointed out to me that kdbusfs - which is conceptually closely related to binderfs - also allowed users to get a private kdbusfs instance in the same IPC namespace by making each mount of kdbusfs a separate instance. I think that makes a lot of sense. Signed-off-by: Christian Brauner Signed-off-by: Greg Kroah-Hartman --- drivers/android/binderfs.c | 41 ++--------------------------------------- 1 file changed, 2 insertions(+), 39 deletions(-) (limited to 'drivers/android') diff --git a/drivers/android/binderfs.c b/drivers/android/binderfs.c index 6f68d6217eb3..4990d65d4850 100644 --- a/drivers/android/binderfs.c +++ b/drivers/android/binderfs.c @@ -379,7 +379,7 @@ 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; + struct ipc_namespace *ipc_ns = current->nsproxy->ipc_ns; get_ipc_ns(ipc_ns); @@ -450,48 +450,11 @@ err_without_dentry: 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); + return mount_nodev(fs_type, flags, data, binderfs_fill_super); } static void binderfs_kill_super(struct super_block *sb) -- cgit From 849d540ddfcd4f232f3b2cf40a2e07eccbd6212c Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Wed, 2 Jan 2019 12:32:18 +0100 Subject: binderfs: implement "max" mount option Since binderfs can be mounted by userns root in non-initial user namespaces some precautions are in order. First, a way to set a maximum on the number of binder devices that can be allocated per binderfs instance and second, a way to reserve a reasonable chunk of binderfs devices for the initial ipc namespace. A first approach as seen in [1] used sysctls similiar to devpts but was shown to be flawed (cf. [2] and [3]) since some aspects were unneeded. This is an alternative approach which avoids sysctls completely and instead switches to a single mount option. Starting with this commit binderfs instances can be mounted with a limit on the number of binder devices that can be allocated. The max= mount option serves as a per-instance limit. If max= is set then only number of binder devices can be allocated in this binderfs instance. This allows to safely bind-mount binderfs instances into unprivileged user namespaces since userns root in a non-initial user namespace cannot change the mount option as long as it does not own the mount namespace the binderfs mount was created in and hence cannot drain the host of minor device numbers [1]: https://lore.kernel.org/lkml/20181221133909.18794-1-christian@brauner.io/ [2]; https://lore.kernel.org/lkml/20181221163316.GA8517@kroah.com/ [3]: https://lore.kernel.org/lkml/CAHRSSEx+gDVW4fKKK8oZNAir9G5icJLyodO8hykv3O0O1jt2FQ@mail.gmail.com/ [4]: https://lore.kernel.org/lkml/20181221192044.5yvfnuri7gdop4rs@brauner.io/ Cc: Todd Kjos Cc: Greg Kroah-Hartman Signed-off-by: Christian Brauner Signed-off-by: Greg Kroah-Hartman --- drivers/android/binderfs.c | 104 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 98 insertions(+), 6 deletions(-) (limited to 'drivers/android') diff --git a/drivers/android/binderfs.c b/drivers/android/binderfs.c index 4990d65d4850..89788969bc04 100644 --- a/drivers/android/binderfs.c +++ b/drivers/android/binderfs.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -44,6 +45,24 @@ static dev_t binderfs_dev; static DEFINE_MUTEX(binderfs_minors_mutex); static DEFINE_IDA(binderfs_minors); +/** + * binderfs_mount_opts - mount options for binderfs + * @max: maximum number of allocatable binderfs binder devices + */ +struct binderfs_mount_opts { + int max; +}; + +enum { + Opt_max, + Opt_err +}; + +static const match_table_t tokens = { + { Opt_max, "max=%d" }, + { Opt_err, NULL } +}; + /** * binderfs_info - information about a binderfs mount * @ipc_ns: The ipc namespace the binderfs mount belongs to. @@ -53,13 +72,16 @@ static DEFINE_IDA(binderfs_minors); * created. * @root_gid: gid that needs to be used when a new binder device is * created. + * @mount_opts: The mount options in use. + * @device_count: The current number of allocated binder devices. */ struct binderfs_info { struct ipc_namespace *ipc_ns; struct dentry *control_dentry; kuid_t root_uid; kgid_t root_gid; - + struct binderfs_mount_opts mount_opts; + int device_count; }; static inline struct binderfs_info *BINDERFS_I(const struct inode *inode) @@ -108,10 +130,17 @@ static int binderfs_binder_device_create(struct inode *ref_inode, /* 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) + if (++info->device_count <= info->mount_opts.max) + minor = ida_alloc_max(&binderfs_minors, BINDERFS_MAX_MINOR, + GFP_KERNEL); + else + minor = -ENOSPC; + if (minor < 0) { + --info->device_count; + mutex_unlock(&binderfs_minors_mutex); return minor; + } + mutex_unlock(&binderfs_minors_mutex); ret = -ENOMEM; device = kzalloc(sizeof(*device), GFP_KERNEL); @@ -185,6 +214,7 @@ err: kfree(name); kfree(device); mutex_lock(&binderfs_minors_mutex); + --info->device_count; ida_free(&binderfs_minors, minor); mutex_unlock(&binderfs_minors_mutex); iput(inode); @@ -230,6 +260,7 @@ static long binder_ctl_ioctl(struct file *file, unsigned int cmd, static void binderfs_evict_inode(struct inode *inode) { struct binder_device *device = inode->i_private; + struct binderfs_info *info = BINDERFS_I(inode); clear_inode(inode); @@ -237,6 +268,7 @@ static void binderfs_evict_inode(struct inode *inode) return; mutex_lock(&binderfs_minors_mutex); + --info->device_count; ida_free(&binderfs_minors, device->miscdev.minor); mutex_unlock(&binderfs_minors_mutex); @@ -244,9 +276,65 @@ static void binderfs_evict_inode(struct inode *inode) kfree(device); } +/** + * binderfs_parse_mount_opts - parse binderfs mount options + * @data: options to set (can be NULL in which case defaults are used) + */ +static int binderfs_parse_mount_opts(char *data, + struct binderfs_mount_opts *opts) +{ + char *p; + opts->max = BINDERFS_MAX_MINOR; + + while ((p = strsep(&data, ",")) != NULL) { + substring_t args[MAX_OPT_ARGS]; + int token; + int max_devices; + + if (!*p) + continue; + + token = match_token(p, tokens, args); + switch (token) { + case Opt_max: + if (match_int(&args[0], &max_devices) || + (max_devices < 0 || + (max_devices > BINDERFS_MAX_MINOR))) + return -EINVAL; + + opts->max = max_devices; + break; + default: + pr_err("Invalid mount options\n"); + return -EINVAL; + } + } + + return 0; +} + +static int binderfs_remount(struct super_block *sb, int *flags, char *data) +{ + struct binderfs_info *info = sb->s_fs_info; + return binderfs_parse_mount_opts(data, &info->mount_opts); +} + +static int binderfs_show_mount_opts(struct seq_file *seq, struct dentry *root) +{ + struct binderfs_info *info; + + info = root->d_sb->s_fs_info; + if (info->mount_opts.max <= BINDERFS_MAX_MINOR) + seq_printf(seq, ",max=%d", info->mount_opts.max); + + return 0; +} + static const struct super_operations binderfs_super_ops = { - .statfs = simple_statfs, - .evict_inode = binderfs_evict_inode, + .evict_inode = binderfs_evict_inode, + .remount_fs = binderfs_remount, + .show_options = binderfs_show_mount_opts, + .statfs = simple_statfs, }; static int binderfs_rename(struct inode *old_dir, struct dentry *old_dentry, @@ -407,6 +495,10 @@ static int binderfs_fill_super(struct super_block *sb, void *data, int silent) if (!info) goto err_without_dentry; + ret = binderfs_parse_mount_opts(data, &info->mount_opts); + if (ret) + goto err_without_dentry; + info->ipc_ns = ipc_ns; info->root_gid = make_kgid(sb->s_user_ns, 0); if (!gid_valid(info->root_gid)) -- cgit From c13295ad219d8bb0e47942d4cfc8251de449a67e Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Fri, 11 Jan 2019 00:25:41 +0100 Subject: binderfs: rename header to binderfs.h It doesn't make sense to call the header binder_ctl.h when its sole existence is tied to binderfs. So give it a sensible name. Users will far more easily remember binderfs.h than binder_ctl.h. Signed-off-by: Christian Brauner Signed-off-by: Greg Kroah-Hartman --- drivers/android/binderfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/android') diff --git a/drivers/android/binderfs.c b/drivers/android/binderfs.c index 89788969bc04..f6341893b5ba 100644 --- a/drivers/android/binderfs.c +++ b/drivers/android/binderfs.c @@ -31,7 +31,7 @@ #include #include #include -#include +#include #include "binder_internal.h" -- cgit From 36bdf3cae09df891b191f3955c8e54a2e05d67d0 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Fri, 11 Jan 2019 11:19:40 +0100 Subject: binderfs: reserve devices for initial mount The binderfs instance in the initial ipc namespace will always have a reserve of 4 binder devices unless explicitly capped by specifying a lower value via the "max" mount option. This ensures when binder devices are removed (on accident or on purpose) they can always be recreated without risking that all minor numbers have already been used up. Cc: Todd Kjos Cc: Greg Kroah-Hartman Signed-off-by: Christian Brauner Signed-off-by: Greg Kroah-Hartman --- drivers/android/binderfs.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers/android') diff --git a/drivers/android/binderfs.c b/drivers/android/binderfs.c index f6341893b5ba..ad3ad2f7f9f4 100644 --- a/drivers/android/binderfs.c +++ b/drivers/android/binderfs.c @@ -40,6 +40,8 @@ #define INODE_OFFSET 3 #define INTSTRLEN 21 #define BINDERFS_MAX_MINOR (1U << MINORBITS) +/* Ensure that the initial ipc namespace always has devices available. */ +#define BINDERFS_MAX_MINOR_CAPPED (BINDERFS_MAX_MINOR - 4) static dev_t binderfs_dev; static DEFINE_MUTEX(binderfs_minors_mutex); @@ -127,11 +129,14 @@ static int binderfs_binder_device_create(struct inode *ref_inode, struct inode *inode = NULL; struct super_block *sb = ref_inode->i_sb; struct binderfs_info *info = sb->s_fs_info; + bool use_reserve = (info->ipc_ns == &init_ipc_ns); /* Reserve new minor number for the new device. */ mutex_lock(&binderfs_minors_mutex); if (++info->device_count <= info->mount_opts.max) - minor = ida_alloc_max(&binderfs_minors, BINDERFS_MAX_MINOR, + minor = ida_alloc_max(&binderfs_minors, + use_reserve ? BINDERFS_MAX_MINOR : + BINDERFS_MAX_MINOR_CAPPED, GFP_KERNEL); else minor = -ENOSPC; -- cgit From 7fefaadd6a962987baac50e7b3c4c3d5ef9b55c6 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Sat, 12 Jan 2019 01:06:03 +0100 Subject: binderfs: handle !CONFIG_IPC_NS builds kbuild reported a build faile in [1]. This is triggered when CONFIG_IPC_NS is not set. So let's make the use of init_ipc_ns conditional on CONFIG_IPC_NS being set. [1]: https://lists.01.org/pipermail/kbuild-all/2019-January/056903.html Signed-off-by: Christian Brauner Signed-off-by: Greg Kroah-Hartman --- drivers/android/binderfs.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/android') diff --git a/drivers/android/binderfs.c b/drivers/android/binderfs.c index ad3ad2f7f9f4..9518e2e7da05 100644 --- a/drivers/android/binderfs.c +++ b/drivers/android/binderfs.c @@ -129,7 +129,11 @@ static int binderfs_binder_device_create(struct inode *ref_inode, struct inode *inode = NULL; struct super_block *sb = ref_inode->i_sb; struct binderfs_info *info = sb->s_fs_info; +#if defined(CONFIG_IPC_NS) bool use_reserve = (info->ipc_ns == &init_ipc_ns); +#else + bool use_reserve = true; +#endif /* Reserve new minor number for the new device. */ mutex_lock(&binderfs_minors_mutex); -- cgit From 7e7ca7744a539f1a172e3b81c29d000787e3d774 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 16 Jan 2019 10:42:59 +0000 Subject: binderfs: fix error return code in binderfs_fill_super() Fix to return a negative error code -ENOMEM from the new_inode() and d_make_root() error handling cases instead of 0, as done elsewhere in this function. Fixes: 849d540ddfcd ("binderfs: implement "max" mount option") Signed-off-by: Wei Yongjun Reviewed-by: Christian Brauner Signed-off-by: Greg Kroah-Hartman --- drivers/android/binderfs.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/android') diff --git a/drivers/android/binderfs.c b/drivers/android/binderfs.c index 9518e2e7da05..e4ff4c3fa371 100644 --- a/drivers/android/binderfs.c +++ b/drivers/android/binderfs.c @@ -518,6 +518,7 @@ static int binderfs_fill_super(struct super_block *sb, void *data, int silent) sb->s_fs_info = info; + ret = -ENOMEM; inode = new_inode(sb); if (!inode) goto err_without_dentry; -- cgit From 7c4d08fc4d5aca073bd4ebecbb9eda5e4d858b71 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 21 Jan 2019 11:48:02 +0100 Subject: binderfs: remove outdated comment The comment stems from an early version of that patchset and is just confusing now. Cc: Al Viro Signed-off-by: Christian Brauner Signed-off-by: Greg Kroah-Hartman --- drivers/android/binderfs.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers/android') diff --git a/drivers/android/binderfs.c b/drivers/android/binderfs.c index e4ff4c3fa371..898d847f8505 100644 --- a/drivers/android/binderfs.c +++ b/drivers/android/binderfs.c @@ -373,10 +373,6 @@ static int binderfs_rename(struct inode *old_dir, struct dentry *old_dentry, 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; -- cgit From e98e6fa18636609f14a7f866524950a783cf4fbf Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 21 Jan 2019 11:48:03 +0100 Subject: binderfs: prevent renaming the control dentry - make binderfs control dentry immutable: We don't allow to unlink it since it is crucial for binderfs to be useable but if we allow to rename it we make the unlink trivial to bypass. So prevent renaming too and simply treat the control dentry as immutable. - add is_binderfs_control_device() helper: Take the opportunity and turn the check for the control dentry into a separate helper is_binderfs_control_device() since it's now used in two places. - simplify binderfs_rename(): Instead of hand-rolling our custom version of simple_rename() just dumb the whole function down to first check whether we're trying to rename the control dentry. If we do EPERM the caller and if not call simple_rename(). Suggested-by: Al Viro Signed-off-by: Christian Brauner Signed-off-by: Greg Kroah-Hartman --- drivers/android/binderfs.c | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) (limited to 'drivers/android') diff --git a/drivers/android/binderfs.c b/drivers/android/binderfs.c index 898d847f8505..e73f9dbee099 100644 --- a/drivers/android/binderfs.c +++ b/drivers/android/binderfs.c @@ -346,34 +346,26 @@ static const struct super_operations binderfs_super_ops = { .statfs = simple_statfs, }; +static inline bool is_binderfs_control_device(const struct dentry *dentry) +{ + struct binderfs_info *info = dentry->d_sb->s_fs_info; + return info->control_dentry == dentry; +} + 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)) + if (is_binderfs_control_device(old_dentry) || + is_binderfs_control_device(new_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; + return simple_rename(old_dir, old_dentry, new_dir, new_dentry, flags); } static int binderfs_unlink(struct inode *dir, struct dentry *dentry) { - if (BINDERFS_I(dir)->control_dentry == dentry) + if (is_binderfs_control_device(dentry)) return -EPERM; return simple_unlink(dir, dentry); -- cgit From 36975fc3e5f241cc4f45df4ab4624d7d5199d9ed Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 21 Jan 2019 11:48:04 +0100 Subject: binderfs: rework binderfs_fill_super() Al pointed out that on binderfs_fill_super() error deactivate_locked_super() will call binderfs_kill_super() so all of the freeing and putting we currently do in binderfs_fill_super() is unnecessary and buggy. Let's simply return errors and let binderfs_fill_super() take care of cleaning up on error. Suggested-by: Al Viro Signed-off-by: Christian Brauner Signed-off-by: Greg Kroah-Hartman --- drivers/android/binderfs.c | 41 +++++++++++------------------------------ 1 file changed, 11 insertions(+), 30 deletions(-) (limited to 'drivers/android') diff --git a/drivers/android/binderfs.c b/drivers/android/binderfs.c index e73f9dbee099..89a2ee1a02f6 100644 --- a/drivers/android/binderfs.c +++ b/drivers/android/binderfs.c @@ -461,12 +461,9 @@ static const struct inode_operations binderfs_dir_inode_operations = { static int binderfs_fill_super(struct super_block *sb, void *data, int silent) { + int ret; struct binderfs_info *info; - int ret = -ENOMEM; struct inode *inode = NULL; - struct ipc_namespace *ipc_ns = current->nsproxy->ipc_ns; - - get_ipc_ns(ipc_ns); sb->s_blocksize = PAGE_SIZE; sb->s_blocksize_bits = PAGE_SHIFT; @@ -488,15 +485,17 @@ static int binderfs_fill_super(struct super_block *sb, void *data, int silent) sb->s_op = &binderfs_super_ops; sb->s_time_gran = 1; - info = kzalloc(sizeof(struct binderfs_info), GFP_KERNEL); - if (!info) - goto err_without_dentry; + sb->s_fs_info = kzalloc(sizeof(struct binderfs_info), GFP_KERNEL); + if (!sb->s_fs_info) + return -ENOMEM; + info = sb->s_fs_info; + + info->ipc_ns = get_ipc_ns(current->nsproxy->ipc_ns); ret = binderfs_parse_mount_opts(data, &info->mount_opts); if (ret) - goto err_without_dentry; + 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; @@ -504,12 +503,9 @@ static int binderfs_fill_super(struct super_block *sb, void *data, int silent) if (!uid_valid(info->root_uid)) info->root_uid = GLOBAL_ROOT_UID; - sb->s_fs_info = info; - - ret = -ENOMEM; inode = new_inode(sb); if (!inode) - goto err_without_dentry; + return -ENOMEM; inode->i_ino = FIRST_INODE; inode->i_fop = &simple_dir_operations; @@ -520,24 +516,9 @@ static int binderfs_fill_super(struct super_block *sb, void *data, int silent) 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: - put_ipc_ns(ipc_ns); - iput(inode); - kfree(info); + return -ENOMEM; - return ret; + return binderfs_binder_ctl_create(sb); } static struct dentry *binderfs_mount(struct file_system_type *fs_type, -- cgit From 01b3f1fc568352a1ffdcd3ee82a0297f16cc9bd9 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 21 Jan 2019 11:48:05 +0100 Subject: binderfs: rework binderfs_binder_device_create() - switch from d_alloc_name() + d_lookup() to lookup_one_len(): Instead of using d_alloc_name() and then doing a d_lookup() with the allocated dentry to find whether a device with the name we're trying to create already exists switch to using lookup_one_len(). The latter will either return the existing dentry or a new one. - switch from kmalloc() + strscpy() to kmemdup(): Use a more idiomatic way to copy the name for the new dentry that userspace gave us. Suggested-by: Al Viro Signed-off-by: Christian Brauner Signed-off-by: Greg Kroah-Hartman --- drivers/android/binderfs.c | 39 +++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 20 deletions(-) (limited to 'drivers/android') diff --git a/drivers/android/binderfs.c b/drivers/android/binderfs.c index 89a2ee1a02f6..1e077498a507 100644 --- a/drivers/android/binderfs.c +++ b/drivers/android/binderfs.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -106,7 +107,7 @@ bool is_binderfs_device(const struct inode *inode) * @userp: buffer to copy information about new device for userspace to * @req: struct binderfs_device as copied from userspace * - * This function allocated a new binder_device and reserves a new minor + * This function allocates a new binder_device and reserves a new minor * number for it. * Minor numbers are limited and tracked globally in binderfs_minors. The * function will stash a struct binder_device for the specific binder @@ -122,10 +123,10 @@ static int binderfs_binder_device_create(struct inode *ref_inode, struct binderfs_device *req) { int minor, ret; - struct dentry *dentry, *dup, *root; + struct dentry *dentry, *root; struct binder_device *device; - size_t name_len = BINDERFS_MAX_NAME + 1; char *name = NULL; + size_t name_len; struct inode *inode = NULL; struct super_block *sb = ref_inode->i_sb; struct binderfs_info *info = sb->s_fs_info; @@ -168,12 +169,13 @@ static int binderfs_binder_device_create(struct inode *ref_inode, inode->i_uid = info->root_uid; inode->i_gid = info->root_gid; - name = kmalloc(name_len, GFP_KERNEL); + req->name[BINDERFS_MAX_NAME] = '\0'; /* NUL-terminate */ + name_len = strlen(req->name); + /* Make sure to include terminating NUL byte */ + name = kmemdup(req->name, name_len + 1, GFP_KERNEL); if (!name) goto err; - strscpy(name, req->name, name_len); - device->binderfs_inode = inode; device->context.binder_context_mgr_uid = INVALID_UID; device->context.name = name; @@ -192,24 +194,21 @@ static int binderfs_binder_device_create(struct inode *ref_inode, root = sb->s_root; inode_lock(d_inode(root)); - dentry = d_alloc_name(root, name); - if (!dentry) { + + /* look it up */ + dentry = lookup_one_len(name, root, name_len); + if (IS_ERR(dentry)) { inode_unlock(d_inode(root)); - ret = -ENOMEM; + ret = PTR_ERR(dentry); 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)); - ret = -EEXIST; - goto err; - } - dput(dup); + if (d_really_is_positive(dentry)) { + /* already exists */ + dput(dentry); + inode_unlock(d_inode(root)); + ret = -EEXIST; + goto err; } inode->i_private = device; -- cgit From 4198479524aeccaf53c3a4cc73784982535573fa Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 21 Jan 2019 11:48:06 +0100 Subject: binderfs: kill_litter_super() before cleanup Al pointed out that first calling kill_litter_super() before cleaning up info is more correct since destroying info doesn't depend on the state of the dentries and inodes. That the opposite remains true is not guaranteed. Suggested-by: Al Viro Signed-off-by: Christian Brauner Signed-off-by: Greg Kroah-Hartman --- drivers/android/binderfs.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/android') diff --git a/drivers/android/binderfs.c b/drivers/android/binderfs.c index 1e077498a507..ba88be172aee 100644 --- a/drivers/android/binderfs.c +++ b/drivers/android/binderfs.c @@ -531,11 +531,12 @@ static void binderfs_kill_super(struct super_block *sb) { struct binderfs_info *info = sb->s_fs_info; + kill_litter_super(sb); + 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 = { -- cgit From 29ef1c8e16aed079ac09989d752e38d412b6e1a8 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 21 Jan 2019 11:48:07 +0100 Subject: binderfs: drop lock in binderfs_binder_ctl_create The binderfs_binder_ctl_create() call is a no-op on subsequent calls and the first call is done before we unlock the suberblock. Hence, there is no need to take inode_lock() in there. Let's remove it. Suggested-by: Al Viro Signed-off-by: Christian Brauner Signed-off-by: Greg Kroah-Hartman --- drivers/android/binderfs.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers/android') diff --git a/drivers/android/binderfs.c b/drivers/android/binderfs.c index ba88be172aee..d537dcdb5d65 100644 --- a/drivers/android/binderfs.c +++ b/drivers/android/binderfs.c @@ -400,8 +400,6 @@ static int binderfs_binder_ctl_create(struct super_block *sb) if (!device) return -ENOMEM; - inode_lock(d_inode(root)); - /* If we have already created a binder-control node, return. */ if (info->control_dentry) { ret = 0; @@ -440,12 +438,10 @@ static int binderfs_binder_ctl_create(struct super_block *sb) inode->i_private = device; info->control_dentry = dentry; d_add(dentry, inode); - inode_unlock(d_inode(root)); return 0; out: - inode_unlock(d_inode(root)); kfree(device); iput(inode); -- cgit From 01684db950ea2b840531ab9298a8785776b6f6e8 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 21 Jan 2019 11:48:08 +0100 Subject: binderfs: switch from d_add() to d_instantiate() In a previous commit we switched from a d_alloc_name() + d_lookup() combination to setup a new dentry and find potential duplicates to the more idiomatic lookup_one_len(). As far as I understand, this also means we need to switch from d_add() to d_instantiate() since lookup_one_len() will create a new dentry when it doesn't find an existing one and add the new dentry to the hash queues. So we only need to call d_instantiate() to connect the dentry to the inode and turn it into a positive dentry. If we were to use d_add() we sure see stack traces like the following indicating that adding the same dentry twice over the same inode: [ 744.441889] CPU: 4 PID: 2849 Comm: landscape-sysin Not tainted 5.0.0-rc1-brauner-binderfs #243 [ 744.441889] Hardware name: Dell DCS XS24-SC2 /XS24-SC2 , BIOS S59_3C20 04/07/2011 [ 744.441889] RIP: 0010:__d_lookup_rcu+0x76/0x190 [ 744.441889] Code: 89 75 c0 49 c1 e9 20 49 89 fd 45 89 ce 41 83 e6 07 42 8d 04 f5 00 00 00 00 89 45 c8 eb 0c 48 8b 1b 48 85 db 0f 84 81 00 00 00 <44> 8b 63 fc 4c 3b 6b 10 75 ea 48 83 7b 08 00 74 e3 41 83 e4 fe 41 [ 744.441889] RSP: 0018:ffffb8c984e27ad0 EFLAGS: 00000282 ORIG_RAX: ffffffffffffff13 [ 744.441889] RAX: 0000000000000038 RBX: ffff9407ef770c08 RCX: ffffb8c980011000 [ 744.441889] RDX: ffffb8c984e27b54 RSI: ffffb8c984e27ce0 RDI: ffff9407e6689600 [ 744.441889] RBP: ffffb8c984e27b28 R08: ffffb8c984e27ba4 R09: 0000000000000007 [ 744.441889] R10: ffff9407e5c4f05c R11: 973f3eb9d84a94e5 R12: 0000000000000002 [ 744.441889] R13: ffff9407e6689600 R14: 0000000000000007 R15: 00000007bfef7a13 [ 744.441889] FS: 00007f0db13bb740(0000) GS:ffff9407f3b00000(0000) knlGS:0000000000000000 [ 744.441889] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 744.441889] CR2: 00007f0dacc51024 CR3: 000000032961a000 CR4: 00000000000006e0 [ 744.441889] Call Trace: [ 744.441889] lookup_fast+0x53/0x300 [ 744.441889] walk_component+0x49/0x350 [ 744.441889] ? inode_permission+0x63/0x1a0 [ 744.441889] link_path_walk.part.33+0x1bc/0x5a0 [ 744.441889] ? path_init+0x190/0x310 [ 744.441889] path_lookupat+0x95/0x210 [ 744.441889] filename_lookup+0xb6/0x190 [ 744.441889] ? __check_object_size+0xb8/0x1b0 [ 744.441889] ? strncpy_from_user+0x50/0x1a0 [ 744.441889] user_path_at_empty+0x36/0x40 [ 744.441889] ? user_path_at_empty+0x36/0x40 [ 744.441889] vfs_statx+0x76/0xe0 [ 744.441889] __do_sys_newstat+0x3d/0x70 [ 744.441889] __x64_sys_newstat+0x16/0x20 [ 744.441889] do_syscall_64+0x5a/0x120 [ 744.441889] entry_SYSCALL_64_after_hwframe+0x44/0xa9 [ 744.441889] RIP: 0033:0x7f0db0ec2775 [ 744.441889] Code: 00 00 00 75 05 48 83 c4 18 c3 e8 26 55 02 00 66 0f 1f 44 00 00 83 ff 01 48 89 f0 77 30 48 89 c7 48 89 d6 b8 04 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 03 f3 c3 90 48 8b 15 e1 b6 2d 00 f7 d8 64 89 [ 744.441889] RSP: 002b:00007ffc36bc9388 EFLAGS: 00000246 ORIG_RAX: 0000000000000004 [ 744.441889] RAX: ffffffffffffffda RBX: 00007ffc36bc9300 RCX: 00007f0db0ec2775 [ 744.441889] RDX: 00007ffc36bc9400 RSI: 00007ffc36bc9400 RDI: 00007f0dad26f050 [ 744.441889] RBP: 0000000000c0bc60 R08: 0000000000000000 R09: 0000000000000001 [ 744.441889] R10: 0000000000000000 R11: 0000000000000246 R12: 00007ffc36bc9400 [ 744.441889] R13: 0000000000000001 R14: 00000000ffffff9c R15: 0000000000c0bc60 Cc: Al Viro Signed-off-by: Christian Brauner Signed-off-by: Greg Kroah-Hartman --- drivers/android/binderfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/android') diff --git a/drivers/android/binderfs.c b/drivers/android/binderfs.c index d537dcdb5d65..6a2185eb66c5 100644 --- a/drivers/android/binderfs.c +++ b/drivers/android/binderfs.c @@ -212,7 +212,7 @@ static int binderfs_binder_device_create(struct inode *ref_inode, } inode->i_private = device; - d_add(dentry, inode); + d_instantiate(dentry, inode); fsnotify_create(root->d_inode, dentry); inode_unlock(d_inode(root)); -- cgit