All of lore.kernel.org
 help / color / mirror / Atom feed
From: Luis Henriques <lhenriques@suse.de>
To: Christian Brauner <brauner@kernel.org>
Cc: Eric Sandeen <sandeen@sandeen.net>,
	 Theodore Ts'o <tytso@mit.edu>,
	Andreas Dilger <adilger.kernel@dilger.ca>,
	 Alexander Viro <viro@zeniv.linux.org.uk>,
	 Jan Kara <jack@suse.cz>,  Miklos Szeredi <miklos@szeredi.hu>,
	 Amir Goldstein <amir73il@gmail.com>,
	linux-ext4@vger.kernel.org,  linux-fsdevel@vger.kernel.org,
	linux-unionfs@vger.kernel.org,  linux-kernel@vger.kernel.org
Subject: Re: [PATCH 1/3] fs_parser: handle parameters that can be empty and don't have a value
Date: Sun, 03 Mar 2024 21:31:42 +0000	[thread overview]
Message-ID: <87wmqj80jl.fsf@suse.de> (raw)
In-Reply-To: <20240302-lamellen-hauskatze-b36d3207d73d@brauner> (Christian Brauner's message of "Sat, 2 Mar 2024 18:56:51 +0100")

Christian Brauner <brauner@kernel.org> writes:

> On Sat, Mar 02, 2024 at 12:46:41PM +0100, Christian Brauner wrote:
>> On Fri, Mar 01, 2024 at 03:45:27PM +0000, Luis Henriques wrote:
>> > Christian Brauner <brauner@kernel.org> writes:
>> > 
>> > > On Thu, Feb 29, 2024 at 04:30:08PM +0000, Luis Henriques wrote:
>> > >> Currently, only parameters that have the fs_parameter_spec 'type' set to
>> > >> NULL are handled as 'flag' types.  However, parameters that have the
>> > >> 'fs_param_can_be_empty' flag set and their value is NULL should also be
>> > >> handled as 'flag' type, as their type is set to 'fs_value_is_flag'.
>> > >> 
>> > >> Signed-off-by: Luis Henriques <lhenriques@suse.de>
>> > >> ---
>> > >>  fs/fs_parser.c | 3 ++-
>> > >>  1 file changed, 2 insertions(+), 1 deletion(-)
>> > >> 
>> > >> diff --git a/fs/fs_parser.c b/fs/fs_parser.c
>> > >> index edb3712dcfa5..53f6cb98a3e0 100644
>> > >> --- a/fs/fs_parser.c
>> > >> +++ b/fs/fs_parser.c
>> > >> @@ -119,7 +119,8 @@ int __fs_parse(struct p_log *log,
>> > >>  	/* Try to turn the type we were given into the type desired by the
>> > >>  	 * parameter and give an error if we can't.
>> > >>  	 */
>> > >> -	if (is_flag(p)) {
>> > >> +	if (is_flag(p) ||
>> > >> +	    (!param->string && (p->flags & fs_param_can_be_empty))) {
>> > >>  		if (param->type != fs_value_is_flag)
>> > >>  			return inval_plog(log, "Unexpected value for '%s'",
>> > >>  				      param->key);
>> > >
>> > > If the parameter was derived from FSCONFIG_SET_STRING in fsconfig() then
>> > > param->string is guaranteed to not be NULL. So really this is only
>> > > about:
>> > >
>> > > FSCONFIG_SET_FD
>> > > FSCONFIG_SET_BINARY
>> > > FSCONFIG_SET_PATH
>> > > FSCONFIG_SET_PATH_EMPTY
>> > >
>> > > and those values being used without a value. What filesystem does this?
>> > > I don't see any.
>> > >
>> > > The tempting thing to do here is to to just remove fs_param_can_be_empty
>> > > from every helper that isn't fs_param_is_string() until we actually have
>> > > a filesystem that wants to use any of the above as flags. Will lose a
>> > > lot of code that isn't currently used.
>> > 
>> > Right, I find it quite confusing and I may be fixing the issue in the
>> > wrong place.  What I'm seeing with ext4 when I mount a filesystem using
>> > the option '-o usrjquota' is that fs_parse() will get:
>> > 
>> >  * p->type is set to fs_param_is_string
>> >    ('p' is a struct fs_parameter_spec, ->type is a function)
>> >  * param->type is set to fs_value_is_flag
>> >    ('param' is a struct fs_parameter, ->type is an enum)
>> > 
>> > This is because ext4 will use the __fsparam macro to set define a
>> > fs_param_spec as a fs_param_is_string but will also set the
>> > fs_param_can_be_empty; and the fsconfig() syscall will get that parameter
>> > as a flag.  That's why param->string will be NULL in this case.
>> 
>> Thanks for the details. Let me see if I get this right. So you're saying that
>> someone is doing:
>> 
>> fsconfig(..., FSCONFIG_SET_FLAG, "usrjquota", NULL, 0); // [1]
>> 
>> ? Is so that is a vital part of the explanation. So please put that in the
>> commit message.
>> 
>> Then ext4 defines:
>> 
>> 	fsparam_string_empty ("usrjquota",		Opt_usrjquota),
>> 
>> So [1] gets us:
>> 
>>         param->type == fs_value_is_flag
>>         param->string == NULL
>> 
>> Now we enter into
>> fs_parse()
>> -> __fs_parse()
>>    -> fs_lookup_key() for @param and that does:
>> 
>>         bool want_flag = param->type == fs_value_is_flag;
>> 
>>         *negated = false;
>>         for (p = desc; p->name; p++) {
>>                 if (strcmp(p->name, name) != 0)
>>                         continue;
>>                 if (likely(is_flag(p) == want_flag))
>>                         return p;
>>                 other = p;
>>         }
>> 
>> So we don't have a flag parameter defined so the only real match we get is
>> @other for:
>> 
>>         fsparam_string_empty ("usrjquota",		Opt_usrjquota),
>> 
>> What happens now is that you call p->type == fs_param_is_string() and that
>> rejects it as bad parameter because param->type == fs_value_is_flag !=
>> fs_value_is_string as required. So you dont end up getting Opt_userjquota
>> called with param->string NULL, right? So there's not NULL deref or anything,
>> right?
>> 
>> You just fail to set usrjquota. Ok, so I think the correct fix is to do
>> something like the following in ext4:
>> 
>>         fsparam_string_empty ("usrjquota",      Opt_usrjquota),
>>         fs_param_flag        ("usrjquota",      Opt_usrjquota_flag),
>> 
>> and then in the switch you can do:
>> 
>> switch (opt)
>> case Opt_usrjquota:
>>         // string thing
>> case Opt_usrjquota_flag:
>>         // flag thing
>> 
>> And I really think we should kill all empty handling for non-string types and
>> only add that when there's a filesystem that actually needs it.
>
> So one option is to do the following:

Thanks a lot of your review (I forgot to thank you in my other reply!).

Now, I haven't yet tested this properly, but I think that's a much simpler
and cleaner way of fixing this issue.  Now, although it needs some
testing, I think the patch has one problem (see comment below).

Do you want me to send out a cleaned-up version[*] of it after some
testing (+ a similar fix for overlayfs)?  Or would you rather handle this
yourself?

(I'll probably won't be able to look into it until mid-week.)

[*] Obviously, keeping a notice that you were the original author.

> From 8bfb142e6caba70704998be072222d6a31d8b97b Mon Sep 17 00:00:00 2001
> From: Christian Brauner <brauner@kernel.org>
> Date: Sat, 2 Mar 2024 18:54:35 +0100
> Subject: [PATCH] [UNTESTED]
>
> Signed-off-by: Christian Brauner <brauner@kernel.org>
> ---
>  fs/ext4/super.c           | 32 ++++++++++++++++++--------------
>  include/linux/fs_parser.h |  3 +++
>  2 files changed, 21 insertions(+), 14 deletions(-)
>
> diff --git a/fs/ext4/super.c b/fs/ext4/super.c
> index ebd97442d1d4..bd625f06ec0f 100644
> --- a/fs/ext4/super.c
> +++ b/fs/ext4/super.c
> @@ -1724,10 +1724,6 @@ static const struct constant_table ext4_param_dax[] = {
>  	{}
>  };
>  
> -/* String parameter that allows empty argument */
> -#define fsparam_string_empty(NAME, OPT) \
> -	__fsparam(fs_param_is_string, NAME, OPT, fs_param_can_be_empty, NULL)
> -
>  /*
>   * Mount option specification
>   * We don't use fsparam_flag_no because of the way we set the
> @@ -1768,10 +1764,8 @@ static const struct fs_parameter_spec ext4_param_specs[] = {
>  	fsparam_enum	("data",		Opt_data, ext4_param_data),
>  	fsparam_enum	("data_err",		Opt_data_err,
>  						ext4_param_data_err),
> -	fsparam_string_empty
> -			("usrjquota",		Opt_usrjquota),
> -	fsparam_string_empty
> -			("grpjquota",		Opt_grpjquota),
> +	fsparam_string_or_flag ("usrjquota",	Opt_usrjquota),
> +	fsparam_string_or_flag ("grpjquota",	Opt_grpjquota),
>  	fsparam_enum	("jqfmt",		Opt_jqfmt, ext4_param_jqfmt),
>  	fsparam_flag	("grpquota",		Opt_grpquota),
>  	fsparam_flag	("quota",		Opt_quota),
> @@ -2183,15 +2177,25 @@ static int ext4_parse_param(struct fs_context *fc, struct fs_parameter *param)
>  	switch (token) {
>  #ifdef CONFIG_QUOTA
>  	case Opt_usrjquota:
> -		if (!param->string)
> -			return unnote_qf_name(fc, USRQUOTA);
> -		else
> +		if (param->type == fs_value_is_string) {
> +			if (!*param->string)
> +				return unnote_qf_name(fc, USRQUOTA);
> +
>  			return note_qf_name(fc, USRQUOTA, param);
> +		}
> +
> +		// param->type == fs_value_is_flag
> +		return note_qf_name(fc, USRQUOTA, param);
                       ^^^^^^^^^^^^
I think this isn't correct -- the correct function to call in this case is
unnote_qf_name(), not note_qf_name().  The same comment applies to the
grpjquota parameter handling.

Cheers,
-- 
Luís

>  	case Opt_grpjquota:
> -		if (!param->string)
> -			return unnote_qf_name(fc, GRPQUOTA);
> -		else
> +		if (param->type == fs_value_is_string) {
> +			if (!*param->string)
> +				return unnote_qf_name(fc, GRPQUOTA);
> +
>  			return note_qf_name(fc, GRPQUOTA, param);
> +		}
> +
> +		// param->type == fs_value_is_flag
> +		return note_qf_name(fc, GRPQUOTA, param);
>  #endif
>  	case Opt_sb:
>  		if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE) {
> diff --git a/include/linux/fs_parser.h b/include/linux/fs_parser.h
> index 01542c4b87a2..4f45141bea95 100644
> --- a/include/linux/fs_parser.h
> +++ b/include/linux/fs_parser.h
> @@ -131,5 +131,8 @@ static inline bool fs_validate_description(const char *name,
>  #define fsparam_bdev(NAME, OPT)	__fsparam(fs_param_is_blockdev, NAME, OPT, 0, NULL)
>  #define fsparam_path(NAME, OPT)	__fsparam(fs_param_is_path, NAME, OPT, 0, NULL)
>  #define fsparam_fd(NAME, OPT)	__fsparam(fs_param_is_fd, NAME, OPT, 0, NULL)
> +#define fsparam_string_or_flag(NAME, OPT) \
> +	__fsparam(fs_param_is_string, NAME, OPT, fs_param_can_be_empty, NULL), \
> +	fsparam_flag(NAME, OPT)
>  
>  #endif /* _LINUX_FS_PARSER_H */
> -- 
>
> 2.43.0
>


  reply	other threads:[~2024-03-03 21:31 UTC|newest]

Thread overview: 22+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-02-29 16:30 [PATCH 0/3] fs_parser: handle parameters that can be empty and don't have a value Luis Henriques
2024-02-29 16:30 ` [PATCH 1/3] " Luis Henriques
2024-03-01 13:31   ` Christian Brauner
2024-03-01 15:45     ` Luis Henriques
2024-03-02 11:46       ` Christian Brauner
2024-03-02 17:56         ` Christian Brauner
2024-03-03 21:31           ` Luis Henriques [this message]
2024-03-04  9:04             ` Christian Brauner
2024-03-03 21:17         ` Luis Henriques
2024-03-07 15:13       ` Jan Kara
2024-03-08  9:53         ` Christian Brauner
2024-03-08 10:12           ` Luis Henriques
2024-03-08 23:09             ` Jan Kara
2024-03-11 10:26               ` Luis Henriques
2024-03-11 16:01                 ` Jan Kara
2024-02-29 16:30 ` [PATCH 2/3] ext4: fix mount parameters check for empty values Luis Henriques
2024-03-01 13:36   ` Christian Brauner
2024-03-01 15:47     ` Luis Henriques
2024-02-29 16:30 ` [PATCH 3/3] overlay: " Luis Henriques
2024-03-01 13:37   ` Christian Brauner
2024-03-01 13:12 ` [PATCH 0/3] fs_parser: handle parameters that can be empty and don't have a value Christian Brauner
2024-03-01 14:54   ` Eric Sandeen

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=87wmqj80jl.fsf@suse.de \
    --to=lhenriques@suse.de \
    --cc=adilger.kernel@dilger.ca \
    --cc=amir73il@gmail.com \
    --cc=brauner@kernel.org \
    --cc=jack@suse.cz \
    --cc=linux-ext4@vger.kernel.org \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-unionfs@vger.kernel.org \
    --cc=miklos@szeredi.hu \
    --cc=sandeen@sandeen.net \
    --cc=tytso@mit.edu \
    --cc=viro@zeniv.linux.org.uk \
    /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
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.