[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20220513110741.uofbacfs7li4cqio@riteshh-domain>
Date: Fri, 13 May 2022 16:37:41 +0530
From: Ritesh Harjani <ritesh.list@...il.com>
To: Eric Biggers <ebiggers@...nel.org>
Cc: linux-fscrypt@...r.kernel.org, linux-ext4@...r.kernel.org,
linux-f2fs-devel@...ts.sourceforge.net,
Lukas Czerner <lczerner@...hat.com>,
Theodore Ts'o <tytso@....edu>,
Jaegeuk Kim <jaegeuk@...nel.org>,
Jeff Layton <jlayton@...nel.org>
Subject: Re: [PATCH v2 5/7] ext4: fix up test_dummy_encryption handling for
new mount API
On 22/04/30 10:08PM, Eric Biggers wrote:
> From: Eric Biggers <ebiggers@...gle.com>
>
> Since ext4 was converted to the new mount API, the test_dummy_encryption
> mount option isn't being handled entirely correctly, because the needed
> fscrypt_set_test_dummy_encryption() helper function combines
> parsing/checking/applying into one function. That doesn't work well
> with the new mount API, which split these into separate steps.
>
> This was sort of okay anyway, due to the parsing logic that was copied
> from fscrypt_set_test_dummy_encryption() into ext4_parse_param(),
> combined with an additional check in ext4_check_test_dummy_encryption().
> However, these overlooked the case of changing the value of
> test_dummy_encryption on remount, which isn't allowed but ext4 wasn't
> detecting until ext4_apply_options() when it's too late to fail.
> Another bug is that if test_dummy_encryption was specified multiple
> times with an argument, memory was leaked.
>
> Fix this up properly by using the new helper functions that allow
> splitting up the parse/check/apply steps for test_dummy_encryption.
>
> Fixes: cebe85d570cf ("ext4: switch to the new mount api")
> Signed-off-by: Eric Biggers <ebiggers@...gle.com>
I just had a small observation. Feel free to check it at your end too.
> ---
> fs/ext4/ext4.h | 6 ---
> fs/ext4/super.c | 131 +++++++++++++++++++++++++-----------------------
> 2 files changed, 67 insertions(+), 70 deletions(-)
>
> diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
> index a743b1e3b89ec..f6d6661817b63 100644
> --- a/fs/ext4/ext4.h
> +++ b/fs/ext4/ext4.h
> @@ -1440,12 +1440,6 @@ struct ext4_super_block {
>
> #ifdef __KERNEL__
>
> -#ifdef CONFIG_FS_ENCRYPTION
> -#define DUMMY_ENCRYPTION_ENABLED(sbi) ((sbi)->s_dummy_enc_policy.policy != NULL)
> -#else
> -#define DUMMY_ENCRYPTION_ENABLED(sbi) (0)
> -#endif
> -
> /* Number of quota types we support */
> #define EXT4_MAXQUOTAS 3
>
> diff --git a/fs/ext4/super.c b/fs/ext4/super.c
> index 64ce17714e193..43e4cd358b33b 100644
> --- a/fs/ext4/super.c
> +++ b/fs/ext4/super.c
> @@ -87,7 +87,7 @@ static struct inode *ext4_get_journal_inode(struct super_block *sb,
> static int ext4_validate_options(struct fs_context *fc);
> static int ext4_check_opt_consistency(struct fs_context *fc,
> struct super_block *sb);
> -static int ext4_apply_options(struct fs_context *fc, struct super_block *sb);
> +static void ext4_apply_options(struct fs_context *fc, struct super_block *sb);
> static int ext4_parse_param(struct fs_context *fc, struct fs_parameter *param);
> static int ext4_get_tree(struct fs_context *fc);
> static int ext4_reconfigure(struct fs_context *fc);
> @@ -1989,31 +1989,12 @@ ext4_sb_read_encoding(const struct ext4_super_block *es)
> }
> #endif
>
> -static int ext4_set_test_dummy_encryption(struct super_block *sb, char *arg)
> -{
> -#ifdef CONFIG_FS_ENCRYPTION
> - struct ext4_sb_info *sbi = EXT4_SB(sb);
> - int err;
> -
> - err = fscrypt_set_test_dummy_encryption(sb, arg,
> - &sbi->s_dummy_enc_policy);
> - if (err) {
> - ext4_msg(sb, KERN_WARNING,
> - "Error while setting test dummy encryption [%d]", err);
> - return err;
> - }
> - ext4_msg(sb, KERN_WARNING, "Test dummy encryption mode enabled");
> -#endif
> - return 0;
> -}
> -
> #define EXT4_SPEC_JQUOTA (1 << 0)
> #define EXT4_SPEC_JQFMT (1 << 1)
> #define EXT4_SPEC_DATAJ (1 << 2)
> #define EXT4_SPEC_SB_BLOCK (1 << 3)
> #define EXT4_SPEC_JOURNAL_DEV (1 << 4)
> #define EXT4_SPEC_JOURNAL_IOPRIO (1 << 5)
> -#define EXT4_SPEC_DUMMY_ENCRYPTION (1 << 6)
> #define EXT4_SPEC_s_want_extra_isize (1 << 7)
> #define EXT4_SPEC_s_max_batch_time (1 << 8)
> #define EXT4_SPEC_s_min_batch_time (1 << 9)
> @@ -2030,7 +2011,7 @@ static int ext4_set_test_dummy_encryption(struct super_block *sb, char *arg)
>
> struct ext4_fs_context {
> char *s_qf_names[EXT4_MAXQUOTAS];
> - char *test_dummy_enc_arg;
> + struct fscrypt_dummy_policy dummy_enc_policy;
> int s_jquota_fmt; /* Format of quota to use */
> #ifdef CONFIG_EXT4_DEBUG
> int s_fc_debug_max_replay;
> @@ -2061,9 +2042,8 @@ struct ext4_fs_context {
> ext4_fsblk_t s_sb_block;
> };
>
> -static void ext4_fc_free(struct fs_context *fc)
> +static void __ext4_fc_free(struct ext4_fs_context *ctx)
> {
> - struct ext4_fs_context *ctx = fc->fs_private;
> int i;
>
> if (!ctx)
> @@ -2072,10 +2052,15 @@ static void ext4_fc_free(struct fs_context *fc)
> for (i = 0; i < EXT4_MAXQUOTAS; i++)
> kfree(ctx->s_qf_names[i]);
>
> - kfree(ctx->test_dummy_enc_arg);
> + fscrypt_free_dummy_policy(&ctx->dummy_enc_policy);
> kfree(ctx);
> }
>
> +static void ext4_fc_free(struct fs_context *fc)
> +{
> + __ext4_fc_free(fc->fs_private);
> +}
> +
> int ext4_init_fs_context(struct fs_context *fc)
> {
> struct ext4_fs_context *ctx;
> @@ -2148,6 +2133,29 @@ static int unnote_qf_name(struct fs_context *fc, int qtype)
> }
> #endif
>
> +static int ext4_parse_test_dummy_encryption(const struct fs_parameter *param,
> + struct ext4_fs_context *ctx)
> +{
> + int err;
> +
> + if (!IS_ENABLED(CONFIG_FS_ENCRYPTION)) {
> + ext4_msg(NULL, KERN_WARNING,
> + "test_dummy_encryption option not supported");
> + return -EINVAL;
> + }
> + err = fscrypt_parse_test_dummy_encryption(param,
> + &ctx->dummy_enc_policy);
> + if (err == -EINVAL) {
> + ext4_msg(NULL, KERN_WARNING,
> + "Value of option \"%s\" is unrecognized", param->key);
> + } else if (err == -EEXIST) {
> + ext4_msg(NULL, KERN_WARNING,
> + "Conflicting test_dummy_encryption options");
> + return -EINVAL;
> + }
> + return err;
> +}
> +
> #define EXT4_SET_CTX(name) \
> static inline void ctx_set_##name(struct ext4_fs_context *ctx, \
> unsigned long flag) \
> @@ -2410,29 +2418,7 @@ static int ext4_parse_param(struct fs_context *fc, struct fs_parameter *param)
> ctx->spec |= EXT4_SPEC_JOURNAL_IOPRIO;
> return 0;
> case Opt_test_dummy_encryption:
> -#ifdef CONFIG_FS_ENCRYPTION
> - if (param->type == fs_value_is_flag) {
> - ctx->spec |= EXT4_SPEC_DUMMY_ENCRYPTION;
> - ctx->test_dummy_enc_arg = NULL;
> - return 0;
> - }
> - if (*param->string &&
> - !(!strcmp(param->string, "v1") ||
> - !strcmp(param->string, "v2"))) {
> - ext4_msg(NULL, KERN_WARNING,
> - "Value of option \"%s\" is unrecognized",
> - param->key);
> - return -EINVAL;
> - }
> - ctx->spec |= EXT4_SPEC_DUMMY_ENCRYPTION;
> - ctx->test_dummy_enc_arg = kmemdup_nul(param->string, param->size,
> - GFP_KERNEL);
> - return 0;
> -#else
> - ext4_msg(NULL, KERN_WARNING,
> - "test_dummy_encryption option not supported");
> - return -EINVAL;
> -#endif
> + return ext4_parse_test_dummy_encryption(param, ctx);
> case Opt_dax:
> case Opt_dax_type:
> #ifdef CONFIG_FS_DAX
> @@ -2623,10 +2609,11 @@ static int parse_apply_sb_mount_options(struct super_block *sb,
> if (s_ctx->spec & EXT4_SPEC_JOURNAL_IOPRIO)
> m_ctx->journal_ioprio = s_ctx->journal_ioprio;
>
> - ret = ext4_apply_options(fc, sb);
> + ext4_apply_options(fc, sb);
> + ret = 0;
>
> out_free:
> - kfree(s_ctx);
> + __ext4_fc_free(s_ctx);
I think we can still call ext4_fc_free(fc) and we don't need __ext4_fc_free().
Right?
-ritesh
> kfree(fc);
> kfree(s_mount_opts);
> return ret;
> @@ -2792,9 +2779,9 @@ static int ext4_check_test_dummy_encryption(const struct fs_context *fc,
> {
> const struct ext4_fs_context *ctx = fc->fs_private;
> const struct ext4_sb_info *sbi = EXT4_SB(sb);
> + int err;
>
> - if (!IS_ENABLED(CONFIG_FS_ENCRYPTION) ||
> - !(ctx->spec & EXT4_SPEC_DUMMY_ENCRYPTION))
> + if (!fscrypt_is_dummy_policy_set(&ctx->dummy_enc_policy))
> return 0;
>
> if (!ext4_has_feature_encrypt(sb)) {
> @@ -2808,13 +2795,35 @@ static int ext4_check_test_dummy_encryption(const struct fs_context *fc,
> * needed to allow it to be set or changed during remount. We do allow
> * it to be specified during remount, but only if there is no change.
> */
> - if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE &&
> - !DUMMY_ENCRYPTION_ENABLED(sbi)) {
> + if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE) {
> + if (fscrypt_dummy_policies_equal(&sbi->s_dummy_enc_policy,
> + &ctx->dummy_enc_policy))
> + return 0;
> ext4_msg(NULL, KERN_WARNING,
> - "Can't set test_dummy_encryption on remount");
> + "Can't set or change test_dummy_encryption on remount");
> return -EINVAL;
> }
> - return 0;
> + /*
> + * fscrypt_add_test_dummy_key() technically changes the super_block, so
> + * it technically should be delayed until ext4_apply_options() like the
> + * other changes. But since we never get here for remounts (see above),
> + * and this is the last chance to report errors, we do it here.
> + */
> + err = fscrypt_add_test_dummy_key(sb, &ctx->dummy_enc_policy);
> + if (err)
> + ext4_msg(NULL, KERN_WARNING,
> + "Error adding test dummy encryption key [%d]", err);
> + return err;
> +}
> +
> +static void ext4_apply_test_dummy_encryption(struct ext4_fs_context *ctx,
> + struct super_block *sb)
> +{
> + if (!fscrypt_is_dummy_policy_set(&ctx->dummy_enc_policy))
> + return;
> + EXT4_SB(sb)->s_dummy_enc_policy = ctx->dummy_enc_policy;
> + memset(&ctx->dummy_enc_policy, 0, sizeof(ctx->dummy_enc_policy));
> + ext4_msg(sb, KERN_WARNING, "Test dummy encryption mode enabled");
> }
>
> static int ext4_check_opt_consistency(struct fs_context *fc,
> @@ -2901,11 +2910,10 @@ static int ext4_check_opt_consistency(struct fs_context *fc,
> return ext4_check_quota_consistency(fc, sb);
> }
>
> -static int ext4_apply_options(struct fs_context *fc, struct super_block *sb)
> +static void ext4_apply_options(struct fs_context *fc, struct super_block *sb)
> {
> struct ext4_fs_context *ctx = fc->fs_private;
> struct ext4_sb_info *sbi = fc->s_fs_info;
> - int ret = 0;
>
> sbi->s_mount_opt &= ~ctx->mask_s_mount_opt;
> sbi->s_mount_opt |= ctx->vals_s_mount_opt;
> @@ -2942,10 +2950,7 @@ static int ext4_apply_options(struct fs_context *fc, struct super_block *sb)
>
> ext4_apply_quota_options(fc, sb);
>
> - if (ctx->spec & EXT4_SPEC_DUMMY_ENCRYPTION)
> - ret = ext4_set_test_dummy_encryption(sb, ctx->test_dummy_enc_arg);
> -
> - return ret;
> + ext4_apply_test_dummy_encryption(ctx, sb);
> }
>
>
> @@ -4667,9 +4672,7 @@ static int __ext4_fill_super(struct fs_context *fc, struct super_block *sb)
> if (err < 0)
> goto failed_mount;
>
> - err = ext4_apply_options(fc, sb);
> - if (err < 0)
> - goto failed_mount;
> + ext4_apply_options(fc, sb);
>
> #if IS_ENABLED(CONFIG_UNICODE)
> if (ext4_has_feature_casefold(sb) && !sb->s_encoding) {
> --
> 2.36.0
>
Powered by blists - more mailing lists