LKML Archive on lore.kernel.org
help / color / mirror / Atom feed
From: Kees Cook <keescook@chromium.org>
To: Christian Brauner <christian.brauner@ubuntu.com>
Cc: gregkh@linuxfoundation.org, tkjos@android.com,
	linux-kernel@vger.kernel.org, ard.biesheuvel@linaro.org,
	ardb@kernel.org, arve@android.com, hridya@google.com,
	joel@joelfernandes.org, john.stultz@linaro.org,
	kernel-team@android.com, linux-kselftest@vger.kernel.org,
	maco@android.com, naresh.kamboju@linaro.org, shuah@kernel.org,
	Todd Kjos <tkjos@google.com>
Subject: Re: [PATCH v2] binderfs: port to new mount api
Date: Fri, 13 Mar 2020 16:08:30 -0700	[thread overview]
Message-ID: <202003131608.D3A8AED8A7@keescook> (raw)
In-Reply-To: <20200313153427.141789-1-christian.brauner@ubuntu.com>

On Fri, Mar 13, 2020 at 04:34:27PM +0100, Christian Brauner wrote:
> When I first wrote binderfs the new mount api had not yet landed. Now
> that it has been around for a little while and a bunch of filesystems
> have already been ported we should do so too. When Al sent his
> mount-api-conversion pr he requested that binderfs (and a few others) be
> ported separately. It's time we port binderfs. We can make use of the
> new option parser, get nicer infrastructure and it will be easier if we
> ever add any new mount options.
> 
> This survives testing with the binderfs selftests:
> 
> for i in `seq 1 1000`; do ./binderfs_test; done
> 
> including the new stress tests I sent out for review today:
> 
>  TAP version 13
>  1..1
>  # selftests: filesystems/binderfs: binderfs_test
>  # [==========] Running 3 tests from 1 test cases.
>  # [ RUN      ] global.binderfs_stress
>  # [  XFAIL!  ] Tests are not run as root. Skipping privileged tests
>  # [==========] Running 3 tests from 1 test cases.
>  # [ RUN      ] global.binderfs_stress
>  # [       OK ] global.binderfs_stress
>  # [ RUN      ] global.binderfs_test_privileged
>  # [       OK ] global.binderfs_test_privileged
>  # [ RUN      ] global.binderfs_test_unprivileged
>  # # Allocated new binder device with major 243, minor 4, and name my-binder
>  # # Detected binder version: 8
>  # [==========] Running 3 tests from 1 test cases.
>  # [ RUN      ] global.binderfs_stress
>  # [       OK ] global.binderfs_stress
>  # [ RUN      ] global.binderfs_test_privileged
>  # [       OK ] global.binderfs_test_privileged
>  # [ RUN      ] global.binderfs_test_unprivileged
>  # [       OK ] global.binderfs_test_unprivileged
>  # [==========] 3 / 3 tests passed.
>  # [  PASSED  ]
>  ok 1 selftests: filesystems/binderfs: binderfs_test
> 
> Cc: Todd Kjos <tkjos@google.com>
> Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
> Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>

Reviewed-by: Kees Cook <keescook@chromium.org>

-Kees

