@@ -216,6 +216,12 @@ struct fscrypt_prepared_key {
*/
size_t device_count;
#endif
+ /*
+ * For destroying asynchronously.
+ */
+ struct work_struct work;
+ /* A pointer to free after destroy. */
+ void *ptr_to_free;
enum fscrypt_prepared_key_type type;
};
@@ -526,8 +532,7 @@ fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key,
}
static inline void
-fscrypt_destroy_inline_crypt_key(struct super_block *sb,
- struct fscrypt_prepared_key *prep_key)
+fscrypt_destroy_inline_crypt_key(struct fscrypt_prepared_key *prep_key)
{
}
@@ -748,7 +753,8 @@ int fscrypt_prepare_key(struct fscrypt_prepared_key *prep_key,
const u8 *raw_key, const struct fscrypt_info *ci);
void fscrypt_destroy_prepared_key(struct super_block *sb,
- struct fscrypt_prepared_key *prep_key);
+ struct fscrypt_prepared_key *prep_key,
+ void *ptr_to_free);
int fscrypt_set_per_file_enc_key(struct fscrypt_info *ci, const u8 *raw_key);
@@ -107,11 +107,11 @@ void fscrypt_put_master_key_activeref(struct super_block *sb,
for (i = 0; i <= FSCRYPT_MODE_MAX; i++) {
fscrypt_destroy_prepared_key(
- sb, &mk->mk_direct_keys[i]);
+ sb, &mk->mk_direct_keys[i], NULL);
fscrypt_destroy_prepared_key(
- sb, &mk->mk_iv_ino_lblk_64_keys[i]);
+ sb, &mk->mk_iv_ino_lblk_64_keys[i], NULL);
fscrypt_destroy_prepared_key(
- sb, &mk->mk_iv_ino_lblk_32_keys[i]);
+ sb, &mk->mk_iv_ino_lblk_32_keys[i], NULL);
}
memzero_explicit(&mk->mk_ino_hash_key,
sizeof(mk->mk_ino_hash_key));
@@ -190,13 +190,36 @@ int fscrypt_prepare_key(struct fscrypt_prepared_key *prep_key,
return 0;
}
-/* Destroy a crypto transform object and/or blk-crypto key. */
-void fscrypt_destroy_prepared_key(struct super_block *sb,
- struct fscrypt_prepared_key *prep_key)
+static void __destroy_key(struct fscrypt_prepared_key *prep_key)
{
+ void *ptr_to_free = prep_key->ptr_to_free;
+
crypto_free_skcipher(prep_key->tfm);
fscrypt_destroy_inline_crypt_key(prep_key);
memzero_explicit(prep_key, sizeof(*prep_key));
+ if (ptr_to_free)
+ kfree_sensitive(ptr_to_free);
+}
+
+static void __destroy_key_work(struct work_struct *work)
+{
+ struct fscrypt_prepared_key *prep_key =
+ container_of(work, struct fscrypt_prepared_key, work);
+
+ __destroy_key(prep_key);
+}
+
+/* Destroy a crypto transform object and/or blk-crypto key. */
+void fscrypt_destroy_prepared_key(struct super_block *sb,
+ struct fscrypt_prepared_key *prep_key,
+ void *ptr_to_free)
+{
+ prep_key->ptr_to_free = ptr_to_free;
+ if (fscrypt_fs_uses_extent_encryption(sb)) {
+ INIT_WORK(&prep_key->work, __destroy_key_work);
+ queue_work(system_unbound_wq, &prep_key->work);
+ } else
+ __destroy_key(prep_key);
}
/* Given a per-file encryption key, set up the file's crypto transform object */
@@ -608,8 +631,8 @@ static void put_crypt_info(struct fscrypt_info *ci)
fscrypt_put_direct_key(ci->ci_enc_key);
if (type == FSCRYPT_KEY_PER_INFO) {
fscrypt_destroy_prepared_key(ci->ci_sb,
+ ci->ci_enc_key,
ci->ci_enc_key);
- kfree_sensitive(ci->ci_enc_key);
}
}
@@ -155,8 +155,7 @@ struct fscrypt_direct_key {
static void free_direct_key(struct fscrypt_direct_key *dk)
{
if (dk) {
- fscrypt_destroy_prepared_key(dk->dk_sb, &dk->dk_key);
- kfree_sensitive(dk);
+ fscrypt_destroy_prepared_key(dk->dk_sb, &dk->dk_key, dk);
}
}
btrfs sometimes frees extents while holding a mutex. This makes it hard to free the prepared keys associated therewith, as the free process may need to take a semaphore. Just offloading freeing to rcu doesn't work, as rcu may call the callback in softirq context, which also doesn't allow taking a semaphore. Thus, for extent infos, offload their freeing to the general system workqueue. Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me> --- fs/crypto/fscrypt_private.h | 12 +++++++++--- fs/crypto/keyring.c | 6 +++--- fs/crypto/keysetup.c | 31 +++++++++++++++++++++++++++---- fs/crypto/keysetup_v1.c | 3 +-- 4 files changed, 40 insertions(+), 12 deletions(-)