| From: Ian Kent <raven@themaw.net> |
| Subject: vfs: parse: deal with zero length string value |
| Date: Tue, 20 Sep 2022 15:26:29 +0800 |
| |
| Parsing an fs string that has zero length should result in the parameter |
| being set to NULL so that downstream processing handles it correctly. For |
| example, the proc mount table processing should print "(none)" in this |
| case to preserve mount record field count, but if the value points to the |
| NULL string this doesn't happen. |
| |
| Link: https://lkml.kernel.org/r/166365878918.39016.12757946948158123324.stgit@donald.themaw.net |
| Signed-off-by: Ian Kent <raven@themaw.net> |
| Cc: Al Viro <viro@zeniv.linux.org.uk> |
| Cc: Carlos Maiolino <cmaiolino@redhat.com> |
| Cc: David Howells <dhowells@redhat.com> |
| Cc: kernel test robot <oliver.sang@intel.com> |
| Cc: Miklos Szeredi <miklos@szeredi.hu> |
| Cc: Siddhesh Poyarekar <siddhesh@gotplt.org> |
| Cc: Theodore Ts'o <tytso@mit.edu> |
| Signed-off-by: Andrew Morton <akpm@linux-foundation.org> |
| --- |
| |
| fs/fs_context.c | 17 ++++++++++++----- |
| fs/fs_parser.c | 16 ++++++++++++++++ |
| include/linux/fs_context.h | 3 ++- |
| 3 files changed, 30 insertions(+), 6 deletions(-) |
| |
| --- a/fs/fs_context.c~vfs-parse-deal-with-zero-length-string-value |
| +++ a/fs/fs_context.c |
| @@ -96,7 +96,9 @@ int vfs_parse_fs_param_source(struct fs_ |
| if (strcmp(param->key, "source") != 0) |
| return -ENOPARAM; |
| |
| - if (param->type != fs_value_is_string) |
| + /* source value may be NULL */ |
| + if (param->type != fs_value_is_string && |
| + param->type != fs_value_is_empty) |
| return invalf(fc, "Non-string source"); |
| |
| if (fc->source) |
| @@ -175,10 +177,15 @@ int vfs_parse_fs_string(struct fs_contex |
| }; |
| |
| if (value) { |
| - param.string = kmemdup_nul(value, v_size, GFP_KERNEL); |
| - if (!param.string) |
| - return -ENOMEM; |
| - param.type = fs_value_is_string; |
| + if (!v_size) { |
| + param.string = NULL; |
| + param.type = fs_value_is_empty; |
| + } else { |
| + param.string = kmemdup_nul(value, v_size, GFP_KERNEL); |
| + if (!param.string) |
| + return -ENOMEM; |
| + param.type = fs_value_is_string; |
| + } |
| } |
| |
| ret = vfs_parse_fs_param(fc, ¶m); |
| --- a/fs/fs_parser.c~vfs-parse-deal-with-zero-length-string-value |
| +++ a/fs/fs_parser.c |
| @@ -197,6 +197,8 @@ int fs_param_is_bool(struct p_log *log, |
| struct fs_parameter *param, struct fs_parse_result *result) |
| { |
| int b; |
| + if (param->type == fs_value_is_empty) |
| + return 0; |
| if (param->type != fs_value_is_string) |
| return fs_param_bad_value(log, param); |
| if (!*param->string && (p->flags & fs_param_can_be_empty)) |
| @@ -213,6 +215,8 @@ int fs_param_is_u32(struct p_log *log, c |
| struct fs_parameter *param, struct fs_parse_result *result) |
| { |
| int base = (unsigned long)p->data; |
| + if (param->type == fs_value_is_empty) |
| + return 0; |
| if (param->type != fs_value_is_string) |
| return fs_param_bad_value(log, param); |
| if (!*param->string && (p->flags & fs_param_can_be_empty)) |
| @@ -226,6 +230,8 @@ EXPORT_SYMBOL(fs_param_is_u32); |
| int fs_param_is_s32(struct p_log *log, const struct fs_parameter_spec *p, |
| struct fs_parameter *param, struct fs_parse_result *result) |
| { |
| + if (param->type == fs_value_is_empty) |
| + return 0; |
| if (param->type != fs_value_is_string) |
| return fs_param_bad_value(log, param); |
| if (!*param->string && (p->flags & fs_param_can_be_empty)) |
| @@ -239,6 +245,8 @@ EXPORT_SYMBOL(fs_param_is_s32); |
| int fs_param_is_u64(struct p_log *log, const struct fs_parameter_spec *p, |
| struct fs_parameter *param, struct fs_parse_result *result) |
| { |
| + if (param->type == fs_value_is_empty) |
| + return 0; |
| if (param->type != fs_value_is_string) |
| return fs_param_bad_value(log, param); |
| if (!*param->string && (p->flags & fs_param_can_be_empty)) |
| @@ -253,6 +261,8 @@ int fs_param_is_enum(struct p_log *log, |
| struct fs_parameter *param, struct fs_parse_result *result) |
| { |
| const struct constant_table *c; |
| + if (param->type == fs_value_is_empty) |
| + return 0; |
| if (param->type != fs_value_is_string) |
| return fs_param_bad_value(log, param); |
| if (!*param->string && (p->flags & fs_param_can_be_empty)) |
| @@ -268,6 +278,8 @@ EXPORT_SYMBOL(fs_param_is_enum); |
| int fs_param_is_string(struct p_log *log, const struct fs_parameter_spec *p, |
| struct fs_parameter *param, struct fs_parse_result *result) |
| { |
| + if (param->type == fs_value_is_empty) |
| + return 0; |
| if (param->type != fs_value_is_string || |
| (!*param->string && !(p->flags & fs_param_can_be_empty))) |
| return fs_param_bad_value(log, param); |
| @@ -278,6 +290,8 @@ EXPORT_SYMBOL(fs_param_is_string); |
| int fs_param_is_blob(struct p_log *log, const struct fs_parameter_spec *p, |
| struct fs_parameter *param, struct fs_parse_result *result) |
| { |
| + if (param->type == fs_value_is_empty) |
| + return 0; |
| if (param->type != fs_value_is_blob) |
| return fs_param_bad_value(log, param); |
| return 0; |
| @@ -287,6 +301,8 @@ EXPORT_SYMBOL(fs_param_is_blob); |
| int fs_param_is_fd(struct p_log *log, const struct fs_parameter_spec *p, |
| struct fs_parameter *param, struct fs_parse_result *result) |
| { |
| + if (param->type == fs_value_is_empty) |
| + return 0; |
| switch (param->type) { |
| case fs_value_is_string: |
| if ((!*param->string && !(p->flags & fs_param_can_be_empty)) || |
| --- a/include/linux/fs_context.h~vfs-parse-deal-with-zero-length-string-value |
| +++ a/include/linux/fs_context.h |
| @@ -50,7 +50,8 @@ enum fs_context_phase { |
| */ |
| enum fs_value_type { |
| fs_value_is_undefined, |
| - fs_value_is_flag, /* Value not given a value */ |
| + fs_value_is_flag, /* Does not take a value */ |
| + fs_value_is_empty, /* Value is not given */ |
| fs_value_is_string, /* Value is a string */ |
| fs_value_is_blob, /* Value is a binary blob */ |
| fs_value_is_filename, /* Value is a filename* + dirfd */ |
| _ |