> ---
> /* v2 */
> - Christian Brauner <christian.brauner@ubuntu.com>:
>   - Commit message adapted to new stresstest output after porting to
>     XFAIL infrastructure.
>     For the stresstest patchset see:
>     https://lore.kernel.org/r/20200313152420.138777-1-christian.brauner@ubuntu.com
> ---
>  drivers/android/binderfs.c | 200 +++++++++++++++++++------------------
>  1 file changed, 104 insertions(+), 96 deletions(-)
> 
> diff --git a/drivers/android/binderfs.c b/drivers/android/binderfs.c
> index f303106b3362..9ecad74183a3 100644
> --- a/drivers/android/binderfs.c
> +++ b/drivers/android/binderfs.c
> @@ -18,7 +18,7 @@
>  #include <linux/module.h>
>  #include <linux/mutex.h>
>  #include <linux/mount.h>
> -#include <linux/parser.h>
> +#include <linux/fs_parser.h>
>  #include <linux/radix-tree.h>
>  #include <linux/sched.h>
>  #include <linux/seq_file.h>
> @@ -48,26 +48,30 @@ static dev_t binderfs_dev;
>  static DEFINE_MUTEX(binderfs_minors_mutex);
>  static DEFINE_IDA(binderfs_minors);
>  
> -enum {
> +enum binderfs_param {
>  	Opt_max,
>  	Opt_stats_mode,
> -	Opt_err
>  };
>  
>  enum binderfs_stats_mode {
> -	STATS_NONE,
> -	STATS_GLOBAL,
> +	binderfs_stats_mode_unset,
> +	binderfs_stats_mode_global,
>  };
>  
> -static const match_table_t tokens = {
> -	{ Opt_max, "max=%d" },
> -	{ Opt_stats_mode, "stats=%s" },
> -	{ Opt_err, NULL     }
> +static const struct constant_table binderfs_param_stats[] = {
> +	{ "global", binderfs_stats_mode_global },
> +	{}
>  };
>  
> -static inline struct binderfs_info *BINDERFS_I(const struct inode *inode)
> +const struct fs_parameter_spec binderfs_fs_parameters[] = {
> +	fsparam_u32("max",	Opt_max),
> +	fsparam_enum("stats",	Opt_stats_mode, binderfs_param_stats),
> +	{}
> +};
> +
> +static inline struct binderfs_info *BINDERFS_SB(const struct super_block *sb)
>  {
> -	return inode->i_sb->s_fs_info;
> +	return sb->s_fs_info;
>  }
>  
>  bool is_binderfs_device(const struct inode *inode)
> @@ -246,7 +250,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);
> +	struct binderfs_info *info = BINDERFS_SB(inode->i_sb);
>  
>  	clear_inode(inode);
>  
> @@ -264,97 +268,84 @@ static void binderfs_evict_inode(struct inode *inode)
>  	}
>  }
>  
> -/**
> - * 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)
> +static int binderfs_fs_context_parse_param(struct fs_context *fc,
> +					   struct fs_parameter *param)
>  {
> -	char *p, *stats;
> -	opts->max = BINDERFS_MAX_MINOR;
> -	opts->stats_mode = STATS_NONE;
> -
> -	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;
> -		case Opt_stats_mode:
> -			if (!capable(CAP_SYS_ADMIN))
> -				return -EINVAL;
> +	int opt;
> +	struct binderfs_mount_opts *ctx = fc->fs_private;
> +	struct fs_parse_result result;
>  
> -			stats = match_strdup(&args[0]);
> -			if (!stats)
> -				return -ENOMEM;
> +	opt = fs_parse(fc, binderfs_fs_parameters, param, &result);
> +	if (opt < 0)
> +		return opt;
>  
> -			if (strcmp(stats, "global") != 0) {
> -				kfree(stats);
> -				return -EINVAL;
> -			}
> +	switch (opt) {
> +	case Opt_max:
> +		if (result.uint_32 > BINDERFS_MAX_MINOR)
> +			return invalfc(fc, "Bad value for '%s'", param->key);
>  
> -			opts->stats_mode = STATS_GLOBAL;
> -			kfree(stats);
> -			break;
> -		default:
> -			pr_err("Invalid mount options\n");
> -			return -EINVAL;
> -		}
> +		ctx->max = result.uint_32;
> +		break;
> +	case Opt_stats_mode:
> +		if (!capable(CAP_SYS_ADMIN))
> +			return -EPERM;
> +
> +		ctx->stats_mode = result.uint_32;
> +		break;
> +	default:
> +		return invalfc(fc, "Unsupported parameter '%s'", param->key);
>  	}
>  
>  	return 0;
>  }
>  
> -static int binderfs_remount(struct super_block *sb, int *flags, char *data)
> +static int binderfs_fs_context_reconfigure(struct fs_context *fc)
>  {
> -	int prev_stats_mode, ret;
> -	struct binderfs_info *info = sb->s_fs_info;
> +	struct binderfs_mount_opts *ctx = fc->fs_private;
> +	struct binderfs_info *info = BINDERFS_SB(fc->root->d_sb);
>  
> -	prev_stats_mode = info->mount_opts.stats_mode;
> -	ret = binderfs_parse_mount_opts(data, &info->mount_opts);
> -	if (ret)
> -		return ret;
> -
> -	if (prev_stats_mode != info->mount_opts.stats_mode) {
> -		pr_err("Binderfs stats mode cannot be changed during a remount\n");
> -		info->mount_opts.stats_mode = prev_stats_mode;
> -		return -EINVAL;
> -	}
> +	if (info->mount_opts.stats_mode != ctx->stats_mode)
> +		return invalfc(fc, "Binderfs stats mode cannot be changed during a remount");
>  
> +	info->mount_opts.stats_mode = ctx->stats_mode;
> +	info->mount_opts.max = ctx->max;
>  	return 0;
>  }
>  
> -static int binderfs_show_mount_opts(struct seq_file *seq, struct dentry *root)
> +static int binderfs_show_options(struct seq_file *seq, struct dentry *root)
>  {
> -	struct binderfs_info *info;
> +	struct binderfs_info *info = BINDERFS_SB(root->d_sb);
>  
> -	info = root->d_sb->s_fs_info;
>  	if (info->mount_opts.max <= BINDERFS_MAX_MINOR)
>  		seq_printf(seq, ",max=%d", info->mount_opts.max);
> -	if (info->mount_opts.stats_mode == STATS_GLOBAL)
> +
> +	switch (info->mount_opts.stats_mode) {
> +	case binderfs_stats_mode_unset:
> +		break;
> +	case binderfs_stats_mode_global:
>  		seq_printf(seq, ",stats=global");
> +		break;
> +	}
>  
>  	return 0;
>  }
>  
> +static void binderfs_put_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);
> +	sb->s_fs_info = NULL;
> +}
> +
>  static const struct super_operations binderfs_super_ops = {
>  	.evict_inode    = binderfs_evict_inode,
> -	.remount_fs	= binderfs_remount,
> -	.show_options	= binderfs_show_mount_opts,
> +	.show_options	= binderfs_show_options,
>  	.statfs         = simple_statfs,
> +	.put_super	= binderfs_put_super,
>  };
>  
>  static inline bool is_binderfs_control_device(const struct dentry *dentry)
> @@ -653,10 +644,11 @@ static int init_binder_logs(struct super_block *sb)
>  	return ret;
>  }
>  
> -static int binderfs_fill_super(struct super_block *sb, void *data, int silent)
> +static int binderfs_fill_super(struct super_block *sb, struct fs_context *fc)
>  {
>  	int ret;
>  	struct binderfs_info *info;
> +	struct binderfs_mount_opts *ctx = fc->fs_private;
>  	struct inode *inode = NULL;
>  	struct binderfs_device device_info = { 0 };
>  	const char *name;
> @@ -689,16 +681,14 @@ static int binderfs_fill_super(struct super_block *sb, void *data, int silent)
>  
>  	info->ipc_ns = get_ipc_ns(current->nsproxy->ipc_ns);
>  
> -	ret = binderfs_parse_mount_opts(data, &info->mount_opts);
> -	if (ret)
> -		return ret;
> -
>  	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;
> +	info->mount_opts.max = ctx->max;
> +	info->mount_opts.stats_mode = ctx->stats_mode;
>  
>  	inode = new_inode(sb);
>  	if (!inode)
> @@ -730,36 +720,54 @@ static int binderfs_fill_super(struct super_block *sb, void *data, int silent)
>  			name++;
>  	}
>  
> -	if (info->mount_opts.stats_mode == STATS_GLOBAL)
> +	if (info->mount_opts.stats_mode == binderfs_stats_mode_global)
>  		return init_binder_logs(sb);
>  
>  	return 0;
>  }
>  
> -static struct dentry *binderfs_mount(struct file_system_type *fs_type,
> -				     int flags, const char *dev_name,
> -				     void *data)
> +static int binderfs_fs_context_get_tree(struct fs_context *fc)
>  {
> -	return mount_nodev(fs_type, flags, data, binderfs_fill_super);
> +	return get_tree_nodev(fc, binderfs_fill_super);
>  }
>  
> -static void binderfs_kill_super(struct super_block *sb)
> +static void binderfs_fs_context_free(struct fs_context *fc)
>  {
> -	struct binderfs_info *info = sb->s_fs_info;
> +	struct binderfs_mount_opts *ctx = fc->fs_private;
>  
> -	kill_litter_super(sb);
> +	kfree(ctx);
> +}
>  
> -	if (info && info->ipc_ns)
> -		put_ipc_ns(info->ipc_ns);
> +static const struct fs_context_operations binderfs_fs_context_ops = {
> +	.free		= binderfs_fs_context_free,
> +	.get_tree	= binderfs_fs_context_get_tree,
> +	.parse_param	= binderfs_fs_context_parse_param,
> +	.reconfigure	= binderfs_fs_context_reconfigure,
> +};
>  
> -	kfree(info);
> +static int binderfs_init_fs_context(struct fs_context *fc)
> +{
> +	struct binderfs_mount_opts *ctx = fc->fs_private;
> +
> +	ctx = kzalloc(sizeof(struct binderfs_mount_opts), GFP_KERNEL);
> +	if (!ctx)
> +		return -ENOMEM;
> +
> +	ctx->max = BINDERFS_MAX_MINOR;
> +	ctx->stats_mode = binderfs_stats_mode_unset;
> +
> +	fc->fs_private = ctx;
> +	fc->ops = &binderfs_fs_context_ops;
> +
> +	return 0;
>  }
>  
>  static struct file_system_type binder_fs_type = {
> -	.name		= "binder",
> -	.mount		= binderfs_mount,
> -	.kill_sb	= binderfs_kill_super,
> -	.fs_flags	= FS_USERNS_MOUNT,
> +	.name			= "binder",
> +	.init_fs_context	= binderfs_init_fs_context,
> +	.parameters		= binderfs_fs_parameters,
> +	.kill_sb		= kill_litter_super,
> +	.fs_flags		= FS_USERNS_MOUNT,
>  };
>  
>  int __init init_binderfs(void)
> 
> base-commit: f17f06a0c7794d3a7c2425663738823354447472
> -- 
> 2.25.1
> 

-- 
Kees Cook

  reply	other threads:[~2020-03-13 23:08 UTC|newest]

Thread overview: 29+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-03-11  7:52 WARNING: at refcount.c:190 refcount_sub_and_test_checked+0xac/0xc8 - refcount_t: underflow; use-after-free Naresh Kamboju
2020-03-11  7:52 ` Naresh Kamboju
2020-03-11  9:13 ` Christian Brauner
2020-03-11  9:13   ` Christian Brauner
2020-03-11 10:53 ` [PATCH] binderfs: use refcount for binder control devices too Christian Brauner
2020-03-11 18:25   ` Todd Kjos
2020-03-12 13:15   ` [PATCH 1/3] binderfs: port tests to test harness infrastructure Christian Brauner
2020-03-12 13:15     ` [PATCH 2/3] binderfs: add stress test for binderfs binder devices Christian Brauner
2020-03-12 23:53       ` Kees Cook
2020-03-13 12:54         ` Christian Brauner
2020-03-12 13:15     ` [PATCH 3/3] binderfs_test: switch from /dev to /tmp as mountpoint Christian Brauner
2020-03-12 23:54       ` Kees Cook
2020-03-13 12:55         ` Christian Brauner
2020-03-12 21:24     ` [PATCH] binderfs: port to new mount api Christian Brauner
2020-03-12 23:56       ` Kees Cook
2020-03-13 12:55         ` Christian Brauner
2020-03-13 12:56           ` Christian Brauner
2020-03-12 23:51     ` [PATCH 1/3] binderfs: port tests to test harness infrastructure Kees Cook
2020-03-13 15:24     ` [PATCH v2 " Christian Brauner
2020-03-13 15:24       ` [PATCH v2 2/3] binderfs_test: switch from /dev to a unique per-test mountpoint Christian Brauner
2020-03-13 23:07         ` Kees Cook
2020-03-13 15:24       ` [PATCH v2 3/3] binderfs: add stress test for binderfs binder devices Christian Brauner
2020-03-13 23:08         ` Kees Cook
2020-03-16 22:44           ` Hridya Valsaraju
2020-03-17  8:27             ` Christian Brauner
2020-03-13 23:07       ` [PATCH v2 1/3] binderfs: port tests to test harness infrastructure Kees Cook
2020-03-13 15:34     ` [PATCH v2] binderfs: port to new mount api Christian Brauner
2020-03-13 23:08       ` Kees Cook [this message]
2020-03-18 12:29       ` Greg KH

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=202003131608.D3A8AED8A7@keescook \
    --to=keescook@chromium.org \
    --cc=ard.biesheuvel@linaro.org \
    --cc=ardb@kernel.org \
    --cc=arve@android.com \
    --cc=christian.brauner@ubuntu.com \
    --cc=gregkh@linuxfoundation.org \
    --cc=hridya@google.com \
    --cc=joel@joelfernandes.org \
    --cc=john.stultz@linaro.org \
    --cc=kernel-team@android.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-kselftest@vger.kernel.org \
    --cc=maco@android.com \
    --cc=naresh.kamboju@linaro.org \
    --cc=shuah@kernel.org \
    --cc=tkjos@android.com \
    --cc=tkjos@google.com \
    --subject='Re: [PATCH v2] binderfs: port to new mount api' \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).