fscrypt: stop holding extra request_queue references

Now that the fscrypt_master_key lifetime has been reworked to not be
subject to the quirks of the keyrings subsystem, blk_crypto_evict_key()
no longer gets called after the filesystem has already been unmounted.
Therefore, there is no longer any need to hold extra references to the
filesystem's request_queue(s).  (And these references didn't always do
their intended job anyway, as pinning a request_queue doesn't
necessarily pin the corresponding blk_crypto_profile.)

Stop taking these extra references.  Instead, just pass the super_block
to fscrypt_destroy_inline_crypt_key(), and use it to get the list of
block devices the key needs to be evicted from.

Signed-off-by: Eric Biggers <ebiggers@google.com>
Link: https://lore.kernel.org/r/20220901193208.138056-3-ebiggers@kernel.org
This commit is contained in:
Eric Biggers 2022-09-01 12:32:07 -07:00
parent d7e7b9af10
commit 22e9947a4b
5 changed files with 65 additions and 61 deletions

View File

@ -184,7 +184,7 @@ struct fscrypt_symlink_data {
struct fscrypt_prepared_key { struct fscrypt_prepared_key {
struct crypto_skcipher *tfm; struct crypto_skcipher *tfm;
#ifdef CONFIG_FS_ENCRYPTION_INLINE_CRYPT #ifdef CONFIG_FS_ENCRYPTION_INLINE_CRYPT
struct fscrypt_blk_crypto_key *blk_key; struct blk_crypto_key *blk_key;
#endif #endif
}; };
@ -344,7 +344,8 @@ int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key,
const u8 *raw_key, const u8 *raw_key,
const struct fscrypt_info *ci); const struct fscrypt_info *ci);
void fscrypt_destroy_inline_crypt_key(struct fscrypt_prepared_key *prep_key); void fscrypt_destroy_inline_crypt_key(struct super_block *sb,
struct fscrypt_prepared_key *prep_key);
/* /*
* Check whether the crypto transform or blk-crypto key has been allocated in * Check whether the crypto transform or blk-crypto key has been allocated in
@ -390,7 +391,8 @@ fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key,
} }
static inline void static inline void
fscrypt_destroy_inline_crypt_key(struct fscrypt_prepared_key *prep_key) fscrypt_destroy_inline_crypt_key(struct super_block *sb,
struct fscrypt_prepared_key *prep_key)
{ {
} }
@ -600,7 +602,8 @@ extern struct fscrypt_mode fscrypt_modes[];
int fscrypt_prepare_key(struct fscrypt_prepared_key *prep_key, int fscrypt_prepare_key(struct fscrypt_prepared_key *prep_key,
const u8 *raw_key, const struct fscrypt_info *ci); const u8 *raw_key, const struct fscrypt_info *ci);
void fscrypt_destroy_prepared_key(struct fscrypt_prepared_key *prep_key); void fscrypt_destroy_prepared_key(struct super_block *sb,
struct fscrypt_prepared_key *prep_key);
int fscrypt_set_per_file_enc_key(struct fscrypt_info *ci, const u8 *raw_key); int fscrypt_set_per_file_enc_key(struct fscrypt_info *ci, const u8 *raw_key);

View File

@ -21,12 +21,6 @@
#include "fscrypt_private.h" #include "fscrypt_private.h"
struct fscrypt_blk_crypto_key {
struct blk_crypto_key base;
int num_devs;
struct request_queue *devs[];
};
static int fscrypt_get_num_devices(struct super_block *sb) static int fscrypt_get_num_devices(struct super_block *sb)
{ {
if (sb->s_cop->get_num_devices) if (sb->s_cop->get_num_devices)
@ -162,49 +156,42 @@ int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key,
const struct inode *inode = ci->ci_inode; const struct inode *inode = ci->ci_inode;
struct super_block *sb = inode->i_sb; struct super_block *sb = inode->i_sb;
enum blk_crypto_mode_num crypto_mode = ci->ci_mode->blk_crypto_mode; enum blk_crypto_mode_num crypto_mode = ci->ci_mode->blk_crypto_mode;
int num_devs = fscrypt_get_num_devices(sb); struct blk_crypto_key *blk_key;
int queue_refs = 0; struct request_queue **devs;
struct fscrypt_blk_crypto_key *blk_key; unsigned int num_devs;
unsigned int i;
int err; int err;
int i;
blk_key = kzalloc(struct_size(blk_key, devs, num_devs), GFP_KERNEL); blk_key = kmalloc(sizeof(*blk_key), GFP_KERNEL);
if (!blk_key) if (!blk_key)
return -ENOMEM; return -ENOMEM;
blk_key->num_devs = num_devs; err = blk_crypto_init_key(blk_key, raw_key, crypto_mode,
fscrypt_get_devices(sb, num_devs, blk_key->devs);
err = blk_crypto_init_key(&blk_key->base, raw_key, crypto_mode,
fscrypt_get_dun_bytes(ci), sb->s_blocksize); fscrypt_get_dun_bytes(ci), sb->s_blocksize);
if (err) { if (err) {
fscrypt_err(inode, "error %d initializing blk-crypto key", err); fscrypt_err(inode, "error %d initializing blk-crypto key", err);
goto fail; goto fail;
} }
/* /* Start using blk-crypto on all the filesystem's block devices. */
* We have to start using blk-crypto on all the filesystem's devices. num_devs = fscrypt_get_num_devices(sb);
* We also have to save all the request_queue's for later so that the devs = kmalloc_array(num_devs, sizeof(*devs), GFP_KERNEL);
* key can be evicted from them. This is needed because some keys if (!devs) {
* aren't destroyed until after the filesystem was already unmounted err = -ENOMEM;
* (namely, the per-mode keys in struct fscrypt_master_key). goto fail;
*/
for (i = 0; i < num_devs; i++) {
if (!blk_get_queue(blk_key->devs[i])) {
fscrypt_err(inode, "couldn't get request_queue");
err = -EAGAIN;
goto fail;
}
queue_refs++;
err = blk_crypto_start_using_key(&blk_key->base,
blk_key->devs[i]);
if (err) {
fscrypt_err(inode,
"error %d starting to use blk-crypto", err);
goto fail;
}
} }
fscrypt_get_devices(sb, num_devs, devs);
for (i = 0; i < num_devs; i++) {
err = blk_crypto_start_using_key(blk_key, devs[i]);
if (err)
break;
}
kfree(devs);
if (err) {
fscrypt_err(inode, "error %d starting to use blk-crypto", err);
goto fail;
}
/* /*
* Pairs with the smp_load_acquire() in fscrypt_is_key_prepared(). * Pairs with the smp_load_acquire() in fscrypt_is_key_prepared().
* I.e., here we publish ->blk_key with a RELEASE barrier so that * I.e., here we publish ->blk_key with a RELEASE barrier so that
@ -215,24 +202,31 @@ int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key,
return 0; return 0;
fail: fail:
for (i = 0; i < queue_refs; i++)
blk_put_queue(blk_key->devs[i]);
kfree_sensitive(blk_key); kfree_sensitive(blk_key);
return err; return err;
} }
void fscrypt_destroy_inline_crypt_key(struct fscrypt_prepared_key *prep_key) void fscrypt_destroy_inline_crypt_key(struct super_block *sb,
struct fscrypt_prepared_key *prep_key)
{ {
struct fscrypt_blk_crypto_key *blk_key = prep_key->blk_key; struct blk_crypto_key *blk_key = prep_key->blk_key;
int i; struct request_queue **devs;
unsigned int num_devs;
unsigned int i;
if (blk_key) { if (!blk_key)
for (i = 0; i < blk_key->num_devs; i++) { return;
blk_crypto_evict_key(blk_key->devs[i], &blk_key->base);
blk_put_queue(blk_key->devs[i]); /* Evict the key from all the filesystem's block devices. */
} num_devs = fscrypt_get_num_devices(sb);
kfree_sensitive(blk_key); devs = kmalloc_array(num_devs, sizeof(*devs), GFP_KERNEL);
if (devs) {
fscrypt_get_devices(sb, num_devs, devs);
for (i = 0; i < num_devs; i++)
blk_crypto_evict_key(devs[i], blk_key);
kfree(devs);
} }
kfree_sensitive(blk_key);
} }
bool __fscrypt_inode_uses_inline_crypto(const struct inode *inode) bool __fscrypt_inode_uses_inline_crypto(const struct inode *inode)
@ -282,7 +276,7 @@ void fscrypt_set_bio_crypt_ctx(struct bio *bio, const struct inode *inode,
ci = inode->i_crypt_info; ci = inode->i_crypt_info;
fscrypt_generate_dun(ci, first_lblk, dun); fscrypt_generate_dun(ci, first_lblk, dun);
bio_crypt_set_ctx(bio, &ci->ci_enc_key.blk_key->base, dun, gfp_mask); bio_crypt_set_ctx(bio, ci->ci_enc_key.blk_key, dun, gfp_mask);
} }
EXPORT_SYMBOL_GPL(fscrypt_set_bio_crypt_ctx); EXPORT_SYMBOL_GPL(fscrypt_set_bio_crypt_ctx);
@ -369,7 +363,7 @@ bool fscrypt_mergeable_bio(struct bio *bio, const struct inode *inode,
* uses the same pointer. I.e., there's currently no need to support * uses the same pointer. I.e., there's currently no need to support
* merging requests where the keys are the same but the pointers differ. * merging requests where the keys are the same but the pointers differ.
*/ */
if (bc->bc_key != &inode->i_crypt_info->ci_enc_key.blk_key->base) if (bc->bc_key != inode->i_crypt_info->ci_enc_key.blk_key)
return false; return false;
fscrypt_generate_dun(inode->i_crypt_info, next_lblk, next_dun); fscrypt_generate_dun(inode->i_crypt_info, next_lblk, next_dun);

View File

@ -105,9 +105,12 @@ void fscrypt_put_master_key_activeref(struct fscrypt_master_key *mk)
WARN_ON(!list_empty(&mk->mk_decrypted_inodes)); WARN_ON(!list_empty(&mk->mk_decrypted_inodes));
for (i = 0; i <= FSCRYPT_MODE_MAX; i++) { for (i = 0; i <= FSCRYPT_MODE_MAX; i++) {
fscrypt_destroy_prepared_key(&mk->mk_direct_keys[i]); fscrypt_destroy_prepared_key(
fscrypt_destroy_prepared_key(&mk->mk_iv_ino_lblk_64_keys[i]); sb, &mk->mk_direct_keys[i]);
fscrypt_destroy_prepared_key(&mk->mk_iv_ino_lblk_32_keys[i]); fscrypt_destroy_prepared_key(
sb, &mk->mk_iv_ino_lblk_64_keys[i]);
fscrypt_destroy_prepared_key(
sb, &mk->mk_iv_ino_lblk_32_keys[i]);
} }
memzero_explicit(&mk->mk_ino_hash_key, memzero_explicit(&mk->mk_ino_hash_key,
sizeof(mk->mk_ino_hash_key)); sizeof(mk->mk_ino_hash_key));

View File

@ -154,10 +154,11 @@ int fscrypt_prepare_key(struct fscrypt_prepared_key *prep_key,
} }
/* Destroy a crypto transform object and/or blk-crypto key. */ /* Destroy a crypto transform object and/or blk-crypto key. */
void fscrypt_destroy_prepared_key(struct fscrypt_prepared_key *prep_key) void fscrypt_destroy_prepared_key(struct super_block *sb,
struct fscrypt_prepared_key *prep_key)
{ {
crypto_free_skcipher(prep_key->tfm); crypto_free_skcipher(prep_key->tfm);
fscrypt_destroy_inline_crypt_key(prep_key); fscrypt_destroy_inline_crypt_key(sb, prep_key);
memzero_explicit(prep_key, sizeof(*prep_key)); memzero_explicit(prep_key, sizeof(*prep_key));
} }
@ -494,7 +495,8 @@ static void put_crypt_info(struct fscrypt_info *ci)
if (ci->ci_direct_key) if (ci->ci_direct_key)
fscrypt_put_direct_key(ci->ci_direct_key); fscrypt_put_direct_key(ci->ci_direct_key);
else if (ci->ci_owns_key) else if (ci->ci_owns_key)
fscrypt_destroy_prepared_key(&ci->ci_enc_key); fscrypt_destroy_prepared_key(ci->ci_inode->i_sb,
&ci->ci_enc_key);
mk = ci->ci_master_key; mk = ci->ci_master_key;
if (mk) { if (mk) {

View File

@ -143,6 +143,7 @@ invalid:
/* Master key referenced by DIRECT_KEY policy */ /* Master key referenced by DIRECT_KEY policy */
struct fscrypt_direct_key { struct fscrypt_direct_key {
struct super_block *dk_sb;
struct hlist_node dk_node; struct hlist_node dk_node;
refcount_t dk_refcount; refcount_t dk_refcount;
const struct fscrypt_mode *dk_mode; const struct fscrypt_mode *dk_mode;
@ -154,7 +155,7 @@ struct fscrypt_direct_key {
static void free_direct_key(struct fscrypt_direct_key *dk) static void free_direct_key(struct fscrypt_direct_key *dk)
{ {
if (dk) { if (dk) {
fscrypt_destroy_prepared_key(&dk->dk_key); fscrypt_destroy_prepared_key(dk->dk_sb, &dk->dk_key);
kfree_sensitive(dk); kfree_sensitive(dk);
} }
} }
@ -231,6 +232,7 @@ fscrypt_get_direct_key(const struct fscrypt_info *ci, const u8 *raw_key)
dk = kzalloc(sizeof(*dk), GFP_KERNEL); dk = kzalloc(sizeof(*dk), GFP_KERNEL);
if (!dk) if (!dk)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
dk->dk_sb = ci->ci_inode->i_sb;
refcount_set(&dk->dk_refcount, 1); refcount_set(&dk->dk_refcount, 1);
dk->dk_mode = ci->ci_mode; dk->dk_mode = ci->ci_mode;
err = fscrypt_prepare_key(&dk->dk_key, raw_key, ci); err = fscrypt_prepare_key(&dk->dk_key, raw_key, ci);